Reuters數(shù)據(jù)集下載速度慢,可以在我的repo庫中找到下載,下載后放到~/.keras/datasets/目錄下,即可正常運行。
完整代碼 歡迎Fork、Star
構建神經(jīng)網(wǎng)絡將路透社新聞分類,一共有46個類別。因為有多個類別,屬于多分類問題,而每條數(shù)據(jù)只屬于一個類別,所以是單標簽多分類問題;如果每條數(shù)據(jù)可以被分到多個類別中,那問題則屬于多標簽多分類問題。
路透社數(shù)據(jù)集
Reuters數(shù)據(jù)集發(fā)布在1986年,一系列短新聞及對應話題的數(shù)據(jù)集;是文本分類問題最常用的小數(shù)據(jù)集。和IMDB、MNIST數(shù)據(jù)集類似,Reuters數(shù)據(jù)集也可以通過Keras直接下載。
加載數(shù)據(jù)集
from keras.datasets import reuters
(train_data,train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)
有8982條訓練集,2246條測試集。
每個樣本表示成整數(shù)列表。
>>> train_data[10]
[1, 245, 273, 207, 156, 53, 74, 160, 26, 14, 46, 296, 26, 39, 74, 2979,
3554, 14, 46, 4689, 4329, 86, 61, 3499, 4795, 14, 61, 451, 4329, 17, 12]
也可以將整數(shù)列表轉(zhuǎn)換成原始數(shù)據(jù)[英文句子]
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]]) #偏移3個:0,1,2保留下標,分別表示:“padding,” “start of sequence,” and “unknown.”
準備數(shù)據(jù)
整數(shù)數(shù)據(jù)向量化,與IMDB數(shù)據(jù)集處理方法相同。
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)
標簽的向量化有兩種方法:將標簽列表轉(zhuǎn)換成整數(shù)張量;使用one-hot編碼。One-hot編碼方式是類別數(shù)據(jù)常用的一種數(shù)據(jù)格式,也稱為categorical encoding。
def to_one_hot(labels, dimension=46):# 46個類別
results = np.zeros((len(labels), dimension))
for i, label in enumerate(labels):
results[i, label] = 1.
return results
one_hot_train_labels = to_one_hot(train_labels)
one_hot_test_labels = to_one_hot(test_labels)
Keras中有一個內(nèi)置的One-hot編碼轉(zhuǎn)換函數(shù):
from keras.utils.np_utils import to_categorical
one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)
模型搭建
使用Dense線性連接堆棧結構,每層網(wǎng)絡只能處理上層網(wǎng)絡的輸出結果。如果網(wǎng)絡層丟失了一些關于分類問題的信息,那么下一層網(wǎng)絡并不能恢復這些信息:每個網(wǎng)絡層潛在地成為一個信息處理瓶頸。
網(wǎng)絡定義
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'))
model.add(layers.Dense(46, activation='softmax'))
關于這個網(wǎng)絡架構有兩點需要注意:
- 最后一層網(wǎng)絡神經(jīng)元數(shù)目為46.意味著每個輸入樣本最終變成46維的向量。輸出向量的每個數(shù)表示不同的類別;
- 最后一層網(wǎng)絡使用softmax激活函數(shù)--網(wǎng)絡會輸出一個46類的概率分布。每個輸入最終都會產(chǎn)生一個46維的向量,每個數(shù)表示屬于該類別的概率,46個數(shù)加起來等于1.
最好的損失函數(shù)為categorical_crossentropy---衡量兩個概率分布之間的距離:網(wǎng)絡的輸出向量和標簽的真實分布向量。通過最小化兩個分布之間的距離,訓練網(wǎng)絡模型,使得輸出向量盡可能與真實分布相似。
model.compile(optimizer='rmsprop',loss='categorical_crossentropy', metrics=['accuracy'])
模型驗證
在訓練數(shù)據(jù)中分出1000條樣本做為驗證集。
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:]
訓練20個epochs
history = model.fit(partial_x_train,partial_y_train,epochs=20,batch_size=512,validation_data=(x_val, y_val))
訓練集和驗證集的損失值變化

訓練集和驗證集的準確率變化

模型在第9次epochs之后開始過擬合。我們將epochs設置為5重新訓練,同時在測試集上測試。
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.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)
# [0.9565213431445807, 0.79697239536954589] 損失值,準確率
準確率達到80%.比隨機猜測好。
預測新數(shù)據(jù)
使用predict函數(shù),產(chǎn)生一個46維的概率分布。在測試數(shù)據(jù)上進行預測:
predictions = model.predict(x_test)
在預測結果中概率最大的類別就是預測類:
np.argmax(predictions[0])#第一條新聞的預測類 4
另一種標簽、損失函數(shù)處理方式
直接將列表轉(zhuǎn)換成numpy數(shù)組
y_train = np.array(train_labels)
y_test = np.array(test_labels)
需要改變的是損失函數(shù)的選擇。categorical_crossentropy損失函數(shù)期望標簽數(shù)據(jù)使用categorical encoding編碼方式。整數(shù)標簽,應該使用sparse_categorical_crossentropy損失函數(shù):
model.compile(optimizer='rmsprop',loss='sparse_categorical_crossentropy',metrics=['acc'])
新的損失函數(shù)在數(shù)學表示上與categorical_crossentropy損失函數(shù)相同,只是接口不同。
有充分大規(guī)模中間層的重要性
因為最終分為46類,中間層的神經(jīng)元數(shù)目不應該小于46個。如果中間層數(shù)目小于46,有4個,將會產(chǎn)生信息瓶頸。
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(4, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])
model.fit(partial_x_train,partial_y_train,epochs=20,batch_size=128,validation_data=(x_val, y_val))
最終訓練結果最高為71%,降低了8個百分點。主要原始是模型試圖將大量的信息壓縮到低緯度空間中表示,丟失了大量重要的信息。
小結
- N分類問題,網(wǎng)絡最后Dense層神經(jīng)元數(shù)目為N;
- 單標簽多分類問題中,最后一層的激活函數(shù)為softmax,產(chǎn)生一個包含N類的概率分布;
- categorical crossentropy是處理單標簽多分類問題最常用的損失函數(shù);
- 在多分類問題中有兩種標簽處理方式:
- 使用categorical encoding(one-hot)編碼,將標簽one-hot化,同時使用categorical_crossentropy作為損失函數(shù);
- 編碼成整數(shù)向量,使用sparse_categorical_crossentropy作為損失函數(shù);
- 如果分類數(shù)目過大,應該避免網(wǎng)絡中間層數(shù)目過小(比分類數(shù)目小--信息壓縮),產(chǎn)生信息瓶頸。