Eager模式简介以及运用
- 1、什么是Eager模式?
- 2、Eager模式下的基本运算
- 1)基本运算
- 2)当做python运算的时候,tensor自动的变成一个python对象去参与运算
- 3)有关于变量的相关操作
- 3、如何自动求解微分
- 1)对于变量情况:
- 2)对于常量来说:
- 3)对于多次微分:
- 4、自定义训练
- 1)导入数据,创建Dataset
- 2)创建模型
- 3)自定义训练
1、什么是Eager模式?
使用过TensorFlow的大家都会知道, TF通过计算图将计算的定义和执行分隔开, 这是一种声明式(declaretive)的编程模型. 确实, 这种静态图的执行模式优点很多,但是在debug时确实非常不方便(类似于对编译好的C语言程序调用,此时是我们无法对其进行内部的调试), 因此有了Eager Execution, 这在TensorFlow v1.5首次引入. 引入的Eager Execution模式后, TensorFlow就拥有了类似于Pytorch一样动态图模型能力, 我们可以不必再等到see.run(*)才能看到执行结果, 可以方便在IDE随时调试代码,查看OPs执行结果. tf.keras封装的太好了 。不利于适用于自定义的循环与训练,添加自定义的循环 是一个命令式的编程环境,它使得我们可以立即评估操作产生的结果,而无需构建计算图。
图运算模式:把一系列的操作搭建好,然后再进行操作,某一步出现错误的话,很难排查,不利于自定义的动作 eager模式:做一步,就能看到结果,交互模式(命令行模式),增加了网络调试的灵活程度,在TensorFlow2的时候,默认的使用了eager模式
首先声明一个比较常见的问题: 至于为什么要导入除了第一行意外的另外几行,我在训练的时候遇到了一个问题,问题如下: “Failed to get convolution algorithm. This is probably because cuDNN failed to initialize” 网上大多数人说什么tf和cuDNN的版本不匹配啊啥的,最后发现可能是GPU内存不足造成的。需要在程序前加以下一段代码:————参考了这篇博客 “Failed to get convolution algorithm. This is probably because cuDNN failed to initialize”错误的解决办法.成功的解决了问题。意思是对GPU进行按需分配。主要原因是我的图像比较大,消耗GPU资源较多。但我的显卡(GTX1060TI)显存只有6GB,所以会出现这个错误。这个错误提示有很大的误导性,让人一直纠结CUDA和CuDNN的版本问题。
代码语言:javascript复制#先导入必要的库
import tensorflow as tf
#下面就是加入的部分
from tensorflow.compat.v1 import ConfigProto
from tensorflow.compat.v1 import InteractiveSession
config = ConfigProto()
config.gpu_options.allow_growth = True
session = InteractiveSession(config=config)
2、Eager模式下的基本运算
1)基本运算
代码语言:javascript复制x=[[2,]] #返回[[2]]
m=tf.matmul(x,x) #两个X相乘 返回一个tensor <tf.Tensor: shape=(1, 1), dtype=int32, numpy=array([[4]])>
m.numpy() #将tensor转化为numpy数组
#例如定义一个二维数组
a=tf.constant([[1,2],[3,4]])
a.numpy() #array([[1, 2],[3, 4]])
c=tf.multiply(a,m)
# c返回值:<tf.Tensor: shape=(2, 2), dtype=int32, numpy=array([[ 4, 8],[12, 16]])>
# 再看一个例子
num=tf.convert_to_tensor(10) #将numpy数字转化为tensor _——> <tf.Tensor: shape=(), dtype=int32, numpy=10>
2)当做python运算的时候,tensor自动的变成一个python对象去参与运算
代码语言:javascript复制for i in range(num.numpy()):
i=tf.convert_to_tensor(i)
if int(i%2)==0: #当做int运算的时候,tensor自动的变成一个python对象
print(str(i.numpy()) ":even")
else:
print(str(i.numpy()) ":odd")
#再例如
g=tf.convert_to_tensor(10) # ——>>> <tf.Tensor: shape=(), dtype=int32, numpy=10>
float(g) #——> 10.0
3)有关于变量的相关操作
记住下面的这几个API方法即可
代码语言:javascript复制v=tf.Variable(0.0)#创建一个变量
(v 1).numpy() #对于这个变量,可以直接使用他的值 返回:1.0
v.assign(5) #通过这个方法 直接改变变量的值 返回:<tf.Variable 'UnreadVariable' shape=() dtype=float32, numpy=5.0>
v.assign_add(1) #增加变量的值 返回:<tf.Variable 'UnreadVariable' shape=() dtype=float32, numpy=6.0>
v.read_value() #直接读取变量的值,读取出来的结果是一个tensor 返回:<tf.Tensor: shape=(), dtype=float32, numpy=6.0>
总结:可以看到在eager执行下,每个操作后的返回值是tf.Tensor,其包含具体值,不再像Graph模式下那样只是一个计算图节点的符号句柄。由于可以立即看到结果,这非常有助于程序debug。更进一步地,调用tf.Tensor.numpy()方法可以获得Tensor所对应的numpy数组。
3、如何自动求解微分
使用tape来记录我们的运算过程,进一步求解微分。不管对于变量还是常量的跟踪运算,都要求一种float的数据运算类型。
1)对于变量情况:
代码语言:javascript复制w=tf.Variable([[1.0]])
with tf.GradientTape() as t:
loss=w*w #来记录我们的运算,GradientTape()——>上下文管理器 自动的跟踪变量的运算,如果是个常量,那么就需要人工的去规定他,让这个磁带去跟踪常量的计算过程
grad=t.gradient(loss,w) #t.gradient()这个方法:求解loss对w的微分 <tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[2.]], dtype=float32)>
2)对于常量来说:
代码语言:javascript复制v=tf.constant([[3.0]])
with tf.GradientTape() as t:
t.watch(v) #让t去跟踪常量的运算,因为v是一个常量
loss=v*v
dloss_dv=t.gradient(loss,v)
dloss_dv.numpy() #——>>> array([[6.]], dtype=float32)
3)对于多次微分:
注意:对于GradientTape()持有的资源,记录的这些运算,在调用了t.gradient() 这个方法之后会立即释放,在同一运算中,计算多个微分的话是不行的,如果要如此,需要在里面添加一个参数。具体如下:
代码语言:javascript复制v=tf.Variable([[3.0]])
with tf.GradientTape(persistent=True) as t:
t.watch(v)
y=v*v
z=y*y
dy_dv=t.gradient(y,v) #<tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[6.]], dtype=float32)>
dz_dv=t.gradient(z,v) #<tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[108.]], dtype=float32)>
4、自定义训练
这次的自定义训练,参考数据集是手写数字mnist,模型也比较简单,主要是掌握具体的方法。
1)导入数据,创建Dataset
代码语言:javascript复制import tensorflow as tf
import matplotlib.pyplot as plt
(train_images,train_labels),_=tf.keras.datasets.mnist.load_data() #下载数据
train_images=tf.expand_dims(train_images,-1) #卷积神经网络图片输入的时候要求有通道数 扩张维度 等同于numpy的用法 np.expand_dims(img,-1)
#刚刚说过,自定义训练的时候,输入类型要求为float类型
train_images=tf.cast(train_images/255,tf.float32) #图片转为float类型
train_labels=tf.cast(train_labels,tf.int64)
#建立数据集
dataset=tf.data.Dataset.from_tensor_slices((train_images,train_labels))
dataset=dataset.shuffle(10000).batch(32) #这里的dataset是一个可迭代的对象
2)创建模型
代码语言:javascript复制model=tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(16,(3,3),activation="relu",input_shape=(28,28,1)))#通道数为1的任意大小的图片都能够输入进来
model.add(tf.keras.layers.Conv2D(32,(3,3),activation="relu"))
model.add(tf.keras.layers.GlobalAveragePooling2D())
model.add(tf.keras.layers.Dense(10,activation="softmax"))
3)自定义训练
1、自定义训练的时候,我要先定义他的优化函数,在tf2里面,优化函数全部归到了optimizers里面。
代码语言:javascript复制optimizer=tf.keras.optimizers.Adam()
2、定义loss的函数,计算损失值,SparseCategoricalCrossentropy()是一个可调用的对象。
代码语言:javascript复制loss_func=tf.keras.losses.SparseCategoricalCrossentropy()
————————————————————————————————— (可忽略)中间先来一个没有经过训练的情况,直接通过model调用:
代码语言:javascript复制features,labels=next(iter(dataset)) #iter(dataset) 将dataset迭代出来 next()取出下一个数据
predictions=model(features) # TensorShape([32, 10])
tf.argmax(predictions,axis=1)
print(labels)
输出结果:
上面之所以不准,是因为没有经过训练
至于下面这两个代码,我查询了一下它的具体用法: features,labels=next(iter(dataset)) tf.argmax(predictions,axis=1) ///tf.argmax(predictions,axis=1) 这个函数的主要功能是返回最大值所在的坐标。主要用在分类的时候,如果只是简单的输出,只是对于每一类可能性的预测的输出,但是我要要的输出必须是确定的哪一类,所以需要确定里面的最大的值(也就是说最可能是哪一类)。 ///dataset是一个可迭对象,用iter对他进行迭代,然后用next方法取出列表里面的下一个数据 next(it,’-1’) 这个-1是默认值,从-1的下一个也就是0开始取,其实还是列表的第一个。
—————————————————————————————————
3、定义损失函数
代码语言:javascript复制#定义损失函数
def loss(model,x,y):
y_=model(x) #y_是预测的label y是实际的label
return loss_func(y,y_)
4、定义每一个批次的训练
代码语言:javascript复制#这是一个训练批次(batch)所需做的事情,怎么训练的?
def train_step(model,images,labels):
#在这一步当中,要计算我们的损失值与可训练参数的梯度值,需要建立一个gradient tape
with tf.GradientTape() as t: #tf.GradientTape()跟踪运算——>loss_step的值对于可训练参数的变化,追踪损失函数
loss_step=loss(model,images,labels)
#计算loss值和可训练参数的梯度
grads=t.gradient(loss_step,model.trainable_variables) #model.trainable_variables()模型的可训练参数
#怎么去优化呢?运用之前写好的optimizers,来改变我们的变量值,使得我们的梯度下降的最快
optimizer.apply_gradients(zip(grads,model.trainable_variables))#通过这个优化器,对变量进行一定的修改
5、定义训练的函数
代码语言:javascript复制def train():
for epoch in range(10): #对所有的数据训练10次
#对dataset进行迭代
for (batch,(images,label)) in enumerate(dataset): #batch就是那个enumerate,给它加上了序号0/1/2/3...
train_step(model,images,labels) #进行一步训练>>>这里的images,labels就是从上一个语句中迭代出来的
print("Epoch{}is finshed".format(epoch))
6、开始训练
代码语言:javascript复制train()
—————————————— 总结:主要是了解Eager模式的下面的一些操作,最重要的是学会如何的自定义训练。分为哪几步? 定义优化器 定义损失函数 定义每一个批次的训练 定义训练函数 开始训练