2019年1月24日 星期四

深度學習 - IMDb數據集 - Embedding層添加


詞嵌入是自然語言處理(NLP)中語言模型與表徵學習技術的統稱。概念上而言,它涉及從每個單詞一維的空間到具有更低維度的連續向量空間的數學嵌入,每個單詞或詞組被映射為實數域上的向量。與one-hot 編碼得到的詞向量不同,詞嵌入是從數據中學習得到的。常見的詞向量維度是256、512 或1024(處理非常大的詞表時)。與此相對,one-hot 編碼的詞向量維度通常為 20000 或更高(對應包含20000 個標記的詞表)。因此,詞向量可以將更多的信息塞入更低的維度中。
one-hot 編碼得到的向量是二進制的、稀疏的(絕大部分元素都是0)、維度很高的(維度大小等於詞表中的單詞個數),而詞嵌入是低維的浮點數向量(即密集向量,與稀疏向量相對)

其中理論的部分,有空再來詳讀,先來實際練習將embedding層加入模型中,
以IMDb實際操作如下。

#加載IMDb 數據,準備用於Embedding 層
from keras.datasets import imdb
from keras import preprocessing
max_features = 10000 #特徵單詞的個數
maxlen = 20 #文本只截取前20個字(後續再討論)
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
#將整數列表轉換成形狀為(samples,maxlen) 的二維整數張量
x_train = preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = preprocessing.sequence.pad_sequences(x_test, maxlen=maxlen)

看一下轉換後的資料,只擷取文章的前20個字。
>>>x_train[0]
Out[3]:  array([  65,   16,   38, 1334,   88,   12,   16,  283,    5,   16, 4472,
        113,  103,   32,   15,   16, 5345,   19,  178,   32])

添加Embedding層,如要連接Dense層至少需要兩個參數:標記的個數(這裡是10000,即最
大單詞索引+1)和嵌入的維度(這裡是8),input_length取決於文本的長度(這裡給一個變數maxlen)
from keras.models import Sequential
from keras.layers import Flatten, Dense, Embedding
model = Sequential()
model.add(Embedding(10000, 8, input_length=maxlen))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))

看看model結構
>>>model.summary()

#選擇優化器、損失函數、指標
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
#丟進fit訓練
history = model.fit(x_train, y_train,epochs=10,batch_size=32,validation_split=0.2)


#將訓練過程畫出
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b+', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b+', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

可以得到驗證精度在75%左右,大概在epochs = 5 左右開始overfit。

接著我們來看訓練過程與maxlen的關係,在正常情況下,maxlen 越大越好,表示文章的完整性越高,但是整篇文章都丟進來訓練會浪費計算資源,那要取多少才好呢?
修改一下編碼,讓maxlen 從50~500跑看看驗證精度的變化,雖然時間有點久...我先去買個飲料先。

可以看到,當文本的內容大約200個字左右,精度就差不多在87~88%左右,其實這也是大部分film review文章的長度。
藉由此可以得知之後在調整訓練模型的時候,maxlen取值約200就好,不需要在更大了。

完整程式碼如下:
from keras.layers import Embedding
import numpy as np
embedding_layer = Embedding(1000, 64)
x_len_of_film_review=[] #紀錄maxlen長度
y_film_review_acc=[]  #記錄隨maxlen 長度變化的精度
from keras.datasets import imdb
from keras import preprocessing
#讓maxlen 從50~500 總共訓練11次看精度變化
for i in range(50,501,50):
    max_features = 10000    maxlen = i
    x_len_of_film_review.append(maxlen)  #紀錄maxlen長度
    (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)

    x_train = preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen)
    x_test = preprocessing.sequence.pad_sequences(x_test, maxlen=maxlen)

    from keras.models import Sequential
    from keras.layers import Flatten, Dense, Embedding
    model = Sequential()
    model.add(Embedding(10000, 8, input_length=maxlen))
    model.add(Flatten())
    model.add(Dense(1, activation='sigmoid'))
    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
    model.summary()

    history = model.fit(x_train, y_train,epochs=5,batch_size=32,validation_split=0.2)
    val_acc = history.history['val_acc']
    val_acc_mean = np.mean(val_acc)
    # 記錄隨maxlen 長度變化的精度 (取每次訓練平均值)
    y_film_review_acc.append(val_acc_mean)

import matplotlib.pyplot as plt
plt.plot(x_len_of_film_review, y_film_review_acc, 'bo')
plt.title('maxlen of film review vs val_acc')
plt.xlabel('maxlen of film review')
plt.ylabel('val_acc_mean')
plt.legend()
plt.show()




沒有留言:

張貼留言