跳至主要内容

学习曲线教程:什么是学习曲线?

了解学习曲线如何帮助您评估数据并识别最优解。
· 15分钟 阅读

使用 AI 探索

机器学习模型中产生误差的两个主要原因是:

  • 偏差(Bias)描述的是做出简化假设以便更容易近似目标函数的模型;例如,一个模型可能学到“全世界所有身高 5'9 的男性都穿中码上衣”——这显然带有偏差。
  • 方差(Variance)描述的是模型预测的可变性;即当我们改变用于训练的数据时,模型预测会发生多大变化。

为了获得更准确的解决方案,我们力求减少模型中的偏差和方差。这并非易事。偏差与方差彼此对立——降低其中一个将会提升另一个,这被称为偏差-方差权衡。

在本文中,您将了解:

  • 如何检测一个模型是高偏差还是高方差
  • 如何诊断出现上述任一症状的模型
  • 如何构建拟合良好的模型

在进入误差症状的检测之前,让我们先更深入地理解偏差-方差权衡。

认识偏差-方差权衡

所有监督学习算法都致力于实现同一目标:在给定一些输入数据(X)的情况下,为目标变量(y)估计映射函数(f_hat)。我们将机器学习模型试图近似的函数称为目标函数。

更换用于近似目标变量的输入数据,很可能会得到不同的目标函数,这可能影响模型的预测输出。随着训练数据变化,我们的目标函数发生变化的程度称为方差。我们不希望模型具有高方差,因为算法也许在训练时表现完美,但无法泛化到未见过的样本。

variance
[来源:Wikipedia]

在上图中,近似得到的目标函数是绿色线,最佳拟合线是黑色。注意绿色线对训练数据学得有多好。它尽力确保红色与蓝色观测被分开。如果我们用新的观测来训练该模型,它会学到一个全新的目标函数,并尝试重复同样的行为。

设想我们用线性方法(如线性回归)来近似目标函数。首先要注意,线性回归假设输入数据与我们要预测的目标之间存在线性关系。而现实世界的事件复杂得多。以牺牲一定灵活性为代价,这种简单假设使目标函数的学习更快、更易理解。我们将这种范式称为偏差。

Bias
[来源:Wikipedia]

在上图中,红线表示学习到的目标函数。许多观测落在模型预测值相距很远的地方。

我们可以通过让模型更灵活来降低偏差,但这会引入方差。反过来,我们也可以通过简化模型来降低方差,但这会引入偏差。这种关系是无法逃避的。最佳做法是选择并配置一个模型,使其在偏差与方差之间达成平衡。

tradeoff between bias and variance.
[来源:Wikipedia]

由于有未知因素影响目标函数,模型中总会存在一定误差,这被称为不可约误差。在上图中,这可通过观察“总误差”曲线最低点之下仍然存在的误差来体现。要构建理想模型,我们必须在偏差和方差之间找到一种平衡,使总误差最小化。图中以名为“最佳模型复杂度”的虚线加以说明。

接下来,我们用学习曲线进一步展开讨论偏差与方差。

学习曲线的剖析

学习曲线用来展示随着训练集规模增加,模型性能的变化。另一种用法是展示模型在某个时间段内的性能变化。我们通常用它来诊断从数据中增量学习的算法。其做法是分别在训练集与验证集上评估模型,然后绘制测得的性能。

例如,假设我们用某个机器学习算法建模了一组输入与输出之间的关系。我们从在一个样本上训练模型、并用一百个样本进行验证开始。您觉得会发生什么?如果您认为模型能完美学会这一个训练样本,那是正确的——不会有任何误差。

对一对输入到输出的关系建模并不难;您只需要“记住”它。难点在于当面对新的、未见过的样本时仍能做出准确预测。由于我们的模型把训练数据学得太好了,它在泛化到未见过的数据时会表现很差。结果是在验证数据上表现不佳。这意味着模型在训练数据与验证数据上的性能将存在较大差距。我们将这种差距称为泛化误差。

如果我们的算法想要在验证集上有更好的预测表现,就需要加入更多数据。向训练数据引入新样本不可避免地会改变模型的目标函数。我们可以监控并绘制随着训练集扩充,模型在训练与验证上的误差得分如何演变。

这意味着图中会显示两条不同的结果曲线:

  • 训练曲线:由训练数据计算的曲线;用于告知模型学得有多好。
  • 验证曲线:由验证数据计算的曲线;用于告知模型对未见样本的泛化能力。

这些曲线展示了随着数据增长,模型的表现如何,因此称为学习曲线。

注意:同样的流程也可用于了解模型随时间的学习情况。我们不是监控数据变大时模型的表现,而是监控它随时间学习得如何。例如,您可能决定学习一门新语言。您对该语言的掌握程度可被评估并赋予一个数值分数,以展示 52 周内的学习进展。

