【啄米日常】3:一個不負責任的Keras介紹(下)

本來上篇文末說,(下)可能會推遲一會兒……

但是我控制不住我即己啊!一開始寫就想一下子全部寫完啊!!

所以我又開始筆耕不綴了,這篇專欄寫完,我基本是棵廢菇了……

(下)主要說一下Keras的有用特性,以及一些常見問題,如果還有精力的話,補一些使用Keras的陷阱,沒精力這部分就留到番外篇了。理解這些特性對深入了解Keras有比較重要的幫助。

callable,全部Layer都要callable!

Keras的一大性質是所有的layer對象都是callable的。所謂callable,就是能當作函數一樣來使用,層的這個性質不需要依賴任何模型就能成立。比方說你想算算一個向量x的sigmoid值是多少,如果用keras的話,你可以這樣寫:

import keras.backend as Kfrom keras.layers import Activationimport numpy as npx = K.placeholder(shape=(3,))y = Activation("sigmoid")(x)f = K.function([x],[y])out = f([np.array([1,2,3])])

很顯然,我絕對不會寫這種代碼來求sigmoid,這個例子只是說明,可以這麼干,而可以這麼乾的話很多工作就會很方便。比方說有一次我想給目標函數加一項全變差的正則項,而全變差可以用特定的卷積來實現, 那麼我的目標函數的全變差正則項完全就可以用一個Convolution2D層來實現。把層和模型當作張量的函數來使用,是需要認真貫徹落實的一個東西。

順便我們也複習一下上一篇文章說的符號式計算方法。正文第1行先定義了一個「佔位符」,它的shape是一個長為3的向量。所謂佔位符就是「先佔個位置「的符號,翻譯成中文就是」此處應有一個長為3的向量「。注意第2行,這行我們使用了一個激活層,激活層的激活函數形式是sigmoid,在激活層的後面又有一個括弧,括弧內是我們的輸入張量x,可以看到,層對象『Activation("sigmoid")』是被當做一個函數來使用的。上篇文章說層就是張量到張量的運算,那麼其輸出y自然也是一個張量。

第3行通過調用function函數對計算圖進行編譯,這個計算圖很簡單,就是輸入張量經過sigmoid作用變成輸出向量,計算圖的各種優化通過這一步得以完成,現在,f就是一個真正的函數了,就可以按照一般的方法使用了。

之前說了,模型也是張量到張量的映射,所以Layer是Model的父類,因此,一個模型本身也可以像上面一樣使用。總而言之,在Keras中,層對象是callable的。

Node:Keras的網路層復用

Keras的網路層復用是一個很常用的需求,例如當某一層與多個層相連時,實際上這層是將同一種計算方式復用多次。再比如你用一個網路來抽取兩條微博的特徵,然後在後面用網路來判斷二者是否是同一個主題,那麼抽取兩次微博的特徵這一工作就可以復用同一個網路。

Keras的網路復用由一個叫「Node」,或稱「計算節點」的東西來實現。籠統地說,每當在某個輸入上調用層時,就會為網路層添加一個節點。這個節點將輸入張量映射為輸出的張量,當你多次調用該層,就會產生多個結點,結點的下標是0,1,2,3...

如果僅僅是這樣,這部分的東西你依然不需要了解,問題在於,當一個層有多個計算節點時,它的input,output,input_shape,output_shape等屬性可能是ill-defined的,因為不清楚你想要的output或input是哪一個。

此時,需要使用get_output_at(),get_input_at(),get_output_shape_at()等以at為後綴結尾的函數,at的對象就是層的節點編號。例如get_output_shape_at(2)就會返回第3個輸出張量的shape。

Shape與Shape自動推斷

使用過Keras的都知道,Keras的所有的層有一個「input_shape」的參數,用來指定輸入張量的shape。然而這個input_shape,或者有時候是input_dim,只需要在模型的首層加以指定。一旦模型的首層的input_shape指定了,後面的各層就不用再指定,而會根據計算圖自動推斷。這個功能稱為shape的自動推斷。

Keras的自動推斷依賴於Layer中的get_output_shape_for函數來實現,如果大家還記得上一篇文章的話,在提到如何編寫自己的Keras層時,我們提到如果你的網路層改變了輸入張量的shape,就應該複寫get_output_shape_for這個函數,以使後面的層能知道本層輸出的shape。

