项目篇(三)防止过拟合
一、介绍这对我们来说是一个非常失败的模型因为我们不是要一个复杂的、在训练集上预测完全精准的模型而是想要构建一个泛化能力尽可能高的模型。如果一个模型能够对它没见过且性质相似的数据做出准确预测那我们就可以认为它具有不错的泛化能力这个模型就是相对成功的。那为什么模型会出现过拟合的状态呢其实这是因为机器和我们程序员一样都有强迫症。模型在针对训练集的优化过程中不找到最小的误差点它们是不会停的。我们说过拟合是机器学习模型在训练数据集上通过拟合函数形成的。在过拟合的形成过程中一般会经历三个阶段欠拟合、平衡点和过拟合。在初期的时候模型对训练集的拟合还未完善能力不够强 偏差也比较大。此时的模型就处于欠拟合的状态把模型应用于测试集数据效果肯定也不好所以还需要继续训练。随着训练次数增多以及模型自身的调整优化模型的拟合能力变得越来越强就差不多可以用了。此刻实际上已经出现了欠拟合和过拟合之间的平衡点。然而如果训练继续进行模型经过充分训练后就会完全拟合训练集数据。如果把此时的模型应用于测试集我们并不一定能得到好的效果很容易出现很高的误差。这就是过拟合的状态。1、对于回归问题来说你可以通过下面的图理解过拟合2、对于分类问题来说你可以通过下面的图理解过拟合二、解决过拟合问题的方法1、一般来说数据点少模型很容易就能完全描绘出从特征到标签的映射。所以增加数据集中数据的数量可以在一定程度上防止过拟合。2、如果特征工程做得不好数据集中的无用信息多就会放大过拟合的影响因为模型描绘出来的是无用信息和标签之间关系当然泛化能力就受影响。所以进行优质的特征工程筛除无用的特征去芜取精去伪存真有利于避免过拟合。3、我们前面提到模型比较复杂时它在训练集上的精度可以想多高就多高这是因为从数学的角度多次函数的图像远远比一次、二次函数复杂。模型越复杂所能够覆盖的特征空间就越大。所以如果数据集较小或者说问题比较简单我们尽量选择简单的模型。凡事选择简单的解决方案这个道理人称奥卡姆剃刀定理。因为数据集很难收集特征工程又很考验机器学习工程师的水平所以防止过拟合的最佳策略就是要在模型的简单和复杂之间寻找一个平衡点。3 个模型决策树、线性回归和随机森林相对而言决策树和线性回归模型比较容易出现过拟合问题而随机森林则本身就可以基于决策树的过拟合问题实现优化。三、决策树模型的构造和剪枝决策树这个算法的原理很好理解它就是将一大堆的 if…else 语句进行连接直到最后得到想要的结果。算法中的各个节点是根据训练数据集中的特征形成的。在对特征节点的选择不同时就可以生成很多不一样的决策树。生成一个决策树有两个阶段分别是构造和剪枝。1、构造构造就是选择什么特征作为节点生成决策树的过程。在构造过程中有三种节点根节点就是树的最顶端节点内部节点就是树中间的那些节点叶节点就是树最底部的节点。在这些节点之间存在一种父子关系。比如根节点会有子节点子节点还有子节点到了叶节点就停止了不再有子节点。在构造过程中我们可以根据信息增益的程度来选择哪个属性作为根节点哪些属性作为子节点什么时候停止并得到目标状态也就是叶节点。信息增益就是数据集的熵和选定某个特征的条件熵之差而熵就是对随机变量不确定性的度量熵越大不确定性越大。我们应该选择熵比较小的特征作为根节点来增加信息的增益。2、剪枝如果我们在构造的过程中选择不同的特征作为根节点然后根节点下面选择不同的特征形成内部节点就可以得到另外一棵决策树也就是一个新的模型由于 if…else 可以无限制地写下去对于任何训练集只要树的深度足够决策树肯定能够达到 100% 的准确率。我们在前面讲过这并不是一件好事因为这样的模型认为其它所有数据的特点都和训练集中的数据完全一样的所以它的泛化能力会很差。对于这种情况我们要在构造过程中对决策树进行剪枝让树的深度不要太深以免决策树太过于和精确。这样“模糊”一些的决策树虽然在训练集上面的分数会降低但是能够得到更强的泛化能力。举例相亲数据一棵经过剪枝的决策树剪枝之后树的深度从原来的 3 层变成了 2 层。再来看看下面这个决策树回归模型对某个数据集中数据点的拟合示意图在这张图中绿色线是当决策树深度为 5 是的模型拟合状态蓝色线是决策树深度为 2 时的模型拟合状态。很明显经过深度为 5 的模型训练集损失小但深度为 2 的模型泛化能力会更好。其实决策树的深度是一个可调的超参数也就是 max_depth。这个超参数能限制树的最大深度把超过设定深度的树枝全部剪掉这也是最常见的剪枝策略。from sklearn.tree import DecisionTreeRegressor #导入决策树回归模型 model_dtr DecisionTreeRegressor() #创建决策树回归模型 model_dtr_cut DecisionTreeRegressor(max_depth3) #创建深度为3的决策树回归模型 model_dtr.fit(X_train, y_train) #拟合决策树模型 model_dtr_cut.fit(X_train, y_train) #拟合深度为3的决策树模型 y_valid_preds_dtr model_dtr.predict(X_valid) #用决策树模型预测验证集 y_valid_preds_dtr_cut model_dtr_cut.predict(X_valid) #用深度为2的决策树模型预测验证集 from sklearn.metrics import r2_score, median_absolute_error #导入Sklearn评估模块 print(训练集上的R平方分数-决策树: %0.4f % r2_score(y_train, model_dtr.predict(X_train))) print(训练集上的R平方分数-深度为3的决策树: %0.4f % r2_score(y_train, model_dtr_cut.predict(X_train))) print(测试集上的R平方分数-决策树: %0.4f % r2_score(y_valid, model_dtr.predict(X_valid))) print(测试集上的R平方分数-深度为3的决策树: %0.4f % r2_score(y_valid, model_dtr_cut.predict(X_valid)))训练集上的R平方分数-决策树: 1.0000训练集上的R平方分数-深度为3的决策树: 0.8045测试集上的R平方分数-决策树: 0.2857测试集上的R平方分数-深度为3的决策树: 0.4870可以看到未经剪枝的决策树在训练集上的分数是满分但是在验证集上的得分低得十分离谱。而设定了深度的决策树也就是剪枝它的训练集上的分数有所下降可在验证集上的分数是大幅提升的体现出了更好的泛化能力。所以我们这次的“剪枝”相当成功四、线性回归模型的正则化我们再来看看怎么防止线性回归模型的过拟合现象。1、拟合如何产生线性回归模型其实就是通过梯度下降确定参数的过程。如果数据集中有 3 个特征公式就是如果在 x1、x2、x3 这些特征中有某个特征的值域较大而如果模型拟合的结果中其权重参数 w 的值也比较大那么这个特征就会占据“主导”地位使模型往这些较大值的位置偏移形成了对这些值的“过拟合”。那么如果我们能让这类特征项的权重参数变小也许就可以得到更为平衡的模型来防止过拟合现象的出现。这种在一定程度上减小这些参数的值的方法就是机器学习中的正则化regularization。具体来说在损失函数当中加入的正则项也叫惩罚项也就是给需要训练的函数加上一些规矩、一些限制让它们不要自我膨胀。2、正则化线性回归的损失函数的正则项有两种添加方法分别叫做 L1 正则项和 L2 正则项。添加 L1 正则项之后一些特征的权重会变小一些绝对值较小的系数甚至直接变为 0相当于抛弃了一些特征来增强模型的泛化能力。这种回归也叫 Lasso 回归。添加 L2 正则项之后模型在不抛弃任何一个特征的情况下会缩小回归系数也就是某些特征的权重让模型相对稳定通常模型的泛化能力也更好。这种回归也叫 Rigde 回归。下面我们就使用 Sklearn 包中的 Lasso 回归和 Ridge 回归模型比较一下 L1 和 L2 正则化方法from sklearn.linear_model import LinearRegression #导入线性回归模型 from sklearn.linear_model import Lasso from sklearn.linear_model import Ridge model_lr LinearRegression() #创建线性回归模型 model_lasso Lasso() #创建Lasso回归模型 model_ridge Ridge() #创建Ridge回归模型 model_lr.fit(X_train, y_train) #拟合线性回归模型 model_lasso.fit(X_train, y_train) #拟合Lasso回归模型 model_ridge.fit(X_train, y_train) #拟合Ridge回归模型 from sklearn.metrics import r2_score, median_absolute_error #导入Sklearn评估模块 print(训练集上的R平方分数-线性回归: %0.4f % r2_score(y_train, model_lr.predict(X_train))) print(训练集上的R平方分数-Lasso回归: %0.4f % r2_score(y_train, model_lasso.predict(X_train))) print(训练集上的R平方分数-Ridge回归: %0.4f % r2_score(y_train, model_ridge.predict(X_train))) print(测试集上的R平方分数-线性回归: %0.4f % r2_score(y_valid, model_lr.predict(X_valid))) print(测试集上的R平方分数-Lasso回归: %0.4f % r2_score(y_valid, model_lasso.predict(X_valid))) print(测试集上的R平方分数-Ridge回归: %0.4f % r2_score(y_valid, model_ridge.predict(X_valid)))训练集上的R平方分数-线性回归: 0.6732训练集上的R平方分数-Lasso回归: 0.6731训练集上的R平方分数-Ridge回归: 0.6354测试集上的R平方分数-线性回归: 0.4719测试集上的R平方分数-Lasso回归: 0.4737测试集上的R平方分数-Ridge回归: 0.4992你会发现从普通线性回归到 Lasso 回归再到 Ridge 回归训练集分数呈现下降的趋势这就是防止过拟合的惩罚项发挥了作用在测试集上分数则正好相反从普通线性回归和 Lasso 回归再到 Ridge 回归反而呈现略微升高的趋势。不过整体来说差异似乎不是很大说明目前这个线性回归模型的过拟合现象其实并不明显。