underfit-overfit.md 10.4 KB
Newer Older
1 2 3 4 5 6 7 8
# 欠拟合和过拟合

你有没有类似这样的体验?考试前突击背了模拟题的答案,模拟题随意秒杀。但考试时出的题即便和模拟题相关,只要不是原题依然容易考挂。换种情况来说,如果考试前通过自己的学习能力从模拟题的答案里总结出一个比较通用的解题套路,考试时碰到这些模拟题的变种更容易答对。

有人曾依据这种现象对学生群体简单粗暴地做了如下划分:

![](../img/student_categories.png)

M
muli 已提交
9
这里简要总结上图中学生的特点:
10

M
muli 已提交
11 12 13 14
* 学渣:能力不行,也不认真做作业,容易考挂
* 学痞:能力不错,但喜欢裸奔,但还是可能考得比学渣好
* 学痴:能力不行,但贵在认真,考不赢学霸但秒掉学渣毫无压力
* 学霸:有能力,而且卖力,考完后喜大普奔
15

M
muli 已提交
16
(现在问题来了,学酥应该在上图的哪里?)
17 18 19

学生的考试成绩和看起来与自身的训练量以及自身的学习能力有关。但即使是在科技进步的今天,我们依然没有完全知悉人类大脑学习的所有奥秘。的确,依赖数据训练的机器学习和人脑学习不一定完全相同。但有趣的是,机器学习模型也可能由于自身不同的训练量和不同的学习能力而产生不同的测试效果。为了科学地阐明这个现象,我们需要从若干机器学习的重要概念开始讲解。

M
muli 已提交
20
## 训练误差(模考成绩)和泛化误差(考试成绩)
21

M
muli 已提交
22
在实践中,机器学习模型通常在训练数据集上训练并不断调整模型里的参数。之后,我们通常把训练得到的模型在一个区别于训练数据集的测试数据集上测试,并根据测试结果评价模型的好坏。机器学习模型在训练数据集上表现出的误差叫做**训练误差**,在任意一个测试数据样本上表现出的误差的期望值叫做**泛化误差**
23

M
muli 已提交
24
训练误差和泛化误差的计算可以利用我们之前提到的损失函数,例如[线性回归](linear-regression-scratch.md)里用到的平方误差和[多类逻辑回归](softmax-regression-scratch.md)里用到的交叉熵损失函数。
25 26 27

之所以要了解训练误差和泛化误差,是因为统计学习理论基于这两个概念可以科学解释本节教程一开始提到的模型不同的测试效果。我们知道,理论的研究往往需要基于一些假设。而统计学习理论的一个假设是:

A
Aston Zhang 已提交
28
> 训练数据集和测试数据集里的每一个数据样本都是从同一个概率分布中相互独立地生成出的(独立同分布假设)。
29

30
基于以上独立同分布假设,给定任意一个机器学习模型及其参数,它的训练误差的期望值和泛化误差都是一样的。然而从之前的章节中我们了解到,在机器学习的过程中,模型的参数并不是事先给定的,而是通过训练数据学习得出的:模型的参数在训练中使训练误差不断降低。所以,如果模型参数是通过训练数据学习得出的,那么训练误差的期望值无法高于泛化误差。换句话说,通常情况下,由训练数据学到的模型参数会使模型在训练数据上的表现不差于在测试数据上的表现。
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

因此,一个重要结论是:

> 训练误差的降低不一定意味着泛化误差的降低。机器学习既需要降低训练误差,又需要降低泛化误差。

## 欠拟合和过拟合

实践中,如果测试数据集是给定的,我们通常用机器学习模型在该测试数据集的误差来反映泛化误差。基于上述重要结论,以下两种拟合问题值得注意:

* **欠拟合**:机器学习模型无法得到较低训练误差。
* **过拟合**:机器学习模型的训练误差远小于其在测试数据集上的误差。

我们要尽可能同时避免欠拟合和过拟合的出现。虽然有很多因素可能导致这两种拟合问题,在这里我们重点讨论两个因素:模型的选择和训练数据集的大小。

### 模型的选择

