08-07 细分构建机器学习应用程序的流程-测试模型 细分构建机器学习应用程序的流程-测试模型 一、1.1 metrics评估指标 二、1.2 测试回归模型 三、1.3 测试分类模型 四、1.4 欠拟合和过拟合 五、1.5 交叉验证和模型一起使用 六、1.6 模型特定交叉验证


更新、更全的《机器学习》的更新网站,更有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即报告决定系数((R^2)),可以理解成MSE的标准版,(R^2)的公式为

[R^2 = 1-{frac {{frac{1}{n}sum_{i=1}^n(y^{(i)}-hat{y^{(i)}})^2}} {{frac{1}{n}}sum_{i=1}^n(y^{(i)}-mu_{(y)})^2} } ]

其中(mu_{(y)})(y)的平均值,即({{frac{1}{n}}sum_{i=1}^n(y^{(i)}-mu_{(y)})^2})(y)的方差,公式可以写成

[R^2 = 1-{frac{MSE}{Var(y)}} ]

(R^2)的取值范围在(0-1)之间,如果(R^2=1),则均方误差(MSE=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——将正类预测为负类数
误差矩阵 - - -
- - 真实值 真实值
- - 1 0
预测值 1 True Positive(TP) False Positive(FP)
预测值 0 True Negative(TN) False Negative(FN)

3.1 1.3.1 准确度

准确度(accuracy_socre)定义为

[P = {frac{TP+FN}{TP+FP+TN+FN}} = frac{正确预测的样本数}{样本总数} ]
# 查准率示例
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)定义为

[P = {frac{TP}{TP+FP}} = frac{正确预测为正类的样本数}{预测为正类的样本总数} ]
# 查准率示例
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等)定义为

[R = {frac{TP}{TP+FN}} = frac{正确预测为正类的样本数}{正类总样本数} ]
# 查全率示例
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值

通常情况下通过查准率和查全率度量模型的好坏,但是查准率和查全率是一对矛盾的度量工具,查准率高的时候查全率低;查全率高的时候查准率低,因此工业上对不不同的问题对查准率和查全率的侧重点会有所不同。

例如癌症的预测中,正类是健康,反类是患有癌症。较高的查准率可能会导致健康的人被告知患有癌症;较高的查全率可能会导致患有癌症的患者会被告知健康。

(F_1)值(f1_score等)定义为

[F_1 = {frac{2*P*R}{P+R}} = {frac{2*TP}{2TP+FP+FN}} = {frac{2*TP}{样例总数+TP-TN}} ]

(F_eta)定义为:

[F_eta = {frac{(1+eta^2)*P*R}{eta^2*P+R}} ]

(F_eta)是在(F_1)值的基础上加权得到的,它可以更好的权衡查准率和查全率。

  1. (eta<1)时,(P)的权重减小,即(R)查准率更重要
  2. (eta=1)时,(F_eta = F_1)
  3. (eta>1)时,(P)的权重增大,即(P)查全率更重要
# 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),假正例率和真正例率分别定义为:

[FPR = {frac{FP}{FP+TN}} ext{假正例率} \ TPR = {frac{TP}{TP+FN}} ext{真正例率} ]
# 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()

08-07 细分构建机器学习应用程序的流程-测试模型
细分构建机器学习应用程序的流程-测试模型
一、1.1 metrics评估指标
二、1.2 测试回归模型
三、1.3 测试分类模型
四、1.4 欠拟合和过拟合
五、1.5 交叉验证和模型一起使用
六、1.6 模型特定交叉验证

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 欠拟合和过拟合

第二部分讲解线性回归时讲到,(0)误差的模型也许并不是最好的,因为模型是通过训练集得到的,由于训练集可能存在噪声,因此训练集并不一定能代表测试集,更不一定能代表未来新数据。虽然这样的模型可能很好的拟合训练数据,但是对未来数据可能并没有较好的拟合能力,这种现象成为过拟合。

# 过拟合图例
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)

    return 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(0, 7)
    plt.ylim(0, 8)
    plt.xlabel('x')
    plt.ylabel('y')
    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(0, 7)
    plt.ylim(0, 8)
    plt.xlabel('x')
    plt.ylabel('y')
    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()

08-07 细分构建机器学习应用程序的流程-测试模型
细分构建机器学习应用程序的流程-测试模型
一、1.1 metrics评估指标
二、1.2 测试回归模型
三、1.3 测试分类模型
四、1.4 欠拟合和过拟合
五、1.5 交叉验证和模型一起使用
六、1.6 模型特定交叉验证

如上图所示每张图都有相同分布的8个样本点,红点明显是一个噪声点,接下来将讲解上述8张图。暂时不用太关心线性回归和多项式回归是什么,这两个以后你都会学习到,此处引用只是为了方便举例。

  • 图1:线性回归拟合样本点,可以发现样本点距离拟合曲线很远,这个时候一般称作欠拟合(underfitting)
  • 图2:一阶多项式回归拟合样本点,等同于线性回归
  • 图3:三阶多项式回归拟合样本点,表现还不错
  • 图4:五阶多项式回归拟合样本点,明显过拟合
  • 图5:七阶多项式回归拟合样本点,已经拟合了所有的样本点,毋庸置疑的过拟合
  • 图7:十阶多项式回归拟合样本点,拟合样本点的曲线和七阶多项式已经没有了区别,可以想象十阶之后的曲线也类似于七阶多项式的拟合曲线

从上图可以看出,过拟合模型将会变得复杂,对于线性回归而言,它可能需要更高阶的多项式去拟合样本点,对于其他机器学习算法,也是如此。这个时候你也可以想象,过拟合虽然对拟合的样本点的误差接近0,但是对于未来新数据而言,如果新数据的(x=2),如果使用过拟合的曲线进行拟合新数据,那么会给出(y=0.5)的预测值,也就是说把噪声的值给了新数据,这样明显是不合理的。

4.1 4.9.4 交叉验证

08-07 细分构建机器学习应用程序的流程-测试模型
细分构建机器学习应用程序的流程-测试模型
一、1.1 metrics评估指标
二、1.2 测试回归模型
三、1.3 测试分类模型
四、1.4 欠拟合和过拟合
五、1.5 交叉验证和模型一起使用
六、1.6 模型特定交叉验证

08-07 细分构建机器学习应用程序的流程-测试模型
细分构建机器学习应用程序的流程-测试模型
一、1.1 metrics评估指标
二、1.2 测试回归模型
三、1.3 测试分类模型
四、1.4 欠拟合和过拟合
五、1.5 交叉验证和模型一起使用
六、1.6 模型特定交叉验证

对训练数据集切割做交叉验证也是防止模型过拟合的一个很好的方法。

一般会把数据按照某种比例分为训练集、测试集。训练集用来训练模型,把测试集当做未来新样本的样本集用来评估模型。然后交叉验证可以认为就是不断地重复训练模型、测试模型。如果数据量较大的话,会把训练集按照某种比例分成训练集、验证集、测试集,使用训练集训练参数;使用验证集训练超参数;使用测试集测试模型性能。

4.1.1 4.9.4.1 简单交叉验证

把数据集按照某种比例,将数据集中的数据随机的分为训练集和测试集。然后不断的改变模型参数训练出一组模型,每训练完一个模型就用测试集测试,最后得到性能最好的模型。

  1. 初始值(c=1)
  2. 训练模型
  3. 测试模型,(c+1)
  4. 如果(c<11)改变模型参数,跳转到步骤1;反之,停止训练
  5. 从模型集({c_1,c_2,cdots,c_{10}})中选择性能最优的模型
# 简单交叉验证
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折交叉验证

08-07 细分构建机器学习应用程序的流程-测试模型
细分构建机器学习应用程序的流程-测试模型
一、1.1 metrics评估指标
二、1.2 测试回归模型
三、1.3 测试分类模型
四、1.4 欠拟合和过拟合
五、1.5 交叉验证和模型一起使用
六、1.6 模型特定交叉验证

将数据随机的分为(k)个子集((k)的取值范围一般在([1-20])之间),然后取出(k-1)个子集进行训练,另一个子集用作测试模型,重复(k)次这个过程,得到最优模型。

  1. 将数据分为(k)个子集
  2. 选择(k-1)个子集训练模型
  3. 选择另一个子集测试模型
  4. 重复2-3步,直至有(k)个模型
  5. 选择(k)个模型中性能最优的模型
# 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 留一法交叉验证

(k)折交叉验证类似,属于(k)折交叉验证的特例,即一个数据集(T)中有(n)个数据,当(k=n-1)时,(k)折交叉验证即为留一法交叉验证。

# 留一法交叉验证
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('...
迭代次数:', 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'