本文翻译自Gaurav Bhatthttp://deeplearn-ai.com 发表的NEURAL TENSOR NETWORK: EXPLORING RELATIONS AMONG TEXT ENTITIES

在这篇文章中,我将介绍神经张量网络 (Neural Tensor Network, NTN),并使用 NTN 预测新的三元组关系。
用代码直接跳到GitHub仓库

对关系进行推理的神经模型

NTN 旨在找到实体 之间的关系,即以一定的确定性预测 的关系 R。神经张量网络 (NTN) 将标准线性神经网络层替换为双线性张量层,该层直接将两个实体向量跨多个维度关联起来。该模型通过以下基于 NTN 的函数计算两个实体处于某种关系的可能性的分数:
image.png
其中是2022-02-21-神经张量网络回顾 - 图2标准非线性的单元应用,2022-02-21-神经张量网络回顾 - 图3是张量,双线性张量积2022-02-21-神经张量网络回顾 - 图4产生向量2022-02-21-神经张量网络回顾 - 图5,其中每个条目张量的一个切片2022-02-21-神经张量网络回顾 - 图6计算:2022-02-21-神经张量网络回顾 - 图7。其它参数为关系R是一个神经网络的标准形式:2022-02-21-神经张量网络回顾 - 图82022-02-21-神经张量网络回顾 - 图9.

神经张量层图示

2022-02-21-神经张量网络回顾 - 图10
NTN 使用张量变量2022-02-21-神经张量网络回顾 - 图11对两个实体之间的关系进行乘法建模,如上所示,NTN 是简单神经层的扩展,添加了这些张量变量。

TRAINING OBJECTIVES

NTN 使用对比最大边距目标函数进行训练。给定训练样本中的三元组为2022-02-21-神经张量网络回顾 - 图12, 负样本是通过随机替换第二个实体来创建的,其中 j 是随机索引。最后,目标函数定义为
image.png
其中,2022-02-21-神经张量网络回顾 - 图14 是正则化参数。

Implementation Details

现在,我们已经看到了 NTN 的工作原理,是时候深入研究实施了。这里要考虑的一个重要点是,每个给定的关系都有自己的一组张量参数。让我快速概述一下我们需要在 Keras 的帮助下做什么。
image.png
每个关系都归因于一个单独的 Keras 模型,该模型还添加了张量参数。现在,假设在模型初始化和组合之间添加了张量层。在后面的帖子中,我将解释张量层的构造。从上图中,您可以轻松得出结论,我们需要以某种方式处理训练数据,以便可以同时将其传递给所有单独的模型。我们想要的是只更新那些与特定关系相对应的张量参数。然而,Keras 不允许我们在更新单独的模型的同时留下其余部分。因此,我们需要将数据划分为不同的关系。每个训练样本将由所有关系的一个实例组成,即每个关系有一对实体。

实现 NTN 层

让我们从实现神经张量层开始。本节的先决条件是在 Keras 中编写自定义层。我们首先使用参数 inp_size、out_size 和激活来初始化 NTN 类。 inp_size 是输入变量的形状,在我们的例子中是实体; out_size 是张量参数的个数(k),activation 是要使用的激活函数(默认为 tanh)

  1. from ntn_input import *
  2. from keras import activations
  3. class ntn_layer(Layer):
  4. def __init__(self, inp_size, out_size, activation='tanh', **kwargs):
  5. super(ntn_layer, self).__init__(**kwargs)
  6. self.k = out_size
  7. self.d = inp_size
  8. self.activation = activations.get(activation)
  9. self.test_out = 0

维度的命名保持不变,即 k 对应于每个关系的张量参数的数量,d 是实体的形状。

现在,我们需要初始化张量层参数。为了更好地理解我们在这里做什么,看一下张量网络的下图。
2022-02-21-神经张量网络回顾 - 图16
我们初始化四个张量的参数,即 W、V、b 和 U,如下所示:

  1. def build(self,input_shape):
  2. self.W = self.add_weight(name='w',shape=(self.d, self.d, self.k), initializer='glorot_uniform', trainable=True)
  3. self.V = self.add_weight(name='v', shape=(self.k, self.d*2), initializer='glorot_uniform', trainable=True)
  4. self.b = self.add_weight(name='b', shape=(self.k,), initializer='zeros', trainable=True)
  5. self.U = self.add_weight(name='u', shape=(self.k,), initializer='glorot_uniform',trainable=True)
  6. super(ntn_layer, self).build(input_shape)

