一、算法核心原理
????????RMSProp(Root Mean Square Propagation)是深度學習先驅Geoffrey Hinton在2012年提出的優化算法,它基于AdaGrad算法的改進,創新性地解決了傳統梯度下降法中學習率固定不變的局限性。該算法的核心機制在于采用指數加權移動平均(EWMA,詳情可參考連接:【深度學習】通俗易懂的基礎知識:指數加權平均)方法,實現了對參數更新幅度的自適應動態調整。
二、RMSProp算法的公式推導
? ? ? ? AdaGrad算法(【人工智能】神經網絡的優化器optimizer(二):Adagrad自適應學習率優化器)對于學習率的動態調整設計思路:把全部歷史梯度一視同仁全部打包進行平方和,導致學習提早結束。
????????RMSProp算法對于學習率的動態調整設計思路:通過將Momentum動量優化器(【人工智能】神經網絡的優化器optimizer(一):Momentum動量優化器)中采用的指數加權平均(EWMA)應用到學習率動態調整中,以便于通過指數衰減將近期數據賦予高權重,歷史數據逐漸“遺忘”,從而產生類似“滑動”的機制,避免學習率過早衰減的問題。
????????????????????????????????????????????????
????????其中:
-
:當前時刻的加權平均值
-
:上一時刻的加權平均值(初始值?
)
-
:想要觀察的時刻?t 的值,在該文章中代表 t 時間的梯度
-
:衰減因子(0<β<1),控制歷史數據的權重分布?
????????RMSProp為每個參數維護一個狀態變量(通常記作 V),用于記錄梯度平方的指數衰減平均值,剩下的其他公式就和AdaGrad算法是一樣的了:
????????????????????????????????????????????????????????????????????????????
? ? ? ? 其算法的特點也和AdaGrad算法差不多:
-
?分母設計?:
近似為梯度的均方根(Root Mean Square),反映歷史梯度的幅值
-
?自適應效果?:
-
梯度方向振蕩劇烈(
較大)→ 學習率降低,抑制震蕩
-
梯度方向平緩(
較小)→ 學習率增大,加速收斂?
-
-
穩定常數:
(如1e-8)用于數值穩定,防止分母為零?
三、RMSProp算法和AdaGrad算法的對比
?特性? | ?RMSProp? | ?AdaGrad? |
---|---|---|
歷史梯度累積方式 | 指數衰減加權平均 | 全局累積 |
學習率衰減趨勢 | 動態平衡,避免過早趨近零 | 單調遞減,后期更新停滯 |
適用場景 | 非平穩目標、深層網絡、RNN? | 稀疏數據、淺層網絡 |
????????根據上述內容可以總結出幾個特點:
-
解決AdaGrad缺陷?:AdaGrad累積所有歷史梯度導致后期學習率過小,RMSProp通過衰減系數?γ 削弱早期梯度的影響,使學習率在訓練中保持有效調整能力?;
-
?適應非平穩目標?:在RNN等動態系統中,梯度分布隨時間變化,RMSProp的指數衰減能更快響應近期變化?;
-
?超參數影響?:γγ?過大易忽略新梯度信息,過小則退化為類似AdaGrad,通常取0.9經驗值?。
????????綜上所述,RMSProp的衰減機制通過加權平均平衡歷史與當前梯度,實現學習率的穩定自適應調整,顯著提升非凸優化問題的訓練效率?。
四、代碼實現
? ? ? ? 以下是使用python編寫的兩個算法的動畫對比圖,可以觀察到RMSProp算法相比AdaGrad算法會快速往原點滑動,然后在原點附近來回滑動,通過修改超參可以調整算法的步幅以及方向,建議各位自行嘗試:
? ? ? ? 代碼源碼如下:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation# 生成樣本數據
np.random.seed(42)
sample_data = np.random.randn(100, 2) * 2# 定義測試函數
def loss_func(x, y):return 0.1 * x ** 2 + 2 * y ** 2 + np.sin(x) * np.cos(y)# 優化器實現
class AdaGrad:def __init__(self, params, lr=0.1):self.params = params.copy()self.lr = lrself.cache = {k: 0 for k in params.keys()}self.history = {k: [v] for k, v in params.items()}def step(self, grads):for key in self.params:self.cache[key] += grads[key] ** 2self.params[key] -= self.lr * grads[key] / (np.sqrt(self.cache[key]) + 1e-8)self.history[key].append(self.params[key])class RMSProp:def __init__(self, params, lr=0.1, gamma=0.9):self.params = params.copy()self.lr = lrself.gamma = gammaself.cache = {k: 0 for k in params.keys()}self.history = {k: [v] for k, v in params.items()}def step(self, grads):for key in self.params:self.cache[key] = self.gamma * self.cache[key] + (1 - self.gamma) * grads[key] ** 2self.params[key] -= self.lr * grads[key] / (np.sqrt(self.cache[key]) + 1e-8)self.history[key].append(self.params[key])# 初始化優化器
initial_params = {'x': -4, 'y': 4}
adagrad = AdaGrad(initial_params.copy())
rmsprop = RMSProp(initial_params.copy())# 創建可視化
fig = plt.figure(figsize=(12, 5))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)# 繪制樣本數據
ax1.scatter(sample_data[:, 0], sample_data[:, 1], c='black', s=10)
ax2.scatter(sample_data[:, 0], sample_data[:, 1], c='black', s=10)# 繪制等高線
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = loss_func(X, Y)
ax1.contour(X, Y, Z, levels=20, alpha=0.5)
ax2.contour(X, Y, Z, levels=20, alpha=0.5)# 動畫更新函數
def update(frame):# 計算梯度x_adagrad, y_adagrad = adagrad.params['x'], adagrad.params['y']grads = {'x': 0.2 * x_adagrad + np.cos(x_adagrad) * np.cos(y_adagrad),'y': 4 * y_adagrad - np.sin(x_adagrad) * np.sin(y_adagrad)}adagrad.step(grads)x_rmsprop, y_rmsprop = rmsprop.params['x'], rmsprop.params['y']grads = {'x': 0.2 * x_rmsprop + np.cos(x_rmsprop) * np.cos(y_rmsprop),'y': 4 * y_rmsprop - np.sin(x_rmsprop) * np.sin(y_rmsprop)}rmsprop.step(grads)# 更新軌跡ax1.plot(adagrad.history['x'], adagrad.history['y'], 'b-', lw=1)ax2.plot(rmsprop.history['x'], rmsprop.history['y'], 'r-', lw=1)return ax1, ax2# 運行動畫
ani = FuncAnimation(fig, update, frames=100, interval=200)
ax1.set_title('AdaGrad (Blue)')
ax2.set_title('RMSProp (Red)')
plt.tight_layout()
plt.show()