摘要:介紹權重衰減的基本原理,及其和正則化的不同,並分別給出正則化和權重衰減的Tensorflow實現方式。使用兩者能夠等效的隨機梯度下降作爲例子說明兩者的不同。在使用一些自適應優化算法時最好使用解耦的權重衰減而不是正則化。
目錄
-
基本原理
-
正則化實現
-
解耦權重衰減實現
-
小結
主要參考文獻
【1】“Comparing biases for minimal network construction with back-propagation”
【2】“Decoupled Weight Decay Regularization”
1. 基本原理
由參考文獻【1】,權重衰減的表示形式爲:
其中爲權重衰減的比率,爲學習率。
一般深度學習框架及很多書本中將權重衰減等效爲正則化。這在隨機梯度下降中是成立的,只要在代價函數中增加一項:,兩者在數學上就是等價的。
但在其他自適應學習率的方法中,其實不能簡單地將正則化當成權重衰減來使用,這就導致在實際使用中自適應優化算法通常會比隨機梯度下降的泛化精度差很多。
參考文獻【2】提出一種解耦的權重衰減方式,能在自適應方法中實現權重衰減。本質就是迴歸權重衰減的最初形式,在權重更新的步驟中進行衰減,而不是在代價函數中加上正則化。
下圖爲文獻【2】給出的權重衰減以及正則化下的隨機梯度下降算法,下文給出具體實現。
2. 正則化實現
使用Tensorflow框架,底層設計隨機梯度下降算法,並計算代價函數和範數。
下面按照準備數據——選擇模型——計算代價函數、梯度,進行訓練的順序進行實現。
首先準備高維迴歸數據集,特徵數量遠高於訓練樣本數,十分容易過擬合。
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
num_train, num_test = 20, 100
num_features = 200
true_w, true_b = tf.ones((num_features, 1)) * 0.01, 0.05
features = tf.random.normal((num_train + num_test, num_features))
noises = tf.random.normal((num_train + num_test, 1)) * 0.01
labels = tf.matmul(features, true_w) + tf.convert_to_tensor(true_b) + noises
train_data, test_data = features[:num_train, :], features[num_train:, :]
train_labels, test_labels = labels[:num_train], labels[num_train:]
其次選擇模型類型,這裏使用線性模型。
class Model(object):
def __init__(self):
self.W = tf.Variable(np.random.random((200, 1)), dtype=tf.float32)
self.b = tf.Variable(0.0)
def __call__(self, x):
return tf.matmul(x, self.W) + self.b
然後定義代價函數和梯度,對模型進行訓練。代價函數採用均方誤差。
這部分是正則化和權重衰減的主要區別所在。
首先我們使用比率爲0的正則化,觀察一下過擬合。
def loss(y_true, y_pred):
return tf.reduce_mean(tf.square(y_true - y_pred))
def l2_reg(ws):
return tf.norm(ws) ** 2
def train(model, inputs, outputs, lr):
with tf.GradientTape() as g:
current_loss = loss(outputs, model(inputs)) + 0 * l2_reg(model.W)
grad_w, grad_b = g.gradient(current_loss, [model.W, model.b])
model.W.assign_sub(lr * grad_w)
model.b.assign_sub(lr * grad_b)
model = Model()
ws, bs = [], []
train_losses, test_losses = [], []
epochs = range(120)
for epoch in epochs:
ws.append(model.W.numpy())
bs.append(model.b.numpy())
train(model, train_data, train_labels, 0.01)
train_losses.append(loss(train_labels, model(train_data)))
test_losses.append(loss(test_labels, model(test_data)))
觀察過擬合時的訓練損失和驗證損失之間的變化。可以看出,訓練損失較小但驗證損失一直很大,明顯的過擬合現象。
下面將正則化的比率調爲1,觀察訓練損失和驗證損失。此時隨着訓練的進行,驗證損失慢慢也和訓練損失一樣小。正則化確實能有效緩解過擬合。
current_loss = loss(outputs, model(inputs)) + 1 * l2_reg(model.W)
3. 解耦權重衰減實現
權重衰減的比率和正則化比率之間的關係如下,因此比率爲1的的正則化對應0.02的權重衰減。
程序大體相同,改動如下,即將代價函數中的範數刪除,然後在權重更新時減去權重衰減項。
current_loss = loss(outputs, model(inputs))
# ...
model.W.assign_sub(lr * grad_w + 0.02 * model.W)
此時的訓練和驗證損失如下圖,和正則化起的效果基本相同。
4. 小結
-
權重衰減,以及正則化均能有效緩解過擬合。
-
隨機梯度下降中正則化和權重衰減等價。
-
其他自適應算法中兩者不等價,要實現權重衰減只能用原始定義。