深度學習的基礎 - 迴歸神經網絡簡介

介紹

讓我打開這篇文章,提出一個問題 - “工作中愛我們深入學習”,這對你有意義嗎?不是真的 - 讀這篇文章 - “我們喜歡深度學習”。完美感!這句話中的一點混亂使句子語無倫次。那麼,我們能期望神經網絡能夠理解它嗎?並不是的!如果人類大腦對它的含義感到困惑,我相信神經網絡將很難解讀這樣的文本。

在日常生活中有多個這樣的任務,當他們的序列受到干擾時會完全被打亂。例如,我們之前看到的語言 - 單詞序列定義了它們的含義,時間序列數據 - 時間定義事件的發生,基因組序列的數據 - 每個序列具有不同的含義。存在多種這樣的情況,其中信息序列確定事件本身。如果我們試圖將這些數據用於任何合理的輸出,我們需要一個能夠訪問一些關於數據的先驗知識的網絡來完全理解它。因此,循環神經網絡發揮作用。

 

目錄

  1. 需要處理序列的神經網絡
  2. 什麼是遞歸神經網絡(RNN)?
  3. 詳細瞭解復發神經元
  4. Excel中復發神經元的前向傳播
  5. RNN中的反向傳播(BPTT)
  6. 在Keras實施RNN
  7. 消失和爆炸的梯度問題
  8. 其他RNN架構

 

需要處理序列的神經網絡

在深入研究循環神經網絡的細節之前,讓我們思考一下,如果我們真的需要一個專門處理信息序列的網絡。此外,我們可以使用此類網絡實現哪些任務。

循環神經網絡的優點在於它們的應用多樣性。當我們處理RNN時,他們有很強的能力處理各種輸入和輸出類型。

  • 情緒分類 - 這可以是簡單地將推文分類爲積極和消極情緒的任務。所以這裏的輸入是不同長度的推文,而輸出是固定類型和大小。
  • 圖像字幕 - 在這裏,假設我們有一個圖像,我們需要一個文本描述。所以我們有一個輸入 - 圖像,一系列或一系列單詞作爲輸出。此處圖像可能具有固定大小,但輸出是不同長度的描述

  • 語言翻譯 - 這基本上意味着我們有一些特定語言的文本讓我們說英語,我們希望用法語翻譯它。每種語言都有自己的語義,並且對同一個句子有不同的長度。所以這裏的輸入和輸出都有不同的長度。

因此,RNN可用於將輸入映射到不同類型,長度的輸出,並且在其應用中相當普遍。看看他們的應用程序,讓我們看看RNN的架構是怎樣的。

 

什麼是迴歸神經網絡?

假設任務是預測句子中的下一個單詞。讓我們嘗試使用MLP完成它。那麼在MLP中會發生什麼。在最簡單的形式中,我們有一個輸入層,一個隱藏層和一個輸出層。輸入層接收輸入,應用隱藏層激活,然後我們最終接收輸出。

讓我們有一個更深的網絡,其中存在多個隱藏層。所以這裏,輸入層接收輸入,應用第一個隱藏層激活,然後將這些激活發送到下一個隱藏層,並通過層連續激活以產生輸出。每個隱藏層的特徵在於其自身的權重和偏差。

由於每個隱藏層都有自己的權重和激活,因此它們的行爲是獨立的。現在的目標是確定連續輸入之間的關係。我們可以爲隱藏層提供輸入嗎?我們可以!

 

這裏,這些隱藏層的權重和偏差是不同的。因此,這些層中的每一個都獨立地行爲並且不能組合在一起。要將這些隱藏層組合在一起,我們將對這些隱藏層具有相同的權重和偏差。

我們現在可以將這些層組合在一起,所有隱藏層的權重和偏差都是相同的。所有這些隱藏層可以在一個循環層中一起滾動。

所以就像將輸入提供給隱藏層一樣。在任何時候,復發神經元的步長權重將是相同的,因爲它現在是單個神經元。因此,重複神經元存儲先前輸入的狀態並與當前輸入組合,從而保持當前輸入與先前輸入的某種關係。

 

詳細瞭解復發神經元

我們首先要做一個簡單的任務。讓我們採用字符級RNN,我們有一個單詞“Hello”。因此,我們提供前4個字母,即h,e,l,l,並要求網絡預測最後一個字母即'o'。所以這裏的任務詞彙只有4個字母{h,e,l,o}。在涉及自然語言處理的實際場景中,詞彙表包括整個維基百科數據庫中的單詞或語言中的所有單詞。爲簡單起見,我們採用了一小組詞彙。

