神经张量网络:探索文本实体之间的关系

2017年11月21日

本文翻译自Gaurav Bhatthttp://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的函数计算两个实体处于特定关系的可能性分数:

其中

是标准非线性的单元应用,
是张量,双线性张量积
产生向量
,其中每个条目张量的一个切片
计算:
。其它参数为关系R是一个神经网络的标准形式:

可视化神经张量层

NTN使用张量变量

对两个实体之间的关系进行乘法建模。如上所示,NTN是对简单神经层的扩展,增加了这些张量变量。所以,如果我们从上图中删除
,最后,目标函数被定义为

这是一个简单的实体向量连接,以及偏向项。

培训目标

NTN采用对比式最大余量目标函数进行训练。给定训练样本中的三元组

,则通过随机的将第二个实体替换为
来创建负样本,其中j是随机索引。最后,目标函数被定义为

其中,
是正则化参数。

实施细节

现在,我们看到了NTN的工作,是时候深入实施了。这里要考虑的重要一点是,每个给定的关系都有其自己的一组张量参数。让我简单介绍一下在Keras的帮助下我们需要做些什么。

每个关系都归因于一个单独的Keras模型,它也增加了张量参数。现在,假定张量层是在模型初始化和组合之间添加的。在后面的文章中,我将解释张量层的构造。从上图可以很容易得出结论,我们需要以某种方式处理训练数据,以便它可以同时传递到所有单独的模型。我们想要的只是更新与特定关系相对应的张量参数。然而,Keras 并没有让我们更新一个单独的模型,而剩下的。所以我们需要把数据分成不同的关系。每个训练样本将包含所有关系的一个实例,也就是每个关系的一对实体。

实施NTN层

让我们从实施神经张量层开始。这部分的先决条件是在Keras编写自定义图层。如果您不确定这意味着什么,那么请查看Keras文档的 编写你自己的keras图层。

我们首先用参数inp_sizeout_sizeactivation来初始化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是实体的形状。

现在,我们需要初始化张量图层参数。为了更好的理解我们在这里做什么,看一下张量网络的下图。

我们初始化四个张量参数,即WVbU如下:

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ml

调参过程中的参数 学习率,权重衰减,冲量(learning_rate , weight_decay , momentum)

无论是深度学习还是机器学习,大多情况下训练中都会遇到这几个参数,今天依据我自己的理解具体的总结一下,可能会存在错误,还请指正. learning_rate , ...

4808
来自专栏https://www.cnblogs.com/L

【机器学习】--机器学习之朴素贝叶斯从初始到应用

机器学习算法中,有种依据概率原则进行分类的朴素贝叶斯算法,正如气象学家预测天气一样,朴素贝叶斯算法就是应用先前事件的有关数据来估计未来事件发生的概率。

812
来自专栏CreateAMind

keras doc 6 卷积层Convolutional

本文摘自 http://keras-cn.readthedocs.io/en/latest/layers/convolutional_layer/

872
来自专栏ml

深度学习之图像的数据增强

   在图像的深度学习中,为了丰富图像训练集,更好的提取图像特征,泛化模型(防止模型过拟合),一般都会对数据图像进行数据增强, 数据增强,常用的方式,就是旋转图...

6727
来自专栏机器之心

专栏 | 递归卷积神经网络在解析和实体识别中的应用

35213
来自专栏AI科技评论

大会 | DiracNets:无需跳层连接的ResNet

AI 科技评论按:本文作者 David 9,首发于作者的个人博客,AI 科技评论获其授权转载。 虚拟化技术牺牲硬件开销和性能,换来软件功能的灵活性;深度模型也类...

3196
来自专栏素质云笔记

python + sklearn ︱分类效果评估——acc、recall、F1、ROC、回归、距离

之前提到过聚类之后,聚类质量的评价: 聚类︱python实现 六大 分群质量评估指标(兰德系数、互信息、轮廓系数) R语言相关分类效果评估: R语言...

5707
来自专栏量子位

Attention!神经网络中的注意机制到底是什么?

原作:Adam Kosiorek 安妮 编译自 GitHub 量子位 出品 | 公众号 QbitAI 神经网络的注意机制(Attention Mechanism...

3985
来自专栏PaddlePaddle

转载|使用PaddleFluid和TensorFlow实现图像分类网络SE_ResNeXt

视觉(vision)、自然语言处理(Nature Language Processing, NLP)、语音(Speech)是深度学习研究的三大方向。三大领域各自...

1173
来自专栏用户2442861的专栏

循环神经网络教程第二部分-用python,numpy,theano实现一个RNN

作者:徐志强 链接:https://zhuanlan.zhihu.com/p/22289383 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,...

782

扫码关注云+社区