Keras 中的 Adam 優化器(Optimizer)演算法+源碼研究

上篇文章《如何用 TensorFlow 實現 GAN》的代碼裡面用到了 Adam 優化器(Optimizer),深入研究了下,感覺很有趣,今天為大家分享一下,對理解深度學習訓練和權值學習過程、凸優化理論比較有幫助。

先看看上一篇用到的代碼(keras/examples/mnist_acgan.py):

# 手機上讀代碼時建議橫屏

# 從 keras.optimizers 導入 Adam 模塊

from keras.optimizers import Adam

# 創建鑒別器 (D) 和生成器 (G) 代碼略,見上一篇公眾號文章

# Adam 優化器超參(Hyper Parameter)

adam_lr = 0.0002

adam_beta_1 = 0.5

discriminator = build_discriminator()

discriminator.compile(

optimizer=Adam(lr=adam_lr, beta_1=adam_beta_1),

loss=[binary_crossentropy, sparse_categorical_crossentropy]

)

generator = build_generator(latent_size)

generator.compile(optimizer=Adam(lr=adam_lr, beta_1=adam_beta_1),

loss=binary_crossentropy)

上面代碼中,兩個模型 G 和 D 的優化器均選擇 Adam,超參數一樣,具體數值設置參考了【1】。

可能你會有疑問,什麼是優化器(Optimizer)?

很簡單~就是模型訓練的指導教練,能告訴模型權值該怎麼調整,調整多大量,最終目標是讓模型權值 theta 調整到最優,使得代價函數 f(theta) 最小。在所有優化演算法中,基於梯度(Gradient)的演算法最為常用,即首先計算 f(theta) 對 theta 的梯度 d(theta),theta 沿著梯度下降的方向進行調整。

Adam 也是一種基於基於梯度的優化演算法,該方法實現簡潔,計算高效,內存佔用少,適合非平穩目標函數,超參數有符合直覺的解釋,無需複雜的調參過程。

先看下 Adam 演算法的描述,需要閱讀文獻【2】。

Keras 對應的代碼實現位於 keras/keras/optimizers.py

# 手機上讀代碼時建議橫屏

class Adam(Optimizer):

"""

# 參數

lr: float >= 0. 學習速率、學習步長,值越大則表示權值調整動作越大,對應上圖演算法中的參數 alpha;

beta_1: 接近 1 的常數,(有偏)一階矩估計的指數衰減因子;

beta_2: 接近 1 的常數,(有偏)二階矩估計的指數衰減因子;

epsilon: 大於但接近 0 的數,放在分母,避免除以 0 ;

decay: 學習速率衰減因子,【2】演算法中無這個參數;

"""

def __init__(self, lr=0.001, beta_1=0.9, beta_2=0.999,

epsilon=1e-8, decay=0., **kwargs): # 【2】中的默認參數設置

super(Adam, self).__init__(**kwargs)

self.iterations = K.variable(0) # 迭代次數變數

self.lr = K.variable(lr) # 學習速率

self.beta_1 = K.variable(beta_1)

self.beta_2 = K.variable(beta_2)

self.epsilon = epsilon

self.decay = K.variable(decay)

self.initial_decay = decay

def get_updates(self, params, constraints, loss):

grads = self.get_gradients(loss, params) # 計算梯度 g_t

self.updates = [K.update_add(self.iterations, 1)] # 迭代次數加 1

lr = self.lr

if self.initial_decay > 0: # 如果初始學習速率衰減因子不為0,

# 則隨著迭代次數增加,學習速率將不斷減小

lr *= (1. / (1. + self.decay * self.iterations))

t = self.iterations + 1 # 就是公式中的 t

# 有偏估計到無偏估計的校正值

# 這裡將循環內的公共計算提到循環外面,提高速度

lr_t = lr * (K.sqrt(1. - K.pow(self.beta_2, t)) /

(1. - K.pow(self.beta_1, t)))

shapes = [K.get_variable_shape(p) for p in params] # 獲得權值形狀

ms = [K.zeros(shape) for shape in shapes] # 一階矩估計初始值

vs = [K.zeros(shape) for shape in shapes] # 二階矩估計初始值

self.weights = [self.iterations] + ms + vs

for p, g, m, v in zip(params, grads, ms, vs):

m_t = (self.beta_1 * m) + (1. - self.beta_1) * g # 一階矩估計

v_t = (self.beta_2 * v) + (1. - self.beta_2) * K.square(g) # 二階矩估計

p_t = p - lr_t * m_t / (K.sqrt(v_t) + self.epsilon) # 權值更新

self.updates.append(K.update(m, m_t))

self.updates.append(K.update(v, v_t))

new_p = p_t

# 對權值加約束

if p in constraints:

c = constraints[p]

new_p = c(new_p)

self.updates.append(K.update(p, new_p))

return self.updates

# 獲取當前超參數

def get_config(self):

config = {lr: float(K.get_value(self.lr)),

beta_1: float(K.get_value(self.beta_1)),

beta_2: float(K.get_value(self.beta_2)),

decay: float(K.get_value(self.decay)),

epsilon: self.epsilon}

base_config = super(Adam, self).get_config()

return dict(list(base_config.items()) + list(config.items()))

從上面實現可以看到,權值 theta 每個元素更新的調整幅度,與學習速率 alpha 成正比,與(無偏)一階矩估計值成正比,(無偏)二階矩估計值的平方根成反比。

文獻【2】 在 Adam 優化器基礎上提出了擴展的 Adamax 優化器,演算法上變化為將二階矩估計變為無窮階矩,感興趣的讀者可以繼續研究下演算法和 Keras 源碼實現。

Reference

【1】Alec Radford,Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks,[1511.06434] Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks

【2】Diederik, ADAM : A METHOD FOR STOCHASTIC OPTIMIZATION,A Method for Stochastic Optimization

如果你覺得本文對你有幫助,請關注公眾號,將來會有更多更好的文章推送!

weixin.qq.com/r/MDludlz (二維碼自動識別)


推薦閱讀:

如何看待ACL2017 所有掛arxiv但是沒有在提交時註明的paper全部被拒稿?
是否可把CNN用於一維的信號?
如何評價 Caffe2go?
梯度下降法的神經網路容易收斂到局部最優,為什麼應用廣泛?
人臉驗證任務中的目標方程為什麼還要加入分類誤差?

TAG:深度学习DeepLearning |