我覺得把課本上的案例先自己抄一遍,然后將書看一遍。最后再寫一篇博客記錄自己所學過程的感悟。雖然與課本有很多相似之處。但自己寫一遍感悟會更深
電影評論分類(二分類問題)
本節使用的是IMDB數據集,使用Jupyter作為編譯器。這是我剛開始使用Jupyter,不得不說它的自動補全真的不咋地(以前一直用pyCharm)但是看在能夠分塊運行代碼的份上,忍了。用pyCharm敲代碼確實很爽,但是調試不好調試(可能我沒怎么用心學),而且如果你完全不懂代碼含義的話,就算你運行成功也不知道其中的含義,代碼有點白敲的感覺,如果中途出現錯誤,有的時候很不好找。但是Jupyter就好一點,你可以使用多個cell,建議如果不打印一些東西,cell還是少一點,不然聯想功能特別弱,敲代碼特別難受。
1. 加載IMDB數據集
僅保留前10000個最常出現的單詞,低頻單詞被舍棄
from keras.datasets import imdb (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
train_data[0]
train_labels[0]
max([max(sequence) for sequence in train_data])
下面這段代碼:將某條評論迅速解碼為英文單詞
word_index = imdb.get_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]])
2. 將整數序列編碼為二進制矩陣
# 對列表進行one-hot編碼,將其轉換為0和1組成的向量
def vectorize_sequences(sequences, dimension=10000):
# 創建一個形狀為(len(sequences), dimension)的零矩陣
# 序列[3, 5]將會被轉換成10000維向量,只有索引3和5的元素為1,其余為0
results = np.zeros((len(sequences), dimension))
# 將results[i]指定索引設為1
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]
標簽向量化
y_train = np.asarray(train_labels).astype('float32') y_test = np.asarray(test_labels).astype('float32')
3. 模型定義
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'))
4. 編譯模型
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
5. 配置優化器
from keras import optimizers model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss='binary_crossentropy', metrics=['accuracy'])
6. 使用自定義的損失和指標
from keras import losses from keras import metrics model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss=losses.binary_crossentropy, metrics=[metrics.binary_crossentropy])
7. 留出驗證集
x_val = x_train[:10000] partial_x_train = x_train[10000:] y_val = y_train[:10000] partial_y_train = y_train[10000:]
8. 訓練模型
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)) history_dict = history.history history_dict.keys()
9. 繪制訓練損失和驗證損失
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(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()
損失降得太狠了,訓練的損失和精度不太重要,反應訓練集的訓練程度。重點是驗證精度
10. 繪制訓練精度和驗證精度
plt.clf() # 清除圖像 acc = history_dict['acc'] val_acc = 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('Accuracy') plt.legend() plt.show()
可以看到驗證的精度並不高,只有86%左右。而訓練的精度達到幾乎100%,兩者精度相差太大,出現了過擬合。為了防止過擬合,可以在3輪之后停止訓練。還有很多方法降低過擬合。我們一般看驗證精度曲線就是找最高點對應的輪次,然后從頭開始訓練一個新的模型
11. 從頭開始訓練一個模型
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)
最終結果如下:
results
[0.28940243008613586, 0.88488]
得到了88%的精度,還有待優化的空間
12. 使用訓練好的模型在新數據上生成預測結果
model.predict(x_test)
array([[0.20151292], [0.9997969 ], [0.9158534 ], ..., [0.1382984 ], [0.0817486 ], [0.69964325]], dtype=float32)
可見。網絡對某些樣本的結果是非常確信(大於等於0.99),但對其他結果卻不怎么確信
13. 總結
1. 加載數據集->對數據集進行預處理->模型定義->編譯模型->配置優化器->使用自定義的損失和指標->留出驗證集->訓練模型->繪制圖像
2. 對於二分類問題,網絡的最后一層應該是只有一個單元並使用sigmoid激活Dense層,網絡輸出應該是0~1范圍內的標量,表示概率值
3. 對於二分類問題的sigmoid標量輸出,應該使用binary_crossentropy(二元交叉熵)損失函數。