以前学习用的资料,整理电脑,放在这里,以备后续使用
TensorBoard简介
TensorBoard是TensorFlow自带的一个强大的可视化工具,也是一个Web应用程序套件。TensorBoard目前支持7种可视化,Scalars,Images,Audio,Graphs,Distributions,Histograms和Embeddings。其中可视化的主要功能如下。
(1)Scalars:展示训练过程中的准确率、损失值、权重/偏置的变化情况。
(2)Images:展示训练过程中记录的图像。
(3)Audio:展示训练过程中记录的音频。
(4)Graphs:展示模型的数据流图,以及训练在各个设备上消耗的内存和时间。
(5)Distributions:展示训练过程中记录的数据的分部图。
(6)Histograms:展示训练过程中记录的数据的柱状图。
(7)Embeddings:展示词向量后的投影分部。
TensorBoard通过运行一个本地服务器,来监听6006端口。在浏览器发出请求时,分析训练时记录的数据,绘制训练过程中的图像。TensorBoard的可视化界面如下图所示
如图相册的菜单所示,可以依次绘制出主要功能的界面图像,下面分别介绍这些菜单页面具体功能,这里采用的是mnist_with_summaries的例子
Scalars面板
scalars面板的左侧是一些选项,包括Ignore outlines in chart scaling(不按照整表的范文显示)、data downloadlinks(数据下载链接),smoothing(图像的曲线平滑程度)以及Horizontal Axis
(水平轴)的表示,其中水平轴的表示分为3种(STEP代表迭代次数,RELATIVE代表按照训练集和测试集的相对值,WALL代表按照时间)如下图所测所示,右侧给出了准确率
和交叉熵损失函数值的变化曲线(迭代次数是1000次)。
Scalars面板中还绘制了每一层的偏置(biases)和权重(weights)的变化曲线,包括每次迭代的最大值、最小值、平均值和标准差等,如下图所示
IMAGES面板
下图展示了训练数据及和测试数据集进过预处理后图片的样子
AUDIO面板
AUDIO面板是展示训练过程中处理的音频数据。mnist_with_summaries中不含有音频例子,这里不做展示。
GRAPHS面板
GRAPHS面板是对理解神经网络结构最优帮助的一个面板,他直观的展示了数据流图。下图界面中结点之间的连线即为数据流,连线
越粗,说明两个结点之间流动的张量(tensor)越多。
在graph面板的左侧,可以选择迭代步骤。可以用不同的颜色来表示不同的Structrue(整个数据流图的结构),或者用不同的Color来表示不同的Device(设备)。例如
使用多个GPU时,各个节点分别使用的GPU不同。
当选择特定的某次迭代(如第899次)时,可以显示出各个节点的Compute time(计算时间)以及Memory(内存消耗),如下图所示
DISTRIBUTIONS面板
distributions面板和histograms面板类似,只不过是用平面来表示来自特定层的激活前后、权重和偏置的分布。下图展示的是激活之前和激活之后
数据分布。
HISTOGRAMS面板
histograms面板立体的展来自特顶层的激活前后、权重和偏置的分布。下图展示的是激活之前和激活之后的分布数据。
EMBEDDINGS面板
EMBEDDINGS面板在minst例子中无法展示。在以后的可视化例子中会有展示,这里先不做介绍。
以上就是tensorboard主要面板的介绍,下一次进行可视化例子的介绍
作为TensorFlow
的一项极其亮眼的功能,TensorBoard
给我们提供了极其方便而强大的可视化环境。它可以帮助我们理解整个神经网络的学习过程、数据的分布、性能瓶颈等等。
当我们要对模型的正确率、loss
值、学习速度等标量进行可视化时,经常会用到折线图。
上面是在训练MNIST
文字识别模型时,对各阶段的日志记录进行可视化后所得到的折线图(左侧是测试集,右侧是训练集),可以看到loss
值在逐步减少,模型的学习正在往好的方向发展。
下面是以及其友好的方式展示MNIST
的训练集数据的示例截图。
在我们要进行Data Augmentation
之前,十分有必要进行数据验证和数据清洗,此时TensorBoard
就能给我们带来不少的便利。
与展示图像类似,这项功能在数据验证和清洗时十分有用,甚至可以进行音量的调节和音频的下载。
当我们想知道神经网络各层的权重和偏移量的初始值,以及它们在各阶段是如何发生变化的,从而确认到底有没有发生梯度消失的“悲剧”,可以利用这项功能。
如果我们想直观地观察神经网络的结构,从而帮助我们定位模型的性能瓶颈所在,可以像下面这样做。
下面是利用t-SNE
算法对MNIST
的图像数据进行降维处理后得到的大致分布图。
首先介绍TensorBoard
中各个记号到底代表什么意思
记号 | 含义 |
---|---|
![]() |
某个name scope内部的所有节点,双击可以看到内部详情 |
![]() |
不连续的节点序列 |
![]() |
单个节点(变量) |
![]() |
单个节点(常量) |
![]() |
统计信息summary节点 |
![]() |
各操作间的数据流 |
![]() |
各操作间的控制流 |
![]() |
输入张量转换节点的引用 |
随着神经网络越发复杂,节点数量会发生爆炸式增长,这时候使用多层name scope
把它们进行管理相当有必要,尤其是在调参和debug
阶段。
下面这幅图可以清晰地展示name scope
在TensorBoard
发挥的巨大作用:
编程写法也相当简单:
with tf.name_scope('loss'):
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=labels))
tf.summary.scalar('loss', loss)
那么,最终计算并输出的loss
的完整名称就为loss/loss
。
Device View
表示的是各个阶段GPU
和CPU
的占用情况,如下图所示,绿色的是GPU
,蓝色的是CPU
。
如下图所示,左边的是内存使用率情况,右边的是计算资源使用率情况,他们的值越高,图的颜色就越深。
于是,我们就可以得知cross_entropy
节点对于内存的消耗并不高,但是却十分考验硬件的计算能力。
编程写法十分简单明了,对tf.FileWriter
添加tf.RunMetadata
即可:
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
run_metadata = tf.RunMetadata()
summary, _ = sess.run([merged, train_step],
feed_dict={x: ..., y: ...},
options=run_options,
run_metadata=run_metadata)
train_writer.add_run_metadata(run_metadata, 'step%03d' % i)
train_writer.add_summary(summary, i)
仔细观察计算图中连接各节点的边,就可以发现上面写着有对应Tensor
的维度。
如下图,我们可以确认卷积神经网络的Convolution
层和Pooling
层的参数数量到底是否正确。
tf.summary.scalar('loss', loss)
这里,第一参数时标量名,第二参数是标量值
tf.summary.histogram('bias', bias)
tf.summary.image('preprocess', tf.reshape(images, [-1, 28, 28, 1]), 10)
这里解释一下第二参数的含义,reshape
里面的四个参数分别代表[图像数, 每幅图的高度, 每幅图的宽度, 每幅图的通道数],-1
表示根据实际数据(在这里是images
)进行动态计算,上面的10
表示最多展示十幅图。
tf.summary.audio('audio', audio, sampling_frequency)
audio
是一个三维或者二维tensor
,含义是[音频数, 每个音频的帧数, 每个音频的通道数]或者[音频数, 每个音频的帧数]。
sampling_frequency
从名字就可以看出来了,就是音频的采样率。
从明天起,做一个幸福的人
喂马、劈柴,周游世界
从明天起,关心粮食和蔬菜
我有一所房子,面朝大海,春暖花开
--------致 海子
没有海子的浪漫,但有同样的情怀,祝福每一个人,愿意分享自己的学习成果。海子的明天,彼岸,我的今天,当下。今天分享对scalar的学习成果。
scalar是用来显示accuracy,cross entropy,dropout等标量变化趋势的函数。 通过scalar可以看到这些量随着训练加深的一个逐步变化的过程,进而可以看出我们模型的优劣。
注意:
1)scalar只能用于单个标量的显示,不能显示张量;
2)scalar可以显示多次训练的结果
# 观察值
correct_prediction = tf.equal(self.labels, tf.argmax(logits, 1))
self.accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))
print(correct_prediction)
tf.summary.scalar('optimizer', self.accuracy) # 记录优化器的变化
2)按照之前的介绍进行操作,打开IE中的tensorboard,就可以看到scalar:
图中横坐标表示训练次数,纵坐标表示该标量的具体值,从这张图上可以看出,随着训练次数的增加,损失函数的值是在逐步减小的。
可以看到图上有直线,有曲线,这时使用相同的配置多次训练,然后相同的数据在同一个图上显示的结果,进入该log所在的文件夹,删除历史记录,仅仅保留最新的结果,就会如下图一样,出现一个比较干净的图。
3)tensorboard左侧的工具栏上的smoothing,表示在做图的时候对图像进行平滑处理,这样做是为了更好的展示参数的整体变化趋势。如果不平滑处理的话,有些曲线波动很大,难以看出趋势。0 就是不平滑处理,1 就是最平滑,默认是 0.6。
4)工具栏中的Horizontal Axis指的是横轴的设置:
STEP:默认选项,指的是横轴显示的是训练迭代次数
RELATIVE:指相对时间,相对于训练开始的时间,也就是说是训练用时 ,单位是小时
WALL:指训练的绝对时间
train_writer = tf.summary.FileWriter(log_dir + '/train', self.sess.graph)
那么第二次训练就可以设置为:
train_writer = tf.summary.FileWriter(log_dir + '/train_1', self.sess.graph)
这样当按照常规步骤打开tensorboard时,在面板的左侧就会显示不同的训练结果文件,如果要打开,则勾选相应的文件即可。
如上图所示,当勾选多个时,在图中就会以不同的颜色显示不同的图像。
def variable_summaries(self,var):
with tf.name_scope('summaries'):
# 计算参数的均值,并使用tf.summary.scaler记录
mean = tf.reduce_mean(var)
tf.summary.scalar('mean', mean)
# 计算参数的标准差
with tf.name_scope('stddev'):
stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
# 使用tf.summary.scaler记录记录下标准差,最大值,最小值
tf.summary.scalar('stddev', stddev)
tf.summary.scalar('max', tf.reduce_max(var))
tf.summary.scalar('min', tf.reduce_min(var))
# 用直方图记录参数的分布
tf.summary.histogram('histogram', var)
要显示那个张量,就可以直接用这个函数来完成。
self.conv = tf.layers.Conv2D(
filters=self.n_filter,
kernel_size=[self.y_size, self.x_size],
strides=[self.y_stride, self.x_stride],
padding='SAME',
data_format=self.data_format,
activation=None,
use_bias=not self.batch_normal,#在使用批标准化技术时,一般只要权重,不要偏移
kernel_initializer=tf.constant_initializer(weight_init_value),
trainable=True,
name='%s_conv' % (self.name))
self.variable_summaries(weight_init_value)
这段代码就可以显示卷积层的卷积核的权重值。
注意这里将函数 tf.layers.Conv2D的输入参数kernel_initializer,的初始化值weight_init_value作为要显示的参数,就可以实现对卷积核权重值的显示。在namescope的作用下,使用上述方法可以显示各个层中的这个参数。如下图所示:
2018年12月2日,发现上面写的对张量的显示方法是有问题的,当时写的是可以用kernel_initializer参数来初始化要显示的变量,这样虽然能显示出结果,但是这个结果就是图在初始化时候的值,在整个运算过程中是不变的,因此没有意义。
有效的方法如下:
def get_output(self, inputs, is_training=True):
with tf.name_scope('%s_cal' % (self.name)) as scope:
# hidden states
self.hidden = self.conv(inputs=inputs)
self.variable_summaries(self.conv.weights)
# batch normalization 技术
if self.batch_normal:
self.hidden = self.bn(self.hidden, training=is_training)
# activation
if self.activation == 'relu':
self.output = tf.nn.relu(self.hidden)
elif self.activation == 'tanh':
self.output = tf.nn.tanh(self.hidden)
elif self.activation == 'leaky_relu':
self.output = self.leaky_relu(self.hidden)
elif self.activation == 'sigmoid':
self.output = tf.nn.sigmoid(self.hidden)
elif self.activation == 'none':
self.output = self.hidden
return self.output
之前,是在层定义过程中加入数据采集的语句,这样是有问题的,应该在层运算get_output的过程中加入数据采集语句。self.variable_summaries(self.conv.weights)
而且,数据采集的对象不能是kernel_initializer,而应该是层权重这个变量:self.conv.weights,注意是weights,而不是get_weights(),试了好多次才成功。
正确的结果如下:
1)可以看到标量的变化趋势。如果我们看到损失函数的值随着训练迭代次数的增加,逐渐减小,则说明我们的模型是有效的,可以通过训练得到一个好的结果;反之,如果发现损失函数没有变小,或者震荡很大,那么则说明我们的模型不是很好,需要调整。
2)可以多图对比。我们调整模型的参数,进行多次训练,进行多图对比,就可以发现哪次的图形好看,就说明这次的模型参数设置更为科学。
参考文献:
1、https://blog.csdn.net/gg_18826075157/article/details/78440766
2、https://blog.csdn.net/wgj99991111/article/details/84294450