什么是过拟合和欠拟合
什么是过拟合和欠拟合,我们来看一下下图:
在图中,第一张图是欠拟合,第二张图拟合刚好,第三张图是过拟合;
- 欠拟合就是模型在训练样本或者验证数据集以及测试数据集中都表现很差;模型的学习能力较弱,而数据复杂度较高的情况出现,此时模型由于学习能力不足,无法学习到数据集中的“一般规律”,因而导致泛化能力弱,
- 过拟合就是模型在训练样本中表现得过于优越,而在验证数据集以及测试数据集中表现很差;
欠拟合常见解决方法有:
- 增加新特征,可以考虑加入进特征组合、高次特征,来增大假设空间;
- 添加多项式特征,这个在机器学习算法里面用的很普遍,例如将线性模型通过添加二次项或者三次项使模型泛化能力更强;
- 减少正则化参数,正则化的目的是用来防止过拟合的,但是模型出现了欠拟合,则需要减少正则化参数;
- 使用非线性模型,比如核SVM 、决策树、深度学习等模型;
- 调整模型的容量(capacity),通俗地,模型的容量是指其拟合各种函数的能力;
- 容量低的模型可能很难拟合训练集;使用集成学习方法,如Bagging ,将多个弱学习器Bagging。
过拟合常见解决方法有:
- 在神经网络模型中,可使用权值衰减的方法,即每次迭代过程中以某个小因子降低每个权值。
- 选取合适的停止训练标准,使对机器的训练在合适的程度;
- 保留验证数据集,对训练成果进行验证;
- 获取额外数据进行交叉验证;
- 正则化,即在进行目标函数或代价函数优化时,在目标函数或代价函数后面加上一个正则项,一般有L1正则与L2正则等。
通过学习曲线检测过拟合与欠拟合
下面我们通过一个例子来进行验证,我准备来一组数据集
,如下:
其中y=1.0就是我们需要模型正确划分的数据,首先我们来画出y=1.0,y=-1.0的散点图来查看数据的分布情况;代码如下:
data = pd.read_csv('data.csv')
# 刷选y=1很y=-1的数据
data_0 = data[data['y'] == -1.0]
data_1 = data[data['y'] == 1]
# 绘制散点图
plt.scatter(np.array(data_0['x1']),np.array(data_0['x2']),color='r')
plt.scatter(np.array(data_1['x1']),np.array(data_1['x2']))
plt.show()
运行代码,图表如下:
通过查看数据集的分布,这里我们将使用三个模型来训练这个数据集。
- 决策树模型,
- 逻辑回归模型
- 支持向量机模型
其中一个模型会过拟合,一个欠拟合,还有一个正常。首先,我们将编写代码为每个模型绘制学习曲线,最后我们将查看这些学习曲线,判断每个模型对应哪个曲线。
首先,请记住三个模型的学习曲线外观大概如下所示:
learning_curve
首先我们将使用函数 learning_curve:
train_sizes, train_scores, test_scores = learning_curve(
estimator, X, y, cv=None, n_jobs=1, train_sizes=np.linspace(.1, 1.0, num_trainings))
不需要担心该函数的所有参数(你可以在此处了解详情),这里,我们将解释主要参数:
-
estimator
,是我们针对数据使用的实际分类器,例如LogisticRegression()
或GradientBoostingClassifier()
。 -
X
和y
是我们的数据,分别表示特征和标签。 -
train_sizes
是用来在曲线上绘制每个点的数据大小。 -
train_scores
是针对每组数据进行训练后的算法训练得分。 -
test_scores
是针对每组数据进行训练后的算法测试得分。
两个注意事项:
训练和测试得分是一个包含 3 个值的列表,这是因为函数使用了 3 折交叉验证。
非常重要:可以看出,我们使用训练和测试误差来定义我们的曲线,而这个函数使用训练和测试得分来定义曲线。二者是相反的,因此误差越高,得分就越低。因此,当你看到曲线时,你需要自己在脑中将它颠倒过来,以便与上面的曲线对比。
定义函数
了解了learning_curve的参数后,我们来定义一个函数(draw_learning_curves),该函数根据不同模型画出相应训练得分跟交叉验证得分的图表;代码如下:
def draw_learning_curves(X, y, estimator, num_trainings, title):
train_sizes, train_scores, test_scores = learning_curve(
estimator, X, y, cv=None, n_jobs=1, train_sizes=np.linspace(.1, 1.0, num_trainings))
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
plt.grid()
plt.title(title + " Learning Curves")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.plot(train_scores_mean, 'o-', color="g",
label="Training score")
plt.plot(test_scores_mean, 'o-', color="y",
label="Cross-validation score")
plt.legend(loc="best")
plt.show()
定义模型分类器
定好好函数后,我们定义三个模型(决策树模型,逻辑回归模型,支持向量机模型)的分类器,然后调用上面的函数画图;代码如下:
### Logistic Regression
estimator1 = LogisticRegression()
### Decision Tree
estimator2 = GradientBoostingClassifier()
### Support Vector Machine
estimator3 = SVC(kernel='rbf', gamma=1000)
all_estimators = [estimator1, estimator2, estimator3]
all_title = ['Logistic Regression', 'Decision Tree', 'Support Vector Machine']
for estimator, title in list(zip(all_estimators, all_title)):
utils.draw_learning_curves(X2, y2, estimator, 6, title)
运行代码后,可以得出三个模型的得分图表:
我们得出了图表,那根据上述模型曲线,你觉得哪个模型欠拟合,哪个过拟合,哪个正好?
我们可以根据这些曲线得出结论:
- 逻辑回归模型的训练和测试得分很低。
- 决策树模型的训练和测试得分很高。
-
支持向量机模型的训练得分很高,测试得分很低。
由此可以判断,逻辑回归模型欠拟合,支持向量机模型过拟合,决策树正常。
同样,我们可以翻转这些曲线(因为它们测量的是得分,而原始曲线测量的是错误),并将它们与下面的三条曲线对比,可以看出它们与我们之前看到的三条曲线很像。(注意:我们需要翻转曲线并不意味着错误是 1 减去得分。只是表示模型越来越好的话,错误会降低,得分会升高。)
现在我们应该检测在实际模型中是否这样。当我们绘制每个模型的界限曲线时,结果如下所示:
当我们查看上述模型时,第一个模型欠拟合,第二个正常,第三个过拟合,这种现象合理吗?合理吧?我们看到数据由圆圈或方框正确地划分出来。我们的模型按以下形式划分数据:
- 逻辑回归模型使用一条直线,这太简单了。在训练集上的效果不太好,因此欠拟合。
- 决策树模型使用一个方形,拟合的很好,并能够泛化。因此,该模型效果很好。
- 支持向量机模型实际上在每个点周围都画了一个小圆圈。它实际上是在记住训练集,无法泛化。因此 过拟合。
上面步骤就是通过学习曲线检测过拟合与欠拟合的过程,通过学习曲线检测模型是否过拟合与欠拟合可以更好让你选择模型进行训练,大家可以自己尝试一下。