08-07 细分构建机器学习应用程序的流程-测试模型
更新、更全的《机器学习》的更新网站,更有python、go、数据结构与算法、爬虫、人工智能教学等着你:https://www.cnblogs.com/nickchen121/p/11686958.html
细分构建机器学习应用程序的流程-测试模型
对于分类问题,我们可能会使用k近邻算法、决策树、逻辑回归、朴素贝叶斯法、支持向量机、随机森林;对于回归问题,我们可能会使用线性回归、决策树、随机森林。在工业上,我们不可能会对客户说,这是我训练的几个模型,你想用哪个我就给你哪个。一般而言这是不可能的,通常对于这几个模型,我们会通过某种度量模型的工具,选择一个最优的模型推给客户。
在训练模型那一章节,对于每个模型我们都使用了模型自带的score()方法对模型的性能进行了一个度量,但是score()方法对于分类模型,只是简单的度量了模型的性能;对于回归模型,score()方法只是计算了R2报告分数。这样的度量是很片面的,通常我们会使用sklearn.metics和sklearn.model_selection库下的模块对度量模型性能。
一、1.1 metrics评估指标
模块提供了各种评估指标,并且用户可以自定义评估指标,对于metrics评估指标,主要分为以下两种类型:
* 以_score结尾的为模型得分,一般情况越大越好
* 以_error或_loss结尾的为模型的偏差,一般情况越小越好
接下来我们将通过分类模型、回归模型来详细讲解metrics评估指标。
二、1.2 测试回归模型
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from sklearn import datasets
%matplotlib inline
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
/Applications/anaconda3/lib/python3.7/importlib/_bootstrap.py:219: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
return f(*args, **kwds)
/Applications/anaconda3/lib/python3.7/importlib/_bootstrap.py:219: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
return f(*args, **kwds)
回归模型常用的metrics评估指标有:r2_score、explained_variance_score等
* explained_variance_score(y_true, y_pred, sample_weight=None, multioutput='uniform_average'):回归方差(反应自变量与因变量之间的相关程度)
* mean_absolute_error(y_true, y_pred, sample_weight=None, multioutput='uniform_average'):平均绝对值误差
* mean_squared_error(y_true, y_pred, sample_weight=None, multioutput='uniform_average'):均方差
* median_absolute_error(y_true, y_pred):中值绝对误差
* r2_score(y_true, y_pred, sample_weight=None, multioutput='uniform_average'):R平方值
2.1 1.2.1 r2_socre
r2_score即报告决定系数(R2)(R2) ,可以理解成MSE的标准版,R2R2 的公式为
其中μ(y)μ(y) 是yy 的平均值,即1n∑ni=1(y(i)−μ(y))21n∑i=1n(y(i)−μ(y))2 为yy 的方差,公式可以写成
R2R2 的取值范围在0−10−1 之间,如果R2=1R2=1 ,则均方误差MSE=0MSE=0 ,即模型完美的拟合数据。
# 报告决定系数得分
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
boston = datasets.load_boston()
X = boston.data
y = boston.target
lr = LinearRegression()
lr.fit(X, y)
lr_predict = lr.predict(X)
lr_r2 = r2_score(y, lr_predict)
print('报告决定系数:{:.2f}'.format(lr_r2))
报告决定系数:0.74
2.2 1.2.1 explained_variance_score
# 解释方差示例
from sklearn.linear_model import LinearRegression
from sklearn.metrics import explained_variance_score
boston = datasets.load_boston()
X = boston.data
y = boston.target
lr = LinearRegression()
lr.fit(X, y)
lr_predict = lr.predict(X)
ex_var = explained_variance_score(y, lr_predict)
print('解释方差:{:.2f}'.format(ex_var))
解释方差:0.74
三、1.3 测试分类模型
回归模型常用的metrics评估指标有:accuracy_socre、precision_score、recall_score、f1_score等
* accuracy_score(y_true,y_pre): 精度
* auc(x, y, reorder=False): ROC曲线下的面积;较大的AUC代表了较好的performance。
* average_precision_score(y_true, y_score, average='macro', sample_weight=None):根据预测得分计算平均精度(AP)
* brier_score_loss(y_true, y_prob, sample_weight=None, pos_label=None):越小的brier_score,模型效果越好
* confusion_matrix(y_true, y_pred, labels=None, sample_weight=None):通过计算混淆矩阵来评估分类的准确性 返回混淆矩阵
* f1_score(y_true, y_pred, labels=None, pos_label=1, average='binary', sample_weight=None): F1值
* log_loss(y_true, y_pred, eps=1e-15, normalize=True, sample_weight=None, labels=None):对数损耗,又称逻辑损耗或交叉熵损耗
* precision_score(y_true, y_pred, labels=None, pos_label=1, average='binary',):查准率或者精度; precision(查准率)=TP/(TP+FP)
* recall_score(y_true, y_pred, labels=None, pos_label=1, average='binary', sample_weight=None):查全率 ;recall(查全率)=TP/(TP+FN)
* roc_auc_score(y_true, y_score, average='macro', sample_weight=None):计算ROC曲线下的面积就是AUC的值,the larger the better
* roc_curve(y_true, y_score, pos_label=None, sample_weight=None, drop_intermediate=True);计算ROC曲线的横纵坐标值,TPR,FPR
二分类问题中根据样例的真实类别和模型预测类别的组合划分为真正例(true positive)、假正例(false positive)、真反例(true negative)、假反例(false negative)四种情形,令TP、FP、TN、FN分别表示对应的样例数,样例总数=TP+FP+TN+FN样例总数=TP+FP+TN+FN 。
- TP——将正类预测为正类数
- FP——将负类预测为正类数
- TN——将负类预测为负类数
- FN——将正类预测为负类数
误差矩阵 | - | - | - |
---|---|---|---|
- | - | 真实值 | 真实值 |
- | - | 1 | 0 |
预测值 | 1 | True Positive(TP) | False Positive(FP) |
预测值 | 0 | True Negative(TN) | False Negative(FN) |
3.1 1.3.1 准确度
准确度(accuracy_socre)定义为
# 查准率示例
from sklearn import datasets
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target
lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)
y_pred = lr.predict(X)
print('准确度:{:.2f}'.format(
accuracy_score(y, y_pred)))
准确度:0.97
3.2 1.3.2 查准率
查准率(precision_score)定义为
# 查准率示例
from sklearn import datasets
from sklearn.metrics import precision_score
from sklearn.linear_model import LogisticRegression
iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target
lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)
y_pred = lr.predict(X)
print('查准率:{:.2f}'.format(
precision_score(y, y_pred, average='weighted')))
查准率:0.97
3.3 1.3.3 查全率
查全率(recall_score等)定义为
# 查全率示例
from sklearn.metrics import recall_score
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target
lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)
y_pred = lr.predict(X)
print('查全率:{:.2f}'.format(recall_score(y, y_pred, average='weighted')))
查全率:0.97
3.4 1.3.4 F1值
通常情况下通过查准率和查全率度量模型的好坏,但是查准率和查全率是一对矛盾的度量工具,查准率高的时候查全率低;查全率高的时候查准率低,因此工业上对不不同的问题对查准率和查全率的侧重点会有所不同。
例如癌症的预测中,正类是健康,反类是患有癌症。较高的查准率可能会导致健康的人被告知患有癌症;较高的查全率可能会导致患有癌症的患者会被告知健康。
F1F1 值(f1_score等)定义为
FβFβ 定义为:
FβFβ 是在F1F1 值的基础上加权得到的,它可以更好的权衡查准率和查全率。
- 当β<1β<1 时,PP 的权重减小,即RR 查准率更重要
- 当β=1β=1 时,Fβ=F1Fβ=F1
- 当β>1β>1 时,PP 的权重增大,即PP 查全率更重要
# F1值示例
from sklearn import datasets
from sklearn.metrics import f1_score
from sklearn.linear_model import LogisticRegression
iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target
lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)
y_pred = lr.predict(X)
print('F1值:{:.2f}'.format(f1_score(y, y_pred, average='weighted')))
F1值:0.97
3.5 1.3.5 ROC曲线
ROC(receiver operating characteristic,ROC)曲线也可以度量模型性能的好坏,ROC曲线顾名思义是一条曲线,它的横轴是假正例率(false positive rate,FPR),纵轴是真正例率(true positive rate,TPR),假正例率和真正例率分别定义为:
# ROC示例
from sklearn import datasets
from sklearn.metrics import roc_curve
from sklearn.linear_model import LogisticRegression
iris_data = datasets.load_iris()
X = iris_data.data[0:100, :]
y = iris_data.target[0:100]
lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)
y_pred = lr.predict(X)
fpr, tpr, thresholds = roc_curve(y, y_pred)
plt.xlabel('FPR', fontsize=15)
plt.ylabel('TPR', fontsize=15)
plt.title('FPR-TPR', fontsize=20)
plt.plot(fpr, tpr)
plt.show()
3.6 1.3.6 AUC面积
由于ROC曲线有时候无法精准度量模型的好坏,因此会使用ROC曲线关于横纵轴围成的面积称为AUC(area under ROC curve,AUC)来度量模型的好坏,AUC值越大的模型,则模型越优。
# AUC示例
from sklearn import datasets
from sklearn.metrics import roc_auc_score
from sklearn.linear_model import LogisticRegression
iris_data = datasets.load_iris()
X = iris_data.data[0:100, :]
y = iris_data.target[0:100]
lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)
y_pred = lr.predict(X)
# 计算AUC值
print('AUC值:{:.2f}'.format(roc_auc_score(y, y_pred, average='weighted')))
AUC值:1.00
四、1.4 欠拟合和过拟合
第二部分讲解线性回归时讲到,00 误差的模型也许并不是最好的,因为模型是通过训练集得到的,由于训练集可能存在噪声,因此训练集并不一定能代表测试集,更不一定能代表未来新数据。虽然这样的模型可能很好的拟合训练数据,但是对未来数据可能并没有较好的拟合能力,这种现象成为过拟合。
# 过拟合图例
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
%matplotlib inline
# 自定义数据并处理数据
data_frame = {'x': [2, 1.5, 3, 3.2, 4.22, 5.2, 6, 6.7],
'y': [0.5, 3.5, 5.5, 5.2, 5.5, 5.7, 5.5, 6.25]}
df = pd.DataFrame(data_frame)
X, y = df.iloc[:, 0].values.reshape(-1, 1), df.iloc[:, 1].values.reshape(-1, 1)
# 线性回归
lr = LinearRegression()
lr.fit(X, y)
def poly_lr(degree):
"""多项式回归"""
poly = PolynomialFeatures(degree=degree)
X_poly = poly.fit_transform(X)
lr_poly = LinearRegression()
lr_poly.fit(X_poly, y)
y_pred_poly = lr_poly.predict(X_poly)
<span class="hljs-keyword">return</span> y_pred_poly
def plot_lr():
"""对线性回归生成的图线画图"""
plt.scatter(X, y, c='k', edgecolors='white', s=50)
plt.plot(X, lr.predict(X), color='r', label='lr')
# 噪声
plt.scatter(2, 0.5, c='r')
plt.text(2, 0.5, s='\((2,0.5)\)')
plt.xlim(<span class="hljs-number">0</span>, <span class="hljs-number">7</span>)
plt.ylim(<span class="hljs-number">0</span>, <span class="hljs-number">8</span>)
plt.xlabel(<span class="hljs-string">'x'</span>)
plt.ylabel(<span class="hljs-string">'y'</span>)
plt.legend()
def plot_poly(degree, color):
"""对多项式回归生成的图线画图"""
plt.scatter(X, y, c='k', edgecolors='white', s=50)
plt.plot(X, poly_lr(degree), color=color, label='m={}'.format(degree))
# 噪声
plt.scatter(2, 0.5, c='r')
plt.text(2, 0.5, s='\((2,0.5)\)')
plt.xlim(<span class="hljs-number">0</span>, <span class="hljs-number">7</span>)
plt.ylim(<span class="hljs-number">0</span>, <span class="hljs-number">8</span>)
plt.xlabel(<span class="hljs-string">'x'</span>)
plt.ylabel(<span class="hljs-string">'y'</span>)
plt.legend()
def run():
plt.figure()
plt.subplot(231)
plt.title('图1(线性回归)', fontproperties=font, color='r', fontsize=12)
plot_lr()
plt.subplot(232)
plt.title('图2(一阶多项式回归)', fontproperties=font, color='r', fontsize=12)
plot_poly(1, 'orange')
plt.subplot(233)
plt.title('图3(三阶多项式回归)', fontproperties=font, color='r', fontsize=12)
plot_poly(3, 'gold')
plt.subplot(234)
plt.title('图4(五阶多项式回归)', fontproperties=font, color='r', fontsize=12)
plot_poly(5, 'green')
plt.subplot(235)
plt.title('图5(七阶多项式回归)', fontproperties=font, color='r', fontsize=12)
plot_poly(7, 'blue')
plt.subplot(236)
plt.title('图6(十阶多项式回归)', fontproperties=font, color='r', fontsize=12)
plot_poly(10, 'violet')
plt.show()
run()
如上图所示每张图都有相同分布的8个样本点,红点明显是一个噪声点,接下来将讲解上述8张图。暂时不用太关心线性回归和多项式回归是什么,这两个以后你都会学习到,此处引用只是为了方便举例。
- 图1:线性回归拟合样本点,可以发现样本点距离拟合曲线很远,这个时候一般称作欠拟合(underfitting)
- 图2:一阶多项式回归拟合样本点,等同于线性回归
- 图3:三阶多项式回归拟合样本点,表现还不错
- 图4:五阶多项式回归拟合样本点,明显过拟合
- 图5:七阶多项式回归拟合样本点,已经拟合了所有的样本点,毋庸置疑的过拟合
- 图7:十阶多项式回归拟合样本点,拟合样本点的曲线和七阶多项式已经没有了区别,可以想象十阶之后的曲线也类似于七阶多项式的拟合曲线
从上图可以看出,过拟合模型将会变得复杂,对于线性回归而言,它可能需要更高阶的多项式去拟合样本点,对于其他机器学习算法,也是如此。这个时候你也可以想象,过拟合虽然对拟合的样本点的误差接近0,但是对于未来新数据而言,如果新数据的x=2x=2 ,如果使用过拟合的曲线进行拟合新数据,那么会给出y=0.5y=0.5 的预测值,也就是说把噪声的值给了新数据,这样明显是不合理的。
4.1 4.9.4 交叉验证
对训练数据集切割做交叉验证也是防止模型过拟合的一个很好的方法。
一般会把数据按照某种比例分为训练集、测试集。训练集用来训练模型,把测试集当做未来新样本的样本集用来评估模型。然后交叉验证可以认为就是不断地重复训练模型、测试模型。如果数据量较大的话,会把训练集按照某种比例分成训练集、验证集、测试集,使用训练集训练参数;使用验证集训练超参数;使用测试集测试模型性能。
4.1.1 4.9.4.1 简单交叉验证
把数据集按照某种比例,将数据集中的数据随机的分为训练集和测试集。然后不断的改变模型参数训练出一组模型,每训练完一个模型就用测试集测试,最后得到性能最好的模型。
- 初始值c=1c=1
- 训练模型
- 测试模型,c+1c+1
- 如果c<11c<11 改变模型参数,跳转到步骤1;反之,停止训练
- 从模型集{c1,c2,⋯,c10}{c1,c2,⋯,c10} 中选择性能最优的模型
# 简单交叉验证
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
# 导入鸢尾花数据
iris_data = datasets.load_iris()
X = iris_data.data[:, [0, 1]]
y = iris_data.target
# random_state=1可以确保结果不随机,stratify=y可以确保每个分类的结果都有相同的比例
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=1, stratify=y)
print('不同类别所有样本数量:{}'.format(np.bincount(y)))
print('不同类别训练数据数量:{}'.format(np.bincount(y_train)))
print('不同类别测试数据数量:{}'.format(np.bincount(y_test)))
不同类别所有样本数量:[50 50 50]
不同类别训练数据数量:[35 35 35]
不同类别测试数据数量:[15 15 15]
4.1.2 4.9.4.2 分层k折交叉验证
将数据随机的分为kk 个子集(kk 的取值范围一般在[1−20][1−20] 之间),然后取出k−1k−1 个子集进行训练,另一个子集用作测试模型,重复kk 次这个过程,得到最优模型。
- 将数据分为kk 个子集
- 选择k−1k−1 个子集训练模型
- 选择另一个子集测试模型
- 重复2-3步,直至有kk 个模型
- 选择kk 个模型中性能最优的模型
# k折交叉验证
import numpy as np
from sklearn import datasets
# StratifiedKFold会按照原有标签的分布情况对数据分层
from sklearn.model_selection import StratifiedKFold
# 导入鸢尾花数据
iris_data = datasets.load_iris()
X = iris_data.data[:, [0, 1]]
y = iris_data.target
# n_splits=10相当于k=10
kfold = StratifiedKFold(n_splits=3, random_state=1)
kfold = kfold.split(X, y)
for k, (train_data, test_data) in enumerate(kfold):
print(train_data,test_data)
print('迭代次数:{}'.format(k), '训练数据长度:{}'.format(
len(train_data)), '测试数据长度:{}'.format(len(test_data)))
[ 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 67 68 69
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
88 89 90 91 92 93 94 95 96 97 98 99 117 118 119 120 121 122
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
141 142 143 144 145 146 147 148 149] [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 100 101
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116]
迭代次数:0 训练数据长度:99 测试数据长度:51
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 34
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
53 54 55 56 57 58 59 60 61 62 63 64 65 66 84 85 86 87
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
106 107 108 109 110 111 112 113 114 115 116 134 135 136 137 138 139 140
141 142 143 144 145 146 147 148 149] [ 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 67
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 117 118
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133]
迭代次数:1 训练数据长度:99 测试数据长度:51
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 50 51
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79 80 81 82 83 100 101 102 103
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
122 123 124 125 126 127 128 129 130 131 132 133] [ 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 84 85
86 87 88 89 90 91 92 93 94 95 96 97 98 99 134 135 136 137
138 139 140 141 142 143 144 145 146 147 148 149]
迭代次数:2 训练数据长度:102 测试数据长度:48
4.1.3 4.9.4.3 随机排列交叉验证
# k折交叉验证
import numpy as np
from sklearn import datasets
# StratifiedKFold会按照原有标签的分布情况对数据分层
from sklearn.model_selection import ShuffleSplit
# 导入鸢尾花数据
iris_data = datasets.load_iris()
X = iris_data.data[:, [0, 1]]
y = iris_data.target
# n_splits=10相当于k=10
kfold = ShuffleSplit(n_splits=3, random_state=1)
kfold = kfold.split(X)
for k, (train_data, test_data) in enumerate(kfold):
print(train_data,test_data)
print('迭代次数:{}'.format(k), '训练数据长度:{}'.format(
len(train_data)), '测试数据长度:{}'.format(len(test_data)))
[ 42 92 66 31 35 90 84 77 40 125 99 33 19 73 146 91 135 69
128 114 48 53 28 54 108 112 17 119 103 58 118 18 4 45 59 39
36 117 139 107 132 126 85 122 95 11 113 123 12 2 104 6 127 110
65 55 144 138 46 62 74 116 93 100 89 10 34 32 124 38 83 111
149 27 23 67 9 130 97 105 145 87 148 109 64 15 82 41 80 52
26 76 43 24 136 121 143 49 21 70 3 142 30 147 106 47 115 13
88 8 81 60 0 1 57 22 61 63 7 86 96 68 50 101 20 25
134 71 129 79 133 137 72 140 37] [ 14 98 75 16 131 56 141 44 29 120 94 5 102 51 78]
迭代次数:0 训练数据长度:135 测试数据长度:15
[ 18 37 59 111 65 119 127 102 121 118 90 146 3 51 100 133 105 23
57 123 49 9 72 126 124 145 68 143 6 13 120 89 135 22 99 92
130 39 58 81 52 117 4 17 138 97 70 109 148 42 73 115 5 76
38 86 122 80 95 34 60 129 112 7 26 19 14 30 15 44 20 137
107 64 41 79 50 131 108 144 104 8 74 94 103 31 82 55 125 32
54 48 83 149 2 33 93 136 35 75 63 29 0 46 78 66 140 67
128 106 28 16 87 45 47 113 77 40 21 101 69 53 24 134 43 116
141 142 25 147 56 61 96 10 84] [132 1 114 62 110 27 91 36 85 98 88 11 139 71 12]
迭代次数:1 训练数据长度:135 测试数据长度:15
[ 62 135 20 56 77 55 65 87 5 97 117 10 142 74 17 12 45 102
50 96 124 48 8 47 122 148 29 130 71 147 7 128 104 91 140 79
60 136 86 67 33 68 0 129 49 121 99 32 59 110 101 14 6 123
108 37 107 111 21 26 42 58 75 78 90 145 139 63 38 18 40 119
100 126 134 28 72 144 80 46 113 149 85 2 81 116 35 115 138 137
16 125 105 11 120 141 76 93 109 88 57 41 9 53 95 106 92 66
22 23 36 13 132 61 83 39 70 131 146 98 64 103 30 84 94 127
82 1 43 27 89 52 73 69 112] [ 51 133 19 31 24 34 114 54 143 4 15 25 118 3 44]
迭代次数:2 训练数据长度:135 测试数据长度:15
4.1.4 4.9.4.3 留一法交叉验证
与kk 折交叉验证类似,属于kk 折交叉验证的特例,即一个数据集TT 中有nn 个数据,当k=n−1k=n−1 时,kk 折交叉验证即为留一法交叉验证。
# 留一法交叉验证
import numpy as np
from sklearn import datasets
from sklearn.model_selection import LeaveOneOut
# 导入鸢尾花数据
iris_data = datasets.load_iris()
X = iris_data.data[:, [0, 1]]
y = iris_data.target
loo = LeaveOneOut()
loo
LeaveOneOut()
loo.get_n_splits(X)
150
count = 0
for train_index, test_index in loo.split(X):
if count < 10:
print("训练集长度:", len(train_index), "测试集长度:", len(test_index))
count += 1
if count == loo.get_n_splits(X)-1:
print('...\n迭代次数:', count)
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
训练集长度: 149 测试集长度: 1
...
迭代次数: 149
4.1.5 4.9.4.4 时间序列分割
时间序列分割一般对时间序列算法做测试,他切割的原理是:测试集的数据和上几个数据会有一定的联系。
from sklearn.model_selection import TimeSeriesSplit
X = np.array([[1, 2], [2, 4], [3, 2], [2, 4], [1, 2], [3, 2]])
y = np.array([1, 3, 3, 4, 5, 4])
# max_train_size指训练数据个数,n_splits指切割次数
tscv = TimeSeriesSplit(n_splits=5, max_train_size=3)
tscv
TimeSeriesSplit(max_train_size=3, n_splits=5)
for train_index, test_index in tscv.split(X):
print("训练数据索引:", train_index, "测试数索引:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
训练数据索引: [0] 测试数索引: [1]
训练数据索引: [0 1] 测试数索引: [2]
训练数据索引: [0 1 2] 测试数索引: [3]
训练数据索引: [1 2 3] 测试数索引: [4]
训练数据索引: [2 3 4] 测试数索引: [5]
五、1.5 交叉验证和模型一起使用
如果只是对交叉验证有一定的了解,那么问题则是,我们如何把使用交叉验证的思想,训练模型呢?使用for循环吗?不,我们可以使用sklearn自带的交叉验证评分方法。
5.1 1.5.1 cross_val_score
交叉验证中的cross_val_score,即最普通的交叉验证和模型一起使用的方法,该方法需要指定模型、训练集数据和评分方法,然后可以得出每一次测试模型的分数。
from sklearn.metrics import SCORERS
# 可以使用的评分方法
SCORERS.keys()
dict_keys(['explained_variance', 'r2', 'neg_median_absolute_error', 'neg_mean_absolute_error', 'neg_mean_squared_error', 'neg_mean_squared_log_error', 'accuracy', 'roc_auc', 'balanced_accuracy', 'average_precision', 'neg_log_loss', 'brier_score_loss', 'adjusted_rand_score', 'homogeneity_score', 'completeness_score', 'v_measure_score', 'mutual_info_score', 'adjusted_mutual_info_score', 'normalized_mutual_info_score', 'fowlkes_mallows_score', 'precision', 'precision_macro', 'precision_micro', 'precision_samples', 'precision_weighted', 'recall', 'recall_macro', 'recall_micro', 'recall_samples', 'recall_weighted', 'f1', 'f1_macro', 'f1_micro', 'f1_samples', 'f1_weighted'])
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000)
scores = cross_val_score(clf, X, y, cv=10, scoring='accuracy')
scores
array([1. , 0.93333333, 1. , 1. , 0.93333333,
0.93333333, 0.93333333, 1. , 1. , 1. ])
print('准确率:{:.4f}(+/-{:.4f})'.format(scores.mean(), scores.std()*2))
准确率:0.9733(+/-0.0653)
5.2 1.5.2 cross_validate
交叉验证中cross_validate方法,相比较cross_val_score方法可以指定多个指标,并且cross_validate方法会返回模型fit_time训练和score_time评分的时间。
from sklearn.model_selection import cross_validate
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000)
cross_validate(clf, X, y, cv=10, scoring=[
'accuracy', 'recall_weighted'], return_train_score=True)
{'fit_time': array([0.04038572, 0.06277108, 0.07863808, 0.03404975, 0.03079391,
0.04499412, 0.04462409, 0.06048512, 0.05675983, 0.03511214]),
'score_time': array([0.00144005, 0.00148797, 0.00143886, 0.00105596, 0.00098372,
0.00138307, 0.00099993, 0.00111103, 0.0020051 , 0.00080705]),
'test_accuracy': array([1. , 0.93333333, 1. , 1. , 0.93333333,
0.93333333, 0.93333333, 1. , 1. , 1. ]),
'train_accuracy': array([0.97037037, 0.97777778, 0.97037037, 0.97037037, 0.97777778,
0.97777778, 0.98518519, 0.97037037, 0.97037037, 0.97777778]),
'test_recall_weighted': array([1. , 0.93333333, 1. , 1. , 0.93333333,
0.93333333, 0.93333333, 1. , 1. , 1. ]),
'train_recall_weighted': array([0.97037037, 0.97777778, 0.97037037, 0.97037037, 0.97777778,
0.97777778, 0.98518519, 0.97037037, 0.97037037, 0.97777778])}
5.3 1.5.3 cross_val_predict
交叉验证中的cross_val_predict方法可以获取每个样本的预测结果,即每一个样本都会被作为测试数据。
from sklearn.model_selection import cross_val_predict
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000)
per_sample = cross_val_predict(clf, X, y, cv=10)
per_sample
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
from sklearn.metrics import accuracy_score
accuracy_score(y, per_sample)
0.9733333333333334
六、1.6 模型特定交叉验证
sklearn构建的内部自带交叉验证优化的估计器,如LassoCV、RidgeCV等。
from sklearn import datasets
from sklearn.metrics import r2_score
from sklearn.linear_model import Lasso, LassoCV
boston = datasets.load_boston()
X = boston.data
y = boston.target
reg = Lasso()
reg.fit(X, y)
Lasso(alpha=1.0, copy_X=True, fit_intercept=True, max_iter=1000,
normalize=False, positive=False, precompute=False, random_state=None,
selection='cyclic', tol=0.0001, warm_start=False)
y_pred = reg.predict(X)
'报告决定系数:{:.2f}'.format(r2_score(y, y_pred))
'报告决定系数:0.68'
reg = LassoCV(cv=5)
reg.fit(X, y)
LassoCV(alphas=None, copy_X=True, cv=5, eps=0.001, fit_intercept=True,
max_iter=1000, n_alphas=100, n_jobs=None, normalize=False,
positive=False, precompute='auto', random_state=None,
selection='cyclic', tol=0.0001, verbose=False)
y_pred = reg.predict(X)
'报告决定系数:{:.2f}'.format(r2_score(y, y_pred))
'报告决定系数:0.70'