06.27 深度學習:將新聞報道按照不同話題性質進行分類

深度學習的廣泛運用之一就是對文本按照其內容進行分類。例如對新聞報道根據其性質進行劃分是常見的應用領域。在本節,我們要把路透社自1986年以來的新聞數據按照46個不同話題進行劃分。網絡經過訓練後,它能夠分析一篇新聞稿,然後按照其報道內容,將其歸入到設定好的46個話題之一。深度學習在這方面的應用屬於典型的“單標籤,多類別劃分”的文本分類應用。

我們這裡採用的數據集來自於路透社1986年以來的報道,數據中每一篇新聞稿附帶一個話題標籤,以用於網絡訓練,每一個話題至少含有10篇文章,某些報道它內容很明顯屬於給定話題,有些報道會模稜兩可,不好確定它到底屬於哪一種類的話題,我們先把數據加載到機器裡,代碼如下:

from keras.datasets import reuters
(train_data, train_label), (test_data, test_labels) = reuters.load_data(num_words=10000)

keras框架直接附帶了相關數據集,通過執行上面代碼就可以將數據下載下來。上面代碼運行後結果如下:

深度學習:將新聞報道按照不同話題性質進行分類

從上面運行結果看,它總共有8982條訓練數據和2246條測試數據。跟我們上節數據類型一樣,數據裡面對應的是每個單詞的頻率編號,我們可以通過上一節類似的代碼,將編號對應的單詞從字典中抽取出來結合成一篇文章,代碼如下:

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)

上面代碼運行後結果如下:

深度學習:將新聞報道按照不同話題性質進行分類

如同上一節,我們必須要把訓練數據轉換成數據向量才能提供給網絡進行訓練,因此我們像上一節一樣,對每條新聞創建一個長度為一萬的向量,先把元素都初始為0,然後如果某個對應頻率的詞在文本中出現,那麼我們就在向量中相應下標設置為1,代碼如下:

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)
print(x_train[0])

上面代碼運行後,我們就把訓練數據變成含有1或0的向量了:

深度學習:將新聞報道按照不同話題性質進行分類

其實我們可以直接調用keras框架提供的接口一次性方便簡單的完成:

from keras.utils.np_utils import to_categorical
one_hot_train_labels = to_categorical(train_label)
one_hot_test_labels = to_categorical(test_labels)

接下來我們可以著手構建分析網絡,網絡的結構與上節很像,因為要解決的問題性質差不多,都是對文本進行分析。然而有一個重大不同在於,上一節我們只讓網絡將文本劃分成兩種類別,而這次我們需要將文本劃分為46個類別!上一節我們構造網絡時,中間層網絡我們設置了16個神經元,由於現在我們需要在最外層輸出46個結果,因此中間層如果只設置16個神經元那就不夠用,由於輸出的信息太多,如果中間層神經元數量不足,那麼他就會成為信息過濾的瓶頸,因此這次我們搭建網絡時,中間層網絡節點擴大為6個,代碼如下:

from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
#當結果是輸出多個分類的概率時,用softmax激活函數,它將為46個分類提供不同的可能性概率值
model.add(layers.Dense(46, activation='softmax'))
#對於輸出多個分類結果,最好的損失函數是categorical_crossentropy

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

像上一節一樣,在網絡訓練時我們要設置校驗數據集,因為網絡並不是訓練得次數越多越好,有了校驗數據集,我們就知道網絡在訓練幾次的情況下能夠達到最優狀態,準備校驗數據集的代碼如下:

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=20, batch_size=512, 
validation_data=(x_val, y_val))

代碼進行了20個週期的循環訓練,由於數據量比上一節小,因此速度快很多,與上一節一樣,網絡的訓練並不是越多越好,它會有一個拐點,訓練次數超出後,效果會越來越差,我們把訓練數據圖形化,以便觀察拐點從哪裡開始:

import matplotlib.pyplot as plt
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

上面代碼運行後結果如下:

深度學習:將新聞報道按照不同話題性質進行分類

