基於Keras的imdb數據集電影評論情感二分類


IMDB數據集下載速度慢,可以在我的repo庫中找到下載,下載后放到~/.keras/datasets/目錄下,即可正常運行。)中找到下載,下載后放到~/.keras/datasets/目錄下,即可正常運行。

電影評論分類:二分類

二分類可能是機器學習最常解決的問題。我們將基於評論的內容將電影評論分類:正類和父類。

IMDB數據集

IMDB數據集有5萬條來自網絡電影數據庫的評論;其中2萬5千條用來訓練,2萬5千條用來測試,每個部分正負評論各占50%.
划分訓練集、測試集的必要性:不能在相同的數據集上對機器學習模型進行測試。因為在訓練集上模型表現好並不意味着泛化能力好(在沒有見過的數據上仍然表現良好),而我們關心的是模型的泛化能力.
和MNIST數據集類似,IMDB數據集也集成在Keras中,同時經過了預處理:電影評論轉換成了一系列數字,每個數字代表字典中的一個單詞。

加載數據集

from keras.datasets import imdb

(train_data,train_labels),(test_data,test_labels) = imdb.load_data(num_words=10000)

num_words=10000意味着只保留訓練集中最常出現的前10000個詞,不經常出現的單詞被拋棄---最終所有評論的維度保持相同。
變量train_data,test_data是電影評論的列表,每條評論由數字(對應單詞在詞典中出現的位置下標)列表組成。train_labels,test_labels是0,1列表,0負面評論,1表示正面評論。

>>> train_data[0]
[1, 14, 22, 16, ... 178, 32]
>>> train_labels[0]
1

預處理數據

不能直接將list類型的數據送到神經網絡中訓練,必須將list類型轉換為tensor張量類型。有兩種轉換方式:

  • 填充列表使每個列表長度都相同,然后轉換為整數類型的張量,形狀為(samples, word_indices),使用張量作為神經網絡的第一層(Embedding層,能處理這樣的整數類型張量);
  • 將列表進行one-hot編碼,轉換成0、 1向量。然后用Dense網絡層作為神經網絡的第一層,處理浮點類型向量數據。
import numpy as np

def vectorize_sequences(sequences, dimension=10000):
	results = np.zeros((len(sequences), dimension)) #數據集長度,每個評論維度10000
	for i, sequence in enumerate(sequences):
		results[i, sequence] = 1 # one-hot
	return results

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')

構建網絡模型

現在輸入數據是向量類型,對應標簽數據為標量(0, 1):最容易處理的數據格式。處理這類問題最常見的網絡模型是Dense網絡層的線性堆疊,Dense(16, activation='relu')。

Dense網絡層接收的參數16表示網絡層神經元數目。一個神經元單元表示網絡層表示空間的一個維度。16個神經元表示網絡層權重系數形狀為(input_dimensions, 16);dot點積運算將會把輸出張量轉換為16維度的表示張量。越多的神經元數目表示神經網絡可以學習到更加復雜的特征表示,相應的計算效率降低,也可能學到不必要的特征。
Dense網絡層堆疊時回答兩個關鍵的問題:

  • 有多少層網絡?
  • 每個層網絡有多少個神經元?

激活函數能給網絡模型增加非線性表示。
使用的網絡模型如下:

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'))

最后,選擇損失函數和優化算法。因為是二分類問題,網絡模型的輸出是一個概率,最好使用binary_crossentropy損失函數,也可以使用mean_squared_error均方誤差損失函數。但是交叉熵是處理模型輸出為概率時最佳選擇。
配置使用rmsprop優化算法以及binary_crossentropy損失函數,選擇監測的指標。

model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['accuracy'])

這里參數使用字符串傳遞,因為在keras中對應字符串已經定義好了。同時,可以傳遞函數類型給相應的參數。如:

from keras import losses
from keras import metrics

model.compile(optimizer=optimizers.RMSprop(lr=0.001),
			loss=losses.binary_crossentropy,
			metrics=[metrics.binary_accuracy])

模型驗證

為了在訓練過程中在沒有見過數據上監測准確率變化,需要分出一個驗證集。從訓練集中分出10000條做驗證集。

x_val = x_train[:10000]
partial_x_train = x_train[10000:]

y_val = y_train[:10000]
partial_y_train = y_train[10000:]

在512小批量,迭代20次進行模型訓練,同時在驗證集上監測准確率變化。

model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])

history = model.fit(partial_x_train,partial_y_train,epochs=20,batch_size=512,validation_data=(x_val, y_val))

通過調用model.fit()方法返回一個history對象,history對象有history屬性---一個包含在訓練過程中記錄的數據的字典。

>>> history_dict = history.history
>>> history_dict.keys()
[u'acc', u'loss', u'val_acc', u'val_loss']

字典包括4個鍵--在訓練、驗證過程中被監測的指標。

在圖中畫出訓練損失和驗證損失的變化圖:

import matplotlib.pyplot as plt

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

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

plt.plot(epochs, loss_values, 'bo', label='Training loss')#bo:blue dot藍點
plt.plot(epochs, val_loss_values, 'b', label='Validation loss')#b: blue藍色
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()

訓練集和驗證集上准確率變化:

plt.clf()

acc_values = history_dict['acc']
val_acc_values = history_dict['val_acc']
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()


可以從上面的兩張圖中看到,訓練損失逐漸降低,訓練准確度逐漸提高--這正是使用梯度下降算法,我們所期望看到的。但是,驗證損失和驗證集上的准確率卻在第4次迭代左右開始變差----模型在訓練集上表現良好,在沒有見過的數據上表現很差(泛化能力差)。用術語講,模型發生了過擬合。在這里可以,使模型在第4次epoch時,訓練結束,這樣來盡可能避免過擬合現象的發生。

重新訓練

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'])

model.fit(x_train,y_train,epochs=4,batch_size=512)
results = model.evaluate(x_test, y_test)
#[0.2929924130630493, 0.88327999999999995] 模型准確率為88%

使用訓練好的模型在新數據上做預測

使用訓練好的模型做預測----訓練模型的目的.
可以使用predict函數對數據進行預測,給出為正面評論的概率。

>>> model.predict(x_test)
array([[ 0.98006207]
[ 0.99758697]
[ 0.99975556]
...,
[ 0.82167041]
[ 0.02885115]
[ 0.65371346]], dtype=float32)

小結

  • 在把數據送到模型訓練之前,需要對原始數據進行預處理---將原始數據轉換成tensor張量格式。
  • 使用relu為激活函數的Dense網絡層的線性連接能解決很大一部分問題;
  • 在二分類問題中,網絡模型應該以包含一個神經元,激活函數為sigmoid的Dense層結束;輸出是介於0~1的標量,可以理解為概率。
  • 二分類問題,sigmoid標量輸出,對應損失函數應該選擇binary_crossentropy;
  • rmsprop優化算法大多數情況下是一個很好的選擇,無論問題是什么。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM