深度學習框架之Keras初識:像搭積木般的玩轉神經網絡

1. 寫在前面

如果是剛入深度學習的新手小白,可能有着只學習了一點深度學習的理論,也見識到了各種神經網絡的強大而不能立馬實現的煩惱,想學習TensorFlow,pytorch等出色強大的深度學習框架,又看到那代碼晦澀難懂而有些想知難而退,這時候,我覺得有必要掌握一下Keras了,這是個啥? Keras是高級神經網絡API,因爲Keras短小精悍,非常適合快速原型製作和神經網絡的搭建。在很短的時間內,就能夠建立一個模型,以實現出色的結果,讓神經網絡的搭建像積木一樣簡單,更重要的一點學習深度學習網絡,能快速實現,有滿滿的成就感,不僅可以增加對知識的理解,更可以給自己提供源源不斷的學習動力。基於這些,想把自己學習Keras的經歷整理一下,因爲我也是小白學起,正好邊學邊整理。 最近又正好看了《將夜》,發現昊天世界的修煉等級名稱比較有趣(初識,感知,不惑,洞玄,知命),所以爲了增加趣味性,把Keras學習系列的名字和修煉級別關聯起來了,因爲我們學習本身就是一場修行。

深度學習框架經過更新很快,TensorFlow,Pytorch等傳播盛行,但短小精悍的Keras在未來仍會佔據一席之地,並長期佔據下去

今天開始學習Keras,算是對Keras初識吧,今天主要是學習如何用Keras搭建神經網絡模型並如何使用模型(教你快速搭建LeNet5,ResNet網絡等)等,最後通過今天的學習簡單的搭建一個小模型,完成一個簡單的2分類任務感受一下,今天的實戰任務不太強勢,因爲想先整理一下Keras的基礎知識。

知識大綱:

  • Keras的建立和使用(模型定義,模型配置,模型訓練,模型預測評估及保存整套流程先走一邊)
  • Keras的模塊細節(重點層次的介紹, 網絡配置,預處理)
  • 用Keras搭建神經網絡實現簡單的二分類

對於Keras,還需要知道,目前Keras已經更新到了2.3.0, 因爲Keras是神經網絡API,所以目前需要藉助於那些框架作爲後端: TensorFlow,theano等都支持Keras了。使用的時候,得先安裝這些框架中的一種。

pip install tensorflow
pip install keras

# 更新numpy到最新版本
pip install -U numpy

學習過程中,如果有不懂的知識點,要記得參考官方文檔:

OK, let’s go!

2. Keras建立及使用流程

2.1 定義模型

首先我們要搭建神經網絡的時候,要會定義模型,就好比搭積木,我們光有積木可不行,得在心中有一個最後搭建成什麼樣子的一個模型出來(比如我們想搭個房子,得有一個這樣的房子目標,這樣搭建完了之後我們才能用,這個房子就類似於模型的定義的東西。)

