神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情。
而深度学习框架可以帮助我们自动地完成这种求梯度运算。
Tensorflow一般使用梯度磁带tf.GradientTape()
来记录正向运算过程,然后反播磁带自动得到梯度值。
这种利用tf.GradientTape求微分的方法叫做Tensorflow的自动微分机制。
一,利用梯度磁带求导数
- 自动“监控”所有可训练变量:GradientTape 默认(watch_accessed_variables=True)将所有可训练变量(created by
tf.Variable()
, where trainable=True)视为需要“监控”的变量。 - 对于不可训练的变量(比如tf.constant)可以使用tape.watch()对其进行“监控”。
- 此外,还可以设定watch_accessed_variables=False,然后使用tf.watch()精确控制需要“监控”哪些变量。
import tensorflow as tf
tf.enable_eager_execution()
import numpy as np
# f(x) = a*x**2 + b*x + c的导数
x = tf.Variable(0.0, name="x", dtype=tf.float32)
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)
# 构建梯度环境
with tf.GradientTape() as tape:
# 构建计算过程
y = a * tf.pow(x, 2) + b * x + c
# 求导
dy_dx = tape.gradient(y, x)
print(dy_dx)
输出
tf.Tensor(-2.0, shape=(), dtype=float32)
对常量张量也可以求导,需要增加watch()
with tf.GradientTape() as tape:
tape.watch([a,b,c])
y = a*tf.pow(x,2) + b*x + c
dy_dx,dy_da,dy_db,dy_dc = tape.gradient(y,[x,a,b,c])
print(dy_da)
print(dy_dc)
输出
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor(1.0, shape=(), dtype=float32)
可以求二阶导数
with tf.GradientTape() as tape2:
with tf.GradientTape() as tape1:
y = a*tf.pow(x,2) + b*x + c
dy_dx = tape1.gradient(y,x)
dy2_dx2 = tape2.gradient(dy_dx,x)
print(dy2_dx2)
输出
tf.Tensor(2.0, shape=(), dtype=float32)
二,利用梯度磁带和优化器求最小值
使用optimizer.apply_gradients()求最小值
# 求f(x) = a*x**2 + b*x + c的最小值
# 使用optimizer.apply_gradients
x = tf.Variable(0.0, name="x", dtype=tf.float32)
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
for _ in range(1000):
with tf.GradientTape() as tape:
y = a * tf.pow(x, 2) + b * x + c
dy_dx = tape.gradient(y, x)
optimizer.apply_gradients(grads_and_vars=[(dy_dx, x)])
tf.print("y =", y, "; x =", x)
输出
y = 0 ; x = 0.999998569
使用optimizer.minimize() 求最小值
# 求f(x) = a*x**2 + b*x + c的最小值
# 使用optimizer.apply_gradients
x = tf.Variable(0.0, name="x", dtype=tf.float32)
# 注意f()无参数
def f():
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)
y = a * tf.pow(x, 2) + b * x + c
return (y)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
for _ in range(1000):
optimizer.minimize(f)
tf.print("y =", f(), "; x =", x)
实际上根据官方文档的说明,minimize()
就是compute_gradients()
和apply_gradients()
这两个方法的简单组合,minimize()
的源码如下:
def minimize(self, loss, global_step=None, var_list=None,
gate_gradients=GATE_OP, aggregation_method=None,
colocate_gradients_with_ops=False, name=None,
grad_loss=None):
grads_and_vars = self.compute_gradients(
loss, var_list=var_list, gate_gradients=gate_gradients,
aggregation_method=aggregation_method,
colocate_gradients_with_ops=colocate_gradients_with_ops,
grad_loss=grad_loss)
vars_with_grad = [v for g, v in grads_and_vars if g is not None]
if not vars_with_grad:
raise ValueError(
"No gradients provided for any variable, check your graph for ops"
" that do not support gradients, between variables %s and loss %s." %
([str(v) for _, v in grads_and_vars], loss))
return self.apply_gradients(grads_and_vars, global_step=global_step,
name=name)
·compute_gradients()·: 根据loss目标函数计算梯度.
apply_gradients()
: 使用计算得到的梯度来更新对应的variable.
为什么minimize()
要分开两个步骤:
- 有时候需要对梯度做一定的修正,例如为了防止 gradient vanishing(梯度消失)或 gradient explosion (梯度爆炸),需要事先采取一些干预措施以避免程序出现nan情况。
- 有时候需要给计算得到的梯度乘以一个权重、或创建多GPU优化器、或其它操作。
#参考链接