Tensorflow-2-Tensorboard使用

2023-11-07

一、概述

机器学习如此复杂,训练模型的时候,摸不清背后到底是如何运行的。自己设置的参数和关键变量,如果能看到在训练时的变化情况,可以为后面的参数调优阶段提供很大的便利。

Tensorboard就是这样一个工具。

它刻意将模型抽象成图像,tensor每一步是如何流动的,一目了然。

通过适当的代码设置,还能将指定的关键变量在训练时的变化情况绘制成曲线图,以便训练完成后观察变量的变化情况,来更加准确定位问题。

这篇文章简单介绍一下tensorboard的基本用法。

二、Tensorboard使用

tensorboard(以下简称tb)的操作,从创建一个FileWriter开始。

在接下来的代码中,我参照CS231N课程的数据集例子,用tensorflow(以下简称tf)写了一个Logistic Regression,并以此来说明tb的基本用法。

用到的notebook我的github上可以找到。使用之前,请确保执行

conda env create -f environment_tf.yml

来创建和我一样的运行环境。如有问题,可以留言或者ISSUE。

创建好环境之后,运行

source activate tb-lab

激活conda环境,然后运行

jupyter notebook

来使用本例中的notebook

下面的示例程序用tf做了一个两层的分类网络。将下图中的数据集分类。

这里写图片描述

最终分类效果是这样的。

这里写图片描述

tb的使用,大致归纳为三步:

  • 调用tf中的FileWriter将自己关注的数据写到磁盘
  • 在命令行使用tensorboard --logdir /path/to/log启动tbweb app
  • 然后在本地浏览器输入localhost:6006来使用tb

下面具体看一下怎么使用。

1.生成模型图

生成模型图只需要一句话就行。比如说,现在已经初始化好了变量,处理好了数据,部分代码如下:

# 数据初始化
N = 100 # number of points per class
D = 2 # dimensionality
K = 3 # number of classes
X = np.zeros((N * K, D)) # data matrix (each row = single example)
y = np.zeros(N * K, dtype='uint8') # class labels
for j in range(K):
    ix = range(N * j, N * (j + 1))
    r = np.linspace(0.0, 1, N) # radius
    t = np.linspace(j * 4, (j + 1) * 4, N) + np.random.randn(N) * 0.2 # theta
    X[ix] = np.c_[r * np.sin(t), r * np.cos(t)]
    y[ix] = j
# lets visualize the data:
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.show()

# 输入数据
inputs = tf.placeholder(tf.float32, [N * K, D])

# 数据标签
targets = tf.placeholder(tf.float32, [None, K])

softmax_w_1 = tf.Variable(tf.truncated_normal((D, h), stddev=0.1), dtype=tf.float32, name='weight_1')

softmax_b_1 = tf.Variable(tf.zeros((1, h)), dtype=tf.float32)

softmax_w_2 = tf.Variable(tf.truncated_normal((h, K), stddev=0.1), dtype=tf.float32, name='weight_2')

softmax_b_2 = tf.Variable(tf.zeros((1, K)), dtype=tf.float32)

省略部分代码......

prediction = tf.nn.softmax(logits_2)

准备开始训练的时候,加上一句

tf.summary.FileWriter('./logs/summary', session.graph)

例如,在session开始的时候添加就好。

with tf.Session() as session:
    session.run(init)

    merged = tf.summary.merge_all()
    writer = tf.summary.FileWriter('./logs/summary', session.graph)

我们要看整个模型的图像,因此传入session.graph对象。

这时,来到命令行,切换到notebook所在的目录,然后执行

tensorboard --logdir logs/summary

logs/summary就是代码中定义的目录路径。tb启动后会提示到浏览器用localhost:6006去打开tb应用。

在浏览器打开后,切换到GRAPH标签,看到的模型图时这样的。

这里写图片描述

这个时候其他标签还没有内容,因为还没有在代码中进行添加。

上面的图片,就是现在这个模型的原始图像,没有进行任何分组和加工。看起来很乱,有一些标签,例如slice什么的,都不知道是什么意思。

图中,

  • 每条曲线代表tensor的流向
  • 每个椭圆代表一个操作,如addmatmul
  • 每个圆角矩形代表一组操作,可以双击放大,看到这个组里面的细节
  • 整张图片可以放大缩小,随意拖动;点开每个节点,右上角都会有这个节点的详细信息

下面来加工一下,为模型图分组,让图像更加清晰有条理。

2.使用name_scope分组

调用tf.name_scope()方法来为graph分组。

我想清楚看到

  • 输入Inputs
  • 标签Targets
  • 两组Weightbias变量
  • 两个隐藏层的输出Logits_1Logits_2
  • 损失函数loss
  • 训练准确率Accuracy以及
  • 整个训练过程Train

