一、前言
我们为什么要存日志?因为我们希望通过tensorboard组件让它来读日志,然后分析这个日志之后,在外部页面给我们展示tensorflow一个流程,整个的计算图。存日志所放的路径跟我们后面启动tensorboard组件所要读取的路径必须是一致的。那如何启动tensorboard呢?
在python里面,先导入tensorflow,然后导入tensorboard,在安装的时候,它自动给你安装tensorboard。
后面是日志的路径。
前面是版本,后面给了一个tensorboard路径,粘贴这个路径,放在浏览器里面打开。如图:
二、代码解释
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
max_steps = 1000#最大迭代次数
learning_rate = 0.001#学习率
dropout = 0.9# 保留的数据
data_dir = './MNIST_data'
log_dir = './logs/mnist_with_summaries'
mnist = input_data.read_data_sets(data_dir, one_hot=True)#把y这一列变成one_hot编码
sess = tf.InteractiveSession()#初始
with tf.name_scope('input'):#with块中名字才是最重要的 一个块
x = tf.placeholder(tf.float32, [None, 784], name='x-input')
y_ = tf.placeholder(tf.float32, [None, 10], name='y-input')
with tf.name_scope('input_reshape'):
# 784维度变形为图片保持到节点
image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])#-1代维度表不管有多少个 1代表1个通道 28*28个
tf.summary.image('input', image_shaped_input, 10)#当做一个图片存起来
# 定义神经网络的初始化方法
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)#截断的正态分布 这里可以用he_initinelize
return tf.Variable(initial)#创建一个变量
def bias_variable(shape):#截距
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
# 以下代码是关于画图的
# 定义Variable变量的数据汇总函数,我们计算出变量的mean、stddev、max、min
# 对这些标量数据使用tf.summary.scalar进行记录和汇总
# 使用tf.summary.histogram直接记录变量var的直方图数据
def variable_summaries(var):
with tf.name_scope('summaries'):
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.scalar('stddev', stddev)
tf.summary.scalar('max', tf.reduce_max(var))
tf.summary.scalar('min', tf.reduce_min(var))
tf.summary.histogram('histogram', var)
# 设计一个MLP多层神经网络来训练数据
# 在每一层中都对模型数据进行汇总
def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):#定义一个隐藏层 input_dim上一层 output_dim本层输出
with tf.name_scope(layer_name):
with tf.name_scope('weights'):
weights = weight_variable([input_dim, output_dim])#shape传进来是上一层输入,本层输出 如果是MLP,就是全连接可以知道参数个数
variable_summaries(weights)#把权重的各个指标(方差,平均值)进行总结
with tf.name_scope('biases'):
biases = bias_variable([output_dim])
variable_summaries(biases)
with tf.name_scope('Wx_plus_b'):
preactivate = tf.matmul(input_tensor, weights) + biases#带到激活函数之前的公式
tf.summary.histogram('pre_activations', preactivate)
activations = act(preactivate, name='activation')#运用激活函数 函数里面传函数 高阶函数
tf.summary.histogram('activations', activations)
return activations
# 我们使用刚刚定义的函数创建一层神经网络,输入维度是图片的尺寸784=28*28
# 输出的维度是隐藏节点数500,再创建一个Dropout层,并使用tf.summary.scalar记录keep_prob
hidden1 = nn_layer(x, 784, 500, 'layer1')#建立第一层 隐藏层
with tf.name_scope('dropout'):
keep_prob = tf.placeholder(tf.float32)
tf.summary.scalar('dropout_keep_probability', keep_prob)
dropped = tf.nn.dropout(hidden1, keep_prob)#应用drop_out函数 保留下来的数据
# 然后使用nn_layer定义神经网络输出层,其输入维度为上一层隐含节点数500,输出维度为类别数10
# 同时激活函数为全等映射identity,暂时不使用softmax
y = nn_layer(dropped, 500, 10, 'layer2', act=tf.identity)#建立第二层 输出层
# 使用tf.nn.softmax_cross_entropy_with_logits()对前面的输出层的结果进行Softmax
# 处理并计算交叉熵损失cross_entropy,计算平均的损失,使用tf.summary.scalar进行统计汇总
with tf.name_scope('cross_entropy'):
diff = tf.nn.softmax_cross_entropy_with_logits(logits=y, labels=y_)#输出层给的结果logits=y #每一行的y是有10个数预测10个值 然后利用这10个值做归一化 然后具备一个概率的含义 第二步计算交叉熵
with tf.name_scope('total'):
cross_entropy = tf.reduce_mean(diff)#平均损失
tf.summary.scalar('cross_entropy', cross_entropy)
# 下面使用Adam优化器对损失进行优化,同时统计预测正确的样本数并计算正确率accuracy,汇总
with tf.name_scope('train'):
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)#AdamOptimizer比SGD更好一些,下降速度更快,更容易计算局部最优解 ,当数据量大的时候不如SGD
#learning_rate虽然是固定的,后面会自适应,根据上一次的结果 所以大数据量的话,不如定义好策略,这样省时间
with tf.name_scope('accuracy'):
with tf.name_scope('correct_prediction'):
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))#预测值最大的索引 和真实值的索引
with tf.name_scope('accuracy'):
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))#true 1 false 0 reduce_mean 是一个比例得到的结果
tf.summary.scalar('accuracy', accuracy)
# 因为我们之前定义了太多的tf.summary汇总操作,逐一执行这些操作太麻烦,
# 使用tf.summary.merge_all()直接获取所有汇总操作,以便后面执行
merged = tf.summary.merge_all()
# 定义两个tf.summary.FileWriter文件记录器再不同的子目录,分别用来存储训练和测试的日志数据
train_writer = tf.summary.FileWriter(log_dir + '/train', sess.graph)
test_writer = tf.summary.FileWriter(log_dir + '/test')
# 同时,将Session计算图sess.graph加入训练过程,这样再TensorBoard的GRAPHS窗口中就能展示
# 整个计算图的可视化效果,最后初始化全部变量
tf.global_variables_initializer().run()
# 定义feed_dict函数,如果是训练,需要设置dropout,如果是测试,keep_prob设置为1
def feed_dict(train):
if train:#如果是训练的话需要Droupout 测试的时候不要Droupout
xs, ys = mnist.train.next_batch(100)#每一次拿一批次数据去训练
k = dropout
else:
xs, ys = mnist.test.images, mnist.test.labels#真正测试的话全部测试,不是拿一批次的数据了
k = 1.0
return {x: xs, y_: ys, keep_prob: k}
# 执行训练、测试、日志记录操作
# 创建模型的保存器
saver = tf.train.Saver()
for i in range(max_steps):#max_steps迭代次数
if i % 10 == 0:#每执行10次的时候汇总一次信息 然后计算测试集的一次准确率 因为传的是Flase
summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))
test_writer.add_summary(summary, i)#然后写出
print('Accuracy at step %s: %s' % (i, acc))
else:
if i % 100 == 99:#如果到100次
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
run_metadata = tf.RunMetadata()#保存的是元数据信息
summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True))# summary写的是跑完之后的数据
train_writer.add_run_metadata(run_metadata, 'step%03d' % i)
train_writer.add_summary(summary, 1)#写文件
saver.save(sess, log_dir + 'model.ckpt', i)
print('Adding run metadata for', i)
else:#不是10次,也不是100次 ,说明其他批次,则训练数据
summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True))#训练
train_writer.add_summary(summary, i)
train_writer.close()
test_writer.close()
#tensorboard 参考博客
# https://www.tensorflow.org/guide/summaries_and_tensorboard?hl=zh-cn
#启动命令
# tensorboard --logdir=/tmp/tensorflow/mnist
我们依次解释下:
mnist = input_data.read_data_sets(data_dir, one_hot=True)#把y这一列变成one_hot编码
首先读数据,如果加上one_hot=true,就是y不是一个,y每行有十个;
sess = tf.InteractiveSession()#初始
打开一个sess。
with tf.name_scope('input'):#with块中名字才是最重要的 一个块
x = tf.placeholder(tf.float32, [None, 784], name='x-input')
y_ = tf.placeholder(tf.float32, [None, 10], name='y-input')
Name_scope,有两个输入,就写在下面x, m行784列,y_ ,m行10 列,用placeholder创建好一个tensor节点。
with tf.name_scope('input_reshape'):
# 784维度变形为图片保持到节点
image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])#-1代维度表不管有多少个 1代表1个通道 28*28个
tf.summary.image('input', image_shaped_input, 10)#当做一个图片存起来
为了好画图,把这里的图片进行一下reshape,tf.reshape传x,是4维的,-1代表所有的m行,因为x是m行784列;28,28是高和宽,这样写是把二维矩阵变成m*28*28三维矩阵,不剩什么东西了,m*784等价于m×28×28,所以如果我用四维的话,最后一个只能是1。
通常图片处理都会把图片数据分成四个维度,最后的1代表图片的颜色通道,mnist数据是黑白的,channel频道,单通道就是1。
reshape之后转成四个维度,目的是为了交给tf.summary.image函数,它要的就是四维的数组,它就可以画图,后面的10是告诉它里面有10个类别。
我们先看下启动的tensorboard截图如下:
这里面的图像就是读的logs里面的内容。scalars标量,images图片,graphs是DAG有向无环图,histograms是柱状图。
代码里面,tf.summary.image把一些图像进行reshape,然后画图,它画在了images目录下面,双击缩小,就是原始图,28*28大小。我们就可以在tensorboard里看到这些数据。
再来看下这个代码:
with tf.name_scope('input'):#with块中名字才是最重要的 一个块
x = tf.placeholder(tf.float32, [None, 784], name='x-input')
y_ = tf.placeholder(tf.float32, [None, 10], name='y-input')
with tf.name_scope('input_reshape'):
# 784维度变形为图片保持到节点
image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])#-1代维度表不管有多少个 1代表1个通道 28*28个
tf.summary.image('input', image_shaped_input, 10)#当做一个图片存起来
所以这里的10,不是十个分类,是让它给画十张图片,训练集、测试集各10个。
至于这个x是哪十个,得看placeholder占位符传的是什么,它就会画10个,把这个参数改了,再去跑,刷新,就可以有更多或者更少的图片。
在tensorboard里最想要的是main graph,就是DAG图,为什么只有几个节点?如图:
x和y_一定在input里面,双击input后得到如下所示:
X是m行784列,它去哪、给谁用去了,可以根据连线跟踪到。 我们具体看下连线,可知
X input其中一个给到reshape(图中first连线),另一个到layer1走正向传播(图中second连线),还有一个交给训练集(图中three连线)。
因为走反向传播,链式法则求导的时候,有可能拿到正向传播里面的每一个节点的x,然后反过来算里面的梯度。所以需要用到x给到train。
线越粗,数据量就越大,线越细,数量量就越小。
再看下这个代码:
with tf.name_scope('input_reshape'):
# 784维度变形为图片保持到节点
image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])#-1代维度表不管有多少个 1代表1个通道 28*28个
tf.summary.image('input', image_shaped_input, 10)#当做一个图片存起来
和对应的image上的节点:
tf.reshape画图,reshape之后把结果放到input里面去,可以在images里找到画的图。
再接着往下看代码:
# 定义神经网络的初始化方法
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)#截断的正态分布 这里可以用he_initinelize
return tf.Variable(initial)#创建一个变量
def bias_variable(shape):#截距
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
有x就要去走正向传播,然后定义一下w的初始化,定义一下b的初始化,因为每一层传播的时候都要去做这个事情,所以可以把它的初始化放到前面抽象一下。truncated_normal传一下w的形状,stddev为0.1,没有太大的关系,在过程当中会去调整的。Bias0.1,也没关系,都会去调整。
接下来设计一个MLP多层神经网络来训练数据,在每一层中都对模型数据进行汇总。
def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):#定义一个隐藏层 input_dim上一层 output_dim本层输出
with tf.name_scope(layer_name):
with tf.name_scope('weights'):
weights = weight_variable([input_dim, output_dim])#shape传进来是上一层输入,本层输出 如果是MLP,就是全连接可以知道参数个数
variable_summaries(weights)#把权重的各个指标(方差,平均值)进行总结
with tf.name_scope('biases'):
biases = bias_variable([output_dim])
variable_summaries(biases)
with tf.name_scope('Wx_plus_b'):
preactivate = tf.matmul(input_tensor, weights) + biases#带到激活函数之前的公式
tf.summary.histogram('pre_activations', preactivate)
activations = act(preactivate, name='activation')#运用激活函数 函数里面传函数 高阶函数
tf.summary.histogram('activations', activations)
return activations
nn_layer就是在构建每一层神经网络,重点看summaries,summaries都是在tensorflow上可以展示的内容,tf.summary.histogram中pre_activations,激活函数之前的结果就是z, 还有激活函数后的概率p,它们都画到图里去了。
在histogram可以看到结果z和relu(z),
我们再具体的看激活函数之前的结果Z,
纵轴和代码有关,max_steps最大迭代此处是1000次,纵轴就是迭代的次数,曲线是激活函数之前的结果,它是x和w相乘之后再加上b的结果,每个结果都在图上。因为每次迭代有很多条数据,每一层很多个神经元,每个神经元都有激活函数之前的结果,它基本还是正态分布的。
在第一个隐藏层加上激活函数之后,它就不再是正态分布了,很多数据被抑制了。Relu函数中,小于0的被归为0,大于0才是 z,只剩了下一半。如图:
bias的变化,随着迭代次数的增多,里面每个bias都差不多。 如下图:
W的最高点的值没有太大变化,也就是说数量集没有太大的变化,但是它还是在0附近。如下图: