深度學習二:Neural art:用機器模仿梵高

http://phunter.farbox.com/post/mxnet-tutorial2 html

題注:原本這是第三集的內容,可是Eric Xie 勤勞又機智的修復了mxnet和cuDNN的協做問題,我就把這篇看成一個卷積網絡ConvNet(CNN)神奇而有趣的例子,寓教於樂給你們提起學習興趣,原計劃的CNN教學順延到下一集。 node

Neural art:用機器模仿梵高

Neural art是個讓機器模仿已有畫做的繪畫風格來把一張照片從新繪畫的算法,好比給一張貓的照片和一張梵高的自畫像,咱們就能夠獲得用梵高風格畫出來的貓,好比這個樣子(圖二爲梵高在1889年的自畫像,引用自wikipedia):

Neural art算法來自於這篇論文 「A Neural Algorithm of Artistic Style」 by Leon A. Gatys, Alexander S. Ecker, and Matthias Bethge,連接在http://arxiv.org/abs/1508.06576 有興趣的觀衆朋友們能夠閱讀。它的基本想法是,利用一個多層的卷積網絡(CNN)抽象出給定繪畫做品裏一些高級的隱藏特徵用來模仿繪畫風格,並把這個繪畫風格應用到一個新的圖片上。這類的圖像生成模型是深度學習的一個方向,好比谷歌的Inception把一個羊的圖片和一個雲的圖片生成羊形狀的雲之類的「迷幻類」圖像也是相似模型的一種。Facebook也有相似的生成模型,他們基於這篇http://arxiv.org/abs/1406.2661由DMLC做者@antinucleon等人合做的文章。
Neural art算法模型有多種實現方式,好比這裏這裏是兩個Lua/Torch版的實現,這片論文的gitxiv下面也包含了五花八門的各類實現,他們都是實現論文中的VGG模型並用caffe描述,MXnet在你們的提議下固然也要實現一下了。按照慣例,mxnet這麼有意思的深度學習工具,咱們去幫它的github加個星,你們說好很差啊?傳送門:https://github.com/dmlc/mxnet python

MXnet的Neural art樣例

MXnet的neural art範例在mxnet/example/neural-style/目錄下。由於這個例子須要大量的計算,推薦安裝GPU版的mxnet。安裝mxnet GPU版的教程參見前一集http://phunter.farbox.com/post/mxnet-tutorial1 這裏就再也不重複。固然了,mxnet的CPU和GPU無縫鏈接,若是沒有GPU可使用CPU版本,只是須要每張圖耐心等待約40-50分鐘。
選擇安裝:mxnet可選使用cuDNN加速。對Neural art的例子,cuDNN v3和v4都可運行,v4在個人GTX 960上比v3稍微快約2-3秒。你們能夠到這裏https://developer.nvidia.com/cudnn 申請開發者項目,若是批准經過能夠下載安裝cuDNN工具包,具體請參照nVidia官方教程或者簡單的執行這幾步(參考來源Install Caffe on EC2 from scratch (Ubuntu, CUDA 7, cuDNN))):linux

