【深度学习】村通网之——谈谈Tensorflow Eager Execution机制之新特性示例(二)

前言

本文是【深度学习】村通网之——谈谈Tensorflow Eager Execution机制之静态图和动态图的区别(一)的后续讲解,没看过前文的可以先去看再来理解此文会比较容易。
书接上文,上文介绍了几个特性,但是具体的例子比较少,本文重点在与举例,让大家更深入了解Eager Execution机制带来的便利。
下面所有的示例都依赖于下面的代码块:

import tensorflow as tf
import numpy as np
import tensorflow.contrib.eager as tfe

# 开启动态图机制
tfe.enable_eager_execution()

直接使用operation进行卷积操作

# 随机生成两张图片,即batch_size=2,大小为28x28,三通道
images=np.random.randn(2,28,28,3).astype(np.float32)

# 卷积核参数
filter=tf.get_variable("conv_w0",shape=[5,5,3,20],initializer=tf.truncated_normal_initializer)

# 对生成的batch_size的数码进行卷积操作,可以立即获得结果
conv=tf.nn.conv2d(input=images,filter=filter,strides=[1,2,2,1],padding="VALID")

# print(conv)

# 显示结果的numpy数组的shape
print(conv.numpy().shape)

运行结果如下:

(2, 12, 12, 20)

成功获得卷积后结果的shape。

自动计算梯度(导数)

开启Eager模式后,正向传播很直观很好理解,但应该怎么求梯度呢?在tfe中共有四个函数直接服务于反向传播,它们是:

  • tfe.gradients_function
  • tfe.value_and_gradients_function
  • tfe.implicit_gradients
  • tfe.implicit_value_and_gradients
    其中,前两个函数用来求输入函数相对于他所有参数的梯度,也就是对函数的所有输入参数求导;后两个函数用来对计算过程中用到的所有变量求导。这四个函数是python中的装饰器,可以这样写:
@tfe.gradients_function
def f(x, y):
    return x ** 2 + y ** 3
# 计算f(x,y)在x=1,y=2处的导数
f(1., 2.)

当然也可以不使用装饰器,将函数名作为参数传入,作为回调函数:

def f(x, y):
    return x ** 2 + y ** 3
g = tfe.gradients_function(f)
# 同样可以计算f(x,y)在x=1,y=2处的导数
g(1., 2.)

计算所有参数的梯度

tfe.gradients_function和tfe.value_and_gradients_function的功能是对函数的输入参数求导,即他们的输入是一个函数,输出是输入函数相对于它所有参数的梯度函数。
不同之处是,tfe.gradients_function返回的格式为[参数1梯度,参数2梯度…],tfe.value_and_gradients_function返回的格式为(函数值,[参数1梯度,参数2梯度])。后者比前者多了一个计算出的函数值。

def func(x):
	return x*x+2*x+4.0

# 将函数名作为参数传入,作为回调函数
grad1=tfe.gradients_function(func)
print(grad1(2.0))

grad2=tfe.value_and_gradients_function(func)
print(grad2(2.0))

输出:

[<tf.Tensor: id=13, shape=(), dtype=float32, numpy=6.0>]
(<tf.Tensor: id=19, shape=(), dtype=float32, numpy=12.0>, [<tf.Tensor: id=24, shape=(), dtype=float32, numpy=6.0>])

将x=2带入func函数,得到的函数值为22+22+4=12。对func求导后:dx=2*x+2,则dx(x=2)=6。

计算所有变量的梯度

在实际使用中,我们希望对图中的变量(Variable)求导,因为模型中的变量是我们需要做梯度下降进行优化的部分。
tfe.implicit_gradients和tfe.implicit_value_and_gradients的功能就是对“计算过程中所有用到的变量”求导的函数。他们的不同之处是:tfe.implicit_gradients返回的是[(梯度1,变量值1),(梯度2,变量值2)…],tfe.implicit_value_and_gradients返回的是(函数值,[(梯度1,变量值1),(梯度2,变量值2)…])。同样,后者比前者多了计算出的函数值。

x=tfe.Variable(initial_value=1.0,name="x")
y=tfe.Variable(initial_value=1.0,name="y")

def func(a):
	return x+2*a*x*y

grad1=tfe.implicit_gradients(func)
print(grad1(5.0))

grad2=tfe.implicit_value_and_gradients(func)
print(grad2(5.0))

输出:

[(<tf.Tensor: id=26, shape=(), dtype=float32, numpy=11.0>, <tf.Variable 'x:0' shape=() dtype=float32, numpy=1.0>), (<tf.Tensor: id=23, shape=(), dtype=float32, numpy=10.0>, <tf.Variable 'y:0' shape=() dtype=float32, numpy=1.0>)]
(<tf.Tensor: id=40, shape=(), dtype=float32, numpy=11.0>, [(<tf.Tensor: id=45, shape=(), dtype=float32, numpy=11.0>, <tf.Variable 'x:0' shape=() dtype=float32, numpy=1.0>), (<tf.Tensor: id=42, shape=(), dtype=float32, numpy=10.0>, <tf.Variable 'y:0' shape=() dtype=float32, numpy=1.0>)])

