圖解RNN和文本分類

在這裏插入圖片描述

CNN和RNN

在這裏插入圖片描述
RNN類似於CNN,但多了一個自我指向的路徑
CNN是一種前饋網絡,前饋網絡意味着,每個神經元只與前一層的神經元相連。接收前一層的輸出,並輸出給下一層.各層間沒有反饋
RNN是一種特殊的神經網絡結構, 它是根據"人的認知是基於過往的經驗和記憶"這一觀點提出的. 不僅接收前一層的輸出,並輸出給下一層,同層之間有反饋,維護一個狀態作爲下一步額外輸入,同層之間使用的相當的參數,每一步使用相同的激活函數
在這裏插入圖片描述
http://dprogrammer.org/rnn-lstm-gru

CNN主要處理定長的數據(圖片分類),RNN主要處理變長的數據,序列式問題(文本情感分析,圖片生成,機器翻譯)

RNN細節

在這裏插入圖片描述
St=fw(St1,xt)S_t=f_w(S_{t-1},x_t) 具體 St=tanh(WSt1+Uxt)W,US_t=tanh(W S_{t-1} + Ux_t),W,U都是矩陣
再通過矩陣V變換可以變爲一個概率值:y^t=softmax(VSt)\hat y_t=softmax(VS_t)

正向傳播

在這裏插入圖片描述
E(y,y^)=tE(yt,y^t)E(y,\hat y)=\sum_tE(y_t,\hat y_t)

反向傳播

上面的WVUW、V、U都是一樣的,所以計算梯度是要統一加起來
EW=tEtW\frac{\partial E}{\partial W}=\sum_t\frac{\partial E_t}{\partial W}
某一點
E3W=E3y^3y^3S3S3W\frac{\partial E_3}{\partial W}=\frac{\partial E_3}{\partial \hat y_3} \frac { \partial \hat y_3}{\partial S_3} \frac{\partial S_3}{\partial W}

St=tanh(WSt1+Uxt)由 S_t=tanh(W S_{t-1} + Ux_t) 是由前面推導出S3S2S_3是S_2的函數,不斷遞歸
E3W=k=03E3y^3y^3S3(j=k+13SjSj1)SkW\frac{\partial E_3}{\partial W}= \sum_{k=0}^3 \frac{\partial E_3}{\partial \hat y_3} \frac { \partial \hat y_3}{\partial S_3} (\prod _{j=k+1}^{3} \frac {\partial S_j}{\partial S_{j-1}})\frac{\partial S_k}{\partial W}

計算損失函數速度緩慢慢,我們可以存儲中間的變量加速
Tanh容易梯度消失,輸出值接近+1,-1小,多次就會消失,所以較遠的影響小,優化如下

在這裏插入圖片描述

RNN擴展

多層網絡

在這裏插入圖片描述

增加多層,跟上面比增加了2層,底層的輸出變成高層的輸入
同層之間都有一個自身循環
增加了網絡擬合能力

雙向循環

在這裏插入圖片描述

使得學到上下文信息,兩個狀態拼接後進入輸出層,進一步提高了表達能力,但是無法實時輸出結果

RNN變種:LSTM(長短期記憶網絡)

循環網絡的每層度用一套參數,所以參數過少,參數過載,不像CNN中會因爲參數過多而過擬合,RNN的參數需要記住的參數太多,所以我們讓RNN的LSTM,它進行了選擇性機制:門實現(sigmod,記憶多少)

  1. 選擇性輸出
  2. 選擇性遺忘
  3. 選擇性輸入

在這裏插入圖片描述
在這裏插入圖片描述
Cell的狀態傳遞:
×\times: 遺忘門判斷當前狀態需要遺忘上面
++:輸入合併形成新的狀態傳到後一個單元

在這裏插入圖片描述
遺忘門:輸出0~1之間的向量,與 C t-1點積,使得遺忘一些東西
在這裏插入圖片描述
傳入門:控制傳入信息

在這裏插入圖片描述
輸出門:進行輸出

LSTM單元實現
scale=1.0/math.sqrt(num_embedding_size+num_lstm_nodes[-1])/3.0
lstm_init=tfv1.random_uniform_initializer(-scale,scale)
# 生成 W,h,b
def params_for_lstm_cell(x_size,h_size,bias_size):
   x_w=tfv1.get_variable("x_weight",x_size)
   h_w=tfv1.get_variable("h_weight", h_size)
   b=tfv1.get_variable('biases',bias_size,initializer=tf.constant_initializer(0.0))
   return  x_w,h_w,b
with tfv1.variable_scope('lstm_nn',initializer=lstm_init):
	# 輸入門
   with tfv1.variable_scope("input"):
		ix, ih, ib = params_for_lstm_cell(
	       # 詞語長度:num_embedding_size = 16
	       # lstm每層size:num_lstm_nodes = [32,32] 第一層32個第二層也是32個
	
           # [16,32]
           x_size=[num_embedding_size,num_lstm_nodes[0]],
           # [入的大小,輸出的大小]
           h_size=[num_lstm_nodes[0],num_lstm_nodes[0]],
           bias_size=[1,num_lstm_nodes[0]])
    # 輸出門
   with tfv1.variable_scope("output"):
       ox, oh, ob = params_for_lstm_cell(
           x_size=[num_embedding_size, num_lstm_nodes[0]],
           h_size=[num_lstm_nodes[0], num_lstm_nodes[0]],
           bias_size=[1, num_lstm_nodes[0]])
    # 遺忘門
   with tfv1.variable_scope("forget"):
       fx, fh, fb = params_for_lstm_cell(
           x_size=[num_embedding_size, num_lstm_nodes[0]],
           h_size=[num_lstm_nodes[0], num_lstm_nodes[0]],
           bias_size=[1, num_lstm_nodes[0]])
	# 記憶門
   with tfv1.variable_scope("memory"):
       cx, ch, cb = params_for_lstm_cell(
           x_size=[num_embedding_size, num_lstm_nodes[0]],
           h_size=[num_lstm_nodes[0], num_lstm_nodes[0]],
           bias_size=[1, num_lstm_nodes[0]])   
	#  兩個初始化狀態
	#  狀態初始化
   state=tfv1.Variable(tfv1.zeros([batch_size,num_lstm_nodes[0]],trainabel=False))
    # h初始化
   h = tfv1.Variable(tfv1.zeros([batch_size, num_lstm_nodes[0]], trainabel=False))
   for i in range(num_timesteps):
   		# [batch_size,1,embed_size]
       embed_input=embed_input[:,i,:]
       embed_input=tfv1.reshape(embed_input,[batch_size,num_lstm_nodes])

       forget_gate = tf.sigmoid(tf.matmul(embed_input,fx)+tf.matmul(h,fh)+fb)
       input_gate = tf.sigmoid(tf.matmul(embed_input, ix) + tf.matmul(h, ih) + ib)
       output_gate = tf.sigmoid(tf.matmul(embed_input, ox) + tf.matmul(h, oh) + ob)
       mid_state=tf.tanh(tf.matmul(embed_input,cx)+tf.matmul(h,ch))
       state=mid_state*input_gate+state*forget_gate
       h=output_gate*tf.tanh(state)
   last=h

LSTM文本分類模型

在這裏插入圖片描述
使用分詞操作得到一次分詞(LSTM,深度學習,…)

import  jieba # 分詞工具
def generate_seg_file(input_file,out_seg_file):
    print("generate_seg_file...")
    with open(input_file,'r',encoding='UTF-8') as f:
        lines=f.readlines()
    with open(out_seg_file,'w',encoding='UTF-8') as f :
        for line in lines:
            # \r回車 \n換行  \t製表符
            # 脫去換行回車,使用製表符分割
            # 體育  科比牛皮無解
            # label content
            label, content = line.strip('\r\n').split('\t')
            # 對內容進行分詞
            word_iter = jieba.cut(content)
            word_content = ''
            for word in word_iter:
                word = word.strip(' ')
                if word != '':
                    word_content += word+' '
            out_line = '%s\t%s\n' % (label,word_content.strip(' '))
            # 體育   科比 牛皮 無解
            f.write(out_line)
    print("done")

然後使用embedding輸入網絡(之前使用的是onehot是一個傳統的分類方法,embedding隨着梯度下降調整,使得跟文本意思更相關,意思更接近),但這個模型有信息侷限,雖然用的輸出包含所有輸入的信息,但是會有位置關係(位置近的影響更大),所以我們使用了圖三改進,讓LSTM形成雙向網絡,然後進組合輸出(比如拼接,average/max pooling等等)

MLP:多層感知神經網絡,也就是普通的全連接網絡,假如多分類使用softmax,雙分類直接就是sigmod即可

HAN

https://www.aclweb.org/anthology/N16-1174.pdf
在這裏插入圖片描述

更接近於人們的思想,首先提取關鍵句,再提取關鍵詞分析,句子和詞語的兩層機制,並加入的attention

CNN文本分類

在這裏插入圖片描述
http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/
CNN假如將單詞變成統一長度的就可以解決邊長的(設定一個標準長度,假如少於標準長度,就padding填充,大於標準長度,截取到標準長度),這樣的缺點會忽略一些信息

模型運作
  1. 每個詞都用一個長度爲6的embedding表達
  2. 然後使用一維卷積
  3. 時間方向最大池化(從句子開頭到結束)
  4. 進行全連接

R-CNN文本分類

結合RNN和CNN的文本分類

在這裏插入圖片描述

模型運作:結合RNN提取信息,CNN信息抽象
  1. 使用雙向RNN對每一個詞提取信息,提取信息分爲:當前詞之前的上文信息,當前詞之後的下文信息,當前詞的信息
  2. 三個信息合併做一維卷積,得到一維向量
  3. 再做max-pooling

這樣會使得embedding參數過大,那麼會過擬合

所以使用embedding壓縮

在這裏插入圖片描述

第一張:每個詞取一個embedding,每個都需要一個長長的向量,比如 20k×256=5.12M20k \times 256 = 5.12M

embedding_initializer=tfv1.random_normal_initializer(-1.0,1.0)
with tfv1.variable_scope(
    'embedding',initializer=embedding_initializer):
        embeddings=tfv1.get_variable('embedding',[vocab_size,num_embedding_size],tfv1.float32)
        # 比如input傳入三個詞解析成id 後爲 [1,3,4] -> 所以找出 [embedding[1], embedding[3],embedding[4]]
        embed_input=tfv1.nn.embedding_lookup(embeddings,input)

第二張:寫成一個二維矩陣,使用行向量和列向量進行拼接,20k×256=36k\sqrt {20k} \times256 = 36k

這樣做的原因不會對結果精度造成損失,有壓縮和無壓縮一樣,但原來的embedding會有冗餘,使用壓縮後,共享embedding提取更加有效

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