A
Aston Zhang 已提交
47 48 49
在本节的开头,我们提到一个学生可以有特定的学习能力。类似地,一个机器学习模型也有特定的拟合能力。拿多项式函数举例,一般来说,高阶多项式函数(拟合能力较强)比低阶多项式函数(拟合能力较弱)更容易在相同的训练数据集上得到较低的训练误差。需要指出的是,给定数据集,过低拟合能力的模型更容易欠拟合,而过高拟合能力的模型更容易过拟合。模型拟合能力和误差之间的关系如下图。

![](../img/error_model_complexity.png)
50 51 52 53


### 训练数据集的大小

A
Aston Zhang 已提交
54
在本节的开头,我们同样提到一个学生可以有特定的训练量。类似地,一个机器学习模型的训练数据集的样本数也可大可小。一般来说,如果训练数据集过小,特别是比模型参数数量更小时,过拟合更容易发生。除此之外,泛化误差不会随训练数据集里样本数量增加而增大。
55

M
muli 已提交
56 57
![](../img/model_vs_data.png)

58 59 60 61
为了理解这两个因素对拟合和过拟合的影响,下面让我们来动手学习。

## 多项式拟合

M
muli 已提交
62
我们以多项式拟合为例。给定一个**标量**数据点集合`x`和对应的标量目标值`y`,多项式拟合的目标是找一个K阶多项式,其由向量`w`和位移`b`组成,来最好地近似每个样本`x``y`。用数学符号来表示就是我们将学`w``b`来预测
63 64 65 66 67 68 69 70 71

$$\hat{y} = b + \sum_{k=1}^K x^k w_k$$

并以平方误差为损失函数。特别地,一阶多项式拟合又叫线性拟合。

### 创建数据集

这里我们使用一个人工数据集来把事情弄简单些,因为这样我们将知道真实的模型是什么样的。具体来说我们使用如下的二阶多项式来生成每一个数据样本

72
$$y = 1.2x - 3.4x^2 + 5.6x^3 + 5.0 + \text{noise}$$
73 74 75

这里噪音服从均值0和标准差为0.1的正态分布。

A
Aston Zhang 已提交
76
需要注意的是,我们用以上相同的数据生成函数来生成训练数据集和测试数据集。两个数据集的样本数都是100。
77

M
muli 已提交
78
```{.python .input}
A
Aston Zhang 已提交
79 80 81 82 83
%matplotlib inline
import sys
sys.path.append('..')
import gluonbook as gb
from mxnet import autograd, gluon, nd
A
Aston Zhang 已提交
84
from mxnet.gluon import data as gdata, loss as gloss, nn
A
Aston Zhang 已提交
85 86 87 88 89
import matplotlib as mpl
import matplotlib.pyplot as plt

gb.set_fig_size(mpl)
```
90

A
Aston Zhang 已提交
91
```{.python .input}
A
Aston Zhang 已提交
92
n_train = 100
A
Aston Zhang 已提交
93
n_test = 100
94
true_w = [1.2, -3.4, 5.6]
95 96 97 98 99
true_b = 5.0
```

下面生成数据集。

M
muli 已提交
100
```{.python .input}
A
Aston Zhang 已提交
101 102 103 104 105 106
features = nd.random.normal(shape=(n_train + n_test, 1))
poly_features = nd.concat(features, nd.power(features, 2),
                          nd.power(features, 3))
labels = (true_w[0] * poly_features[:, 0] + true_w[1] * poly_features[:, 1]
          + true_w[2] * poly_features[:, 2] + true_b)
labels += 0.1 * nd.random.normal(shape=labels.shape)
A
Aston Zhang 已提交
107
```
108

A
Aston Zhang 已提交
109
```{.python .input}
A
Aston Zhang 已提交
110
features[:5], poly_features[:5], labels[:5]
A
Aston Zhang 已提交
111 112 113 114 115
```

```{.python .input}
num_epochs = 100
loss = gloss.L2Loss()
116 117
```

118 119 120 121 122 123
### 定义训练和测试步骤

我们定义一个训练和测试的函数,这样在跑不同的实验时不需要重复实现相同的步骤。

以下的训练步骤在[使用Gluon的线性回归](linear-regression-gluon.md)有过详细描述。这里不再赘述。

