脸书AI首席开发布道师:如何贡献PyTorch代码

全文共9175字,预计学习时长18分钟html

图片来源:redcharlie Unsplash

本文做者是Facebook的机器学习/人工智能首席开发布道师,为其麾下的PyTorch团队助力,曾经是一名软件工程师。前端

值得注意的是,在以前,做者从未使用过PyTorch。他将用本身的亲身经历告诉你:PyTorch并不难学。python

PyTorch内容集锦git

PyTorch及其工做原理:github

• PyTorch是Facebook开发的深度学习框架,用于快速灵活实验。这是一个基于Python的计算软件包,含有 C++后端API。算法

• PyTorch的Python前端有三个不一样部分:数据库

Torch编程

这是一个包含多维张量与数学运算的数据结构包。后端

Torch.nn数组

用于建立并训练神经网络。区域块的导入数据以张量形式传递。

例如:为处理图像而训练卷积神经网络时,可以使用nn.conv2D模块(这一想法来自笔者同事塞斯)。

Torch.optim

该优化算法可训练神经网络。

例如:适用于初学者的SGD或适合进阶者的Adam等算法,均可训练神经网络(这个好例子也源自塞斯)。

Tensors(张量)

PyTorch的Tensors 与Numpy数组相似。不一样之处在于,Tensors数据能够是单个数字,或一维矩阵(向量),或囊括各类数据的多维结构。

算梯度即求导(没错,机器学习即微积分)。求出张量的梯度,即可最大程度地规避错误。

梯度降低这一算法能有效实现错误最小化。错误由数据决定,而数据的分类也有适当与不适当之别。

经过梯度下降不当分类项目数。

张量最重要的特色即自动跟踪梯度。每一个张量中的数据表明神经网路的链接层。这其中,有三点值得一提:

• 阶 (rank): 肯定张量的维数。例如,向量为一阶。

• 形状 (shape): 行数和列数,表现形式为tensor. Size ([x]).

• 数据类型 (type): 给张量元素所指定的数据类型。

PyTorch的神经网络训练循环以下:肯定目标函数、迭代输入其余数据、将数据运用于神经网络、执行梯度操做减小偏差,将异常参数运用于神经网络。

在神经网络中重复迭代整个训练数据集便可。

C++ 后端

C++“后端”有五个不一样部分。

• Autograd: 自动求导,即张量上的记录操做,进而生成自动求导图,基本上依据梯度张量求导。

• ATen: 张量及数学运算库。

• TorchScript: 链接TorchScript的JIT编译器与解释器的界面(JIT为“即时”)

• C++前端: 训练与评价机器学习模型的高级构造。

• C++ 扩展: 经过自定义C++与CUDA的例程扩展,扩展Python的API.

CUDA (计算机统一设备架构) 是由Nvidia推出的并行计算架构及应用程序界面 (API) 模型。

上述内容皆援引自维基百科,总的来讲,CUDA经常使用于GPU上的各项操做。

GPU即图像处理器。可将其视为具备强大运算力的电脑。

“说好的概览呢?这些知识点简直让人摸不着头脑。”虽然上述内容的确晦涩难懂,但它们相当重要。

若想对相关知识有深刻全面的了解,请参阅优达课程(https://classroom.udacity.com/courses/ud188)并阅读PyTorch docs(https://pytorch.org/docs/stable/index.html)这份资料,必能让你获益匪浅。

那份资料并不着眼于PyTorch的方方面面,而是帮助读者开始给GitPyTorch开源数据库贡献代码(https://github.com/pytorch/pytorch)。

在这份repo中,你可根据上述信息,自行推断大量所含文件夹的内容。

搭建开发环境

贡献任何代码以前,务必阅读CONTRIBUTING.md这份文档(https://github.com/pytorch/pytorch/blob/master/CONTRIBUTING.md),切勿有所遗漏。这是笔者从血泪中总结出的教训。

若条件容许,不管是处理Python仍是C++问题,最好都借助GPU。

还可采用其余途径(如AWS DLAMI或Nvidia Docker),这会显著提高运行速度。

做为初学者,笔者犯下的第一个错误就是在本地电脑上开工。因为本地电脑性能不够强大,各类使人困惑头疼的段错误也就层出不穷。

最终,笔者用SSH、MOSH、TMUX等登录GPU服务器,效果极佳。若是想就此作好准备,可参阅如下博文:

• HPC上的GPU运算简介:安装GPU与SSH

https://sydney-informatics-hub.github.io/training.artemis.gpu/setup.html

• PyTorch必备——GPU

https://discuss.pytorch.org/t/solved-make-sure-that-pytorch-using-gpu-to-compute/4870

• GPU内存须知

https://discuss.pytorch.org/t/reserving-gpu-memory/25297

• 安装PyTorch与Tensorflow—— 采用支持CUDA的GPU

https://medium.com/datadriveninvestor/installing-pytorch-and-tensorflow-with-cuda-enabled-gpu-f747e6924779

接下来,搭建PyTorch开发环境。想用SSH登录GPU,须作好如下内务处理操做:

• 链接GitHub帐号,确保在GPU建立SSH密钥,并将其添加至GitHub帐号。

• 你的设备极可能会用到Python。须确保用的是Python 3。你极可能已经安装了 pip。如若否则,务必所有安装。

• 安装Miniconda. 这和pip极为相似。官方网站并无很好的终端安装指南,在此列出笔者的操做方式:

$ with-proxy wget “https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh"$ chmod +x Miniconda-latest-Linux-x86_64.sh$ bash Miniconda3-latest-MacOSX-x86_64.sh

• 重启终端,输入conda,确保操做无误。

• 运行conda config — set auto_activate_base false, 以防止Conda自动激活,否则会干扰机器的运做。

• 运行 conda activate,激活Conda。每次开发前,务必保证conda处于激活态,注意终端的(base)语句便可。

• 安装 ccache。若是无效,在启用conda的状况下执行 conda install ccachewhen conda。若依然无效,请尝试wget。"https://www.samba.org/ftp/ccache/ccache-3.3.3.tar.gz".

• 输入ccache 确保一切奏效,输入conda list确保其出如今安装列表。

• 安装一个faster linker。除非万不得已,否则请勿输入 ln -s /path/to/downloaded/ld.lld /usr/local/bin/ld 指令。笔者曾遇到这种状况,因为它们做用不大,最终还得将相关文档所有删除。

• 运行 pip install ghstack 或 conda install ghstack.

• 运行 pip install ninja 或 conda install ninja.

• 经过pip install flake8 flake8-mypy flake8-bugbear flake8-comprehensions flake8-executable flake8-pyi mccabe pycodestyle pyflakes安装 Flake8,它会为你纠错。若想给文档纠错 (lint),输入 flake8 <FILENAME> 便可。

• 安装一个IDE,经过SSH登录GPU。笔者在Atom. 上查找remote-ssh ,便可获得相关操做说明。

• 最后,安装PyTorch:

$ git clone https://github.com/pytorch/pytorch$ cd pytorch$ git pull --rebase$ git submodule sync — recursive$ git submodule update — init — recursive$ python setup.py develop

python setup.py develop 就是用来编写PyTorch代码的工具。一开始会花些工夫,但一旦写过一次,之后就能使用很多加速方法。

编辑C++时须要构建PyTorch,但编辑Python时则否则。

想把这一系列步骤办好,就得斗志昂扬,心怀热忱。不过一旦完成这不易的任务,你就能够长舒一口气,好好犒赏下本身。

若是碰壁,不要恐慌,笔者也有过相似的经历。参照后文的《常见错误》,或在评论区留言,笔者必竭力相助。搭建好开发环境会花一番工夫,祝各位马到成功。

代码时间到!

开发环境搭建完毕,能够开始写代码了!

若是你以前从未贡献过开源代码,务必略读《行为准则》(https://github.com/pytorch/pytorch/blob/5bc7c1f83dd3ea740ba1d2af286c464862b90743/docs/source/community/governance.rst)。不少代码库都有这样的文档,简言之,作好人,行好事。

关注GitHub的问题 (issues) 选项卡。其中囊括不少社区成员提出的问题。点击训练营 (bootcamp) 标签,查看初学者级别的问题,选择本身能力范围内的任务,加以攻坚。

笔者建议,开始动手前,先阅读相关资料,洞悉个中玄机。还可更新过滤器 (filter) 查看训练营的历史纪录,研究已解决的问题。

若是你胸怀大志,大可点击 pull requests 标签。不是全部的PR都有相关的问题可供参考,有时研究问题自己更容易了解状况。

刚研究这些问题时,“冒牌货综合征”如乌云般笼罩在本身心头。“我又不是科班出身,怎么作得了这个?”打住!相信本身,你能行!

解决问题无须全知全能,找到方向,难题便迎刃而解。

若是你找到一个愿意动手解决的问题,要勇于留言并发问,哪怕是这样的一句话,“我想接下这个任务,请问你能就如何着手给我一些建议吗?”都大有裨益。

这些问题板块下的评论者都很乐于助人,由于若是有人对这些问题感兴趣,他们本身也会很高兴。对此笔者有切身体会,在他们的引导下,笔者解决了一系列问题,本身的PyTorch技术也日臻完善。

接下来是案例分析,阐述笔者如何在一头雾水的状况下解决问题。

第17893号问题:确保Grad_outputs的输出值形状与torch.autograd.grad()一致

幸运的是,据笔者切身经验来讲,PyTorch库的做者每每细致缜密,言之有物。

就此例:https://github.com/pytorch/pytorch/issues/17893而言,结合前文的知识,不难发现,因为该问题涉及“接受输入”,因此这一定与Python前端有关。

乍一看这个问题角度很刁钻,虽然本质上与Python相关,但题目中的autograd,却有将人引向C++之嫌。

笔者建立一个新分支并将其导至最高级的torch文件夹(本例中,所涉及的函数正是torch)。

有趣的是,在最先创立的文件夹中,正好有一个名为autograd ,真可谓无巧不成书。

打开 autograd 文件夹,在 grad( 语句中调用git grep ,以肯定函数定义。能够肯定,其中一个就在 __init__.py 文件中。

更巧的是,在 grad 函数,中 正好有以 *gasp*为名的变量,也就是说只须要作一个简单的condition,一切便迎刃而解。

如题所示, outputs 与 grad_outputs 须形状一致。经过NumPy,有一个简单的法子能够作到这点。

import numpy as np...if np.shape(outputs) != np.shape(grad_outputs):

raise RuntimeError("grad_outputs and outputs do not have the same shape")

做为一名优秀的软件工程师,笔者很清楚只有先测试代码,才能保证本身的PR滴水不漏。不过首先,得确认程序的兼容性没有问题。

据CONTRIBUTING.md 文件所示, 先得运行 python test/run_test.py。

运行结果并不尽如人意,那么到底是哪里出了纰漏?原来PyTorch在调用NumPy并不直接照搬,而是会作出相应调整。只能这样说,PyTorch能够无损调用NumPy中的函数。在这篇文章(https://github.com/wkentaro/pytorch-for-numpy-users)中,笔者获悉如何实现两种程序的代码转化。

此外,这篇文章中还提到,只能在一个(而非多个)张量中读取形状需求,所以笔者调用shape() 语句时也出了差错。所以,__init__.py 中 grad() 函数代码也须修改,详情以下:

for out, grad in zip(outputs, grad_outputs):
if not out.shape == grad.shape:
raise RuntimeError("grad_outputs and outputs do not have the same shape")

再次测试程序,此次终于成功,接下来是报错测试。

打开test 文件夹,里面是带有test_grad 函数的 test_autograd.py 文件,一切都是那么的简洁明了!

在敲定测试方案前,笔者先加入一些打印语句。谨记,打印语句是Python编程师的鼎力助手。

打印随机变量有助于深刻了解张量,并从程序上将其与梯度张量相比较。

此外,读取其余测试,了解项目中相关对象的建立与测试,也能使人获益匪浅。最终,测试以下:

grad_out = torch.ones(2)try:

torch.autograd.grad(

outputs=[grad_sum], grad_outputs=[grad_out],

inputs=[x], create_graph=True)

self.assertFail()

except RuntimeError as error:

self.assertEqual(str(error), "grad_outputs and outputs do not have the same shape")

运行上述测试,万无一失。接下来,用Flake8纠错,在GitHub上提交修改结果,建立PR,添加问题标签,确认与原问题标签一致,提交,搞定收工!

很快,笔者就收到了评论,这些意见逻辑清晰、很有洞见,所以也极易落实。

第一桩PR圆满完成!幸亏只用Python就可解决,毕竟,又有谁敢去编辑臭名昭著的C++呢?不过明知山有虎,偏向虎山行,有请下一个问题!

第22963号问题: 经过Torch.flatten(),输入零维张量,得出零维张量(非一维)

起初,笔者并未意识到该问题涉及C++API, 但仔细一看(再次给出题者点个赞),一切不言而喻。

切勿一见讨论而萌生退意,围绕这些问题的讨论每每蕴含不少信息。在接手问题以前,要确保本身有解决的方法。

该问题涉及两方面,一是“返回值”,二是“输入值”。输入值与“前端”相关,即Python;而“输出值”与“后端”相关,即C++。

建立新分支,大胆一试。大部分编程都是这么一回事——怀揣但愿,放手一试。

首先,探寻 flatten() 函数的奥秘。而这即是打印语句的用武之地。

导航回 test 文件夹, 在flatten( 上调用git grep 。通过一番查询,笔者刚好在test_torch.py文件找到test_flatten 函数。

打开文件,研究其断言,明白本身所找何物后,输入以下打印语句:

# Test that flatten returns 1-dim tensor when given a 0-dim tensor

zero_dim_tensor = torch.tensor(123)

one_dim_tensor = torch.tensor([123])

cool_dim_tensor = torch.tensor([1,2,3])

flat0 = zero_dim_tensor.flatten()

flat1 = one_dim_tensor.flatten()

flat2 = cool_dim_tensor.flatten()print("--zero dim tensor--")

print(zero_dim_tensor)

print(zero_dim_tensor.shape)

print(flat0)

print(flat0.shape)

print("--one dim tensor--")

print(one_dim_tensor)

print(one_dim_tensor.shape)

print(flat1)

print(flat1.shape)

print("--cool dim tensor--")

print(cool_dim_tensor)

print(cool_dim_tensor.shape)

print(flat2)

print(flat2.shape)

输出值以下:

--zero dim tensor--

tensor(123)

torch.Size([])

tensor(123)

torch.Size([])

--one dim tensor--

tensor([123])

torch.Size([1])

tensor([123])

torch.Size([1])

—cool dim tensor--

tensor([1, 2, 3])

torch.Size([3])

tensor([1, 2, 3])

torch.Size([3])

结果与问题相吻合。展开后,零维张量与一维张量是等值的。

根据这些打印语句可得,这个等值性是由其形状(shape)为torch.Size([1])决定的。一经展开,零维向量等于一维向量。

接下来在代码中验证这一观点。笔者在GitHub库中搜索“flatten”的实例。在Python中,的确有Flatten模块这一说法。

由此,笔者第一次意识到本身得处理C++编程,通常来讲人们不肯从事模块编辑,而这其中另有玄机。

有这么一种模块框架,名为 caffe2。你大可谷歌百度,简言之,它是PyTorch的一部分,而且人们每每不同意使用这一框架。对笔者来讲,编辑Caffe2不是个好主意。

无奈地盯着tensor_flatten.cpp文件,笔者彷佛只能止步于此。这但是让人头疼的C++,就算费上九牛二虎之力,恐怕到头来本身仍束手无策。

鼓起勇气求助以前,笔者对着屏幕足足懊丧了一个小时,“冒牌者综合征”如阴云般在脑海中挥之不去。

干坐着泄气可不是理想的应对之策。若是不是坐在办公室,笔者恐怕早就发帖询问该从哪里下手了。所幸的是,办公桌几步以外,即是好心的工程师同伴。谢天谢地,在他们的点拨下,阴云散去,柳暗花明。

解决这一难题的关键在于aten. 由于 ATen 即张量运算库,而flatten 与运算息息相关。

明白这个道理须要花一番工夫。若是你因无处解惑而束手无策,可参阅PyTorch技术文档(https://pytorch.org/docs/stable/torch.html?highlight=flatten#torch.flatten)。

这代表,想解决这个问题,须找到带有各类参数(如input, start_dum, end_dim)的flatten 函数,返回一个Tensor 。根据这个说法,很明显以前笔者的尝试有如南辕北辙。

笔者决定在顶层调用 git grep,实现Tensor flatten( 。因而瓜熟蒂落地获得atn, TensorShape.cpp。这代码看上去没问题,准没错。

为了完全理解该代码,笔者审阅了数遍,并确保单步执行的函数符合逻辑。

这代码看起来如何?简洁易懂!这样每当输入一维张量时,它就会模仿flatten 函数,而非返回输入值:

if (self.dim() == 0) {

shape.reserve(1);

shape.push_back(1);

return self.reshape(shape);

}

更新测试,代入合适的结果,而后一切水到渠成。接下来就是纠错,作PR,获取建议反馈,贡献PyTorch代码大功告成!

点滴感悟

笔者贡献开源代码已有些时日,如下几点经验教训值得牢记:

1. 敢于探索 不要对代码心生畏惧。这些代码都是聪明人写出来的(对于大项目来讲更是如此),其中蕴含许多玄机可供探索。敢于揭开那些代码的神秘面纱,哪怕不许备为其添砖加瓦,仅仅是了解其工做原理,也能使本身获益匪浅。

2. 勤于发问 没必要害羞瑟缩,GitHub开源社区倡导共享精神,主张领导力培养。别人会由于你的发问与参与心潮澎湃,进而伸出援助之手。

3. 乐于学习 你并非团队中的冒牌者,只要本身乐于学习勇于拼搏,哪怕不是科班出身,不能作到凡事心照不宣,你依然能够为团队发展添砖加瓦。

4. 善于钻研 多读资料。查阅README,技术文档,参与论坛,这些都能让人受益颇多。

5. 勇于尝试 哪怕本身不是稳操胜券,想不出万全之策,也不要踌躇不前,要勇于尝试。

望诸位的编程之路一路顺风!


常见错误一览

Import not found, or import not connecting

1. 确认是否键入输入值。

2. 确认conda 是否激活。

3. 重启PyTorch。

NumPy functions don’t work

1. 先确认可否在不影响效果的状况下,用 PyTorch 替换Numpy。

2. 如若否则,在GitHub上建立问题,征求PyTorch可兼容的函数来达到预期效果。亦可本身写出函数。

Not all the tests on my PR are passing

1. 别气馁,深呼吸,相信本身。

2. 有时,这不是你的错。检查日志文件,留意是否有来自本机的新消息,若收到新消息,从新访问代码。

3. 你并未合并本身的PR,但团队其余成员合并了各自的PR。不过你有个好团队,伙伴们会告知你并为你分忧。


推荐阅读专题

留言 点赞 关注

咱们一块儿分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”


(添加小编微信:dxsxbb,加入读者圈,一块儿讨论最新鲜的人工智能科技哦~)

相关文章
相关标签/搜索