Tensorflow-2-Tensorboard使用

作者: darkie | 来源:发表于2017-07-27 12:53 被阅读0次

    1c8b: 概述

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

    Tensorboard就是这样一个工具。

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

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

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


    1c8c: 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图像了。


    1c8d: 总结

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

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

    相关文章

      网友评论

        本文标题:Tensorflow-2-Tensorboard使用

        本文链接:https://www.haomeiwen.com/subject/pejnkxtx.html