Keras定義模型由兩種方式: 序列模型SequentialModel, 差異在於不同的拓撲結構。下面具體來看看:(注意下面用到的各種層不懂沒事,後面會介紹,相當於積木的各個模塊,Keras已經實現好了,拿過來用就行了,先看看是怎麼定義模型的)

  1. 序列模型Sequential
    序列模型各層之間是依次順序的線性關係,模型結構通過一個列表來制定或者採用.add的方式堆疊上去。意思就是說,我每個模塊類似於往上直接堆疊,後面的輸入就是前面的輸出,不能有其他的結構了。這種方式非常簡單,只需要把神經網絡塊摞起來就行。但是缺點就是如果神經網絡結構不遵循線性關係,這種方式不能用。下面體會一下Keras的強大吧。 看看如何用Sequential搭建比較經典的網絡LeNet5

    LeNet 提出於 1986 年,是最早用於數字識別的 CNN 網絡,輸入尺寸是 32 * 32。它輸入的是灰度的圖像,整個的網絡結構是:輸入層→C1 卷積層→S2 池化層→C3 卷積層→S4 池化層→C5 卷積層→F6 全連接層→Output 全連接層,對應的 Output 輸出類別數爲 10。 結構是這個樣子:
    在這裏插入圖片描述

    先用列表的方式:

    from keras.models import Sequential
    from keras.layers import Dense, Flatten
    from keras.layers import Conv2D, MaxPooling2D
    
    layers = [
    # 第一層卷積層: 6個卷積核, 大小爲5*5, relu激活函數
    Conv2D(6, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)),
    # 第二層是最大池化層
    MaxPooling2D(pool_size=(2,2),
    # 第三層卷積層: 16個卷積核, 大小5*5, relu激活函數
    Conv2D(16, kernel_size=(5, 5), activation='relu'),
    # 第四層最大池化層
    MaxPooling2D(pool_size=(2, 2)),
    # 第五層將參數扁平化, 在Lenet5中稱爲卷積,實際上這一層是一維向量,和全連接一樣
    Flatten(),
    Dense(120, activation='relu'),
    # 全連接層,輸出節點個數爲84個
    Dense(84, activation='relu'),
    # 輸出層  用softmax激活函數計算分類概率
    Dense(10, activation='softmax')
    ]
    
    ## 建立模型
    model = Sequential(layers)
    

    還可以逐層添加網絡結構:

    model = Sequential()
    # 第一層卷積層: 6個卷積核, 大小爲5*5, relu激活函數
    model.add(Conv2D(6, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)))
    # 第二層是最大池化層
    model.add(MaxPooling2D(pool_size=(2,2)))
    
    # 第三層卷積層: 16個卷積核, 大小5*5, relu激活函數
    model.add(Conv2D(16, kernel_size=(5, 5), activation='relu'))
    
    # 第四層最大池化層
    model.add(MaxPooling2D(pool_size=(2, 2)))
    
    # 第五層將參數扁平化, 在Lenet5中稱爲卷積,實際上這一層是一維向量,和全連接一樣
    model.add(Flatten())
    model.add(Dense(120, activation='relu'))
    
    # 全連接層,輸出節點個數爲84個
    model.add(Dense(84, activation='relu'))
    
    # 輸出層  用softmax激活函數計算分類概率
    model.add(Dense(10, activation='softmax'))
    

    是不是有種堆積木的感覺呢?可以用model.summary()看一下模型結構:
    在這裏插入圖片描述

  2. 通用模型Model
    序貫模型的好處是簡單操作,真的和搭積木一樣一層層的往上搭就行,但是神經網絡的結構只能是線性的才行,沒法搭建出複雜的神經網絡,而通用模型可以設計非常複雜、任意拓撲結構的神經網絡,例如有向無環網絡、共享層網絡等。相比於序列模型只能依次線性逐層添加,通用模型能夠比較靈活地構造網絡結構,設定各層級的關係。下面看看如何用Model搭建一個挺複雜的網絡,我從論文裏面隨便截了個圖:
    在這裏插入圖片描述
    這個看起來挺複雜吧,萬一你想實現呢? 有了Keras,一切皆有可能。

    # 確定好輸入,注意指定輸入形狀
    x = Input(shape=(input_shape))    # 這個input_shape得自己根據情況指定
    
    # 卷積層的搭建 
    # 第一個卷積層模塊
    conv_x = Conv2D(filters=64, kernel_size=(8,1), padding='same')(x)
    conv_x = BatchNormalization()(conv_x)
    conv_x = Activation('relu')(conv_x)
    
    conv_y = Conv2D(filters=64, kernel_size=(5,1), padding='same')(conv_X)
    conv_y = BatchNormalization()(conv_y)
    conv_y = Activation('relu')(conv_y)
     
    conv_z = Conv2D(filters=64, kernel_size=(3, 1), padding='same')(conv_y)
    conv_z = BatchNormalization()(conv_z)
    
    # 跳遠連接
    is_expand_channels = not (input_shape[-1] == 64)  # 得保證維度
    if is_expand_channels:
        shortcut_y = Conv2D(filters=64, kernel_size=(1, 1), padding='same')(x)
        shortcut_y = BatchNormalization()(shortcut_y)
    else:
        shortcut_y = BatchNormalization()(x)
    
    y = Add()([shortcut_y, conv_z])   # 跳遠連接
    y = Activation('relu')(y)
    
    """上面實現了第一個卷積層模塊了,第二個,第三個卷積層模塊和上面基本一樣,就是參數改改就OK,下面跳過去了,太多了要不然,作爲例子,不用那麼多,直接就是最後池化,然後輸出"""
    full = GlobalAveragePooling2D()(y)   
    out = Dense(class_num, activation='softmax')(full)
    
    # 建立模型
    ResNet = Model(inputs=x, outputs=out)
    

