神經張量網絡:探索文本實體之間的關系
歡迎大家前往雲加社區,獲取更多騰訊海量技術實踐幹貨哦~
譯者:Waitingalone
本文翻譯自Gaurav Bhatt在 http://deeplearn-ai.com 發表的NEURAL TENSOR NETWORK: EXPLORING RELATIONS AMONG TEXT ENTITIES。文中版權、圖像代碼等數據均歸作者所有。為了本土化,翻譯內容略作修改。
在這篇文章中,我將介紹神經張量網絡(NTN),如在用神經張量網絡推理知識庫的推理中所描述的那樣 。我的NTN實現使用最新版本的Python 2.7,Keras 2.0和Theano 0.9。
用代碼直接跳到GitHub倉庫。
什麽是知識庫完成?
在知識庫完成中,任務是確定兩個實體對之間的關系。例如,考慮兩個實體對 -<cat, tail> 和<supervised learning, machine learning>。如果我們被要求確定給定的兩對之間的關系 - <cat,R,tail>和<supervised learning, R, machine learning> - 那麽第一個關系可以最好的歸結為有型,而第二個關系可以被歸結為實例。所以,我們可以將這兩個對重新定義為 <cat,has,tail>和<supervised learning,instance of,machine learning>。神經張量網絡(NTN)在實體 - 關系對的數據庫上訓練,用於探究實體之間的附加關系。這是通過將數據庫中的每個實體(即每個對象或個體)表示為一個向量來實現的。這些載體可以捕獲有關該實體的事實,以及它是如何可能是某種關系的一部分。每個關系都是通過一個新的神經張量網絡的參數來定義的,這個神經張量網絡可以明確地涉及兩個實體向量
使用NTN預測新的關系三元組。
關系推理的神經模型
能夠認識到某些事實純粹是由於其他現有的關系而存在的,是學習常識推理的模型的目標。NTN旨在發現實體<e1,e2>之間的關系,即對於<e1,e2>確定性地預測關系R. 例如,(e1,R,e2) = (Bengal tiger, has part, tail) 這個關系是否真實且具有確定性。神經張量網絡(NTN)用一個雙線性張量層代替一個標準的線性神經網絡層,它直接關聯了多個維度上的兩個實體向量。該模型通過下列基於NTN的函數計算兩個實體處於特定關系的可能性分數:
其中是標準非線性的單元應用,是張量,雙線性張量積產生向量
可視化神經張量層
NTN使用張量變量 對兩個實體之間的關系進行乘法建模。如上所示,NTN是對簡單神經層的擴展,增加了這些張量變量。所以,如果我們從上圖中刪除 ,最後,目標函數被定義為
這是一個簡單的實體向量連接,以及偏向項。
培訓目標
NTN采用對比式最大余量目標函數進行訓練。給定訓練樣本中的三元組,則通過隨機的將第二個實體替換為來創建負樣本,其中j是隨機索引。最後,目標函數被定義為
其中,是正則化參數。
實施細節
現在,我們看到了NTN的工作,是時候深入實施了。這裏要考慮的重要一點是,每個給定的關系都有其自己的一組張量參數。讓我簡單介紹一下在Keras的幫助下我們需要做些什麽。
每個關系都歸因於一個單獨的Keras模型,它也增加了張量參數。現在,假定張量層是在模型初始化和組合之間添加的。在後面的文章中,我將解釋張量層的構造。從上圖可以很容易得出結論,我們需要以某種方式處理訓練數據,以便它可以同時傳遞到所有單獨的模型。我們想要的只是更新與特定關系相對應的張量參數。然而,Keras 並沒有讓我們更新一個單獨的模型,而剩下的。所以我們需要把數據分成不同的關系。每個訓練樣本將包含所有關系的一個實例,也就是每個關系的一對實體。
實施NTN層
讓我們從實施神經張量層開始。這部分的先決條件是在Keras編寫自定義圖層。如果您不確定這意味著什麽,那麽請查看Keras文檔的 編寫你自己的keras圖層。
我們首先用參數inp_size,out_size和activation來初始化NTN類。該inp_size是輸入變量的形狀,在我們的例子中的實體; 所述out_size是張量參數(K),和激活是要使用的激活函數(默認為tanh)。
from ntn_input import * from keras import activations class ntn_layer(Layer): def __init__(self, inp_size, out_size, activation=‘tanh‘, **kwargs): super(ntn_layer, self).__init__(**kwargs) self.k = out_size self.d = inp_size self.activation = activations.get(activation) self.test_out = 0
維的命名保持不變,即k對應於每個關系的張量參數個數,d是實體的形狀。
現在,我們需要初始化張量圖層參數。為了更好的理解我們在這裏做什麽,看一下張量網絡的下圖。
我們初始化四個張量參數,即W,V,b和U如下:
def build(self,input_shape): self.W = self.add_weight(name=‘w‘,shape=(self.d, self.d, self.k), initializer=‘glorot_uniform‘, trainable=True) self.V = self.add_weight(name=‘v‘, shape=(self.k, self.d*2), initializer=‘glorot_uniform‘, trainable=True) self.b = self.add_weight(name=‘b‘, shape=(self.k,), initializer=‘zeros‘, trainable=True) self.U = self.add_weight(name=‘u‘, shape=(self.k,), initializer=‘glorot_uniform‘,trainable=True) super(ntn_layer, self).build(input_shape)
在這裏,我們用glorot_uniform 采樣初始化參數 。在實踐中,這種初始化比其他初始化導致更好的性能。add_weight 函數的另一個參數是可訓練的,如果我們不想更新特定的可調參數,可以設置為false。例如,我們可以將W參數設置為不可訓練的,如前所述,NTN模型將表現得像一個簡單的神經網絡。
一旦參數被初始化,那麽是時候實現下面的等式了:
上面的等式給出了每個實體對的分數。正如你所看到的,我們必須叠代k個張量參數(張量模型的切片)。這是通過計算每個叠代的中間產品來完成的,最後,匯總所有這些產品。下面的代碼片段為你做這個。請不要更改函數的名稱,因為它們與Keras API一致。
def call(self ,x ,mask=None): e1=x[0] # 實體 1 e2=x[1] # 實體 2 batch_size = K.shape(e1)[0] V_out, h, mid_pro = [],[],[] for i in range(self.k): # 計算內部產品 V_out = K.dot(self.V[i],K.concatenate([e1,e2]).T) temp = K.dot(e1,self.W[:,:,i]) h = K.sum(temp*e2,axis=1) mid_pro.append(V_out+h+self.b[i]) tensor_bi_product = K.concatenate(mid_pro,axis=0) tensor_bi_product = self.U*self.activation(K.reshape(tensor_bi_product,(self.k,batch_size))).T self.test_out = K.shape(tensor_bi_product) return tensor_bi_product
最後,要完成NTN層的實現,我們必須添加以下功能。這與NTN無關; Keras使用以下函數進行內部處理。
def compute_output_shape(self, input_shape): return (input_shape[0][0],self.k)
我們已經建立了可以像Keras中的任何其他神經層一樣調用的NTN層。讓我們看看如何在真實的數據集上使用NTN層。
數據集
我將使用文中提到的Wordbase和Freebase數據集。我已經準備好了數據集(預處理的一部分從GitHub存儲庫中獲取),並且可以進行如下處理。
import ntn_input data_name = ‘wordbase‘ # ‘wordbase‘ or ‘freebase‘ data_path = ‘data‘+data_name raw_training_data = ntn_input.load_training_data(ntn_input.data_path) raw_dev_data = ntn_input.load_dev_data(ntn_input.data_path) entities_list = ntn_input.load_entities(ntn_input.data_path) relations_list = ntn_input.load_relations(ntn_input.data_path) indexed_training_data = data_to_indexed(raw_training_data, entities_list, relations_list) indexed_dev_data = data_to_indexed(raw_dev_data, entities_list, relations_list) (init_word_embeds, entity_to_wordvec) = ntn_input.load_init_embeds(ntn_input.data_path) num_entities = len(entities_list) num_relations = len(relations_list)
此時您可以打印並查看實體及其對應的關系。現在,我們需要根據關系來劃分數據集,以便所有Keras模型都可以同時更新。我已經包括一個預處理功能,為您執行此步驟。此步驟中還添加了否定樣本。負樣本作為損壞的樣本傳遞給prepare_data函數。如果corrupt_samples = 1,則對應於每個訓練樣本添加一個負樣本。這意味著,整個訓練數據集將會翻倍。
import ntn_input
e1,e2,labels_train,t1,t2,labels_dev,num_relations = prepare_data(corrupt_samples)
NTN的定義存儲在一個名為ntn的文件中,很容易導入使用。
建立模型
為了訓練模型,我們需要定義對比最大邊緣損失函數。
def contrastive_loss(y_true, y_pred): margin = 1 return K.mean(y_true * K.square(y_pred) + (1 - y_true) * K.square(K.maximum(margin - y_pred, 0)))
我們應該可以從Keras編譯函數中調用這個自定義的丟失函數。
from ntn import * def build_model(num_relations): Input_x, Input_y = [], [] for i in range(num_relations): Input_x.append(Input(shape=(dimx,))) Input_y.append(Input(shape=(dimy,))) ntn, score = [], [] # 存儲單獨的張量參數 for i in range(num_relations): # 通過每個切片叠代 ‘k‘ ntn.append(ntn_layer(inp_size=dimx, out_size=4)([Input_x[i],Input_y[i]])) score.append(Dense(1,activation=‘sigmoid‘)(ntn[i])) all_inputs = [Input_x[i]for i in range(num_relations)] all_inputs.extend([Input_y[i]for i in range(num_relations)]) # 聚合所有模型 model = Model(all_inputs,score) model.compile(loss=contrastive_loss,optimizer=‘adam‘) return model
最後,我們需要匯總數據以訓練模型
e, t, labels_train, labels_dev = aggregate(e1, e2, labels_train, t1, t2, labels_dev, num_relations) model.fit(e, labels_train, nb_epoch=10, batch_size=100, verbose=2)
在這一點上,你可以看到模型開始訓練,每個個體模型的損失逐漸減少。此外,為了計算NTN在知識庫數據集上的準確性,我們需要計算所有關系的成本,並選擇最大分數的成本。正如本文所述,所達到的準確度接近88%(平均)。
下一步是什麽?
在這篇文章中,我們看到了建立知識庫完成的神經張量網絡。在下一篇文章中,我們將看到NTN如何用於解決其他NLP問題,例如基於非事實問題的回答。。
原文鏈接:http://deeplearn-ai.com/2017/11/21/neural-tensor-network-exploring-relations-among-text-entities/
原文作者:Gaurav Bhatt
相關閱讀
推薦算法之協同過濾
實習生的監控算法: 利用時間序列模型進行曲線預測
用R和Keras深度學習的例子
此文已由作者授權雲加社區發布,轉載請註明原文出處
神經張量網絡:探索文本實體之間的關系