程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

AI100發表於2017-06-09

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

作者 | Ryan Dahl

編譯 | AI100 

tinyclouds.org/residency/


去年,在我研究TensorFlow出了一番成果後,我開始申請Google Brain的首屆見習專案(Google Brain Residency Program),最後居然成功了。受邀參加該專案的共有24人,每個人都有著不同的機器學習背景。

我們24個人,需要在Google位於山景城的深度學習研究實驗室工作一年,每天跟Google的科學家和工程師們一起,共同來做TensorFlow的前沿研究。想想就把我給興奮壞了。

如今,這個為期一年的專案已經結束了,確實收穫滿滿。我也希望把學到的一些東西,結合我的心得,總結一下,分享出來,希望能為在機器學習的道路上耕耘的夥伴兒提供一些幫助。

先說說我起初擬定的目標吧。我的目標是修正老電影或電視劇的畫面

想象一下,畫面粗糙的90年代電視劇,或是60年代黑白電影,要是能被修正為色彩華麗的4K畫面,觀看體驗會有多棒!

而這事看上去完全可行:我們很輕鬆就能把4K視訊轉換成滿是顆粒感的、低解析度的、甚至是隻有黑白兩色的視訊,只要訓練出某個監督模型來反轉這個過程就可以了。而且,可用的訓練資料無窮無盡。咳咳,這個想法簡直太強悍了!

帶著這樣的目標,我再一次(上一次是為Node.js專案)從紐約布魯克林搬到舊金山灣區,以更好地實現這項深度學習技術。幾天後,我的日常生活就變成了跟Google的深度學習專家進行討論、在Google龐大的軟體庫內瀏覽程式碼,blablabla...

接下來我要開始大談這些日子以來,我所做的很多技術細節。當然,如果不想看,可以直接跳到總結部分。

超解析度的畫素遞迴

眾所周知,FBI在《犯罪現場調查》(CSI)中所用的縮放技術是不可能實現的。沒人能任意放大照片。然而,在你放大照片影像時把相關畫素所構成的合理圖形呈現出來,這還是有可能做到的。能夠平滑地提升影像解析度,將是我實現目標的第一步。

該問題在本文中用 超解析度 一詞來描述,很久以前人們就在嘗試解決它了。

據此,我們認識到簡單使用ConvNet無法徹底解決該問題:它只是把你輸入的低解析度影像的畫素間距(L2)最小化來輸出高解析度影像。這類損失函式所學到的,是輸出所有可能結果的平均值——所輸出的影像看上去就比較模糊了。我們想要的模型是這樣的:對於給定的低解析度影像,它能從所有可能的強化結果中選出那張特定的、效果最好的高解析度影像。如果是“強化”一張關於樹的模糊照片,我們會希望它能給出枝、葉在位置上的一些細節,即便它們的位置並非是枝、葉在樹上的實際位置。

某種條件型的GAN(生成式對抗網路)看上去很有希望,但構建起來較難,經過幾次失敗的嘗試後,我們換成了另一種新型的生產式模型:PixelCNN,它也比較有戲。(等我們啟動之後,用GAN解決來超解析度問題的SRGAN就釋出了,它生成的結果非常好。)

PixelCNN是一種奇怪的反直覺模型。它將影像生成問題重寫成每次選擇一個畫素序列。像LSTM(長短時記憶網路)這樣的門控制遞迴網路在序列生成方面是非常成功的,它通常會用在單詞或字元上。PixelCNN巧妙地構建出一個卷積神經網路(CNN),它能基於先前的畫素的概率分佈來精確生成畫素。這是RNN和CNN的混合功能。

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

示意圖由 van den Oord 等人所繪

意外的是,PixelCNN所生成的影像看起來非常自然。不像艱難平衡兩種目標的對抗網路,該模型的目標只有一個,因而面對超引數的變化,它有更好的穩健性。也就是說,它更易於優化。

解決超解析度問題的首次嘗試,我野心過大,選用了ImageNet來訓練PixelCNN。(跟CIFAR-10、CelebA或LSUN相比,ImageNet是個較難的資料集,很多生成式模型研究都在用它。)但很顯然,按畫素來序列生成影像的過程極其緩慢。輸出影像的尺寸大於64x64時,耗時將超過數小時!然而,在我把影像的尺寸限制到小尺寸,並使用臉部或臥室類的小型資料集後,得出的結果就開始令人激動了。

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

圖1:用名人臉部影像資料集訓練出來的超解析度畫素遞迴模型所生成的高解析度影像。左側為測試資料集所用的8x8低解析度輸入影像。中間為PixelCNN模型所輸出的32x32高解析度影像,右側是原始的32x32解析度影像。我們的模型優先整合臉部特徵,而後去合成較為逼真的頭髮與皮膚方面的細節。

由於在Google可以獲取到無窮的計算資源,如何擴大訓練的規模便成為該專案的另一個目標——因為即便採用這些小型的資料集,在單個CPU上完成訓練也要花上數週的時間。

非同步隨機梯度下降演算法(Asynchronous SGD)是最理想的分散式訓練方法。使用這種方法,你用N臺機器獨立來訓練同以個模型,但每個時間步長都要共享一次權重引數。權重引數被託管在一臺單獨的“引數伺服器”上,該伺服器在每個時間步長內都進行遠端過程呼叫(RPC),以獲得最新數值併傳送梯度更新。如果資料管道足夠好,你就可以線性增加模型每秒內的訓練步數,方法是增加機器——因為機器之間互不依賴。然而,當機器增加時,由於老機器更新了權重,新機器的權重會逐步過期或“落伍”。在分類網路中,這裡的問題不大,把訓練的規模擴增到幾十臺機器不難。但PixelCNN卻對過時的梯度極其敏感,在它的非同步隨機梯度下降演算法內增加機器幾乎沒有任何收益。

另一個方法,是用同步隨機梯度下降演算法(Synchronous SGD)。使用這一方法,機器在每個時間步長內都進行同步,且每臺機器的梯度都會被平均。它與隨機梯度下降演算法在數學上是相同的。更多的機器會增大批尺寸。但同步隨機梯度下降演算法(Sync SGD)允許各機器使用更小、更快的批尺寸,從而來增加每秒的步數(steps/sec)。

然而,同步隨機梯度下降演算法也有它自己的問題。首先,它需要大量的機器經常進行同步,這必然導致停機時間的增加。其次,除非將每臺機器的批尺寸設為1,否則它無法通過增加機器來增加每秒訓練的步數。最終,我發現簡單的設定是用一臺機器安裝8個GPU來使用同步隨機梯度下降演算法——但完成訓練仍需花上數天的時間。

採用大規模計算的另一個辦法,是進行規模更大的超引數搜尋。如何來確定所用的批尺寸?把它們全都試一遍。在找到論文中所用的配置前,我嘗試過數百種配置。

如何定量評估結果,則是另外一個難題。如何才能證明我們的影像比基準模型好?衡量超解析度質量的典型方法,是對比強化影像與原始影像的對應畫素點之間的距離(峰值訊雜比,PSNR)。雖說本模型輸出的臉部影像在質量上明顯更好,但在畫素對比上,平均看來它們還不如基準模型所輸出的模糊影像。我們嘗試用PixelCNN本身的相似度測量來證明我們的樣本比基準版本有著更高的概率值,但同樣失敗了。最後,我們把這項任務眾包給人類評估員——詢問他們哪些影像看上去更真實。這倒奏效了。

具體的結果請檢視這篇論文:超解析度的畫素遞迴

https://arxiv.org/abs/1702.00783

PixColor: 關於著色的嘗試

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

PixColor輸出的雙色模式

Slim的創造者Sergio Guadarrama一直在嘗試給影像著色。他跟我說過一個試驗:用分量介面(該介面中影像的灰度、顏色相互分離)獲取一張224×224×3的影像,將其顏色通道降至28×28×2的超低解析度,然後用雙線性插值法再把顏色通道放大,所得影像與顏色解析度很高的原始影像相比幾乎沒有差別。

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

圖3:你需要的只是一些顏色。頂行是原始彩色影像。中間行是降低取樣率後的真實色度影像,尺寸縮小至28畫素。底行是雙線性提高中間行的取樣率並結合原始灰度影像的結果。

這表明,把問題變成僅預測低解析度顏色,我們就可以簡化著色問題。我原本已準備好徹底放棄PixelCNN了,因為它顯然無法放大小影像,但用來生成28×28×2的影像還是很可行的。通過將顏色數值簡化為32個數字而非256,我們進一步簡化了著色問題。

Sergio構建了一個“改進的”網路,它能夠清理低解析度顏色的輸出,並將溢位邊界的顏色推回至正確位置——使用前饋式影像對影像卷積神經網路進行訓練,損失僅為L2。我們還用一個預訓練好的ResNet作為條件網路,用以消解額外的損耗項需求,畢竟在超解析度專案中我們已經用過這樣的損耗項。

使用這些方法後,無論是眾包評估還是用顏色直方圖相交評估,我們都能得出ImageNet上最好的結果。事實證明,經過正確訓練的PixelCNN可以很好地模擬影像統計資料,不發生任何模式崩潰。

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

圖7:實驗室顏色空間中的顏色通道的邊緣統計資料。左:每種方法的直方圖以藍色顯示,ImageNet的測試資料集直方圖以黑色顯示。右圖:顏色通道的直方圖相交。

由於模型為每個灰度輸入的可能著色宣告瞭一個概率分佈,我們可以對該分佈進行多次取樣,以獲取同一輸入的不同著色。下圖用結構相似度(SSIM) 很好地展示了分佈的多樣性:

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

圖8:為證明我們的模型可生成不同的樣本,我們用多尺度SSIM對比了同一輸入的兩種輸出。上圖顯示了ImageNet測試資料集的SSIM距離直方圖。圖中在多個SSIM間距上分別顯示了每對影像。SSIM值為1表示兩張影像完全相同。

該模型離遠未完美。ImageNet儘管龐大,但不能代表所有的影像。而該模型在處理非ImageNet影像時並不理想。我們發現,真實的黑白照片(不同於彩色轉化為灰度的黑白照)會得出不同的統計資料,並能出現很多彩色照片中所沒有的物體。比如,Model T汽車的彩色照片不多,ImageNet影像集中可能一張都沒有。採用更大的資料集和更好的資料擴增,也許能簡化這些問題。

想了解影像質量的話,可以來看看這些圖:

  • 處於我們模型處理中間階段的一小組非常難處理的影像

http://tinyclouds.org/residency/step1326412_t100/index.html

  • 用於我們模型的ImageNet隨機測試資料集影像

http://tinyclouds.org/residency/rld_28px3_t100_500_center_crop_224/

作為對比,下面是用其他演算法來處理同一ImageNet測試資料集的結果:

  • 給影像著色!

http://tinyclouds.org/residency/ltbc_500_center_crop_224/index.html

  • 彩色影像著色

http://tinyclouds.org/residency/cic_500_center_crop_224/index.html

  • 自動著色的學習表示

http://tinyclouds.org/residency/lrac_500_center_crop_224/index.html

最後,完整的細節都在我們的論文中: PixColor: Pixel Recursive Colorization

https://arxiv.org/abs/1705.07208

失敗與未報告的實驗

這一年期間,我曾短暫著迷過許多業餘的小專案,儘管它們都失敗了……接下來我會簡單來描述其中的幾個:

大數的素因數分解

素因數分解一向都是個大難題。但即便是如今,我們仍在不斷發現有關素數分解的新問題。如果為深度神經網路提供足夠的例項,它能不能找出一些新東西?Mohammad和我嘗試過兩種方法。他修改了Google機器翻譯的seq2seq神經模型,該模型把一個半素大數的整數序列作為輸入,並將其素因素預測為輸出。我則使用一個較為簡單的模型,它將定長整數作為輸入,並用幾個全連線層來預測輸入的分類:素數或合數。這兩種方法都只學到了最為明顯的規律(如果尾數為0,那它就不是素數!),我們只能拋棄這個想法。

Adversarial Dreaming

受Michael Gygli的專案啟發,我想探究一下鑑別器能否充當它自己的生成器。為此,我構建出一個簡單的二元分類卷積神經網路來判斷輸入的真假。為了生成影像,你需要給出噪點並讓它使用梯度來更新輸入(有時被稱為deep dreaming),令該網路把“真實”類別最大化。該模型通過交替生成“假”例項來進行訓練,而後跟典型的GAN鑑別器一樣,通過升級權重來區分真假例項。

我的想法是,鑑於更少的架構決策,該網路相比一般的GAN可能更容易訓練。事實上,它用MNIST確實可以工作。下圖中每一欄都在展示:不同的噪音影像被逐漸推向紅色MNIST數值的情形。

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

但我沒法讓它在CIFAR-10資料集上工作,並且它的實際意義也極為有限。這遺憾了,我相信 "Adversarial Dreaming" 會是一個很酷的論文題目。

使用PixelCNN來訓練生成器

PixelCNN生成樣本的時間過長,這讓我很沮喪。於是,我就想試試能不能用一個預訓練的PixelCNN訓練出前饋式影像對影像卷積神經網路(8x8至32x32尺寸的LSUN臥室圖片集)。我所設定的訓練方法是:在前饋式網路的輸出上進行自動迴歸。在PixelCNN下更新權重以便將概率最大化。它用這樣的線條生成了非常奇怪的影像:

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

對非同步隨機梯度下降演算法的修改探索

如前文所述,很多模型都不適用於非同步隨機梯度下降演算法。最近,一篇名為DCASGD論文提出了一種解決過時梯度問題的可能方法:在機器開始步進去應用它們權重的前,在權空間(weight space)使用差分向量。這種方法可大大減少每一個人的訓練時間。不幸的是,我沒能在TensorFlow上重複他們的結果,也無法實現我所想出的幾個類似想法。這裡可能有Bug。(如果想獲取我的實現方法,請通過內部渠道聯絡我)