M
muli 已提交
124
```{.python .input}
A
Aston Zhang 已提交
125
def fit_and_plot(features_train, features_test, labels_train, labels_test):
A
Aston Zhang 已提交
126 127
    net = nn.Sequential()
    net.add(nn.Dense(1))
M
muli 已提交
128
    net.initialize()
A
Aston Zhang 已提交
129 130 131
    batch_size = min(10, labels_train.shape[0])
    train_iter = gdata.DataLoader(gdata.ArrayDataset(
        features_train, labels_train), batch_size, shuffle=True)
A
Aston Zhang 已提交
132 133
    trainer = gluon.Trainer(net.collect_params(), 'sgd',
                            {'learning_rate': 0.01})
A
Aston Zhang 已提交
134
    train_ls, test_ls = [], []
A
Aston Zhang 已提交
135
    for _ in range(num_epochs):
A
Aston Zhang 已提交
136
        for X, y in train_iter:
137
            with autograd.record():
A
Aston Zhang 已提交
138
                l = loss(net(X), y)
A
Aston Zhang 已提交
139
            l.backward()
140
            trainer.step(batch_size)
A
Aston Zhang 已提交
141 142 143 144
        train_ls.append(loss(net(features_train),
                             labels_train).mean().asscalar())
        test_ls.append(loss(net(features_test),
                            labels_test).mean().asscalar())
A
Aston Zhang 已提交
145 146
    plt.xlabel('epochs')
    plt.ylabel('loss')
A
Aston Zhang 已提交
147 148
    plt.semilogy(range(1, num_epochs+1), train_ls)
    plt.semilogy(range(1, num_epochs+1), test_ls)
M
muli 已提交
149 150
    plt.legend(['train','test'])
    plt.show()
A
Aston Zhang 已提交
151
    return ('weight:', net[0].weight.data(), 'bias:', net[0].bias.data())
152 153
```

M
muli 已提交
154
### 三阶多项式拟合(正常)
155

156
我们先使用与数据生成函数同阶的三阶多项式拟合。实验表明这个模型的训练误差和在测试数据集的误差都较低。训练出的模型参数也接近真实值。
157

M
muli 已提交
158
```{.python .input}
A
Aston Zhang 已提交
159 160
fit_and_plot(poly_features[:n_train, :], poly_features[n_train:, :],
             labels[:n_train], labels[n_train:])
161 162 163 164
```

### 线性拟合(欠拟合)

165
我们再试试线性拟合。很明显,该模型的训练误差很高。线性模型在非线性模型(例如三阶多项式)生成的数据集上容易欠拟合。
166

M
muli 已提交
167
```{.python .input}
A
Aston Zhang 已提交
168 169
fit_and_plot(features[:n_train, :], features[n_train:, :], labels[:n_train],
             labels[n_train:])
170 171 172 173
```

### 训练量不足(过拟合)

174
事实上,即便是使用与数据生成模型同阶的三阶多项式模型,如果训练量不足,该模型依然容易过拟合。让我们仅仅使用两个训练样本来训练。很显然,训练样本过少了,甚至少于模型参数的数量。这使模型显得过于复杂,以至于容易被训练数据集中的噪音影响。在机器学习过程中,即便训练误差很低,但是测试数据集上的误差很高。这是典型的过拟合现象。
175

M
muli 已提交
176
```{.python .input}
A
Aston Zhang 已提交
177 178
fit_and_plot(poly_features[0:2, :], poly_features[n_train:, :], labels[0:2],
             labels[n_train:])
179 180
```

181
我们还将在后面的章节继续讨论过拟合问题以及应对过拟合的方法,例如正则化。
182

A
Aston Zhang 已提交
183
## 小结
184 185

* 训练误差的降低并不一定意味着泛化误差的降低。
186
* 欠拟合和过拟合都是需要尽量避免的。我们要注意模型的选择和训练量的大小。
187 188 189 190


## 练习

A
Aston Zhang 已提交
191 192 193
* 学渣、学痞、学痴和学霸对应的模型复杂度、训练量、训练误差和泛化误差分别是怎样的?
* 如果用一个三阶多项式模型来拟合一个线性模型生成的数据,可能会有什么问题?为什么?
* 在我们本节提到的三阶多项式拟合问题里,有没有可能把1000个样本的训练误差的期望降到0,为什么?
S
Sheng Zha 已提交
194

A
Aston Zhang 已提交
195 196 197 198

## 扫码直达[讨论区](https://discuss.gluon.ai/t/topic/983)

![](../img/qr_underfit-overfit.svg)