underfit-overfit.md 9.3 KB
Newer Older
1 2
# 欠拟合和过拟合

A
revise  
Aston Zhang 已提交
3
在前几节基于Fashion-MNIST数据集的实验中,我们评价了模型在训练数据集和测试数据集上的表现。如果你动手改变过实验中的模型结构或者超参数的话,你也许发现了:当模型在训练数据集上更准确时,在测试数据集上的准确率既可能上升又可能下降。这是为什么呢?
4

A
revise  
Aston Zhang 已提交
5 6

## 训练误差和泛化误差
7

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

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

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

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

16
基于以上独立同分布假设,给定任意一个机器学习模型及其参数,它的训练误差的期望值和泛化误差都是一样的。然而从之前的章节中我们了解到,在机器学习的过程中,模型的参数并不是事先给定的,而是通过训练数据学习得出的:模型的参数在训练中使训练误差不断降低。所以,如果模型参数是通过训练数据学习得出的,那么训练误差的期望值无法高于泛化误差。换句话说,通常情况下,由训练数据学到的模型参数会使模型在训练数据上的表现不差于在测试数据上的表现。
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

因此,一个重要结论是:

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

## 欠拟合和过拟合

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

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

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

### 模型的选择

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

![](../img/error_model_complexity.png)
36 37 38 39


### 训练数据集的大小

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

M
muli 已提交
42 43
![](../img/model_vs_data.png)

44 45 46 47
为了理解这两个因素对拟合和过拟合的影响,下面让我们来动手学习。

## 多项式拟合

M
muli 已提交
48
我们以多项式拟合为例。给定一个**标量**数据点集合`x`和对应的标量目标值`y`,多项式拟合的目标是找一个K阶多项式,其由向量`w`和位移`b`组成,来最好地近似每个样本`x``y`。用数学符号来表示就是我们将学`w``b`来预测
49 50 51 52 53 54 55 56 57

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

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

### 创建数据集

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

58
$$y = 1.2x - 3.4x^2 + 5.6x^3 + 5.0 + \text{noise}$$
59 60 61

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

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

M
muli 已提交
64
```{.python .input}
A
Aston Zhang 已提交
65 66 67 68 69 70
%matplotlib inline
import sys
sys.path.append('..')
import gluonbook as gb
import matplotlib as mpl
import matplotlib.pyplot as plt
A
Aston Zhang 已提交
71 72
from mxnet import autograd, gluon, nd
from mxnet.gluon import data as gdata, loss as gloss, nn
A
Aston Zhang 已提交
73
```
74

A
Aston Zhang 已提交
75
```{.python .input}
A
Aston Zhang 已提交
76
n_train = 100
A
Aston Zhang 已提交
77
n_test = 100
78
true_w = [1.2, -3.4, 5.6]
79 80 81 82 83
true_b = 5.0
```

下面生成数据集。

M
muli 已提交
84
```{.python .input}
A
Aston Zhang 已提交
85 86 87 88 89
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)
A
Aston Zhang 已提交
90
labels += nd.random.normal(scale=0.1, shape=labels.shape)
A
Aston Zhang 已提交
91
```
92

A
Aston Zhang 已提交
93
```{.python .input}
A
Aston Zhang 已提交
94
features[:5], poly_features[:5], labels[:5]
A
Aston Zhang 已提交
95 96 97 98 99
```

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

102 103 104 105 106 107
### 定义训练和测试步骤

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

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

M
muli 已提交
108
```{.python .input}
A
Aston Zhang 已提交
109 110
gb.set_fig_size(mpl)

A
Aston Zhang 已提交
111
def fit_and_plot(train_features, test_features, train_labels, test_labels):
A
Aston Zhang 已提交
112 113
    net = nn.Sequential()
    net.add(nn.Dense(1))
M
muli 已提交
114
    net.initialize()
A
Aston Zhang 已提交
115
    batch_size = min(10, train_labels.shape[0])
A
Aston Zhang 已提交
116
    train_iter = gdata.DataLoader(gdata.ArrayDataset(
A
Aston Zhang 已提交
117
        train_features, train_labels), batch_size, shuffle=True)
A
Aston Zhang 已提交
118 119
    trainer = gluon.Trainer(net.collect_params(), 'sgd',
                            {'learning_rate': 0.01})
A
Aston Zhang 已提交
120
    train_ls, test_ls = [], []
A
Aston Zhang 已提交
121
    for _ in range(num_epochs):
A
Aston Zhang 已提交
122
        for X, y in train_iter:
123
            with autograd.record():
A
Aston Zhang 已提交
124
                l = loss(net(X), y)
A
Aston Zhang 已提交
125
            l.backward()
126
            trainer.step(batch_size)
A
Aston Zhang 已提交
127 128 129 130
        train_ls.append(loss(net(train_features),
                             train_labels).mean().asscalar())
        test_ls.append(loss(net(test_features),
                            test_labels).mean().asscalar())
A
Aston Zhang 已提交
131 132
    plt.xlabel('epochs')
    plt.ylabel('loss')
A
Aston Zhang 已提交
133 134
    plt.semilogy(range(1, num_epochs+1), train_ls)
    plt.semilogy(range(1, num_epochs+1), test_ls)
M
muli 已提交
135 136
    plt.legend(['train','test'])
    plt.show()
A
Aston Zhang 已提交
137
    return ('weight:', net[0].weight.data(), 'bias:', net[0].bias.data())
138 139
```

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

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

M
muli 已提交
144
```{.python .input}
A
Aston Zhang 已提交
145 146
fit_and_plot(poly_features[:n_train, :], poly_features[n_train:, :],
             labels[:n_train], labels[n_train:])
147 148 149 150
```

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

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

M
muli 已提交
153
```{.python .input}
A
Aston Zhang 已提交
154 155
fit_and_plot(features[:n_train, :], features[n_train:, :], labels[:n_train],
             labels[n_train:])
156 157 158 159
```

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

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

M
muli 已提交
162
```{.python .input}
A
Aston Zhang 已提交
163 164
fit_and_plot(poly_features[0:2, :], poly_features[n_train:, :], labels[0:2],
             labels[n_train:])
165 166
```

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

A
Aston Zhang 已提交
169
## 小结
170 171

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


## 练习

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

A
Aston Zhang 已提交
181 182 183 184

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

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