第三章 模型搭建和评估–建模
经过前面的两章的知识点的学习,我可以对数数据的本身进行处理,比如数据本身的增删查补,还可以做必要的清洗工作。那么下面我们就要开始使用我们前面处理好的数据了。这一章我们要做的就是使用数据,我们做数据分析的目的也就是,运用我们的数据以及结合我的业务来得到某些我们需要知道的结果。那么分析的第一步就是建模,搭建一个预测模型或者其他模型;我们从这个模型的到结果之后,我们要分析我的模型是不是足够的可靠,那我就需要评估这个模型。今天我们学习建模,下一节我们学习评估。
我们拥有的泰坦尼克号的数据集,那么我们这次的目的就是,完成泰坦尼克号存活预测这个任务。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from IPython.display import Image
import sklearn as sk
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
plt.rcParams['figure.figsize'] = (10,6) #设置输出图片大小
【思考】这些库的作用是什么呢?
numpy是科学计算库用于科学计算用
pandas属于文件处理分析库,同时panda底层基于numpy,所以可以兼具计算。
matplotlib和seaborn库都属于绘图库
IPython中的Image为图形展示库
sklearn为机器学习库,其中包含了许多的机器学习算法实现。
载入我们提供清洗之后的数据(clear_data.csv),大家也将原始数据载入(train.csv),说说他们有什么不同
data = pd.read_csv('test_fin.csv')
data.head(5)
train_data = pd.read_csv('train.csv')
train_data.head(5)
相比于train.csv中的数据,test_clear.csv中的数据少了Cabin因为在数据处理的过程我,我个人认为这个特征的缺失值过多,所以直接舍去了,同时对Age数据中的缺失值进行了填充。
模型搭建
- 处理完前面的数据我们就得到建模数据,下一步是选择合适模型
- 在进行模型选择之前我们需要先知道数据集最终是进行监督学习还是无监督学习
- 模型的选择一方面是通过我们的任务来决定的。
- 除了根据我们任务来选择模型外,还可以根据数据样本量以及特征的稀疏性来决定
- 刚开始我们总是先尝试使用一个基本的模型来作为其baseline,进而再训练其他模型做对比,最终选择泛化能力或性能比较好的模型
这里我的建模,并不是从零开始,自己一个人完成完成所有代码的编译。我们这里使用一个机器学习最常用的一个库(sklearn)来完成我们的模型的搭建
下面给出sklearn的算法选择路径
【思考】数据集哪些差异会导致模型在拟合数据是发生变化
主要是数据的不均衡差异,在数据拟合过程树模型会不断的向数据量占比大的一方倾斜,导致模型失真。
任务一:切割训练集和测试集
这里使用留出法划分数据集
- 将数据集分为自变量和因变量
- 按比例切割训练集和测试集(一般测试集的比例有30%、25%、20%、15%和10%)
- 使用分层抽样
- 设置随机种子以便结果能复现
【思考】
- 划分数据集的方法有哪些?
- 为什么使用分层抽样,这样的好处有什么?
#将数据划分为自变量和因变量,其中因变量为Survived,其余的为自变量
X = data
y = train_data.Survived
对数据进行分割,其中使用sklearn中的train_test_split进行数据分割
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,stratify=y,random_state=0)
print(X_train.shape,X_test.shape)
思考:什么情况下数据集分割不用随机选取?
train_test_split用法如下:
X_train,X_test, y_train, y_test =sklearn.model_selection.train_test_split(train_data,train_target,test_size=0.4, random_state=0,stratify=y_train)
train_data:所要划分的样本特征集
train_target:所要划分的样本结果
test_size:样本占比,如果是整数的话就是样本的数量
random_state:是随机数的种子。
随机数种子:其实就是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。比如你每次都填1,其他参数一样的情况下你得到的随机数组是一样的。但填0或不填,每次都会不一样。
stratify是为了保持split前类的分布。
所以当需要重复试验是,要保证得到一组一样的随机数时不用随机选取。
任务二:模型创建
- 创建基于线性模型的分类模型(逻辑回归)
- 创建基于树的分类模型(决策树、随机森林)
- 分别使用这些模型进行训练,分别的到训练集和测试集的得分
- 查看模型的参数,并更改参数值,观察模型变化
提示2
- 逻辑回归不是回归模型而是分类模型,不要与
LinearRegression
混淆 - 随机森林其实是决策树集成为了降低决策树过拟合的情况
- 线性模型所在的模块为
sklearn.linear_model
- 树模型所在的模块为
sklearn.ensemble
#线性模型在sklearn中的linear_model中
from sklearn.linear_model import LogisticRegression
#加载随机森林分类器
from sklearn.ensemble import RandomForestClassifier
#实例化线性模型为lr
LR = LogisticRegression()
#输入特征和标签进行训练
LR.fit(X_train,y_train)
print(LR.score(X_train,y_train))
print(LR.score(X_test,y_test))
LR2 = LogisticRegression(C=50)
LR2.fit(X_train,y_train)
print(LR2.score(X_train,y_train))
print(LR2.score(X_test,y_test))
使用随机森林进行分类
RFC = RandomForestClassifier()
RFC.fit(X_train,y_train)
print('train score: {}'.format(RFC.score(X_train,y_train)))
print('test score: {}'.format(RFC.score(X_test,y_test)))
修改模型中的参数
RFC2 = RandomForestClassifier(n_estimators=50,max_depth=5)
RFC2.fit(X_train,y_train)
print('train score: {}'.format(RFC2.score(X_train,y_train)))
print('test score: {}'.format(RFC2.score(X_test,y_test)))
【思考】
- 为什么线性模型可以进行分类任务,背后是怎么的数学关系
- 对于多分类问题,线性模型是怎么进行分类的
线性模型通过计算出对应的值之后将值传入sigmoid函数中进行计算。sigmoid函数如下:
f
(
x
)
=
1
1
+
e
−
x
f(x) = \frac{1}{1 + e^{-x}}
f(x)=1+e−x1
代码实现如下:
def sigmoid(x):
return 1/(1+np.exp(-x))
x = np.arange(-10,10,0.1)
y = sigmoid(x)
plt.plot(x,y)
plt.show()
我们看到无论x的值多大,计算出来的值始终在0和1之间,所以我们可以以0.5为一个临界,大于0.5的为一类,小于0.5的为另一类。这样线性模型就实现了分类的功能。
考虑任意多类(不仅是两类)的分类问题。Exponential model 的形式是这样的:
假设第i个特征对第k类的贡献是
w
k
i
w_{ki}
wki,则数据点
(
x
1
,
.
.
.
x
n
)
(x_1,...x_n)
(x1,...xn)属于第k类的概率正比于
e
x
p
(
w
k
1
x
1
+
.
.
.
+
w
k
n
x
n
)
exp(w_{k1}x_1+...+w_{kn}x_n)
exp(wk1x1+...+wknxn)。(省略bias)因为一个数据点属于各类的概率之和为1,所以可以得到
P
(
y
=
k
)
=
e
x
p
(
∑
i
=
1
n
w
k
i
x
i
)
∑
k
‘
e
x
p
(
∑
i
=
1
n
w
k
’
i
x
i
)
P(y=k) = \frac{exp(\sum_{i=1}^{n}w_{ki}x_i)}{\sum_{k^{‘}}exp(\sum_{i=1}^{n}w_{k^{’}i}x_i)}
P(y=k)=∑k‘exp(∑i=1nwk’ixi)exp(∑i=1nwkixi)
任务三:输出模型预测结果
- 输出模型预测分类标签
- 输出不同分类标签的预测概率
提示3
- 一般监督模型在sklearn里面有个
predict
能输出预测标签,predict_proba
则可以输出标签概率
#预测标签
pred = LR.predict(X_train)
#查看预测标签
pred[:10]
# 预测标签概率
pred_proba = LR.predict_proba(X_train)
pred_proba[:10]
【思考】
- 预测标签的概率对我们有什么帮助
从预测标签概率中我们可以将分类概率较低的挑选出来,因为及时该样本被分类出来了,但是由于概率较低,所以相对来说该样本的可信度较低。
模型搭建和评估-评估
根据之前的模型的建模,我们知道如何运用sklearn这个库来完成建模,以及我们知道了的数据集的划分等等操作。那么一个模型我们怎么知道它好不好用呢?以至于我们能不能放心的使用模型给我的结果呢?那么今天的学习的评估,就会很有帮助。
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from IPython.display import Image
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.rcParams['figure.figsize'] = (10, 6) # 设置输出图片大小
任务:加载数据并分割测试集和训练集
from sklearn.model_selection import train_test_split
clear_data = pd.read_csv('clear_data.csv')
train_data = pd.read_csv('train.csv')
X = _clear_data
y = train_data['Survived']
#分割数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=0)
#使用随机森林进行分类
RFC = RandomForestClassifier()
RFC.fit(X_train,y_train)
模型评估
- 模型评估是为了知道模型的泛化能力。
- 交叉验证(cross-validation)是一种评估泛化性能的统计学方法,它比单次划分训练集和测试集的方法更加稳定、全面。
- 在交叉验证中,数据被多次划分,并且需要训练多个模型。
- 最常用的交叉验证是 k 折交叉验证(k-fold cross-validation),其中 k 是由用户指定的数字,通常取 5 或 10。
- 准确率(precision)度量的是被预测为正例的样本中有多少是真正的正例
- 召回率(recall)度量的是正类样本中有多少被预测为正类
- f-分数是准确率与召回率的调和平均
【思考】:将上面的概念进一步的理解,大家可以做一下总结
模型评估就是评估模型在非训练集上的识别效果,荡魔心在训练集上训练出来的效果很好,但是用一组新的样本来评估模型时,模型的表现能力不足。这说明该模型的泛化能力不足。或者说该模型方差高,也叫做过拟合。
交叉验证:交叉验证是通过将训练集平均分为n分其中的一份作为验证集,验证集的作用是在模型训练的过程中去不断的对模型进行评估,而不是想测试机那样需要模型训练完成以后在进行评估,通过交叉验证的验证集是交替变换的,这样能保证这个数据集中的数据都能够被有效的利用到。
准确率:指的是你预测的10个正的样品中,它真正标签为正的数量占比
召回率:指的是你预测的10个正的样品中,它真正标签为正的数量占所以标签为正的数量之比。
f-分数: F − s c o r e = ( 1 + β 2 ) ∗ 精 确 率 ∗ 召 回 率 β 2 ∗ 精 确 率 + 召 回 率 F-score = (1+\beta^2)*\frac{精确率*召回率}{\beta^2*精确率+召回率} F−score=(1+β2)∗β2∗精确率+召回率精确率∗召回率
任务一:交叉验证
- 用10折交叉验证来评估之前的逻辑回归模型
- 计算交叉验证精度的平均值
提示4
- 交叉验证在sklearn中的模块为
sklearn.model_selection
from sklearn.model_selection import cross_val_score
RFC = RandomForestClassifier()
scores = cross_val_score(RFC,X_train,y_train,cv=10)
scores
# 平均交叉验证分数
print("Average cross-validation score: {:.2f}".format(scores.mean()))
思考
- k折越多的情况下会带来什么样的影响?
k折越多带来的评估时间越长,对电脑的性能有一定的影响。
任务二:混淆矩阵
- 计算二分类问题的混淆矩阵
- 计算精确率、召回率以及f-分数
【思考】什么是二分类问题的混淆矩阵,理解这个概念,知道它主要是运算到什么任务中的
混淆矩阵是一个二维方阵,包含四个情况:
- 真实值是positive,模型认为是positive的数量(True Positive=TP)
- 真实值是positive,模型认为是negative的数量(False Negative=FN):这就是统计学上的第二类错误(Type II Error)
- 真实值是negative,模型认为是positive的数量(False Positive=FP):这就是统计学上的第一类错误(Type I Error)
- 真实值是negative,模型认为是negative的数量(True Negative=TN)
准 确 率 = T P + T N T P + T N + F P + F N 准确率 = \frac{TP + TN}{TP + TN + FP + FN} 准确率=TP+TN+FP+FNTP+TN
精 确 度 = T P T P + F P 精确度 = \frac{TP}{TP + FP} 精确度=TP+FPTP
召 回 率 = T P T P + F N 召回率 = \frac{TP}{TP + FN} 召回率=TP+FNTP
F − s c o r e = ( 1 + β 2 ) ∗ 精 确 率 ∗ 召 回 率 β 2 ∗ 精 确 率 + 召 回 率 F-score = (1+\beta^2)*\frac{精确率*召回率}{\beta^2*精确率+召回率} F−score=(1+β2)∗β2∗精确率+召回率精确率∗召回率
提示5
- 混淆矩阵的方法在sklearn中的
sklearn.metrics
模块 - 混淆矩阵需要输入真实标签和预测标签
- 精确率、召回率以及f-分数可使用
classification_report
模块
from sklearn.metrics import confusion_matrix
#训练模型
lr = LogisticRegression(C=50)
lr.fit(X_train, y_train)
pred = lr.predict(X_train)
#混淆矩阵
confusion_matrix(y_train, pred)
from sklearn.metrics import classification_report
# 精确率、召回率以及f1-score
print(classification_report(y_train, pred))
【思考】
- 如果自己实现混淆矩阵的时候该注意什么问题
主要是保证矩阵中的位置正确。
任务三:ROC曲线
- 绘制ROC曲线
【思考】什么是ROC曲线,ROC曲线的存在是为了解决什么问题?
分类模型(例如"逻辑回归”)的结果是一个大于 0 且小于 1 的概率,此时我们还需要一个阈值,才能界定是否患病,通常我们把阈值设为 0.5,这样当结果大于 0.5 时可判定为患病,否则判定为未患病。
而阈值可以取 0 到 1 之间的任意一个值,对每一个阈值,都有一个混淆矩阵与之对应,有了混淆矩阵,我们就可以求出一对 Sensitivity 和 Specificity,通过这两个数,我们就可以在一个以 1-Specificity 为横坐标,Sensitivity 为纵坐标的坐标系上画一个点,把所有可能的阈值所产出的点连起来,就是 ROC 曲线。
提示6
- ROC曲线在sklearn中的模块为
sklearn.metrics
- ROC曲线下面所包围的面积越大越好
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_test, lr.decision_function(X_test))
plt.plot(fpr, tpr, label="ROC Curve")
plt.xlabel("FPR")
plt.ylabel("TPR (recall)")
# 找到最接近于0的阈值
close_zero = np.argmin(np.abs(thresholds))
plt.plot(fpr[close_zero], tpr[close_zero], 'o', markersize=10, label="threshold zero", fillstyle="none", c='k', mew=2)
plt.legend(loc=4)
思考6
- 对于多分类问题如何绘制ROC曲线
对于多分类问题,ROC曲线的获取主要有两种方法:
假设测试样本个数为m,类别个数为n。在训练完成后,计算出每个测试样本的在各类别下的概率或置信度,得到一个[m, n]形状的矩阵P,每一行表示一个测试样本在各类别下概率值(按类别标签排序)。相应地,将每个测试样本的标签转换为类似二进制的形式,每个位置用来标记是否属于对应的类别(也按标签排序,这样才和前面对应),由此也可以获得一个[m, n]的标签矩阵L。
①方法一:每种类别下,都可以得到m个测试样本为该类别的概率(矩阵P中的列)。所以,根据概率矩阵P和标签矩阵L中对应的每一列,可以计算出各个阈值下的假正例率(FPR)和真正例率(TPR),从而绘制出一条ROC曲线。这样总共可以绘制出n条ROC曲线。最后对n条ROC曲线取平均,即可得到最终的ROC曲线。
②方法二:
首先,对于一个测试样本:1)标签只由0和1组成,1的位置表明了它的类别(可对应二分类问题中的‘’正’’),0就表示其他类别(‘’负‘’);2)要是分类器对该测试样本分类正确,则该样本标签中1对应的位置在概率矩阵P中的值是大于0对应的位置的概率值的。基于这两点,将标签矩阵L和概率矩阵P分别按行展开,转置后形成两列,这就得到了一个二分类的结果。所以,此方法经过计算后可以直接得到最终的ROC曲线。
【思考】你能从这条ROC曲线的到什么信息?这些信息可以做什么?
从这条ROC曲线可以看出曲线下方的面积较大,该线几乎位于y=x直线的上方。这些信息可以判定一个模型的好坏。