Tensorflow 2.0版本虽然推出已有些时日了,前两天 2.1 版本也已经放出来了,但笔者基于一些猜想,一直兴趣乏匮。今天读了读官方说明书,又在一些网站看了看先锋人士的使用体会,果不其然。最终得出结论:熟悉 TF1.x 的开发者们暂时没有必要切换到 2.x 版本。具体地,我分为以下几个方面来讨论。
第一,2.x 在性能上没有提升。曾有研究者对各大深度学习框架进行综合对比,TF 在运算效率上以第一名的王者姿态傲视群雄,排名第二的 Pytorch 用时在 TF 的 90%。这样的成绩得益于 TF 独一无二的关于静态图的设定;其余知名的框架诸如 Mxnet、Caffe、Pytorch 都是基于动态图。静态图的高效让 TF 毫无疑问成为工业界的宠儿,但由于代码过于繁复,其也饱受诟病,被指责对于新手太不友好,也不利于模型调试,学术界的主导地位眼睁睁地被 Pytorch 夺去。想来 Tensorflow 的开发者 Google 也不会甘心,一直在思考怎么简化 TF,所以有了高层封装 Keras。但 Keras 本质上没有改变静态图的设定,在关键模型细节上的调试,还得回到 TF 的底层,使用底层函数来实现。在 2.x 版本,Google 下定决心,要改就改的彻底!引入急切模式 (eager execution),让编程如同 Pytorch 一样简单,但同时也保留静态图的底层设定。具体的方式在于使用函数修饰器 tf.function 来修饰简单代码,如同以下示例。
@tf.function
def train(model, dataset, optimizer):
for x, y in dataset:
with tf.GradientTape() as tape:
prediction = model(x)
loss = loss_fn(prediction, y)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
这样的简化毫无疑问极大地减少了代码量,使用者也无需再自行定义占位符、计算图和会话,让新人可以更快上手。但对比于 1.x 版本,运算效率提升了吗?答案是没有。对熟悉 1.x 静态图编程的开发者来说,适应一套新的编程模式,反倒是一个麻烦。
第二,tf.contrib 被重组。tf.contrib 模块中的大多数函数由 TF 开源社区提供,由 TF 的爱好者们来维护,包括一些复杂的算子、神经网络层、损失函数、优化器等。tf.contrib 有多好用,常用 TF 的人都知道。它提供的方法常常能够让我们仅用一行代码写完一整套复杂的算法。来到 2.x 版本,tf.contrib 被完全重组,一部分函数成功通过时间检验加入到 TF 核中;一部分基于 API 规划加入 Keras 中;一部分仍待开发和测试,转移到 TF-Addons ;剩下的,出于稳定性和兼容性的考虑,被完全移除。无论这些优秀的函数通过以上何种方式进行转移,对于有不少往期代码和项目需要维护的开发者来说都是巨大的麻烦。因为官方没有提供说明书帮助开发者进行快速迁移,何去何从都无从考究。官方将这些希望寄托给 TF 社区,由 TF 爱好者们来逐渐完善。
第三,开源项目还不充沛。
笔者认为,基于以上三点考虑,TF1.x 将在很长一段时间内保持主流。TF 官方也宣布,将至少在一年以内保持对 TF1.x 的关注,修复其中的 bug,整合到 tensorflow.compat.v1 中。对于计算机专业的开发者而言,知识如瀚海,时间更为精贵,大可以先将精力投入于更有价值的方面,将 TF2.x 置于一边,采用静观其变的态度,有需要时再行学习。而高度依赖于现有开源项目的工业界,短期内定然不会出现面向 TF2.x 的大规模迁移活动。
目前来看,1.x 版本中 TF1.11 和 TF1.12 是使用人数较多的,也是反馈比较不错的,后续的 TF1.13 和 TF1.14 都已经在为 TF2.0 做准备,多了不必要的功能和提示,推荐想要维持在 1.x 的开发者将 TF 降级到 1.11 版本使用。
尽管没有打算使用 TF2.x 的新特性,为了顺应时势,笔者还是将其升级到了 2.0 版本,并对往期笔记进行了重新整理,这里提供给大家。
为了帮助 TF1.x 的用户逐步适应 2.x 版本的使用,TF2.x 中保留了 1.x 绝大多数的功能和特性,包括计算图和会话的设计,统一存放在 tensorflow.compat.v1 中。
# 在TF2.x环境下运行1.x
import tensorflow.compat.v1 as tf #从TF2.x中导入1.x
tf.disable_eager_execution() #关闭急切模式
# 图
tf.reset_default_graph() #重置默认图
graph = tf.Graph() #新建图(Graph)
graph = tf.get_default_graph() #默认图(Graph)
# with graph.device('/gpu:0') #指定计算设备,多GPU并行的关键
# os.environ['CUDA_VISIBLE_DEVICES'] = '0' #指定参与计算的GPU设备
# graph.finalize() #锁定计算图,无法继续添加节点
# 变量
with tf.name_scope('TF1.x'): #name_scope仅对Variable有影响
with tf.variable_scope('base',reuse=tf.AUTO_REUSE):
X = tf.Variable([[1,2,3],[4,5,6]], trainable=False, dtype=tf.float32, name='X') #生成变量(Variable)
X1 = tf.Variable([[1,2,3],[4,5,6]], name='X1')
X2 = tf.Variable([[-1,-2,-3],[-4,-5,-6]], name='X2')
W = tf.Variable(tf.random_normal((3,2)), name='W', dtype=tf.float32)
with tf.variable_scope('TF1.x/base',reuse=tf.AUTO_REUSE):
b = tf.get_variable(shape=(2),name='b',initializer=tf.zeros_initializer)
b = tf.get_variable(shape=(2),name='b',initializer=tf.ones_initializer)
b = tf.get_variable(shape=(2),name='b',initializer=tf.constant_initializer(2))
b = tf.get_variable(shape=(2),name='b',initializer=tf.random_uniform_initializer(-0.5,0.5))
b = tf.get_variable(shape=(2),name='b',initializer=tf.random_normal_initializer(0.2))
b = tf.get_variable(shape=(2),name='b',initializer=tf.truncated_normal_initializer(0.2))
with tf.variable_scope('TF1.x/base',reuse=True):
b = tf.get_variable(name='b') #提取变量(Variable)
X.name #查看名称(string)
X.shape #查看形状(TensorShape)
tf.global_variables() #查看全局变量(list[Variable])
tf.trainable_variables() #查看可训练变量(list[Variable])
# 张量生成
tf.placeholder(tf.float32, (None,3), name='Xs') #占位符(Tensor)
tf.range(10) #整数序列(Tensor)
tf.one_hot(X1,10) #one-hot数组(Tensor)
tf.zeros((2,3),dtype=tf.int32) #零数组(Tensor)
tf.ones((2,3),dtype=tf.int32) #一数组(Tensor)
tf.fill((2,3),value=2.0) #常数数组(Tensor)
tf.constant([1,2],dtype=tf.int32) #定值数组(Tensor)
tf.random_uniform((2,3,5),seed=1) #均匀分布随机数数组(Tensor)
tf.random_normal((2,3,5),stddev=1.5) #正态分布随机数数组(Tensor)
tf.truncated_normal((2,3,5),stddev=1.5) #正态分布随机数数组(大于2倍sigma将被剪裁)(Tensor)
# 张量运算
tf.add(X1,X2) #加法(Tensor)
tf.add_n([X1,X2]) #加法(Tensor)
tf.subtract(X1,X2) #减法(Tensor)
tf.multiply(X1,X2) #乘法(Tensor)
tf.square(X) #平方(Tensor)
tf.matmul(X,W) #点积(Tensor)
tf.reduce_sum(X) #求和(Tensor)
tf.reduce_mean(X) #平均 (Tensor)
tf.transpose(X,[1,0]) #转置(Tensor)
tf.reshape(X,(-1,)) #变形(Tensor)
tf.argmax(X,1) #最大值索引(Tensor)
tf.clip_by_value(X,clip_value_min=-1,clip_value_max=1) #范围裁剪(Tensor)
tf.clip_by_norm(X,clip_norm=0.5) #模裁剪(Tensor)
tf.greater(X1,X2) #比较大小(Tensor)
tf.less(X1,X2) #比较大小(Tensor)
tf.equal(X1,X2) #相等判断(Tensor)
tf.where(tf.greater(X1,X2),X1,X2) #条件取值(Tensor)
tf.cond(tf.greater(X1,X2),lambda:X1,lambda:X2) #条件取值(Tensor)
tf.while_loop(lambda X: tf.greater(X,X1),lambda X:X+1,[X2]) #条件循环(Tensor)
tf.shape(X) #维度(Tensor)
tf.expand_dims(X,axis=0) #扩充维度(Tensor)
tf.squeeze(X) #压缩维度(Tensor)
tf.cast(X,tf.float32) #改变元素类型(Tensor)
tf.gather(X,[1]) #切片(Tensor)
tf.split(X,2,axis=0) #拆分(list[Tensor])
tf.concat([X1,X2],axis=0) #合并(Tensor)
tf.sequence_mask(X,4) #生成Mask矩阵(Tensor)
tf.boolean_mask(X,mask=tf.greater(X,2)) #应用Mask矩阵(Tensor)
所有的op在定义完成后需要将地址保存在内存中,在会话中执行后生效。
# 开启会话
config = tf.ConfigProto( #会话设置(ConfigProto)
allow_soft_placement=True, #允许动态分配GPU内存
log_device_placement=True) #打印设备信息
config.gpu_options.allow_growth = True #按需分配GPU显存
config.gpu_options.per_process_gpu_memory_fraction = 0.4 #指定分配GPU显存
sess = tf.InteractiveSession(config=config) #开启交互会话(Session)
# sess = tf.Session(config=config) #开启普通会话(Session)
# 赋值
assign_op = tf.assign(X,[[5,3,2],[1,4,7]]) #赋值(Op-N)
assign_op = tf.assign_add(X,[[0,0,0],[0,0,0]]) #附加(Op-N)
assign_op = tf.assign_sub(X,[[0,0,0],[0,0,0]]) #附减(Op-N)
# 滑动平均
ema = tf.train.ExponentialMovingAverage(0.97) #滑动平均类(EMA)
ema_op = ema.apply([W]) #衰减一次(Op-N)
ema.average(W) #提取滑动平均值变量(Variable)
# 数据迭代器
dataset = tf.data.Dataset.from_tensor_slices(X) #拆分数据(DatasetV1Adapter)
dataset = dataset.shuffle(1000).batch(100) #打乱并生成批量(DatasetV1Adapter)
iterator = tf.data.Iterator.from_structure( #创建迭代器(Iterator)
tf.data.get_output_types(dataset), #元素类型
tf.data.get_output_shapes(dataset)) #形状
init_iterator_op = iterator.make_initializer(dataset) #激活迭代器(Op-1)
iterator.get_next() #获取批量数据(Tensor)
# 控制依赖
with tf.control_dependencies([init_iterator_op]):
control_op = tf.no_op(name='control') #组合上述Ops(Op-N)
# control_op = ema_op #后发(Op-N)
# 全局初始化
init_op = tf.global_variables_initializer() #初始化全局变量(Op-1)
# 运行主程序
sess.run(init_op)
sess.run(init_iterator_op)
sess.run(control_op)
sess.run(assign_op)
for _ in range(5):
sess.run(ema_op)
# 关闭会话
sess.close() #关闭会话
深度学习的第一步是构建神经网络架构。由于 TF2.x 中移除了 TF1.x 中的 contrib 模块,因此无法使用原来由社区提供的简易方法搭建包括条件随机场在内的部分网络架构。
# 卷积神经网络
x = tf.placeholder(tf.float32, shape=(32,320,320,3), name='image')
with tf.variable_scope('TF1.x/conv',reuse=tf.AUTO_REUSE):
w = tf.get_variable(shape=(3,3,1,24), #卷积核(Variable)
initializer=tf.random_normal_initializer(0.2),
name='w')
b = tf.get_variable(shape=(24), #偏置项(Variable)
initializer=tf.zeros_initializer,
name='b')
tf.nn.bias_add(tf.nn.conv2d(x,filter=w,strides=[1,1,1,1],padding='SAME'),b) #卷积(Tensor)
tf.nn.sigmoid(x) #Sigmoid激活(Tensor)
tf.nn.tanh(x) #Tanh激活(Tensor)
tf.nn.relu(x) #ReLU激活(Tensor)
tf.nn.moments(x,axes=[1,2]) #平均值与方差(list[Tensor])
tf.nn.max_pool(x,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID') #最大池化层(Tensor)
tf.nn.avg_pool(x,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID') #平均池化层(Tensor)
tf.nn.dropout(x, rate=0.1) #丢弃层(Tensor)
# 循环神经网络
h = tf.placeholder(tf.float32, shape=(32,100), name='word_vector')
lstm = tf.nn.rnn_cell.BasicLSTMCell(num_units=320) #基础LSTM单元(BasicLSTMCell)
state = lstm.zero_state(32,tf.float32) #初始状态(LSTMStateTuple(c,h))
for _ in range(4):
y, state = lstm(h, state)
# 多层感知机
with tf.variable_scope('TF1.x/mlp',reuse=tf.AUTO_REUSE):
embedding = tf.get_variable(shape=(10000,100), #嵌入层(Variable)
initializer=tf.truncated_normal_initializer(0.2),
name='embedding')
w = tf.get_variable(shape=(100,3), #线性变换参数(Variable)
initializer=tf.truncated_normal_initializer(0.2),
name='w')
b = tf.get_variable(shape=(3), #偏置项(Variable)
initializer=tf.zeros_initializer,
name='b')
x = tf.nn.embedding_lookup(embedding,[0,254]) #嵌入查找(Tensor)
z = tf.nn.bias_add(tf.matmul(x,w),b) #全连接层(Tensor)
深度学习的第二步是定义损失函数以及优化器。
# 回归损失
pred = tf.random_normal(shape=(64,)) #预测值(Tensor)
label = tf.random_normal(shape=(64,)) #标签值(Tensor)
loss = tf.reduce_mean(tf.square(pred-label)) #MSE(Tensor)
loss = tf.reduce_mean(tf.abs(pred-label)) #MAE(Tensor)
# 分类损失
pred_prob = tf.nn.softmax(z,axis=1) #softmax标准化(Tensor)
pred = tf.argmax(pred_prob,axis=1) #预测分类(Tensor)
label = tf.cast(tf.random_uniform(shape=(2,),maxval=3),tf.int32) #标签分类(Tensor)
label_prob = tf.one_hot(label,depth=3) #标签概率分布(Tensor)
loss = tf.nn.softmax_cross_entropy_with_logits(logits=pred_prob,labels=label_prob) #交叉熵损失函数(标签为概率分布)(Tensor)
loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=pred_prob,labels=label)#交叉熵损失函数(标签为数值)(Tensor)
# 正则项
tf.reduce_sum(tf.abs(w)) #L1范数(Tensor)
tf.rsqrt(tf.reduce_sum(tf.square(w))) #L2范数(Tensor)
# 优化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01) #SGD优化器(Optimizer)
optimizer = tf.train.AdamOptimizer(learning_rate=0.01) #Adam优化器(Optimizer)
train_op = optimizer.minimize(loss) #损失优化(Op-N)
# 衰减学习率
global_step = tf.Variable(0,trainable=False) #定义初始迭代轮数(Variable)
lr = tf.train.exponential_decay(0.1,global_step,100,0.96,staircase=True) #创建衰减学习率对象(Tensor)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=lr) #将衰减学习率应用到优化器(Tensor)
train_op = optimizer.minimize(loss)
# 梯度剪裁
grads = optimizer.compute_gradients(loss) #计算梯度(list[(grad,Variable)])
for i, (g, v) in enumerate(grads):
if g is not None: #存在梯度
grads[i] = (tf.clip_by_norm(g, 0.5), v)
train_op = optimizer.apply_gradients(grads) #应用剪裁后的梯度(Op-N)
技术上,参数的保存与命名是迁移学习的基础。
saver = tf.train.Saver({'name':X}) #定义模型存储类并规定载入的变量参数(Saver)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
saver.save(sess,'model/') #保存会话
saver.restore(sess,'model/') #读取会话
TF 独有的数据格式 TFRecord 使得数据读取速度更快,但同时也占用更大空间,通常只用于整数、浮点以及字节,而不用于字符串。
# 准备
integers = [2,5,7]
floats = [6.,0.,3.,4.]
bytes = b'\n\x06\x04' #np.array([10,6,4], dtype=np.uint8).tostring()
# 写入TFRecord
writer = tf.python_io.TFRecordWriter('data.tfrecords') #定义写入类对象(TFRecordWriter)
features = tf.train.Features(feature={ #生成Features协议信息(Features)
'int':tf.train.Feature(int64_list=tf.train.Int64List(value=integers)),
'float':tf.train.Feature(float_list=tf.train.FloatList(value=floats)),
'byte':tf.train.Feature(bytes_list=tf.train.BytesList(value=[bytes])),
})
example = tf.train.Example(features=features) #封装Features(Example)
writer.write(example.SerializeToString()) #Example序列化并写入
writer.close() #关闭写入类对象
# 查看TFRecord
example = next(tf.python_io.tf_record_iterator('data.tfrecords'))#查看数据类型(bytes)
print(tf.train.Example.FromString(example))
# 读取TFRecord
reader = tf.TFRecordReader() #定义读取类对象(TFRecordReader)
queue = tf.train.string_input_producer(['data.tfrecords']) #定义读取队列(FIFOQueue)
_, serialized_example = reader.read(queue) #按队列读取(Tensor)
features = tf.parse_single_example(serialized_example,features={ #解析Example(dict[Tensor])
'int':tf.FixedLenFeature([3],tf.int64),
'float':tf.FixedLenFeature([4],tf.float32),
'byte':tf.FixedLenFeature([1],tf.string),
})
integers = tf.cast(features['int'], tf.int64)
floats = tf.cast(features['float'], tf.float32)
bytes = tf.decode_raw(features['byte'], tf.uint8)
with tf.Session() as sess:
tf.global_variables_initializer().run()
coord = tf.train.Coordinator() #多线程处理(Coordinator)
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
print(sess.run([integers, floats, bytes]))
coord.request_stop()
coord.join(threads=threads)
TensorBoard 使我们能够实时监控漫长的训练过程。
# 设定监测目标
tf.summary.histogram('name',X) #将变量加入监测目标(Tensor)
tf.summary.scalar('name',X) #将标量加入监测目标(Tensor)
# tf.summary.image('name',X) #将图像加入监测目标(Tensor)
# tf.summary.text('name',X) #将文本加入监测目标(Tensor)
# tf.summary.audio('name',X) #将音频加入监测目标(Tensor)
summary_op = tf.summary.merge_all() #将以上监测目标汇总(Op-1)
# 配置监测程序
options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE) #配置运行时记录的信息对象(RunOptions)
run_metadata = tf.RunMetadata() #配置运行时记录的Proto对象
writer = tf.summary.FileWriter('logs',tf.get_default_graph()) #配置日志文件
# with tf.Session() as sess:
# summary = sess.run(summary_op,options=options,run_metadata=run_metadata)
# writer.add_run_metadata(run_metadata,'step %03d'%i) #添加间隔记录至日志文件
# writer.add_summary(summary,i) #添加检测目标汇总至日志文件
writer.close() #关闭日志文件
# 打开TensorBoard界面
# tensorboard --logdir='D:/logs' #命令行指令(而后浏览器中输入localhost:6006)
在以下的深度学习程序模板中,我们将尽可能多地囊括以上内容。该程序支持多 GPU 并行训练。
import os
import argparse
import numpy as np
from time import time
import tensorflow.compat.v1 as tf
tf.disable_eager_execution()
from sklearn.datasets import load_boston
from sklearn.preprocessing import scale
from sklearn.model_selection import train_test_split
parser = argparse.ArgumentParser(description='An example application for TF1.x.')
# model config
parser.add_argument('-num_inputs', default=13, type=int, help='input units.')
parser.add_argument('-num_hiddens', default=8, type=int, help='hidden units.')
parser.add_argument('-num_outputs', default=1, type=int, help='output units.')
parser.add_argument('-reg_lambda', default=0.01, type=float, help='rate for L2 regularization.')
# experiment config
parser.add_argument('-max_iters', default=1000, type=int, help='max iteration nums.')
parser.add_argument('-batch_size', default=128, type=int, help='batch size.')
parser.add_argument('-lr', default=0.1, type=float, help='peak learning rate.')
parser.add_argument('-decay_rate', default=0.95, type=float, help='decay rate for learning rate.')
parser.add_argument('-decay_per_steps', default=100, type=int, help='decay rate for learning rate.')
parser.add_argument('-grad_norm', default=0.05, type=float, help='max gradient clip value.')
parser.add_argument('-seed', default=120, type=int, help='random seed.')
parser.add_argument('-print_per_steps', default=200, type=int, help='print step.')
parser.add_argument('-gpu_devices', default='0', type=str, help='same with CUDA_VISIBLE_DEVICES.')
args = parser.parse_args()
np.random.seed(args.seed)
tf.set_random_seed(args.seed)
if args.gpu_devices:
os.environ['CUDA_VISIBLE_DEVICES'] = args.gpu_devices
class Iterator:
def __init__(self,data,label,batch_size=1000000):
self.data = data
self.label = label
self.batch_size = batch_size
self.now = 0
self.num_batches = (len(self.data)-1) // self.batch_size + 1
def shuffle(self):
id_list = np.arange(len(self.data))
new_id_list = np.random.permutation(id_list)
self.data = self.data[new_id_list]
self.label = self.label[new_id_list]
def next(self):
if self.now < self.num_batches:
batch_data = self.data[self.now*self.batch_size:(self.now+1)*self.batch_size]
batch_label = self.label[self.now*self.batch_size:(self.now+1)*self.batch_size]
self.now += 1
return batch_data,batch_label
else:
self.now = 0
raise StopIteration
def main():
# reset graph
tf.reset_default_graph()
# load data
boston = load_boston()
data = scale(boston.data)
label = scale(boston.target.reshape(-1,1))
X_train,X_test,y_train,y_test = train_test_split(data,label,test_size=0.1)
del data,label
# build iterator
train_iterator = Iterator(X_train,y_train,args.batch_size)
test_iterator = Iterator(X_test,y_test)
# build network
with tf.variable_scope('net',reuse=tf.AUTO_REUSE), tf.device('/cpu:0'):
# general computation
X = tf.placeholder(shape=(None,args.num_inputs), dtype=tf.float32)
y = tf.placeholder(shape=(None,1), dtype=tf.float32)
global_step = tf.Variable(0,trainable=False,name='step')
lr = tf.train.exponential_decay(args.lr,global_step,args.decay_per_steps,args.decay_rate,staircase=True)
optimizer = tf.train.AdamOptimizer(learning_rate=lr)
# paralleled computations
grads = []
Xs = tf.split(X,len(args.gpu_devices.split(',')),axis=0)
for i,device in enumerate(args.gpu_devices.split(',')):
with tf.device('/gpu:%s'%device):
W1 = tf.Variable(tf.random_normal(shape=(args.num_inputs,args.num_hiddens)),name='layer_0/w')
b1 = tf.Variable(tf.random_normal(shape=(1,args.num_hiddens)),name='layer_0/b')
W2 = tf.Variable(tf.random_normal(shape=(args.num_hiddens,args.num_outputs)),name='layer_1/w')
b2 = tf.Variable(tf.random_normal(shape=(1,args.num_outputs)),name='layer_1/b')
Z1 = tf.nn.tanh(tf.add(tf.matmul(Xs[i],W1), b1))
Z2 = tf.nn.sigmoid(tf.add(tf.matmul(Z1,W2), b2))
loss = tf.reduce_mean(tf.square(y - Z2)) + args.reg_lambda * \
(tf.reduce_mean(tf.square(W1)) + \
tf.reduce_mean(tf.square(W2)))
grads.append(optimizer.compute_gradients(loss))
# aggregate gradients
average_grads = []
for g_v_ in zip(*grads):
split_grads = [item[0] for item in g_v_ if item[0] is not None]
if split_grads:
average_grads.append((tf.multiply(tf.add_n(split_grads),1/len(split_grads)),g_v_[0][1]))
else:
average_grads.append((None,g_v_[0][1]))
# clip gradients
for i,(g,v) in enumerate(average_grads):
if g is not None:
average_grads[i] = (tf.clip_by_norm(g, args.grad_norm), v)
# apply gradients
train_op = optimizer.apply_gradients(average_grads)
# operation
update_step_op = tf.assign_add(global_step,1)
# training
tic = time()
config = tf.ConfigProto(allow_soft_placement=True,log_device_placement=True)
config.gpu_options.allow_growth = True
with tf.Session(config=config) as sess:
# initialization
sess.run(tf.global_variables_initializer())
for i in range(args.max_iters):
# train
train_iterator.shuffle()
while True:
try:
X_batch, y_batch = train_iterator.next()
except StopIteration:
break
l,step,_ = sess.run((loss,global_step,train_op), feed_dict={X: X_batch, y: y_batch})
sess.run(update_step_op)
# evaluate
if step % args.print_per_steps == 0:
test_l = 0
while True:
try:
X_batch, y_batch = test_iterator.next()
except StopIteration:
break
test_l += sess.run(loss, feed_dict={X: X_batch, y: y_batch})
print('epoch %d, step %d, train loss %.4f, test loss %.4f, time %.2fs'
%(i,step,l,test_l,time()-tic))
tic = time()
# test
if i == args.max_iters - 1:
test_l = 0
while True:
try:
X_batch, y_batch = test_iterator.next()
except StopIteration:
break
test_l += sess.run(loss, feed_dict={X: X_batch, y: y_batch})
print('epoch %d, step %d, train loss %.4f, test loss %.4f, time %.2fs'
%(i,step,l,test_l,time()-tic))
tic = time()
if __name__ == '__main__':
main()