您已经了解了学习曲线的基本构成;接下来用一个真实数据集来实践,加深直观认识。

用例:预测房地产估值

我们将使用数据集:房地产估值市场历史数据集。这些数据采集自新北市新店区,包含市场历史数据。

我们的任务是基于以下特征来预测房地产估值:

  • X1=交易日期(例如,2013.250=2013 年 3 月,2013.500=2013 年 6 月,等等)
  • X2=房屋年龄(单位:年)
  • X3=距离最近捷运站的距离(单位:米)
  • X4=步行生活圈内的便利店数量(整数)
  • X5=地理坐标,纬度(单位:度)
  • X6=地理坐标,经度(单位:度)

目标变量定义为:

  • Y=每单位面积房价(以新台币万元/坪计,坪为当地面积单位,1 坪 = 3.3 平方米)

我们要预测的目标为连续型,因此该问题需要使用回归技术。

先来快速查看一下数据:

import pandas as pd

data = pd.read_excel("/content/gdrive/MyDrive/real_estate_valuation_data.xlsx")
print(data.info())
data.head()

>>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 414 entries, 0 to 413
Data columns (total 8 columns):
 #   Column                                  Non-Null Count  Dtype  
---  ------                                  --------------  -----  
 0   No                                      414 non-null    int64  
 1   X1 transaction date                     414 non-null    float64
 2   X2 house age                            414 non-null    float64
 3   X3 distance to the nearest MRT station  414 non-null    float64
 4   X4 number of convenience stores         414 non-null    int64  
 5   X5 latitude                             414 non-null    float64
 6   X6 longitude                            414 non-null    float64
 7   Y house price of unit area              414 non-null    float64
dtypes: float64(6), int64(2)
memory usage: 26.0 KB
None

No    X1 transaction date    X2 house age    X3 distance to the nearest MRT station    X4 number of convenience stores    X5 latitude    X6 longitude    Y house price of unit area
0    1    2012.916667    32.0    84.87882    10    24.98298    121.54024    37.9
1    2    2012.916667    19.5    306.59470    9    24.98034    121.53951    42.2
2    3    2013.583333    13.3    561.98450    5    24.98746    121.54391    47.3
3    4    2013.500000    13.3    561.98450    5    24.98746    121.54391    54.8
4    5    2012.833333    5.0    390.56840    5    24.97937    121.54245    43.1

这里有一个名为 No 的额外特征,在该数据的文档中未被提及。它可能是索引,但为简化起见我们将其移除。此外,特征名与文档所给不一致,我们也会做清理。

# rename the columns
renamed_columns = [col.split()[0] for col in data.columns]
renamed_columns_map = {data.columns[i]:renamed_columns[i] for i in range(len(data.columns))}

data.rename(renamed_columns_map, axis=1, inplace=True)

# remove No column
data.drop("No", axis=1, inplace=True)

print(data.head())

# separate features and target data
features, target = data.columns[:-1], data.columns[-1]

X = data[features]
y = data[target]

在拆分特征与目标标签之前,最终数据集如下所示:


        X1          X2          X3      X4      X5          X6        Y
0   2012.916667     32.0    84.87882    10  24.98298    121.54024   37.9
1   2012.916667     19.5    306.59470   9   24.98034    121.53951   42.2
2   2013.583333     13.3    561.98450   5   24.98746    121.54391   47.3
3   2013.500000     13.3    561.98450   5   24.98746    121.54391   54.8
4   2012.833333     5.0     390.56840   5   24.97937    121.54245   43.1

为演示高偏差、高方差以及拟合良好的方案,我们将构建三个模型:决策树回归器、支持向量机回归,以及随机森林回归。构建完模型后,我们会为每个模型绘制学习曲线,并分享一些诊断技巧。

诊断学习曲线

解读学习曲线主要看其形状。理解了曲线的形态与动态后,我们就可以用它来诊断机器学习模型行为中的问题。

Scikit-learn 中的 learning_curve() 函数让我们可以轻松监控训练与验证得分,这正是绘制学习曲线所需的。

传给 learning_curve() 函数的参数如下:

  • estimator:用于近似目标函数的模型
  • X:输入数据
  • y:目标
  • cv:交叉验证的划分策略
  • scoring:评估模型性能的指标
  • train_sizes:用于生成学习曲线的训练样本绝对数量;此处取值完全随机。

模型 1:决策树回归器

高方差的模型被称为过拟合。它把训练数据乃至随机噪声都学得非常好,因此在训练数据上表现优异,但无法泛化到未见样本。当所用算法对问题而言过于灵活,或模型训练时间过长时,我们会观察到这种行为。

例如,决策树回归器是非线性机器学习算法。非线性算法通常偏差低、方差高。这意味着数据集的变化会导致目标函数发生较大波动。

