天天看點

基于Keras的IMDB資料集的損失率和準确率對比實戰: 

Keras 簡介 

本文的代碼示例全都使用Keras 實作。Keras 是一個Python 深度學習架構,可以友善地定 義和訓練幾乎所有類型的深度學習模型。Keras 最開始是為研究人員開發的,其目的在于快速 實驗。

Keras 具有以下重要特性:

  •  相同的代碼可以在 CPU 或 GPU 上無縫切換運作。
  •  具有使用者友好的 API,便于快速開發深度學習模型的原型。
  •  内置支援卷積網絡(用于計算機視覺)、循環網絡(用于序列處理)以及二者的任意 組合。
  • 支援任意網絡架構:多輸入或多輸出模型、層共享、模型共享等。這也就是說,Keras 能夠建構任意深度學習模型,無論是生成式對抗網絡還是神經圖靈機。 Keras 基于寬松的MIT 許可證釋出,這意味着可以在商業項目中免費使用它。它與所有版 本的 Python 都相容(截至 2017 年年中,從 Python 2.7 到 Python 3.6 都相容)。 Keras 已有200 000 多個使用者,既包括創業公司和大公司的學術研究人員和工程師,也包括 研究所學生和業餘愛好者。Google、Netflix、Uber、CERN、Yelp、Square 以及上百家創業公司都在 用 Keras 解決各種各樣的問題。Keras 還是機器學習競賽網站Kaggle 上的熱門架構,最新的深 度學習競賽中,幾乎所有的優勝者用的都是 Keras 模型,如圖 3-2 所示。
基于Keras的IMDB資料集的損失率和準确率對比實戰: 

 Keras、TensorFlow、Theano 和 CNTK

Keras 是一個模型級(model-level)的庫,為開發深度學習模型提供了高層次的構模組化塊。 它不處理張量操作、求微分等低層次的運算。相反,它依賴于一個專門的、高度優化的張量庫 來完成這些運算,這個張量庫就是Keras 的後端引擎(backend engine)。 Keras 沒有選擇單個張 量庫并将Keras 實作與這個庫綁定,而是以子產品化的方式處理這個問題(見圖3-3)。是以,幾 個不同的後端引擎都可以無縫嵌入到 Keras 中。目前,Keras 有三個後端實作:TensorFlow 後端、 Theano 後端和微軟認知工具包(CNTK,Microsoft cognitive toolkit)後端。未來 Keras 可能會擴 展到支援更多的深度學習引擎。

基于Keras的IMDB資料集的損失率和準确率對比實戰: 

TensorFlow、CNTK 和 Theano 是當今深度學習的幾個主要平台。Theano 由蒙特利爾大學的 MILA 實驗室開發,TensorFlow 由 Google 開發,CNTK 由微軟開發。你用Keras 寫的每一段代 碼都可以在這三個後端上運作,無須任何修改。也就是說,你在開發過程中可以在兩個後端之 間無縫切換,這通常是很有用的。例如,對于特定任務,某個後端的速度更快,那麼我們就可以無縫切換過去。我們推薦使用TensorFlow 後端作為大部分深度學習任務的預設後端,因為它 的應用最廣泛,可擴充,而且可用于生産環境。 通過 TensorFlow(或 Theano、CNTK), Keras 可以在 CPU 和GPU 上無縫運作。在 CPU 上運作 時,TensorFlow 本身封裝了一個低層次的張量運算庫,叫作Eigen;在GPU 上運作時,TensorFlow 封裝了一個高度優化的深度學習運算庫,叫作 NVIDIA CUDA 深度神經網絡庫(cuDNN)。

使用 Keras 開發:概述 

 你已經見過一個Keras 模型的示例,就是MNIST 的例子。典型的Keras 工作流程就和那個 例子類似。 (1) 定義訓練資料:輸入張量和目标張量。 (2) 定義層組成的網絡(或模型),将輸入映射到目标。 (3) 配置學習過程:選擇損失函數、優化器和需要監控的名額。 (4) 調用模型的 fit 方法在訓練資料上進行疊代。 定義模型有兩種方法:一種是使用 Sequential 類(僅用于層的線性堆疊,這是目前最常 見的網絡架構),另一種是函數式 API(functional API,用于層組成的有向無環圖,讓你可以構 建任意形式的架構)。 前面講過,這是一個利用 Sequential 類定義的兩層模型(注意,我們向第一層傳入了輸 入資料的預期形狀)。

from keras import models 
from keras import layers 
 