讓我們看看如何使用上述結構來預測單詞“hello”中的第五個字母。在上述結構中,藍色RNN塊將稱爲遞推公式的東西應用於輸入向量以及其先前狀態。在這種情況下,字母“h”之前沒有任何內容,讓我們取字母“e”。因此,當字母“e”被提供給網絡時,遞歸公式應用於字母“e”和先前的狀態,即字母“h”。這些被稱爲輸入的各種時間步長。因此,如果在時間t,輸入爲“e”,則在時間t-1,輸入爲“h”。遞推公式適用於e和h兩者。我們得到一個新的州。

當前狀態的公式可寫爲 -

這裏,Ht是新狀態,ht-1是先前狀態,而xt是當前輸入。我們現在有一個先前輸入的狀態而不是輸入本身,因爲輸入神經元會在我們之前的輸入上應用轉換。因此,每個連續輸入都被稱爲時間步長。

在這種情況下,我們有四個輸入要給網絡,在遞推公式期間,在每個時間步驟將相同的功能和相同的權重應用於網絡。

採用最簡單形式的遞歸神經網絡,假設激活函數爲tanh,復發神經元的權重爲Whh,輸入神經元的權重爲Wxh,我們可以在時間t寫出狀態方程 -

在這種情況下,復發神經元只是將前一個狀態考慮在內。對於較長的序列,等式可以涉及多個這樣的狀態。一旦計算出最終狀態,我們就可以繼續產生輸出

現在,一旦計算出當前狀態,我們就可以計算輸出狀態 -

讓我總結一下復發神經元的步驟 -

  1. 輸入的單個時間步驟被提供給網絡,即xt被提供給網絡
  2. 然後我們使用當前輸入和先前狀態的組合來計算其當前狀態,即我們計算ht
  3. 當前的ht在下一個時間步驟變爲ht-1
  4. 我們可以根據問題的要求選擇儘可能多的時間步驟,並結合以前所有州的信息
  5. 一旦完成所有時間步驟,則使用最終當前狀態來計算輸出yt
  6. 然後將輸出與實際輸出進行比較,並生成錯誤
  7. 然後將錯誤反向傳播到網絡以更新權重(我們將在後面的部分中詳細介紹反向傳播)並對網絡進行培訓

我們來看看如何在Excel中計算這些狀態並獲得輸出。

 

Excel中復發神經元的前向傳播

我們先來看看輸入 -

輸入是一個熱編碼。我們的整個詞彙表是{h,e,l,o},因此我們可以輕鬆地對輸入進行熱編碼。

現在輸入神經元將使用權重wxh將輸入轉換爲隱藏狀態。我們將權重隨機初始化爲3 * 4矩陣 -

步驟1:

現在對於字母“h”,對於隱藏狀態,我們需要Wxh * Xt。通過矩陣乘法,我們得到它 -

第2步:

現在轉向復發神經元,我們將Whh作爲1 * 1矩陣的權重,   偏差也是1 * 1矩陣 

對於字母“h”,之前的狀態是[0,0,0],因爲之前沒有字母。

所以計算 - >(whh * ht-1 +偏差)

第3步:

現在我們可以得到當前狀態 -

因爲對於h,沒有先前的隱藏狀態我們將tanh函數應用於此輸出並獲得當前狀態 -

第4步:

現在我們繼續下一個州。“e”現在提供給網絡。ht的處理輸出現在變爲ht-1,而熱編碼的e是xt。我們現在計算當前狀態ht。

Whh * ht-1 +偏差將是 -

Wxh * xt將 -

第5步:

現在計算字母“e”的ht,

現在這將成爲下一個狀態的ht-1,並且反覆出現的神經元將使用它和新角色來預測下一個狀態。

第6步:

在每個狀態下,遞歸神經網絡也會產生輸出。讓我們計算字母e的yt。

第7步:

可以通過應用softmax函數來計算來自詞彙表的特定字母的概率。所以我們將有softmax(yt)

如果我們轉換這些概率以理解預測,我們會看到模型說“e”之後的字母應該是h,因爲字母“h”的概率最高。這是否意味着我們做錯了什麼?不,所以我們這裏很難訓練網絡。我們剛剛給它看了兩個字母。所以它幾乎沒有學到任何東西。

現在,我們面臨的下一個大問題是,在迴歸神經網絡的情況下,反向傳播是如何工作的。在有反饋循環時如何更新權重?

 

迴歸神經網絡(BPTT)中的反向傳播

想象一下如果在遞歸神經網絡的情況下如何更新權重,可能會有點挑戰。因此,爲了理解和可視化反向傳播,讓我們在所有時間步驟中展開網絡。在RNN中,我們可能會或可能不會在每個時間步都有輸出。

