深度學(xué)習(xí)的廣泛運(yùn)用之一就是對文本按照其內(nèi)容進(jìn)行分類。例如對新聞報道根據(jù)其性質(zhì)進(jìn)行劃分是常見的應(yīng)用領(lǐng)域。在本節(jié),我們要把路透社自1986年以來的新聞數(shù)據(jù)按照46個不同話題進(jìn)行劃分。網(wǎng)絡(luò)經(jīng)過訓(xùn)練后妖异,它能夠分析一篇新聞稿,然后按照其報道內(nèi)容领追,將其歸入到設(shè)定好的46個話題之一他膳。深度學(xué)習(xí)在這方面的應(yīng)用屬于典型的“單標(biāo)簽,多類別劃分”的文本分類應(yīng)用绒窑。
我們這里采用的數(shù)據(jù)集來自于路透社1986年以來的報道棕孙,數(shù)據(jù)中每一篇新聞稿附帶一個話題標(biāo)簽,以用于網(wǎng)絡(luò)訓(xùn)練,每一個話題至少含有10篇文章蟀俊,某些報道它內(nèi)容很明顯屬于給定話題钦铺,有些報道會模棱兩可,不好確定它到底屬于哪一種類的話題肢预,我們先把數(shù)據(jù)加載到機(jī)器里矛洞,代碼如下:
from keras.datasets import reuters
(train_data, train_label), (test_data, test_labels) = reuters.load_data(num_words=10000)
keras框架直接附帶了相關(guān)數(shù)據(jù)集,通過執(zhí)行上面代碼就可以將數(shù)據(jù)下載下來烫映。上面代碼運(yùn)行后結(jié)果如下:
從上面運(yùn)行結(jié)果看缚甩,它總共有8982條訓(xùn)練數(shù)據(jù)和2246條測試數(shù)據(jù)。跟我們上節(jié)數(shù)據(jù)類型一樣窑邦,數(shù)據(jù)里面對應(yīng)的是每個單詞的頻率編號擅威,我們可以通過上一節(jié)類似的代碼,將編號對應(yīng)的單詞從字典中抽取出來結(jié)合成一篇文章冈钦,代碼如下:
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)
上面代碼運(yùn)行后結(jié)果如下:
如同上一節(jié)郊丛,我們必須要把訓(xùn)練數(shù)據(jù)轉(zhuǎn)換成數(shù)據(jù)向量才能提供給網(wǎng)絡(luò)進(jìn)行訓(xùn)練,因此我們像上一節(jié)一樣瞧筛,對每條新聞創(chuàng)建一個長度為一萬的向量厉熟,先把元素都初始為0,然后如果某個對應(yīng)頻率的詞在文本中出現(xiàn)较幌,那么我們就在向量中相應(yīng)下標(biāo)設(shè)置為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])
上面代碼運(yùn)行后,我們就把訓(xùn)練數(shù)據(jù)變成含有1或0的向量了:
其實(shí)我們可以直接調(diào)用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)
接下來我們可以著手構(gòu)建分析網(wǎng)絡(luò)乍炉,網(wǎng)絡(luò)的結(jié)構(gòu)與上節(jié)很像绢片,因?yàn)橐鉀Q的問題性質(zhì)差不多,都是對文本進(jìn)行分析岛琼。然而有一個重大不同在于底循,上一節(jié)我們只讓網(wǎng)絡(luò)將文本劃分成兩種類別,而這次我們需要將文本劃分為46個類別槐瑞!上一節(jié)我們構(gòu)造網(wǎng)絡(luò)時熙涤,中間層網(wǎng)絡(luò)我們設(shè)置了16個神經(jīng)元,由于現(xiàn)在我們需要在最外層輸出46個結(jié)果困檩,因此中間層如果只設(shè)置16個神經(jīng)元那就不夠用祠挫,由于輸出的信息太多,如果中間層神經(jīng)元數(shù)量不足悼沿,那么他就會成為信息過濾的瓶頸等舔,因此這次我們搭建網(wǎng)絡(luò)時,中間層網(wǎng)絡(luò)節(jié)點(diǎn)擴(kuò)大為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'))
#當(dāng)結(jié)果是輸出多個分類的概率時软瞎,用softmax激活函數(shù),它將為46個分類提供不同的可能性概率值
model.add(layers.Dense(46, activation='softmax'))
#對于輸出多個分類結(jié)果逢唤,最好的損失函數(shù)是categorical_crossentropy
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
像上一節(jié)一樣,在網(wǎng)絡(luò)訓(xùn)練時我們要設(shè)置校驗(yàn)數(shù)據(jù)集涤浇,因?yàn)榫W(wǎng)絡(luò)并不是訓(xùn)練得次數(shù)越多越好鳖藕,有了校驗(yàn)數(shù)據(jù)集,我們就知道網(wǎng)絡(luò)在訓(xùn)練幾次的情況下能夠達(dá)到最優(yōu)狀態(tài)只锭,準(zhǔn)備校驗(yàn)數(shù)據(jù)集的代碼如下:
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:]
有了數(shù)據(jù)著恩,就相當(dāng)于有米入鍋,我們可以把數(shù)據(jù)輸入網(wǎng)絡(luò)進(jìn)行訓(xùn)練:
history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512,
validation_data=(x_val, y_val))
代碼進(jìn)行了20個周期的循環(huán)訓(xùn)練蜻展,由于數(shù)據(jù)量比上一節(jié)小喉誊,因此速度快很多,與上一節(jié)一樣纵顾,網(wǎng)絡(luò)的訓(xùn)練并不是越多越好伍茄,它會有一個拐點(diǎn),訓(xùn)練次數(shù)超出后施逾,效果會越來越差敷矫,我們把訓(xùn)練數(shù)據(jù)圖形化,以便觀察拐點(diǎn)從哪里開始:
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()
上面代碼運(yùn)行后結(jié)果如下:
通過上圖觀察我們看到汉额,以藍(lán)點(diǎn)表示的是網(wǎng)絡(luò)對訓(xùn)練數(shù)據(jù)的判斷準(zhǔn)確率曹仗,該準(zhǔn)確率一直在不斷下降,但是藍(lán)線表示的是網(wǎng)絡(luò)對校驗(yàn)數(shù)據(jù)判斷的準(zhǔn)確率蠕搜,仔細(xì)觀察發(fā)現(xiàn)怎茫,它一開始是迅速下降的,過了某個點(diǎn)妓灌,達(dá)到最低點(diǎn)后就開始上升轨蛤,這個點(diǎn)大概是在epochs=9那里,所以我們把前面對網(wǎng)絡(luò)訓(xùn)練的循環(huán)次數(shù)減少到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'))
#當(dāng)結(jié)果是輸出多個分類的概率時旬渠,用softmax激活函數(shù),它將為46個分類提供不同的可能性概率值
model.add(layers.Dense(46, activation='softmax'))
#對于輸出多個分類結(jié)果俱萍,最好的損失函數(shù)是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))
完成訓(xùn)練后端壳,我們把結(jié)果輸出看看:
results = model.evaluate(x_test, one_hot_test_labels)
print(results)
上面兩句代碼運(yùn)行結(jié)果為:
右邊0.78表示告丢,我們網(wǎng)絡(luò)對新聞進(jìn)行話題分類的準(zhǔn)確率達(dá)到78%,差一點(diǎn)到80%损谦。我們從測試數(shù)據(jù)集中拿出一條數(shù)據(jù)岖免,讓網(wǎng)絡(luò)進(jìn)行分類,得到結(jié)果再與其對應(yīng)的正確結(jié)果比較看看是否一致:
predictions = model.predict(x_test)
print(predictions[0])
print(np.sum(predictions[0]))
print(np.argmax(predictions[0]))
print(one_hot_test_labels[0])
我們讓網(wǎng)絡(luò)對每一條測試數(shù)據(jù)一一進(jìn)行判斷照捡,并把它對第一條數(shù)據(jù)的判斷結(jié)果顯示出來颅湘,最后我們打印出第一條測試數(shù)據(jù)對應(yīng)的分類,最后看看網(wǎng)絡(luò)給出去的結(jié)果與正確結(jié)果是否一致栗精,上面代碼運(yùn)行后結(jié)果如下:
從上面運(yùn)行結(jié)果看到闯参,網(wǎng)絡(luò)對第一條數(shù)據(jù)給出了屬于46個分類的概率瞻鹏,其中下標(biāo)為3的概率值最大,也就是第一條數(shù)據(jù)屬于分類4的概率最大鹿寨,最后打印出來的測試數(shù)據(jù)對應(yīng)的正確結(jié)果來看新博,它也是下標(biāo)為3的元素值為1,也就是說數(shù)據(jù)對應(yīng)的正確分類是4脚草,由此我們網(wǎng)絡(luò)得到的結(jié)果是正確的赫悄。
前面提到過,由于網(wǎng)絡(luò)最終輸出結(jié)果包含46個元素馏慨,因此中間節(jié)點(diǎn)的神經(jīng)元數(shù)目不能小于46埂淮,因?yàn)樾∮?6,那么有關(guān)46個元素的信息就會遭到擠壓写隶,于是在層層運(yùn)算后會導(dǎo)致信息丟失倔撞,最后致使最終結(jié)果的準(zhǔn)確率下降,我們試試看是不是這樣:
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'))
#當(dāng)結(jié)果是輸出多個分類的概率時慕趴,用softmax激活函數(shù),它將為46個分類提供不同的可能性概率值
model.add(layers.Dense(46, activation='softmax'))
#對于輸出多個分類結(jié)果误窖,最好的損失函數(shù)是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)
上面代碼運(yùn)行后,輸出的results結(jié)果如下:
[1.4625472680649796, 0.6705253784505788]
從上面結(jié)果看到秩贰,我們代碼幾乎沒變霹俺,致使把第二層中間層神經(jīng)元數(shù)量改成4,最終結(jié)果的準(zhǔn)確率就下降10個點(diǎn)毒费,所以中間層神經(jīng)元的減少導(dǎo)致信息壓縮后丙唧,最后計算的準(zhǔn)確度缺失。反過來你也可以試試用128個神經(jīng)元的中間層看看準(zhǔn)確率有沒有提升想际。
到這里不知道你發(fā)現(xiàn)沒有,神經(jīng)網(wǎng)絡(luò)在實(shí)際項目中的運(yùn)用有點(diǎn)類似于樂高積木溪厘,你根據(jù)實(shí)際需要胡本,通過選定參數(shù),用幾行代碼配置好基本的網(wǎng)絡(luò)結(jié)構(gòu)畸悬,把訓(xùn)練數(shù)據(jù)改造成合適的數(shù)字向量侧甫,然后就可以輸入到網(wǎng)絡(luò)中進(jìn)行訓(xùn)練,訓(xùn)練過程中記得用校驗(yàn)數(shù)據(jù)監(jiān)測最優(yōu)訓(xùn)練次數(shù)蹋宦,防止過度擬合披粟。
在網(wǎng)絡(luò)的設(shè)計過程中,其背后的數(shù)學(xué)原理我們幾乎無需了解冷冗,只需要憑借經(jīng)驗(yàn)守屉,根據(jù)項目的性質(zhì),設(shè)定網(wǎng)絡(luò)的各項參數(shù)蒿辙,最關(guān)鍵的其實(shí)在根據(jù)項目數(shù)據(jù)性質(zhì)對網(wǎng)絡(luò)進(jìn)行調(diào)優(yōu)拇泛,例如網(wǎng)絡(luò)設(shè)置幾層好滨巴,每層幾個神經(jīng)元,用什么樣的激活函數(shù)和損失函數(shù)等等俺叭,這些操作與技術(shù)無關(guān)兢卵,取決以個人經(jīng)驗(yàn),屬于“藝術(shù)”的范疇绪颖。
更詳細(xì)的講解和代碼調(diào)試演示過程秽荤,請點(diǎn)擊鏈接
更多技術(shù)信息,包括操作系統(tǒng)柠横,編譯器窃款,面試算法,機(jī)器學(xué)習(xí)牍氛,人工智能晨继,請關(guān)照我的公眾號: