1.7 模型验证

在训练完模型之后,需要对模型进行评价,决定是否采用训练后的模型。本书前面的例子都是直接在训练数据上验证模型,这种方法存在一个很严重的问题:模型有可能过拟合。

什么情况下会出现过拟合呢?过拟合常常出现在以下3种情况下:

  • 数据有噪声;
  • 数据集过小;
  • 模型太复杂。

在解决过拟合问题之前,需要有一个判断过拟合的依据,就是使用训练集之外的数据进行验证。

在sklearn中,有两种常用的验证方法:留出验证法和交叉验证法。

留出验证法的操作方式是在训练之前,从总数据集中按一定规则(或随机)抽取一部分数据作为验证数据集,然后在模型训练完成之后,在验证集上对模型的预测效果进行验证。

如果模型在训练集上的效果非常好,但是在验证集上的效果很差,就说明模型出现了过拟合现象。

sklearn中的留出验证法可以通过sklearn.model_selection.train_test_split函数实现。下面是一个模型过拟合的例子(此例使用Jupyter Notebook编写):

In:
    import numpy as np
    from sklearn.model_selection import train_test_split
    # 导入线性模型和多项式特征构造模块
    from sklearn import  linear_model
    from sklearn.preprocessing import  PolynomialFeatures
    from sklearn.metrics import r2_score
    import matplotlib.pyplot as plt
    %matplotlib inline

接下来需要生成20个样本点,并加入一些随机扰动,生成数据的代码如下:

In:
    # 样本数量
    n_samples = 20
    x = np.array([i+2 for i in range(n_samples)]) * 4
    # 在log函数曲线上加入随机噪声
    y = 3 * np.log(x) + np.random.randint(0,3,n_samples)
    plt.scatter(x,y)

生成的样本如图1-16所示。

图 1-16 生成数据

然后对数据集进行划分,留出一部分验证集,其中test_size是验证集在总数据集中的占比:

In:
    x_train,x_test,y_train,y_test = train_test_split(x,y,test_size = 0.3)

下面使用二项式拟合的方式来拟合这个模型。在sklearn中,二项式拟合的实现方法是先将训练特征转换为二项式特征,然后再使用线性回归模型进行拟合,相关代码如下:

In:
    def poly_fit(degree):
        poly_reg =PolynomialFeatures(degree=degree)
        # 转换成二次特征
        x_ploy_train = poly_reg.fit_transform(x_train.reshape(-1,1))
        # 对x_test进行同样的转换
        x_ploy_test = poly_reg.transform(x_test.reshape(-1,1))
        clf = linear_model.LinearRegression()
        clf.fit(x_ploy_train,y_train.reshape(-1,1))
        # 创建子图,绘制训练集上的预测结果
        plt.subplot(121)
        y_train_pred = clf.predict(x_ploy_train)
        sorted_indices = np.argsort(x_train)
        plt.plot(x_train[sorted_indices],y_train_pred[sorted_indices])
        plt.scatter(x_train,y_train)
        # 创建子图,绘制验证集上的预测结果
        plt.subplot(122)
        y_test_pred = clf.predict(x_ploy_test)
        # 同时对x_test和y_test_pred进行排序
        sorted_indices = np.argsort(x_test)
        plt.plot(x_test[sorted_indices],y_test_pred[sorted_indices])
        plt.scatter(x_test,y_test)
        # 计算r2_score
        print("Train R2 score",r2_score(y_train_pred,y_train))
        print("Test R2 score",r2_score(y_test_pred,y_test))

然后逐步提高多项式的次数,观察模型效果与多项式次数之间的关系。二次多项式拟合代码如下,结果如图1-17所示:

In:
    poly_fit(2)
Out:
    Train R2 score 0.8102748526150807
    Test R2 score 0.7661602866338544

图 1-17 二次多项式拟合结果

五次多项式拟合代码如下,结果如图1-18所示:

In:
    poly_fit(5)
Out:
    Train R2 score 0.9087433750748772
    Test R2 score 0.8326903541105737

图 1-18 五次多项式拟合结果

十次多项式拟合代码如下,结果如图1-19所示:

In:
    poly_fit(10)
Out:
    Train R2 score 0.9176237285551411
    Test R2 score -0.13339572778788056

图 1-19 十次多项式拟合结果

上述案例使用train_test_split将数据集划分为两个部分,训练集和验证集。然后在训练集上进行模型训练,训练完成后,分别计算训练集和验证集上的模型指标。

结果发现,随着模型复杂度的提升(多项式特征次数越来越高),模型在训练集上的表现(R2 Score)越来越好,在验证集上的效果却是先变好再变坏。这就是模型过拟合的表现。

交叉验证法可以理解为留出验证法的升级版,以sklearn中的KFold检验为例,其算法步骤如下。

(1) 将数据集划分为n个互斥子集。

(2) 以其中一个子集作为验证集,其他子集作为训练集,训练模型。

(3) 选择另一个子集作为验证集,其他子集作为训练集,再训练模型。

(4) 如此进行n次训练和测试,得到n个结果。

在发现模型过拟合后,可以尝试通过如下几个方法解决过拟合的问题:

  • 重新清洗数据;
  • 增加数据量;
  • 采用正则化方法;
  • 选择合适的模型。