参考自:https://blog.csdn.net/guangcheng0312q/article/details/100117550

使用Python程序流程控制模型流程

上文中有所提到,在静态图中如果存在placeholder和feed的方式,那么就不能使用if else、while等语句,因为静态图在构图的过程中还没有数据输入,所以并不知道应该走那条路,这时候就会在构图过程中报错。我们举例来看:
静态图需要使用tf.cond()函数控制模型的解构。

def relu(x):
	# 使用if else条件控制块
	if(x>=0):
		return x
	else:
		return tf.zeros(x.shape,dtype=tf.float64)

def true_relu(x):
	# 使用tf.cond代替if else实现条件控制
	cond=tf.greater_equal(x,tf.constant(0.0,dtype=tf.float64))
	zeros=tf.zeros(x.shape,dtype=tf.float64)

	y=tf.cond(cond[0][0],lambda:x,lambda: zeros)
	return y

# 手动创建一个图
g2 = tf.Graph()
with g2.as_default():
	# 在静态图中需要对未输入具体值的输入层使用占位符进行占位
	x1=tf.placeholder(tf.float64,shape=(1,1))
	x2=tf.placeholder(tf.float64,shape=(1,1))

	result1=true_relu(x1)

	# 使用result2=x2*relu(x2) #会报错
	result2=true_relu(x2)

with tf.Session(graph=g2) as sess:
	print(sess.run(fetches=[result1,result2],feed_dict={x1:np.asarray([[5.0]]),x2:np.asarray([[-5.0]])}))

输出:

[array([[5.]]), array([[-0.]])]

你说,我不管,我就是要用if else语句控制,那么执行result2=x2*relu(x2)的时候就会报错:

xxxx(前面不看) use TensorFlow ops such as tf.cond to execute subgraphs conditioned on the value of a tensor.

他会提示“使用Tensorflow的操作(比如说tf.cond)去执行以张量值为条件的子图”,所以说还是省省吧,我们来看动态图:

def relu1(x):
	# 使用if else条件控制块
	if(x>=0):
		return x
	else:
		return tf.zeros(x.shape,dtype=tf.float64)

def relu2(x):
	# 使用tf.cond代替if else实现条件控制
	cond=tf.greater_equal(x,tf.constant(0.0,dtype=tf.float64))
	zeros=tf.zeros(x.shape,dtype=tf.float64)

	y=tf.cond(cond[0][0],lambda:x,lambda: zeros)
	return y

# 在动态图中直接将numpy数组输入
x1=np.asarray([[-5.0]])
x2=np.asarray([[5.]])


result1=relu1(x1)
result2=relu1(x2)

print("result1:{},result2:{}".format(result1,result2))

result3=relu2(x1)
result4=relu2(x2)

print("result3:{},result4:{}".format(result3,result4))

输出:

result1:[[-0.]],result2:[[5.]]
result3:[[0.]],result4:[[5.]]

程序可以正常执行。

自动优化

出于动态图机制,我们可以直接把损失函数传入优化器中进行优化:

w=tf.get_variable("w",initializer=3.0)

def loss(x):
	return w*x

# 创建优化器
optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.1)

for i in range(5):
	optimizer.minimize(lambda:loss(5.0))
	print(w.numpy())

输出:

2.5
2.0
1.5
1.0
0.5

可以看到,变量w的值在不断被更新,以减小loss的值。
优化器中的minimize函数用于最小化loss,并更新var_list
该函数是简单的合并了compute_gradients()与apply_gradients()函数,返回为一个优化更新后的var_list,如果global_step非None,该操作还会为global_step做自增操作。

  • compute_gradients()函数:对var_list中的变量计算loss的梯度,该函数为函数minimize()的第一部分,返回一个以元组(gradient, variable)组成的列表
  • apply_gradients()函数:将计算出的梯度应用到变量上,对变量进行一定的修改。梯度下降法一般是计算x1-learning_rate*gradent_x1,并将结果赋给x1,这样就完成一次对x1的更新,这个函数是函数minimize()的第二部分。如果有多个变量,则都会更新,对global_step做自增操作,需要注意的是,对于不同的优化算子,这一步进行的计算是不同的,比如说adam优化器和momentum优化器的计算过程就是不同的,但是目的只有一个,优化参数。

在compute_gradients()函数与apply_gradients()函数之间可以对一步计算出的梯度进行处理,这样就变成了一个在原来优化器基础上自定义的优化器。实际上,我们完全可以使用上文提到的梯度函数从头到尾定义一个新的优化器。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值