背景:
由于最近的几个项目中在使用TensorFlow,但是一直使用python API,使得写代码的过程渐渐的成为了“调包”的过程,程序员也渐渐成为了“调包侠”。
想深入学习TensorFlow,但是其源码比较大又太复杂,没有好的学习方法,没有好的师傅领进门,常常让人感觉到无从下手。
之前喜欢在IDE下调试源码,这样既能加深对代码的理解,又能提升学习的效率;一直喜欢侯捷那句“源码面前,了无秘密”。
我是老王,出于对技术的极致追求,使用TensorFlow的时候总想窥探其底层的秘密,所以耗费大量的精力开发出了适合学习TensorFlow的可调试工具。
TensorFlow代码很难调试,这个大家已达成共识,不过,就算是难,也还是需要调试的,毕竟谁也没有把握不出bug,看看这篇文章能不能让你减轻一点调试时的痛苦。
学习的方式没有好坏之分,只有适合与不适合的方法。
关键词:TensorBoard 、可视化监督、tf.Print 、TensorFlow 、tfdbg
为什么需要调试
通过单步调试,查看堆栈信息和变量实时值,胜过任何资深讲师的讲解
Tensorflow的学习困惑:
虽然tensorflow是开源的
但是,学习tensorflow的方法只能是用各种源代码阅读工具(Source Insight)
进行源码级的阅读、理解、分析代码间的关系流程,
由于tensorflow是基于C++开发,各种模板(template)类(class)具有可继承性,
一个基类会派生出多个子类型,每个子类型才是真正要实现的功能,
这给学习人员带来了很大的挑战性;目前,网上提供的调试方法大致有:
1、使用 TensorBoard 可视化进行监督
2、使用 tf.Print 操作输出变量值
3、使用CLI TensorFlow 调试工具tfdbg
4、Other…..
把这些调试工具能运行起来,学习和使用这些调试工具都需要花费大量的时间,
本人(王工)在使用的时候也有“力不从心”的感觉。
众所周知,在用C/C++语言编程这个领域,最优的开发调试环境IDE当属于Microsoft Visual Studio/ VC6.0工具
为学习而开发工具:
为了从底层彻底掌握和使用tensorflow-r1.8-CPU(不支持GPU)这个项目
我本人(王工)经历了三个月的亲身学习和调试。
tensorflow-r1.8-CPU经过我编译之后,包含中间的临时文件,*.obj文件等,
占用了26G的硬盘空间,把这些代码*.h,*.cc文件进行编译并生成带有调试信息的*.lib,
共占用了11G的硬盘空间,下面是库 VS2019-tensorflow-r1.8-CPU-Debug 的截图:
工具包含内容:
1、VS2019-tensorflow-r1.8-CPU整个工程文件
说明:
此文件是基于vs2019编译出的带有Debug信息的TensorFlow可调试库。
意味着可以在windows平台下使用vs2019单步调试TensorFlow的源码。
单步调试概览:
2、调试样例
multibox_detector_test示例:
/**********************************************************************
* * TensorFlow C++多盒对象检测演示
本演示基于 [使用深度神经网络进行扩展对象检测],,并使用该multibox_model.pb文件,检测图像中包含的对象:用于实时人员检测和跟踪相机预览。
*********************************************************************/
演示源图如下surfers.jpg:
经过示例的运行结果图:
输出信息:
========================wyx demo的调试输出=================
2020-09-16 15:20:52.399710: Tensor<type: uint8 shape: [228,480,3] values: [[160 140 149]]...>
2020-09-16 15:20:52.412927: ===== Top 5 Detections ======
2020-09-16 15:20:52.426605: Detection 0: L:324.406 T:76.3239 R:373.032 B:214.923 (635) score: 0.239021
2020-09-16 15:20:52.444433: Detection 1: L:332.595 T:76.064 R:372.239 B:204.336 (523) score: 0.237581
2020-09-16 15:20:52.470445: Detection 2: L:143.498 T:86.0459 R:187.015 B:195.247 (387) score: 0.173919
2020-09-16 15:20:52.525309: Detection 3: L:144.457 T:86.2542 R:185.335 B:165.518 (219) score: 0.1659
2020-09-16 15:20:52.568824: Detection 4: L:306.302 T:76.0753 R:370.994 B:217.478 (634) score: 0.160769* ****************************/
load_pre_trained_recognize_objects_test示例:
/**********************************************************************
* tensorflow C++图像分类提取演示
此示例演示如何加载预先训练的 TensorFlow 网络并使用它
进行分类提取,以识别出图像是属于以下1000种对象中哪一种类型:
dummy
tench
goldfish
大白鲨
虎鲨
hammerhead
electric ray
stingray
cock
hen
ostrich
*********************************************************************/
输入图片文件: tensorflow_demo_release\label_image\data\grace_hopper.jpg
经过运算,输出分类的结果如下:
/******************************************************
result:
military uniform 0.834305
mortarboard 0.0218694
academic gown 0.0103581
pickelhaube 0.00800818
bulletproof vest 0.0053509
军装 0.834305
砂浆板 0.0218694
学术礼服 0.0103581
皮克尔豪贝 0.00800818
防弹背心 0.0053509
******************************************************/
通过以上两个调试样例,你可以学习到detector和 recognize的更多细节,
从而进一步根据需求去修改它们
core_framework_all_test演示工程介绍:
包含以下24个单个子示例,你可以单步调试,方便对单个模块进行深入理解,单步运行每个功能的细节.
transform_graph_test演示工程:
包含以下25个单个子示例,你可以单步调试,方便对单个模块进行深入理解,单步运行每个功能的细节.
transform_graph_test测试案例功能简介
- 1-1 .测试Conv2D操作的文件:fold_batch_norms_test.cc位于 transform_graph_test\transform_graph_test\test_cc\fold_batch_norms_test.cc
- 1-2 TestFoldBatchNormsConv2D 该测试案例展示了手工建立一个op(操作)Conv2D的计算节点,
- 手工将节点参数赋初值,然后调用root.ToGraphDef()函数
- 将节点的网表转换成GraphDef类型,最后,根据转换后的网表建立一个session,
- 并调用TF_ASSERT_OK(session->Run({}, {"output"}, {}, &original_outputs));执行运算.
- 示例代码如下:
Tensor input_data(DT_FLOAT, TensorShape({1, 1, 6, 2}));
test::FillValues<float>(&input_data, {1.0f, 4.0f, 2.0f, 5.0f, 3.0f, 6.0f, -1.0f, -4.0f, -2.0f, -5.0f, -3.0f, -6.0f});
Output input_op = Const(root.WithOpName("input_op"), Input::Initializer(input_data));
Tensor weights_data(DT_FLOAT, TensorShape({1, 2, 2, 2}));
test::FillValues<float>(&weights_data,{1.0f, 2.0f, 3.0f, 4.0f, 0.1f, 0.2f, 0.3f, 0.4f});
Output weights_op =Const(root.WithOpName("weights_op"), Input::Initializer(weights_data));
Output conv_op = Conv2D(root.WithOpName("conv_op"), input_op, weights_op,{1, 1, 1, 1}, "VALID");
Tensor mul_values_data(DT_FLOAT, TensorShape({2}));
test::FillValues<float>(&mul_values_data, {2.0f, 3.0f});
Output mul_values_op = Const(root.WithOpName("mul_values"),Input::Initializer(mul_values_data));
Output mul_op = Mul(root.WithOpName("output"), conv_op, mul_values_op);
GraphDef original_graph_def;
TF_ASSERT_OK(root.ToGraphDef(&original_graph_def));
std::unique_ptr<Session> original_session(NewSession(SessionOptions()));
TF_ASSERT_OK(original_session->Create(original_graph_def));
std::vector<Tensor> original_outputs;
TF_ASSERT_OK(original_session->Run({}, {"output"}, {}, &original_outputs));transform_graph_test.cc
调试的跟踪:
调试图1:
调试图2:
查看变量versions的当前值
调试图3:
查看op_def变量的当前值
调试图4:
查看DeviceMgr->devices变量的当前值
调试图5:
查看director_session->client_graph变量的当前值
调试图6:
查看真正执行DeepConv2D()函数调试及堆栈变量的查看图:
调试图_7
transform_graph_test.png,用函数ReadTextProto()
读取一个*.pbtxt模型文件
然后解析成GraphDef类型的调试及内部变量和堆栈的查看.
最后,工程演示就先介绍到此,其它的演示就不再一一赘述了。
上述调试可以带你,洞悉每个step运行的变量值,跟踪函数的执行变量值。
如何获取此工具:
百度下载链接:链接:https://pan.baidu.com/s/1HkSznXLps30ymes6OzHWOQ
提取码:1234
作者:王工,联系方式:
电话:13510160446,微信同号(时间:周一到周六,9:00~17:00)