在今年3月的TensorFlow开发者峰会上,谷歌公布了SwiftForTensorFlow项目,并提到该项目将于4月开源。就在四月即将过去的时候,谷歌终于在GitHub上发布了SwiftForTensorFlow的源代码。说到Swift语言,大家第一个想到的就是Apple。因此,很自然地,SwiftForTensorFlow乍一看似乎是只有iOS开发人员才需要关心的事情。不过,实际上iOS开发者不需要关心SwiftForTensorFlow,机器学习开发者需要关心SwiftForTensorFlow。SwiftForTensorFlow不适用于iOS开发。可以使用Apple提供的CoreML框架:CoreML的工作流程如上图所示。上面的CoreML机器学习模型可以是网上找的现成的,也可以自己开发(一般是基于MXNet,由TensorFlow等开发的模型转换而来)。所以,事实上,CoreML并不关心你的模型是用Python加TensorFlow、Swift加TensorFlow,还是MXNet编写的。最后,iOS应用程序通过CoreML框架调用CoreML格式的模型。所以SwiftForTensorFlow不是为了iOS开发,而是为了替代Python!其实,这并不奇怪。尽管人们总是将Swift与Apple联系在一起,但实际上,Swift之父ChrisLattner曾在GoogleBrain(顺便提一下,Python之父GuidoVanRossum于2012年底离开Google前往Dropbox)。Lattner在Swift发布之前发推文说:“下个月我将成为第一个也是唯一一个拥有4年Swift编程经验的人:-)”Python有什么问题?其实在机器学习场景下,Python存在很多问题:部署麻烦,运行时依赖太多。首先,为移动应用程序带来大量的Python包是不现实的。其次,很多公司的生产环境基于运维需求,并不想部署大量的Python包。目前的补救办法是使用Python来训练模型,而真正的推理(应用)阶段则用其他语言重写,比如C++,这样会导致重复劳动,拖慢开发周期。动态类型,没有编译时类型检查。这导致许多错误直到运行时才被发现。在机器学习的背景下,这个问题的后果更加严重,因为机器学习模型往往需要很长时间才能训练和运行。事实上,大型Python项目非常依赖单元测试,通过单元测试捕获许多错误。但是在机器学习的背景下,单元测试是没有用的。对于普通的程序,跑一次单元测试可能不到半个小时。如果发现错误,只需更改它并重新运行即可。至于机器学习,模型跑了半个月,报错,发现是编码错误。此时尽量寻找心理阴影区。并发很难,臭名昭著的GIL问题。机器学习模型对计算能力的贪婪需求迫切需要通过并发来缓解。性能太差了。事实上,像PyTorch这样的框架不遗余力地解决Python的性能问题。TensorFlow依赖图形模型(详见下一节)和C++、CUDA自定义操作来避免Python的性能问题。使用C++,CUDA自定义操作带来两个问题:C++是一门复杂的语言。特别是,许多研究人员和数据分析师没有C++经验。使用C++/CUDA自定义TensorFlow操作会导致与硬件的紧耦合(CUDA意味着只能运行在NvidiaGPU上),很难迁移到新的硬件上。这对谷歌来说尤为关键,因为除了使用Nvidia的GPU外,谷歌还有自己的TPU。你真的在使用TensorFlow时写Python吗?让我们看一个简短的TensorFlow代码示例:m,feed_dict={x:[[2.]]}))这是合法的Python代码,但是仔细看,这些代码实际上做了什么,我们会发现这些代码实际上构造了一个图m,然后运行图m通过tf.Session()的run方法。下面这段代码可能更明显。我们要迭代dataset数据集,在TensorFlow下需要这样写:dataset=tf.data.Dataset.range(100)iterator=dataset.make_one_shot_iterator()next_element=iterator.get_next()foriinrange(100):value=sess.run(next_element)asserti==value我们看到不能直接用Python来迭代数据集,而是通过TensorFlow提供的方法构建迭代器。这种情况可以类比使用Python访问SQL数据库:t=('RHAT',)q='SELECT*FROMstocksWHEREsymbol=?'c.execute(q,t)这里,我们构造一条SQL请求语句,然后通过Pythonexecuteit""(执行)这些语句。表面上看你写的是Python,其实关键逻辑在SQL语句里。更准确地说,您是在Python中构造SQL语句,然后运行构造的语句。这称为元编程。同理,在TensorFlow下,表面上写的是Python,其实关键逻辑在TensorFlowgraph中。更准确地说,您正在用Python构建TensorFlow图,然后运行构建的图。事实上,在2017年万圣节(10月31日),谷歌发布了TensorFlowEagerExecution(贪婪执行),让你可以直接用Python编程,而不是使用Python元编程TensorFlow图。使用EagerExecution,上面两段TensorFlow代码可以改写为:importtensorflowastfimporttensorflow.contrib.eagerastfe#Enablegreedyexecutionmodetfe.enable_eager_execution()x=[[2.]]m=tf.matmul(x,x)print(m)dataset=tf.data.Dataset.from_tensor_slices([1,2,3,4,5,6])dataset=dataset.map(tf.square).shuffle(2).batch(2)#Python-styleiteratorClassforxintfe.Iterator(dataset):print(x)你看,TensorFlow显然可以“很好”地用Python编程。兜兜转转之前怎么折腾了那么多?因为性能。机器学习,尤其是现代的复杂模型,对计算能力有着极高的要求。TensorFlow图可以很好地应对对计算能力的贪婪需求,而Python无法应对。TensorFlow图是专门为机器学习的需求而设计的,因此可以很好地优化它们以提高性能。然而,性能优化并非没有代价。为了更好的优化,TensorFlow图对模型有很多假设(这些假设也是另一方面的限制),也需要分阶段进行构建和运行(静态图模型)。这会影响模型的灵活性和表现力。因此,缺乏对动态图模型的支持是TensorFlow的一大痛点。兼顾性能和灵活性TensorFlowEagerExecutation支持动态图,但性能较差(还记得我们前面提到的Python的性能和GIL问题吗?);常规TensorFlow具有良好的性能,但缺乏灵活性。那么,有没有一种解决方案可以兼顾两者呢?机器学习界在这方面做了很多探索。传统上,解释器(TensorFlowEagerExecutation本质上是解释器)的性能通常可以通过JIT来提高。PyTorch试图通过TracingJIT(跟踪即时编译)来提高性能(PyTorch基于Python并支持动态图模型,但存在Python性能问题)。简单来说,TracingJIT就是统计频繁执行的操作,编译成机器码执行,以优化性能。但是TracingJIT也存在一些问题,包括“unrolled”操作可能导致trace很长,可能污染trace导致排查困难,无法用“latercode”进行优化等等。因此,最终TensorFlow选择了代码生成这条路。即解析动态图模型代码,自动生成对应的TensorFlow图程序。而正是这个选择导致了Python的出局。图程序提取(黄框)是Swift的关键技术ForTensorFlowPython有很多动态特性,使得Python无法可靠地进行静态分析。那么,只有两个选择:裁剪Python语言,得到一个便于静态分析的子集。改变语言。其实谷歌在2017年就开源了一个Tangent项目,当时做Tangent就是为了解决自动微分的问题——自动微分还依赖于代码的分析,Python语言很难分析。但是Python的类高度依赖动态特性,很难支持这样的子集。而如果你连类这样的抽象层次都不支持,那基本上就完全不像Python了。所以,只需更改语言。顺便说一下,TensorFlow选择了静态分析后生成代码再编译的路线,但其实生成代码并不一定需要使用编译器。2010年提出的LightweightModularStaging(LMS)技术可以支持运行时代码生成,无需编译器。但是在LMS技术下,对控制流的支持需要一些单一的特性,只有极少数的语言(比如Scala)才支持。所以即使你用了LMS,你还是需要换掉Python。TensorFlow之所以没有选择LMS,除了能进行LMS的语言很少外,还有一个原因是LMS需要用户干预。例如,在Scala下,需要将数据类型显式包装到Rep类型中以支持LMS。为什么是斯威夫特?其实编程语言虽然有这么多,但是选择的范围并不是很大。首先,语言的生态系统很重要。选择一门语言实际上就是选择了语言的生态系统,包括开发环境、调试工具、文档、教程、库和用户。这排除了创建新语言和使用大多数学术语言的选择。然后,活力导致大量语言的淘汰。如前所述,Python高度动态的特性使其难以可靠地进行静态分析。同样,R、Ruby、JavaScript等动态语言也被排除在外。甚至像TypeScript、Java、C#、Scala这样动态调度盛行的静态语言也不行。具体来说,这些语言的主要抽象特征(类和接口)实际上是基于高度动态的构造。例如,在Java中,Foofoo=newBar();foo.m()调用Bar类的m方法,而不是Foo类的m方法。Google自己的Go语言也有这个问题。Go的接口也是动态调度的。此外,Go没有泛型,当然Go的映射类型在语言中内置了一些类似泛型的特性。如果TensorFlow使用Go语言,Tensor必须像map一样内置到Go语言中,而Go语言社区提倡轻量级设计,Go语言中内置Tensor与其核心理念相悖。在SwiftForTensorFlow发布的同一天,Go发布了一个新设计的logo所以只剩下屈指可数的选项:C++RustSwiftJuliaC++很复杂,而且C++有太多未定义行为的坏名声。此外,C++严重依赖C宏和模板元编程。Rust的学习曲线非常陡峭。Julia是一种非常有趣的语言。虽然是动态的,但Julia有很多类型特化的黑科技,所以可能支持TensorFlow图特征提取。不过Julia的社区比Swift小,Swift之父在GoogleBrain,TensorFlow最终选择了Swift。当然,需要指出的是,Swift的类也是高度动态的,但是Swift有静态的,比如sturt和enum。结构,而这些结构还支持泛型、方法和协议(Swift的协议提供了类似接口的特性和混合)。这使得Swift既可以可靠地进行静态分析,又可以使用高级抽象。还有,还记得我们前面提到的Python的缺点吗?让我们看看Swift在这些方面的表现如何:易于部署。Swift可以编译成机器码,用Swift编写的ML模型可以编译成简单易部署的.o/.h文件。静态类型,提供编译器检查。另一方面,静态类型也可以让IDE更智能地提示错误。这在实际编程中非常有帮助。Swift尚不支持语言级别的并发,但它与pthreads配合得很好。而Swift即将在语言层面加入并发支持。Swift性能良好,对内存要求不高。由于Swift在移动端应用广泛,因此Swift社区非常重视性能优化。加入显式内存所有权支持后,Swift可以在很多场景下替代C++。基于LLVM(别忘了,Swift之父也是LLVM之父),可以直接访问LLVM底层,可以为Nvidia和AMD显卡生成LLVM。显卡核心。因此,未来基于Swift定制TensorFlow操作,也将是SwiftForTensorFlow的一个优势。当然,现在的机器学习社区已经积累了大量的Python组件,所以SwiftForPython也提供了Python的互操作性。例如下面的代码展示了如何在swift中访问python的numpy库(注释为python代码):importPythonletnp=Python.import("numpy")//importnumpyasnpleta=np.arange(15).reshape(3,5)//a=np.arange(15).reshape(3,5)letb=np.array([6,7,8])//b=np.array([6,7,8])Python即将衰落?最后,我们简单展望一下Python的未来。想想现在Python主要用于哪些场景?教学。是的,Python非常适合作为教学语言,但仅仅适合作为教学语言是不够的。年长的读者可能还记得小时候微机课(是的,那时候还是用“微机”来称呼计算机)上的Logo语言(操作小乌龟画图)。现在还有多少人在用?曾经辉煌的Pascal语言是为教学而开发的,现在还有多少人在使用它?而且,教学语言的选择在很大程度上受语言流行程度的影响,而不是相反。当年闹得沸沸扬扬的MIT计算机科学和编程入门课程从scheme改成python的原因之一就是python更受欢迎。工具。Python好写一些小工具,因为Python的标准库很好,写起来不啰嗦,小工具没有考虑性能问题,项目小,缺少编译时类型检测问题不大。但是这个领域已经逐渐被Go蚕食,因为Go的标准库也很优秀,写起来也很简洁,性能比Python好,部署比Python方便。Web开发。Python的web开发其实更多的是因为Python的流行,库多,招人容易,而不是它有多适合web开发。在Web开发方面,Python有太多竞争对手。古老的PHP犹在,PHP7修复了很多被诟病的缺陷,Facebook等巨头为其开发配套工具。Ruby也仍然很受欢迎。Flask这个非常流行的Pythonweb开发框架,其实最早是借鉴了Ruby的Sinatra框架的设计。高性能Web开发越来越强调高IO和非阻塞。Node.js和Go在这方面表现出色。Netty和Vert.x也出现在了Java社区中(比库多,好招,谁比得上Java?)。所以,Python在这方面确实没有优势。科学计算。就目前而言,Python在这方面仍然具有显着优势。然而,曾经有一段时间Fortran也主宰了科学计算。现在还有多少人在用?而且,由于Python的性能问题,实际上大量的Python科学计算库在底层严重依赖C或C++。如果哪天转过来,比当年的Fortran快多了。机器学习。不得不说,AI和ML的浪潮给了Python一个助力。因为科学计算相对不太受关注。与R相比,它在统计分析领域也很受欢迎,但Python却从未受到如此大的关注。这是因为R不适合写工具和开发web。说明Python作为机器学习主流语言的地位并不稳定(Python在机器学习领域的火爆更多是因为科学计算的积累和普及,而不是真正适合建模机器学习问题)。所以,Python的没落是很有可能的~