通過上圖觀察我們看到,以藍點表示的是網絡對訓練數據的判斷準確率,該準確率一直在不斷下降,但是藍線表示的是網絡對校驗數據判斷的準確率,仔細觀察發現,它一開始是迅速下降的,過了某個點,達到最低點後就開始上升,這個點大概是在epochs=9那裡,所以我們把前面對網絡訓練的循環次數減少到9:

from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
#當結果是輸出多個分類的概率時,用softmax激活函數,它將為46個分類提供不同的可能性概率值
model.add(layers.Dense(46, activation='softmax'))
#對於輸出多個分類結果,最好的損失函數是categorical_crossentropy
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
history = model.fit(partial_x_train, partial_y_train, epochs=9, batch_size=512,
validation_data=(x_val, y_val))

完成訓練後,我們把結果輸出看看:

results = model.evaluate(x_test, one_hot_test_labels)
print(results)

上面兩句代碼運行結果為:

深度學習:將新聞報道按照不同話題性質進行分類

右邊0.78表示,我們網絡對新聞進行話題分類的準確率達到78%,差一點到80%。我們從測試數據集中拿出一條數據,讓網絡進行分類,得到結果再與其對應的正確結果比較看看是否一致:

predictions = model.predict(x_test)
print(predictions[0])
print(np.sum(predictions[0]))
print(np.argmax(predictions[0]))
print(one_hot_test_labels[0])

我們讓網絡對每一條測試數據一一進行判斷,並把它對第一條數據的判斷結果顯示出來,最後我們打印出第一條測試數據對應的分類,最後看看網絡給出去的結果與正確結果是否一致,上面代碼運行後結果如下:

深度學習:將新聞報道按照不同話題性質進行分類

從上面運行結果看到,網絡對第一條數據給出了屬於46個分類的概率,其中下標為3的概率值最大,也就是第一條數據屬於分類4的概率最大,最後打印出來的測試數據對應的正確結果來看,它也是下標為3的元素值為1,也就是說數據對應的正確分類是4,由此我們網絡得到的結果是正確的。

前面提到過,由於網絡最終輸出結果包含46個元素,因此中間節點的神經元數目不能小於46,因為小於46,那麼有關46個元素的信息就會遭到擠壓,於是在層層運算後會導致信息丟失,最後致使最終結果的準確率下降,我們試試看是不是這樣:

from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(4, activation='relu'))
#當結果是輸出多個分類的概率時,用softmax激活函數,它將為46個分類提供不同的可能性概率值
model.add(layers.Dense(46, activation='softmax'))
#對於輸出多個分類結果,最好的損失函數是categorical_crossentropy
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
history = model.fit(partial_x_train, partial_y_train, epochs=9, batch_size=512,
validation_data=(x_val, y_val))
results = model.evaluate(x_test, one_hot_test_labels)
print(results)

上面代碼運行後,輸出的results結果如下:

[1.4625472680649796, 0.6705253784505788]

從上面結果看到,我們代碼幾乎沒變,致使把第二層中間層神經元數量改成4,最終結果的準確率就下降10個點,所以中間層神經元的減少導致信息壓縮後,最後計算的準確度缺失。反過來你也可以試試用128個神經元的中間層看看準確率有沒有提升。

到這裡不知道你發現沒有,神經網絡在實際項目中的運用有點類似於樂高積木,你根據實際需要,通過選定參數,用幾行代碼配置好基本的網絡結構,把訓練數據改造成合適的數字向量,然後就可以輸入到網絡中進行訓練,訓練過程中記得用校驗數據監測最優訓練次數,防止過度擬合。

在網絡的設計過程中,其背後的數學原理我們幾乎無需瞭解,只需要憑藉經驗,根據項目的性質,設定網絡的各項參數,最關鍵的其實在根據項目數據性質對網絡進行調優,例如網絡設置幾層好,每層幾個神經元,用什麼樣的激活函數和損失函數等等,這些操作與技術無關,取決以個人經驗,屬於“藝術”的範疇。


分享到:


相關文章: