新聞分類:人工智能多分類問題

上一節我們提到了三個非常經典的問題,他們分別是:

  1. 二分類問題(電影評論好壞傾向性判斷)
  2. 多分類問題(將新聞按照主題分類)
  3. 迴歸問題(根據房地產數據估算房地產價格)

上一篇中,我們介紹了其中的二分類問題,這一篇我們介紹其中的多分類問題。如果你沒有閱讀過上一篇,請先閱讀上一篇,否則下文很多內容你會不知所云。現在我們開始今天的話題:

實際的背景是這樣的:路透社將新聞分爲了 46 個互斥的大類,一篇文章可能歸屬於其中的一類或多類,我們需要做的就是將新聞報道自動歸類。問題不是與上一篇一樣的非黑即白、非此即彼類型的判斷了,而是考慮每篇文章是不同的各個分類的概率。稍加思考,我們就會發現這個問題雖然與上個問題有如上的不同,但是其相同部分其實更多,我們只需根據不同的特殊情況進行一定的更改就好了。具體的內容下面分別說明,相同部分簡略說明,如有疑問請閱讀上篇文章:

  1. 數據與前文一樣,都可進行相同的初始化,即按照索引,將文章數據處理爲單詞索引的序列串,用 one-hot 方法處理向量使其可以爲網絡所處理。有區別的是這一次的結果,label 也需要處理,因爲結果不是兩個值,也是一個張量了。

  2. 仍然採用 relu 激活的中間層,投射的空間維度不能是 16 了,這裏改成 64,原因是因爲結果太多,用十六個維度去包含六十四個結果的信息,會在訓練的過程中丟失過多的信息,導致準確率會有較大的下降,因此這裏採用 64 層。

  3. 對於損失函數,上一篇的 binary_crossentropy 就不夠用了,需要修改損失函數,sparse_categorical_crossentropy 適用於多分類情況的損失函數,前者與後者之間只是接口上的不同,需要注意一下。

  4. 我們仍舊訓練 20 次,也出現了上次的問題,過擬合,只不過這一次是出現在第九次的迭代後,因此我們將迭代此處改爲九,重新訓練網絡。圖我放在了代碼的前面,可以查看:

    1. 最後一層的網絡,激活不應該用 sigmoid,而應該用 softmax,這樣才能輸出類別上的概率分佈。這一點與二分類分佈不一樣。
  5. 最後啓動訓練網絡,進行訓練,大致可以達到 80% 的準確度。

  6. 到這裏就結束了,但是還有兩個問題值得關注一下。我們用隨機分類器隨機對文章進行分類,準確率是 19%,上一篇中的二分類的可以達到 50%。我們最後每條測試數據得到的結果是一個 46 維度的向量,是各個類別的概率值,其相加爲 1(由於運算精度問題,最後其實是跟 1 有可能有一個很小的偏差),最大概率的類別就是我們的預測類別。相關代碼已經在最後給出,可以參考查看。

沒有更多新的東西了,就簡單介紹這些,圖片代碼如下給出:

image
image

#!/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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章