從去年 Tony Beltramelli 發表 pix2code 論文和 Airbnb 推出 sketch2code 後,這一領域才開始嶄露頭角。
更多幹貨內容請關注微信公眾號“AI 前線”,(ID:ai-front)
目前,自動化前端開發的最大障礙是計算能力。不過,我們可以使用當前的深度學習演算法和人造的訓練資料來探索人工前端自動化。
在這篇文章中,我們將教會一個神經網路如何基於一張設計原型圖片來編寫基本的 HTML 和 CSS 程式碼。以下是該過程的簡要概述:
1)為神經網路提供的設計圖
2)神經網路將圖片轉化成 HTML 程式碼
3)渲染輸出
我們將通過三次迭代來構建神經網路。
第一次迭代得到的是最基礎的版本,先了解圖樣中的活動部件。第二次迭代得到的是 HTML 程式碼,將著重於自動化所有步驟,並解釋神經網路層。最後一次迭代得到的是 bootstrap 版本,我們將建立一個模型可用於泛化和探索 LSTM 層。
所有的程式碼都放在了 Github 和 FloydHub 上。
這些模型是基於 Beltramelli 的 pix2code 論文和 Jason Brownlee 的影象自然語言描述教程而構建的,程式碼使用 Python 和 Keras(一個基於 Tensorflow 的框架)編寫。
如果你是剛接觸深度學習的新手,建議你先大概瞭解下 Python,反向傳播和卷積神經網路。
讓我們再簡明扼要地複述下我們的目標。我們想構建一個神經網路,生成與螢幕截圖對應的 HTML/CSS 程式碼。
在訓練神經網路時,你可以給它幾個截圖和與之對應的 HTML 程式碼。
在學習過程中,它會逐一預測所有匹配的 HTML 標籤。在預測下一個標籤時,它會接收到螢幕截圖以及在那一個點上所有匹配的標籤。
這個 Google Sheet 包含了一個簡單的訓練資料樣本。
建立一個能夠逐字預測的模型是現在最常見的方式,也是我們在這個教程中將會使用的方式,雖然還有其他的方式。
請注意,對於每次預測,神經網路得到的都是相同的截圖。也就是說,如果要預測 20 個單詞的話,它將會收到相同的圖樣 20 次。在現在這個階段,請不要擔心神經網路的工作原理,而是著重關注神經網路的輸入輸出。
讓我們看下之前的標籤。假設我們要訓練網路來預測“I can code”這句話。當它收到“I”的時候,它能預測到“can”。下一次,它將收到“I can”,並且預測到“code”。每次它會收到之前所有的單詞,並且只需預測接下來的一個單詞。
神經網路從資料中建立出不同的特徵,用於連線輸入和輸出資料,建立模型,以便理解每張截圖中所包含的內容和 HTML 語法,這就得到了預測下一個標籤所需要的知識。
在將訓練過的模型用在現實當中時,情況跟訓練模型時差不多。每次使用相同的螢幕截圖逐一生成文字。不同的是,現在它不會直接收到正確的 HTML 標籤,而是會收到它迄今為止已經生成的標籤,然後去預測下一個標籤。整個預測的過程會從“起始標籤”開始,在預測到“結束標籤”或達到最大限度時終止。
讓我們開始構建一個 Hello World 的版本。我們將為神經網路提供一個顯示有“Hello World!”的網頁截圖,並且訓練它生成對應的標籤。
首先,神經網路將圖樣對映成一組畫素值列表。每個畫素點有 RGB 三個通道,每個通道的值都在 0-255 之間。
為了讓神經網路理解這些標記,我使用了獨熱編碼(one hot encoding)。因此“I can code”這一句就可以對映成:
上圖中包含了開始和結束標籤。這些標籤控制著神經網路預測的開始和結束時間。
對於輸入資料,我們將用不同的句子,從第一個單詞開始,然後逐步新增每個單詞。輸出的資料總是一個單詞。
句子遵循與單詞相同的邏輯。它們也需要相同的輸入長度,但是在這裡我們限制的是句子最大的長度,而不是單詞的數量。如果句子比最大長度短,則用空詞填充它,空詞完全由零組成。
正如所看到的,單詞都是從右向左列印出來的。這樣的話,每次訓練都會迫使每個單詞改變自己的位置,這樣模型就可以記住單詞順序而不是每個單詞的位置。
在下圖中有四次預測,每一行表示一次預測。從左邊起,是以 RGB 通道表示的影象:紅色、綠色和藍色,還有之前提到過的單詞。括號之外,是一個接一個的預測,最後以紅色方塊結束。
在 Hello World 版本中,我們用了三個記號:“start”、“Hello World!”和“end”。記號可以是任何東西,它可以是一個字元、單詞或句子。雖然使用字元記號需要更少的詞彙量,但會限制神經網路。單詞記號往往有最好的表現。
這裡我們做出了預測:
10 epochs:start start start
100 epochs: start <HTML><center><H1>Hello World!</H1></center></HTML> <HTML><center><H1>Hello World!</H1></center></HTML>
300 epochs: start <HTML><center><H1>Hello World!</H1></center></HTML> end
在收集資料之前構建了第一個版本。 在這個專案的早期,我想辦法從 Geocities 網站獲得了一箇舊的存檔副本,其中包含 3800 萬多個網站。不過,當時我被資料衝昏了頭腦,卻忽視了縮減 100K 詞彙所需要的巨大工作量。
處理 TB 級的資料需要很好的硬體或者極大的耐心。 在用我的 Mac 電腦執行出現了幾次問題以後,最後選擇了功能強大的遠端伺服器。為了保持工作的順暢,預計我將會租用包含 8 核 CPU 和一個 1GPS 網路連線的遠端測試機組。
在我明白輸入和輸出之前,很多東西都說不通。 輸入 X 是螢幕截圖和之前已經預測的標籤,輸出 Y 是下一個要預測標籤。當我明白這個以後,理解它們之間的關係就更容易了,而且構建不同的構架也更容易了。
小心兔子洞陷阱。 因為這個專案跟深度學習的很多領域都有交叉,在研究的過程中我好多次都陷入對其他領域的研究中。我花了一週的時間從頭開始編寫 RNN,過度著迷於向量空間,又被其他的實現所迷惑。
圖片到編碼網路其實就是 Image Caption 模型。 但即使我已經意識到了這點,我仍然忽略了很多有關 Image Caption 的論文,只是因為覺得它們沒有那麼酷。當我發現到這點後,我加快了對問題的瞭解。
Floydhub 是一個深度學習訓練平臺。在我剛開始接觸深度學習時,才知道有這個平臺。從那以後,我都在用它來訓練和管理深度學習試驗。你可以在 10 分鐘內安裝它並且執行你的第一個模型。這是在雲端 GPU 上訓練模型的最佳選擇。
如果你是剛接觸 Floydhub,推薦你去看下他們的 2 分鐘安裝教程和我的 5 分鐘概覽教程。
複製倉庫:
登入並啟動 FloydHub 命令列工具:
在 FloydHub 的雲 GPU 機器上執行 Jupyter notebook:
所有的 notebook 都在 FloydHub 目錄中準備好了。執行起來之後,你可以在這裡找到第一個 notebook:floydhub/Helloworld/helloworld.ipynb。
如果想要更多詳細的指導和標記說明,請參閱我早期寫的文章。
在這個版本中,我們將會自動化 Hello world 中的很多步驟。這一章節將主要關注如何建立一個可伸縮的實現和神經網路中的動態部分。
雖然這個版本還無法基於隨機網站來預測 HTML,但它仍然很適合用於對問題的動態部分進行探索。
下圖所示的是構架元件展開後的樣子。
主要有兩個部分,編碼器和解碼器。編碼器用於建立影象特徵和標籤特徵。特徵是由網路建立用來連線圖樣和標籤的基本構建塊。在編碼的最後階段,我們將影象特徵和前一個標籤中的單詞關聯起來。
然後解碼器通過圖樣和標籤特徵的組合來建立下一個標籤特徵,而這個特徵則會通過全連線神經網路來預測下一個標籤。
因為我們需要為每一個單詞插入一個截圖,這就成了我們訓練網路時的一個瓶頸(例子)。因此,我們沒有直接使用圖片,而是將生成標籤所需要的資訊提取出來。
之後,我們用一個預先在 ImageNet 上預先訓練的卷積神經網路,將這些提取出來的資訊編碼到圖片特徵中。
在最終分類之前,我們將特徵從層中提取出來。
最終,我們得到了 1536 張 8×8 畫素的影象作為特徵圖。儘管這些特徵很難被人理解,但是神經網路可以從這些特徵中抽取出物件和元素的位置。
在 Hello World 的版本中,我們使用了獨熱編碼來代表標籤。而在這個版本中,我們將在輸入中使用詞向量(word embedding),並繼續使用獨熱編碼表示輸出。
在保持每個句子的構造方式不變的情況下,改變對映記號的方法。獨熱編碼將每個單詞視為一個獨立的單元。但在這裡,我們將輸入資料中的每個單詞轉換為數值列表。這些數值代表了不同標籤之間的關係。
詞向量的維度是 8,但受詞彙量大小的影響,維度經常會在 50 到 500 之間變化。
每個單詞的 8 個數字類似於一般神經網路中的權重,用於對映不同單詞之間的關係。
神經網路可以用這些特徵來連線輸入資料與輸出資料。現在,先不要關心它們是什麼,我們將在下一節深入探討這個問題。
我們把詞向量送到 LSTM 中執行,之後會返回一系列的標籤特徵。這些標籤特徵然後會被送到 TimeDistributed 密集層執行。
在處理詞向量的同時,還會進行另一個處理。影象特徵首先會被扁平化,所有的數值會被轉換成一個數字列表。然後我們在這個層上應用一個密集層來抽取高階特徵,隨後這些影象特徵會被連線到標籤特徵。
這可能有點難以理解,所以讓我們把處理的過程拆開來看。
我們先將詞向量送到 LSTM 層中執行。如下圖所示,所有的句子都會被填充到三個記號的最大長度。
為了混合訊號並找到更高階別的模式,我們會用 TimeDistributed 密集層應用在標籤特徵上。TimeDistributed 密集層與一般的密集層相同,只不過具有多個輸入和輸出。
與此同時,我們會準備影象。我們整理了所有迷你影象特徵,然後將它們轉換成一組列表。其中的資訊沒有變,只是組織方式變了。
跟之前提過的一樣,為了混合訊號和提取更高階的概念,我們應用了一個密集層。 而且由於我們只需要處理一個輸入值,所以我們可以用一個常規密集層。其後,為了將影象特徵連線到標籤特徵,我們複製了影象特徵。
在這種情況下,我們就有三個標籤特徵。因此,我們得到了相同數量的影象特徵和標籤特徵。
所有的句子都經過填充,以便建立三個標籤特徵。由於我們已經預處理過了影象特徵,現在我們可以為每個標籤特徵新增一個影象特徵。
在將每個影象特徵新增到對應的標籤特徵之後,我們最終得到了三組影象標籤特徵組合。之後,我們將它們作為解碼器的輸入。
這裡,我們使用影象標籤特徵組合來預測下一個標籤。
在下面的例子中,我們使用三個影象標籤特徵組合來輸出下一個標籤特徵。
請注意,這裡 LSTM 層的 sequence 被設定為 false。由此,LSTM 層返回的是一個預測的特徵,而不是輸入序列的長度。在我們的例子中,這將是下一個標籤的特徵,包含了最終預測所需的資訊。
最終的預測
密集層像傳統前饋神經網路那樣,將下一個標籤特徵中的 512 個值與 4 個最終預測連線起來。假設我們的詞彙表中有四個詞:start、hello、world 和 end。
詞彙預測可以是 [0.1,0.1,0.1,0.7]。密集層中的 softmax 啟用函式分佈概率是 0 到 1,所有預測的總和等於 1。在這種情況下,它預測第 4 個單詞會是下一個標籤。 然後,將獨熱編碼 [0,0,0,1] 轉換為對映值,比如“end”。
這裡有原始網站供參考。
對我來說,LSTM 比 CNN 更難理解。當我展開所有的 LSTM 後,它們變得更容易理解。Fast.ai 在 RNN 上的視訊非常有用。另外,在嘗試瞭解特徵的原理之前,請先關注輸入特徵和輸出特徵本身。從頭開始構建詞彙表比縮減巨大的詞彙表要容易的多。包括字型、div 標籤大小、hex 顏色值、變數名稱和普通的單詞。大多數庫被建立來解析文字檔案而不是程式碼。在文件中,所有內容都由空格分隔,但在程式碼中,則需要使用自定義的解析方式。可以使用在 ImageNet 上訓練的模型來提取特徵。這可能看起來違反直覺,因為 ImageNet 幾乎沒有 Web 影象。然而,與從頭開始訓練的 pix2code 模型相比,這樣做的損失要高出 30%。當然,我也對使用基於網頁截圖的預訓練的 inception-resnet 型別的模型很感興趣。
在我們的最終版本中,我們將使用 pix2code 論文中生成的 Bootstrap 網站的一個資料集。通過使用 Twitter 的 Bootstrap,我們可以將 HTML 和 CSS 相結合,並且縮減詞彙表的大小。
我們將確保它能夠為之前沒有看過的截圖生成標籤,還將深入研究它是如何建立對螢幕截圖和標籤的認知的。
我們將使用 17 個簡化過的記號,然後將這些記號轉成 HTML 和 CSS,而不是在 Bootstrap 標籤上進行訓練。這個資料集包括 1500 個測試截圖和 250 幅驗證影象。每個截圖平均有 65 個記號,總共將生成 96925 個訓練樣本。
通過對 pix2code 論文中的模型做了一些調整,該模型可以以 97%的準確度預測網頁元件(BLEU 4-ngram 貪心搜尋,稍後會介紹更多)。
一種端到端的方法
在 Image Caption 模型中,從預訓練好的模型中提取特徵的效果很好。但經過幾次實驗後,我發現 pix2code 的端到端方法的效果更好。預訓練的模型尚未用網頁資料訓練過,只是用在分類上。
在這個模型中,我們用輕量級的卷積神經網路替換了預訓練好的影象特徵。但是我們沒有使用 max-pooling 來增加資訊密度,而是增加了步幅,用以維護元素的位置和顏色。
這裡可以使用卷積神經網路(CNN)和遞迴神經網路(RNN)這兩種核心模型。最常見的 RNN 是長短期記憶(LSTM)網路,也是我將要講的。
我在之前的文章中已經介紹過很多很棒的 CNN 教程了,所以這裡我就只重點介紹下 LSTM。
LSTM 的難點之一是時間步的概念。原始神經網路可以被認為有兩個時間步。如果你給它“hello”,它會預測到“world”。但是,想要預測更多的時間步是很困難的。在下面的例子中,輸入四個時間步,每個單詞對應一個。
LSTM 適用於含有時間步的輸入,是一個適合有序資訊的神經網路。如果你展開我們的模型,就會看到像下圖所示的那樣。對於每個向下遞推的步驟,你需要保持同樣的權重。對於舊的輸出和新的輸出,你可以分別設定一套權重。
將加權後的輸入和輸出用啟用函式連線在一起,它就是對應時間步的輸出。由於我們重複使用這些權重,它們將從一些輸入中提取資訊,並建立起有關序列的知識。
以下是 LSTM 中每個時間步的簡化版本。
為了理解這個邏輯,我建議你參考 Andrew Trask 的精彩教程,自己從頭開始構建一個 RNN。
每個 LSTM 層的單元(unit)數量決定了它的記憶能力,以及每個輸出特徵的大小。需要再次指出的是,一個特徵是用於在層與層之間傳輸資訊的一長串數字。
LSTM 層中的每個單元會學習跟蹤語法的不同方面。下面是對一個單位跟蹤原始 div 資訊的視覺化結果,是我們用來訓練 Bootstrap 模型的簡化標籤。
每個 LSTM 單元會維護一個細胞狀態(cell state)。把細胞狀態想象成記憶,而權重和啟用函式用來以不同的方式修改狀態。這使得 LSTM 層能夠微調每個輸入要保留和丟棄哪些資訊。
除了每個輸入傳遞輸出特徵之外,LSTM 層還會傳遞細胞狀態,其中每個單元都分別對應一個值。為了理解 LSTM 中的元件是如何相互作用的,我推薦 Colah 的教程、Jayasiri 的 Numpy 實現以及 Karphay 的講座和文章。
找到一個公平的方法來衡量準確性非常困難。假設你選擇逐字比較,那麼如果你的預測中有一個字不同步,那麼你的準確性就可能會是零。如果你刪掉一個符合預測的單詞,最後的準確性也可能是 99%。
我用的是 BLEU 測評法,這是一種用於機器翻譯和影象字幕模型的最佳實踐。它按照 1 到 4 個單詞序列把句子分成四個 gram。在下面的預測中的“cat”應該是“code”。
為了拿到最終的分數,你需要將得到的數字都乘以 25%,(4/5)*0.25 + (2/4)*0.25 + (1/3)*0.25 + (0/2)*0.25 = 0.2 + 0.125 + 0.083 + 0 = 0.408。求和的結果再乘以句子長度的處罰值。因為我們例子中的句子長度是正確的,所以求和的結果直接就是最終的結果。
你可以通過增加 gram 的數量讓它變得更難。四個 gram 的模型是最符合人類翻譯的模型。我建議使用下面的程式碼執行幾個例子並閱讀 Wiki 頁,來加深對這方面的瞭解。
一些輸出樣本的連結:
生成網站 1——original 1
https://emilwallner.github.io/bootstrap/pred_1/
生成網站 2——original 2
https://emilwallner.github.io/bootstrap/real_2/
生成網站 3——original 3
https://emilwallner.github.io/bootstrap/pred_3/
生成網站 4——original 4
https://emilwallner.github.io/bootstrap/pred_4/
生成網站 5——original 5
https://emilwallner.github.io/bootstrap/pred_5/
理解不同模型的弱點,而不是測試隨機模型。開始的時候,我使用了類似批次標準化和雙向網路這類隨機模型,並嘗試實現注意力機制。然後看了測試資料,才發現這樣無法準確預測顏色和位置,我意識到 CNN 有一個弱點。這就導致我採用更大的步幅來取代 maxpooling。之後驗證損失就從 0.12 變為 0.02,而 BLEU 得分從 85%提高到了 97%。
如果它們相關的話,只使用預先訓練好的模型。鑑於給定的資料集很小,我認為經過預訓練的影象模型會提高效能。從我的實驗來看,端到端的模型訓練起來比較慢,需要更多的記憶體,但是精度要高出 30%。
在遠端伺服器上執行模型時,你的計劃需要有所調整。在我的 Mac 上,它會按字母順序來讀取檔案。但是在遠端伺服器上,它是隨機定位的。這就造成了截圖和程式碼之間的不匹配。雖然它仍然收斂,但驗證資料比我修正之後的要差 50%。
確保你理解庫函式。包括詞彙表中空記號的填充空格。當我沒有新增填充空格時,預測中沒有包括任何一個空記號。這也是我看了好幾遍輸出結果之後才注意到的,而且模型從來沒有預測到一個單記號。經過快速的檢查,我發現到這甚至不在詞彙表中。另外,詞彙表要以相同的順序進行訓練和測試。
試驗時使用更輕量級的模型。使用 GRU 而不是 LSTM 會將每個 epoch 週期減少 30%,並且對效能沒有太大的影響。
前端開發是應用深度學習的理想領域。因為生成資料很容易,並且目前的深度學習演算法可以對映絕大部分的邏輯。
其中最令人興奮的領域之一是注意力機制在 LSTM 上的應用。這不僅會提高準確性,而且還能使我們能夠直觀地看到 CNN 在產生標籤時將焦點放在了哪裡。
注意力機制也是標籤、樣式表、指令碼和後端之間溝通的關鍵。注意力層可以跟蹤變數,確保神經網路能夠在程式語言之間進行通訊。
但在不久的將來,最大的問題在於如何找到一種可伸縮的方式來生成資料,這樣就可以逐步加入字型、顏色、文字和動畫。
到目前為止,大部分的進步都是發生在將草圖轉化為模板應用程式的過程中。在不到兩年的時間裡,我們將可以在紙上畫一個應用程式,並在不到一秒的時間內就可以獲得相應的前端程式碼。Airbnb 的設計團隊和 Uizard 已經構建了兩個可用的原型。
執行所有的模型
嘗試不同的超引數
測試一個不同的 CNN 構架
新增雙向 LSTM 模型
用不同的資料集來實現模型
使用相應的語法建立一個可靠的隨機應用程式或網頁生成器。
從草圖到應用模型的資料。將應用程式或網頁截圖自動轉換為草圖,並使用 GAN 建立不同型別的草圖。
應用一個注意力層,視覺化每個預測在影象上的焦點,類似於這個模型。
為模組化方法建立一個框架。比如,有字型的多個編碼器模型,其中一個用於顏色,另一個用於排版,之後將這些編碼器整合到一個解碼器中。獲得穩定的固體影象特徵是一個好兆頭。
為神經網路提供簡單的 HTML 元件,並且教它使用 CSS 生成動畫。
原文連結:
https://blog.floydhub.com/turning-design-mockups-into-code-with-deep-learning/
更多幹貨內容請關注微信公眾號“AI 前線”,(ID:ai-front)