Swift For TensorFlow開源,敲響了Python的喪鐘?
作者:weakish
來源:論智
今年3月的TensorFlow開發者峰會上,Google宣布了Swift For TensorFlow項目,提到這一項目將在4月開源。就在4月快過去的時候,Google終於在GitHub上公開了Swift For TensorFlow的源代碼。
提到Swift語言,大家第一個想到的是Apple。所以,很自然地,Swift For TensorFlow,乍聽起來,似乎僅僅是iOS開發者需要關心的事。
然而,其實iOS開發者並不需要關心Swift For TensorFlow,反而是機器學習開發者都需要關心Swift For TensorFlow.
Swift For TensorFlow並不面向iOS開發
目前而言,iOS應用,如果想集成機器學習功能,那可以通過Apple提供的Core ML框架:
Core ML的工作流程如上圖所示,上面的Core ML機器學習模型,可以是你從網上找的現成的,也可能是你自己開發的(一般由基於MXNet、TensorFlow等開發的模型轉換而來)。所以,其實Core ML並不在意你的模型是用Python加TensorFlow編寫的,還是Swift加TensorFlow編寫的,甚至是用MXNet編寫的。最終iOS應用都是通過Core ML框架調用Core ML格式的模型。
所以,Swift For TensorFlow並不面向iOS開發,而是要取代Python!
其實這並不奇怪。雖然人們總是把Swift和Apple聯繫起來,但其實Swift之父Chris Lattner在Google Brain(順便提下,Python之父Guido Van Rossum在2012年底離開Google去了Dropbox)。
Lattner在Swift發布前發推:「下個月我將是第一個也是唯一一個有4年swift編程經驗的人 :-)」
Python有什麼不好
雖然Python是現在最流行的機器學習語言,但其實在機器學習場景下,Python的問題還真不少:
部署麻煩,運行時依賴太多。首先,移動端應用,帶上一大堆Python包,不太現實。其次,很多公司的生產環境,基於運維需求,不想部署大量Python包。目前的補救方法是,使用Python訓練模型,而真正的推理(運用)階段用別的語言,比如C++重寫,這導致了重複勞動,拖慢了開發周期。
動態類型,沒有編譯期類型檢查。這導致很多錯誤要到運行時才能發現。在機器學習場景下,這一問題的後果更加嚴重,因為機器學習模型常常需要訓練、運行很長時間。事實上,大型的Python項目都非常依賴單元測試,通過單元測試捕捉很多錯誤。但在機器學習的場景下,單元測試沒有用武之地。普通程序,跑一遍單元測試可能也就不到半小時,發現報錯,改過來再跑一遍就是了。而機器學習,模型跑了半個月,報錯了,發現是代碼編寫錯誤,試求此時心理陰影面積。
並發困難,臭名昭著的GIL問題。而機器學習模型對算力的貪婪需求,迫切需要靠並發緩解。
性能太差。事實上,像PyTorch這樣的框架,挖空心思補救Python的性能問題。而TensorFlow依靠圖模型(詳見下一節)以及C++、CUDA定製操作來規避Python的性能問題。使用C++、CUDA定製操作帶來了兩個問題:
C++是一門複雜的語言。尤其是很多研究人員和數據分析人員,並不具備C++經驗。
使用C++/CUDA定製TensorFlow操作導致和硬體緊密耦合(CUDA意味著只能在Nvidia的GPU上跑),遷移到新硬體困難。這一點對Google來說尤其關鍵,因為Google除了用Nvidia的GPU外,自家還有TPU。
使用TensorFlow的時候,你真的在寫Python嗎?
讓我們看一段簡短的TensorFlow代碼示例:
這上面是合法的Python代碼,但是仔細看看,這些代碼實際上幹了什麼,我們就會發現,其實這些代碼構建了一個圖m,然後通過的方法運行了圖m。
下面一段代碼可能更明顯,我們想迭代數據集,在TensorFlow下需要這樣寫:
我們看到,我們不能直接使用Python迭代數據集,而要通過TensorFlow提供的方法構建迭代器。
這一情況可以類比使用Python訪問SQL資料庫:
這裡,我們構造了SQL請求語句,然後通過Python「執行」()這些語句。表面上你在寫Python,其實關鍵的邏輯在SQL語句里。更準確地說,你是在用Python構造SQL語句,然後運行構造的語句。這稱為元編程(meta programming)。
同理,在TensorFlow下,表面上你在寫Python,其實關鍵的邏輯都在TensorFlow圖裡。更準確地說,你是在用Python構造TensorFlow圖,然後運行構造的圖。
實際上,2017年萬聖節(10月31日),Google發布了TensorFlow Eager Execution(貪婪執行),讓你可以直接使用Python編程,而不是使用Python元編程TensorFlow圖。
使用Eager Execution,上面兩段TensorFlow代碼可以改寫為:
你看,TensorFlow明明可以「好好地」用Python編程的嘛。之前為什麼要這麼大費周折地繞一個圈子?
因為性能。
機器學習,尤其是現代的複雜模型,有著極高的算力需求。TensorFlow圖可以很好地應對貪婪的算力需求,而Python則對此力不從心。
TensorFlow圖專門針對機器學習的需求設計,所以可以很好地優化,以提升性能。然而,性能的優化並不是沒有代價的,為了更好地優化,TensorFlow圖對模型有著許多假設(這些假設從另一方面來說也是限制),也要求構造、運行分階段進行(靜態圖模型)。這影響了模型的靈活性和表達力,因此,不支持動態圖模型是TensorFlow的一大痛點。
兼顧性能與靈活性
TensorFlow Eager Executation支持動態圖,但是性能很差(還記得我們之前提到的Python的性能和GIL問題嗎?);常規的TensorFlow性能好,但靈活性不行。那麼,有沒有可以兼顧兩者的方案呢?
機器學習社區在這方面做了很多探索。
傳統上,解釋器(TensorFlow Eager Executation本質上是一個解釋器)的性能,常常可以通過JIT來提升。PyTorch就嘗試通過Tracing JIT(跟蹤即時編譯)提升性能(基於Python、支持動態圖模型的PyTorch飽受Python性能問題的困擾)。簡單來說,Tracing JIT統計頻繁執行的操作,將其編譯為機器碼執行,從而優化性能。然而,Tracing JIT有一些問題,包括「展開的」運算可能導致非常長的trace,可能污染trace導致排錯困難,無法利用「以後的代碼」加以優化,等等。
因此,最終TensorFlow選擇了代碼生成這一條路。也就是對動態圖模型代碼進行分析,自動生成對應的TensorFlow圖程序。而正是這一選擇,導致了Python的出局。
圖程序提取(黃色方框)是Swift For TensorFlow的關鍵技術
Python有一大堆動態特性,使得Python無法被可靠地靜態分析。
那麼,就只有兩個選擇:
對Python語言進行剪裁,得到一個便於靜態分析的子集。
換語言。
實際上,Google在2017年開源過一個Tangent項目。當時做Tangent,是為了解決自動微分(automatic differentiation)問題——自動微分同樣依賴於對代碼進行分析,而Python語言很難分析。然而,Python的類高度依賴動態特性,很難在這樣的子集上得到支持。而如果連類這樣的抽象層級都不支持的話,那基本上已經完全不像Python了。
所以,就換語言吧。
順帶提一下,TensorFlow選擇了靜態分析後生成代碼並編譯的路線,但其實生成代碼並不一定非得使用編譯器。2010年提出的Lightweight Modular Staging(LMS)技術可以支持在運行時進行代碼生成,無需編譯器。不過,LMS技術下,對控制流程的支持需要一些極少數語言(比如Scala)才支持的奇異特性。所以即使用LMS,也一樣需要換掉Python。而TensorFlow之所以沒有選擇LMS,除了可以進行LMS的語言極少之外,還有一個原因是LMS需要用戶介入,比如,在Scala下,需要將數據類型顯式地包裹進Rep類型才能支持LMS。
為什麼是Swift?
其實,雖然有這麼多編程語言,可供選擇的範圍並不大。
首先,語言的生態系統很重要。選擇一門語言,其實也是選擇這門語言的生態系統,包括開發環境、調試工具、文檔、教程、庫、用戶。這就排除了創造一門新語言和使用大多數學術性語言兩個選項。
然後,動態性導致大批語言出局。之前已經提過,Python的大量動態特性導致難以可靠地靜態分析。同理,像R、Ruby、JavaScript之類的動態語言,也被排除了。
甚至像TypeScript、Java、C#、Scala這樣的靜態語言也不行,因為在這些語言中,動態分發(dynamic dispatch)非常普遍。具體來說,這些語言的主要抽象特徵(類和介面)其實基於高度動態的構造。比如,在Java中,調用的是類的方法,而不是類的方法。
Google自家的Go語言也有這個問題。Go的介面也是動態分發的。而且,Go沒有泛型,當然,Go的map類型具有一些內建在語言中的類似泛型的特性。如果TensorFlow使用Go語言的話,也得像一樣內建進Go語言才行,而Go語言社區推崇輕量設計,在Go語言中內建有悖其核心理念。
在Swift For TensorFlow發布的同一天,Go發布了新設計的logo
那麼,剩下的選擇就屈指可數了:
C++
Rust
Swift
Julia
C++很複雜,而且C++有著未定義行為太多的不好名聲,另外,C++大量依賴C宏和模板元編程。Rust的學習曲線非常陡峭。Julia是個很有趣的語言,雖然它是動態的,但是Julia有很多類型專門化(type specialization)的黑科技,因此也許可以支持TensorFlow圖特徵提取。不過,Julia的社區比Swift小,再加上Swift之父在Google Brain,最終TensorFlow選擇了Swift.
當然,需要指出的是,Swift的類同樣是高度動態的,但是Swift有sturt和enum這些靜態的結構,而且這些結構同樣支持泛型、方法、協議(Swift的協議提供類似介面的特性及mixin)。這使得Swift既可以被可靠地靜態分析,又能有可用的高層抽象。
另外,還記得之前我們提到的Python的缺點嗎?讓我們看看Swift在這些方面的表現:
部署簡單。Swift可以編譯為機器碼,基於Swift編寫的ML模型可以編譯為簡單易部署的文件。
靜態類型,提供編譯器檢查。另一方面,靜態類型也讓IDE可以更加智能地提示錯誤。這在實際編程中極有幫助。
Swift在語言層面還不支持並發,但可以很好地配合pthreads使用。而且Swift即將在語言層面加入並發支持。
Swift性能很好,對內存的需求也不高。由於Swift在移動端使用很普遍,因此Swift社區很重視性能優化。等顯式內存所有權支持加入後,Swift在很多場景下可以取代C++. Swift基於LLVM(別忘了,Swift之父也是LLVM之父),能夠直接訪問LLVM底層,而LLVM可以為Nvidia和AMD顯卡生成GPU核。因此,未來基於Swift定製TensorFlow操作也會是Swift For TensorFlow的優勢。
當然,目前機器學習社區積累了很多Python組件,因此,Swift For Python也提供了Python互操作性。比如,下面的代碼展示了如何在swift下訪問python的numpy庫(注釋為python代碼):
Python即將沒落?
最後,我們簡單展望下Python的前途。
想想現在Python主要用於什麼場景?
教學。是的,Python作為教學語言很合適,但是,僅僅適合作為教學語言,遠遠不夠。上了年紀的讀者可能還記得小時候微機課(是的,那時候還用「微機」稱呼電腦)上教的Logo語言(操作小烏龜畫圖),現在還有多少人用呢?曾經風光無限的Pascal語言就是為教學而開發的,現在還有多少人用呢?而且,教學語言的選擇很大程度上受語言流行程度影響,而不是相反。當年鬧得沸沸揚揚的MIT計算機科學和編程入門課程從scheme換成python,理由之一就是python更流行。
工具。Python用來寫一些小工具不錯,因為Python的標準庫非常出色,寫起來不啰嗦,小工具也不太考慮性能問題,項目規模小,缺乏編譯期類型檢測也不是大問題。但這一塊已經逐漸被Go蠶食,因為Go的標準庫同樣出色,寫起來也很簡潔,性能還比Python好,部署起來還比Python方便。
web開發。Python的web開發,其實更多地是因為Python流行,庫多,容易招人,而不是真的有多麼適合web開發。在web開發方面,Python有太多競爭者。古老的PHP仍有活力,PHP 7修正了很多一直被詬病的缺陷,還有Facebook這樣的巨頭為其開發配套工具。Ruby也仍然很受歡迎,很火的Python web開發框架Flask,其實最早就是借鑒了Ruby的Sinatra框架的設計。而高性能web開發越來越強調高IO、非阻塞,Node.js和Go在這方面表現出色,Java社區也出現了Netty和Vert.x(要比庫多,好招人,誰比得過Java?)。所以,Python在這方面實在沒什麼優勢。
科學計算。目前而言,Python在這方面仍有顯著優勢。然而,曾幾何時,Fortran也在科學計算領域佔據統治地位。現在還有多少人用呢?而且,由於Python的性能問題,實際上大量Python的科學計算庫底層大量依賴C或C++的,有朝一日轉移的話,會比當年的Fortran快太多。
機器學習。不得不說,AI和ML的浪潮,給Python打了強心針。因為科學計算相對而言,關注度沒有那麼高。對比一下R,在統計分析領域也很流行,但從來沒有Python這麼受關注,這是因為R並不適合寫工具和開發web. 然而,成也蕭何,敗也蕭何,Swift For TensorFlow的出現,就說明Python的機器學習主流語言的地位並不穩固(Python在機器學習領域的流行,更多地是因為科學計算方面的積累和流行程度,而不是真正適合建模機器學習問題)。
所以說,Python的沒落,很有可能喲~
※葯的價格是固定的嗎?用Python分析了上千家藥房!遠離被宰!
※Python_cmd的各種實現方法及優劣
TAG:Python |