在这里,我们使用 glorot_uniform 采样初始化参数。在实践中,这种初始化比其他初始化产生更好的性能。add_weight 函数的另一个参数 - trainable,如果我们不想更新特定的可调参数,可以将其设置为 false。例如,我们可以将 W 参数设置为不可训练,并且 NTN 模型将表现得像一个简单的神经网络,如前所述。

一旦参数被初始化,是时候实现以下等式了:
image.png
上面的等式给每个实体对打分。 如您所见,我们必须迭代 k 个张量参数(张量模型的切片)。 这是通过计算每次迭代的中间产品,最后聚合所有这些产品来完成的。 以下代码片段为您完成了这项工作。 请不要更改函数的名称,因为它们与 Keras API 一致。

  1. def call(self ,x ,mask=None):
  2. e1=x[0] # entity 1
  3. e2=x[1] # entity 2
  4. batch_size = K.shape(e1)[0]
  5. V_out, h, mid_pro = [],[],[]
  6. for i in range(self.k): # computing the innner products
  7. V_out = K.dot(self.V[i],K.concatenate([e1,e2]).T)
  8. temp = K.dot(e1,self.W[:,:,i])
  9. h = K.sum(temp*e2,axis=1)
  10. mid_pro.append(V_out+h+self.b[i])
  11. tensor_bi_product = K.concatenate(mid_pro,axis=0)
  12. tensor_bi_product = self.U*self.activation(K.reshape(tensor_bi_product,(self.k,batch_size))).T
  13. self.test_out = K.shape(tensor_bi_product)
  14. return tensor_bi_product

最后,要完成 NTN 层的实现,我们必须添加以下函数

  1. def compute_output_shape(self, input_shape):
  2. return (input_shape[0][0],self.k)

我们已经构建了 NTN 层,它可以像 Keras 中的任何其他神经层一样被调用。 让我们看看如何在真实数据集上使用 NTN 层。

数据集

如本文所述,我将使用 Wordbase 和 Freebase 数据集。 略…

建立模型

为了训练模型,我们需要定义对比最大边际损失函数。

  1. def contrastive_loss(y_true, y_pred):
  2. margin = 1
  3. return K.mean(y_true * K.square(y_pred) + (1 - y_true) * K.square(K.maximum(margin - y_pred, 0)))

我们应该能够从 Keras 编译函数中调用这个自定义损失函数。

  1. from ntn import *
  2. def build_model(num_relations):
  3. Input_x, Input_y = [], []
  4. for i in range(num_relations):
  5. Input_x.append(Input(shape=(dimx,)))
  6. Input_y.append(Input(shape=(dimy,)))
  7. ntn, score = [], [] # storing separate tensor parameters
  8. for i in range(num_relations): # iterating through each slice 'k'
  9. ntn.append(ntn_layer(inp_size=dimx, out_size=4)([Input_x[i],Input_y[i]]))
  10. score.append(Dense(1,activation='sigmoid')(ntn[i]))
  11. all_inputs = [Input_x[i]for i in range(num_relations)]
  12. all_inputs.extend([Input_y[i]for i in range(num_relations)]) # aggregating all the models
  13. model = Model(all_inputs,score)
  14. model.compile(loss=contrastive_loss,optimizer='adam')
  15. return model

最后,我们需要聚合数据以训练模型

  1. e, t, labels_train, labels_dev = aggregate(e1, e2, labels_train, t1, t2, labels_dev, num_relations)
  2. model.fit(e, labels_train, nb_epoch=10, batch_size=100, verbose=2)

此时您可以看到模型开始训练,并且每个单独模型的损失逐渐减少。 此外,为了计算知识库数据集上 NTN 的准确性,我们需要计算所有关系的成本,并选择得分最高的关系。 如论文所述,所达到的准确度接近 88%(平均)

参考

GauravBh1010tt/DeepLearn/neural tensor network-Github
神经张量网络:探索文本实体之间的关系