權重衰減是應對過擬合問題的常用方法。
\(L_2\)范數正則化
在深度學習中,我們常使用L2范數正則化,也就是在模型原先損失函數基礎上添加L2范數懲罰項,從而得到訓練所需要最小化的函數。
L2范數懲罰項指的是模型權重參數每個元素的平方和與一個超參數的乘積。如:\(w_1\),\(w_2\)是權重參數,b是偏差參數,帶\(L_2\)范數懲罰項的新損失函數為:
\(\lambda\)調節了懲罰項的比重。
有了\(L_2\)范數后,在隨機梯度下降中,以單層神經網絡為例,權重的迭代公式變更為:
\(\eta\)為學習率,\(\mathcal{B}\)為樣本數目,可見:\(L_2\)范數正則化令權重的每一步更新分別添加了\(-\lambda w_1\)和\(\lambda w_2\)。這就是\(L_2\)范數正則化被稱為權重衰減(weight decay)的原因。
在實際中,我們有時也在懲罰項中添加偏差元素的平方和。
假設神經網絡中某一個神經元的輸入是\(x_1,x_2\),使用激活函數\(\phi\)並輸出\(\phi(x_1w_1+x_2w_2+b)\)。假設激活函數\(\phi\)是ReLU、tanh或sigmoid,如果\(w_1,w_2,b\)都非常接近0,那么輸出也接近0。也就是說,這個神經元的作用比較小,甚至就像是令神經網絡少了一個神經元一樣。這樣便有效降低了模型的復雜度,降低了過擬合。
高維線性回歸實驗
我們通過高維線性回歸為例來引入一個過擬合問題,並使用L2范數正則化來試着應對過擬合。
生成數據集
設數據樣本特征的維度為p。對於訓練數據集和測試數據集中特征為\(x_1,x_2,…,x_n\)的任一樣本,我們使用如下的線性函數來生成該樣本的標簽:
其中噪音項ϵ服從均值為0和標准差為0.1的正態分布。
為了較容易地觀察過擬合,我們考慮高維線性回歸問題,例如設維度p=200;同時,我們特意把訓練數據集的樣本數設低,例如20。
n_train = 20
n_test = 100
num_inputs = 200
true_w = nd.ones((num_inputs, 1)) * 0.01
true_b = 0.05
features = nd.random.normal(shape=(n_train+n_test, num_inputs))
labels = nd.dot(features, true_w) + true_b
labels += nd.random.normal(scale=0.01, shape=labels.shape)
train_features, test_features = features[:n_train, :], features[n_train:, :]
train_labels, test_labels = labels[:n_train], labels[n_train:]
初始化模型參數
def init_params():
w = nd.random.normal(scale=1, shape=(num_inputs, 1))
b = nd.zeros(shape=(1,))
params = [w, b]
for param in params:
param.attach_grad()
return params
定義L2范數懲罰項
def l2_penalty(w):
return (w**2).sum() / 2
定義訓練和測試
batch_size = 1
num_epochs = 10
lr = 0.003
net = gb.linreg
loss = gb.squared_loss
def fit_and_plot(lambd):
w, b = params = init_params()
train_ls = []
test_ls = []
for _ in range(num_epochs):
for X, y in gb.data_iter(batch_size, n_train, features, labels):
with autograd.record():
# 添加了 L2 范數懲罰項。
l = loss(net(X, w, b), y) + lambd * l2_penalty(w)
l.backward()
gb.sgd(params, lr, batch_size)
train_ls.append(loss(net(train_features, w, b),
train_labels).mean().asscalar())
test_ls.append(loss(net(test_features, w, b),
test_labels).mean().asscalar())
gb.semilogy(range(1, num_epochs+1), train_ls, 'epochs', 'loss',
range(1, num_epochs+1), test_ls, ['train', 'test'])
return 'w[:10]:', w[:10].T, 'b:', b
設置lambd=0,訓練誤差遠小於測試(泛化)誤差,這是典型的過擬合現象。
fit_and_plot(lambd=0)
# output
('w[:10]:',
[[ 0.30343655 -0.08110731 0.64756584 -1.51627898 0.16536537 0.42101485
0.41159022 0.8322348 -0.66477555 3.56285167]]
<NDArray 1x10 @cpu(0)>, 'b:',
[ 0.12521751]
<NDArray 1 @cpu(0)>)
使用正則化,過擬合現象得到一定程度上的緩解。然而依然沒有學出較准確的模型參數。這主要是因為訓練數據集的樣本數相對維度來說太小。
fit_and_plot(lambd=5)
# output
('w[:10]:',
[[ 0.01602661 -0.00279179 0.03075662 -0.07356022 0.01006496 0.02420521
0.02145572 0.04235912 -0.03388886 0.17112994]]
<NDArray 1x10 @cpu(0)>, 'b:',
[ 0.08771407]
<NDArray 1 @cpu(0)>)