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的版本问题。

#先导入必要的库
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)基本运算

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对象去参与运算

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方法即可

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)对于变量情况:

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)对于常量来说:

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() 这个方法之后会立即释放,在同一运算中,计算多个微分的话是不行的,如果要如此,需要在里面添加一个参数。具体如下:

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

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)创建模型

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里面。

optimizer=tf.keras.optimizers.Adam()

2、定义loss的函数,计算损失值,SparseCategoricalCrossentropy()是一个可调用的对象

loss_func=tf.keras.losses.SparseCategoricalCrossentropy()

—————————————————————————————————
(可忽略)中间先来一个没有经过训练的情况,直接通过model调用:

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、定义损失函数

#定义损失函数
def loss(model,x,y):
    y_=model(x)  #y_是预测的label   y是实际的label
    return loss_func(y,y_)

4、定义每一个批次的训练

#这是一个训练批次(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、定义训练的函数

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、开始训练

train() 

——————————————
总结:主要是了解Eager模式的下面的一些操作,最重要的是学会如何的自定义训练。分为哪几步?
定义优化器
定义损失函数
定义每一个批次的训练
定义训练函数
开始训练

GitHub 加速计划 / te / tensorflow
184.55 K
74.12 K
下载
一个面向所有人的开源机器学习框架
最近提交(Master分支:2 个月前 )
a49e66f2 PiperOrigin-RevId: 663726708 3 个月前
91dac11a This test overrides disabled_backends, dropping the default value in the process. PiperOrigin-RevId: 663711155 3 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