前面是关于线性拟合和构建简单神经网络的内容,对于构建过程的可视化,tensorflow提供了一个强大的工具tensorboard。
Tensorboard的使用
tensorflow2与tensorflow1不同使用的动态图,可以说tensorflow中是没有”图”的概念的(reference3)。个人认为对于morvan大佬的自建神经网络,在tensorflow2情况下是十分受阻碍的。
首先使用tf.summary.create_file_writer函数来创建SummaryWriter对象,tensorflow2下使用tensorboard构建神经网络的结构,则还需要使用tf.function装饰器,获得可调用的tensorflow图。然后使用tf.summary.trace_on/tf.summary.trace_export这组函数来进行追踪计算图的信息。
让人头疼的是对于输入的训练数据,以及初始化的参数,这些由于受到动态图的影响只能成为variableop_resource(下图中layer1_w…等名称),动静结合的痛苦,个人也没找到很好的解决方式。

import tensorflow as tf import numpy as np from datetime import datetime def add_layer(inputs, Weights, biases, n_layer, activation_function=None): layer_name = 'layer%s' % n_layer with tf.name_scope(layer_name): with tf.name_scope('Wx_plus_b'): Wx_plus_b = tf.matmul(inputs, Weights) + biases if activation_function is None: outputs = Wx_plus_b else: outputs = activation_function(Wx_plus_b) return outputs def loss(predicted_y, target_y): return tf.math.reduce_mean(tf.math.square(predicted_y - target_y)) def train_one_step(optimizer, x_data, y_data): with tf.GradientTape() as tape: l1 = add_layer(x_data, Wl, bl, n_layer=1, activation_function=tf.nn.relu) prediction = add_layer(l1, Wp, bp, n_layer=2, activation_function=None) with tf.name_scope('loss'): loss_val = loss(prediction, y_data) with tf.name_scope('trains'): grads = tape.gradient(loss_val, [Wl, bl, Wp, bp]) # 这里只使用Wl,bl也能得到结果 optimizer.apply_gradients(zip(grads, [Wl, bl, Wp, bp])) return loss_val @tf.function def train(optimizer, x_data, y_data): loss_val = train_one_step(optimizer, x_data, y_data) with summary_writer.as_default(): tf.summary.scalar('loss', loss_val, step=0) return loss_val x_data = np.linspace(-1, 1, 300)[:, np.newaxis] noise = np.random.normal(0, 0.05, x_data.shape) y_data = np.square(x_data) - 0.5 + noise x_data = x_data.astype(np.float32) y_data = y_data.astype(np.float32) Wl = tf.Variable(tf.random.normal((1, 10)), name='Wl') Wp = tf.Variable(tf.random.normal((10, 1)), name='Wp') bl = tf.Variable(tf.zeros((1, 10)) + 0.1, name='bl') bp = tf.Variable(tf.zeros((1, 1)) + 0.1, name='bp') optimizer = tf.optimizers.SGD(learning_rate=0.1) # 优化器 stamp = datetime.now().strftime("%Y%m%d-%H%M%S") logdir = 'logs/%s' % stamp summary_writer = tf.summary.create_file_writer(logdir) # 创建file writer tf.summary.trace_on() last_loss = train(optimizer, x_data, y_data) with summary_writer.as_default(): tf.summary.trace_export( name="my_func_trace", step=0, profiler_outdir=logdir)
keras model的Tensorboard使用
相比于上面的通过,tensorflow对于keras model则做了封装。三句话解决战斗。不同于上面使用函数进行实现神经网络,keras model将参数变量加入到了model.variables中,在使用keras自定义模型时,则可以利用model.variables进行tensorboard的图构建的追踪。
import tensorflow as tf from tensorflow import keras import numpy as np from datetime import datetime logdir = "logs\\" + datetime.now().strftime("%Y%m%d-%H%M%S") tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir) # 拟合数据的创建 x_data = np.linspace(-1, 1, 300)[:, np.newaxis] noise = np.random.normal(0, 0.05, x_data.shape) y_data = np.square(x_data) - 0.5 + noise x_data = x_data.astype(np.float32) noise = noise.astype(np.float32) y_data = y_data.astype(np.float32) # print(x_data.shape) # 定义一个一个模型 model = keras.models.Sequential([ keras.layers.Dense(10, activation='relu'), keras.layers.Dense(1, activation='linear') ]) model.compile( optimizer=tf.keras.optimizers.SGD(0.01), loss='mse') model.fit(x_data, y_data, epochs=100, callbacks=[tensorboard_callback])
所以在利用keras model构建的神经网络图就很简洁,结构也突出,所以尽量使用keras model来建立自己的神经网络。


关于Lazy loading
首先在Morvan博客-tensorflow2学习(二)改写代码时,稍微阅读了tensorflow的源码,就看到了lazyloader这个类。
在tensorflow1中,需要将构建的静态图加入到session中,进行计算,在session是不推荐使用tf.add的类似操作的,这会导致lazy loading(Lazy loading is a term that refers to a programming pattern when you defer declaring/initializing an object until it is loaded.),虽然不是bug,但是当操作复杂且多的情况下,会导致内存溢出。
x = tf.Variable(10, name='x') y = tf.Variable(20, name='y') z = tf.add(x, y) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for _ in range(10): sess.run(z) writer.close()
x = tf.Variable(10, name='x') y = tf.Variable(20, name='y') with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for _ in range(10): sess.run(tf.add(x, y)) # create the op add only when you need to compute it writer.close()
正常 lazy loading
正如上面两段代码(reference5),使用print tf.get_default_graph().as_graph_def(),打印出它们的图,你会发现for循环的存在使得lazy loading会存10份add操作,而正常只会存一份add操作。
在改写morvan大佬代码时,对于train过程,train过程是加入了循环来优化参数来减小loss,由于动静结合的问题,使得在tf.function的静态图下存在tf操作,最终在使用tensorboard时,内存跑到3GB溢出(暂时对于修改morvan大佬代码的tensorboard本人不太成功归结于动静结合)。
所以在面对lazy loading问题,首先第一要考虑减少在for循环中减少tf操作,其次需要考虑结构化Tensorflow模型代码,个人也在看如何结构化tensorflow的博文(reference6),最终去尝试解决上面的“动静结合”的问题。
系列:
Morvan博客-tensorflow2学习(一)
Morvan博客-tensorflow2学习(二)
Morvan博客-tensorflow2学习(三)
tensorflow2学习(四)
reference
1. https://morvanzhou.github.io/tutorials/machine-learning/tensorflow/5-16-transfer-learning/
2. https://tensorflow.google.cn/guide
3. https://github.com/tensorflow/tensorboard/issues/1961
4. https://github.com/tensorflow/tensorboard/issues/1929
5. http://web.stanford.edu/class/cs20si/lectures/notes_02.pdf
6. https://danijar.com/structuring-your-tensorflow-models/