本文構建了一個能同時完成四個任務的的深度神經網絡: 生成圖像描述、生成類似單詞、以圖搜圖和根據描述搜圖。傳統上這些任務分別須要一個模型,但咱們如今要用一個模型來完成全部這些任務。git
衆所周知,神經網絡十分擅長處理特定領域的任務 (narrow task),但在處理多任務時結果並非那麼理想。github
這與人類的大腦不一樣,人類的大腦可以在多樣化任務中使用相同的概念。例如,假如你歷來沒據說過 「分形」(fractal),請看下面這張圖:數據庫
數學之美:分形圖像 安全
上圖是一個分形圖像。在看到一張分形圖像後,人可以處理多個與之相關的任務:網絡
-
在一組圖像中,區分一隻貓的圖像和分形圖像;架構
-
在一張紙上,粗略地畫一個分形圖像;ide
-
將分形圖像與非分形圖像進行分類;函數
-
閉上眼睛,想象一下分形圖像是什麼樣子的。post
那麼,你是如何完成這些任務的呢?大腦中有專門的神經網絡來處理這些任務嗎?性能
現代神經科學認爲,大腦中的信息是在不一樣的部位進行分享和交流的。對於這種多任務性能是如何發生的,答案可能在於如何在神經網絡中存儲和解釋數據。
「表示」 的精彩世界
顧名思義,「表示」(representation) 就是信息在網絡中編碼的方式。當一個單詞、一個句子或一幅圖像 (或其餘任何東西) 做爲輸入提供給一個訓練好的神經網絡時,它就隨着權重乘以輸入和應用激活在連續的層上進行轉換。最後,在輸出層,咱們獲得一串數字,咱們將其解釋爲類的標籤或股票價格,或網絡爲之訓練的任何其餘任務。
輸入 -> 輸出的神奇轉換是由連續層中發生的輸入轉換產生的。輸入數據的這些轉換即稱爲 「表示」(representations)。一個關鍵的想法是,每一層都讓下一層更容易地完成它的工做。使連續層的週期變得更容易的過程會致使激活 (特定層上輸入數據的轉換) 變得有意義。
有意義是指什麼呢?讓咱們看下面的示例,該示例展現了圖像分類器中不一樣層的激活。
圖像分類網絡的做用是將像素空間中的圖像轉化爲更高級的概念空間。例如,一張汽車的圖像最初被表示爲 RGB 值,在第一層開始被表示爲邊緣空間,而後在第二層被表示爲圓圈和基本形狀空間,在倒數第二層,它將開始表示爲高級對象 (如車輪、車門等)。
這種愈來愈豐富的表示 (因爲深度網絡的分層性質而自動出現) 使得圖像分類的任務變得簡單。最後一層要作的就是斟酌,好比說,車輪和車門的概念更像汽車,耳朵和眼睛的概念更像人。
你能用這些表示作什麼 ?
因爲這些中間層存儲有意義的輸入數據編碼,因此能夠對多個任務使用相同的信息。例如,你可使用一個語言模型 (一個通過訓練的、用於預測下一個單詞的遞歸神經網絡),並解釋某個特定神經元的激活,從而預測句子帶有的情緒。
一個使人驚訝的事實是,情感神經元是在無監督的語言建模任務中天然產生的。網絡被訓練去預測下一個單詞,它的任務中並無被要求去預測情感。也許情感是一個很是有用的概念,以致於網絡爲了更好地進行語言建模而發明它。
一旦你理解了 「表示」 這個概念,你就會開始從徹底不一樣的角度來理解深層神經網絡。你會開始將感知表示 (sensing representations) 做爲一種可轉換的語言,使不一樣的網絡(或同一網絡的不一樣部分) 可以彼此通訊。
經過構建一個四合一的網絡來探索表示
爲了充分理解 「表示」,讓咱們來構建一個能同時完成四個任務的的深度神經網絡:
-
圖像描述生成器:給定圖像,爲其生成描述
-
類似單詞生成器:給定一個單詞,查找與之類似的其餘單詞
-
視覺類似的圖像搜索:給定一幅圖像,找出與之最類似的圖像
-
經過描述圖像內容進行搜索:給出文本描述,搜索具備所描述的內容的圖像
這裏的每個任務自己就是一個項目,傳統上分別須要一個模型。但咱們如今要用一個模型來作全部這些任務。
Pytorch 代碼:
https://github.com/paraschopra/one-network-many-uses
第一部分:看圖說話 (Image Captioning)
在網上有不少實現 Image Captioning 的很好的教程,因此這裏不打算深刻講解。個人實現與這個教程中的徹底相同:https://daniel.lasiman.com/post/image-captioning/。關鍵的區別在於,個人實現是在 Pytorch 中實現的,而這個教程使用的是 Keras。
接下來,你須要下載 Flickr8K 數據集。你還須要下載圖像描述。提取「caption_datasets」 文件夾中的文字描述。
模型
Image Captioning 通常有兩個組成部分:
a) 圖像編碼器 (image encoder),它接收輸入圖像並以一種對圖像描述有意義的格式來表示圖像;
b) 圖說jie碼器 (caption decoder),它接受圖像表示,並輸出文本描述。
image encoder 是一個深度卷積網絡,caption decoder 則是傳統的 LSTM/GRU 遞歸神經網絡。固然,咱們能夠從頭開始訓練它們。但這樣作須要比咱們現有的 (8k 圖像)更多的數據和更長的訓練時間。所以,咱們不從頭開始訓練圖像編碼器,而是使用一個預訓練的圖像分類器,並使用它的 pre-final 層的激活。
這是一個示例。我使用 PyTorch modelzoo 中可用的 Inception 網絡,該網絡在ImageNet 上進行了訓練,能夠對 100 個類別的圖像進行分類,並使用它來提供一個能夠輸入給遞歸神經網絡中的表示。
請注意,Inception network 從未針對圖說生成任務進行過訓練。然而,它的確有效!
咱們也可使用一個預訓練的語言模型來做爲 caption decoder。但這一次,因爲我從新實現了一個運行良好的模型,因此能夠從頭開始訓練jie碼器。
完整的模型架構以下圖所示:
你能夠從頭開始訓練模型,可是須要在 CPU 上花費幾天時間 (我尚未針對 GPU 進行優化)。但不用擔憂,你也能夠享受一個已經訓練完成的模型。(若是你是從頭開始訓練,請注意,我在大約 40 epochs 時中止訓練,當時運行的平均損失約爲 2.8)。
性能
我實現了性能良好的 beam search 方法。下面是網絡爲測試集中的圖像生成的圖說示例(之前從未見過)。
用我本身的照片試試,讓咱們看看網絡生成的圖說是什麼:
效果不錯!使人印象深入的是,網絡知道這張照片裏有一個穿着白色 T 恤的男人。但語法有點偏離 (我相信經過更多的訓練能夠修正),但基本的要點抓住了。
若是輸入的圖像包含網絡從未見過的東西,它每每會失敗。例如,我很好奇網絡會給iPhone X 的圖像貼上什麼樣的標籤。
效果不太好。但總的來講,我對它的表現很是滿意,這爲咱們使用網絡在學習給圖像生成圖說時開發的 「表示」 來構建其餘功能提供了良好的基礎。
第二部分:查找類似單詞
回想一下咱們如何從圖像表示中jie碼圖說。咱們將該表示提供給 LSTM/GRU 網絡,生成一個輸出,將其解釋爲第一個單詞,而後將第一個單詞返回到網絡以生成第二個單詞。這個過程一直持續到網絡生成一個表示句子結束的特殊標記爲止。
爲了將單詞反饋到網絡中,咱們須要將單詞轉換爲表示,再輸入給網絡。這意味着,若是輸入層包含 300 個神經元,那麼對於全部圖說中的 8000 多個不一樣的單詞,咱們須要有一個 300 個相關聯的數字,惟一地指定那個單詞。將單詞字典轉換成數字表示的過程稱爲詞彙嵌入 (或詞彙表示)。
咱們能夠下載和使用已經存在的詞彙嵌入,如 word2vec 或 GLoVE。但在這個示例中,咱們從頭開始學習詞彙嵌入。咱們從隨機生成的詞彙嵌入開始,探索在訓練結束時,網絡對單詞的瞭解。
因爲咱們沒法想象 100 維的數字空間,咱們將使用一種稱爲 t-SNE 的奇妙技術來在 2維中可視化學習的詞彙嵌入。t-SNE 是一種降維技術,它試圖使高維空間中的鄰域同時也是低維空間中的鄰域。
詞彙嵌入的可視化
讓咱們來看看 caption decoder 學習到的詞彙嵌入空間 (不像其餘語言任務有數百萬單詞和句子,咱們的jie碼器在訓練數據集中只有 ~30k 的句子)。
所以,咱們的網絡已經瞭解到像 「play」、「plays」 和 「playing」 這樣的詞彙是很是類似的 (它們具備類似的表示形式,如紅色箭頭所示的緊密聚類)。讓咱們在這個二維空間中探索另外一個區域:
這個區域彷佛有一堆數字 ——「two」、「three」、「four」、「five」,等等。
上圖,它知道 people 和 children 兩個單詞類似。並且,它還隱式地推斷出了物體的形狀。
類似詞彙
咱們可使用 100 維表示 (100-dimensional representation) 來構建一個函數,該函數提出與輸入單詞最類似的單詞。它的工做原理很簡單:採用 100 維的表示,並找出它與數據庫中全部其餘單詞的餘弦類似度。
讓咱們來看看與 「boy」 這個單詞最類似的單詞:
結果不錯。「Rider」 除外,但 「kids」、「kid」 和 「toddler」 都是正確的。
這個網絡認爲與 「chasing」 類似的詞彙是:
「Chases」 是能夠的,但我不肯定爲何它認爲 「police」 與 「chasing」 相似。
單詞類比 (Word analogies)
關於詞彙嵌入的一個使人興奮的事實是,你能夠對它們進行微積分。你能夠用兩個單詞(如 「king」 和 「queen」) 並減去它們的表示來獲得一個方向。當你把這個方向應用到另外一個詞的表示上 (如 「man」),你會獲得一個與實際相似詞 (好比 「woman」) 很接近的表示。這就是爲何 word2vec 一經推出就如此受歡迎的緣由:
我很好奇經過 caption decoder 學習到的表示是否具備相似的屬性。儘管我持懷疑態度,由於訓練數據並不大 (大約 3 萬個句子),我仍是嘗試了一下。
網絡學習到的類比並不完美 (有些單詞字面上出現的次數<10 次,因此網絡沒有足夠的信息可供學習)。但仍有一些類比。
若是 riding 對應 sitting,那麼 walking 對應什麼呢?個人網絡認爲應該是 「laying」(這個結果還不錯!)
一樣,若是 「man」 的複數是 「men」,那麼 「woman」 的複數應該是什麼呢:
第二個結果是 「women」,至關不錯了。
最後,若是 grass 對應 green,那麼 sky 對應什麼呢:
網絡認爲 sky 對應 silver 或 grey 的,雖然沒有出現 blue,但它給的結果都是顏色詞。使人驚訝的是,這個網絡可以推斷顏色的方向。
第三部分:查找類似圖像
若是單詞表示將相似的單詞聚在一塊兒,那麼圖像表示 (Inception 支持的圖像編碼器輸出) 呢?我將相同的 t-SNE 技術應用於圖像表示 (在 caption decoder 的第一步中做爲輸入的 300-dimensional tensor)。
可視化
這些點是不一樣圖像的表示 (不是所有 8K 圖像,大約是 100 張圖像的樣本)。紅色箭頭指向附近的一組表示的聚類。
賽車的圖像被聚類在一塊兒。
孩子們在森林 / 草地玩耍的圖像也被聚類在一塊兒。
籃球運動員的圖像被聚類在一塊兒。
查找與輸入圖像類似的圖像
對於查找類似單詞任務,咱們被限制在測試集詞彙表中尋找類似的單詞 (若是測試集中不存在某個單詞,咱們的 caption decoder 就不會學習它的嵌入)。然而,對於相似的圖像任務,咱們有一個圖像表示生成器 (image representation generator),它能夠接受任何輸入圖像並生成其編碼。
這意味着咱們可使用餘弦類似度方法來構建一個按圖像搜索的功能,以下所示:
步驟 1:獲取數據庫或目標文件夾中的全部圖像,並存儲它們的表示 (由 image encoder給出)
步驟 2:當用戶但願搜索與已有圖像最類似的圖像時,使用新圖像的表示並在數據庫中找到最接近的圖像 (由余弦類似度給出)
谷歌圖像可能正式使用這種 (或相似的) 方法來支持其反向圖像搜索功能。
讓咱們看看這個網絡是如何工做的:
上面這張圖像是我本身的。咱們使用的模型之前從未見過它。當我查詢相似圖像時,網絡從 Flickr8K 數據集輸出以下圖像:
是否是很像?我沒想到會有這麼好的表現,但咱們確實作到了!
第四部分:經過描述查找圖像
在最後一部分中,咱們將反向運行 caption generator。所以,咱們不是獲取圖像併爲其生成標題,而是輸入標題 (文本描述) 並找到與之最匹配的圖像。
過程以下:
-
步驟 1:不是歷來自編碼器的 300 維圖像表示開始,而是從一個徹底隨機的 300 維輸入張量開始
-
步驟 2:凍結整個網絡的全部層 (即指示 PyTorch 不要計算梯度)
-
步驟 3:假設隨機生成的輸入張量來自 image encoder,將其輸入到 caption decoder中
-
步驟 4:獲取給定隨機輸入時網絡生成的標題,並將其與用戶提供的標題進行比較
-
步驟 5:計算比較生成的標題和用戶提供的標題的損失
-
步驟 6:找到使損失最小的輸入張量的梯度
-
步驟 7:根據梯度改變輸入張量的方向 (根據學習率改變一小步)
-
繼續步驟 4 到步驟 7,直到收斂或當損失低於某個閾值時爲止
-
最後一步:取最終的輸入張量,並利用它的值,經過餘弦類似度找到離它最近的圖像
結果至關神奇的:
我搜索了 「a dog」,這是網絡找到的圖像:
搜索 「a boy smiling」:
最後,搜索:
前兩個結果是:
以及
總結和挑戰
全部這些操做的代碼能夠從 github 存儲庫下載執行:
https://github.com/paraschopra/one-network-many-uses
這個存儲庫包括了用於數據預處理、模型描述、預訓練的圖說生成網絡、可視化的代碼。但不包括 Flickr8K 數據集或標題,須要單獨下載。
若是你想更進一步,這裏有一個挑戰:從給定的描述生成圖像。
這比本文中處理的要難 10 倍,但我感受這是可行的。若是一項服務不只可以搜索與文本對應的圖像,並且可以動態地生成圖像,那該多酷啊。
在將來,若是 Google Images 實現了這個功能,並可以爲不存在的圖像提供結果 (好比「兩隻獨角獸在披薩作成的地毯上飛翔」),我不會感到驚訝的。
就這樣。祝你能安全愉快地探索表示的世界。