RMSProp算法
在AdaGrad算法中,因為調整學習率時分母上的變量 s t \boldsymbol{s}_t st一直在累加按元素平方的小批量随機梯度,是以目标函數自變量每個元素的學習率在疊代過程中一直在降低(或不變)。是以,當學習率在疊代早期降得較快且目前解依然不佳時,AdaGrad算法在疊代後期由于學習率過小,可能較難找到一個有用的解。為了解決這一問題,RMSProp算法對AdaGrad算法做了一點小小的修改。
算法内容
之前說過指數權重移動平均。不同于AdaGrad算法裡狀态變量 s t \boldsymbol{s}_t st是截至時間步 t t t所有小批量随機梯度 g t \boldsymbol{g}_t gt按元素平方和,RMSProp算法将這些梯度按元素平方做指數權重移動平均。
具體來說,給定超參數 0 ≤ γ < 1 0 \leq \gamma < 1 0≤γ<1,RMSProp算法在時間步 t > 0 t>0 t>0計算
s t ← γ s t − 1 + ( 1 − γ ) g t ⊙ g t . \boldsymbol{s}_t \leftarrow \gamma \boldsymbol{s}_{t-1} + (1 - \gamma) \boldsymbol{g}_t \odot \boldsymbol{g}_t. st←γst−1+(1−γ)gt⊙gt.
RMSProp算法将目标函數自變量中每個元素的學習率通過按元素運算重新調整,然後更新自變量,這是和AdaGrad算法一樣的梯度下降公式:
x t ← x t − 1 − η s t + ϵ ⊙ g t , \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon}} \odot \boldsymbol{g}_t, xt←xt−1−st+ϵ
η⊙gt,
其中
- η \eta η是學習率
- ϵ \epsilon ϵ是為了維持數值穩定性而添加的常數,如 1 0 − 6 10^{-6} 10−6。
因為RMSProp算法的狀态變量 s t \boldsymbol{s}_t st是對平方項 g t ⊙ g t \boldsymbol{g}_t \odot \boldsymbol{g}_t gt⊙gt的指數權重移動平均,是以可以看作是最近 1 / ( 1 − γ ) 1/(1-\gamma) 1/(1−γ)個時間步的小批量随機梯度平方項的權重平均。如此一來,自變量每個元素的學習率在疊代過程中就不再一直降低(或不變)。
還是使用相同的例子
- 目标函數 f ( x ) = 0.1 x 1 2 + 2 x 2 2 f(\boldsymbol{x})=0.1x_1^2+2x_2^2 f(x)=0.1x12+2x22
- 學習率為0.4
在AdaGrad算法中,自變量在疊代後期的移動幅度較小。但在同樣的學習率下,RMSProp算法可以更快逼近最優解。
from matplotlib import pyplot as plt
def show_trace_2d(f, results):
plt.plot(*zip(*results), '-o', color='#ff7f0e')
x1, x2 = np.meshgrid(np.arange(-5.5, 1.0, 0.1), np.arange(-3.0, 1.0, 0.1))
plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')
plt.xlabel('x1')
plt.ylabel('x2')
def train_2d(trainer):
x1, x2, s1, s2 = -5, -2, 0, 0 # s1和s2是自變量狀态,本章後續幾節會使用
results = [(x1, x2)]
for i in range(20):
x1, x2, s1, s2 = trainer(x1, x2, s1, s2)
results.append((x1, x2))
print('epoch %d, x1 %f, x2 %f' % (i + 1, x1, x2))
return results
%matplotlib inline
import math
import torch
def rmsprop_2d(x1, x2, s1, s2):
g1, g2, eps = 0.2 * x1, 4 * x2, 1e-6
s1 = gamma * s1 + (1 - gamma) * g1 ** 2
s2 = gamma * s2 + (1 - gamma) * g2 ** 2
x1 -= eta / math.sqrt(s1 + eps) * g1
x2 -= eta / math.sqrt(s2 + eps) * g2
return x1, x2, s1, s2
def f_2d(x1, x2):
return 0.1 * x1 ** 2 + 2 * x2 ** 2
eta, gamma = 0.4, 0.9
show_trace_2d(f_2d, train_2d(rmsprop_2d))
實作RMSProp算法
def get_data_ch7():
data = np.genfromtxt('data/airfoil_self_noise.dat', delimiter='\t')
data = (data - data.mean(axis=0)) / data.std(axis=0)
return torch.tensor(data[:1500, :-1], dtype=torch.float32), \
torch.tensor(data[:1500, -1], dtype=torch.float32) # 前1500個樣本(每個樣本5個特征)
features, labels = get_data_ch7()
def init_rmsprop_states():
s_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
s_b = torch.zeros(1, dtype=torch.float32)
return (s_w, s_b)
def rmsprop(params, states, hyperparams):
gamma, eps = hyperparams['gamma'], 1e-6
for p, s in zip(params, states):
s.data = gamma * s.data + (1 - gamma) * (p.grad.data)**2
p.data -= hyperparams['lr'] * p.grad.data / torch.sqrt(s + eps)
将初始學習率設為0.01,并将超參數 γ \gamma γ設為0.9。此時,變量 s t \boldsymbol{s}_t st可看作是最近 1 / ( 1 − 0.9 ) = 10 1/(1-0.9) = 10 1/(1−0.9)=10個時間步的平方項 g t ⊙ g t \boldsymbol{g}_t \odot \boldsymbol{g}_t gt⊙gt的權重平均。
def train_ch7(optimizer_fn, states, hyperparams, features, labels,
batch_size=10, num_epochs=2):
# 初始化模型
net, loss = linreg, squared_loss
w = torch.nn.Parameter(torch.tensor(np.random.normal(0, 0.01, size=(features.shape[1], 1)), dtype=torch.float32),
requires_grad=True)
b = torch.nn.Parameter(torch.zeros(1, dtype=torch.float32), requires_grad=True)
def eval_loss():
return loss(net(features, w, b), labels).mean().item()
ls = [eval_loss()]
data_iter = torch.utils.data.DataLoader(
torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)
for _ in range(num_epochs):
start = time.time()
for batch_i, (X, y) in enumerate(data_iter):
l = loss(net(X, w, b), y).mean() # 使用平均損失
# 梯度清零
if w.grad is not None:
w.grad.data.zero_()
b.grad.data.zero_()
l.backward()
optimizer_fn([w, b], states, hyperparams) # 疊代模型參數
if (batch_i + 1) * batch_size % 100 == 0:
ls.append(eval_loss()) # 每100個樣本記錄下目前訓練誤差
# 列印結果和作圖
print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
set_figsize()
plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
plt.xlabel('epoch')
plt.ylabel('loss')
亦可以使用pytorch内置的optim.RMSProp算法來實作:
train_pytorch_ch7(torch.optim.RMSprop, {'lr': 0.01, 'alpha': 0.9},
features, labels)