检查数据
如果您不知道要使用的内容,很难知道该怎么办,因此让我们加载数据集并进行查看。如果愿意,您可以在Jupyter Notebook中继续学习。
- import pandas as pd
- import numpy as np
- df = pd.read_csv("survey.csv")
- num_columns = len(df.columns)
- pd.set_option("display.max_columns", num_columns)
- df.head(30)
head()默认情况下,pandas 函数返回数据框的前5行,但我想多看一点以更好地了解数据集。
当我们讨论它时,我们也来看看数据框的形状。运行df.shape将返回有关我们数据框维数的信息(在这种情况下,这是行和列的数量),这实际上将告诉您您正在使用多少示例和功能。
专家提示:您可以使用来返回数据集中的观测值数量,df.shape[0]并使用来返回数据集中的要素数量df.shape[1]。这在构建模型时通常很有帮助。
首先要了解的是我们的数据集包含哪些功能。您可以在Kaggle上阅读此数据集的每个功能的描述。
我没有一个一个地检查每个功能,而是选择了懒惰路线并运行以下命令:
- feature_names = df.columns.tolist()
- feature_names.remove('Timestamp')
- feature_names.remove('comments')
- for column in feature_names:
- print column
- print df[column].value_counts(dropna=False)
这会在笔记本中打印出一个单元格,其中包含有关我的数据集的大量信息,我只需滚动即可轻松使用这些信息。当我浏览数据集时,我一直在关注诸如重复标签,错误或空值以及其他看起来不太正确的事情。
尽管许多色谱柱仍然可以使用,但仍有两根色谱柱需要清洁。这是我发现在将该模型输入机器学习算法之前需要注意的事项列表。
该Age列包含尚未出生的人(负数)。
该Age列还包含不太可能对他们的工作场所进行调查的儿童(例如5岁)。
该Age列中包含99999999999岁的某人,他们应该在吉尼斯世界纪录书之类的东西中。除了笑话,这个值是错误的。
列出了49个不同的值Gender。此外,“男性”和“男性”表面上是相同的,但目前被视为两个不同的类别。
self_employed 并work_interfere包含一些空值。
对于数字特征,快速检查特征之间的任何可能关联也可能会有帮助。您可以使用熊猫scatter_matrix轻松可视化数据。
分开功能和标签
如果我们使用监督式机器学习技术,则需要对每个观察的特征和标签之间的数据进行区分。最终,这取决于您要预测或分类的内容。在这种情况下,我已经决定要建立一个分类器,以预测是否有人会寻求治疗。
- features = df.drop('treatment', 1)
- labels = df['treatment']
清除空值(缺少数据)
让我们首先要弄清楚如何处理中发现的空值做启动self_employed 和work_interfere。在这两种情况下,该列均包含分类数据。重要的是要注意处理分类数据和数字数据的空值之间的区别,因为处理将取决于存在的数据类型。
由于如何处理丢失的数据很大程度上取决于数据的上下文和性质,因此没有确切的公式化方法可以处理丢失的数据。例如,数据是随机丢失还是在丢失数据和某些其他预测变量之间存在隐藏关系?
处理缺失数据的一种选择是简单地忽略或删除缺失数据的行,将其从我们的分析中丢弃。但是,由于信息丢失,这种方法通常不受欢迎-尤其是在丢失的数据中存在某种模式的情况下。
处理缺失值的另一种方法是归因(imputation),在该方法中,我们用对该值可能是的最佳猜测来替换空值。基本实现将简单地将所有缺失的值替换为给定特征的所有值的均值/中位数/众数。更高级的实现将使用其他功能,以便为您的缺失价值开发一个估算器。
以下面的示例为例,它是一个包含三个要素(天气状况,温度和湿度)的小型数据集,用于预测我是否可能打网球。
天气 | 温度 | 湿度 | 打网球? | |
1个 | 多云的 | 60 | N | 是 |
2 | 多雨的 | 75 | 80% | N |
3 | 多云的 | N | 50% | 没有 |
4 | 阳光明媚 |
如果我们删除所有具有空值的行,我们将只剩下一行,而我们的预测变量总是会猜测我会打网球,因为那是唯一可以借鉴的例子。假设我们改为选择均值插补替换第3行中的零温度值。在这种情况下,第3行的温度将被人为地报告为65。如果我们想更轻松地替换第1行的湿度的零值,我们可以训练估算器来猜测天气和温度数据可能给出的值提供。重要的是要注意,通过数据插补,您可以通过创建接近(或等于)平均值的新值来人为地减少数据集中的变化。
Scikit-learn提供了一种不完善的实现来处理丢失的数据,如下例所示。
- from sklearn.preprocessing import Imputer
- imputer = Imputer(missing_values = 'NaN', strategy = 'mean', axis = 0)
- imputer.fit(features)
- features = imputer.transform(features)
我之前提到过,“性别”有49个不同的值,我怀疑其中一些值不应视为不同的类别。最终,我决定将数据重新划分为三类:男性,女性和其他(占非二进制和跨性别人士)。
如果我想构建一个可以清除传入数据的预处理引擎,那么我可能会采取一种更高级的方法,并使用一些巧妙的正则表达式对该列进行重新分类。las,我选择了快速而肮脏的方法。
- male_terms = ["male", "m", "mal", "msle", "malr", "mail", "make", "cis male", "man", "maile", "male (cis)", "cis man"]
- female_terms = ["female", "f", "woman", "femake", "femaile", "femake", "cis female", "cis-female/femme", "female (cis)", "femail", "cis woman"]
- def clean_gender(response):
- if response.lower().rstrip() in male_terms:
- return "Male"
- elif response.lower().rstrip() in female_terms:
- return "Female"
- else:
- return "Other"
- df['Gender'] = df["Gender"].apply(lambda x: clean_gender(x))
离群值检测
正如我之前提到的,似乎有些离群值Age似乎是错误的。负年龄或极大整数之类的示例可能会对我们的机器学习算法的性能产生负面影响,因此我们需要解决它们。
我继续为工作场所中的成年人定义了可接受的年龄范围,并用空值替换了超出此范围的数字。
- df.Age.loc[(df.Age < 18) | (df.Age > 80)] = np.nan
然后可以使用前面介绍的sklearn Imputer处理这些空值。
定义工作范围后,我想可视化此数据集中存在的年龄分布。
- %matplotlib inline
- import seaborn as sns
- sns.set(color_codes=True)
- plot = sns.distplot(df.Age.dropna())
- plot.figure.set_size_inches(6,6)
注意:pandas具有内置的绘图功能供您使用,但是我是seaborn的忠实拥护者,希望向您展示在可视化数据时您可以选择。
好了,目前看来我们正在使用一个非常干净的数据集。但是,我们仍然不准备将数据传递到机器学习模型中。
编码数据
许多机器学习算法都期望数值输入数据,因此我们需要找出一种以数值方式表示分类数据的方法。
一种解决方案是为每个类别任意分配一个数值,并将数据集从原始类别映射到每个对应的数字。例如,让我们看一下数据集中的“休假”列(您因精神疾病而休病假有多容易?)
- df['leave'].value_counts(dropna=False)
它返回以下值。
- df['leave'] = df['leave'].map({'Very difficult': 0, 'Somewhat difficult': 1, 'Don\'t know': 2, 'Somewhat easy': 3, 'Very easy': 4})
- from sklearn import preprocessing
- label_encoder = preprocessing.LabelEncoder()
- label_encoder.fit(df['leave'])
- label_encoder.transform(df['leave'])
编码标称数据的常见解决方案是单热编码。
而不是将分类值替换为数值(标签编码),如下所示
种类 数值编码
1个 猫 1个
2 狗 2
3 蛇 3
4 猫 1个
5 狗 2
6 龟 4
7 狗 2
相反,我们为每个值创建一列,并使用1和0表示每个值的表达式。这些新列通常称为伪变量。
种类 IS_CAT IS_DOG IS_SNAKE IS_TURTLE
1个 猫 1个 0 0 0
2 狗 0 1个 0 0
3 蛇 0 0 1个 0
4 猫 1个 0 0 0
5 狗 0 1个 0 0
6 龟 0 0 0 1个
7 狗 0 1个 0 0
您可以直接在Pandas中进行单热编码,也可以使用sklearn,尽管sklearn有点麻烦,因为单热编码器仅适用于整数值输入。对于我们的示例(输入是字符串),我们需要首先执行标签编码,然后对数据进行一次热编码。
- # Using Pandas
- import pandas as pd
- pd.get_dummies(features['leave'])
- # Using sklearn
- from sklearn.preprocessing import LabelEncoder, OneHotEncoder
- label_encoder = LabelEncoder()
- ohe = OneHotEncoder(categorical_features = ['leave'])
- label_encoded_data = label_encoder.fit_transform(features['leave'])
- ohe.fit_transform(label_encoded_data.reshape(-1,1))
至此,我们已经成功清理了数据并将其转换为机器学习算法可以轻松使用的形式。但是,在这一点上,我们应该考虑某种数据标准化方法是否对我们的算法有利。这取决于(1)数据,(2)我们要学习的内容以及(3)我们计划实现的算法。如果您决定更改机器学习算法,有时需要重新访问此步骤。
数据
假设您具有一个具有不同单位的要素数据集:以开尔文为单位的温度,相对湿度百分比和一年中的某天。如果此数据集表示罗利的天气值,我们可能会看到每个要素的以下范围。
温度:270 K至305 K
湿度:0-1(即30%的湿度由0.3表示)
一年中的一天:0到365
解释这些值时,您在考虑它们时会直观地标准化这些值。例如,您认识到湿度增加0.5(记住:那是50%)比增加0.5开尔文要剧烈得多。但是,如果我们不对这些功能进行缩放,则我们的算法可能会仅仅因为它的比例最大(可能因此温度值的变化对算法最重要)而学会使用温度作为主要预测指标。特征缩放允许所有特征均等地贡献(或更恰当地说,它允许特征相对于其重要性而不是其比例来贡献)。
我们正在尝试学习的
有时,我们希望能够解释我们的系数,并将标度从实际值(即美元)更改为任意值,这会使我们的结果难以理解。
算法
如果您使用诸如梯度下降之类的工具来优化算法,则功能缩放可在所有维度上一致地更新权重。
以下草图是从Andrew Ng的梯度下降幻灯片中借用的。第一个图像表示两个具有不同比例的特征,而第二个图像则表示标准化的特征空间。梯度下降优化可能会花费很长时间来搜索局部最优值,因为它会沿着轮廓来回移动。规范化的特征空间可实现更快的收敛。
需要特征缩放的ML算法:
逻辑回归
支持向量机
感知器
神经网络
PCA
不需要特征缩放的ML算法:
决策树(和随机森林)
朴素贝叶斯
注意:以上列表绝不是详尽无遗的,仅作为示例。
有几种不同的方法可以将要素缩放到标准化范围。最小-最大缩放器和标准缩放器是最常用的两种方法。
最小-最大缩放将您的最低观测值设置为0,将最高观测值设置为1。
标准缩放将转换数据以具有标准法线变形。
实际上,要执行此操作(功能缩放),您可以在sklearn中使用预构建的库。
- # Feature scaling with StandardScaler
- from sklearn.preprocessing import StandardScaler
- scale_features_std = StandardScaler()
- features_train = scale_features_std.fit_transform(features_train)
- features_test = scale_features_std.transform(features_test)
- # Feature scaling with MinMaxScaler
- from sklearn.preprocessing import MinMaxScaler
- scale_features_mm = MinMaxScaler()
- features_train = scale_features_mm.fit_transform(features_train)
- features_test = scale_features_mm.transform(features_test)
关于此实现的一些注意事项:
实际上,您可以决定仅缩放某些列。例如,您不需要缩放来自一键编码的伪变量。
您将希望针对训练和测试集对功能进行相同的缩放(我在这里领先,请继续关注下一节),因此请确保仅变换测试集,而不重新安装缩放器。
我在上面的代码中提供了两种扩展功能集的方法,请选择一种。
拆分数据以进行培训和测试
为了为机器学习算法准备数据,我们需要做的最后一件事是将数据分为训练和测试子集。请记住,机器学习就是通过演示许多示例来教导计算机执行任务。我们将使用可用的数据教授计算机,但理想情况下,该算法将与新数据一起使用。训练/测试拆分背后的原理是牺牲一些数据进行训练,以尝试根据尚未看到的“新”数据评估算法。这很重要,因为我们要确保我们的算法能够从给定的示例中概括其学习内容。
我曾经听过一个很好的类比,将这个概念与学习测试联系起来。假设我们有两名准备同一项考试的学生-一名学生努力理解将要测试的一般概念,而另一名学生只是简单地记住了相关作业中的每个示例。如果测试中包含的新问题不在家庭作业中,但仍与所讨论的概念有关,那么前一个学生的考试成绩将比记住家庭作业答案的学生好得多。在机器学习中,我们使用过拟合一词来描述算法是否对我们作为示例提供的数据进行了过多读取,或者它是否能够概括我们试图教导的概念。
让我们继续将数据分为两个子集(实际上是四个子集,因为我们已经将要素与标签分离了)。
- from sklearn.model_selection import train_test_split
- features_train, features_test, labels_train, labels_test = train_test_split(features, labels, test_size=0.25, random_state = 0)
验证数据
在构建机器学习模型时,我们经常将数据分为三个子集:训练,验证和测试子集。训练数据用于“教”模型,验证数据用于搜索最佳模型体系结构,而测试数据保留为我们模型的公正评估者。在构建模型时,通常会为我们提供有关模型总体设计的选择;验证数据使我们能够评估多个设计,以寻求最佳设计,但是这样做是为了使模型的设计“适合”此子集。因此,测试数据在确定我们的模型将所学内容推广到新数据中的程度方面仍然很有用。
对于数据有限的机器学习问题,希望最大化用于实际训练模型的数据量。但是,您仍然必须为验证集提供足够的数据以评估模型性能。在这种情况下,K折交叉验证可能会派上用场。
对于K折交叉验证,我们将数据集划分为 ķ垃圾箱。然后我们将运行ķ不同的学习实验,每次选择一个bin作为评估子集,同时使用其余的训练我们的算法。然后,我们可以使用所有数据集(尽管不是同时)来平均所有实验的结果,以准确了解其性能,以训练和测试算法的性能。显然,这种方法将花费更多时间,但是它将对我们的机器学习模型进行更准确的评估。
验证子集中的一致分布
如果您在分布相似的数据上训练和评估数据,则将获得最佳结果。因此,在将数据拆分为子集时,最好进行验证。
多少数据就是足够的数据?
在此阶段要做的另一件有用的事情是绘制学习曲线,以判断您是否收集了足够的数据。这些学习曲线绘制了针对不同数量的训练示例的算法的训练和验证分数。
您将看到您的模型将在某一时刻达到平稳状态,并且开始通过添加其他训练示例而获得的收益递减。这主要是有用的,因为它将告诉您要么需要收集更多数据,要么已经收集了足够的数据,并且您不应该花更多的时间来扩展数据集。对于有监督的学习,收集和标记用于模型的其他数据通常会很昂贵。
注意:这是深度学习大放异彩的地方!深度学习技术通常更有能力继续学习并改善您提供的更多数据。
可以在此处找到sklearn学习曲线实现的示例。
维数的诅咒
最后,我想简要指出一点,简单地抛出机器学习算法中存在的所有信息通常不是最好的主意。这通常称为维数诅咒,它观察到随着我们数据集中特征数量的增长,我们需要准确概括的数据量呈指数增长。