一、训练模型方法
我们介绍两种非常不同的训练模型的方法,
- 通过“闭式”方程——直接计算出最适合训练集的模型参数(也 就是使训练集上的成本函数最小化的模型参数)。
- 使用迭代优化的方法,即梯度下降(GD),逐渐调整模型参数直至训练集上的成本函数调至最低,最终趋同于第一种方法计算出来的模型参数。
我们还会研究几个梯度下降的变体,包括批量梯度下降、小批量梯度下降以及随机梯度下降。
二、梯度下降
梯度下降是一种非常通用的优化算法,能够为大范围的问题找到最优解。梯度下降的中心思想就是迭代地调整参数从而使成本函数最小化,梯度下降中一个重要参数是每一步的步长,这取决于超参数学习率。如果学习率太低,算法需要经过大量迭代才能收敛,这将耗费很长时间
训练模型也就是搜寻使成本函数(在训练集上)最小化的参数组合。 这是模型参数空间层面上的搜索: 模型的参数越多, 这个空间的维度就越多, 搜索就越难。 同样是在干草堆里寻找一根针, 在一个三百维的空间里就比在一个三维空间里要棘手得多。 幸
运的是, 线性回归模型的成本函数是凸函数, 针就躺在碗底
- 批量梯度下降
计算梯度下降的每一步时,都是基于完整的训练集X的,被称为批量梯度下降:每一步都使用整批训练数据。因此,面对非常庞大的训练集时,算法会变得极慢 - 随机梯度下降
每次使用一个随机样本,随机梯度下降算法随特征数量扩展的表现比较好:如果要训练的线性模型拥有几十万个特征,使用随机梯度下降比标准方程要快得多,成本函数将不再是缓缓降低直到抵达最小值, 而是不断上上下下, 但是从整体来看, 还是在慢慢下降。 随着时间推移, 最终会非常接近最小值, 但是即使它到达了最小值, 依旧还会持续反弹, 永远不会停止 。 所以算法停下来的参数值肯定是足够好的, 但不是最优的
当成本函数非常不规则时, 随机梯度下降其实可以帮助算法跳出局部最小值,所以相比批量梯度下降,它对找到全局最小值更有优势。 - 模拟退火算法
随机性的好处在于可以逃离局部最优,但缺点是永远定位不出最小值。要解决这个困境,有一个办法是逐步降低学习率。开始的步长比较大(这有助于快速进展和逃离局部最小值),然后越来越小,让算法尽量靠近全局最小值。 - 小批量梯度下降
每一步的梯度计算,既不是基于整个训练集(如批量梯度下降)也不是基于单个实例(如随机梯度下降),而是基于一小部分随机的实例集也就是小批量。相比随机梯度下降,小批量梯度下降的主要优势在于可以从矩阵运算的硬件优化中获得显著的性能提升,特别是需要用到图形处理器时
三、学习率的选择
四、迭代次数
如果设置太低,算法可能在离最优解还很远时就停了;但是如果设置得太高,模型达到最优解后,继续迭代参数不再变化,又会浪费时间。一个简单的办法是,在开始时设置一个非常大的迭代次数,但是当梯度向量的值变得很微小时中断算法。
五、学习曲线
之前使用交叉验证来评估模型的泛化性能,如果模型在训练集和测试集表现都很糟糕,那就是拟合不足,现在我们使用学习曲线来评估模型性能。有两种曲线绘制方法
学习曲线:展现模型在训练集和验证集上,关于“训练集大小”的性能函数,学习曲线显示了对于不同数量的训练样本的估计器的验证和训练评分。它可以帮助我们发现从增加更多的训 练数据中能获益多少,以及估计是否受到更多来自方差误差或偏差误差的影响
第一种思路:首先调用
train_sizes, train_scores, valid_scores = learning_curve(SVC(kernel='linear'), X, y, train_sizes=[50, 80, 110], cv=5)
得到训练集和测试集误差,然后绘制图像。
第二种思路,调用模板函数
## 学习曲线函数,输入数据集和模型调用即可
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
def plot_learning_curves(model, X, y):
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
train_errors, val_errors = [], []
for m in range(1, len(X_train)):
model.fit(X_train[:m], y_train[:m])
y_train_predict = model.predict(X_train[:m])
y_val_predict = model.predict(X_val)
train_errors.append(mean_squared_error(y_train_predict, y_train[:m]))
val_errors.append(mean_squared_error(y_val_predict, y_val))
plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train")
plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val")
plt.show()
from sklearn import linear_model
lin_reg = linear_model.LinearRegression()
plot_learning_curves(lin_reg, X, y)
结果分析1
这条学习曲线是典型的模型拟合不足。 两条曲线均到达高地, 非常接近, 而且相当高
结果分析2
上图是过拟合的结果,改进模型过度拟合的方法之一是提供更多的训练数据,直到验证误差接近训练误差
结果分析3
支持向量机(SVM)可以从更多的训练样本中获益,增加训练样本很可能会增加泛化能力。而朴素贝叶斯不行。
模型的泛化误差可以被表示为三个截然不同的误差之和:
- 偏差
这部分泛化误差的原因在于错误的假设,比如假设数据是线性的,而实际上是二次的。高偏差模型最有可能对训练数据拟合不足 - 方差
这部分误差是由于模型对训练数据的微小变化过度敏感导致的。具有高自由度的模型(例如高阶多项式模型) 很可能也有高方差, 所以很容易对训练数据过度拟合,偏差低,然后在测试集上表现不好,方差高。 - 不可避免的误差
这部分误差是因为数据本身的噪声所致。 减少这部分误差的唯一方法就是清理数据
模型的偏差是训练出来的模型在训练集上的准确度(即在样本上拟合的好不好)。模型的方差是训练出来的模型在测试集上的准确度(即预测的准不准)。增加模型的复杂度通常会显著提升模型的方差, 减少偏差,防止过拟合能力弱。 反过来, 降低模型的复杂度则会提升模型的偏差, 降低方差,防止过拟合能力强。
这部分内容详见我的博客sklearn集成学习
六、验证曲线
绘制单个超参数对训练分数和验证分数的影响,有时有助于发现该估计是否因为某些超参数的值而出现过拟合或欠拟合。调用模板函数即可。
思路:首先调用
train_scores, valid_scores = validation_curve(Ridge(), X, y, "alpha",np.logspace(-7, 3, 3),cv=5)
得到训练集和测试集误差,然后绘制图像
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_digits
from sklearn.svm import SVC
from sklearn.model_selection import validation_curve
digits = load_digits()
X, y = digits.data, digits.target
param_range = np.logspace(-6, -1, 5)
train_scores, test_scores = validation_curve(
SVC(), X, y, param_name="gamma", param_range=param_range,
cv=10, scoring="accuracy", n_jobs=1)
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.title("Validation Curve with SVM")
plt.xlabel("$\gamma$")
plt.ylabel("Score")
plt.ylim(0.0, 1.1)
lw = 2
plt.semilogx(param_range, train_scores_mean, label="Training score",
color="darkorange", lw=lw)
plt.fill_between(param_range, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.2,
color="darkorange", lw=lw)
plt.semilogx(param_range, test_scores_mean, label="Cross-validation score",
color="navy", lw=lw)
plt.fill_between(param_range, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.2,
color="navy", lw=lw)
plt.legend(loc="best")
plt.show()