model = models.Sequential() 
model.add(layers.Dense(32, activation='relu', input_shape=(784,)))
model.add(layers.Dense(10, activation='softmax')) 下面是用函數式 API 定義的相同模型。
input_tensor = layers.Input(shape=(784,)) x = layers.Dense(32, activation='relu')(input_tensor) output_tensor = layers.Dense(10, activation='softmax')(x) 
 
model = models.Model(inputs=input_tensor, outputs=output_tensor) 
           

利用函數式API,你可以操縱模型處理的資料張量,并将層應用于這個張量,就好像這些 層是函數一樣。

一旦定義好了模型架構,使用 Sequential 模型還是函數式API 就不重要了。接下來的步 驟都是相同的。 配置學習過程是在編譯這一步,你需要指定模型使用的優化器和損失函數,以及訓練過程 中想要監控的名額。下面是單一損失函數的例子,這也是目前最常見的。

from keras import optimizers 
 
model.compile(optimizer=optimizers.RMSprop(lr=0.001),          
                loss='mse',               
                metrics=['accuracy']) 
           

最後,學習過程就是通過 fit() 方法将輸入資料的Numpy 數組(和對應的目标資料)傳 入模型,這一做法與 Scikit-Learn 及其他機器學習庫類似。

model.fit(input_tensor, target_tensor, batch_size=128, epochs=10)
           

準備資料

 你不能将整數序列直接輸入神經網絡。你需要将清單轉換為張量。轉換方法有以下兩種。 

  • 填充清單,使其具有相同的長度,再将清單轉換成形狀為 (samples, word_indices) 的整數張量,然後網絡第一層使用能處理這種整數張量的層。
  • 對清單進行 one-hot 編碼,将其轉換為 0 和 1 組成的向量。舉個例子,序列 [3, 5] 将會 被轉換為10 000 維向量,隻有索引為3 和 5 的元素是1,其餘元素都是0。然後網絡第 一層可以用 Dense 層,它能夠處理浮點數向量資料。 

 将整數序列編碼為二進制矩陣

import numpy as np 
 
def vectorize_sequences(sequences, dimension=10000):     
    results = np.zeros((len(sequences), dimension))      
    for i, sequence in enumerate(sequences):         
        results[i, sequence] = 1.      
    return results 
 
x_train = vectorize_sequences(train_data)   
x_test = vectorize_sequences(test_data)  
樣本現在變成了這樣: >>> x_train[0] array([ 0.,  1.,  1., ...,  0.,  0.,  0.])
你還應該将标簽向量化,這很簡單。 
y_train = np.asarray(train_labels).astype('float32') 
y_test = np.asarray(test_labels).astype('float32')
           

 建構網絡

 輸入資料是向量,而标簽是标量(1 和 0),這是你會遇到的最簡單的情況。有一類網絡在這種問題上表現很好,就是帶有relu 激活的全連接配接層(Dense)的簡單堆疊,比如 Dense(16, activation='relu')。 傳入 Dense 層的參數(16)是該層隐藏單元的個數。一個隐藏單元(hidden unit)是該層 表示空間的一個次元。我們在第2 章講過,每個帶有 relu 激活的 Dense 層都實作了下列張量 運算:

output = relu(dot(W, input) + b) 
           

16 個隐藏單元對應的權重矩陣 W 的形狀為 (input_dimension, 16),與 W 做點積相當于 将輸入資料投影到16 維表示空間中(然後再加上偏置向量 b 并應用 relu 運算)。你可以将表 示空間的次元直覺地了解為“網絡學習内部表示時所擁有的自由度”。隐藏單元越多(即更高維 的表示空間),網絡越能夠學到更加複雜的表示,但網絡的計算代價也變得更大,而且可能會導 緻學到不好的模式(這種模式會提高訓練資料上的性能,但不會提高測試資料上的性能)。

對于這種 Dense 層的堆疊,你需要确定以下兩個關鍵架構:

  • 網絡有多少層;
  •  每層有多少個隐藏單元。
  • 現在你隻需要相信我選擇的下列架構: 兩個中間層,每層都有 16 個隐藏單元;
  • 第三層輸出一個标量,預測目前評論的情感。 中間層使用 relu 作為激活函數,最後一層使用 sigmoid 激活以輸出一個 0~1 範圍内的機率 值(表示樣本的目标值等于1 的可能性,即評論為正面的可能性)。relu(rectified linear unit, 整流線性單元)函數将所有負值歸零(見圖3-4),而 sigmoid 函數則将任意值“壓縮”到 [0, 1] 區間内(見圖 3-5),其輸出值可以看作機率值。