示例代码如下。

输入Inputs

with tf.name_scope('Inputs') as scope:
    inputs = tf.placeholder(tf.float32, [N * K, D], name='inputs')

标签Targets

with tf.name_scope('Targets') as scope:
    targets = tf.placeholder(tf.float32, [None, K], name='targets')

两组Weight和bias变量

# 创建第一层的weights和bias
with tf.name_scope('Weight_Set_1') as scope:
    softmax_w_1 = tf.Variable(tf.truncated_normal((D, h), stddev=0.1), dtype=tf.float32, name='weight_1')
    softmax_b_1 = tf.Variable(tf.zeros((1, h)), dtype=tf.float32, name='bias_1')

# 创建第二层的weights和bias
with tf.name_scope('Weight_Set_2') as scope:
    softmax_w_2 = tf.Variable(tf.truncated_normal((h, K), stddev=0.1), dtype=tf.float32, name='weight_2')
    softmax_b_2 = tf.Variable(tf.zeros((1, K)), dtype=tf.float32, name='bias_2')

两个隐藏曾的输出Logits_1和Logits_2

with tf.name_scope('Logits_1') as scope:
    # 第一层的输出logits_1,使用ReLU作为activation function
    logits_1 = tf.nn.relu(tf.add(tf.matmul(X, softmax_w_1), softmax_b_1), name='logits_1')

with tf.name_scope('Logits_2') as scope:
    logits_2 = tf.add(tf.matmul(logits_1, softmax_w_2), softmax_b_2, name='logits_2')

    # 计算最终的预测分数,使用softmax计算最后的分数
    prediction = tf.nn.softmax(logits_2, name='prediction')

损失函数loss

with tf.name_scope('Loss') as scope:
    loss = tf.reduce_mean(
          tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=prediction, name='loss'))

训练准确率Accuracy

with tf.name_scope('Accuracy') as scope:
    # Determine if the predictions are correct
    is_correct_prediction = tf.equal(tf.argmax(prediction, 1), tf.argmax(y_, 1))
    # Calculate the accuracy of the predictions
    accuracy = tf.reduce_mean(tf.cast(is_correct_prediction, tf.float32), name='accuracy')

训练过程Train

with tf.name_scope('Train') as scope:
    # 使用adam最为optimizer
    optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss)

设置好这些代码之后,就可以跟第一步中一样,使用

with tf.Session() as session:
    session.run(init)

    writer = tf.summary.FileWriter('./logs/summary', session.graph)

......

来生成模型图。

训练完成后,再次执行tensorboard --logdir logs/summary,在浏览器中看到的模型图就是这样的了。

这里写图片描述

跟刚才相比,是不是清晰了很多,我所需的所有关注点,这个图上都有了。

这时如何为图中的节点分组。

3.添加变量详情

添加变量详情,刻意在训练结束后,直接看到变量的变化情况。上面两步中,只有GRAPH标签下面有内容,其他标签下面都没有。这一步就是往其他标签下面添加内容。

添加变量详情,使用

tf.summary.histogram()
tf.summary.scalar()

这两个方法。

比如,我想关注:

  • 两组Weight和bias的变化情况
  • 每一次的预测结果的变化情况
  • 训练过程中loss的变化情况
  • 训练过程中准确率的变化情况

我可以通过上述两个方法来添加代码。

注意在使用这两个方法之前,要为变量都加上name关键字参数。

看上面的代码中,有

softmax_w_1 = tf.Variable(tf.truncated_normal((D, h), stddev=0.1), dtype=tf.float32, name='weight_1')

最后这个name='weight_1'就是用来标识softmax_w_1的名称。

为了使结果更加准确,务必为分组中的变量都添加相应的名称标识。

名称都添加了之后,就可以使用下面的代码来添加我们想要的结果了。

# 两组Weight和bias的直方图,用histogram
w_1_hist = tf.summary.histogram('weight_1', softmax_w_1)
b_1_hist = tf.summary.histogram('bias_1', softmax_b_1)
w_2_hist = tf.summary.histogram('weight_2', softmax_w_2)
b_2_hist = tf.summary.histogram('bias_2', softmax_b_2)

# 每次预测值的直方图
logits = tf.summary.histogram('prediction', prediction)

# 损失loss和准确率accuracy的变化,这里用scalar就好,具体原因还没有深究
# 用histogram应该也没有问题的,用了scalar之后,scalar标签下面就有内容了
loss_hist = tf.summary.scalar('loss', loss)
acc_hist = tf.summary.scalar('accuracy', accuracy)

然后,在训练的时候,要调用merge_all(),并用session去执行merge,最后将merge_all()返回的对象(本例中叫summary)添加到FileWriter中才行。看着复杂,一步步看代码还是很清晰的。

# 初始化Variable
init = tf.global_variables_initializer()

# 打开session
with tf.Session() as session:
    # 运行初始化
    session.run(init)

    # 调用merge_all(),将前面添加的所有histogram和scalar合并到一起,方便观察
    merged = tf.summary.merge_all()
    # 同样写入到logs中
    writer = tf.summary.FileWriter('./logs/summary', session.graph)

    training_feed_dict = {inputs: X, targets: y_.eval()}

    for i in range(epochs):
        # 训练的时候,第一个传入merged对象,返回summary
        summary, _, l = session.run(
                [merged, optimizer, loss],
                feed_dict=training_feed_dict)

        if not i % 50:
            print('Epoch {}/{} '.format(i + 1, epochs), 
                  'Training loss: {:.4f}'.format(l))
            # 每50步,将summary对象添加到writer写入磁盘,最后来观察变化
            writer.add_summary(summary, i)

在这里,就可以通过观察这些变量的行为来找问题所在了。

训练完成后,同理打开tb

这里写图片描述

可以看到SCALAR标签下,就有刚才添加的lossaccuracy了。

看这两个变量的行为不错,loss一直降低,accuracy最后达到了%99左右。

这个图片时可以通过选定指定一个区域来放大的,放大之后如下图。

这里写图片描述

这是accuracy放大之后的效果图,可以更加清晰。

接下来到DISTRIBUTIONHISTOGRAM里看看。

这里写图片描述

上图是两组Weights在训练过程中的行为,这里分布均匀,挺正常。如果有问题的话,会保持不变,也就是说模型没有进行学习。

这里写图片描述

这里是两组bias的行为。这里可以看出点问题了,这个实例模型在训练的时候,虽然准确率能到%99,但是loss降到0.5左右就下不去了。

如果在notebook中多次尝试运行整个模型,会发现最后分类完成的那张图片,无法很精准的分类。

看上图,bias_2的分布有点可疑,我还不确定,但是这样异常的不均匀的情况可以给予一定的方向。我现在要做的就是就去看看什么影响了bias_2的正常更新,并且这个影响是不是引起了最终的loss瓶颈。

这里写图片描述

上图是HISTOGRAMWeights的分布,看起来还是比较正常的,分布很均匀。

再来看看bias的。

这里写图片描述

这是两个bias的分部情况,第一个看起来还不错。但是第二个就异常了。刻意点一下图像左下角那个按钮,就可以将图像放大。我放大了第二个bias的图像,如下。

这里写图片描述

简直是找不到规律,虽然不是很明白为什么会这样,但是感觉问题就出在这里了。

这里看到的是2D图像,点左边栏中的OFFSET,就能看到和Weights那一样的3D图像了。

三、总结

就像上面的例子,如果没有tb,我并不能准确知道到底是哪一个变量有异常。tb的可视化功能大大提高了训练网络的效率。

这里是tb的基本使用方式,还有很多有待研究。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Tensorflow-2-Tensorboard使用 的相关文章

  • LDAP: error code 32 - No Such Object

    使用spring ldap创建节点的时候报错 LDAP error code 32 No Such Object 是在调用 this ldapTemplate create ldapUser 的时候报的错 找了半天没有发现原因 最后看到一篇

