传奇工程师卡马克入坑 AI:徒手一周实现反向传播和 CNN

电子说

1.2w人已加入

描述

前言

有这么一个大牛程序员,他在几乎没有接触过神经网络的情况下,仅用了一周时间,在几乎是最基础且受限的编程环境下,从零开始徒手撸码,实现了反向传播和 CNN。今年,这位程序员已经 48 岁了,他叫:约翰 · 卡马克。

约翰 · 卡马克是何方神圣?谁是约翰 · 卡马克?

他是一位集传奇工程师、大神、疯狂程序员、黑客之神、第一人称射击游戏之父、业界活化石、一代玄学码神所有称号为一身的老牌程序员,一举一动都牵动人心。

约翰 ·D· 卡马克二世(John D. Carmack II,出生于 1970 年 8 月 20 日),是美国的电玩游戏程序员、id Software 的创始人之一,id 是一家专门开发电子游戏、电视游戏的公司,成立于 1991 年。

至于 id Software 这家公司都制作过什么游戏呢?说几个你应该就知道了:《CS(反恐精英)》、《半条命》、《毁灭战士》都出自这家公司。

怎么样,对这位卡马克先生多少有些了解了吧?

当然,对技术大牛的一切不提技术水平的吹捧都是耍流氓!——沃茨 · 基硕德,那我们就来说一说这位卡马克大神的技术水平。

卡马克最让人咋舌的冒险就是涉足了第一人称射击游戏领域。他的编程能力得以毫无保留地展现,随后的《德军总部 3D》(Wolfenstein 3D)、《毁灭战士》(Doom)和《雷神之锤》(Quake)就是最好的佐证。这些游戏和它们的后续版本都获取了巨大的成功。

卡马克喜欢在电脑图像领域尝试新的技术,比如他在 Doom 上第一次使用了二叉树分区技术,表面缓存技术则在 Quake 中第一次出现。还有就是后来在 Doom3 里面使用的 “卡马克反转”(即 shadow volume 的 z-fail 方法。事实上并不是卡马克首先创新了这个技术,他在后来独立研究出来)。

卡马克创造的游戏引擎被用来制作其他的第一人称射击游戏,比如《半条命》(Half-life)和《荣誉勋章》(Medal of Honor)。

在 2007 年苹果公司全球软件开发者年会上,卡马克宣布了 id Tech 5,它实际上消除了过去对美工和设计人员的纹理内存限制,允许在像素级别上对整个游戏世界实现独特的定制设计,并提供了几乎无限的视觉真实性。"该技术可以允许" 广袤的户外场景,而室内场景则具有前所未见的艺术细节。

2013 年的 QuakeCon,卡马克表示对函数式编程很感兴趣。他在 Twitter 上表示了 “已经学习 Haskell 一年”,“学习 SICP 和尝试使用 Scheme 中”,并且表示正在用 Haskell 重写德军总部。与此同时,卡马克建议其他游戏开发者尝试函数式编程。

除了游戏领域,卡马克还是个火箭爱好者,并成立了名为犰狳宇航(Armadillo Aerospace)的私人研发团队。

总结起来,这位卡马克大神就是:特别能创造、特别能折腾还特别聪明。在 AI 大火的今天,他又把自己的 “折腾精神” 发挥得淋漓尽致。

大神的一周编程实践:徒手实现反向传播与 CNN

几天之前,卡马克大神在 Facebook 上发表了一篇文章,总结了一下自己如何徒手实现反向传播与 CNN 的事情。以下内容编译自卡马克的自述文章:

间隔了好几年,我终于又可以进行我的一周编程实践了,在编程的世界里我可以在隐士模式下工作,远离日常的工作压力。过去几年,我的妻子一直慷慨地为我打造这种环境,但我一般不善于在工作中休息。

随着 Oculus(卡马克目前所在公司)工作步伐的改变,我打算从头开始编写一些 C ++ 代码来实现神经网络,而且我想用严格的 OpenBSD 系统来实现。有人可能会说这是一个非常随意且不太靠谱的选择,但事实证明这是行得通的。

尽管我没有真正使用过它,但我一直很喜欢 OpenBSD——一个相对简单且足够自用的系统,它具有紧凑的图形界面,并且重视质量和工艺。Linux 什么都好,但图形界面不够紧凑。

我不是 Unix 极客。各种系统我都可以用,但我最喜欢在 Windows 上使用 Visual Studio 进行开发。我认为在老式的 Unix 风格下完成一周的沉浸式工作会很有趣,即使这意味着工作速度要慢一些。这是一次复古计算的冒险——使用 fvwm 和 vi。不是 vim,实际上是 BSD vi。

其实到最后,我也并没有真正全面地探索这个系统,95%的时间都花在基本的 vi/make/gdb 操作中。我喜欢那些操作手册页面,因为我试图在自带的系统中做所有事情,而不诉诸于互联网搜索。阅读诸如 Tektronix 终端等已有 30 多年历史的事物的参考手册是一件很有意思的事情。

