数据预处理的主要目的是为机器学习建模进行数据准备,数据准备的好与坏对模型效果影响比较大。
经常做的一些数据预处理工作有:
> 对缺失值的处理
> 对类别型变量的值进行重新编码
> 把连续型变量进行分箱,然后再按照处理类别型变量的方式重新编码
> 对连续型变量进行标准化和归一化处理
注意事项:
- 以上的预处理工作并不是对所有问题都是必须要做的,不同的算法和问题,对预处理的要求是不一样,如:
> 缺失值对所有算法来说基本上都要处理连续型变量是否要分箱要看具体算法,决策树算法就不需要
>而标准化和归一化在涉及到距离计算的算法中一定要处理,例如 KNN,聚类分析等
常用的三种缺失值处理方法:
- 简单粗暴,直接把有缺失值的整条记录删除。这种方法适合数据样本较大而缺失记录较少的场景,删除缺失记录对整体影响很小。
- 构造一个新变量来标记缺失值:缺失就标记为1,不缺失就标记为0。这种方法认为缺失值本身是一个有意义的信息,不能简单处理掉,必须要标记出来。
- 用一个值替换掉缺失值,具体用什么值来替换这个方法也比较多,例如对数值型变量可以考虑用均值,对类别型变量用频数最大的那个值(众数)。
(1).删除缺失记录 (使用 dropna()方法 ):
# 删除整个数据集中任何一个变量有缺失的记录
dropna = titanic_df.dropna()
dropna.info()
# 对 Age 变量删除缺失值,保存为一个新的变量
Age_dropna = titanic_df[['Age']].dropna()
Age_dropna.info()
删除缺失记录的缺点:
- 删除后只有 183 条记录了,原来可是有891条记录啊,显然这样做不行啊。能不能不要在整个数据集上删除缺失值,而是分析哪个变量就删除该变量的缺失值,当然可以的,你只需要对指定的变量进行dropna()操作就可以了。
(2).构造缺失值标识变量
# 构造一个新的变量 Age_isna,当 Age 是缺失值的时候 Age_isna=1,否则=0
titanic_df['Age_isna']=0
titanic_df.loc[titanic_df['Age'].isnull(), 'Age_isna'] = 1
# 对 Age_isna 进行频数分析,确认 Age_isna 是否构造正确
titanic_df['Age_isna'].value_counts()
(3).替换缺失值
- 以 Age 为例,我们使用均值对它进行替换。先使用mean()计算Age的均值,然后使用fillna()替换掉 Age的缺失值。
## 对年龄缺失值进行均值填充
age_mean = round(titanic_df['Age'].mean())
titanic_df['Age'].fillna(age_mean, inplace=True)
titanic_df.Age.describe()
为什么要对类别变量重新编码?
- 一个最简单的理解就是:机器学习算法要求输入的变量值必须是数值
(1).什么是类别型变量
- 类别型变量 的值一般都是标签,一般都是字符串存储,例如Embarked(登船港口)它的取值是{‘C’, ‘Q’, ‘S’},这就需要把它重新编码为数值。但是能简单的使用1,2,3对C,Q,S重新编码吗?显然不能,因为 C,Q,S 之间并没有数量关系,用1,2,3来编码,算法就会认为S的值是C的值的3倍,这显然是错误的。即使有一些类别变量的标签值用数字来存储,但是同样的道理,我们不能认为它已经是数字了就不需要重新编码,不论类别变量的标签值是字符串还是数字,本质上它只是一个 编号
(2).类别变量重新编码 : 独热编码
什么是独热编码:
- 针对类别型变量,我们必须重新编码,把它们转换为数值型变量,但是信息量还不会丢失。最常用的一种类别变量编码方式 哑变量(Dummy Variables)编码,这是统计界的称呼,机器学习界喜欢叫独热编码(one-hot encoding) 。
- 独热编码就是把 1 个类别型变量转化为 N 个 0/1 标识变量。类别型变量有多少个类别值,转化后的 0/1标识变量就有多少个。例如下图示例的“地区”变量是类别型变量,进行独热编码之后,每一个地区对应一个 0/1 标识变量,用户属于哪个地区,在对应地区的 0/1 标识变量上取值为 1,否则就为 0。一条记录只会在一个变量上取值为 1,其他变量上取值为 0,这就是独热这个词的含义。
import pandas as pd
# 把 Pclass 进行独热编码,保存为新的数据对象,名字叫 Pclass_onehot,prefix 参数设置独热编码后的变量名前缀
Pclass_onehot = pd.get_dummies(titanic_df.Pclass,prefix='Pclass')
# 查看前 5 行
Pclass_onehot.head()
结果:
Pclass_1 | Pclass_2 | Pclass_3 | |
---|---|---|---|
0 | 0 | 0 | 1 |
1 | 1 | 0 | 0 |
2 | 0 | 0 | 1 |
3 | 1 | 0 | 0 |
4 | 0 | 0 | 1 |
说明:
- Pclass 有3个标签值(1,2,3),所以独热编码之后有 3 个新的变量,设置了prefix=’Pclass’,所以三个变量名分别为 Pclass_1, Pclass_2, Pclass_3
(3).连续变量分箱
什么是分箱:
- 分箱,英文叫Binning,这是一个有点专业的叫法,其实就是我们常说的变量分段。例如 年龄本来是连续型变量,现在我们把它分成 5 个段: 0-18岁一个段, 18-25 一个段, 25-40 一个段, 40-60 一个段,60+一个段。这就叫分箱,有时候也叫分桶,还可以叫 变量离散化
分箱的好处:
- 好处一、分箱之后连续型变量就可以变成类别型变量来处理了。变成类别型变量又有什么好处呢?那就是分析起来很方便,直接做频数分析或者直方图分析就可以知道变量的分布情况了。
- 好处二、变量分箱之后能让模型变得更稳健,不太容易过拟合。为什么这么说呢,这个也是容易理解的,例如年龄,没分箱之前,不同的年龄(例如 35 岁和 39岁)对模型的预测结果有一定的影响。但是分箱之后,他们都属于 25-40 这个年龄段,预测结果自然就一样了。你可能会说分箱这样做不是降低了变量的精细度吗?对的,目的就是要降低变量的精细度,这样得到的模型结果可能才会更稳健。
有三种常用的分箱方法:
- 自定义分箱:分箱的边界值自定义
- 等宽分箱: 每一个箱的边界呈等差数列。 例如以 10 为间隔对年龄分箱,那就是按照这
样的区间来分箱: 0-10,10-20,20-30…- 等深分箱: 保证每一个箱内记录数一样或者满足指定的比例。 例如如果要分为 10 个箱,并且每个箱内的记录数要相等(即每个箱内都包含 10%的记录)。有时候等深分箱也不一定要求每一个箱内记录数都相等,例如第 1 个箱占 top10%的记录,第 2 个箱占剩余的 top20%,第 3 个箱占剩余的 top30%…
连续变量分箱 – 自定义分箱
#对 Age 进行自定义分箱
cut_points = [0,18,25,40,60,100]
#定义分箱边界值
titanic_df["Age_bin"] = pd.cut(titanic_df.Age, bins=cut_points)
#分箱后的新变量 Age_bin 继续保存在原始的数据对象 titanic_df 中
# 分箱后的频数分析
titanic_df["Age_bin"].value_counts()
连续变量分箱 – 等宽分箱
变量分箱: 等宽分箱
使用 pandas 的 cut()方法,直接指定等宽分箱的数量,例如 10,代码如下:
# 等宽分箱,分箱数为 10
titanic_df["Age_wbin"] = pd.cut(titanic_df.Age,10)
# 分箱后的频数分析
titanic_df["Age_wbin"].value_counts()
连续变量分箱 – 等深分箱
使用 pandas 的 qcut()方法,直接指定等深分箱的数量,例如 5
# 等深分箱,分箱数为 5
titanic_df["Age_dbin"] = pd.qcut(titanic_df.Age,5)
# 分箱后的频数分析
titanic_df["Age_dbin"].value_counts()
分箱后的变量进行独热编码
# 分箱后得到的变量就变成了类别型变量(这正是我们要分箱的目的),
# 那么就可以用前面学到的独热编码的方法对分箱后的变量进行编码了。
Age_bin_onehot = pd.get_dummies(titanic_df.Age_bin,prefix='Age_bin')
Age_bin_onehot.head()
(4).标准化和归一化
- 标准化和归一化都属于对变量进行无量纲化处理的方法,目的就是为了使不同规格尺度的数据转换到同一规格尺度上。
- 为什么要这么做?
> 无量纲化可以让不属于同一量纲的特征值可以比较:例如身高(cm),体重(kg)
> 无量纲化后模型收敛会加快(运行速度较快)
> 无量纲化对一些模型的结果影响较大,一般都是涉及到距离计算的模型和算法,
> 例如 KNN,聚类分析, SVM 等。
标准化 Standardize
- 标准化要做的事情就是把原始的连续型变量转换为 均值为 0,标准差为 1 的变量
- 我们需要使用 sklearn.preprocessing.Scale()方法来进行标准化
标准化 Standardize (2/3)-方式一
from sklearn import preprocessing
# 使用 Age 变量的数据训练一个标准化转换模型# 特别说明一下:需要使用 titanic_df[['Age']]
# 这样的方式来引用 Age 变量,这样得到的结果是 dataframe
# Age 标准化处理后保存为原数据对象中的新变量 Age_std
titanic_df['Age_std'] = preprocessing.scale(titanic_df[['Age']])
# 查看标准化后的数据
titanic_df['Age_std'].head()
标准化 Standardize (2/3)-方式二
from sklearn import preprocessing
# 实例化一个标准化转换器
stdscaler = preprocessing.StandardScaler()
# 使用Age变量的数据训练一个标准化转换模型
# 特别说明一下:需要使用titanic_df[['Age']]这样的方式来引用Age变量,这样得到的结果是
dataframe,不是series
stdscaler.fit(titanic_df[['Age']])
# Age标准化处理后保存为原数据对象中的新变量Age_std
titanic_df['Age_std'] = stdscaler.transform(titanic_df[['Age']])
# 查看标准化后的数据
titanic_df.head()
归一化(Normalization)
- 归一化就是把原始的连续型变量转换为范围在【a-b】 之间的变量,常见的a=0, b=1,
- 我们需要使用 sklearn.preprocessing.MinMaxScaler()方法来进行归一化
from sklearn import preprocessing
# 实例化一个归一化转换器
minmaxscaler = preprocessing.MinMaxScaler()
# 使用 Age 变量的数据训练一个归一化转换模型
minmaxscaler.fit(titanic_df[['Age']])
# Age 归一化处理后保存为原数据对象中的新变量 Age_normal
titanic_df['Age_normal'] = minmaxscaler.transform(titanic_df[['Age']])
# 查看归一化后的数据
titanic_df.head()
5.数据对象拼接
- 经过了一系列的数据预处理之后,有一些变量是保存在原始的数据对象中,有一些变量可能单独保存为一个数据对象,例如独热编码就是单独的数据对象。为了后续的建模方便,我们需要把这这些分散的dataframe 拼接在一起。可以使pandas.concat()方法来完成这个任务。
# 使用 concat 把 Pclass_onehot 和 titanic_df 两个 dataframe 拼接在一起
titanic_df_merge = pd.concat([titanic_df,Pclass_onehot], axis=1)
titanic_df_merge.head()
6.小结
三种缺失值处理的方法
> 使用 dropna()删除缺失记录
> 构造一个缺失值 0/1 标志变量
> 使用 fillna()方法用一个值对缺失值进行替换对类别型变量处理的方法
> 使用 pandas.get_dummies()方法对类别型变量进行 独热编码三种对连续型变量分箱的方法
> 使用 pandas.cut() 方法,指定分箱边界来进行 自定义分箱
> 使用 pandas.cut() 方法,指定分箱数量来进行 等距分箱
> 使用 pandas.qcunt() 方法,指定分箱数量来进行 等深分箱两种无量纲化的处理方法
> 使用 sklearn.preprocessing.StandardScaler()来进行均值为 0,标准差为 1 的标准化
> 使用 sklearn.preprocessing.MinMaxScaler()来进行[0-1]之间的归一化最后使用了 pandas.concat()对 pandas 的多个 dataframe 进行拼接,合并为一张大的宽表
泰坦尼克案例中,原始数据集train.csv只有12个变量,之前的章节,我们都是基于这个12个变量来进行的探索分析和数据预处理。现在我们要讨论的问题是:能否 基于这12个原始变量构造出来更多的新变量 。答案当然是可以的!
如何基于已有变量构造新的衍生变量的方法,一般来说,有几种常用的新变量构造方法:
- 多个变量之间的数学运算,例如构造一个新变量 z=a/b
- 基于逻辑判断来构造衍生变量,形如 SQL 中的 case when … then …这样的方式
多个特征变量进行组合,例如性别,年龄本来是两个独立的变量,可以把他们组合起来变成新的独热编码变量:男&(0,18],男&(18,25]…
变量构造非常重要,对应用机器学习来说,最重要的工作往往不是选择哪个算法,而是你能否构造出对目标变量有强预测能力的变量,这个工作又被称为特征工程,前面介绍的数据预处理的各种方法,也属于特征工程的范畴,后面我们会有专门的章节来详细讲解特征工程
1.基于多个变量运算构造新变量
在泰坦尼克数据集中,有这样两个变量:
- SibSp:在船兄弟姐妹数/配偶数
- Parch:在船父母数/子女数
- 简单思考一下发现,如果把 SibSp 和 Parch 加起来再加上 1(自己),那么这个新的变量就可以表示家庭大小。我们感觉这个新的变量可能与是否生还有一定相关性,具体有没有,我们可以通过之前学习的数据探索的方法来分析
示例:
# 构造 FamilySize 变量
titanic_df['FamilySize'] = titanic_df['SibSp'] + titanic_df['Parch'] + 1
titanic_df.head(5)
# FamilySize 与 Survived 的交叉表,显示频数
pd.crosstab(titanic_df.Survived,titanic_df.FamilySize,margins=True)
# FamilySize 与 Survived 的交叉表,显示列占比
pd.crosstab(titanic_df.Survived,titanic_df.FamilySize).apply(lambda r:
r/r.sum(), axis=0)
2.基于逻辑判断来构造新变量
例如,我们可以这样来构造一些新变量:
- 构造一个新变量 IsAlone(是否独自一人),规则是:如果 FamilySize=1,那么IsAlone=1,否则 IsAlone=0
- 构造一个新变量IsMother(是否是母亲),规则是:如果性别是女性,父母/子女数大于 0,并且年龄大于 20 岁,那么 IsMother=1,否则 IsMother=0
示例:
# 构造一个新变量 IsAlone(是否独自一人)
titanic_df['IsAlone'] = 0
titanic_df.loc[titanic_df['FamilySize'] == 1, 'IsAlone'] = 1
# 频数统计
titanic_df['IsAlone'].value_counts()
# 构造一个新变量 IsMother(是否是母亲)
titanic_df['IsMother'] = 0
titanic_df.loc[(titanic_df['Sex']=='female') &
(titanic_df['Parch']>0) & (titanic_df['Age']>20),'IsMother'] = 1
# 频数统计
titanic_df['IsMother'].value_counts()
3.多个特征变量进行组合
- 多个特征变量进行组合,例如性别,年龄本来是两个独立的变量,可以把他们组合起来变成新的独热编码变量:男&(0,18],男&(18,25]。。。。 特征组合要求特征都必须是类别型的,所以我们先对年龄变量进行分箱。
示例
# 把 Sex 性别和 Age_bin 特征进行组合
titanic_df['Sex_Age_combo'] = titanic_df['Sex'] + "_" + titanic_df['Age_bin'].astype(str)
titanic_df.head()
# 对 Sex_Age_combo 进行独热编码
Sex_Age_combo_onehot = pd.get_dummies(titanic_df['Sex_Age_combo'], prefix='Sex_Age')
Sex_Age_combo_onehot.head()
- 读取原始数据
- 各种数据预处理
- 构造一张集成好的数据大宽表
- 拆分出训练集和测试集
- 用训练集进行模型训练
- 用户测试集进行模型评估
示例:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
# 拆分训练集和测试集y=f(x)
# trainData系列为训练集, trainData_X:训练集自变量(x), trainData_y:训练集因变量(y)
# 注意:训练集中一定要删除目标值,因为这个值是要训练得出的
trainData_X = TrainData.drop(['Survived'], axis = 1)
# 把原数据集中的结果(可以理解为数据标准)保存到因变量集中,用于将来和训练的结果集对比得正确率
trainData_y = TrainData.Survived
# X_test:代表的是测试集数据(自变量), y_test:代表的是测试集数据(因变量)
X_train, X_test, y_train, y_test = train_test_split(trainData_X, trainData_y, test_size=0.3,
random_state=123456)
# 实例化一个逻辑回归模型(逻辑回归是用于解决分类问题的典型机器学习算法)
lr = LogisticRegression()
# 训练模型(此过程本质上是得到相关数学模型的相关参数,即得到: y=f(x)相关表达式)
lr.fit(X_train, y_train)
# 用模型对测试集进行预测(用之前得到的y=f(x)公式,带入x,求解y)
y_test_pred = lr.predict(X_test)
# 输出测试集上模型评估报告classification_report(即预测试的结果集和实际事先标注的真实结果集进行比较)
print(classification_report(y_test,y_test_pred))
关于模型训练的三点说明:
(1)、数据格式
- 预测模型的训练需要输入数据对象X和y,例如:lr.fit(X_train, y_train)。
- X_train是一个shape为(n_samples, n_features)的2维数组(矩阵),矩阵的列就是
特征变量x,行就是每一条训练样本。- y_train是一个shape为(n_samples,)的一维数组,存放的是目标变量y的值
- _train和y_train的长度必须一致:也就是说(X_train,y_train)必须是成对出现的。另外,sklearn可以接受Pandas的dataframe或者series对象,一般我们都习惯用Pandas来为机器学习建模准备数据。
(2)、数据集拆分
- 我们看到了例子中训练模型用X_train和y_train,评估模型用X_test和y_test。这个train和test就是通过数据集的随机拆分的方法来完成的。
- from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(trainData_X, trainData_y,test_size=0.3, random_state=123456)
test_size=0.3指定了拆分出来的test集数量占比是30%。
(3)、模型评估
- 模型训练出来之后,到底性能如何,这是需要评估的。数据集拆分的目的(拿测试集来评估模型)就是为了让模型评估更加客观,不能既做运动员又做裁判。所以模型评估不能使用训练集,必须在绝对没有参与模型训练的测试集上进行评估。模型评估有很多方法,最常用的指标就是正确率,但是只看一个正确率有时候无法对模型全面评估,所以还会有更多的评估指标,后面会慢慢道来:
> 误分类矩阵
> 准确率,召回率,F1 Score等
> OC曲线,AUC值
> orenz曲线
> KS曲线,KS值
- 对新数据进行同样的数据预处理工作
- 利用训练好的模型对新数据进行预测
- 预测结果保存为文件
示例:
# 不需要 “乘客ID”
Test_X = TestData.drop(['PassengerId'], axis = 1)
# 对测试数据进行预测
pred = lr.predict(Test_X)
# 生成最终提交结果Submission
Submission = pd.DataFrame({'PassengerId':TestData.PassengerId,'Survived':pred})
# 导出为csv文件
Submission.to_csv("Submission.csv", index=False)
# 查看导出文件,读取测试结果数据集
submission_df = pd.read_csv('Submission.csv')
submission_df.head()
- Step1: 数据预处理
> 读取原始数据
> 各种数据预处理
> 构造一张集成好的数据大宽表- Step2: 模型训练和评估
> 拆分出训练集和测试集
> 用训练集进行模型训练
> 用户测试集进行模型评估- Step3: 对新数据做出预测
> 对新数据进行同样的数据预处理工作
> 利用训练好的模型对新数据进行预测
> 预测结果保存为文件
- 数据预处理
- 模型训练和评估
- 模型预测
(1).首先要获得用于训练的数据集
- 常见的数据标注场景有:
> 图像识别:需要对图片的类别进行标注
> 文本情感分析:需要对文本正面和负面情感进行标注
> 信用评分:需要观察足够长的时间,搜集到足够多的坏账用户样本
特别说明:
- 我们现在接触到的训练数据集可能大部分都是批量方式 提供的(就是一个 静态的csv文件 ),但是在 实际工作 中,尤其是 互联网行业,训练数据可能是 实时产生 的,例如广告点击率预估模型,广告从展现到点击这个过程的数据会源源不断的产生。针对批量数据建模就叫 批量学习,针对实时数据建模就叫在线学习,一般这种场景的学习都是只用增量数据来训练模型,所以又叫 增量学习。在线学习对工程能力要求较高,往往需要 大数据工程师 的支持和配合才能很好的完成。
(2).其次要进行数据预处理
在一个机器学习模型开发流程中,数据预处理会占到80%-90%的时间。在上一章的学习中,我们看到了数据预处理要干这些事情:
- 数据清洗:例如缺失值的处理
- 特征构造:从原始数据中加工提取出有用的特征变量
- 特征转换:标准化,归一化,独热编码
- 特征筛选:丢弃掉一些不要的特征变量数据预处理对模型性能影响巨大,它决定了模型性能的上限,选择什么样的算法只不过是无限逼近这个上限而已!
(3).然后进行模型训练(选择机器学习算法)
模型训练 就是把准备好的数据喂给机器学习算法,然后得到一个可以用于对新数据进行预测的模型。
模型训练是一个不断迭代和探索的过程,。实际工作中我们会不断的通过如下这些方法来尝试寻找最好的模型。算法的“好”与“坏”需要进行科学的评估!
选择机器学习算法:
- 不同数据预处理方法
- 选择不同的算法
- 调节算法的参数
- 模型的好坏是通过模型评估来判断。模型评估的方法就是交叉验证:拿一部分数据集来做训练,另一部分数据集来做测试。
模型评估有很多指标:
- 误分类矩阵
- 准确率,召回率,F1 Score等
- ROC曲线,AUC值
- Lorenz曲线
- KS曲线,KS值
(4).模型上线(把模型装载入模型预测引擎)
- 所谓的离线(Offline): 就是指你不是在生产环境中训练模型,你可能是你工作的电脑或者是一台专门用于模型训练的服务器。
- 模型训练: 模型训练是一个不断迭代和探索的过程,直到你对模型评估满意为止。一旦你确认模型OK了,我们经常说的一个术语就叫 模型上线 或者叫 模型部署。
- 模型上线:就把模型装载在模型预测引擎中,当有新数据传入预测引擎的时候,预测引擎就会给出针对这批数据的 预测结果。
- 所以模型预测往往应用的时候是在线的,这就是我们常说的:离线训练,在线预测。
- 当然,如果我们只是研究和学习,模型预测 也可以是 离线 的。
(5).模型应用(使用数据来进行决策)
- 简单来说: 预测模型 的 核心价值 就是 使用数据来做决策 。例如:
> 构建一个信用评分模型,当一个新用户提交资料来申请信用卡的时候,信贷审批系统通过调用信用评分预测模型服务获得对该用户信用度好坏的评分,进而可以 决定是否向它发卡。
> 构建一个 广告点击率预估模型 ,当一个用户访问APP的时候,给用户 呈现点击率预估最高的那个广告。
> 构建一个 文本分类模型,让机器自动的分析给一篇文章打上分类标签,然后把这篇文章 推荐给偏好这个分类的用户
- 预测模型 就是通过输入一系列自变量 X 去预测一个目标变量 y ,它本质是一个 函数拟合 的问题,找到一个能用 X 预测 y 的函数 y=f(x)
- 模型训练的目的就是为了寻找输入变量 (X)与目标变量 (y) 之间的关系,以 线性回归 模型为例:
y=f(X)=w1x1+w2x2+w3x3+…+b- 模型的目标就是找到 f(x) 函数的表达式,即 w1,w2,w3…b 的值,这个问题又称为 函数逼近 的问题,从这个角度来看,它本质是一个 数学问题,也就是我们常说的 函数拟合。
1.分类与回归的概念
- 分类:就是预测数据的类标签,即y的值是类别值,如果y
的值只有2个(0/1)我们称为 二分类,如果y的值有多个,
称为 多分类。很显然,在上一章中介绍的预测泰坦尼克
乘客生还的问题就属于分类问题(二分类问题)。分类模
型我们经常又称为 分类器(Classifier),这个一个更加
形象的叫法。- 回归:对 数值型变量的值进行预测,即y的值是一个数值,例如预测房价,预测商品销售额 都属于回归问题。回归模型又称为 回归器(Regressor)
2.利用分类回归算法实现泰坦拟克生存预测
sklearn 的模型训练函数要求我们把数据集X和y分开准备:即模型训练的 fix(X,y) 方法要求 X和y 是两个参数。
- X 是一个shape为(n_samples, n_features)的 2维数组(你也可以理解为 矩阵),矩阵的列 对应的就是 各个输入变量x,矩阵的行 就是每一条训练样本。
- Y 是一个shape为(n_samples,)的 一维数组,存放的是目标变量y的值。
- X和y的长度必须一致:也就是说(X,y)必须是成对出现的。
- sklearn可以接受X和y是Pandas的dataframe或者series格式,所以我们一般都是通过Pandas来准备数据。
通过下面这两行代码就把X和y数据分离出来了:TrainData中去掉Survived变量,其他的都全部作为X,把Survived作为y
trainData_X = TrainData.drop(['Survived'], axis = 1)
trainData_y = TrainData.Survived
3.模型评估的流程
- 首先需要对 测试集X进行预测,得到 测试集y的预测值
- 基于测试集y的真实值和预测值,可以构造若干种 评估指标和方法来衡量 预测值和真实值 是否比较接近
- 一行代码搞定预测:y_test_pred = lr.predict(X_test)
- 一行代码搞定一个评估方法(当然评估指标有很多,后面会详细介绍):
classification_report(y_test,y_test_pred)
4.lassification_report 评估报告 输出了一些核心评估指标
- precision:准确率。 这个比较容易理解,例如预测了100个人会生还,结果真实生还的人有70个,那么对预测生还(1)这个类来说准确率就是70%。同样针对不生还这个类也有一个准确率,例如预测100个人不会生还,结果真的有82个人没有生还,那么不生还类(0)的准确率就是82%
- recall:召回率。 假如总样本有1000个人,所有生还的人有100个,模型预测生还的人中有72个是正确的,那么召回率就是72%。简单说就是预测正确的数量的覆盖度。
- f1-score:准确率和召回率的一个平衡。 不能简单的说准确率越高越好或者召回率越高越好。为什么?难道不是准确率越高越好吗?是的,还真不一定。举个例子,你非常谨慎,只对最有把握的10个人预测了他们能生还,结果还真的给你蒙对了,准确率100%。很显然,这样的模型其实并没有什么用,因为召回率实在太低了,大部分生还的人都没有预测出来。反过来,你把所有的人都预测能生还,这下好了,召回率达到了100%,但是这样的模型也没有任何意义。
5.再论准确率与召回率
- 对一个分类模型而言,给它一个输入,它就会输出一个标签,这个标签就是它预测的当前输入的类别。假设数据 data1 被模型预测的类别是 Class_A。那么,对于 data1 就有两种可能性:data1本来就是Class_A(预测正确),data1本来不是Class_A(预测错误)。当一个测试集全部被预测完之后,相对于Class_A,会有一些实际是 Class_A 的数据被预测为其他类,也会有一些其实不是Class_A 的,被预测成Class_A,这样的话就导致了下面这个结果:
> 精准率(P)=TP/(TP+FP)=50/(50+10)=5/6=83.33 %
> 召回率(R)=TP/(TP+FN)=50/(50+30)=5/8=62.5 %
- 模型一旦训练完成,要发挥模型的威力就是拿它来对新数据进行预测,进而根据 预测结果 进行 决策 。 模型就有点类似 工厂 先开一个 模具,预测 就像用这个模具源源不断的 铸造出和模具一样的东西。
分类模型 和 回归模型 预测出来的是 结果 不一样的:
- 分类模型 的预测结果是类别标签,有一些算法同时也会给出每一个 类别标签的概率值,有多少个类别就有多少个概率值
- 回归模型 的预测结果就是一个 数值,就不存在概率了。
关于模型预测,注意事项有三:
- 注意模型预测不像模型训练,它不需要输入数据集y,预测的目的就是为了得到y,你自然是无法输入y的。当然你输入X_train也可以的,那就是在训练集上进行预测。
- 有一些算法能提供概率输出,预测概率也是一行代码就能完成:lr.predict_proba(X_test)。需要注意的是,有多少个类别就有多少个对应的概率值输出。
- 另外,预测结果中只包含预测的类别标签或者不同类别的概率值,并不会包含数据集X的信息。你一开始可能会不太习惯,你可以考虑把预测结果拼接到数据集X中去。
示例:
# 对 测试集预测类别值 并打印输出,
# 结果是一维数组。
y_test_pred = lr.predict(Test_X)
y_test_pred
# 对 测试集预测每一个类别的概率值,输出结果
是二维数组。
y_test_pred_prob = lr.predict_proba(Test_X)
# 值显示前10行
y_test_pred_prob[:10]
# 可以提取y_test_pred和y_test_pred_prob通过
DataFrame()拼接起来
# 把预测结果转为dataframe格式,并通过concat()拼
接起来
pred_df =
pd.DataFrame(y_test_pred,columns=['pred'])
pred_prob_df =
pd.DataFrame(y_test_pred_prob,columns=['p_0',
'p_1'])
pd.concat((pred_df,pred_prob_df),axis=1).head()
- 前面介绍的是对原始训练数据拆分出来的测试集进行预测,由于X_train 和 X_test 格式完全一样,所以操作起来非常 简单。如果现在待预测的是一个全新的数据,就会稍微麻烦一点,因为我们通过一系列的数据预处理操作得到了X_train,现在需要对新数据集进行完全相同的预处理操作。
以泰坦尼克数据为例:
- 训练集需要drop掉PassengerId(乘客ID),而测试集(新数据集)需要保留,因为预测结果需要与PassengerId关联在一起。
- 训练集有 Survived这个变量(目标变量),而测试集(新数据集)没有这个变量。
示例:
def prepare_data(path,role):
titanic_df = pd.read_csv(path)
if role == 'train':
# train数据集drop掉:PassengerId,Name,Ticket,Cabin
titanic_df = titanic_df.drop(['PassengerId','Name','Ticket','Cabin'], axis = 1)
else:
# test数据集drop掉Name,Ticket,Cabin
titanic_df = titanic_df.drop(['Name','Ticket','Cabin'], axis = 1)
……..
if role == 'train':
# train数据集保留Survived
data = pd.concat([titanic_df[['Survived','AgeIsMissing','IsAlone','IsMother']],
Pclass,Sex,Embarked,AgeBin,FareBin,FamilySize,SexAgeCombo],axis=1)
else:
# test数据集保留PassengerId
data = pd.concat([titanic_df[['PassengerId','AgeIsMissing','IsAlone','IsMother']],
Pclass,Sex,Embarked,AgeBin,FareBin,FamilySize,SexAgeCombo],axis=1)
return data
# 利用prepare_data()函数对train.csv和test.csv进行同样逻辑的预处理
TrainData = prepare_data(path='./datasets/titanic/train.csv',role='train')
TestData = prepare_data(path='./datasets/titanic/test.csv',role='test')
# drop掉PassengerId剩余变量组合成数据及Test_X
Test_X = TestData.drop(['PassengerId'], axis = 1)
# 对测试数据进行预测
pred = lr.predict(Test_X)
# 把PassengerId和pred通过DataFrame()拼接起来形成最后的提交结果
Submission =
pd.DataFrame({'PassengerId':TestData.PassengerId,'Survived':pred})
Submission
- 机器学习模型有两种形式:参数模型和无参数模型。它们的本质区别是:参数模型假设函数f(x)有特定的形式,例如线性表达式,而无参数模型则没有这个要求。两者各有利弊:
> 参数模型的精度略差但可解释性强。很多情况下,由于限制了f(x)的表达形式,所以参数模型的精确性可能会略差一些,但是好处是简单,可解释性强,例如逻辑回归模型。
> 无参数模型可解释性差但更精确。相反,无参数模型可解释性差一些,但是大多数情况下可能模型会更精确。
参数模型:
- 最简单的参数模型就是线性回归,在回归模型中,假定了f(x) 的形式如下:
y=f(X)=w1x1+w2x2+w3x3+…+b- 在这个模型中,w1,w2,w3…b 这些参数的值是需要利用算法估计出来的,
例如使用梯度下降法,寻找到让损失函数取得最小值的参数。 逻辑回归
模型 也是属于参数模型。
无参数模型:
- 无参数模型中,f(x) 的形式 不是一个简单固定的函数,它的形式和 复杂度 与 算法 和 数据 都有很大关系。无参数模型的一个典型例子就是决策树,它的f(x)的表现形式是一个树形结构的决策规则,可以翻译为if…then.. .这样的规则,下图是一个“是否要接一个offer ” 的决策树模型示意,其他的非参数模型还包含:K近邻,神经网络,支持向量机,随机森林 等算法
- 以逻辑回归模型为例,它的模型输出结果就是一些参数值:特征变量的回归系数和截距,就是前面提到的参数模型的w1,w2,w3…b 的值
> coef_ : 特征变量的回归系数,如果是二分类模型,shape是(1,n_features),如果是多分类模型,shape是(n_classes, n_features)
> intercept_ : 截距,二分类模型shape是(1,),多分类模型shape是(n_classes,)
示例:
#再重新做一次模型训练,目的是为了查看逻辑回归模型的两个结果
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
trainData_X = TrainData.drop(['Survived'], axis = 1)
trainData_y = TrainData.Survived
X_train, X_test, y_train, y_test = train_test_split(trainData_X,
trainData_y, test_size=0.3, random_state=123456)
lr = LogisticRegression()
lr.fit(X_train, y_train)
# 查看逻辑回归模型的两个结果:intercept_就是一个单值,coef_的
shape是(1,40),40就是X_train中的变量个数。
print(lr.intercept_ )
print(lr.coef_)
# 查看coef_的shape
print("coef_的shape:",lr.coef_.shape)
# 查看X_train的列数
print("X_train的列数:",len(X_train.columns))
# 为了方便查看,我们把X_train中的变量名与它们的系数值拼接在一起
import numpy as np
# 把变量名称和系数对应起来组合成dataframe
coef = pd.DataFrame(list(zip(X_train.columns, np.transpose(lr.coef_))),
columns=['columns', 'coef'])
# 按照coef的值从高到底排序
coef = coef.sort_values(by =['coef'], ascending=False)
coef
- 通过模型训练之后,得到 coef_ (特征变量的回归系数)与intercept_
(截距)后,你就可以拟合出:
y=f(X)=w1x1+w2x2+w3x3+..+.wn*xn+…+b- 得到了这个函数后,以后再有了对应的自变量(x1,x2…xn…)的数据,我们自然就可以求出 因变量 f(x),即y值,这其实就是本质上模型训练 及模型预测 背后对应的原理与实现