随机推荐

  • JavaScript整理

    第一章 JavaScript概述 1 1 JavaScript简介 JavaScript 简称js 是一种直译式脚本语言 是一种动态类型 弱类型 基于原型的语言 内置支持类型 它的解释器被称为JavaScript引擎 为浏览器的一部分 广泛
  • ajax请求和普通请求的区别

    当浏览器按照window location href index html 进行定期请求时 会清除当前窗口并将服务器响应加载到窗口中 使用ajax请求 当前窗口 文档不受影响 JavaScript代码可以检查请求的结果 并使用这些结果执行所
  • 一些黑科技接口钩子 钉钉,禅道,gitlab,jenkins等

    日常工作中需要做流程的串联 这个时候就需要掌握一些黑科技接口 这些接口甚至是官方文档上并没有提供的 但是我们确实可以使用 进行内部工具开发的一定要记得提供钩子 没有钩子 做不了朋友 钉钉相关 钉钉群中的自定义机器人 curl https o
  • C/C++在线编译器

    一直以来都喜欢用手机看书 尤其是在上班时 看的最多的是编程一类的书 主要是C 看着就想写写代码 可是电脑用不能用 怎么办 于是想到用UC浏览器找找看网上有没有在线的编译器 想什么时候写代码都可以验证 于是就找了几个 各有千秋吧 中文的我没找
  • 在地址栏中输入一段内容,接下来都发生了些什么

    用户发出 URL 请求到页面开始解析的这个过程 就叫做导航 用以定位到新资源 并且将老的资源从页面卸载 一 用户输入 地址栏首先判断输入的内容是搜索内容还是符合url规则的url 如果是搜索内容的话 浏览器会拼接上该搜索内容形成一个新的ur
  • mybatis-plus

    mybatis plus的sql拼接规则 实体对象参数属性有值 那么该属性名会拼接sql语句中 实体对象参数属性为基本属性时 会有默认值 mybatis plus认为是有值 参与sql拼接 mybatis plus与mybatis的对比 m
  • 3d指向检测 ros_3d_pointing_detection

    Introduction The workflow of this project is Detect 2D human joints from color image by Openpose Compute 3D human joints
  • stopPropagation, preventDefault 和 return false 的区别

    http blog csdn net bkq421511585 article details 14166789
  • 使用Docker拉起ES容器和Kibana容器并设置密码Demo

    1 准备条件 安装好docker 在同一台服务器上安装es和kibana 安装docker命令参考 可以按顺序执行如下命令安装 1 sudo yum install y yum utils 2 sudo yum config manager
  • 做擦边网站 服务器放在狗爹,在GoDaddy搭建Prosper202服务器

    记录一下我在GoDaddy搭建Prosper202服务器的过程 1 首先 我购买的是Liunx Deluxe共享虚拟主机 狗爹这个类型的产品可以建多个网站 我有一个域名 www网站已经上线 虽然还没有什么内容 2 为你的Prosper202
  • add_library使用 $<TARGET_OBJECTS:name>

    一 背景 前面介绍了add library的两种格式 今天分享一个实例 Cmake分别生成静态链接库 OBJ链接库 并使用
  • 人人商城小程序消息服务器配置,人人商城小程序订阅消息设置方法和几个坑!...

    操作步骤 第一步 开通订阅消息功能 登录微信小程序官网后台 mp weixin qq com 开通订阅消息 第二步 服务类目 新增 商家自营 gt 服装 鞋 箱包 第三步 添加订阅消息 4个 订阅消息 公共模板库 搜索 订单支付成功通知 编
  • Android仿小米商城底部导航栏(基于BottomNavigationBar)

    简介 现在大多数App都会用到底部导航栏 比如QQ 微信和购物App等等 有了底部导航栏 用户可以随时切换界面 查看不同的内容 Android底部导航栏的实现方式特别多 例如TabHost TabLayout 或者TextView等 都可以
  • 机器学习-支持向量机算法实现与实例程序

    一 SMO算法基础 支持向量就是离分隔超平面最近的那些点 分隔超平面是将数据集分开来的决策边界 支持向量机将向量映射到一个更高维的空间里 在这个空间里建立有一个最大间隔超平面 在分开数据的超平面的两边建有两个互相平行的超平面 建立方向合适的
  • 剑指offer总结

    时间复杂度一般比空间复杂度更重要 因为改进时间对算法的要求更高 是空间换时间 还是时间换空间 一般要看具体的应用 对于普通的应用 一般是空间换时间 因为普通用户更关心速度 而且一般有足够的存储空间允许这样操作 对于嵌入式的软件 一般我们会用
  • 简说C++学习-第一章C++语言概述

    简说C 学习 第一章C 语言概述 1 C 语言的发展 1972年 贝尔实验室在B语言的基础上 做了进一步的充实和完善 设计出了C语言 C语言的优点 语言简洁 使用灵活 方便 具有丰富的运算符和数据类型 可以进行低级操作 适合开发系统软件 程
  • Java jdbc实现多表查询

    数据库中的一张表对应Java中的一个类 我这里示例的是学生类 老师类 成绩类 还有一个用于存储多表查询结果后的SelectAll类 public class Student 学生表 private Integer id 学生编号 priva
  • sublime text 3下载与安装详细教程

    一 下载 打开官网下载链接http www sublimetext com 3 下载Sublime Text 3 portable version 下载下来为 Sublime Text Build 3083 x64 zip 编辑器的包 解压
  • Linux 中统计指定目录下同一类文件总的大小

    root PC1 test ls a map a ped a txt b ped b txt root PC1 test ll h total 1 4G rw r r 1 root root 200M Dec 1 19 42 a map r
  • Tensorflow-2-Tensorboard使用

    一 概述 机器学习如此复杂 训练模型的时候 摸不清背后到底是如何运行的 自己设置的参数和关键变量 如果能看到在训练时的变化情况 可以为后面的参数调优阶段提供很大的便利 Tensorboard就是这样一个工具 它刻意将模型抽象成图像 tens