Python Tensorflow1.x升级到2.x低阶API实践
Posted 肖永威
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python Tensorflow1.x升级到2.x低阶API实践相关的知识,希望对你有一定的参考价值。
1. 常用低阶API的升级
这份文档适用于使用低阶 TensorFlow1.x API升级到Tensorflow 2.x API 的开发者。如果您正在使用高阶 API (tf.keras),可能无需或仅需对您的代码执行很少操作,便可以让代码完全兼容 TensorFlow 2.x。
在 TensorFlow 2.x 中,1.X 的代码不经修改也许还可运行(除了contrib):
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
但是,这样做无法发挥 TensorFlow 2.x 的许多优化改进。这里作者从低阶API代码视角实践Tensorflow1.x升级到2.x低阶API。
升级一些常见的改写经验,先从挥手告别难于理解的占位符开始,包括tf.placeholder,tf.InteractiveSession(),tf.Session()等,如下代码tf.placeholder所示。
#定义输入变量
x = tf.placeholder(dtype=tf.float32,shape=[None,in_units],name='x')
其他的部分常用API及其变化如下表所示。
功能 | Tensorflow 1.X | Tensorflow 2.X |
---|---|---|
梯度优化 | tf.train.AdamOptimizer | tf.optimizers.Adam |
占位符 | tf.placeholder | 无 |
会话 | tf.Session() | 无 |
数据分布 | tf.truncated_normal | tf.random.truncated_normal |
数学计算log | tf.log | tf.math.log |
模型保存 | tf.train.Saver.save | tf.saved_model.save |
模型加载 | tf.train.Saver.restore | tf.saved_model.load |
其中,tensorflow 1.x版本中的placeholder,在tf2中已经被取消,在tf2中,可以用tf.keras.Inputs代替。示例:
# tf1中
input_ids = tf.placeholder(dtype=tf.int32, shape=[None])
#tf2中,改写为:
input_ids = tf.keras.Input(dtype=tf.int32, shape=[None])
2. 关于计算图
在tensorflow 1.x中,由于是基于静态图机制(Graph Execution),需要先构造图,然后才真正运行,因此需要用显示调用Session后,才会真正触发计算,对调试代码非常不利,使用不方便。
在tensorflow 2.x中,默认是基于动态图机制(Eager Execution),就像常规函数一样,调用时就触发计算。对调试代码非常方便。
所以,tf1中session部分代码,可以全部去掉。示例:
# tf1中
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
3. 全连接神经网络升级实践
3.1. 使用公开数据集
使用scikit-learn 提供的简单手写字符识别数据集DIGITS,每行数据宽度为64。
import numpy as np
import tensorflow as tf
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
digits = load_digits() # DIGITS 数据集是 scikit-learn 提供的简单手写字符识别数据集。
digits_y = np.eye(10)[digits.target] # 标签独热编码
# 独热编码是一种稀疏向量,其中:一个元素设为 1,所有其他元素均设为 0。
# 切分数据集,测试集占 20%
X_train, X_test, y_train, y_test = train_test_split(digits.data, digits_y,test_size=0.2, random_state=1)
3.2. 定义全连接神经网络
class DNNModel(tf.Module):
def __init__(self, layer=[64,128,256,10], keep_prob = 0.2, name=None):
super(DNNModel, self).__init__(name=name)
self.layer=layer
self.keep_prob = keep_prob
self.num = len(self.layer)-1 # 网络层数
self.w = []
self.b = []
h_in = self.layer[0]
h_out = self.layer[1]
for i in range(self.num):
# tf.truncated_normal由tf.random.truncated_normal替换
w0 = tf.Variable(tf.random.truncated_normal([h_in,h_out],stddev=0.1),name='weights' +str(i+1) )
b0 = tf.Variable(tf.zeros([h_out],name='biases' + str(i+1)))
self.w.append(w0)
self.b.append(b0)
if i < self.num -1:
h_in = self.layer[i + 1]
h_out = self.layer[i + 2]
self.var = []
for i in range(len(self.w)):
self.var.append(self.w[i])
self.var.append(self.b[i])
@tf.function(input_signature=[tf.TensorSpec([None,64])])
def __call__(self, x):
x = tf.cast(x, tf.float32) # 转换输入数据类型
h1 = x
for i in range(self.num):
#定义前向传播过程
if i < self.num -1:
#h0 = tf.nn.relu(x@self.w[i] + self.b[i], name='layer' +str(i+1))
h0 = tf.nn.relu(tf.matmul(h1, self.w[i]) + self.b[i], name='layer' +str(i+1)) # 或x@w,以及tf.add
#使用dropout
if i == 0:
h1 = h0
else:
h1 = tf.nn.dropout(h0, rate = 1 - self.keep_prob)
else:
h1 = tf.matmul(h1, self.w[i]) + self.b[i]
# 定义输出层
#self.y_conv = tf.nn.softmax(h1,name='y_conv') # 自行编写交叉熵损失,或tf.losses.categorical_crossentropy
self.y_conv = h1 # 直接使用softmax_cross_entropy_with_logits
return self.y_conv
注意:
- python中一切皆对象,函数也是对象,同时也是可调用对象(callable),一个类实例要变成一个可调用对象,只需要实现一个特殊方法__call__()。但是在TensorFlow中,对于自定义模型的时候,经常用call()函数,区别在于__call__()用于class继承tf.Module,call()用于class继承tf.keras.Module。
3.3. 模型训练
layer=[64,128,128,10]
EPOCHS = 100 # 迭代此时
LEARNING_RATE = 0.01 # 学习率
model = DNNModel(layer = layer, keep_prob = 0.2) # 实例化模型类
for epoch in range(EPOCHS):
with tf.GradientTape() as tape: # 追踪梯度
preds = model(X_train)
loss = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(logits=preds, labels=y_train))
#loss = tf.reduce_sum(tf.losses.categorical_crossentropy(y_train, preds))
trainable_variables = model.var # 需优化参数列表
grads = tape.gradient(loss, trainable_variables) # 计算梯度,求导
optimizer = tf.optimizers.Adam(learning_rate=LEARNING_RATE) # Adam 优化器
optimizer.apply_gradients(zip(grads, trainable_variables)) # 更新梯度
# 计算准确度
preds = tf.argmax(model(X_test), axis=1) # 取值最大的索引,正好对应字符标签
labels = tf.argmax(y_test, axis=1)
accuracy = tf.reduce_mean(tf.cast(tf.equal(preds, labels), tf.float32))
# 输出各项指标
if (epoch + 1) % 10 == 0:
print(f'Epoch [epoch+1/EPOCHS], Train loss: loss, Test accuracy: accuracy')
3.4. 模型持久化保存
对于tensorflow2.x版本生成的saved_model模型,没有像1.x版本使用SavedModelBuilder API自定义签名(输入输出的数据类型+方法),2.x版本模型输入的数据类型可以通过@tf.function中的input_signature参数指定,方法目前来看是写死在源码中的,只有signature_constants.PREDICT_METHOD_NAME一种。很多时候模型的输入输出对我们来说是黑盒的,而在调用服务接口的时候我们需要知道模型的输入输出以及签名的key,我们可以使用saved_model_cli show --dir model/test/1 –all来查看我们需要的参数。
tf.saved_model.save(model,'DNNmodel_base')
3.5. 模型加载与使用
restored_model = tf.saved_model.load('DNNmodel_base')
f = restored_model.signatures["serving_default"]
X_test = tf.cast(X_test, tf.float32)
preds = f(X_test)
preds = tf.constant(preds['output_0'])
preds = tf.argmax(preds, axis=1)
labels = tf.argmax(y_test, axis=1)
accuracy = tf.reduce_mean(tf.cast(tf.equal(preds, labels), tf.float32))
print(f'Test accuracy: accuracy')
4. 升级中遇到的问题
4.1. 关于tensorflow-cpu
针对Tensorflow 1.x的GPU安装版本,Tensorflow 2.x不再区分是否gpu,默认直接安装Tensorflow 。当系统检测到gpu并安装cuda后,则自动调用gpu。
如果,我们不需要或没有gpu时,gpu适配对这部分群体是浪费的(占用不必要的资源),于是有了Tensorflow-cpu,我们可以理解其为cpu only版本。
综上,也可以理解为:tensorflow == 1.x对应tensorflow-cpu == 2.x,tensorflow-gpu == 1.x 对应 tensorflow == 2.x。
4.2. 遇到问题
Tensorflow 2.0 使用AdamOptimizer模型报错。
原因:keras与tensorflow版本不匹配
ImportError: cannot import name ‘dtensor’ from ‘tensorflow.compat.v2.experimental’
环境 windows10 、tensorflow2.6版本(Keras已由2.10降至2.6)
5. 关于Tensorflow API简单说明
TensorFlow API一共可以分为三个层次,即低阶API、中阶API、高阶API:
第一层为Python实现的操作符,主要包括各种张量操作算子、计算图、自动微分;
第二层为Python实现的模型组件,对低级API进行了函数封装,主要包括各种模型层,损失函数,优化器,数据管道,特征列等等;
第三层为Python实现的模型成品,一般为按照OOP方式封装的高级API,主要为tf.keras.models提供的模型的类接口。
参考:
[1]. 谷歌开发者. 官方教程 手把手教你如何将 TensorFlow 1 升级到 TensorFlow 2(上). 知乎. 2020.09
[2]. 不要清汤锅. tensorflow / tensorflow-gpu / tensorflow-cpu区别?. CSDN博客. 2020.12
[3].凡心curry. TensorFlow 2 ——神经网络模型. CSDN博客. 2021.04
[4]. Doit. TensorFlow2.0教程-使用低级api训练(非tf.keras). 知乎. 2019.07
[5]. yunfeather. tensorflow中Adam优化器运用. CSDN博客. 2020.05
[6]. 丙吉. keras报错ImportError: cannot import name ‘dtensor’ from ‘tensorflow.compat.v2.experimental’. 简书. 2022.09
[7]. 然后就去远行吧. TensorFlow2.0 —— 模型保存与加载. CSDN博客. 2021.04
[8]. Tester_muller. Tensorflow 2.x 模型-部署与实践. CSDN博客. 2022.08
[9]. 然后就去远行吧. TensorFlow 2.0简介. CSDN博客. 2020.08
[10]. 肖永威. Tensorflow BP神经网络多输出模型在生产管理中应用实践. CSDN博客. 2020.09
以上是关于Python Tensorflow1.x升级到2.x低阶API实践的主要内容,如果未能解决你的问题,请参考以下文章
Python Tensorflow1.x升级到2.x低阶API实践
使用Keras的模型类将Tensorflow 1.x代码迁移到Tensorflow 2.x
TensorFlow1.x 代码实战系列:MNIST手写数字识别