好仍是壞:人工智能二分類問題

上一篇文章咱們介紹了深度學習的 Hello World,代碼寫起來相比其餘語言的 Hello World 有點多,且其背後的不少原理你可能尚未徹底弄懂,但從宏觀上來看,總體的思想是很好理解的。接下包括本篇在內的三篇文章,咱們來用深度學習解決三個實際問題,也是很是經典的三個問題,分別是:python

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

咱們解決這三個問題用到的訓練模型爲:數組

image

今天是第一篇,咱們關注他們其中比較簡單的二分類問題,代碼在最後。微信

實際背景是 IMDB(互聯網電影資料庫,你能夠理解爲國外的豆瓣)有大量的關於電影的評論,可是因爲數據量的問題,很難去人工手動判斷一條評定是對電影的誇獎仍是批評,這須要藉助人工智能的幫助,根據用戶評論的內容去判斷用戶的評論是積極的仍是消極的(爲使問題簡化,咱們取的數據是傾向性很明顯的數據),數據集來自 IMDB,Numpy 數據格式:每個單詞對應一個索引數字,這樣每一條評論就能夠對應一個索引數字的序列,組成一維數組,也是一個一維向量,這樣的多個一維向量組成二維數組,就對應對條評論。網絡

背景介紹完了,整個過程分爲以下幾步,同時思考幾個問題:函數

  1. 因爲每一條評論的長度不同,每一個一維向量長度不一致,這樣的數據很差處理,所以須要將數據進行預處理,這裏採用 one-hot 方法(one-hot 是一個經常使用的方法,後面會有專門的文章介紹),簡單點來 one-hot 作的事情就是:假如一個數組是[1, 3, 5, 3],須要處理成 10 維數據就是[0, 1, 0, 1, 0, 1, 0, 0, 0, 0],對應數數組索引處的數字是 1,其餘數字是 0,本實際問題中,評論處理後的數據是一個向量(25000,10000)。
  2. 針對這種 one-hot 處理過的數據(0-1 類型的訓練數據集),用帶有 relu 激活的以 Dense 爲中間層的網絡進行深度學習表現很好(爲何這個表現的好,爲何 Dense 第一個參數即隱藏層的個數是 16 都是很複雜的問題,後續深刻研究,這裏只下結論將數據投影到 16 維空間中且是兩個中間層的網絡能夠知足要求),隨後一層是將輸出格式化爲 0-1 機率判斷的結果。
  3. 針對損失函數,由於最後輸出的結果是這條評論傾向性的機率值,因此選擇交叉熵做爲損失函數(binary_crossentropy,二元交叉熵對於這種狀況的判斷訓練效果表現較好)。
  4. 最後,由於咱們的模型是要作擬合,是一個有反饋的網絡,從上圖中能夠看出來,所以這裏選擇部分數據用於提供反饋數據(反饋的意思是說先訓練出一個模型,而後用另一些數據進行測試,計算誤差,將誤差結果反饋給網絡,網絡進行參數調整,再一輪訓練)且提供反饋的數據不該該用於訓練,所以從訓練集中取出一部分數據用於提供反饋。這裏選擇訓練數據集中前一萬條數據用於提供反饋數據,後一萬五千條數據用於訓練。
  5. 最後一步是啓動 fit 訓練模型,fit 中 validation_data 參數是用於提供驗證數據的。最後咱們從訓練的過程當中,提取出每一輪循環獲得的數據畫圖直觀看看訓練損失、驗證損失、訓練精度和驗證精度的具體變化:

image

image

咱們從圖中能夠看出,隨着訓練網絡迭代的次數愈來愈多,訓練精度愈來愈大,訓練損失愈來愈小,這是咱們指望的;可是同時差很少在第四次迭代後,驗證損失愈來愈大,驗證精度愈來愈小,這就跟指望不相符了,這不科學,那這是爲何?學習

這是由於因爲迭代次數過多,出現了過擬合。測試

隨着訓練數據迭代次數過多,訓練出的模型爲了更好的擬合訓練集的數據,致使一些參數設置的會過於絕對,出現了過擬合,這是咱們在訓練網絡中常常會遇到的問題,所以咱們認爲在迭代四次是比較好的,相似於數學概念上的極值,更多更少的迭代次數都不夠好,所以咱們將 fit 中的迭代次數改成 4,再次訓練網絡。ui

最後在另外的兩萬五千條測試集上進行驗證,效果還能夠,基本知足要求,問題獲得解答。更多的細節已經寫在了下面代碼相關注釋內容部分了,有興趣請自行閱讀。this

#!/usr/bin/env python3
​
import matplotlib.pyplot as plt
import numpy as np
from keras import layers
from keras import models
from keras.datasets import imdb
​
​
# IMDB 數據集,分類一個評論是正面的仍是反面的
def comment():
    # num_words = 10000 表明取前一萬高頻詞,加入低頻詞數據量會過大且做用較小,暫不考慮
    # 25000 條訓練數據,25000 條測試數據
    (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
​
    # 訓練集被處理成整數數列,每條數據表示一條評論中單詞出現的次數(如 good 出現次數是 5 次)
    # [1, 14, 22, ... 19, 178, 32]
    # print(train_data[0])
​
    # 用 0 表明負面評論,1 表明正面評論
    # print(train_labels[0])
    # print(max([max(sequence) for sequence in train_data]))
​
    # 單詞與索引對照表
    # word_index = imdb.get_word_index()
    # reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
    # decoded_review = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])
​
    # 將評論翻譯成可讀語言
    # this film was just brilliant casting location scenery story direction everyone's really suited the part they
    # print(decoded_review)
​
    # one-hot 方法預處理數據爲向量
    x_train = vectorize_sequences(train_data)
    x_test = vectorize_sequences(test_data)
    y_train = np.asarray(train_labels).astype('float32')
    y_test = np.asarray(test_labels).astype('float32')
​
    # 構造網絡
    model = models.Sequential()
    model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
    model.add(layers.Dense(16, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))
    # 建立編譯模型
    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
    # model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss='binary_crossentropy', metrics=['accuracy'])
    # model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss=losses.binary_crossentropy,
    #               metrics=[metrics.binary_accuracy])
​
    # 因爲這裏有反饋,所以須要有必定的數據進行數據進行驗證,這裏取前一萬個數據進行驗證操做
    x_val = x_train[:10000]
    partial_x_train = x_train[10000:]
    y_val = y_train[:10000]
    partial_y_train = y_train[10000:]
​
    # validation_data 用於傳遞驗證數據
    history = model.fit(partial_x_train, partial_y_train, epochs=4, batch_size=512, validation_data=(x_val, y_val))
​
    # History 是訓練過程當中的全部數據
    history_dict = history.history
    print(history_dict.keys())
    history_dict = history.history
    loss_values = history_dict['loss']
    val_loss_values = history_dict['val_loss']
    epochs = range(1, len(loss_values) + 1)
​
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.plot(epochs, loss_values, 'bo', label='訓練損失')
    plt.plot(epochs, val_loss_values, 'b', label='驗證損失')
    plt.title('訓練和驗證損失')
    plt.xlabel('迭代')
    plt.ylabel('損失')
    plt.legend()
    plt.show()
​
    plt.clf()
    acc = history_dict['acc']
    val_acc = history_dict['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, y_test)
    # [0.3329389461231232, 0.8639600276947021]
    print(results)
    # [[0.1754326], [0.9990357], [0.855113]...]
    print(model.predict(x_test))
​
​
def vectorize_sequences(sequences, dimension=10000):
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.
    return results
​
​
if __name__ == "__main__":
    comment()
  • 本文首發自微信公衆號:RAIS
相關文章
相關標籤/搜索