文章作者:Tyan
博客:noahsnail.com ?|? CSDN ?|? 簡書
注:本文為李沐大神的《動手學深度學習》的課程筆記其障!
欠擬合和過擬合
訓練誤差和泛化誤差
機器學習模型在訓練數(shù)據(jù)集上表現(xiàn)出的誤差叫做訓練誤差,在任意一個測試數(shù)據(jù)樣本上表現(xiàn)出的誤差的期望值叫做泛化誤差章鲤。
統(tǒng)計學習理論的一個假設是:訓練數(shù)據(jù)集和測試數(shù)據(jù)集里的每一個數(shù)據(jù)樣本都是從同一個概率分布中相互獨立地生成出的(獨立同分布假設)。
一個重要結論是:訓練誤差的降低不一定意味著泛化誤差的降低。機器學習既需要降低訓練誤差田盈,又需要降低泛化誤差。
欠擬合和過擬合
- 欠擬合:機器學習模型無法得到較低訓練誤差缴阎。
- 過擬合:機器學習模型的訓練誤差遠小于其在測試數(shù)據(jù)集上的誤差缠黍。
模型的選擇
模型擬合能力和誤差之間的關系如下圖:
error_model_complexity.png
訓練數(shù)據(jù)集的大小
一般來說,如果訓練數(shù)據(jù)集過小药蜻,特別是比模型參數(shù)數(shù)量更小時瓷式,過擬合更容易發(fā)生。除此之外语泽,泛化誤差不會隨訓練數(shù)據(jù)集里樣本數(shù)量增加而增大贸典。
model_vs_data.png
多項式擬合
給定一個標量數(shù)據(jù)點集合x
和對應的標量目標值y
,多項式擬合的目標是找一個K階多項式踱卵,其由向量w
和位移b
組成廊驼,來最好地近似每個樣本x
和y
。用數(shù)學符號來表示就是我們將學w
和b
來預測
$$\hat{y} = b + \sum_{k=1}^K x^k w_k$$
并以平方誤差為損失函數(shù)惋砂,一階多項式擬合又叫線性擬合妒挎。
創(chuàng)建數(shù)據(jù)集
使用二階多項式來生成每一個數(shù)據(jù)樣本黑竞,$y=1.2x?3.4x2+5.6x3+5.0+noise$缕粹,噪音服從均值0和標準差為0.1的正態(tài)分布茴扁。
# 導入mxnet
import mxnet as mx
# 設置隨機種子
mx.random.seed(2)
from mxnet import gluon
from mxnet import ndarray as nd
from mxnet import autograd
# 訓練數(shù)據(jù)數(shù)量
num_train = 100
# 測試數(shù)據(jù)數(shù)量
num_test = 100
# 多項式權重
true_w = [1.2, -3.4, 5.6]
# 多項式偏置
true_b = 5.0
# 生成隨機數(shù)據(jù)x
x = nd.random.normal(shape=(num_train + num_test, 1))
# 計算x的多項式值
X = nd.concat(x, nd.power(x, 2), nd.power(x, 3))
# 計算y
y = true_w[0] * X[:, 0] + true_w[1] * X[:, 1] + true_w[2] * X[:, 2] + true_b
# 查看數(shù)據(jù)
('x:', x[:5], 'X:', X[:5], 'y:', y[:5])
(200L,)
定義訓練和測試步驟
%matplotlib inline
import matplotlib as mpl
mpl.rcParams['figure.dpi']= 120
import matplotlib.pyplot as plt
# 定義訓練過程
def train(X_train, X_test, y_train, y_test):
# 定義線性回歸模型
net = gluon.nn.Sequential()
with net.name_scope():
net.add(gluon.nn.Dense(1))
# 權重初始化
net.initialize()
# 學習率
learning_rate = 0.01
# 迭代周期
epochs = 100
# 訓練的批數(shù)據(jù)大小
batch_size = min(10, y_train.shape[0])
# 創(chuàng)建訓練數(shù)據(jù)集
dataset_train = gluon.data.ArrayDataset(X_train, y_train)
# 讀取數(shù)據(jù)
data_iter_train = gluon.data.DataLoader(dataset_train, batch_size, shuffle=True)
# 訓練方法SGD
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': learning_rate})
# 定義損失函數(shù)
square_loss = gluon.loss.L2Loss()
# 訓練損失
train_loss = []
# 測試損失
test_loss = []
# 進行訓練
for e in range(epochs):
for data, label in data_iter_train:
with autograd.record():
# 進行預測
output = net(data)
# 計算預測值與實際值之間的損失
loss = square_loss(output, label)
# 損失進行反向傳播
loss.backward()
# 更新權重
trainer.step(batch_size)
# 保存訓練損失
train_loss.append(square_loss(net(X_train), y_train).mean().asscalar())
# 保存測試損失
test_loss.append(square_loss(net(X_test), y_test).mean().asscalar())
# 繪制損失
plt.plot(train_loss)
plt.plot(test_loss)
plt.legend(['train','test'])
plt.show()
return ('learned weight', net[0].weight.data(), 'learned bias', net[0].bias.data())
三階多項式擬合(正常)
train(X[:num_train, :], X[num_train:, :], y[:num_train], y[num_train:])
Figure 1
('learned weight',
[[ 1.22117233 -3.39606118 5.59531116]]
<NDArray 1x3 @cpu(0)>, 'learned bias',
[ 4.98550272]
<NDArray 1 @cpu(0)>)
線性擬合(欠擬合)
train(x[:num_train, :], x[num_train:, :], y[:num_train], y[num_train:])
Figure 2
('learned weight',
[[ 19.74101448]]
<NDArray 1x1 @cpu(0)>, 'learned bias',
[-0.23861444]
<NDArray 1 @cpu(0)>)
訓練量不足(過擬合)
train(X[0:2, :], X[num_train:, :], y[0:2], y[num_train:])
Figure 3
('learned weight',
[[ 3.10832024 -0.740421 4.85165691]]
<NDArray 1x3 @cpu(0)>, 'learned bias',
[ 0.29450524]
<NDArray 1 @cpu(0)>)
結論
- 訓練誤差的降低并不一定意味著泛化誤差的降低酌畜。
- 欠擬合和過擬合都是需要盡量避免的凡纳。我們要注意模型的選擇和訓練量的大小辅甥。