我有点意外,C++ 的支持做得不是很好。G++ 不支持 C++ 11,并且 LLVM C++ 不能很好地与 gdb 配合使用。Gdb 也让我踩了不少坑,我同样怀疑是由于 C++ 的问题。我知道你可以获得更新的版本,但我坚持使用基础系统。

事后看来,我还不如完全复古,干脆在 ANSI C 中做所有事情。和许多老程序员一样,有很长一段时间,我一直在琢磨 “也许 C++ 并不像我们想象的那么好”。我仍然喜欢 C++ 的很多方面,但对于我来说用普通的 C 语言来构建小型项目并不困难。

也许下次我再进行一周编程实践时,我会尝试完整的 emacs,这是另一个我还没怎么接触过的主流文化。

我对大多数机器学习算法已经有比较基本的了解,并且我已经完成了一些线性分类器和决策树的工作,但出于某种原因,我从未使用过神经网络。在某种程度上,我怀疑是深度学习太过流行导致那个不愿意人云亦云的内在的我感到抵触。我仍然持有一点反思性的偏见,反对 “把所有的东西都扔在 NN(神经网络)上,让它自己整理出来!”

为了彻底贯彻我这次复古主题的精神,我打印了几篇 Yann LeCun 的旧论文,并打算完全脱离互联网去完成所有事情,这就好像我被困在了某个山间的小屋里,但最后我还是在 YouTube 上看了很多斯坦福 CS231N 课程视频,并发现它们非常有价值。我一般很少看课程视频,因为这通常让我觉得时间花的不值,但在我 “隐退编程” 的这段时间里看这些视频感觉还是很棒的!

我不认为我有什么特别的洞察力来为神经网络添砖加瓦,但对我来说这是非常高效的一周,充分将 “书本知识” 转化为真实体验。

我采用了一种我经常使用的模式:先写出一段粗糙且不怎么优美的代码,初步得到结果,然后用从视频课程学到的东西再写出一段全新且更优美的代码,这样一来两份代码可以并存和交叉检查。

我一开始尝试实现反向传播,结果两次都做错了,数值微分比较至关重要!有趣的是,即使在各个部分都出现错误的情况下,训练仍然能够进行——只要大多数时候符号正确,通常就会取得进展。

我对我的多层神经网络代码非常满意,它已经可以在我未来的工作中直接使用。是的,对于很多重要的事情我一般都使用一个已有的库,但是在很多时候,哪怕只有一个. cpp 和. h 文件是你自己写出来的,还是会方便许多。

我的 CNN 代码还很粗糙但已经凑合能用了,我可能还会再用一两天的时间来完成一个更干净而灵活的实现。

有一件事我觉得很有趣,在加入任何卷积之前,用我的初始 NN 基于 MNIST 进行测试,我得到的结果明显好于 LeCun 98 年的论文中报告的用于比较的非卷积 NN——我使用了包含 100 个节点的单个隐藏层,在测试集上的错误率大约为 2%,而 LeCun 论文中使用了包含更多节点和更深层的网络错误率却是 3%。我将其归因于现代最佳实践——ReLU、Softmax 和更好的初始化过程。

我认为这是关于神经网络工作的最有趣的事情之一 :它非常简单,突破性的进步通常只需要几行代码即可表达出来。这感觉和图形世界中的光线跟踪有一些相似之处,只要你拥有数据并且对运行时间有足够的耐心,你就可以很快地实现基于物理的光传输光线跟踪器,并生成最先进的图像。

通过探索一系列训练参数,我对过度训练 / 泛化 / 正则化有了更好的理解。在我不得不回家的前一天晚上,我不再修改架构,只是玩超参数。“训练!”简直比 “编译!” 更糟糕,更难让人保持专注。

现在,我要开始睁大眼睛寻找新的工作机会了,我迫不及待地想把我学到的新技能用起来!

我有点担心明天进入办公室时,我的邮箱和工作区将会变成什么样子。

之后,大神 Yann LeCun 也回复了卡马克:

欢迎入坑,约翰!在 OpenBSD 上用 vi 来完成这件事实属英雄所为!每个人第一次尝试的时候都会遇到梯度错误。

在过去的 35 年里,我也做过很多次类似的事情。我的第一个反向传播模拟器是在 PDP11 的 FORTRAN 中编写的(大约在 1984 年)。第二个是在 Pascal 上的 Pr1me OS(大约在 1986 年,使用类似 Emacs 的编辑器)。第三个是由 Leon Bottou 和我在 C 中使用 emacs / gcc / make 在我们的 Amiga 1000s 上编写的(1987 年),我们写了一个 lisp 解释器用作交互式前端语言。当我在 1987 年搬到多伦多时,我把这个东西移植到了 Sun OS(BSD Unix)上。直到 2011 年左右,我们一直使用这个系统及其后继者(称为 Lush),2011 年之后我们才切换到 Torch7。但在 2010 年,我开始编写一个名为 EBLearn 的 C ++ 深度学习框架,由 Pierre Sermanet 和 Soumith Chintala 完成并维护。

在我们 1998 年的论文中,MNIST 上的全连接网络的错误率是次优的,因为我们使用了最小平方损失(对于标记噪声往往更加鲁棒),而不是交叉熵,利用交叉熵和更大的网络(>1000 个隐藏单元),错误率可以下降到 1.6%左右。

对于不太了解技术的读者,大概会产生一种 “神仙聊天” 的感觉,每一个字你都认识,但是你就是看不懂,所以我们贴心地为大家解释了一个简约版:

首先,大神卡马克牛逼在哪儿了呢?看下知乎网友 wsivoky 的总结:

48 岁仍然在学习新技术、编写代码

喜欢受限的开发环境(Twitter 上曾说过,受限环境有益)

vi&emacs 都用 (注意是 bsd vi, 不是 vim,前者是 bill joy 当初开发的第一版也是第一个 screen editor )

使用 OpenBSD,但喜欢 Windows Visual Studio

用 makefile&gdb

使用 C++,同时也吐槽 C++

不用 Google Search, 完全看 man(Tektronix terminal 是 bill joy 编写 vi 时代的显示器,更早时候是靠纸张输出... )

对 Deep Learning 如此流行持保留态度

打印了 MNIST 早期论文并尝试实现

最终忍不住看了 cs231n

使用自己喜欢的开发方式:先实现后优化

实现反向传播容易出错

对 CNN 的效果很满意 (Tensor 操作也是"from-scratch-in-C++"CPU 实现的?)

对 NN 代码之少却强大感到兴奋(看到纯手工撸的 NN 运行结果之神奇,是很持久的快感)

编写算法实现对过拟合、正则化、调参有了更好的理解

再简约一点儿,大概就是这样:

卡神:反向传播和 CNN 这东西之前没搞过,那既然如此就自己动手试试吧。一个礼拜之后发现:哎呀~ 神经网络这玩意儿还挺有意思,感觉入坑了。(•̀ ω •́)✧

乐村儿:大兄弟你终于入坑了,来来来,我跟你说,用哥这方法你还有上升空间,以后咱们可以经常交流经验。(*≧︶≦))( ̄▽ ̄*) ゞ

成为大神,从你做起!

卡马克的一周编程实践一出,迅速在国内外程序员圈子里引发骚动。

国外程序员论坛 Reddit 的 MachineLearning 板块下,卡马克一周编程实践的话题受关注度 364,共收获 53 则留言

知乎上也很快有网友发布了相关问题,截止发稿时间,已有 685 人关注,浏览次数 25991

卡马克 Facebook 下的留言大多是这样的:

其实大家为之震动的并非卡马克入坑 AI 这件事本身(当然大神神乎其技的编程水平也确实让人顶礼膜拜),而是卡马克作为一位诸多成就加身的老牌程序员兼 Oculus CTO,仍然狂热地爱着编程这件事本身,并且保持着对一切新鲜事物感到好奇的赤子之心。

当卡马克对 AI、神经网络、深度学习产生兴趣,决定探索一下这些新技术时,他没有直接安装 TensorFlow 或 PyTorch, 而是花了一个星期的时间,通过逐一编写各个功能模块代码,并进行了 MNIST 实验,从头开始实现 CNN 和反向传播。他没有去学习最新的术语,但却从中获得了最有价值的知识。这里不争论 scratching 到底好还是不好,但手撸新技术确实是学习的一种好途径。面对新技术,不纠结要不要尝试、不犹豫会不会太难,而是动手干,无怪乎知乎网友将卡马克称作 “老程序员的标杆”。

这是一个最好的时代,技术日新月异,热点一年一个,对技术人才的需求越来越大。这是一个最坏的时代,深陷技术漩涡的程序员们成为了最容易焦虑的群体,今天是 AI 火了我要不要转行,明天是新技术太多学不动了,后天是 35 岁程序员中年危机了。其实哪里有那么多可焦虑的,有时间焦虑不如多撸几行代码。互联网技术更新确实快,这要求程序员必须终身学习,但这是选择了这个职业的宿命。

卡马克告诉你:忘记中年危机吧,想想我们应该做些什么,才不辜负这美好的时代!

写在最后

这里引用知乎网友姚钢强的回答作为结语。

读过《DOOM 启世录》 两遍,John Carmack 说过:

在信息时代,客观障碍已不复存在,所谓障碍都是主观上的。如果你想动手开发什么全新的技术,你不需要几百万美元的资金,你只需要在冰箱里放满比萨和可乐,再有一台便宜的计算机,和为之献身的决心。我们在地板上睡过,我们从河水中趟过。

他切实做到了,与君共勉。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分