2.2 定義模型的相關配置(model.compile)

模型定義好了,相當於積木已經搭建完成,下面就是訓練之前,要對模型進行一下配置,選擇優化器和損失函數。

Model.compile(optimizer, loss, metrics=[], loss_weights=None, sample_weight_mode=None)

參數簡單看一下:
在這裏插入圖片描述

拿手寫數字識別的例子:

model.compile(loss=keras.metrics.categorical_crossentropy, optimizer=keras.optimizers.Adam(), metrics=['accuracy'])

更多配置信息見下面的細節部分。

2.3 模型的訓練(model.fit)

把相關配置說好了之後,下面就可以進行模型的訓練了。

Model.fit(x, y, batch_size=32, nb_epoch=10, verbose=1, callbacks=[], validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None)


參數看一下:
在這裏插入圖片描述

還是拿手寫數字識別的例子:

"""訓練"""
model.fit(train_x, train_y, batch_size=128, epochs=20, verbose=1, validation_data=(test_x, test_y))

2.4 模型預測和評估(model.predict和model.evaluate)

模型訓練好了之後,就可以進行預測和評估了

Model.predict(self, x, batch_size=32, verbose=0), 返回預測值的numpy array

Model.evaluate(x, y, batch_size=32, verbose=1, sample_weight=None)

舉例:

"""對結果進行評估"""
score = model.evaluate(test_x, test_y)
print('誤差: %.4lf' % score[0])
print('準確率: ', score[1])

2.5 模型的保存(model.save)與重新加載(load_model)

訓練完了之後,一般需要保存模型,畢竟訓練一遍不容易,總不能每次新打開都訓練吧,所以我們最好是把模型給保存起來,下一次預測時候直接調過來用就可以了。

  • 模型的保存

Model.save(self, filepath, overwrite=True, include_optimizer=True)

返回一個HDF5文件。包含:模型的結構(以便重構該模型)、模型的權重、訓練配置(損失函數,優化器等)、優化器的狀態(以便於從上次訓練中斷的地方開始)

model.save('my_model.h5')
  • 模型的加載:

keras.models.load_model(filepath, custom_objects=None, compile=True)

from keras.models import load_model
model = load_model('my_model.h5')

