最近跟着看了一篇博文《深度学习之卷积神经网络CNN及tensorflow代码实现示例》,学习着里面的代码,但是遇到了问题,那就是发现里面很多是tensorflow1的代码,而我用的是tensorflow2。以这个为例子,研究tensorflow1到2的转换。
简单方法-直接用tf1兼容
tf2直接全局转为tf1,这样代码很快就跑起来了。
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
但是,这样没有办法真正让我们掌握tf2,也无法了解其精髓。因此,考虑完全将代码转为tf2。
tf_upgrade_v2转换工具
听说有个tf_upgrade_v2的工具可以快速转换,目前正在研究中。
不过目前经常报错:
‘gbk’ codec can’t decode byte 0x89 in position 561: illegal multibyte sequence
而且根据博文《使用 tf_upgrade_v2 命令把 tensorflow1.x 的代码快速转换成 tensorflow2.0》的转化结果,这里转化的方法,很多函数也只是自动帮你加入了tf.compat.v1,并没有办法真正把你的代码变为tf2的,只能说暂时过渡。
因此,最好还是慢慢搞懂里面的函数,学习经验,彻底代替。
函数API手动替换
学习了《tensorflow 1.X迁移至tensorflow2 的代码写法》,觉得可以逐步替换函数,同时真正了解tf1与tf2的区别。
tf1基于图模式,tf2基于eager模式,tf2更便于我们编程和调试,尽管目前tf1的代码很多,学习tf1的代码时,应该学习其思想,将其以tf2重新实现。
语法的基本改变
首先,print函数这个会报错,因为旧版本python不用加括号,新版本应该加上括号。
诸如此类的代码:
print X_data.shape
改为:
print (X_data.shape)
采用兼容性语句
有许多函数数,由于学习时间短,我暂时没有好的解决方法,只能是加上tf.compat.v1.这样用了
tf.compat.v1.function()
像tf.reset_default_graph函数,h函数用于清除默认图形堆栈并重置全局默认图形
tf.reset_default_graph()
优化器的选择函数
train_step = tf.train.AdamOptimizer(1e-3).minimize(loss)
值得注意的是,这里还得关闭eager模式
tf.compat.v1.disable_eager_execution()
当然,等后面完全迁移成功后,自然我们要转入这一模式的,但是前期一步步来,慢慢学习如何兼容。
更换为最新的函数
自然,有许多函数我们是可以找到最新的名称的,使用上新的名字或者功能。
tf.placeholder函数,生成占位符
tf_X = tf.placeholder(tf.float32,[None,8,8,1])
考虑采用keras的input函数代替。
tf_X = keras.Input(dtype=tf.float32,shape=[8,8,1])
tf.random_normal用于从“服从指定正态分布的序列”中随机取出指定个数的值
conv_filter_w1 = tf.Variable(tf.random_normal([3, 3, 1, 10]))
这个随机数生成,改起来较为容易
conv_filter_w1 = tf.Variable(tf.random.normal([3, 3, 1, 10]))
tf.nn.moments()函数用于计算均值和方差
tf.nn.moments(conv_out2, [0, 1, 2], keep_dims=True)
它变动的只是入口参数:
batch_mean, batch_var = tf.nn.moments(conv_out2, [0, 1, 2], shift=None,keepdims=True,name=None)
数学函数,如像计算对数的tf.log、tf.arg_max(input,axis)根据axis取值的不同返回每行或者每列最大值的索引,主要是都归于math之下了。
tf.math.log()
tf.math.argmax(pred,1)
基本思想的改变
到目前为止,也只是简单的函数名改换,还没有涉及tf1和tf2的根本区别。
目前,暂时还无法对session这一块进行改造,因为对于eager模式和图模式的了解还有限,而这也是讲tf1升级为tf2中,感觉是最重要的地方。
tf2的eager模式,让我们很多函数的执行可以无需借助sess.run。
tf.Session() 创建一个新的TensorFlow会话
with tf.Session() as sess
tf.global_variables_initializer()函数
tf.global_variables_initializer()
原程序中的sess.run,如:
#with tf.Session() as sess:
with tf.compat.v1.Session() as sess:
#sess.run(tf.global_variables_initializer())
sess.run(tf.compat.v1.global_variables_initializer())
for epoch in range(1000): # 迭代1000个周期
for batch_xs,batch_ys in generatebatch(X,Y,Y.shape[0],batch_size): # 每个周期进行MBGD算法
sess.run(train_step,feed_dict={tf_X:batch_xs,tf_Y:batch_ys})
if(epoch%100==0):
res = sess.run(accuracy,feed_dict={tf_X:X,tf_Y:Y})
print (epoch,res)
res_ypred = y_pred.eval(feed_dict={tf_X:X,tf_Y:Y}).flatten() # 只能预测一批样本,不能预测一个样本
print (res_ypred)
实际上,tf2中,sess.run根本无需使用,具体参考博客tensorflow 1.X迁移至tensorflow2 代码写法,另外参考TensorFlow 2 ——神经网络模型,对于使用的adam优化器等部分进行了改进。
完整代码如下,每个部分都运行在jupyter notebook:
加载数据:
import tensorflow as tf
from sklearn.datasets import load_digits
import numpy as np
digits = load_digits()
X_data = digits.data.astype(np.float32)
Y_data = digits.target.astype(np.float32).reshape(-1,1)
print (X_data.shape)
print (Y_data.shape)
数据形状调整:
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
from sklearn.preprocessing import OneHotEncoder
X = X_data.reshape(-1,8,8,1)
#X=X_data
Y = OneHotEncoder().fit_transform(Y_data).todense() #one-hot编码
print(X.shape)
print(Y.shape)
模型构建:
class Model(object):
def __init__(self):
# 随机初始化张量参数
self.conv_filter_w1 = tf.Variable(tf.random.normal([3, 3, 1, 10]))
self.conv_filter_b1 = tf.Variable(tf.random.normal([10]))
self.conv_filter_w2 = tf.Variable(tf.random.normal([3, 3, 10, 5]))
self.conv_filter_b2 = tf.Variable(tf.random.normal([5]))
self.shift = tf.Variable(tf.zeros([5]))
self.scale = tf.Variable(tf.ones([5]))
self.fc_w1 = tf.Variable(tf.random.normal([2*2*5,50]))
self.fc_b1 = tf.Variable(tf.random.normal([50]))
self.out_w1 = tf.Variable(tf.random.normal([50,10]))
self.out_b1 = tf.Variable(tf.random.normal([10]))
def __call__(self, x):
tf_X = tf.cast(x, tf.float32) # 转换输入数据类型
# 卷积层+激活层
relu_feature_maps1 = tf.nn.relu(tf.nn.conv2d(tf_X, self.conv_filter_w1,strides=[1, 1, 1, 1], padding='SAME') + self.conv_filter_b1)
# 池化层
max_pool1 = tf.nn.max_pool(relu_feature_maps1,ksize=[1,3,3,1],strides=[1,2,2,1],padding='SAME')
# 卷积层
conv_out2 = tf.nn.conv2d(relu_feature_maps1, self.conv_filter_w2,strides=[1, 2, 2, 1], padding='SAME') + self.conv_filter_b2
# BN归一化层+激活层
batch_mean, batch_var = tf.nn.moments(conv_out2, [0, 1, 2], shift=None,keepdims=True,name=None)
epsilon = 1e-3
BN_out = tf.nn.batch_normalization(conv_out2, batch_mean, batch_var, self.shift, self.scale, epsilon)
relu_BN_maps2 = tf.nn.relu(BN_out)
# 池化层
max_pool2 = tf.nn.max_pool(relu_BN_maps2,ksize=[1,3,3,1],strides=[1,2,2,1],padding='SAME')
# 将特征图进行展开
max_pool2_flat = tf.reshape(max_pool2, [-1, 2*2*5])
# 全连接层
fc_out1 = tf.nn.relu(tf.matmul(max_pool2_flat, self.fc_w1) + self.fc_b1)
# 输出层
pred = tf.nn.softmax(tf.matmul(fc_out1,self.out_w1)+self.out_b1)
return pred
loss函数
def loss_fn(model, x, y):
preds = model(x)
return tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=preds, labels=y))
accuracy计算:
def accuracy_fn(logits, labels):
preds = tf.argmax(logits, axis=1) # 取值最大的索引,正好对应字符标签
labels = tf.argmax(labels, axis=1)
return tf.reduce_mean(tf.cast(tf.equal(preds, labels), tf.float32))
整体调用:
EPOCHS = 1000 # 迭代此时
LEARNING_RATE = 0.02 # 学习率
model = Model() # 实例化模型类
for epoch in range(EPOCHS):
with tf.GradientTape() as tape: # 追踪梯度
loss = loss_fn(model, X, Y)
trainable_variables = [model.conv_filter_w1, model.conv_filter_b1, model.conv_filter_w2, model.conv_filter_b2,model.scale,model.shift,model.fc_w1,model.fc_b1,model.out_w1,model.out_b1] # 需优化参数列表
grads = tape.gradient(loss, trainable_variables) # 计算梯度
optimizer = tf.optimizers.Adam(learning_rate=LEARNING_RATE) # Adam 优化器
optimizer.apply_gradients(zip(grads, trainable_variables)) # 更新梯度
accuracy = accuracy_fn(model(X), Y) # 计算准确度
# 输出各项指标
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/{EPOCHS}], Train loss: {loss}, Test accuracy: {accuracy}')