想法和總結

聊了這麼多的技術,我大致做些總結,也算是比較深的心得體會吧。

作為軟體工程師,我在機器學習方面並沒有什麼經驗。但基於過去一年對深度學習的研究,我來分享一下在該領域的總體看法,及其同範圍更廣的軟體領域之間的關係。

我堅信,機器學習將改變所有行業,並最終改善每個人的生活,許多行業都會因機器學習所提供的智慧預測而受益。

對於我,我在這個專案中,最初的目標是,在不久的將來,所有人都可以看到查理·卓別林這類老電影的4K版。

不過,我確實發現,這一模型的構建、訓練和除錯都相當困難。當然,大部分的困難是由於我缺乏經驗,這也表明有效訓練這些模型是需要相當豐富的經驗的。

我的工作集中在機器學習最為容易的分支上:監督式學習。但即便有著完美的標註,模型開發仍十分困難。似乎,預測的維度越大,構建模型所花的時間就越長(例如:花大把的時間進行程式設計、除錯和訓練)。

因此,我推薦所有人在開始時都儘可能簡化和限制你的預測

舉一個我們在著色實驗中的例子:我們在開始時試圖讓模型預測整個的RGB影像,而非只預測顏色通道。我們認為,神經網路很容易就能處理好灰度圖(intensity image)並輸出出來,因為我們使用的是跳躍連線(skip connection)。只預測顏色通道依然能改進效能。

如果我以主觀、本能的方式使用“工作”來描述軟體:影像分類工作起來似乎很穩健。生成式模型幾乎很少能工作,人們也不太瞭解這種模型。GAN能輸出很好地影像,但是構建起來幾乎是不可能的——我的經驗是,對架構作出任何小改動都有可能使它無法工作。我聽說強化學習更加困難。但因經驗不足,我對遞迴神經網路不予置評。

但是,隨機梯度下降演算法工作起來是太過於穩定,即使是嚴重的數學錯誤也不會讓它徹底失敗,僅稍微有損於效能。

因為訓練模型經常需要花費很多天,這是一個非常緩慢的 修改—執行 迴圈。

測試文化尚未完全興起。訓練模型時我們需要更好的評斷方法,網路的多個組成部分需要維持特定的均值和變數,不能過度擺動或者留在範圍內。機器學習漏洞使系統的heisenbugs能特別輕鬆地通過測試。

並行化能帶來的好處很有限。增加計算機數後,大規模的超引數搜尋會變得更加容易,但是理想情況下,我們會設計不用仔細除錯也能正常工作的模型。(實際上,我懷疑超引數搜尋能力有限的研究人員將不得不設計出更好的模型,因此他們設計出的模型更加穩定)。

不幸的是,對於很多模型而言,非同步隨機梯度下降演算法並沒有什麼用處——更加精確的梯度通常用處不大。這就是為什麼 DCASGD 的研究方向很重要的原因。

從軟體維護的角度看,關於如何組織機器學習專案大家都鮮有共識。

就像是Rail出現之前的網站:一群隨機PHP指令碼,商業邏輯和標記符號亂混一氣。在TensorFlow專案中,資料管道、數學和超引數/配置管理無組織地混為一團。我認為我們還未發現精美的結構/組織。(或者說是還未重新發現,就像DHH重新發現並普及 MVC那樣。)我的專案結構一直在進步,但是我不會認為它是精美的。

框架會繼續快速進化。我開始使用的是Caffe,不得不稱讚TensorFlow帶來的好處。現在,PyTorch 和 Chainer之類的專案使用動態計算圖形取悅客戶。漫長的修改—行迴圈是開發更好模型的主要阻礙——我懷疑能優先實現快速啟動和快速評估的框架最終會取得成功。儘管擁有TensorBoard和iPython之類的有用工具,但是檢查模型在訓練期間的活動仍然很難。

論文中的訊雜比很低。但是需要改進的空間還很大。人們通常不會坦率承認他們模型的失敗之處,因為學術會議更看重的是準確度而不是透明度。我希望學術會議能接受提交部落格文章,並要求開源實現)。Distill 在這方面的努力值得稱讚。

對機器學習而言,這是一個令人激動的時代。各個層面上有大量工作需要完成:從理論端到框架端,還有很多值得改進的空間。它幾乎和因特網的誕生一樣令人激動。加入這場技術革命吧!

程式設計師想搞機器學習?看看Nodejs之父這一年摸爬滾打的心路歷程

這是你的機器學習系統嗎?

是啊!將資料倒到這一大堆線性代數中,然後在另外一端收集答案。

如果答案是錯誤的呢?

只管攪動這堆線性代數,直到結果開始看起來正確為止。


相關文章