哈哈,上面就是用Keras搭建神經網絡的一整套流程了,簡單總結一下,用Keras搭建神經網絡,步驟如下:

  • 首先需要定義模型,這個有兩種定義方式,序列模型方式Sequential簡單易操作,但搭建的模型結構同樣簡單,線性的纔可以。 通用模型Model方式也不是那麼複雜,並且可以定義強大結構的神經網絡,小建議就是後者了。
  • 有了model,下面就是進行配置,model.compile(),這裏面會指定損失函數,指定優化器,評估標準等(有關模型的超參數在這裏設置
  • 配置好模型,就可以model.fit()訓練,這裏面數據集,迭代次數,batch_size等(有關訓練的參數在這裏設置
  • 訓練好模型,就可以進行預測和評估的任務了model.predict(), model.evaluate()
  • 最後,別忘了model.save()保存模型
  • 當有新的預測要做時,只需要kerars.models.load_model()即可

是不是比較簡單啊,是不是按捺不住體內的洪荒之力,想把之前學習過的網絡結構統統先實現一遍再說? 先別慌,上面流程中直接用到了一些模塊,比如Conv2D, MaxPooling2D,Dense,Flatten等。

下面得對這些重點層做一些講解,對這些模塊瞭解了,才能更好的實現神經網絡。

3. Keras的細節介紹

3.1 重點層次介紹

Keras的層很多,這裏只對上面幾個簡單介紹一下,具體的還得參考官方文檔。

  1. 二維卷積層Conv2D
    二維卷積層,即對圖像的空域卷積。該層對二維輸入進行滑動窗卷積,當使用該層作爲第一層時,應提供input_shape參數。例如input_shape = (128,128,3)代表128*128的彩色RGB圖像(data_format=‘channels_last’).

    Conv2D(filters, kernel_size, activation=None) 創建, 其中 filters 代表卷積核的數量,kernel_size 代表卷積核的寬度和長度,activation 代表激活函數。如果創建的二維卷積層是第一個卷積層,需要提供 input_shape 參數

    卷積的根本目的是從輸入圖片中提取特徵。

  2. 最大池化層MaxPooling2D
    空間池化(也叫亞採樣或下采樣)降低了每個特徵映射的維度,但是保留了最重要的信息。空間池化可以有很多種形式:最大(Max),平均(Average),求和(Sum)等等。
    看下面示意圖: 最大池化就是選取最大的數,而平均池化就是計算平均數作爲最後結果AveragePooling2D
    在這裏插入圖片描述

    MaxPooling2D(pool_size=(2, 2)) 進行創建,其中 pool_size 代表下采樣因子,比如 pool_size=(2,2) 的時候相當於將原來 22 的矩陣變成一個點,即用 22 矩陣中的最大值代替,輸出的圖像在長度和寬度上均爲原圖的一半

    池化的功能是逐步減少輸入表徵的空間尺寸。特別地,池化

    • 使輸入表徵(特徵維度)更小而易操作
    • 減少網絡中的參數與計算數量,從而遏制過擬合
    • 增強網絡對輸入圖像中的小變形、扭曲、平移的魯棒性(輸入裏的微小扭曲不會改變池化輸出——因爲我們在局部鄰域已經取了最大值/平均值)。
    • 幫助我們獲得不因尺寸而改變的等效圖片表徵。這非常有用,因爲這樣我們就可以探測到圖片裏的物體,不論那個物體在哪。
  3. Flatten 層
    Flatten層用來將輸入“壓平”,即把多維的輸入一維化,常用在從卷積層到全連接層的過渡。Flatten不影響batch的大小。

    model = Sequential()
    model.add(Convolution2D(64, 3, 3, border_mode='same', 	
    input_shape=(3, 32, 32)))
    # now: model.output_shape == (None, 64, 32, 32)
    model.add(Flatten())
    # now: model.output_shape == (None, 65536)=64*32*32,壓平了
    
  4. Dense層
    全連接層(對上一層的神經元進行全部連接,實現特徵的非線性組合)
    Dense就是常用的全連接層,所實現的運算是output = activation(dot(input, kernel)+bias)。其中activation是逐元素計算的激活函數,kernel是本層的權值矩陣,bias爲偏置向量,只有當use_bias=True纔會添加。

    # as first layer in a sequential model:
    model = Sequential()
    model.add(Dense(32, input_shape=(16,)))  #input_shape=(16,)等價	
    於input_dim=16
    # now the model will take as input arrays of shape (*, 16) ,輸入維	=16
    # and output arrays of shape (*, 32) ,輸出維度=32
    

    “全連接”表示上一層的每一個神經元,都和下一層的每一個神經元是相互連接的。
    加入全連接層也是學習特徵之間非線性組合的有效辦法。卷積層和池化層提取出來的特徵很好,但是如果考慮這些特徵之間的組合,就更好了。

  5. BatchNormalization層
    該層在每個batch上將前一層的激活值重新規範化,即使得其輸出數據的均值接近0,其標準差接近1

    由於在訓練神經網絡的過程中,每一層的 params是不斷更新的,由於params的更新會導致下一層輸入的分佈情況發生改變,所以這就要求我們進行權重初始化,減小學習率。這個現象就叫做internal covariate shift。雖然可以通過whitening來加速收斂,但是需要的計算資源會很大。而Batch Normalizationn的思想則是對於每一組batch,在網絡的每一層中,分feature對輸入進行normalization,對各個feature分別normalization,即對網絡中每一層的單個神經元輸入,計算均值和方差後,再進行normalization。對於CNN來說normalize “Wx+b”而非 “x”,也可以忽略掉b,即normalize “Wx”,而計算均值和方差的時候,是在feature map的基礎上(原來是每一個feature)

  6. Add

  7. Dropout層

  8. LSTM層

這些層都在keras.layers裏面,層太多,沒法一一介紹,只能用的時候查閱官方文檔,不過,看到一篇博客,把所有的層寫成了導圖的方式,所以借用了一下,方便以後查閱,算是對所有層做一個總結吧:

3.2 keras網絡配置

這一塊直接使用參考博客裏面的一個圖作爲整理

其中回調函數callbacks應該是keras的精髓~

3.3 keras預處理功能

3.4 模型可視化

  • model.summary(): prints the details of your layers in a table with the sizes of its inputs/outputs(在表中打印各層的詳細信息及其輸入/輸出的大小)

  • plot_model(): plots your graph in a nice layout. You can even save it as “.png” using SVG() if you’d like to share it on social media. It is saved in “File” then “Open…” in the upper bar of the notebook.(畫出結構圖)

    plot_model(happyModel, to_file='HappyModel.png')
    SVG(model_to_dot(happyModel).create(prog='dot', format='svg'))
    

3.5 模型的節點信息提取

可以進行模型的遷移訓練

# 節點信息提取
config = model.get_config()  # 把model中的信息,solver.prototxt和train.prototxt信息提取出來
model = Model.from_config(config)  # 還回去
# or, for Sequential:
model = Sequential.from_config(config) # 重構一個新的Model模型,用去其他訓練,fine-tuning比較好用

關於更多的細節信息,可以參考Sequential與Model模型、keras基本結構功能(一)

4. 用Keras搭建神經網絡實現簡單的二分類

這個任務沒有什麼特別的意義,就是簡單的用一下Keras搭建網絡,就當消化一下上面的知識吧:

import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation

# 隨機產生數據
data = np.random.random((1000, 100))
labels = np.random.randint(2, size=(1000, 1))

# 創建模型
model= Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
# Dense(32) is a fully-connected layer with 32 hidden units.
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])


