CNN和RNN
RNN類似於CNN,但多了一個自我指向的路徑
CNN是一種前饋網絡,前饋網絡意味着,每個神經元只與前一層的神經元相連。接收前一層的輸出,並輸出給下一層.各層間沒有反饋
RNN是一種特殊的神經網絡結構, 它是根據"人的認知是基於過往的經驗和記憶"這一觀點提出的. 不僅接收前一層的輸出,並輸出給下一層,同層之間有反饋,維護一個狀態作爲下一步額外輸入,同層之間使用的相當的參數,每一步使用相同的激活函數
http://dprogrammer.org/rnn-lstm-gru
CNN主要處理定長的數據(圖片分類),RNN主要處理變長的數據,序列式問題(文本情感分析,圖片生成,機器翻譯)
RNN細節
具體 都是矩陣
再通過矩陣V變換可以變爲一個概率值:
正向傳播
反向傳播
上面的都是一樣的,所以計算梯度是要統一加起來
某一點
是由前面推導出,不斷遞歸
計算損失函數速度緩慢慢,我們可以存儲中間的變量加速
Tanh容易梯度消失,輸出值接近+1,-1小,多次就會消失,所以較遠的影響小,優化如下
RNN擴展
多層網絡
增加多層,跟上面比增加了2層,底層的輸出變成高層的輸入
同層之間都有一個自身循環
增加了網絡擬合能力
雙向循環
使得學到上下文信息,兩個狀態拼接後進入輸出層,進一步提高了表達能力,但是無法實時輸出結果
RNN變種:LSTM(長短期記憶網絡)
循環網絡的每層度用一套參數,所以參數過少,參數過載,不像CNN中會因爲參數過多而過擬合,RNN的參數需要記住的參數太多,所以我們讓RNN的LSTM,它進行了選擇性機制:門實現(sigmod,記憶多少)
- 選擇性輸出
- 選擇性遺忘
- 選擇性輸入
Cell的狀態傳遞:
遺忘門判斷當前狀態需要遺忘上面
輸入合併形成新的狀態傳到後一個單元
遺忘門:輸出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填充,大於標準長度,截取到標準長度),這樣的缺點會忽略一些信息
模型運作
- 每個詞都用一個長度爲6的embedding表達
- 然後使用一維卷積
- 時間方向最大池化(從句子開頭到結束)
- 進行全連接
R-CNN文本分類
結合RNN和CNN的文本分類
模型運作:結合RNN提取信息,CNN信息抽象
- 使用雙向RNN對每一個詞提取信息,提取信息分爲:當前詞之前的上文信息,當前詞之後的下文信息,當前詞的信息
- 三個信息合併做一維卷積,得到一維向量
- 再做max-pooling
這樣會使得embedding參數過大,那麼會過擬合
所以使用embedding壓縮
第一張:每個詞取一個embedding,每個都需要一個長長的向量,比如
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)
第二張:寫成一個二維矩陣,使用行向量和列向量進行拼接,
這樣做的原因不會對結果精度造成損失,有壓縮和無壓縮一樣,但原來的embedding會有冗餘,使用壓縮後,共享embedding提取更加有效