超越Transformer:PyNeuraLogic的神经符号编程探索
- 深入理解神经符号编程的优势
- 简介
在过去的几年里,我们见证了基于Transformer的模型在自然语言处理和计算机视觉等领域的广泛应用。本文将探讨一种简洁、可解释且可扩展的方式来表达深度学习模型,特别是Transformer,通过混合架构的方式,即结合深度学习与符号人工智能。为此,我们将在名为PyNeuraLogic的Python神经符号框架中实现模型。
将符号表示与深度学习相结合,弥补了当前深度学习模型在可解释性和推理技术方面的不足。值得注意的是,增加模型参数的数量并不总是实现预期结果的最佳途径,就像增加相机像素数量并不一定能提升拍照质量一样。
PyNeuraLogic框架基于逻辑编程——逻辑程序包含可微分的参数。该框架特别适合处理小型结构化数据(如分子)和复杂模型(如Transformer和图神经网络)。然而,PyNeuraLogic并不是处理非结构化数据和大型张量数据的最佳选择。
框架的核心是一个可微分的逻辑程序,我们称之为模板。模板由逻辑规则组成,这些规则以抽象方式定义神经网络的结构——可以将模板视为模型架构的设计蓝图。然后将模板应用于每个输入数据实例,以生成针对每个样本独有的神经网络。这种动态架构生成的方式与传统预定义架构的框架有本质区别。
- 符号化Transformer
我们通常将深度学习模型实现为对批量输入张量的张量操作。这是合理的,因为深度学习框架和硬件(如GPU)通常针对处理更大的张量而非形状和大小多变的张量进行了优化。Transformer也不例外,通常将单个标记向量批量处理为一个大矩阵,并将模型表示为对这些矩阵的操作。然而,这种实现方式掩盖了各个输入标记之间的相互关联,这一点在Transformer的注意力机制中尤为明显。
- 注意力机制
注意力机制构成了所有Transformer模型的核心。其经典版本使用了所谓的多头缩放点积注意力。让我们以一个头为例,将缩放点积注意力分解为一个简单的逻辑程序。
注意力机制的目的是决定网络应该关注输入的哪些部分。通过计算值V的加权和来实现,其中权重反映了输入键K和查询Q的兼容性。在这个特定版本中,权重由查询Q和键K的点积除以d_k的平方根后,通过softmax函数计算得到。
(X.weights(X_i, X_j) <= (X.d_k, X.k(X_j).T, X.q(X_i))) | [F.product, F.softmax_agg(agg_terms=[X_j])],
(X.attention(X_i) <= (X.weights(X_i, X_j), X.v(X_j)) | [F.product]
在PyNeuraLogic中,我们可以通过上述逻辑规则充分捕捉注意力机制。第一条规则表示权重的计算——它计算维度的平方根倒数与转置的第j个键向量和第i个查询向量的乘积。然后我们用softmax聚合给定i和所有可能的j的所有结果。
第二条规则计算该权重向量与相应的第j个值向量之间的乘积,并对每个第i个标记的不同j的结果求和。
- 注意力屏蔽
在训练和评估过程中,我们通常限制输入标记可以相互关注的内容。例如,我们希望限制标记向前看和关注即将到来的单词。流行的框架,如PyTorch,通过屏蔽实现这一点,即将缩放的点积结果的元素子集设置为一个非常低的负数。这些数字强制softmax函数将零指定为相应标记对的权重。
(X.weights(X_i, X_j) <= (
X.d_k, X.k(X_j).T, X.q(X_i), X.special.leq(X_j, X_i)
)) | [F.product, F.softmax_agg(agg_terms=[X_j])],
使用我们的符号表示,我们可以通过简单地添加一个约束条件来实现这一点。在计算权重时,我们限制第j个指标小于或等于第i个指标。与掩码相反,我们只计算所需的缩放点积。
- 非标准注意力
当然,符号化的"掩蔽"可以是完全任意的。我们大多数人听说过基于稀疏Transformer的GPT-3⁴(或其应用,例如ChatGPT)⁵。稀疏Transformer的注意力(跨步版本)有两种类型的注意力头:
- 一个只关注前n个标记 (0 ≤ i − j ≤ n)
- 一个只关注每第n个前一个标记 ((i − j) % n = 0)
两种类型头的实现都只需要微小的改变(例如,对于n = 5)。
(X.weights(X_i, X_j) <= (
X.d_k, X.k(X_j).T, X.q(X_i),
X.special.leq(X.D, 5), X.special.sub(X_i, X_j, X.D),
)) | [F.product, F.softmax_agg(agg_terms=[X_j])],
(X.weights(X_i, X_j) <= (
X.d_k, X.k(X_j).T, X.q(X_i),
X.special.mod(X.D, 5, 0), X.special.sub(X_i, X_j, X.D),
)) | [F.product, F.softmax_agg(agg_terms=[X_j])],
我们可以走得更远,将对类似图形(关系)输入的注意力进行概括,就像在关系注意力中一样。⁶这种类型的注意力在图形上运行,其中节点只关注它们的邻居(由边连接的节点)。查询Q、键K和值V是边嵌入与节点向量嵌入相加的结果。
(X.weights(X_i, X_j) <= (X.d_k, X.k(X_i, X_j).T, X.q(X_i, X_j))) | [F.product, F.softmax_agg(agg_terms=[X_j])],
(X.attention(X_i) <= (X.weights(X_i, X_j), X.v(X_i, X_j)) | [F.product,
X.q(X_i, X_j) <= (X.n(X_i)[W_qn], X.e(X_i, X_j)[W_qe]),
X.k(X_i, X_j) <= (X.n(X_j)[W_kn], X.e(X_i, X_j)[W_ke]),
X.v(X_i, X_j) <= (X.n(X_j)[W_vn], X.e(X_i, X_j)[W_ve]),
在我们的例子中,这种类型的注意力与之前显示的缩放点积注意力几乎相同。唯一的区别是添加了额外的术语来捕获边缘。将图作为注意力机制的输入似乎很自然,这并不奇怪,因为Transformer是一种图神经网络,作用于完全连接的图(未应用掩码时)。在传统的张量表示中,这并不是那么明显。
- 编码器
现在,当我们展示注意力机制的实现时,构建整个Transformer编码器块的缺失部分相对简单。
我们已经在关系注意力中看到了如何实现嵌入。对于传统的Transformer,嵌入将非常相似。我们将输入向量投影到三个嵌入向量中——键、查询和值。
X.q(X_i) <= X.input(X_i)[W_q],
X.k(X_i) <= X.input(X_i)[W_k],
X.v(X_i) <= X.input(X_i)[W_v],
查询嵌入通过跳过连接与注意力的输出相加。然后将生成的向量归一化并传递到多层感知机(MLP)。
(X.norm1(X_i) <= (X.attention(X_i), X.q(X_i))) | [F.norm],
对于MLP,我们将实现一个具有两个隐藏层的全连接神经网络,它可以优雅地表达为一个逻辑规则。
(X.mlp(X_i)[W_2] <= (X.norm(X_i)[W_1])) | [F.relu],
最后一个带有规范化的跳过连接与前一个相同。
(X.norm2(X_i) <= (X.mlp(X_i), X.norm1(X_i))) | [F.norm],
我们已经构建了构建Transformer编码器所需的所有部分。解码器使用相同的组件;因此,其实施将是类似的。让我们将所有块组合成一个可微分逻辑程序,该程序可以嵌入到Python脚本中并使用PyNeuraLogic编译到神经网络中。
X.q(X_i) <= X.input(X_i)[W_q],
X.k(X_i) <= X.input(X_i)[W_k],
X.v(X_i) <= X.input(X_i)[W_v],
X.d_k[1 / math.sqrt(embed_dim)],
(X.weights(X_i, X_j) <= (X.d_k, X.k(X_j).T, X.q(X_i))) | [F.product, F.softmax_agg(agg_terms=[X_j])],
(X.attention(X_i) <= (X.weights(X_i, X_j), X.v(X_j)) | [F.product],
(X.norm1(X_i) <= (X.attention(X_i), X.q(X_i))) | [F.norm],
(X.mlp(X_i)[W_2] <= (X.norm(X_i)[W_1])) | [F.relu],
(X.norm2(X_i) <= (X.mlp(X_i), X.norm1(X_i))) | [F.norm],
总结
在本文中,我们分析了Transformer架构并演示了它在名为PyNeuraLogic的神经符号框架中的实现。通过这种方法,我们能够实现各种类型的Transformer,只需对代码进行微小的更改,说明每个人都可以如何快速转向和开发新颖的Transformer架构。它还指出了各种版本的Transformers以及带有GNN的Transformers的明显相似之处。
本文由mdnice多平台发布