在前向傳播的情況下,輸入在每個時間步進入和前進。在這種情況下向後傳播的情況下,我們將比喻及時地改變權重,因此我們將其稱爲反向傳播時間(BPTT)。

在RNN的情況下,如果yt是預測值ȳt是實際值,則誤差計算爲交叉熵損失 -

Et(ȳt,yt)= - ȳtlog(yt)

E(ȳ,y)= - Σȳtlog(yt)

我們通常將完整序列(單詞)視爲一個訓練示例,因此總誤差只是每個時間步(字符)的誤差之和。我們可以看到的權重在每個時間步都是相同的。讓我們總結一下反向傳播的步驟

  1. 首先使用電流輸出和實際輸出計算交叉熵誤差
  2. 請記住,網絡已針對所有時間步驟展開
  3. 對於展開的網絡,針對權重參數計算每個時間步長的梯度
  4. 現在,對於所有時間步長,權重是相同的,可以將梯度組合在一起用於所有時間步長
  5. 然後針對復發神經元和緻密層更新權重

 

展開的網絡看起來很像常規神經網絡。並且反向傳播算法類似於常規神經網絡,只是我們將所有時間步長的誤差梯度組合在一起。如果有100個時間步驟,您認爲可能會發生什麼?這基本上需要很長時間才能使網絡收斂,因爲在展開網絡後變得非常龐大。

如果您不希望深入研究反向傳播的數學,那麼您需要了解的是,一旦您在網絡中展開重複神經元,反向傳播的時間與常規神經網絡中的相似。但是,我將提出一篇關於帶有劃痕的遞歸神經網絡的詳細文章,它將在遞歸神經網絡中具有反向傳播算法的詳細數學。

Keras中迴歸神經網絡的實現

讓我們使用Recurrent Neural networks來預測各種推文的情緒。我們希望將推文預測爲正面或負面。您可以在此處下載數據集

我們有大約1600000條推文來訓練我們的網絡。如果您不熟悉NLP的基礎知識,我強烈建議您閱讀本文。我們還有另一篇關於單詞嵌入的詳細文章,它也有助於您詳細瞭解單詞嵌入。

現在讓我們使用RNN將各種推文分類爲正面或負面。

# import all libraries
import keras
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.layers.convolutional import Conv1D
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
import pandas as pd
import numpy as np
import spacy
nlp=spacy.load("en")

#load the dataset
train=pd.read_csv("../datasets/training.1600000.processed.noemoticon.csv" , encoding= "latin-1")
Y_train = train[train.columns[0]]
X_train = train[train.columns[5]]

# split the data into test and train
from sklearn.model_selection import train_test_split
trainset1x, trainset2x, trainset1y, trainset2y = train_test_split(X_train.values, Y_train.values, test_size=0.02,random_state=42 )
trainset2y=pd.get_dummies(trainset2y)

# function to remove stopwords
def stopwords(sentence):
   new=[]
   sentence=nlp(sentence)
    for w in sentence:
        if (w.is_stop == False) & (w.pos_ !="PUNCT"):
            new.append(w.string.strip())
        c=" ".join(str(x) for x in new)
    return c

# function to lemmatize the tweets
def lemmatize(sentence):
    sentence=nlp(sentence)
    str=""
    for w in sentence:
        str+=" "+w.lemma_
    return nlp(str)

#loading the glove model
def loadGloveModel(gloveFile):
    print("Loading Glove Model")
    f = open(gloveFile,'r')
    model = {}
    for line in f:
        splitLine = line.split()
        word = splitLine[0]
        embedding = [float(val) for val in splitLine[1:]]
        model[word] = embedding
    print ("Done."),len(model),(" words loaded!")
    return model

# save the glove model
model=loadGloveModel("/mnt/hdd/datasets/glove/glove.twitter.27B.200d.txt")

#vectorising the sentences
def sent_vectorizer(sent, model):
    sent_vec = np.zeros(200)
    numw = 0
    for w in sent.split():
        try:
            sent_vec = np.add(sent_vec, model[str(w)])
            numw+=1
        except:
            pass
    return sent_vec

#obtain a clean vector
cleanvector=[]
for i in range(trainset2x.shape[0]):
    document=trainset2x[i]
    document=document.lower()
    document=lemmatize(document)
    document=str(document)
    cleanvector.append(sent_vectorizer(document,model))

#Getting the input and output in proper shape
cleanvector=np.array(cleanvector)
cleanvector =cleanvector.reshape(len(cleanvector),200,1)

#tokenizing the sequences
tokenizer = Tokenizer(num_words=16000)
tokenizer.fit_on_texts(trainset2x)
sequences = tokenizer.texts_to_sequences(trainset2x)
word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))
data = pad_sequences(sequences, maxlen=15, padding="post")
print(data.shape)