tar -zxf cudnn-7.0-linux-x64-v3.0-prod.tgz
cd cuda
sudo cp lib64/* /usr/local/cuda/lib64/
sudo cp include/cudnn.h /usr/local/cuda/include/

若是以前沒有編譯安裝cuDNN版的mxnet,請在make/config.mk裏把USE_CUDNN = 0修改成1從新編譯,並更新安裝對應的Python包。
若是你沒有安裝mxnet GPU版本的條件,也能夠訪問如下這些網站或app玩一下Neural art。這個算法須要大量的GPU計算,如下這些免費或收費的實現都須要排隊。 git

  1. Deepart:網址 https://deepart.io/ 用戶能夠免費提交,平均等待時間爲1周左右,若是想插隊到24小時以內,能夠捐款給網站。
  2. Pikazo App:網址 http://www.pikazoapp.com/ 它至關於把deepart這個網站作成app,須要收費$2.99,也須要排隊。
  3. AI Painter: 網址 https://www.instapainting.com/ai-painter 這是instapainting的一個業務,免費,也是須要排隊。

觀衆朋友們若是正好有一個裝了GPU版mxnet的機器的話,那咱們就開始用mxnet本身動手豐衣足食,還能夠幫朋友們生成有藝術感的微博頭像喲。如下例子中我用我妹 @dudulee的浪裏格朗 家的美貓「破狗」的照片爲例講解具體生成藝術圖的步驟。若是觀衆朋友們想看更多破狗和她妹妹「泥巴」的平常,能夠關注嘟嘟家兩隻貓「破狗」和「泥巴」的微博@POGOGO-NIBABA,內有大量圖片可供深度學習實驗。 github

簡要步驟和參數調整

mxnet使用的是論文中描述的VGG模型,在第一次使用的時候須要執行download.sh下載該模型,mxnet的模型版本佔約幾十MB的空間。下載模型完畢以後,能夠把須要繪畫的原始圖片和模仿的圖片放到input目錄裏,好比說破狗的照片和梵高的圖像,而後執行:web

python run.py --content-image input/pogo.jpg --style-image input/vangogh.jpg

耐心等待1-2分鐘,就能夠看到結果保存在output目錄裏,好比是這樣的:
算法

若是給另一張現代藝術油畫 'Blue Horse' Modern Equine Art Contemporary Horse Daily Oil Painting by Texas Artist Laurie Pace (連接https://www.pinterest.com/pin/407223991276827181/) 替代梵高的畫做讓機器學習風格,破狗能夠畫成這樣的:ubuntu

python run.py --content-image input/pogo.jpg --style-image input/blue_horse.jpg

run.py裏有一些能夠調整的參數,若是想調試輸出效果能夠按照以下解釋調整: 網絡

  • --model 指定模型。例子裏暫時只有vgg這一個模型,之後可能添加前面提到的inception等其餘模型。暫時先不用改。
  • --content-image 內容圖片,好比上面的「破狗」的照片
  • --style-image 輸入的繪畫原做的路徑,好比上面的的「梵高自畫像」。
  • --stop-eps 模型裏用eps值表明兩幅圖的風格類似度,在訓練的過程裏會看到這個值逐漸收斂,值越小類似度越高。stop-eps參數指定的是收斂的終止值,通常越小就表明畫的越像,但若是過小了會須要不少的計算時間來收斂,默認0.005已經能夠獲得不錯的效果,可適當減少到0.004等。
  • --content-weight --style-weight 內容圖片和繪畫原做的相對權值,默認是10:1,若是發現繪畫風格過於強烈塗抹一片,可適當修改成20:1或者30:1,反之改小。
  • --max-num-epochs 最大收斂步數,默認是1000步。不過通常畫做在200步左右就能找到差很少合適的eps風格類似值,這個最大收斂步數不須要修改。
  • --max-long-edge 長邊最大邊長。程序會自動把輸入圖片按照這個值等比例縮放,好比上面的圖就是縮放到高度爲512像素。程序運行時間及內存消耗約和圖片面積成正比,由於卷積網絡的計算量每一個像素相關,700像素的圖片差很少比500像素的圖片多一倍內存和運行時間。在接下來的對比測試裏面能夠看到,512像素的圖差很少須要1.4GB顯存,適合2G顯存的顯卡好比nvidia顯卡的macbook pro等娛樂一下就足夠了,4GB的顯卡差很少最高能夠處理到850-900像素的圖片,要想上1080p就得有Titan X的12GB了。一樣的,計算時間也會相應拉長,它也和顯卡的CUDA核心數約成反比。如今你基本上明白了爲何上面提到的免費版都須要排隊幾個小時到幾周不等了。
  • --lr logistic regression的梯度降低(SGD)學習率,用來尋找既在內容上知足「像破狗」又在風格上「像梵高」的生成圖像。較大的eta收斂較快,節省計算時間但會在最小值附近跳躍。默認值0.1,能夠調整到0.2和0.3均可以。
  • --gpu 使用第幾個GPU,默認是0號GPU,適合只有一塊顯卡的用戶(好比我家裏的機器)。若是土豪你有4塊顯卡能夠並行使用,只須要指定爲--gpu 0,1,2,3就能夠了,8塊顯卡以此類推,mxnet支持多塊顯卡並行並且顯存分配效率很高。若是沒有GPU並能忍耐40分鐘左右算一張圖,--gpu -1也能夠指定爲純CPU計算。
  • --output 輸出文件名。
  • --save-epochs 是否保存中間結果,默認每50步保存一下結果。
  • -remove-noise 降噪參數,默認0.2,能夠下降一些爲0.15,這就是高斯降噪的半徑。程序在學習模仿畫做的過程裏會使用兩個白噪聲圖片逼近風格圖和內容圖,在最終生成的圖片裏面可能殘留一些沒必要要的噪聲點,程序裏面能夠降噪處理。

可能遇到的問題

內存不足

運行時消耗的顯存和圖像的面積成正比,若是圖像的縮放目標邊長太大,極可能會顯存不足,mxnet的提示錯誤信息以下:

terminate called after throwing an instance of 'dmlc::Error' what():  [18:23:33] src/engine/./threaded_engine.h:295: [18:23:33] src/storage/./gpu_device_storage.h:39: Check failed: e == cudaSuccess || e == cudaErrorCudartUnloading CUDA: out of memory

對於512邊長的圖片,mxnet須要1.4G顯存,通常的nVidia版的macbook pro或者其餘有2G顯存的機器能夠跑起來;對於850邊長的圖片,mxnet須要3.7GB顯存,通常4G顯存的機器能夠跑起來。這裏有兩點提醒注意:

  1. 若是你使用的是GTX 970 4G版,它的有效顯存使用最高只到3.5GB,超過這個就會有莫名的錯誤。具體參考這裏
  2. 若是顯存正好差一點能夠關掉系統佔用的顯存,好比在ubuntu下面能夠Alt-Ctrl-F1關掉系統圖形界面,節省幾十MB的顯存。
工做空間不足

若是圖片邊長大於600到700,原始例子裏默認的workspace可能不夠,會出現以下的錯誤信息:

terminate called after throwing an instance of 'dmlc::Error' what():  [18:22:39] src/engine/./threaded_engine.h:295: [18:22:39] src/operator/./convolution-inl.h:256: Check failed: (param_.workspace) >= (required_size)
Minimum workspace size: 1386112000 Bytes
Given: 1073741824 Bytes

Mxnet須要工做緩衝空間,這個變量能夠在模型的定義裏面設置。找到model_vgg19.py文件,把裏面的全部workspace=1024改爲workspace=2048就能夠了。

速度測試

爲了體現MXnet在速度和高效的內存使用上的優點,在這裏咱們選擇這這個Lua (Torch 7)的實現 https://github.com/jcjohnson/neural-style,用同一組圖「破狗」+「梵高」對比測試mxnet和它的性能。實驗條件,單塊GTX 960 4GB,4核AMD CPU,16GB內存。值得提醒的是,Lua版使用的原版的VGG模型在第一次運行的時候也須要下載,佔用大約1GB多的空間。

512像素邊長的圖片

內存消耗
運行時間

MXnet(不使用cuDNN)
1440MB
117s

MXnet(使用cuDNN)
1209MB
89s

Lua Torch 7
2809MB
225s

850像素邊長的圖片

Lua/Torch 7版對於850px邊長沒法測試,等個人Titan X到貨再來一戰。

內存消耗
運行時間

MXnet(不使用cuDNN)
3670MB
350s

MXnet(使用cuDNN)
2986MB
320s

Lua Torch 7
顯存不足
顯存不足

MXnet黑科技(12.21.2015更新)

在reddit上和網友們討論這篇blog後,Lua版的做者回帖表示不服併成功的把內存消耗下降到1.5GB,具體討論請參考這一條reddit的討論。我和Mxnet的各位做者討論以後,拿出MXnet最新壓縮內存使用的黑科技MXNET_BACKWARD_DO_MIRROR迎戰,相關技術細節請參看這條github issue。想使用黑科技版MXnet請更新M到github的最新版本並從新編譯。在執行代碼前加入MXNET_BACKWARD_DO_MIRROR=1便可調用黑科技,好比:

MXNET_BACKWARD_DO_MIRROR=1 python run.py --content-image input/pogo.jpg --style-image input/vangogh.jpg

512像素對比測試

內存消耗
運行時間

MXnet(不使用cuDNN)
1440MB
117s

MXnet(使用cuDNN)
1209MB
89s

MXnet(使用cuDNN和內存壓縮)
1116MB
92s

850像素對比測試

內存消耗
運行時間

MXnet(不使用cuDNN)
3670MB
350s

MXnet(使用cuDNN)
2986MB
320s

MXnet(使用cuDNN和內存壓縮)
2727MB
332s

咱們能夠對比看到使用了壓縮內存選項以後,MXnet損失了一點運行時間,可是內存獲得10%的壓縮,如今4GB內存最高能夠支持1024px的圖片,消耗內存爲3855MB。值得指出的是,Mxnet裏gram matrix的實現暫時沒有用到符號計算的功能(做者告訴我他太忙了以致於偷懶了),等更新了符號計算版本以後會再下降一些內存使用。
簡單來講,MXnet節省一倍的顯存,速度也接近翻倍。該Lua版本的官方樣例使用Titan X(12GB)生成邊長512像素的圖,須要約一到兩分鐘,而mxnet只須要一塊 GTX 960約兩分鐘,對比Titan X(1000$)和GTX 960 (200$) 的價格差距,mxnet差很少省了4-5倍的錢。關於運行速度對比值得提醒的是,Lua版本的速度稍慢主要由於它是用L-BFGS來收斂,收斂效果好可是速度較低,而mxnet使用更快的SGD,它有速度優點可是對不一樣的輸入參數可能須要手工微調,在這一點上不能簡單的說MXnet必定老是快兩倍。
MXnet能達到這樣的速度和高效內存使用,得益於他所在的DMLC組件的高效設計和實現。關於DMLC和MXnet的高效內存設計方法,有興趣的觀衆朋友們能夠前往這裏做深刻閱讀了解DMLC的黑科技。
到這裏,觀衆朋友們應該能夠愉快的玩耍起來Neural Art給本身和朋友們生成藝術圖片了。接下來的部分討論一下機器爲何能學習模仿到繪畫風格。

機器怎麼模仿繪畫風格

這個問題的答案在原論文裏也語焉不詳,做者也沒有想解釋清楚。如下的討論均按我我的根據原文以及reddit和知乎上的相關討論在這裏概述一下,更多討論詳情請參閱:reddit知乎 這裏一併感謝上述連接裏的做者和評論者。

量化表示「繪畫風格」

「繪畫風格」是一個抽象定型的詞語,它可能和圖像的某種高階統計量相關,但不一樣的繪畫風格有不一樣的表示,對於一個沒有具體定義風格的通常性問題,它很難用人工設計算法去完成。幸運的是,咱們知道卷積網絡CNN能夠經過多層卷積提取物體的抽象特徵完成物體識別(請參考Yann Lecun的深度學習教程),這一點「提取抽象特性」的能力被做者借用來描述「風格」。也就是說,通過多層CNN抽象以後的圖片丟棄了像素級的特徵,而保留了高級的繪畫風格。下圖引用自原論文圖1。在文章裏,做者定義了一個5層的CNN網絡,梵高的星空在經過第一二三層的時候保留了一些原圖的細節,可是在第四第五層的時候,就變成了「看起來是梵高星空的樣子」這樣的抽象特徵:


這時候做者機智的想到了,若是把一張梵高一張其餘照片同時都放到這個CNN網絡裏,通過合適的調整讓第二張照片在第四五層接近梵高,而第一二三層保持和原來差很少,那就能夠模仿梵高了!細節上,做者爲了沿用了CNN的特徵抽象能力使用了CNN做物體識別的VGG模型。關於mxnet實現CNN做物體類別識別的相關例子,我在下一集會講到。

學習風格並生成圖像

因而讓機器模仿繪畫風格並生成圖片成了一個優化問題:生成的圖像要像原內容圖,好比我給一張貓的圖片最終仍是要像貓;生成的圖像要像是由風格圖畫的,好比我給了個梵高的圖,我生成的貓的圖片要看起來有梵高的風格。也就是說要找到這樣一箇中間結果,它的內容表示(第一二三層CNN)接近於破狗,它的風格的表示(第四第五層CNN)接近於梵高。在文章裏,做者用一個白噪聲圖片經過梯度降低生成一個接近內容圖的圖片,以及另外一個白噪聲圖片生成一個接近繪畫圖風格的圖片,並定義了神奇的描述紋理的gram matrix定義了這兩個圖的損失函數並加權平均看成優化目標函數,在mxnet的實現裏經過梯度降低(SGD)完成收斂找到這樣一個內容和風格都搭配中間結果。舉例來講,「破狗」和「梵高自畫像」的生成過程的200多步的循環裏,圖像的變化依次以下圖所示:

咱們能夠看到,在剛開始的幾十步裏,圖片更像是原圖和繪畫的簡單紋理的疊加,而隨着循環步數增長,程序慢慢學習到了配色和筆觸的風格,在150步左右基本成型,最終把破狗的照片繪畫成梵高的風格。

模仿風格是否是隻有這一個辦法?

事實上不是的,不少計算圖形學的論文已經針對各類方向作出了一些成果,只是這篇文章利用了深度學習和CNN的方法,其餘相似學習風格的論文能夠參考相關閱讀:

  • "A Parametric Texture Model Based on Joint Statistics of Complex Wavelet Coefficient"http://www.cns.nyu.edu/pub/lcv/portilla99-reprint.pdf 這片文章用小波變換的方式提取了圖片紋理「風格」所對應的二階統計量,和本文提到的論文想法是一致的。
  • 「Style Transfer for Headshot Portraits」 https://people.csail.mit.edu/yichangshih/portrait_web/ 這篇文章針對頭像照片的風格作到了很快的風格學習,而且能夠實時轉換視頻,對於這個有嚴格限制的問題,它的速度比Neural art高到不知道哪裏去了。

結語

做爲深度學習和CNN的例子,Neural art確實很好玩,觀衆朋友們能夠本身用MXnet給本身和朋友們生成有意思的藝術圖片,記得發到微博上加#mxnet#話題分享。值得提醒的是,若是原圖是半身人像類,建議也使用一些人像的畫做來學習風格,好比「破狗」+「梵高」的組合;相對應的,風景圖片最好用風景畫做風格學習。由於風景的表現重點和人像不一樣,強行把風格畫到人像的照片上並不適合,它會看起來像是兩幅圖簡單疊加,這個即便是人類畫家也很差畫在一塊兒。你們好好玩,下一集會詳細講解卷積網絡CNN做物體分類識別,也就是教機器如何識別貓和狗。

相關文章
相關標籤/搜索