模型主要包含四個模塊:提問、回答、記憶存儲、輸入。 輸入模塊首先計算問題和輸入得文本向量表示,而後根據問題計算attention,並以此選擇和問題相關的輸入。而後記憶存儲模塊會根據問題和輸入迭代存儲記憶,並以最後的時序向量做爲答案模塊的輸入,答案模塊結合問題和該向量輸出答案。git
以上圖爲例,輸入時8句話,問題是Where is the football?下面分模塊解析求解過程github
輸入主要採用GRU單元進行編碼,分兩種狀況: 一、當輸入是一個句子時,直接喂到GRU單元,輸出句長個向量表示,這時後續的attention會選擇最相關的詞 二、當輸入是多個句子時,將句子用特殊標識符鏈接,而後將每一個特殊標識符處的隱藏狀態輸出,這時attention會選擇和句子相關的句子bash
也採用GRU單元編碼,不一樣的是這裏只需輸出最後的隱藏狀態,而輸入模塊需輸出所有的隱藏狀態,熟悉tensorflow的朋友必定知道tf.nn.dynamic_rnn,這個函數的輸出是一個擁有兩個值得矢量,其中一個是RNN得最後隱藏狀態,一個是所有的狀態網絡
這一塊分兩部分,attention和記憶更新 一、attention:attention的計算以下圖,在i次迭代的t時刻參數分別爲輸入向量(8句話的向量表示,迭代過程當中保持不變)、上一時刻的記憶、問題向量表示 函數
其中:這裏利用了多種向量類似性計算方法,並拼接後輸入到兩層神經網絡中計算attention值。ui
二、記憶更新:在第i次迭代,計算以下圖,第0次迭代m的初始值爲q,c~t~每次迭代都保存不變,爲輸入(8個句子)向量,h~t~爲隱藏向量表示,每輪迭代中,將每一個時刻的隱藏向量表示和記憶向量上一時刻的向量放入GRU單元,更新記憶向量m。 編碼
在模塊實例圖中,以8個句子和「Where is the football」做爲問題 第一次迭代:經過q和input以及m(初始爲q)計算attention,找到和q最相關的句子是s~7~:"John put down the football",並給予高權重g,以此更新記憶向量m,例如記住‘john’。 第二次迭代:經過q和input、m計算attention,此時m記住了「john」,而不是q了,找到的最相關句子爲S~2~和S~6~,它們都包含了「john」,接着繼續更新記憶向量,最後輸出到回答模塊以下計算表示,a~0~=m(m爲記憶模塊最後的記憶) spa
def gated_gru(self,c_current,h_previous,g_current):
""" gated gru to get updated hidden state :param c_current: [batch_size,embedding_size] :param h_previous:[batch_size,hidden_size] :param g_current: [batch_size,1] :return h_current: [batch_size,hidden_size] """
# 1.compute candidate hidden state using GRU.
h_candidate=self.gru_cell(c_current, h_previous,"gru_candidate_sentence") #[batch_size,hidden_size]
# 2.combine candidate hidden state and previous hidden state using weight(a gate) to get updated hidden state.
h_current=tf.multiply(g_current,h_candidate)+tf.multiply(1-g_current,h_previous) #[batch_size,hidden_size]
return h_current
複製代碼
這部分採用了DMN+中的實現,scala
最後一步和傳統GRU不一樣 所以def attention_mechanism_parallel(self,c_full,m,q,i):
""" parallel implemtation of gate function given a list of candidate sentence, a query, and previous memory. Input: c_full: candidate fact. shape:[batch_size,story_length,hidden_size] m: previous memory. shape:[batch_size,hidden_size] q: question. shape:[batch_size,hidden_size] Output: a scalar score (in batch). shape:[batch_size,story_length] """
q=tf.expand_dims(q,axis=1) #[batch_size,1,hidden_size]
m=tf.expand_dims(m,axis=1) #[batch_size,1,hidden_size]
# 1.define a large feature vector that captures a variety of similarities between input,memory and question vector: z(c,m,q)
c_q_elementwise=tf.multiply(c_full,q) #[batch_size,story_length,hidden_size]
c_m_elementwise=tf.multiply(c_full,m) #[batch_size,story_length,hidden_size]
c_q_minus=tf.abs(tf.subtract(c_full,q)) #[batch_size,story_length,hidden_size]
c_m_minus=tf.abs(tf.subtract(c_full,m)) #[batch_size,story_length,hidden_size]
# c_transpose Wq
c_w_q=self.x1Wx2_parallel(c_full,q,"c_w_q"+str(i)) #[batch_size,story_length,hidden_size]
c_w_m=self.x1Wx2_parallel(c_full,m,"c_w_m"+str(i)) #[batch_size,story_length,hidden_size]
# c_transposeWm
q_tile=tf.tile(q,[1,self.story_length,1]) #[batch_size,story_length,hidden_size]
m_tile=tf.tile(m,[1,self.story_length,1]) #[batch_size,story_length,hidden_size]
z=tf.concat([c_full,m_tile,q_tile,c_q_elementwise,c_m_elementwise,c_q_minus,c_m_minus,c_w_q,c_w_m],2) #[batch_size,story_length,hidden_size*9]
# 2. two layer feed foward
g=tf.layers.dense(z,self.hidden_size*3,activation=tf.nn.tanh) #[batch_size,story_length,hidden_size*3]
g=tf.layers.dense(g,1,activation=tf.nn.sigmoid) #[batch_size,story_length,1]
g=tf.squeeze(g,axis=2) #[batch_size,story_length]
return g
def x1Wx2_parallel(self,x1,x2,scope):
""" :param x1: [batch_size,story_length,hidden_size] :param x2: [batch_size,1,hidden_size] :param scope: a string :return: [batch_size,story_length,hidden_size] """
with tf.variable_scope(scope):
x1=tf.reshape(x1,shape=(self.batch_size,-1)) #[batch_size,story_length*hidden_size]
x1_w=tf.layers.dense(x1,self.story_length*self.hidden_size,use_bias=False) #[self.hidden_size, story_length*self.hidden_size]
x1_w_expand=tf.expand_dims(x1_w,axis=2) #[batch_size,story_length*self.hidden_size,1]
x1_w_x2=tf.matmul(x1_w_expand,x2) #[batch_size,story_length*self.hidden_size,hidden_size]
x1_w_x2=tf.reshape(x1_w_x2,shape=(self.batch_size,self.story_length,self.hidden_size,self.hidden_size))
x1_w_x2=tf.reduce_sum(x1_w_x2,axis=3) #[batch_size,story_length,hidden_size]
return x1_w_x2
複製代碼
模型實現完整代碼見github:github.com/DLZWY/Machi… 其中實現了dmn用於多標籤文檔分類的方法。其中的query和input相同,全部attention求的是與query最相關的詞3d