上一節我們提到了三個非常經典的問題,他們分別是:
- 二分類問題(電影評論好壞傾向性判斷)
- 多分類問題(將新聞按照主題分類)
- 迴歸問題(根據房地產數據估算房地產價格)
上一篇中,我們介紹了其中的二分類問題,這一篇我們介紹其中的多分類問題。如果你沒有閱讀過上一篇,請先閱讀上一篇,否則下文很多內容你會不知所云。現在我們開始今天的話題:
實際的背景是這樣的:路透社將新聞分爲了 46 個互斥的大類,一篇文章可能歸屬於其中的一類或多類,我們需要做的就是將新聞報道自動歸類。問題不是與上一篇一樣的非黑即白、非此即彼類型的判斷了,而是考慮每篇文章是不同的各個分類的概率。稍加思考,我們就會發現這個問題雖然與上個問題有如上的不同,但是其相同部分其實更多,我們只需根據不同的特殊情況進行一定的更改就好了。具體的內容下面分別說明,相同部分簡略說明,如有疑問請閱讀上篇文章:
數據與前文一樣,都可進行相同的初始化,即按照索引,將文章數據處理爲單詞索引的序列串,用 one-hot 方法處理向量使其可以爲網絡所處理。有區別的是這一次的結果,label 也需要處理,因爲結果不是兩個值,也是一個張量了。
仍然採用 relu 激活的中間層,投射的空間維度不能是 16 了,這裏改成 64,原因是因爲結果太多,用十六個維度去包含六十四個結果的信息,會在訓練的過程中丟失過多的信息,導致準確率會有較大的下降,因此這裏採用 64 層。
對於損失函數,上一篇的 binary_crossentropy 就不夠用了,需要修改損失函數,sparse_categorical_crossentropy 適用於多分類情況的損失函數,前者與後者之間只是接口上的不同,需要注意一下。
我們仍舊訓練 20 次,也出現了上次的問題,過擬合,只不過這一次是出現在第九次的迭代後,因此我們將迭代此處改爲九,重新訓練網絡。圖我放在了代碼的前面,可以查看:
- 最後一層的網絡,激活不應該用 sigmoid,而應該用 softmax,這樣才能輸出類別上的概率分佈。這一點與二分類分佈不一樣。
最後啓動訓練網絡,進行訓練,大致可以達到 80% 的準確度。
到這裏就結束了,但是還有兩個問題值得關注一下。我們用隨機分類器隨機對文章進行分類,準確率是 19%,上一篇中的二分類的可以達到 50%。我們最後每條測試數據得到的結果是一個 46 維度的向量,是各個類別的概率值,其相加爲 1(由於運算精度問題,最後其實是跟 1 有可能有一個很小的偏差),最大概率的類別就是我們的預測類別。相關代碼已經在最後給出,可以參考查看。
沒有更多新的東西了,就簡單介紹這些,圖片代碼如下給出:
#!/usr/bin/env python3
import copy
import numpy as np
from keras import layers
from keras import models
from keras.datasets import reuters
def classify():
(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)
# print('訓練集長度:', len(train_data))
# print('測試集長度:', len(test_data))
# print(train_data[10])
# 查看原文
# word_index = reuters.get_word_index()
# reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# decoded_newswire = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])
# print(decoded_newswire)
# 輸出某一標籤的分類索引
# print(train_labels[3])
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
# 將編碼轉換成張量
# y_train = np.array(train_labels)
# y_test = np.array(test_labels)
one_hot_train_labels = to_one_hot(train_labels)
one_hot_test_labels = to_one_hot(test_labels)
# one_hot_train_labels = to_categorical(train_labels)
# one_hot_test_labels = to_categorical(test_labels)
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
# model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['acc'])
# model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
x_val = x_train[:1000]
partial_x_train = x_train[1000:]
y_val = one_hot_train_labels[:1000]
partial_y_train = one_hot_train_labels[1000:]
history = model.fit(partial_x_train, partial_y_train, epochs=9, batch_size=512, validation_data=(x_val, y_val))
# 圖片輸出測試結果
# loss = history.history['loss']
# val_loss = history.history['val_loss']
# epochs = range(1, len(loss) + 1)
# plt.plot(epochs, loss, 'bo', label='訓練損失')
# plt.plot(epochs, val_loss, 'b', label='驗證損失')
# plt.title('訓練損失和驗證損失')
# plt.xlabel('迭代')
# plt.ylabel('精度')
# plt.legend()
# plt.show()
# plt.clf()
# acc = history.history['acc']
# val_acc = history.history['val_acc']
# plt.plot(epochs, acc, 'bo', label='訓練精度')
# plt.plot(epochs, val_acc, 'b', label='驗證精度')
# plt.title('訓練精度和驗證精度')
# plt.xlabel('迭代')
# plt.ylabel('精度')
# plt.legend()
# plt.show()
results = model.evaluate(x_test, one_hot_test_labels)
# 80%
print(results)
# 如果是隨機分類器
test_labels_copy = copy.copy(test_labels)
np.random.shuffle(test_labels_copy)
hits_array = np.array(test_labels) == np.array(test_labels_copy)
# 19%
print(float(np.sum(hits_array)) / len(test_labels))
# 預測值
predictions = model.predict(x_test)
print(predictions[10].shape)
print(np.sum(predictions[10]))
print(np.argmax(predictions[10]))
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1.
return results
def to_one_hot(labels, dimension=46):
results = np.zeros((len(labels), dimension))
for i, label in enumerate(labels):
results[i, label] = 1.
return results
if __name__ == "__main__":
classify()