开发者在炼丹的过程中会不会遇到这些问题呢?比如说深度学习模型训练过程中盯 Log 盯得头晕眼花?复杂模型结构全靠脑补?网络各层效果无法评估?模型评估指标难以综合权衡?想必各位「炼丹师」、「调参侠」都中过招吧?莫慌,飞桨给大家「送解药」来了! 这剂解药就是VisualDL 2.0——飞桨可视化分析工具全面改版升级,帮众位开发者们从 「炼丹萌新」 变身「太上老君」。怀疑我们自吹自擂?那就让我们一起看看 VisualDL 到底是啥?先上图为证!看到这里,有些小伙伴可能已经大概知道 VisualDL 是什么了,不知道的同学请继续往下看,反正花花绿绿的,至少比 log 看着养眼多了! 具体来说,VisualDL 是飞桨团队为广大深度学习开发者订制的功能完备的可视化分析工具,可以帮助深度学习开发者了解训练过程中模型参数的变化趋势、 网络层对数据特征的提取情况、网络结构应该如何调整、精度与召回是否达到了理想的平衡等,从而实现有方向性的错误排查,实现高效的模型调优。其实,VisualDL 1.0 早在 2017 年就推出了,随着飞桨开源深度学习框架历经 3 年深入产业实践的不断打磨,VisualDL 本次也完成了 2.0 版本的全新蜕变!相较 VisualDL 1.0,新版本的 API 设计更加简洁易用、与框架衔接更为顺畅、功能更丰富,界面设计也全面进行了升级,致力为老用户带来全新体验,为新用户带来使用上的惊喜。VisualDL 2.0 共有五大可视化功能,下面我们来逐一讲解。标量可以通过图表的形式展示 Loss、Accuracy、Learning Rate 等参数指标在训练过程中的变化趋势,帮助开发者了解其是否朝着理想的方向优化,便于发现异常情况,及时排查并修复问题;另外,通过对比多组实验的训练参数,开发者们能深入探究参数对模型效果的影响,加速模型的调优。add_scalar(tag, value, step, walltime=None) 数据样本分析可以在多种深度学习任务中发挥重要作用。例如在计算机视觉任务中,该功能不仅可以查看每个训练批次中的指定的样本图片,也可以展示图像数据的在训练过程中间阶段的提取特征情况,便于开发者们观察图片数据在训练过程中的状态,进而深入了解训练过程及效果。add_image(tag, img, step, walltime=None) 在语音识别或合成任务中,数据样本分析功能可以帮助开发者实时获取训练过程中的音频数据,评估语音识别或合成的效果,挑选最优的训练模型。add_audio(tag, audio_array, step, sample_rate) 模型结构功能支持一键可视化模型网络结构。开发者们可查看模型属性、节点信息、节点输入输出等,并支持节点搜索功能,协助开发者们快速分析模型结构并了解数据流向。visualdl --model ./log/model --port 8080 直方图可以展示 Tensor(weight、bias、gradient 等)数据在训练过程中的变化趋势,深入了解模型各层效果,帮助开发者精准调整模型结构。add_histogram(tag, values, step, walltime=None, buckets=10) PR 曲线可以展示每一分类在不同阈值下的精度(Precision)- 召回率(Recall)曲线,帮助开发者权衡模型精度和召回率之间的平衡,设定最佳阈值。精度是指正样本的数量除以所有被选中样本的数量,而召回率是指所有正样本中被推理正确的比例,由此可见精度越高,召回率越高,我们的模型就越好。但是很多情况下,鱼和熊掌不可兼得,无法同时保证精度和召回率都是最高,此时需要通过 PR 曲线,权衡精度与召回率,确定最佳阈值。add_pr_curve(tag, labels, predictions, step=None, num_thresholds=10) 为了便于大家更好的了解 VisualDL,我们结合一个简单的实际模型例子来更直观的看下这几个功能。真的是很简单的例子——那就是深度学习中的「Hello World」,手写数字识别!假设在一个风和日丽的早晨,作为深度学习爱好者的你,想要建立一个简单的 LeNet 模型进行手写数字识别,在辛苦完成模型搭建后,进入模型训练时,却看到了如下一幕:你心里一沉,这精度怎么抖得那么厉害啊。训练中哪里出现问题了呢?到底谁是「凶手」?可是连问题出在哪都不知道,模型优化和修复更是无从下手。这时你想起几日前偶然发现的深度学习可视化分析工具——VisualDL,心中顿时升起希望,因为在训练之前,已经把下面的代码添加到训练脚本中了。#使用VisualDL的第一步是在训练脚本中添加如下代码创建日志文件,用于记录训练中产生的数据。 from visualdl import LogWriter log_writer = LogWriter('./paddle_lenet_log')
# 使用标量(Scalar)记录Loss、Accuracy参数,用于观察其变化趋势 writer.add_scalar(tag='train/loss', step=step, value=cost) writer.add_scalar(tag='train/acc', step=step, value=accuracy)
# 使用数据样本分析功能记录每批次第一张数据,用于查看图像数据 img = np.reshape(batch[0][0], [28, 28, 1]) * 255 writer.add_image(tag='train/input', step=step, img=img)
# 使用直方图功能记录所有参数,用于查看weight、bias在训练步数中的分布变化 for param in params: values = fluid.global_scope().find_var(param).get_tensor() writer.add_histogram(tag='train/{}'.format(param), step=step, values=values)
# 记录PR曲线查看不同阈值下的精度和召回率 labels = np.array(batch)[:, 1] for i in range(10): label_i = np.array(labels == i, dtype='int32') prediction_i = pred[:, i] writer.add_pr_curve(tag='train/class_{}_pr_curve'.format(i), labels=label_i, predictions=prediction_i, step=step, num_thresholds=20) # 使用save_inference_model保存模型结构至“./model”目录,便于训练后使用模型结构(Graph)功能查看模型网络结构 fluid.io.save_inference_model(dirname='./model', feeded_var_names=['img'],target_vars=[predictions], executor=exe) 接下来就执行下面的命令来开始奇幻的破案(调优)之旅吧!visualdl--logdir ./paddle_lenet_log --port 8080 执行命令后,即可得到这样一个网址 http://localhost:8080/,打开浏览器输入这个网址即可查看究竟发生了什么。首先来看看案发现场。。1. 点击 「标量数据」 页签查看模型的 Loss 和 Accuracy 整体变化趋势,也就是把刚才的日志信息通过图形直观的展示出来。这部分对应的代码如下所示:writer.add_scalar(tag='train/loss', step=step, value=cost) writer.add_scalar(tag='train/acc', step=step, value=accuracy) 一眼望去,这哪是 Loss 和 Accuracy 的变化曲线啊,这明显是心率不齐啊,让人有打 120 叫救护车的冲动了!显而易见,Accuracy 一直在 10% 左右徘徊,Loss 一直无法下降,由此我们开始逐个查看问题出在哪个环节。首先看看模型结构的实际样子,是否是结构设计出了问题。2. 点击 「网络结构」 页签,将保存在 「./model」 目录下的模型文件拖进页面即可看到模型的结构。通过观察模型结构、节点属性、模型属性、数据流向,咱们可以直观的发现整体结构是符合预期的,也就是说模型网络本身是清白的,那么是否是模型 「吃进」 的数据有问题呢? 3. 点击 「样本数据」 查看训练中的样本数据。这部分对应的代码如下所示:# 使用数据样本分析功能记录每批次第一张数据,用于查看图像数据 img = np.reshape(batch[0][0], [28, 28, 1]) * 255 writer.add_image(tag='train/input', step=step, img=img) 通过查看每批次数据的第一张图片,发现输入数据也是没有问题的。那么咱们再来「回放录像」,看看每一时间步的参数变化情况吧。4. 点击 「直方图」 页签,查看训练过程中每一时间步权重和偏差的变化情况,就如同回放整个训练过程的监控录像一样,让训练过程中参数变化不正常的问题无所遁形!这部分对应的代码如下所示:for param in params: values = fluid.global_scope().find_var(param).get_tensor() writer.add_histogram(tag='train/{}'.format(param), step=step, values=values) 通过查看后发现,每一层的权重和偏差的变化正常。由此证明不是特定层的初始参数配置出现问题,排除了模型结构、数据样本、每层网络参数配置后,还剩下超参数的配置,因此决定尝试不同的学习率(0.001,0.03,0.05,0.08,0.1),并通过 VisualDL 的多实验对比功能,来观察学习率对模型训练效果的影响情况。5. 启动多实验对比功能非常的简单,只需要在训练脚本中参考如下代码实现同一个目录下记录多份不同学习率的训练日志文件,并启动相应训练即可。接着启动 VisualDL,就会得到多组实验记录对比图了。#创建日志文件,储存当lr=0.001时训练结果 log_writer = LogWriter('paddle_lenet_log/lr0.001') #创建日志文件,储存当lr=0.03时训练结果 log_writer = LogWriter('paddle_lenet_log/lr0.03') #创建日志文件,储存当lr=0.05时训练结果 log_writer = LogWriter('paddle_lenet_log/lr0.05') #创建日志文件,储存当lr=0.08时训练结果 log_writer = LogWriter('paddle_lenet_log/lr0.08') #创建日志文件,储存当lr=0.1时训练结果 log_writer = LogWriter('paddle_lenet_log/lr0.1') 你终于发现了问题所在了,原来是学习率设置的过大了,导致 Loss 无法下降收敛,根据 「标量数据」 展示的效果,当学习率为 0.001 时(深蓝色线条),Loss 呈现优美的下降趋势且后续渐渐收敛,Accuracy 呈现逐渐上升并趋于平稳,因此你将学习率设置为 0.001,使得模型呈现最佳效果。但是你还不满足于现状,想要选择一个最佳阈值,使得模型的精准度和召回率都达到最优,于是又开始使用 「PR 曲线」 功能。6. 点击 「PR 曲线」 页签,权衡精确度与召回间的关系,确定最佳阈值。这部分对应的代码如下所示:labels = np.array(batch)[:, 1] for i in range(10): label_i = np.array(labels == i, dtype='int32') prediction_i = pred[:, i] writer.add_pr_curve(tag='train/class_{}_pr_curve'.format(i), labels=label_i, predictions=prediction_i, step=step, num_thresholds=20) 以上图为例,该图是手写数字识别任务中类别 7 对应的 PR 曲线,从图中可以看出,当阈值为 0.10、0.15 或 0.20 时,准确率和召回率同时达到最高值,因此选择上述中的任意阈值,模型都可达到最佳效果。最终,在 VisualDL 的帮助下,你确定了模型阈值为 0.15,学习率为 0.001,至此,一个效果理想的手写数字识别模型搭建完成。如需查看较复杂的应用案例 -- 眼疾识别,请参考:看到这里,相信大家已经对 VisualDL 2.0 有了一个比较全面的了解,也发现它是真的又有用,又好用吧?然而这还不够,飞桨团队还为它增加了诸多软实力:在未来,VisualDL 将会持续加入新的功能组件,致力于为开发者们提供简单易用、功能丰富、性能强大的可视化分析工具,助力模型的优化过程。小伙伴们是不是已经破迫不及待想要亲自上手尝试了呢?欢迎登录 VisualDL 2.0 官网查看功能示例,GitHub 上也提供了详细的使用指南,大家可以踊跃尝试!想要与其他使用 VisualDL 的小伙伴们交流心得么?请加入 VisualDL 官方 QQ 群:1045783368,提出使用中遇到的问题与对于可视化的其他需求,同时还有机会与深度学习高手们面对面过招!如果您加入飞桨官方 QQ 群,您将遇上大批志同道合的深度学习同学。官方 QQ 群:1108045677。如果您想详细了解更多飞桨的相关内容,请参阅以下文档。本文为机器之心发布
✄------------------------------------------------ 加入机器之心(全职记者 / 实习生):hr@jiqizhixin.com 投稿或寻求报道:content@jiqizhixin.com 广告 & 商务合作:bd@jiqizhixin.com
|