首先,我们先生成一组大致符合二次函数规律的训练师数据,再分别用 1) 线性函数,2) 二次函数 和 3) degree=300的多项式函数进行回归分析,结果如下(代码见文末)
在这个示例中,degree=300的情况明显overfit训练数据,linear model为underfit。二次函数则很好的拟合了训练数据,因为训练数据就是有二次函数生成的。
而通常情况下我们并不知道训练数据符合怎样的分布或者具有怎样的规律,那应该如何选择模型?是复杂一些还是简单一些?通常的思路是:通过cross-validation进行检测:
- 如果在训练集效果很好而在测试集很差,说明是overfit
- 若果在训练集和测试集效果都很差,说明是underfit
另一种方法就是……看learning curve
1. learning curve
- 定义
- learning curve 是 模型在训练集和验证集(validation set)上的performance 随 训练集规模 的变化情况。
- 操作
- 在训练集不同大小的子集上多次训练模型即可。
线性函数拟合二次函数时的learning curve如下(代码见文末):
可以看出:
- 训练集很小的时候:
- train error 很小,模型完美契合训练集,但是由于存在噪声,所以推广能力很差,因此验证集误差很大
- 随着训练集的增加,训练误差逐渐上升,当然,验证集误差逐渐降低。
- 最终的误差比较大
- 这是因为选用的 Linear model 过于简单,所以随着训练集 size 的增大,误差最终呈现出 plateau 的变化趋势。
这是一个典型的 underfitting model:因为两条曲线最终都呈 plateau 形态,挨得很近,且误差相对较大。对于 underfitting 的模型来说,使用更多的训练样本并不会提升模型性能。
degree=300的多项式函数拟合二次函数时的learning curve如下(代码见文末):
观察上图,可以得到以下结论:
- 训练集上的误差低于 Linear Regression 时的情况
- 在 train_errors 和 validation_errors 两条曲线之间存在一定的间隙
- 说明在训练集上的效果远好于验证集上的效果 => overfitting
- 当训练集的size不断增大时,两条曲线会越来越近
- 解决 overfitting 问题的方法之一就是输入更多训练数据,直到验证误差与训练误差相差无几。
2. The Bias/Variance Tradeoff
统计与机器学习领域有一条重要的理论结果为:模型的泛化误差(generalization error)可以表示为3项不同误差的和:
- bias(偏差)
- 错误的假设。如训练数据本来是二次函数,但是却选用线性模型拟合。high-bias模型的往往会 underfit 训练数据。
- variance(方差)
- 模型对训练数据的微小变化过于敏感。当模型自由度很高(如高阶多项式模型)时易于具有 high variance 的特性,往往会 overfit 训练数据。
- irreducible error(不可避免的误差)
- 数据本身具有噪声。减小该项误差的唯一方法是 清洗数据。
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
## 生成一些非线性数据
import numpy as np
# import numpy.random as rnd
np.random.seed(42)
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)
X_new=np.linspace(-3, 3, 100).reshape(100, 1)
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
for style, width, degree in (("g-", 1, 300), ("b--", 2, 2), ("r-+", 2, 1)):
polybig_features = PolynomialFeatures(degree=degree, include_bias=False)
std_scaler = StandardScaler()
lin_reg = LinearRegression()
polynomial_reg = Pipeline([
("poly_features", polybig_features),
("std_scaler", std_scaler),
("lin_reg", lin_reg)
])
polynomial_reg.fit(X, y)
y_newbig = polynomial_reg.predict(X_new)
plt.plot(X_new, y_newbig, style, label=str(degree), linewidth=width)
plt.plot(X, y, "b.", linewidth=3)
plt.legend(loc="upper left")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([-3, 3, 0, 10])
plt.show()
## Linear model 拟合 二次函数 时的 learning curve
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, random_state=10)
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[:m])
train_errors.append(mean_squared_error(y_train_predict, y_train[:m]))
val_errors.append(mean_squared_error(y_val_predict, y_val[:m]))
plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train")
plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val")
plt.legend(loc="upper right", fontsize=14)
plt.xlabel("Training set size", fontsize=14)
plt.ylabel("RMSE", fontsize=14)
lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)
plt.axis([0, 80, 0, 3])
plt.show()
## 10^th degree 多项式模型 拟合 二次函数 时的 learning curve
from sklearn.pipeline import Pipeline
polynomial_regression = Pipeline([
("Poly_features", PolynomialFeatures(degree=10, include_bias=False)),
("lin_reg", LinearRegression()),
])
plot_learning_curves(polynomial_regression, X, y)
plt.axis([0, 80, 0, 3])
plt.show()