在所有的Keras中都有這樣一個函數,因此後面的層可以通過查看這個函數的返回值獲取前層的輸入shape,並通過自己的get_output_shape_for將這個信息傳遞下去。

然而,有時候,這個自動推斷會出錯。這種情況發生在一個RNN層後面接Flatten然後又接Dense的時候,這個時候Dense的output_shape無法自動推斷出。這時需要指定RNN的輸入序列長度input_length,或者在網路的第一層通過input_shape就指定。這種情況極少見,大致有個印象即可,遇到的話知道大概是哪裡出了問題就好。

一般而言,神經網路的數據是以batch為單位的,但在指明input_shape時不需要說明一個batch的樣本數。假如你的輸入是一個224*224*3的彩色圖片,在內部運行時數據的shape是(None,224,224,3),這點在你自己編寫層是需要注意。

TH與TF的相愛相殺

相愛沒有,全是相殺

Keras提供了兩套後端,Theano和Tensorflow,這是一件幸福的事,手中拿著饅頭,想蘸紅糖蘸紅糖,想蘸白糖蘸白糖

th和tf的大部分功能都被backend統一包裝起來了,但二者還是存在不小的衝突,有時候你需要特別注意Keras是運行在哪種後端之上,它們的主要衝突有:

  • dim_ordering,也就是維度順序。比方說一張224*224的彩色圖片,theano的維度順序是(3,224,224),即通道維在前。而tf的維度順序是(224,224,3),即通道維在後。dim_ordering不是一個必須和後端搭配的指標,它只規定了輸入圖片的維度順序,只要輸入圖片按此維度順序設置即可正確運行。然而,如果dim_ordering與後端搭配的話——我指的是所有層的dim_ordering都與後端搭配,會提高程序的運行效率。否則,數據的shape會在計算過程中不斷轉來轉去,效率會低一些。
  • 卷積層權重的shape:從無到有訓練一個網路,不會有任何問題。但是如果你想把一個th訓練出來的卷積層權重載入風格為tf的卷積層……說多了都是淚。我一直覺得這個是個bug,數據的dim_ordering有問題就罷了,為啥卷積層權重的shape還需要變換咧?我遲早要提個PR把這個bug修掉!
  • 然後是卷積層kernel的翻轉不翻轉問題,這個我們說過很多次了,就不再多提。

總而言之,相愛沒有,全部都是相殺。儘管Keras已經在統一theano和tensorflow上走了很多很多步,但還需要走更多的一些步。

FAQ與學習資料

FAQ模塊請參考這裡:FAQ - Keras中文文檔

另外,在這裡有一些使用示範,包括與TensorFlow的聯動,分散式訓練等:

CNN眼中的世界

花式自動編碼器

面向小數據集構建圖像分類模型

在Keras模型中使用預訓練的詞向量

將Keras作為tensorflow的精簡介面

Keras/Python深度學習中的網格搜索超參數調優(附源碼)

Keras已經開始組建自己的Keras Zoo,也就是預訓練的模型庫,這個github在:

GitHub - fchollet/fchollet/deep-learning-models

關於我們老提的TH和TF的卷積核轉換,這裡是使用示範:

TF kernel - TH kernel

學習Keras最快的方法莫過於直接閱讀示例代碼了,這裡是example:

keras examples

這個哥們兒fork的Keras自己搞了一個caffe到keras的模型轉換模塊,好像不是很完美,不過說不定能湊合用,先放這兒了:

GitHub - MarcBS/keras

如果有什麼疑問請留言提問~能回答的我會回答然後貼到這裡……回答不來的……我就裝沒看見了

最近會出一個Keras使用的陷阱集錦,或稱防坑指南,如果能做好的話可以作為番外篇~

好,先這樣吧~鞠躬,下台!

推薦閱讀:

CTR預估[四]: Algorithm-LR Bias和Q分布
深度學習(Deep Learning)基礎概念7:搭建多層神經網路的python實現
歷史|從神經元到深度學習
Python3《機器學習實戰》學習筆記(一):k-近鄰演算法(史詩級乾貨長文)

TAG:深度学习DeepLearning | 机器学习 | Keras |