#reshape the data and preparing to train
data=data.reshape(len(cleanvector),15,1)
from sklearn.model_selection import train_test_split
trainx, validx, trainy, validy = train_test_split(data, trainset2y, test_size=0.3,random_state=42 )
#calculate the number of words
nb_words=len(tokenizer.word_index)+1

#obtain theembedding matrix
embedding_matrix = np.zeros((nb_words, 200))
for word, i in word_index.items():
    embedding_vector = model.get(word)
    if embedding_vector is not None:
        embedding_matrix[i] = embedding_vector
print('Null word embeddings: %d' % np.sum(np.sum(embedding_matrix, axis=1) == 0))

trainy=np.array(trainy)
validy=np.array(validy)

#building a simple RNN model
def modelbuild():
    model = Sequential()
    model.add(keras.layers.InputLayer(input_shape=(15,1)))
    keras.layers.embeddings.Embedding(nb_words, 15, weights=[embedding_matrix], input_length=15,
    trainable=False)
 
    model.add(keras.layers.recurrent.SimpleRNN(units = 100, activation='relu',
    use_bias=True))
    model.add(keras.layers.Dense(units=1000, input_dim = 2000, activation='sigmoid'))
    model.add(keras.layers.Dense(units=500, input_dim=1000, activation='relu'))
    model.add(keras.layers.Dense(units=2, input_dim=500,activation='softmax'))
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model
   
#compiling the model
finalmodel = modelbuild()
finalmodel.fit(trainx, trainy, epochs=10, batch_size=120,validation_data=(validx,validy))

如果您運行此模型,它可能無法爲您提供最佳結果,因爲這是一個非常簡單的架構和相當淺的網絡。我強烈建議您使用網絡架構以獲得更好的結果。此外,有多種方法可以預處理數據。預處理應完全取決於手頭的任務。

 

消失和爆炸的梯度問題

RNN的工作原理是信息的結果取決於其先前的狀態或先前的n個時間步長。常規RNN可能難以學習遠程依賴性。例如,如果我們有一個句子,如“吃披薩的男人有紫色的頭髮”。在這種情況下,紫色頭髮的描述是針對男人而不是披薩。所以這是一個長期依賴。

如果我們在這種情況下反向傳播錯誤,我們需要應用鏈規則。要計算第三個時間步之後的第一個錯誤 -

∂E/∂W=∂E/∂y3*∂y3/∂h3*∂h3/∂y2*∂y2/∂h1..並且存在長依賴性。

這裏我們應用鏈規則,如果任何一個梯度接近0,所有梯度將由於乘法而急於以指數方式快速歸零。這些狀態將不再幫助網絡學習任何東西。這被稱爲消失梯度問題。

與爆炸梯度問題相比,消失梯度問題更具威脅性,其中梯度由於單個或多個梯度值變得非常高而變得非常大。

消失梯度問題更令人擔憂的原因是通過將梯度剪切到預定閾值可以容易地解決爆炸梯度問題。幸運的是,還有辦法處理消失的梯度問題。有一些架構,如LSTM(長期短期記憶)和GRU(門控遞歸單位),可用於處理消失的梯度問題。

 

其他RNN架構

正如我們所看到的,當我們要求RNN處理長期依賴時,RNN會遇到消失的梯度問題。隨着參數的數量變得非常大,它們也變得非常難以訓練。如果我們展開網絡,它就變得如此巨大,以至於它的融合是一個挑戰。

長期短期內存網絡 - 通常稱爲“LSTM” - 是一種特殊的RNN,能夠學習長期依賴性。它們由Hochreiter&Schmidhuber介紹。它們在各種各樣的問題上工作得非常好,現在被廣泛使用。LSTM也具有這種類似鏈的結構,但重複模塊具有略微不同的結構。不是擁有單個神經網絡層,而是有多個層,以非常特殊的方式進行交互。它們具有輸入門,忘記門和輸出門。我們很快就會提出有關LSTM的詳細文章。

另一種有效的RNN架構是門控循環單元,即GRU。它們是LSTM的變體,但結構更簡單,更容易訓練。它們的成功主要歸功於控制當前輸入和先前存儲器如何使用的門控網絡信號,以更新當前激活併產生當前狀態。這些門具有自己的權重集,在學習階段自適應地更新。我們這裏只有兩個門,重置一個更新門。請繼續關注有關GRU的更詳細文章。

 

結束筆記

我希望這篇文章能讓您領先於迴歸神經網絡。在接下來的文章中,我們將深入探討回歸神經網絡的複雜數學以及LSTM和GRU的詳細描述。嘗試使用這些RNN的架構,並對其性能和應用程序感到驚訝。請在評論部分分享您的發現和方法。

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