说明文档
写说明文档是很好的。记录所有函数、方法和类,有时在编写函数之前对其进行文档记录。如果文档有时比代码长也可以,“过于清晰”比不够清晰要好。
上面的图像是一个简单的函数rand_rotate(),它随机旋转表示CT体积的3D numpy数组。这些注释很有帮助,因为它们解释了为什么旋转的向量使用(k-1)——这是因为所选的k是1、2或3,而Python是零索引的。像这样简单的说明可以防止以后的混乱。
data processing tutorial code中的说明注释
文档将确保回顾旧代码时,可以快速回忆代码和函数的作用。文档可以防止使用者在看到一些看起来很奇怪的东西时意外地破坏自己的代码,并且有更改它的本能。文档也将使其他人能够理解和使用您的代码。
变量命名
始终使用描述性变量名。“volumetric_attn_gr_truth”是一个比“truth”更好的变量名。
即使在行和列上迭代,也要使用“row”和“col”作为变量名,而不是“i”和“j”。有一次我花了一整天的时间寻找一个非常奇怪的bug,结果发现它是由于错误地迭代2D数组而导致的,因为我在数百行代码中只切换了一行“I”和“j”。那是我最后一次使用单字母变量名。
模块测试
很多人声称他们没有时间为他们的代码编写测试,因为这只是为了研究。我认为测试研究代码更重要,因为研究的全部意义在于你不知道“正确答案”是什么,如果你不知道生成答案的代码是否正确那么如何确保答案是正确的呢?
每次我花一天时间为我的代码编写单元测试时,我都会发现一些错误——有些无关紧要,有些则相当重要。如果你编写单元测试,将发现代码中的错误。如果你为别人的代码编写单元测试,你也会在他们的代码中发现错误。
除了促进代码的正确性,单元测试还可以通过阻止编写一次做太多事情的“上帝函数”来帮助实施良好的代码组织管理。上帝函数通常是测试的噩梦,我们应该将其分解成更小、更易于管理的函数。
至少,最好对代码中最关键的部分进行单元测试,例如复杂的数据处理或模型中奇怪的张量排列。确保代码是正确的决不是浪费时间。
这些单元测试包括对一些内置PyTorch函数的测试,以便进行演示。
可视化纠错
特别是在计算机视觉中,使用可视化来执行健全性检查是很有用的。
matplotlib非常适合查看图像、分割图、带边框的图像等。下面是一个通过将matplotlib的imshow()函数应用于输入图像而产生的可视化效果的示例:
matplotlib可视化
seaborn是为统计数据可视化而设计的。它对于制作热力图和生成性能指标的复杂可视化非常有用。下面是一些在seaborn中可以用大约一行代码绘制的绘图示例:
seaborn可视化
matplotlib和seaborn都可以用来创建可视化效果,即时显示输入数据是否合理、基本真实情况是否合理、数据处理是否没有意外出错、模型的输出是否有意义等。
单元测试和可视化
Demo-0-Unit-Tests-and-Visualization.py 首先运行 src/unit_tests.py中的单元测,然后对PASCAL VOC 2012数据集图像分割进行可视化。
为了运行演示的可视化部分,请更改demo-0-Unit-Tests-and-visualization.py到计算机上的一个文件夹内,在其中存储PASCAL VOC 2012数据集。一旦数据集下载完成,你就可以运行可视化程序。实现可视化的代码位于load_dataset/custom_pascal.py中. 目前,在演示文件中,“images_to_visualize”的总数设置为3;如果希望可视化更多图像,可以进一步增加该数量,例如增加到100。
可视化结果如下:
PASCAL VOC 2012数据集中的飞机图像
resample处理后的飞机图像
从可视化结果中我们可以推断出一些有用的东西:
- 输入图像与图像分割之间的映射是正确的。
- 用于定义像素级分割的整数与标签描述字符串之间的映射是正确的。比如:1正确地映射为“飞机”。
- 重采样步骤并没有“破坏”输入图像或分割图像。
在终端进行可视化
如果处于“非交互式环境”(即没有图形用户界面的终端),则需要关闭交互式显示并保存图形,以便在其他地方打开:
importseabornimportmatplotlibmatplotlib.use('agg') importmatplotlib.pyplotaspltplt.ioff() #seabornfigure: heatmap=seaborn.heatmap(some_dataframe, cmap='Blues', square=True, center=0) heatmap.get_figure().savefig('Descriptive_Figure_Name.png',bbox_inches='tight') plt.close() #matplotlibfigure: plt.imshow(chest_x_ray,cmap='gray') plt.savefig('Other_Descriptive_Figure_Name.png') plt.close()
Python 调试器
Python调试器是一个非常有用的工具,因为它允许在程序崩溃的地方检查变量或对象的状态,并在程序崩溃的地方运行代码片段,以便可以尝试可能的解决方案。使用Python调试器比使用print语句调试效率更高,它将为节省数小时的时间。Python调试器也可以与PyTorch一起使用,检查张量、梯度、记录dataframes等。
要使用Python调试器在终端中以交互方式运行脚本,请使用以下命令:
python-mpdbmyscript.py
输入上述命令后,将看到(Pdb)提示符出现。键入“c”继续。(这只是一个单独的小写字母c,表示continue)。
要退出Python调试器,请使用'q'(这是一个单独的小写字母q,表示quit)。有时候可能需要使用q两次才能完全退出。
如果要在程序中的某个特定点停止,则可以在相关模块中导入pdb,然后将“pdb.set_trace()“在你想要停止的特定点。或者,如果不想费心导入pdb,也可以在想停止的地方输入“assert False”,这样可以保证程序在指定的地方结束(尽管这不是使用Python调试器的正式方式)。
不要使用Jupyter Notebooks
考虑到前面的所有部分,本文建议不要将jupyter notebooks用于机器学习项目,或者真正用于任何需要花费数天以上时间的编码项目。
为什么?
- jupyter notebooks 鼓励你把所有的东西都放在全局命名空间中,这样就产生了一个巨大的怪物模块,它可以做所有的事情,而且没有函数、类和任何结构。
- jupyter notebooks 使代码的重用变得更加困难。函数是可重用的;而单元格5、10和13中的代码是不可重用的。
- jupyter notebooks 使单元测试变得困难。函数和方法可以进行单元测试。单元格5、10和13中的代码不能进行单元测试。
- 代码越有条理(也就是说,越细分为类和函数),jupyter notebooks 的交互性就越差,交互性是人们喜欢jupyter notebook的主要原因。jupyter notebooks 吸引人的交互特性与高度结构化、组织良好的代码本质上是对立的。
- jupyter notebooks 很难正确使用Git版本控制。jupyter notebooks只是大量的JSON文件,因此正确地合并它们或用它们执行提交请求基本上是不可能的。
- jupyter notebooks 使人们很难与他人合作。你必须“轮流”在jupyter notebooks上工作(而不是像使用“常规代码”那样从同一个rep中push/pull)。
- jupyter notebooks 有一个非线性的工作流程,这与可重复的研究完全相反。
那么jupyter notebooks有什么用?一些可能适用的场景是初始数据可视化、家庭作业、交互式演示。
代码编写标准
两个实用的代码编写标准是:
- 编写正确易懂的代码。如果你的代码是正确的,你的模型就更有可能产生好的结果,你的研究结论是正确的,你将创造出一些实际有用的东西。
- 确保任何人都可以复制你所做的一切——例如模型、结果、图形——通过在终端中运行一个命令(例如“python main.py”). 将有助于其他人在你的工作基础上再接再厉,也有助于“未来的你”在自己的工作基础上再接再厉。
总结
- Python是一种很好的机器学习语言
- Git版本控制有助于跟踪不同版本的代码。它可以通过GitHub获得。
- Anaconda是一个包管理器,它支持创建不同的环境,这些环境可能包含不同的Python版本和包。在处理具有冲突依赖关系的多个项目时,它非常有用。
- 将代码组织成模块中的类和函数。在Git存储库中以分层目录结构组织模块。
- 用注释和docstring记录代码
- 使用描述性变量名。不要使用单字母变量名。
- 编写单元测试,特别是对于数据处理和模型中最复杂或最关键的部分。
- 使用matplotlib和seaborn可视化显示数据集、模型输出和模型性能
- 使用Python调试器进行快速、高效的调试
- 不要将jupyter notebooks 用于机器学习项目