文章目录
本文翻译自:原英文版
这篇博客介绍了论文 Hopfield Network is All You Need,以及使用Pytorch实现的新的 Hopfield 层 。
译者注
- 下文中的模式原文为 patterns ,可以理解为想要存储在网络中的一个向量
主要贡献
我们引入了一个新的能量函数以及其对应的更新规则,该更新规则可以保证能量函数收敛到一个极小值点。
新的能量函数将现代Hopfield网络中的能量函数从离散空间推广到连续空间。连续状态下的Hopfield网络保持了离散状态下的一些特性:
- 指数存储能力
- 极快的收敛速度
新的Hopfield网络是连续状态的,因此它是可导的,可以被集成到深度学习架构中去。在此网络中,模式(patterns)经过一次更新就可以得到,这和神经网络中经过一次前向推导激活是一样的。基于此网络,我们可以得到许多新的深度学习网络架构。
令人惊讶的是论文提出的新的更新规则恰好是transformer中的注意力机制。我们使用本文提出的Hopfield网络分析了transformer模型。
关于这篇博客
这篇博客分为三部分:
首先,我们介绍从传统 Hopfield 网络到现代 Hopfield 网络的转变,以及现代 Hopfield 网络如何使用我们新提出的能量函数泛化到连续空间当中去。
其次,我们展示了新的能量函数的一些性质和Transformer网络中自注意力机制的关系。
最后,我们介绍和解释了一个新的pytorch层,同时展示了一些实际的应用场景,比如:现代Hopfield网络和注意力机制在免疫系统分类中的应用、Hopfield池化、两个集合的关联性。
从经典的Hopfield网络到自注意力机制
联想记忆是最早的人工神经模型之一,它可以追溯到上世纪60年代到70年代间。其中最出名的就是Hopfield网络了,它由John Hopfield于1982年提出。联想记忆和其字面意思一样,它的主要目的就是要将一个输入和与这个输入相似的输出联系到一起。换句话说,目的就是存储和取回_模式_(patterns). 下面我们从经典的Hopfield网络开始讲起。
Hopfield 网络
最简单的联想记忆就是对我们希望存储的N个模式 $\{x_i\}_{i=1}^N$ 的外积求和 (Hebbian学习规则)。在经典的Hopfield网络中这些模式是二元的,也就是 $x_i \in \{-1, 1\}^d$,$d$是模式的长度。那么相应的权重矩阵$W$就是
需要存储的模式$X$就存储在权重矩阵$W$中,他们可以通过一个相关的起始状态模式 $\xi$ 被恢复出来。
术语
从现在开始我们使用$\{x_i\}_{i=1}^N$表示N个被存储的模式,使用$\xi$来表示任何状态模式或状态。
如何从任意一个状态$\xi$来得到其最终相关的模式呢?
基本的同步跟新规则 如下 ($sgn$表示取符号 > 0: 1, < 0: -1):
b是偏置向量,可以解释为阈值。异步更新规则每次只更新状态向量$\xi$中的一个位置值,下一次更新另外一个位置。我们说状态变化满足:$\xi^{t+1} = \xi^t$,那么我们就认为其收敛了。
更新规则(2)的异步版本能够最小化能量函数E:
正如Bruck, Goles-Chacc和Hopfield论文中推导的那样,收敛性依赖于矩阵$W$的结构以及节点更新的方法:
- 对于异步更新来说,在$w_{ii}\ge 0$并且$w_{ij} = w_{ji}$的情况下,更新会收敛到一个稳定的状态。
- 对于同步更新规则,在$w_{ij} = w_{ji}$的条件下,更新会收敛到一个稳定状态或者一个状态循环中(循环长度为2)。
对于异步更新规则和对称权重$(w_{ij} = w_{ji})$来讲:$E(\xi^{(t+1)}) \le E(\xi^t)$ 恒成立。当$E(\xi^{(t+1)}) = E(\xi^t)$对于状态向量中每一个位置的值都成立时,我们就得到了一个局部最优解。所有被存储的模式$\{x_i\}_{i=1}^N$都应该是Hopfield网络中的固定点,也就是说:$$x_i = sgn(Wx_i-b)$$并且这些点必须是能量函数$E$的极小值点(译者注:只有这样这些被存储的状态才能被恢复出来,其它点都不收敛)。
在下面的例子中我们省略了偏置分量$b$。这使得我们使用图片的反转版本(也就是把所有像素黑->白,白->黑)会有同样等能量$E$。
我们从Hopfield网络的一个例子开始:在Hopfield网络中存储一个图片之后恢复它。输入图片是:
由于我们的联想记忆是二元的,我们首先将图片转变为黑白图片:
那么权重矩阵$W$就是表示这个黑白图片的向量$x_{Homer}$的外积:
向量的维数为 $d=64*64$。
原始图片在有一半的像素都被遮盖的情况下能否恢复呢?被遮盖的图片如下:
这个被遮盖的状态用向量表示为$\xi$,我们使用权重矩阵$W$一直左乘$\xi$就可以恢复原始图片。
如果我们多存储几张图片呢?这个权重矩阵可以通过所有的模式的外积之和来得到,比如下面我们要存储三张图片:
在这两张图片中,左边的表示我们存储的图片,右边的表示我们恢复的图片。
在上面第一行图片中,我们的恢复过程不再完美,但是有两个有趣的事实我们需要考虑:
- 将第一张原始图片中的下半部分遮住之后,我们会在修改之后的向量中引入大量的-1。因此我们得到了古怪的结果:内积$ $比$ $大。
- 正如我们上面所说的,如果不使用偏置分量b,模式的反转,上例中就是颠倒所有的像素点会导致同样的能量。
在上图中的第一行中,我们得到的图片看起来不正确,但是事实上它是对的。但是我们第二行的例子恢复就不再正确了。我们对于有两个Marge图片的存储导致我们得到的权重将Homer覆盖掉了。上面的两个例子是我们使用权重矩阵$W$只更新了一次所得到的结果,但是结果并不会随着我们多更新几次有所改变。下图展示了我们在存储了6张图片的Hopfield网络中,图片恢复的效果:
显而易见,图片的恢复并不完美。有人会怀疑Hopfield网络有限的存储能力可能是个问题,参考Amit 等人和Torres 等人的论文。但是下面我们说明存储容量并不是导致我们恢复出的图片不完美的直接原因。对于不允许出错的恢复来讲存储容量是:
d是输入的维度。对于允许小部分错误的恢复来讲,存储容量是
那么,在我们上述例子中存储容量大概是$C \approx0.14d=0.146464\approx570$。所以网络容量并不是产生错误的最直接原因。在上面的例子中,我们给定的样本是相关的,这才是导致错误的原因。
因此,我们需要一个可以将紧密联系的模式分开的模型,这样相关的模式就可以被区分开来。
关于存储容量
等式(7)和(8)中存储容量是对于$w_{ii}=0$来讲的。最近folli等人分析了在$w_{ii}\ge 0$情况下Hopfield网络的存储容量。同样的对于$w_{ii}\ge 0$,在可以允许有少量错误的情况下,得到了$C\approx 0.14d$。比率$C/d$经常被叫做载荷参数并用$\alpha$表示。Folli等人的研究表明在$\alpha$非常大的情况下,存在第二个范围,在这个范围内存储容量大很多(也就是更多的固定点存在)。然而, Rocci 等人和 Gosti 等人的报告指出这些对于大的$\alpha$的固定点是不稳定的,并且没有吸引盆。
现代Hopfield网络
存储容量是Hopfield网络的一个重要特点。现代Hopfield网络(又称:绸密联想存储器)引入了一个新的能量函数,这个能量函数带来了更高的容量。离散的现代Hopfield网络先前已经被 Krotov和Hopfield提出了,之后被Demircigil等人 进一步泛化。
- Krotov和Hopfield引入了能量函数:
F 是相互作用函数,N是存储模式的数量。他们选择了一个多项式的交互函数$F(z) = z^a$。在恢复不出现错误的条件下,存储容量是:
允许一小部分错误的情况下,存储容量是:
其中,$\alpha_a$是一个常数,它依赖于允许出错概率这个阈值。对于$a=2$,经典的Hopfiled模型的存储容量就是$C \approx 0.14d$(允许恢复时有极少量错误)。
- Demircigil 等人使用指数相互作用函数$F(z) = exp(z)$扩展了能量函数:
等式(12)可以写为:
其中$X=(x_1, ..., x_N)$是所存储模式组成的数据矩阵。_log-sum-exp_ (lse) 函数定义为:
这个能量函数会带来如下的存储容量:
现在我们来看一下更新规则,它对等式(9)和(12)都适用。对于二元类型的模式来说,也就是$\xi \in \{-1, 1\}^d$,我们使用 $\xi [l]$ 来表示向量 $\xi$ 第 $l$ 个位置上的值。对于公式(9)和公式(12)的能量函数,第$l$个元素 $\xi[l]$的更新规则可以用当前状态$\xi$的能量和$\xi[l]$翻转后的状态的能量差来描述。$E[l]$的更新会降低能量。更新规则是:
对于等式(12)来说,更新规则就是:
其中$\xi^{(l+)}[l]=1, \xi^{(l-)}[l] = -1$,且$\xi^{(l+)}[k] = \xi^{(l-)}[k] = \xi[k] (k \ne l)$
Demircigil等人的论文表明这个更新规则可以使能量函数以高概率在进行一次(异步)更新后收敛。注意,当前状态$\xi$的一次更新对应着$d$次异步更新,也就是每一个维度值$\xil$都需要更新一下。
相比经典的Hopfield网络,现代Hopfield网络没有公式(5)中权重矩阵。取而代之的新的能量函数,它首先对每一个被存储的模式$x_i$与状态模式$\xi$进行点积运算,对于每个运算结果进行函数变换,最后在求和。
使用等式(17),我们再次尝试从存储着6张图片的网络中恢复Homer,相比于经典Hopfield网络,现在它工作的非常好,而且可以存储更多的图片而不仅仅只是6张。
首先,我们和前面一样存储6张图片:
下一步我们将存储的图片数量增加到24:
相比于传统的Hopfield网络,增加的存储容量可以将紧密联系在一起的模式分开。我们现在可以将有强关联的模式区分开,而且可以从许多存储的模式中恢复出来原始模式。
连续值的模式和状态下的新的能量函数
我们将等式(13)中的能量函数泛化到连续空间中去。我们使用等式(13)中的能量的负值的对数再加上一个多项式,这个多项式可以保证状态$\xi$的范数是有限的。新的能量函数定义为:
这个能量函数是根据所存储的模式$X=(x_1, ... x_N)$来构建的,其中$M$是所有存储模式范数的最大值。
根据 Krotov 和 Hopfield 的新论文,新的Hopfield网络中所存储的模式$X^T$可以看作是从$\xi$到隐单元的权重,同时$X$可以看作是隐单元到$\xi$的权重。使用这种解释,我们并没有存储模式,而只是在我们的模型中使用和经典Hopfield网络中同样的权重。
等式(18)的能量函数可以使用凹凸过程(Concave-Convex-Procedure)为$\xi$派生出一个更新规则,详见 Yuille 和 Rangarajan的论文。
- 能量$E(\xi)$可以分成一个凸分量和一个凹分量:$E(\xi) = E_1(\xi) + E_2(\xi)$
- 项 $\frac{1}{2}\xi^T\xi + C = E_1(\xi)$是凸的(C是与$\xi$独立的一个常量)
- 项 $lse(\beta, X^T\xi) = E_2(\xi)$是凹的(由于lse的Hessian矩阵是半正定的,因此lse是凹的, 详情可见论文附录)
- 将CCCP应用到能量函数E上有:
其中 $\nabla_{\xi} lse(\beta, X^T\xi) = X softmax(\beta X^T \xi)$.因此状态模式$\xi$的更新规则如下:
应用凹凸过程得到更新规则,保证了能量函数的单调递减。
我们新的能量函数最重要的性质是:
- 全局收敛到局部最小值
- 指数级存储容量
- 一次更新后即可收敛
指数存储容量和一次更新收敛和Demircigil等人的论文中一样。全局收敛到局部最小值指的是通过公式(22)迭代得到的极值点都是公式(18)所代表的能量函数的稳定点(极值点或鞍点)(几乎可以肯定没有最大值,并且鞍点在实验中从来没有出现)。
新的连续能量函数让我们可以把例子扩展到连续空间下。下面,我们用公式(22)从连续空间下所存储的Hopfield网络中检索图片Homer。我们先将输入图像转换为灰度图像:
下面我们进行与之前一样的实验,只不过这次是在连续空间下做的:
我们再次看到Homer被完美地恢复了。我们已经考虑了模式之间有足够的差异的情况。在此情形下,迭代可以收敛到一个固定点,该固定点靠近我们所存储模式中的一个。然而,如果一些存储的模式彼此很相似,则在相似的模式附近就出现亚稳态。从这个亚稳态附近或从彼此很相似的模式之一开始的迭代,就会收敛到这个亚稳态。学习过程的动态行为可由温度变量$\beta$的倒数控制,见式(22)(译者注:类似于模拟退火算法中的温度)。β值越大对应于温度越低,意味着单个模式的吸引盆保持分离,不太可能出现亚稳态。另一方面,β值越小对应于高温,更可能形成亚稳态。我们现在看同一个例子,但是我们用β=0.5代替β=8,恢复出的状态模式是多个存储模式的叠加。
为了看的更加清楚,我们改变$\beta$的值,来看看恢复出来的图片都是什么样的:
新能量函数对应的更新方式与transformer网络中的自注意力机制
从公式(21)开始:
- 将新的更新规则扩展到多个模式去
- 将这些模式映射到关联空间
- 映射结果
经过这三步我们就可以得到(自)注意力机制。下面我们一步步分开讲解这三步。
对于S个状态模式 $\Xi = (\xi_1, ..., \xi_S)$,公式(21)可以推广如下:
我们首先考虑将$X^T$看作$Y = (y_1, ..., y_N)^T$经过过$W_K$变换后被映射到一个相关联空间中的值;$\Xi^T$为$S$个状态模式$R=(\xi_1, ..., \xi_S)^T$经过$W_Q$被映射到另外一个相关空间中的值。我们设
则有:
在公式(24)和(25)中,$W_Q$和$W_K$是映射矩阵。注意在公式(27)中,是对矩阵$KQ^T$按列做softmax。下面我们对公式(27)进行转置,(此时softmax 按行进行) 可以得到:
现在我们只需要对$Q^{new}$使用另外一个矩阵$W_V$将其投影到另一个空间:
这样我们就得到了 transformer 的注意力机制。如果 $Y=(y_1, ..., y_N)^T$被当作$R$来用,我们就得到了transformer的自注意力机制(self-attention).
我们将公式(29)中的一些变量用 $Y$ 和 $R$ 进行替换之后得到:
这就是我们在pytorch中提供的Hopfield层的基础。
深度学习架构中的 Hopfield 层
基于我们对现代Hopfield网络的研究,我们引入了新的PyTorch Hopfield层。它可以插入到现有的深度神经网络中去,也可以用于多实例学习、基于集合和置换不变学习、联想学习等应用。我们引入了三种类型的Hopfield层:
- _Hopfield_: 用于关联和处理两个集合。比如 transformer attention,它关联keys和queries,再比如两个必须比较的点集。
- _HopfieldPooling_: 用于固定模式搜索、池化操作以及类似LSTM或GRU之类的记忆存储。在这里状态(查询)模式是静态的,而且可以通过学习得到。
- _HopfieldLayer_: 用于存储固定模式或学习中间原型。存储的(键)模式是静态的,而且可以通过学习得到。
Hopfield层是连续、可微的,它可以被集成到深度学习体系结构中,以使其获得联想记忆的能力。下图左侧描绘了一个标准的深度网络:它将一个向量或一组向量从输入传播到输出。右侧描绘了一个深度网络,其中每一层通过Hopfield层获得了联想记忆能力。各层的详细说明如下。
新的Pytorch Hopfield 层相比与transformer的(自)注意力层来说有以下额外的功能:
- 提供两个集合的相关性
- 变量$\beta$可以决定固定点的种类
- 多次更新以获取精确的固定点
- 关联空间的维度可以控制存储容量
- 静态模式可供固定模式搜索
- 可以对模式进行标准化来控制固定点的动态行为
新的Hopfield层可以用下图表示:
下面,我们介绍实现的底层机制。基于这些底层机制,我们给出了三个例子来展示如何使用新的Hopfield网络以及如何利用现代Hopfield网络的原理。
Hopfield 层
从最终模式$Z$的一般形式来看,Z是原始存储的模式 Y/R以及$W_Q$/$W_K$/$W_V$的函数:
令
这里$\overset{\backsim}{W}_V$的秩受到矩阵$W_KW_V$乘积的限制。为了增加Hopfield网络的灵活性,矩阵乘积$W_KW_V$可以使用一个参数矩阵来替换(代码中的flag)。在这种情况下$\overset{\backsim}{W}_V$不再是等式(32)中的乘积,而是一个独立的参数矩阵,就像在原始的transformer中设置的一样。
在等式(31)中,$Y=(y_1, ..., y_N)^T$和$R=(r_1, ..., r_S)^T$被通过矩阵$W_K$和$W_Q$映射到关联空间中去。我们还允许静态状态以及静态存储模式。静态模式的意思是它不依赖于网络的输入,也就是说,它只由偏置的权重来决定,它对于任何网络输入都保持不变。
注意
为了简单起见,我们从现在开始仅用$W_V$表示$W_KW_V$。
等式(31)的一个示意图如下图所示:
注意这里的$W_V$已经包含了输出映射。
哪些矩阵被使用到依赖于底层的任务类型,比如:上图网络结构代码如下:
hopfield = Hopfield(
input_size=3, # R
hidden_size=3,
stored_pattern_size=4, # Y
pattern_projection_size=4, # Y
scaling=beta)
# tuple of stored pattern, state pattern, pattern projection
hopfield((Y, R, Y))
HopfieldLayer 层
当然我们可以使用新的Hopfield层来解决我们上面描述的模式恢复任务。对于这个任务来说,不需要可训练的权重。
hopfield = Hopfield(
scaling=beta,
# do not project layer input
state_pattern_as_static=True,
stored_pattern_as_static=True,
pattern_projection_as_static=True,
# do not pre-process layer input
normalize_stored_pattern=False,
normalize_stored_pattern_affine=False,
normalize_state_pattern=False,
normalize_state_pattern_affine=False,
normalize_pattern_projection=False,
normalize_pattern_projection_affine=False,
# do not post-process layer output
disable_out_projection=True)
# tuple of stored pattern, state pattern, pattern projection
hopfield((Y, R, Y))
通过HopfieldLayer进行Hopfield查找
我们Hopfield模块的一个变体就是可以引入一个可训练但是和输入独立的查找机制。在内部,一个或者多个存储模式和模式映射被训练出来(可以选择一种非共享的方式),然后它们就可以用作一个独立于输入的查找机制。
hopfield_lookup = HopfieldLayer(
input_size=3, # R
hidden_size=3, # W_K
pattern_size=4, # W_V
quantity=4, # W_K
scaling=beta,
stored_pattern_as_static=True,
state_pattern_as_static=True)
# state pattern
hopfield_lookup(R)
Hopfield层的这种特殊变体允许将训练数据用作存储模式,新数据用作状态模式,训练标签用于投影Hopfield层的输出。
hopfield_lookup = HopfieldLayer(
input_size=3, # R
hidden_size=3, # W_K
quantity=4, # W_K
scaling=beta,
lookup_weights_as_separated=True,
lookup_targets_as_trainable=False,
stored_pattern_as_static=True,
state_pattern_as_static=True,
pattern_projection_as_static=True)
# state pattern
hopfield_lookup(R)
HopfieldPooling 层
如果只存在一个静态模式(查询),我们将Hopfield层视为池化层。然后,它实际上是在序列方向上的一个池化操作。静态模式被认为是一个原型模式,因此在Hopfield池化层学习。下面我们给出了存储模式$Y$上Hopfield池化层的两个例子。请注意,池化总是在序列长度的维度上运行,而不是在token embedding的维度上运行。
hopfield_pooling = HopfieldPooling(
input_size=4, # Y
hidden_size=3, # Q
scaling=beta,
quantity=2) # number of state patterns
# stored_pattern and pattern_projection
hopfield_pooling(Y)
在序列方向上进行池化操作实际上就是在所存储模式的token维度上完成的: $Y \in R^{(24)} \to Z \in R^{(24)}$。
这里我们展示另外一个例子:$Y \in R^{(35)} \to Z \in R^{(25)}$
hopfield_pooling = HopfieldPooling(
input_siz=5, # Y
hidden_size=3, # Q
scaling=beta,
quantity=2) # number of state patterns
# stored_pattern and pattern_projection
hopfield_pooling(Y)
DeepRC
现代Hopfield网络的一个应用可以在Widrich等人的论文Modern Hopfield Networks and Attention for Immune Repertoire Classification中找到,现代Hopfield网络的高存储容量被用来解决计算生物学中一个具有挑战性的多实例学习(MIL)问题,称为免疫库分类。
生物学背景
一个人的免疫系统由大量的免疫系统受体(和许多其他东西)组成。这些受体可以用长度可变的氨基酸序列和20个独特的字母来表示,其任务是选择性地与特定病原体的表面结构结合以对抗它们。只有一个可变的受体亚序列可能负责这种结合。由于病原体种类繁多,每个人都有大约$10^7–10^8$个独特的免疫受体,个体间的重叠度较低,它们是从超过$10^{14}$个潜在的受体中取样的。然而,这些受体中只有极少数能与单一特定病原体结合。这意味着一个个体对特定病原体(如特定疾病)表现出免疫反应的免疫系统应包含一些能与该特定病原体结合的序列。反过来说,为了将这些免疫系统分为有和无免疫反应的免疫系统,我们必须找到这个与特定病原体结合的可变亚序列。
因此,免疫系统的分类是非常困难的,因为每个免疫系统包含大量的序列作为实例,其中只有极少数通过携带某个可变子序列来指示正确的类别。这是大海捞针问题的一个突出例子,也是对机器学习方法的一个强烈挑战。
基于现代Hopfield网络,一种被称为DeepRC的方法被设计出来,它由三部分组成:
- 一个sequence-embedding神经网络来提供固定大小的序列表征 (比如 1维的CNN或LSTM),
- 一个Hopfield层用来做sequence-attention,以及
- 一个输出神经网络或者全连接网络作为输出。
下图展示了DeepRC的三个部分:
到目前为止,我们已经讨论了Hopfield层的两个用例:(i)默认设置,其中输入由存储模式和状态模式组成;(ii)Hopfield池化,在这里一个原型模式被学习出来(也就是向量$Q$被学习出来)。对于免疫序列分类,我们有另一个用例。现在Hopfield层的输入有一部分是通过神经网络获得的。
更确切地说,DeepRC attention机制的三个组成部分是:
- sequence-embedding 神经网络 $Y^T$ 的输出直接作为$V$值
- 第二个神经网络,比如:一个self-normalizing 神经网络 (SNN),它和sequence-embedding神经网络共享第一层,并且输出键值$K$,也就是存储的模式
- 与Hopfield池化操作类似,查询向量$Q$被学习出来并和我们正在寻找的子序列绑定。
下图将DeepRC的Hopfield层部分展示了出来:
需要注意的是,对于免疫库分类,实例的数量远远大于特征的数量(每个库大约有300k个实例)。更准确地说,大概相差$10^4$到$10^5$的量级。这在上图中也有表示: $Y^T$的列多于行。基于SNN的复杂的注意力机制大大降低了实例数量,同时将从输入到输出的神经网络的复杂度保持在了一个低水平。
DeepRC中使用Hopfield层的伪代码如下:
Y = EmbeddingNN(I) # e.g. 1D-CNN
K = SNN(Y)
Q = StatePattern(size=3)
hopfield = Hopfield(
scaling=beta,
# do not project layer input
state_pattern_as_static=True,
stored_pattern_as_static=True,
pattern_projection_as_static=True,
# do not pre-process layer input
normalize_stored_pattern=False,
normalize_stored_pattern_affine=False,
normalize_state_pattern=False,
normalize_state_pattern_affine=False,
normalize_pattern_projection=False,
normalize_pattern_projection_affine=False,
# do not post-process layer output
disable_out_projection=True)
# tuple of stored_pattern, state_pattern, pattern_projection
hopfield((K, Q, Y))
相关素材
- 论文本体: Hopfield Networks is All You Need
- Github 仓库: hopfield-layersDeepRC
- 论文:Modern Hopfield Networks and Attention for Immune Repertoire Classification
- Yannic Kilcher's 对于我们两篇论文的讲解视频
- 博客:从 Hopfield角度看 Performers
联系
Johannes Brandstetter 邮箱:brandstetter@ml.jku.at
更多推荐