基于Keras的IMDB資料集的損失率和準确率對比實戰: 
基于Keras的IMDB資料集的損失率和準确率對比實戰: 

下圖顯示了網絡的結構。

基于Keras的IMDB資料集的損失率和準确率對比實戰: 

 模型定義

from keras import models 
from keras import layers 
 
model = models.Sequential() 
model.add(layers.Dense(16, activation='relu', input_shape=(10000,))) model.add(layers.Dense(16, activation='relu')) 
model.add(layers.Dense(1, activation='sigmoid'))
           

什麼是激活函數?為什麼要使用激活函數?

如果沒有 relu 等激活函數(也叫非線性), Dense 層将隻包含兩個線性運算——點積 和加法:

output = dot(W, input) + b

這樣 Dense 層就隻能學習輸入資料的線性變換(仿射變換):該層的假設空間是從輸 入資料到16 位空間所有可能的線性變換集合。這種假設空間非常有限,無法利用多個表示 層的優勢,因為多個線性層堆疊實作的仍是線性運算,添加層數并不會擴充假設空間。 為了得到更豐富的假設空間,進而充分利用多層表示的優勢,你需要添加非線性或激 活函數。relu 是深度學習中最常用的激活函數,但還有許多其他函數可選,它們都有類似 的奇怪名稱,比如 prelu、elu 等。

 最後,你需要選擇損失函數和優化器。由于你面對的是一個二分類問題,網絡輸出是一 個機率值(網絡最後一層使用sigmoid 激活函數,僅包含一個單元),那麼最好使用 binary_ crossentropy(二進制交叉熵)損失。這并不是唯一可行的選擇,比如你還可以使用mean_ squared_error(均方誤差)。但對于輸出機率值的模型,交叉熵(crossentropy)往往是最好 的選擇。交叉熵是來自于資訊論領域的概念,用于衡量機率分布之間的距離,在這個例子中就 是真實分布與預測值之間的距離。 

編譯模型

from keras import optimizers 
 
model.compile(optimizer=optimizers.RMSprop(lr=0.001),                           
              loss='binary_crossentropy',               
              metrics=['accuracy'])
           

實戰: 

将資料集劃分成訓練集和測試集,訓練之後選用測試集驗證損失率和分類準确率

from keras.datasets import imdb
import numpy as np
from keras import models
from keras import layers
import matplotlib.pyplot as plt
def main():
    (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
    word_index = imdb.get_word_index()
    # print(word_index)
    reverse_word_index = dict(
        [(value,key) for (key,value) in word_index.items()]
    )
    decoded_review = ' '.join(
        [reverse_word_index.get(i-3,'?') for i in train_data[0]]
    )
    print(decoded_review)

def vectorize_sequences(sequences,dimension = 10000):
    results = np.zeros((len(sequences),dimension))
    for i,sequences in enumerate(sequences):
        results[i,sequences] = 1.
    return results

def train():
    (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
    x_train = vectorize_sequences(train_data)
    x_test  = vectorize_sequences(test_data)
    y_train = np.asarray(train_labels).astype('float32')
    y_test  = np.asarray(test_labels).astype('float32')

    x_val = x_train[:10000]
    partial_train_data = x_train[10000:]
    y_val = y_train[:10000]
    partial_train_labels = y_train[10000:]


    model = models.Sequential()
    model.add(layers.Dense(16,activation='relu',input_shape=(10000,)))
    model.add(layers.Dense(16,activation='relu'))
    model.add(layers.Dense(1,activation='sigmoid'))

    model.compile(optimizer='rmsprop',
                  loss='binary_crossentropy',
                  metrics=['accuracy'])
    history = model.fit(partial_train_data,
                        partial_train_labels,
                        epochs=20,
                        batch_size=512,
                        validation_data=(x_val, y_val))

    history_dict = history.history
    print(history_dict)
    print(history_dict.keys())

    loss_values = history_dict['loss']
    val_loss_values = history_dict['val_loss']

    epochs = range(1,len(loss_values) + 1)

    plt.plot(epochs,loss_values,'bo',label = 'Training loss')
    plt.plot(epochs,val_loss_values,'b',label ='Validation loss')
    plt.title('Training and validation loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()

    plt.show()


if __name__ == '__main__':

    # main()
    train()

    # print(train_data[0])

           
基于Keras的IMDB資料集的損失率和準确率對比實戰: 
基于Keras的IMDB資料集的損失率和準确率對比實戰: 

繼續閱讀