# 模型訓練
model.fit(data, labels, epochs =10, batch_size=32)

結果如下:
在這裏插入圖片描述
由於是隨機產生的數據集,可能效果並不是那麼好,但是你能看到在訓練階段正確率在提高,損失會降低。

這個例子是不是可以說明,想要實現一個神經網絡,用Keras可以秒搭。 可以自己玩玩別的網絡。

5. 總結

今天是Keras的初識境,主要是認識了一下Keras是個什麼東西,究竟可以實現什麼樣的功能。下面簡單的回顧一下,首先知道了Keras是個神經網絡的高度API,用它可以像搭積木一樣的實現神經網絡模型。 然後學習瞭如何用Keras定義和使用神經網絡模型。 然後就是學習了各種層的簡介(具體看官方文檔),最後用Keras實現了簡單的二分類的項目。 這個項目沒有什麼太大的意義,只是爲了消化一下知識,算是個小熱身吧。

在Keras的感知境,將會通過今天的這些知識,用上面的兩種方式搭建AlexNet神經網絡做手寫數字識別了,據說這個是深度學習界的Hello World級別,所以這個可不能不會。 手寫數字識別任務能基本上把今天學習的所有知識過一遍,這樣就相當於真正的實現了一下,所以暫且放到感知境吧,恭喜,學習了今天的知識,離破鏡就不遠了。

參考:

發佈了78 篇原創文章 · 獲贊 83 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章