循環神經網絡(RNN)

​最近的股市震盪的有點厲害,跌的有點慘,面對如此情景,我波瀾不驚,原因很簡單,前幾年我小試牛刀的時候我意識到了這不是我這種散戶能玩得懂的,如今的我早已空倉。萬物皆可 AI,如何用深度學習的方法去理解呢?當然,本篇不是一個指導買股票的文章,也不會用股票的數據信息去訓練模型,我付不起這樣的責任,也同樣因爲股票的市場行情遠非一點股票價錢數據就可以解釋的。下面我們來聊一聊正事,循環神經網絡(RNN)

爲什麼要引入循環神經網絡?思考一個問題,如果問你明天股票的市場行情是看漲還是看跌,大概率你會搜索一下近一段時間的行情趨勢,然後給我一個猜測的回答,我們認爲明天的行情與近一段時間的股票價格是有關係的,我們人腦會對此進行一定的推測,我們知道神經網絡在某一種程度上來說,也是一種模擬人腦的行爲,那我們的神經網絡可以做預測嗎?我們之前用於訓練神經網絡的是一個獨立的數據信息,我們把它們打亂順序,還是可以得到正確的結果,很顯然,股票的價錢信息是不能打亂順序的,這兩種數據有什麼區別?

一種數據是順序無關的,一種數據是順序有關的。

我們之前的網絡不好用了,我們需要新的網絡去解決這個問題——循環神經網絡。循環神經網絡遍歷數據時,會保存數據的狀態信息,這個狀態信息包含之前數據的信息,它的內部有環狀結構,前一項數據項的輸出,是下一個數據項的輸入,這樣後一項數據會受到前一項數據的影響。

SimpleRNN

我們用一段矩陣處理的代碼來解釋這個問題:

def deal():
    timesteps = 100
    input_features = 32
    output_features = 64
​
    # 隨機輸入,維度:100 * 32
    inputs = np.random.random((timesteps, input_features))
    print('input: ', inputs)
​
    # 初始化爲全 0 向量
    state_t = np.zeros((output_features,))
​
    # 維度:64 * 32
    W = np.random.random((output_features, input_features))
    # 維度:64 * 64
    U = np.random.random((output_features, output_features))
    # 行向量,64
    b = np.random.random((output_features,))
​
    successive_outputs = []
    for input_t in inputs:
        # 雙曲正切函數
        output_t = np.tanh(np.dot(W, input_t) + np.dot(U, state_t) + b)
        print(output_t)
        successive_outputs.append(output_t)
        state_t = output_t
​
    # 100 * 64
    final_output_sequence = np.stack(successive_outputs, axis=0)
    print('output: ', final_output_sequence)

知道了原理,老規矩,我們還是用 Keras 實現,那種內置的實現,真是熟悉的味道,SimpleRNN。SimpleRNN 可以返回每一步輸出的完整序列,也可以返回最終的結果,由 return_sequences 參數控制(True 時,返回完整序列),有了這樣一個好用的循環神經網絡,那我們就可以重新思考 IMDB 電影評論分類問題了。因爲我們有了之前的經驗,因此這一個的代碼就很簡單了:

def imdb_rnn():
    max_features = 10000
​    # 只用前 500 個單詞
    maxlen = 500
    (input_train, y_train), (input_test, y_test) = imdb.load_data(num_words=max_features)
    input_train = sequence.pad_sequences(input_train, maxlen=maxlen)
​
    model = Sequential()
    # 將正整數(索引值)轉換爲固定尺寸的稠密向量,只能用作第一層
    model.add(Embedding(max_features, 32))
    # model.add(LSTM(32))    # 與下一行相互替換
    model.add(SimpleRNN(32))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
    history = model.fit(input_train, y_train, epochs=10, batch_size=128, validation_split=0.2)
​
    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.legend()
    plt.show()
​
    plt.figure()
    plt.plot(epochs, loss, 'bo', label='Training loss')
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    plt.title('Training and validation loss')
    plt.legend()
    plt.show()

我們看看訓練出來的結果:

精度

損失

差不多 85% 的準確率,還算可以,畢竟我們只用了每條評論的前 500 個單詞,我試着調大單詞數目到 1000,除了時間延長了一點外,準確率僅僅提高了 2%,有點得不償失,爲什麼會出現這樣的情況呢?原因是 SimpleRNN 的效果不好,那簡單,換換其他的 RNN 方法吧。Keras 中還有 LSTM 和 GRU 這樣的循環神經網絡。

LSTM 的簡化圖

SimpleRNN 的一個問題是容易遺忘,專業點的術語叫做 梯度消失,就是在處理過程中,記住了最近幾步處理的信息,更早的信息忘記了,LSTM 和 GRU 效果就好得多。比如 LSTM 的做法就是本次計算出來的結果不僅僅在下一次的計算中使用,而且被記住了,在更加靠後的計算中也能拿到這一次的計算結果,在某一種意義上來說,就是跨步驟傳遞信息,也就能實現長期記憶了,這其中的細節信息當然是我們以後要具體討論的,這裏點到爲止。

我們把上面 SimpleRNN 層換成 LSTM 層,代碼已經在上面的註釋中了,然後看看效果怎麼樣:

精度

從這個我們可以看出來,百分之 90% 了,效果好了很多,對比一下我們之前用單詞統計的方法訓練的網絡的結果(87% 左右),這裏也是用了更少的數據,得到了更高的準確度,說明循環神經網絡是有用的。但我們同樣不要忽略一個問題,循環神經網絡計算量更大了,用大了幾倍的計算量,換來這其實沒那麼大的提高,真的是有意義的嗎?這其實跟我們的問題是有關係的,這個實際的問題是評論態度判斷,其實在評論中,積極或者是消極的評論中,一些單詞的出現其實是非常具有代表性的,“劇情無聊”這樣的詞其實非常有說服力,因此對於這類問題,其實可以考慮用更大的數據量去訓練網絡,在計算量小得多的情況下,還能得到一個不錯的結果。當然,如果是一種聊天系統,或者是智能助手,循環神經網絡的優勢就會體現得更明顯。

循環神經網絡的基本內容我們就介紹的差不多了,下一次我們聊聊循環神經網絡有哪些高級的用法。

  • 首發自公衆號:RAIS
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章