Python 的局限
Python 的优势同时也隐含了其劣势。 我亲身感受的痛点有二。
难以保证代码质量
语法灵活的另一种说法是:一个程序有多重写法。现代软件工程里没有孤胆英雄,全靠大家合作。多种可能的写法往往意味着团队容易在 code review 时吵架 —— 而且难以平息,因为不一定有客观选择标准。很多其他语言也有类似问题,比如 Java。解法是,社区里定一些设计模式(design patterns),程序员写程序前先看看有没有可以套用的设计模式,如果有,则遵循之。所以 Java 程序员除了学习 Java 语法,还要学习设计模式。C++ 也有类似的问题。解法之一是 Google 定了一套 code style —— 哪些语法可以用,哪些不许用 —— 按照 Rob Pike 的解释,允许用的部分语法挑出来,就是 Go 的设计初衷。Python 太灵活,以至于 code style 都没法定义得和 C++ 的一样细致 —— PEP8 几乎只是说说排版要求,对语法的选用几乎没有限制。Python 也没法定义模式 —— 太多了,写不完。
Python 为了灵活采用动态类型,所以我们看一个 Python 函数,必须得细读其代码,否则都不知道它有没有返回值,以及返回值是啥。Python 也有语法扩展,要求编程者指明输入输出的数据类型,不过用的人不多 —— 毕竟大家都是冲着“灵活”来的;要是限制灵活性,那就真不如用静态类型语言了。这个结果是,每个 Python 函数都不能太长,否则看不明白了。可是 Python 程序员就是冲着灵活性来的,要的就是信马由缰的感觉,管你懂不懂呢,我自己明白就行,反正发完论文就毕业了。拆分函数细化粒度?不可能的,这辈子都不可能的。
有没有写的很好的 Python 代码呢?有的。比如 Google Tangent。这是一个很小众的项目。作者也只有两个。其代码结构清晰 —— 每个函数基本都在十行代码之内,代码和注释一样长,所以很好懂。不过这也和 Python 用户众多的印象相悖了。我在负责 PaddlePaddle 项目的时候,除了自己努力学习和总结 Python 的模式,也配置 CI 调用各种工具做源码检查,然并卵,这些工具没有智能化到可以自动注释代码,也不会自动拆分太长的函数定义。
难以优化计算效率
Python 的语法丰富、灵活性强,所以解释器写起来很复杂,要优化性能也很难。相比之下,Go 语言语法简洁,表达能力远胜于 C 但是 keyword 总数少于 C,这种简洁使得 Go 程序的性能优化比较容易。在 Go 诞生后几年,Go 编译器对代码的性能优化水平就快速接近 GCC 对 C++ 程序的优化水平了,而 C++ 和 Python 一样,语法丰富,所以编译器里的代码性能优化功能很不容易开发。
有人尝试写 Python 的编译器来代替解释器,从而在程序执行之前先做性能优化。但是 Python 语法比 C++ 更灵活,以至于几乎没法写一个完全支持 Python 标准语法的编译器出来。几个尝试因此作罢。目前的普遍的做法是解释器来做执行时优化(JIT compilation),因为有 runtime 信息,所以相对编译器更容易一些。
在 AI 领域,深度学习训练非常消耗计算资源。TensorFlow 的图模式的解法是:用户写的 Python 程序在执行时并不真的做训练,而是把训练过程输出成一个被称为”计算图“的数据结构,交给 TenosrFlow runtime 这个“解释器”来执行。只要保证 TensorFlow runtime 的执行效率,即可不受 Python 解释器效率的限制。
TensorFlow 图模式用心良苦,也画蛇添足 —— 源程序、各层 IR、以及 binary code 是一直以来人们用来描述计算过程的表达方式,TensorFlow 项目早年间发明的计算图重复造了个轮子,而且造得不专业 —— 图难以表达 if-else、循环、函数定义和调用,更别提 closure、coroutine 和 threading 这样的高级控制流结构了。人工智能工程师的非专业编译器设计让 LLVM 的作者 Chris Lattener 掩面而笑,于是他尝试用 Swift for TensorFlow 替换 Python 作为前端语言,用 MLIR 代替 TensorFlow 中的“计算图” [2] 。
补全局限的尝试
我在负责 PaddlePaddle 期间为了验证 Paddle Fluid 的能力,和我的同事陈曦一起做了一个无人驾驶船,尝试用 Fluid 写 immitation learning 方法,让船能学习人类驾驶员的驾驶技术,详情请见 系列博客[3]。可是如果我们把跑 Python 程序的 MacBook Pro 带上船则太费电,而嵌入式的设备上又不适合跑 Python 写的训练程序。如果每次停船后上传数据到服务器训练,那么船向人学习迭代的进度就太慢了。
为此,当时另一位同事杨杨写了 Paddle Tape,用 C++ 实现了 PyTorch 的自动求导能力,结合 Paddle Fluid 积累的众多用 C++ 写的基本计算单元(operators),Tape 完全是一个 C++ 实现的深度学习系统系统,和 Python 没啥关系了。
2019 年初,我的朋友洪明胜在 Google 负责 Swift for TensorFlow 项目,这也是一个 AI 基础架构去 Python 化的尝试。他当时拉我给 Chris Lattener 的团队分享了 Paddle Tape 和无人船的故事,并修改了幻灯片[4]。
我在蚂蚁集团负责的一个开源分布式深度学习训练系统 ElasticDL,尝试过调用 TensorFlow graph mode、eager execution mode、PyTorch、和 Swift for TensorFlow,很受 Swift for TensorFlow 的设计理念以及和 Python 生态共荣的策略的启发。
Go+ 和数据科学
以上尝试提醒我,语言的选择标准必须包括:语法清晰简练和语法稳定容易学习。也希望语言的使用者是比较有探索精神的一个群体。Go+ 及其基于 Go 社区的用户群体刚好符合这些条件。
在 Go+ 出现之前,也有把 Go 用于数据科学的尝试,也有用 Go 实现的张量运算库(比如 gonum),但是用起来都不如用 numpy 的 Python 程序简练,很直接的一个原因是 Go 的常量需要指定数据类型,而 Python 的则不用。我 写了几个对比 [5] 。
用 Go 定义一个 ndarray 类型的常量,用户需要写:

(编辑:信阳站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|