tf.trainable_variables可以得到整個模型中所有trainable=True的Variable,也是自由處理梯度的基礎
基礎梯度操作方法:
tf.gradients
用來計算導數。該函數的定義如下所示
def gradients(ys, xs, grad_ys=None, name="gradients", colocate_gradients_with_ops=False, gate_gradients=False, aggregation_method=None):
雖然可選參數很多,但是最常使用的還是ys和xs。根據說明得知,ys和xs都可以是一個tensor或者tensor列表。而計算完成以后,該函數會返回一個長為len(xs)的tensor列表,列表中的每個tensor是ys中每個值對xs[i]求導之和。如果用數學公式表示的話,那么 g = tf.gradients(y,x)
可以表示成 ,
tf.clip_by_global_norm
修正梯度值,用於控制梯度爆炸的問題。梯度爆炸和梯度彌散的原因一樣,都是因為鏈式法則求導的關系,導致梯度的指數級衰減。為了避免梯度爆炸,需要對梯度進行修剪。
先來看這個函數的定義:
def clip_by_global_norm(t_list, clip_norm, use_norm=None, name=None):
輸入參數中:t_list為待修剪的張量, clip_norm 表示修剪比例(clipping ratio).
函數返回2個參數: list_clipped,修剪后的張量,以及global_norm,一個中間計算量。當然如果你之前已經計算出了global_norm值,你可以在use_norm選項直接指定global_norm的值。
那么具體如何計算呢?根據源碼中的說明,可以得到
list_clipped[i]=t_list[i] * clip_norm / max(global_norm, clip_norm),
其中 global_norm = sqrt(sum([l2norm(t)**2 for t in t_list]))
可以寫作
其中,
Lic和Lig代表t_list[i]和list_clipped[i],
Nc和Ng代表clip_norm 和 global_norm的值。
其實也可以看到其實Ng就是t_list的L2模。上式也可以進一步寫作
也就是說,當t_list的L2模大於指定的Nc時,就會對t_list做等比例縮放。
這里講解一下具體應用於優化器的方法,
self._lr = tf.Variable(0.0, trainable=False) # lr 指的是 learning_rate tvars = tf.trainable_variables() grads, _ = tf.clip_by_global_norm(tf.gradients(cost, tvars), config.max_grad_norm) # 梯度下降優化,指定學習速率 optimizer = tf.train.GradientDescentOptimizer(self._lr) # optimizer = tf.train.AdamOptimizer() # optimizer = tf.train.GradientDescentOptimizer(0.5) self._train_op = optimizer.apply_gradients(zip(grads, tvars)) # 將梯度應用於變量 # self._train_op = optimizer.minimize(grads)
優化器類處理法:
提取梯度,使用梯度優化變量,效果和上面的例子相同,
# 創建一個optimizer. opt = GradientDescentOptimizer(learning_rate=0.1) # 計算<list of variables>相關的梯度 grads_and_vars = opt.compute_gradients(loss, <list of variables>) # grads_and_vars為tuples (gradient, variable)組成的列表。 #對梯度進行想要的處理,比如cap處理 capped_grads_and_vars = [(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars] # 令optimizer運用capped的梯度(gradients) opt.apply_gradients(capped_grads_and_vars)