赞
踩
神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情。
而深度学习框架可以帮助我们自动地完成这种求梯度运算。
Tensorflow一般使用梯度磁带tf.GradientTape来记录正向运算过程,然后反播磁带自动得到梯度值。
这种利用tf.GradientTape求微分的方法叫做Tensorflow的自动微分机制。
Python 中的 with 语句用于异常处理,封装了 try…except…finally 编码范式,提高了易用性。
with func() as a:
do some thing...
可理解为做了:
a=func()
try{
do some thing...
}
except{
做了些异常处理
}
finally {
做了些收尾工作
}
详见:菜鸟课程with使用
其次,通常而言,在编程语言中,变量的作用域从代码结构形式来看,有块级、函数、类、模块、包等由小到大的级别。但是在Python中,没有块级作用域,也就是类似if语句块、for语句块、with上下文管理器等等是不存在作用域概念的。所以,with里面定义的变量,可以再with外部使用。
详见:python作用域
1.1 可对变量x求导 |
import tensorflow as tf 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)
1.2 对常量求导 |
# 对常量张量也可以求导,需要增加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)
1.3 二阶导数 |
# 可以求二阶导数
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)
1.4 在autograph中使用 |
@tf.function def f(x): a = tf.constant(1.0) b = tf.constant(-2.0) c = tf.constant(1.0) # 自变量转换成tf.float32 x = tf.cast(x,tf.float32) with tf.GradientTape() as tape: tape.watch(x) y = a*tf.pow(x,2)+b*x+c dy_dx = tape.gradient(y,x) return((dy_dx,y)) tf.print(f(tf.constant(0.0))) tf.print(f(tf.constant(1.0))) (-2, 1) (0, 0)
2.1 使用optimizer.apply_gradients |
# 求f(x) = a*x**2 + b*x + c的最小值 # 使用optimizer.apply_gradients import tensorflow as tf 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) lr=0.01 optimizer = tf.keras.optimizers.SGD(learning_rate=lr) for step in range(200): with tf.GradientTape() as tape: y = a*tf.pow(x,2) + b*x + c dy_dx = tape.gradient(y,x) if step % 20 == 0: tf.print("step =",step,"; y =",y,"; x =",x) optimizer.apply_gradients(grads_and_vars=[(dy_dx,x)])#optimizer.apply_gradients(zip(dy_dx,x)) #高维要用zip #x.assign_sub(lr*dy_dx)#结果等价 tf.print("y =",y,"; x =",x) #输出: step = 0 ; y = 1 ; x = 0 step = 20 ; y = 0.445700467 ; x = 0.332391977 step = 40 ; y = 0.19864893 ; x = 0.554299474 step = 60 ; y = 0.0885379314 ; x = 0.702446759 step = 80 ; y = 0.0394613743 ; x = 0.80135107 step = 100 ; y = 0.0175879598 ; x = 0.867380381 step = 120 ; y = 0.00783896446 ; x = 0.911462188 step = 140 ; y = 0.00349384546 ; x = 0.940891385 step = 160 ; y = 0.00155723095 ; x = 0.960538507 step = 180 ; y = 0.000694036484 ; x = 0.973655164 y = 0.0003221035 ; x = 0.98241204
如果我们想在模型更新前对梯度搞一些自定义的操作,TensorFlow中推荐的方式是
x.assign_sub(lr*dy_dx)
)第1点,用optimizers.compute_gradients的话,会报错:
return super(OptimizerV2, self).__getattribute__(name)
AttributeError: 'SGD' object has no attribute 'compute_gradients'
更新模型参数的方法 optimizer.apply_gradients()
需要提供参数 grads_and_vars
,即待更新的变量(如上述代码中的 variables )及损失函数关于这些变量的偏导数(如上述代码中的 grads )。具体而言,这里需要传入一个 Python 列表(List),列表中的每个元素是一个 (变量的偏导数,变量)
对。如果上例中b
也是变量,则需要传入的参数是 [(grad_x, x), (grad_b, b)]
。我们通过grads = tape.gradient(loss, variables)
求出 tape 中记录的 loss 关于variables = [x, b]
中每个变量的偏导数,也就是 grads = [grad_x, grad_b]
,再使用 Python 的 zip()
函数将 grads = [grad_x, grad_b] 和 variables = [x, b]
拼装在一起,就可以组合出所需的参数了。
import tensorflow as tf import numpy as np X_raw = np.array([2013, 2014, 2015, 2016, 2017], dtype=np.float32) y_raw = np.array([12000, 14000, 15000, 16500, 17500], dtype=np.float32) X = tf.constant(X) y = tf.constant(y) a = tf.Variable(initial_value=0.) b = tf.Variable(initial_value=0.) variables = [a, b] num_epoch = 10000 optimizer = tf.keras.optimizers.SGD(learning_rate=5e-4) #优化器可以帮助我们根据计算出的求导结果更新模型参数,从而最小化某个特定的损失函数 for e in range(num_epoch): # 使用tf.GradientTape()记录损失函数的梯度信息 with tf.GradientTape() as tape: y_pred = a * X + b loss = tf.reduce_sum(tf.square(y_pred - y)) # TensorFlow自动计算损失函数关于自变量(模型参数)的梯度 grads = tape.gradient(loss, variables) # TensorFlow自动根据梯度更新参数 optimizer.apply_gradients(grads_and_vars=zip(grads, variables))
2.2 使用optimizer.minimize相当于先用tape求gradient,再apply_gradient |
# 求f(x) = a*x**2 + b*x + c的最小值 # 使用optimizer.minimize # optimizer.minimize相当于先用tape求gradient,再apply_gradient x = tf.Variable(0.0,name = "x",dtype = tf.float32) #注意f()无参数,且返回的是带variable的闭包 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.keras.optimizers.SGD(learning_rate=0.01) for _ in range(1000): optimizer.minimize(f,[x]) tf.print("y =",f(),"; x =",x) y = 0 ; x = 0.999998569
optimizer.minimize内部实际进行了两步:
2.3 autograph中 |
# 在autograph中完成最小值求解 # 使用optimizer.apply_gradients x = tf.Variable(0.0,name = "x",dtype = tf.float32) optimizer = tf.keras.optimizers.SGD(learning_rate=0.01) @tf.function def minimizef(): a = tf.constant(1.0) b = tf.constant(-2.0) c = tf.constant(1.0) for _ in tf.range(1000): #注意autograph时使用tf.range(1000)而不是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)]) y = a*tf.pow(x,2) + b*x + c return y tf.print(minimizef()) tf.print(x) 0 0.999998569
2.4 在autograph中使用optimizer.minimize |
# 在autograph中完成最小值求解 # 使用optimizer.minimize x = tf.Variable(0.0,name = "x",dtype = tf.float32) optimizer = tf.keras.optimizers.SGD(learning_rate=0.01) @tf.function 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) @tf.function def train(epoch): for _ in tf.range(epoch): optimizer.minimize(f,[x]) return(f()) tf.print(train(1000)) tf.print(x) 0 0.999998569
参考:
https://github.com/lyhue1991/eat_tensorflow2_in_30_days/blob/master/2-3,%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%9C%BA%E5%88%B6.md
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。