让我们用决策树回归器演示高方差:

from sklearn.model_selection import learning_curve
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt

# overfitting
decision_tree = DecisionTreeRegressor()

train_sizes, train_scores, test_scores = learning_curve(
    estimator=decision_tree,
    X=X,
    y=y,
    cv=5,
    scoring="neg_root_mean_squared_error",
    train_sizes = [1, 75, 165, 270, 331]
)

train_mean = -train_scores.mean(axis=1)
test_mean = -test_scores.mean(axis=1)

plt.subplots(figsize=(10,8))
plt.plot(train_sizes, train_mean, label="train")
plt.plot(train_sizes, test_mean, label="validation")

plt.title("Learning Curve")
plt.xlabel("Training Set Size")
plt.ylabel("RMSE")
plt.legend(loc="best")

plt.show()
Model 1: Decision tree regressor

当模型需要预测训练中见过的样本时,它几乎不会犯错,但在从未见过的新样本上表现糟糕。您可以通过观察训练曲线和验证曲线之间巨大的泛化误差来看到这种行为。改进此行为的一种方案是向训练数据集中添加更多样本,从而引入偏差。另一种方案是对模型进行正则化(例如限制树的最大深度,避免其长到最大)。

模型 2:支持向量机

高偏差的模型被称为欠拟合。它对训练数据做了过于简单的假设,导致难以学习潜在模式。结果是在训练集和验证集上都出现较高误差。当所用模型对问题而言过于简单,或训练不够充分时,我们会观察到这种行为。

例如,支持向量机是线性机器学习算法。线性算法通常偏差高、方差低。这意味着对目标函数形态做了更多假设。为给我们的模型引入更多偏差,我们通过设置模型中的 C 参数添加了正则化。

让我们用支持向量机演示高偏差:

from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler

# Underfitting
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

svm = SVR(C=0.25)

train_sizes, train_scores, test_scores = learning_curve(
    estimator=svm,
    X=X_scaled,
    y=y,
    cv=5,
    scoring="neg_root_mean_squared_error",
    train_sizes = [1, 75, 150, 270, 331]
)

train_mean = -train_scores.mean(axis=1)
test_mean = -test_scores.mean(axis=1)

plt.subplots(figsize=(10,8))
plt.plot(train_sizes, train_mean, label="train")
plt.plot(train_sizes, test_mean, label="validation")

plt.title("Learning Curve")
plt.xlabel("Training Set Size")
plt.ylabel("RMSE")
plt.legend(loc="best")

plt.show()
Model 2: Support Vector Machine

随着训练集规模增加,训练与验证曲线的泛化间隙会变得非常小。这表明再加入更多样本也无法提升模型性能。解决该问题的办法包括构造更多特征,或让模型更灵活,以减少所做假设的数量。

模型 3:随机森林回归器

拟合良好的模型介于欠拟合与过拟合之间。它在训练数据上的表现也许不及过拟合时那么好,但在面对未见样本时会少犯许多错误。当训练误差上升但仅上升到稳定点、同时验证误差下降到稳定点时,我们可观察到这种行为。

为演示这一点,我们将使用随机森林,它是多个决策树的集成。这意味着模型也是非线性的,但通过创建多个多样化的模型并融合其预测,为模型引入了偏差。

我们还通过设置 max_depth(控制每棵树的最大深度)为 3 来增加正则化。

来看一下代码:

from sklearn.ensemble import RandomForestRegressor

# better
random_forest = RandomForestRegressor(max_depth=3)

train_sizes, train_scores, test_scores = learning_curve(
    estimator=random_forest,
    X=X,
    y=y,
    cv=5,
    scoring="neg_root_mean_squared_error",
    train_sizes = [1, 75, 150, 270, 331]
)

train_mean = -train_scores.mean(axis=1)
test_mean = -test_scores.mean(axis=1)

plt.subplots(figsize=(10,8))
plt.plot(train_sizes, train_mean, label="train")
plt.plot(train_sizes, test_mean, label="validation")

plt.title("Learning Curve")
plt.xlabel("Training Set Size")
plt.ylabel("RMSE")
plt.legend(loc="best")

plt.show()
Model 3: Random Forest Regressor

现在您可以看到我们降低了验证数据上的误差。虽然以牺牲训练数据上的部分性能为代价,但整体上这是一个更好的模型。

泛化误差更小,错误数量也更少。此外,两条曲线在训练集规模达到 250 以上后都趋于稳定,这意味着再增加样本对该模型的提升可能有限。

总之,可以通过学习曲线观察模型的行为。构建机器学习模型的理想情形是尽可能降低误差。导致高误差的两个因素是偏差和方差,而在两者之间取得平衡将带来表现更佳的模型。

主题