霍普菲尔德(Hopfield Network)网络
背景介绍
Hopfield网络,由美国物理学家约翰·霍普菲尔德(John Hopfield)于1982年提出,它是早期人工神经网络的重要代表,对后续神经网络的发展产生了深远影响。
约翰·霍普菲尔德他的研究领域很广阔,包括物理学、生物分子学、神经科学,他的个人荣誉也很广阔,包括美国科学院院士、美国文理学院院士、美国哲学学会会士、美国物理学会会长等。
神经网络的研究最早可以追溯到由麦卡洛克(McCulloch),皮茨(Pitts)在1943年提出的麦卡洛克-皮茨模型(McCulloch-Pitts Model,简称MP模型),MP模型虽然展示了神经元可以通过简单的数学运算实现逻辑功能,但它缺乏学习机制,无法通过训练调整权重。
在1958年,美国科学家弗兰克·罗森布拉特(Frank Rosenblatt)在MP模型的基础上提出了感知机模型。但是当时的感知机还是太简单了,例如:单层的感知机还无法之下异或运算,多层感知机则类似一个黑盒,很多东西都不透明,模型的解释性不强,参数过多容易出错,过度拟合无法保证出现全局最优解等等问题的存在。这使得它没有地方可以很好的运用,因此神经网络的研究进入了长达几十年的低潮期。
神经网络研究的低潮在1982年Hopfield网络的提出之后,被彻底打破!Hopfield网络在联想记忆、数据恢复、优化问题求解等地方有了实际的应用价值。而且它为神经网络的研究提供了新的视角和方法,推动了神经网络技术从简单的线性模型向复杂的非线性动态系统发展。霍普菲尔德网络的成功也体现了跨学科研究的力量,为后续的人工智能和神经科学的发展开辟了新的道路。
基本原理引入
霍普菲尔德网络(Hopfield Network)是一种典型的联想记忆网络(存储网络)。它通过对神经元状态进行迭代更新,使整个网络动态演化逐步收敛到稳定的平衡状态。网络在这些稳定状态中存储了模式,当输入一个残缺或含噪的模式时,系统会自动演化并收敛到与之最接近的稳定状态,从而实现模式的恢复与回忆。
这样可能很笼统很难理解,下面我会具体直观的讲解。
霍普菲尔德网络可以在无需逐个检查的方式下,将现有记忆和新输入之间建立联系。这看似不可能,但我们能从看似无关的生物学领域汲取灵感。
蛋白质是由氨基酸长链折叠成特定的三维结构的,蛋白质在三维空间中的排列方式难以计数,有研究证明,即使蛋白质在纳秒尺度采样不同的构想,达到正确配置的时间仍然超过宇宙年龄的时间。但实际上通常在毫米级别的时间就能快速的完成折叠。
有些人可能不太理解,会提问道:“蛋白质折叠成特定形状为什么不是蛋白质记忆了这种形状,直接按照特定形状折叠呢?”
将蛋白质折叠联想到“记忆”是很自然的,因为它确实展现出一种非随机的、有方向性的特性,似乎“知道”如何快速找到正确的终点,但是蛋白质的折叠不用记忆来描述。先来解释记忆:记忆是需要储存的,然后在需要的时候检索,但是蛋白质的折叠,显然没有检索这一个过程。
实际上,蛋白质的折叠只是落入最稳定有力的配置,就好似,石头从山谷滚落落入谷底的状态。让我们引入能量的概念,对于蛋白质,我们将对蛋白质链中原子相互作用储存的势能做研究。蛋白质的每种形态都会有不同的势能水平,这由这些原子相互作用的总和决定。我们可以抽象的将其视为只有二维,那么能量函数可以视为一个平面,每一个点,都代表一个蛋白质。
点的海拔代表该配置的势能,这就是所谓的蛋白质能量景观 。
蛋白质分子,倾向于最小化它的势能,自然的寻求尽可能低的能量水平。在折叠的过程中,它本质上是在能量景观上面,沿漏斗状路径 高效地向最低自由能滑落。
这就是蛋白质为什么折叠这么快的原因,它不需遍历所有的情况,他们只需要沿着物理系统的自然趋势,将其势能最小化,就能得到目标结构。现在让我们将这种思想关联到霍普菲尔德网络,来实现类似的效果。
假设我们有一个系统,它可以通过T 与其状态 之间的相互作用来编码信息,并且每种配置都具有特定的势能。首先我们需要创建能量景观,来将我们储存的记忆或者状态模式对应能量表面的中的最小值。其次我们需要有推动其到能量最小化的机制,来驱动状态的变化。这样我们就能模拟蛋白质的折叠过程,将残缺或者含噪声的输入,迅速的恢复到初始状态(也就是迅速的到达能量最小值),可能你还一知半解为什么要这样做,请你接着往下看。
Hopfield 网络架构
构建能量景观
构建能量景观当然要知道什么是能量,首先我们得了解一些神经元的基本概念。
考虑一组神经元,他们可以为“+1”,“-1”两种状态,每一个神经元都和其他的神经元连接(全连接),他们中间都有相对应的权重,这里只考虑对称的权重(即神经元i到神经元j的权重和神经元j到神经元i的权重相等),之后在来解释原因。
我们先来解释一下权重(Wij )的概念。例如Wij 是一个正数,那么意味着神经元i和j都紧密耦合,一个神经元会激发另一个神经元。在这种情况下,一个神经元处于激活状态,那么另一个神经元也倾向于激活状态,同样,一个神经元处于抑制状态,那么另一个神经元也倾向于抑制状态,即促进它们状态上的一致性 。相反,如果Wij <0,那么神经元 i 和神经元 j 之间的连接会促进它们状态上的不一致性 。
权重在霍普菲尔德网络中代表着什么:霍普菲尔德网络的工作原理是将要存储的记忆模式(或称为原型模式)编码到这些连接权重中。他的存储是一种写入式的存储,它不需要迭代训练,而是直接通过存储的模式计算权重矩阵。通常,这是通过赫布学习规则(Hebbian learning rule)的变体来实现的。
如果一对神经元 i 和 j 在许多被存储的模式中都处于相同 的状态(比如在模式A中都是+1,在模式B中都是-1),那么它们之间的权重 Wij 就会被加强为正值 。
如果它们在许多被存储的模式中都处于相反 的状态(比如在模式A中是+1/-1,在模式B中是-1/+1),那么它们之间的权重 Wij 就会被加强为负值 。
如果连接权重 Wij 期望神经元 i 和 j 的状态相同(即 Wij >0),但实际上它们的当前状态是相反的(si sj =−1),那么这就产生了冲突 。相反,如果 Wij 期望它们状态相反(即 Wij <0),但实际上它们的当前状态是相同的(si sj =+1),这也产生了冲突 。
注意: 我们会提前训练好霍普菲尔德网络,让他的权重固定,然后根据输入的数据(残缺的含噪声的数据),来确定神经元的状态。
我们认为,高能量状态对应着混乱,无序,反之,低能量状态对应着稳定,有序。所以我们将冲突的总量的大小,映射为能量的大小。 寻找最低能量态,正是为模式去噪,恢复的过程。
我们可以总结一下:Wij xi xj 越大(越接近正无穷)表明当前神经元对的状态与网络所存储的记忆模式之间的契合度越高(冲突越小),从而使系统更接近其稳定态(目标结果)。我们同样将这个概念拓展到整个网络,我们要寻求的是最大的“∑ i ≠ j W i j s i s j \sum_{i \neq j} W_{ij} s_i s_j ∑ i = j W i j s i s j ”。为了对应最小化能量的概念,我们可以转换成求最小的“-∑ i ≠ j W i j s i s j \sum_{i \neq j} W_{ij} s_i s_j ∑ i = j W i j s i s j ”,最终我们将其(-∑ i ≠ j W i j s i s j \sum_{i \neq j} W_{ij} s_i s_j ∑ i = j W i j s i s j )定义为能量计算公式 。从而计算能量的大小,构建能量景观。
注意: 这只是一个概念公式,并不是真正的能量公式。
构建到能量最小值的驱动力
由上文可知,我们希望霍普菲尔德网络能逐渐向能量最小值演化,但是观察公式,我们可以观察出,能量的大小取决于神经元的状态和权重两个部分。
整个权重矩阵 W 实际上就包含了网络所有学习到的、期望的 神经元状态配置,这些配置对应着能量景观中的局部最小值,也就是网络的记忆。
假设我们已经通过学习确定了权重的大小,但是神经元的静默状态是完全未知的,那么问题变成了,如何找到可以最小化整个网络的能量的状态模式。我们当然不会遍历所有的情况,我们会从其中一个记忆的部分或者噪声版本开始,或者是完全随机的配置,随后我们有两种方式来找到最小能量,同步更新和异步更新。
异步更新:在异步更新 模式下,在每个时间步,只选择一个神经元进行更新 。我们可以随机的选择或者顺序的选择要更新的神经元,把更新的神经元设置为“i”,我们将会计算所有其他神经元对神经元 i 的加权影响之和 ∑ i ≠ j W i j s j \sum_{i \neq j} W_{ij} s_j ∑ i = j W i j s j ,将这个结果表示为hi (净输入)。如果hi >0那么表示其他神经元赞成这个hi 为“+1”,反之,如果hi <0,那么,其他神经元赞成这个hi 为“-1“。如果hi =0,会保持当前状态或者随机选择状态。
同步更新:在同步更新 模式下,在每个时间步,所有神经元都根据它们在上一时刻的状态同时计算净输入,并同时更新自己的状态 。更新规则与异步更新一致。
最终我们会到达一种配置,反转任何神经元都会导致,整体能量的上升。这时候我们认为网络已经收敛到稳定状态,也就是整体能量的最小值。我们用这种更新模式,来代替热力学第二定律。
注释 :尽管同步更新在并行计算中看似更高效,但在霍普菲尔德网络的理论分析和实际应用中,异步更新更为常见和推荐 。这是因为它能够严格保证网络的收敛性 ,确保网络总会找到一个稳定的记忆模式,而不会陷入无限循环或不可预测的振荡。这种确定性的收敛行为对于联想记忆的任务至关重要。
公式解释
E = − 1 2 ∑ i ≠ j W i j s i s j E = -\frac{1}{2} \sum_{i \neq j} W_{ij} s_i s_j E = − 2 1 ∑ i = j W i j s i s j 这是霍普菲尔德网络的能量公式,让我们来解释以下几个问题。
1/2的由来
− ∑ i ≠ j W i j s i s j - \sum_{i \neq j} W_{ij} s_i s_j
− i = j ∑ W i j s i s j
这个这部分的含义在上文中讲解过,不多做赘述,其中Wij 代表权重,si和sj代表神经状态,公式表示我们对所有不同的 神经元对 (i,j) 进行求和,且 i不等于j。例如:我们在求和的时候,分别加了W i j s i s j W_{ij} s_i s_j W i j s i s j 和 W j i s j s i W_{ji} s_j s_i W j i s j s i ,换句话说,求和实际上会把每对神经元之间的相互作用计算两次 :一次是 (i,j),另一次是 (j,i)。这两项的结果是完全相同的,为了对每一个神经元都只作用一次,所以我们要乘1/2来抵消这个重复的计数。
能量函数中 1 2 \frac{1}{2} 2 1 的存在使数学推导变得更加简洁和优雅
在异步更新中,我们随机选择一个神经元 k ,并假设只有它的状态从 s k s_k s k 变为 s k ′ s_k' s k ′ ,而其他所有神经元 j ≠ k j \neq k j = k 的状态 s j s_j s j 保持不变。
能量变化 Δ E \Delta E Δ E 可以表示为新能量 E ′ E' E ′ 减去旧能量 E E E :
Δ E = E ′ − E \Delta E = E' - E
Δ E = E ′ − E
我们来分析能量函数中哪些项会发生变化。只有包含 s k s_k s k 的项会改变。
旧能量 E 为:
E = − 1 2 ( ∑ j ≠ k W k j s k s j + ∑ i ≠ k W i k s i s k + ∑ i ≠ k , j ≠ k W i j s i s j ) E = -\frac{1}{2} \left( \sum_{j \neq k} W_{kj} s_k s_j + \sum_{i \neq k} W_{ik} s_i s_k + \sum_{i \neq k, j \neq k} W_{ij} s_i s_j \right)
E = − 2 1 ⎝ ⎛ j = k ∑ W k j s k s j + i = k ∑ W i k s i s k + i = k , j = k ∑ W i j s i s j ⎠ ⎞
新能量 E’ 为:
E ′ = − 1 2 ( ∑ j ≠ k W k j s k ′ s j + ∑ i ≠ k W i k s i s k ′ + ∑ i ≠ k , j ≠ k W i j s i s j ) E' = -\frac{1}{2} \left( \sum_{j \neq k} W_{kj} s_k' s_j + \sum_{i \neq k} W_{ik} s_i s_k' + \sum_{i \neq k, j \neq k} W_{ij} s_i s_j \right)
E ′ = − 2 1 ⎝ ⎛ j = k ∑ W k j s k ′ s j + i = k ∑ W i k s i s k ′ + i = k , j = k ∑ W i j s i s j ⎠ ⎞
注意,最后一个求和项 ∑ i ≠ k , j ≠ k W i j s i s j \sum_{i \neq k, j \neq k} W_{ij} s_i s_j ∑ i = k , j = k W i j s i s j 在 E E E 和 E ′ E' E ′ 中是相同的,因为它不包含 s k s_k s k 。所以它在相减时会被抵消。
那么 Δ E \Delta E Δ E 就简化为:
Δ E = − 1 2 ( ∑ j ≠ k W k j s k ′ s j + ∑ i ≠ k W i k s i s k ′ ) − ( − 1 2 ( ∑ j ≠ k W k j s k s j + ∑ i ≠ k W i k s i s k ) ) \Delta E = -\frac{1}{2} \left( \sum_{j \neq k} W_{kj} s_k' s_j + \sum_{i \neq k} W_{ik} s_i s_k' \right) - \left( -\frac{1}{2} \left( \sum_{j \neq k} W_{kj} s_k s_j + \sum_{i \neq k} W_{ik} s_i s_k \right) \right)
Δ E = − 2 1 ⎝ ⎛ j = k ∑ W k j s k ′ s j + i = k ∑ W i k s i s k ′ ⎠ ⎞ − ⎝ ⎛ − 2 1 ⎝ ⎛ j = k ∑ W k j s k s j + i = k ∑ W i k s i s k ⎠ ⎞ ⎠ ⎞
由于权重的对称性 W i k = W k i W_{ik} = W_{ki} W i k = W k i ,我们可以将第二个求和项中的 W i k s i s k W_{ik} s_i s_k W i k s i s k 改写为 W k i s i s k W_{ki} s_i s_k W k i s i s k 。 所以:
Δ E = − 1 2 ( ∑ j ≠ k W k j s k ′ s j + ∑ j ≠ k W k j s j s k ′ ) + 1 2 ( ∑ j ≠ k W k j s k s j + ∑ j ≠ k W k j s j s k ) \Delta E = -\frac{1}{2} \left( \sum_{j \neq k} W_{kj} s_k' s_j + \sum_{j \neq k} W_{kj} s_j s_k' \right) + \frac{1}{2} \left( \sum_{j \neq k} W_{kj} s_k s_j + \sum_{j \neq k} W_{kj} s_j s_k \right)
Δ E = − 2 1 ⎝ ⎛ j = k ∑ W k j s k ′ s j + j = k ∑ W k j s j s k ′ ⎠ ⎞ + 2 1 ⎝ ⎛ j = k ∑ W k j s k s j + j = k ∑ W k j s j s k ⎠ ⎞
合并相同的项:
Δ E = − 1 2 ( 2 ∑ j ≠ k W k j s k ′ s j ) + 1 2 ( 2 ∑ j ≠ k W k j s k s j ) \Delta E = -\frac{1}{2} \left( 2 \sum_{j \neq k} W_{kj} s_k' s_j \right) + \frac{1}{2} \left( 2 \sum_{j \neq k} W_{kj} s_k s_j \right)
Δ E = − 2 1 ⎝ ⎛ 2 j = k ∑ W k j s k ′ s j ⎠ ⎞ + 2 1 ⎝ ⎛ 2 j = k ∑ W k j s k s j ⎠ ⎞
这里的 − 1 2 -\frac{1}{2} − 2 1 和 2 相乘,就变成了 -1 和 +1。
Δ E = − ∑ j ≠ k W k j s k ′ s j + ∑ j ≠ k W k j s k s j \Delta E = - \sum_{j \neq k} W_{kj} s_k' s_j + \sum_{j \neq k} W_{kj} s_k s_j
Δ E = − j = k ∑ W k j s k ′ s j + j = k ∑ W k j s k s j
整理得到:
Δ E = ∑ j ≠ k W k j s j s k − ∑ j ≠ k W k j s k ′ s j \Delta E = \sum_{j \neq k} W_{kj} s_j s_k - \sum_{j \neq k} W_{kj} s_k' s_j
Δ E = j = k ∑ W k j s j s k − j = k ∑ W k j s k ′ s j
Δ E = ( ∑ j ≠ k W k j s j ) ( s k − s k ′ ) \Delta E = \left( \sum_{j \neq k} W_{kj} s_j \right) (s_k - s_k')
Δ E = ⎝ ⎛ j = k ∑ W k j s j ⎠ ⎞ ( s k − s k ′ )
我们定义神经元 k 的净输入为 h k = ∑ j ≠ k W k j s j h_k = \sum_{j \neq k} W_{kj} s_j h k = ∑ j = k W k j s j 。 所以,最终得到:
Δ E = h k ( s k − s k ′ ) \Delta E = h_k (s_k - s_k')
Δ E = h k ( s k − s k ′ )
如果没有1 2 \frac{1}{2} 2 1 那么最终的结果是:
Δ E ∗ = 2 h k ( s k − s k ′ ) \Delta E^* = 2 h_k (s_k - s_k') Δ E ∗ = 2 h k ( s k − s k ′ )
有了− 1 2 -\frac{1}{2} − 2 1 ,能量变化 Δ E \Delta E Δ E 的表达式直接地 、简洁地 呈现为 h k ( s k − s k ′ ) h_k (s_k - s_k') h k ( s k − s k ′ ) 。这使得能量变化与神经元 k 的净输入 h k h_k h k 和其状态变化 ( s k − s k ′ ) (s_k - s_k') ( s k − s k ′ ) 之间存在一对一的、没有额外常数因子的关系。
为什么权重要对称
保持权重的对称性其实主要是为了避免霍普菲尔德网络陷入周期性振荡,而不是收敛到单一稳定模式。
这其中涉及到的是能量函数(或李亚普诺夫函数)的存在性 。简单的理解能量函数,你可以认为能量的最低点就是稳定点 (正定性),而整个系统,在演化过程中,总会保持能量的不变或减少 (负半定性)完美符合蛋白质的折叠过程不是吗? ,这两个性质在霍普菲尔德网络中其中决定性的作用。
在异步更新 且权重对称 的情况下,霍普菲尔德定义的能量函数
E = − 1 2 ∑ i ≠ j W i j s i s j E = -\frac{1}{2} \sum_{i \neq j} W_{ij} s_i s_j
E = − 2 1 i = j ∑ W i j s i s j
就符合 了李亚普诺夫函数的关键性质。
由于满足了这些条件,我们就可以确信,霍普菲尔德网络在回忆阶段 总会收敛到一个能量的局部最小值 ,而不会陷入无限循环或混沌状态。这些局部最小值正是网络预先存储的记忆模式。
拓展探讨
为什么霍普菲尔德网络不能处理异或问题?
霍普菲尔德网络不能处理异或问题是因为异或是一个非线性可分问题,而霍普菲尔德网络本质上是一种线性模型,无法通过简单的线性组合来区分异或问题中不同的输入组合所对应的输出类别。
注释 :
异或(XOR)是一个经典的二元逻辑运算,其真值表如下:
输入 A
输入 B
输出 (A XOR B)
0
0
0
0
1
1
1
0
1
1
1
0
线性可分 :如果在一个特征空间中,能够找到一个超平面(hyperplane)将不同类别的样本点完全地分开,那么这些样本就是 线性可分 的。
二维空间:超平面为直线。
三维空间:超平面为平面。
更高维度空间: 超平面是高维的线性边界
例如:
一个简单的AND门问题:
(0,0) -> 0
(0,1) -> 0
(1,0) -> 0
(1,1) -> 1
绿线将两类点完全分开。
线性不可分 :如果在一个特征空间中,无法 找到一个超平面将不同类别的样本点完全地分开,那么这些样本就是线性不可分 的。
例如:
异或(XOR)问题:
(0,0) -> 0
(0,1) -> 1
(1,0) -> 1
(1,1) -> 0
没有线可以将两类点分开。
霍普菲尔德网络中单个神经元的状态更新依据其净输入 :h i = ∑ i ≠ j W i j s j h_i = \sum_{i \neq j} W_{ij} s_j h i = ∑ i = j W i j s j 。神经元 i 的新状态将根据 hi 的正负号(例如,若 hi≥0 则为 +1,若 hi<0 则为 −1)来确定。但是当hi =0的时候,这个方程定义了一个线性决策边界 (在多维输入空间中表现为一个超平面)。这意味着,无论网络如何通过学习调整权重 Wij ,每个神经元在决定自身状态时,都只能通过一个线性的“切割”来区分其输入模式。然而,异或 (XOR) 问题 本质上是线性不可分 的。它的输入-输出模式无法通过单一的直线(或更高维度的超平面)在原始输入空间中被完全区分开来。
如何在霍普菲尔德网络中存储多个模式
我们可以简单的将每个模式叠加在一起,根据以下等式计算权重。
这将把每一个模式都变成局部最小值,具体的内容可以阅读文献来获取答案。
Hopfield Networks: Neural Memory Machines | Towards Data Science
Hopfield Networks is All You Need | hopfield-layers
为每个模式独立的挖掘能量井,然后将能量景观叠加在一起。
这进而引出了或普菲尔德网络的局限性 :
在网络中,我们能塑造的能量谷是有限的,如果我们存储过多的模式,那么我们将无法可靠的收敛到储存的模式,甚至出现奇怪的中间记忆,这个模式的数量取决于神经网络的数量,大约是神经原网络的0.14倍。
但是如果两个模式有很强的相似性或者相关性,那么他们会在达到最大模式存储数量之前,就会产生干扰。后面我会实际演示证明。
赫布学习法不分正反
赫布学习(Hebbian learning)的核心思想是“一同激发的神经元,会连接在一起 ”(“Neurons that fire together, wire together”)。它会无差别地强化那些同时活跃的神经元之间的连接,而不会“判断”这些连接是好是坏,或者学习到的模式本身是否“理想” 。
赫布学习不分正反意味着赫布学习规则在更新权重时,不会区分模式的方向。
这意味着在如果模式P被存储,那么模式-P也会被存储,而且,在赫布学习下,这两个模式在权重更新时是等价的。体现在霍普菲尔德网络中,实际的结果就会产生,生成与目标模式完全相反的情况。
下面通过代码来展现
1 2 import numpy as npimport matplotlib.pyplot as plt
1 2 3 4 5 6 7 8 def train (patterns ): num_neurno = patterns[0 ].shape[0 ] w = np.zeros((num_neurno,num_neurno)) for pattern in patterns: pattern = pattern.reshape(-1 ,1 ) w += np.dot(pattern,pattern.T) np.fill_diagonal(w,0 ) return w
1 2 3 def E (state,w ): e = -0.5 *np.dot(state.T,np.dot(w,state)) return e
1 2 3 4 5 6 7 8 9 10 11 12 def update (state,w,index ): net_input = np.dot(w[index,:],state) if net_input > 0 : new_state = 1 elif net_input <0 : new_state = -1 else : new_state = state[index] changed = (new_state != state[index]) state[index]= new_state return state,changed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def work (initial_state,w,n = 1000 ): current_state = np.copy(initial_state) e_history = [E(initial_state,w)] num_neuron = initial_state.shape[0 ] state_history = [np.copy(initial_state)] for i in range (n): index = np.random.randint(num_neuron) current_state,changed = update(current_state,w,index) e_history.append(E(current_state, w)) state_history.append(np.copy(current_state)) return current_state, e_history, state_history
1 2 def create_random_pattern (num_neurons ): return np.random.choice([-1 , 1 ], size=num_neurons)
1 2 3 4 5 6 7 8 9 def add_noise (pattern, noise_level=0.1 ): noisy_pattern = np.copy(pattern) num_to_flip = int (np.round (len (pattern) * noise_level)) if num_to_flip == 0 and noise_level > 0 and len (pattern) > 0 : num_to_flip = 1 flip_indices = np.random.choice(len (pattern), num_to_flip, replace=False ) noisy_pattern[flip_indices] *= -1 return noisy_pattern
1 2 def get_overlap (state1, state2 ): return np.dot(state1, state2) / len (state1)
1 2 3 4 5 6 7 num_neurons = 10 max_iterations = 500 print (f"--- 霍普菲尔德网络可视化 ({num_neurons} 个神经元) ---" )print (f"理论容量上限约: {0.138 * num_neurons:.2 f} 个模式" )--- 霍普菲尔德网络可视化 (10 个神经元) --- 理论容量上限约: 1.38 个模式
1 2 plt.rcParams['font.sans-serif' ] = ['SimHei' ] plt.rcParams['axes.unicode_minus' ] = False
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 print ("\n--- 实验1: 赫布学习的正面情况 (成功召回) ---" )num_patterns_recall = 2 np.random.seed(42 ) stored_patterns_recall = [create_random_pattern(num_neurons) for _ in range (num_patterns_recall)] print (f"存储了 {num_patterns_recall} 个模式:" )for i, p in enumerate (stored_patterns_recall): print (f" 模式 {i+1 } : {p} " ) w_recall = train(stored_patterns_recall) plt.figure(figsize=(14 , 6 )) for i, original_pattern in enumerate (stored_patterns_recall): noise_level = 0.2 initial_state = add_noise(original_pattern, noise_level) print (f"\n测试模式 {i+1 } :" ) print (f" 初始状态 (加{int (noise_level*100 )} %噪声): {initial_state} " ) final_state_recall, e_history_recall, state_history_recall = work(initial_state, w_recall, n=max_iterations) overlaps_recall = [get_overlap(original_pattern, s) for s in state_history_recall] plt.subplot(1 , 2 , 1 ) plt.plot(e_history_recall, label=f'模式 {i+1 } 能量' ) plt.title('能量演化 (成功召回)' ) plt.xlabel('迭代次数' ) plt.ylabel('能量 (E)' ) plt.grid(True ) plt.subplot(1 , 2 , 2 ) plt.plot(overlaps_recall, label=f'模式 {i+1 } 重叠度' ) plt.title('与原始模式重叠度演化 (成功召回)' ) plt.xlabel('迭代次数' ) plt.ylabel('重叠度' ) plt.grid(True ) plt.ylim(-1.1 , 1.1 ) plt.subplot(1 , 2 , 1 ) plt.legend() plt.subplot(1 , 2 , 2 ) plt.legend() plt.tight_layout() plt.show() --- 实验1 : 赫布学习的正面情况 (成功召回) --- 存储了 2 个模式: 模式 1 : [-1 1 -1 -1 -1 1 -1 -1 -1 1 ] 模式 2 : [-1 -1 -1 -1 1 -1 1 1 1 -1 ] 测试模式 1 : 初始状态 (加20 %噪声): [-1 1 1 -1 -1 1 -1 -1 1 1 ] 测试模式 2 : 初始状态 (加20 %噪声): [-1 -1 1 -1 1 -1 1 -1 1 -1 ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 print ("\n" + "=" * 50 )print ("--- 实验2: 赫布学习的反面情况 (存储容量限制) ---" )num_patterns_capacity = 20 np.random.seed(43 ) stored_patterns_capacity = [create_random_pattern(num_neurons) for _ in range (num_patterns_capacity)] print (f"尝试存储 {num_patterns_capacity} 个模式 (超过 {num_neurons} 神经元理论容量 {0.138 * num_neurons:.2 f} ):" )w_capacity = train(stored_patterns_capacity) plt.figure(figsize=(14 , 6 )) test_pattern_idx = 0 original_pattern_test = stored_patterns_capacity[test_pattern_idx] noise_level_capacity = 0.05 initial_state_capacity = add_noise(original_pattern_test, noise_level_capacity) print (f"\n测试存储模式 {test_pattern_idx+1 } (加{int (noise_level_capacity*100 )} %噪声):" )print (f" 初始重叠度: {get_overlap(original_pattern_test, initial_state_capacity):.2 f} " )final_state_capacity, e_history_capacity, state_history_capacity = work(initial_state_capacity, w_capacity, n=max_iterations) overlaps_capacity = [get_overlap(original_pattern_test, s) for s in state_history_capacity] print (f" 最终状态与原始模式重叠度: {get_overlap(original_pattern_test, final_state_capacity):.2 f} " )is_correctly_recalled = False for j, stored_p in enumerate (stored_patterns_capacity): if np.array_equal(stored_p, final_state_capacity) or np.array_equal(stored_p, -final_state_capacity): print (f"最终状态与存储模式 {j+1 } 匹配。" ) is_correctly_recalled = True break if not is_correctly_recalled: print ("未能召回任何存储模式,很可能收敛到虚假态。" ) plt.subplot(1 , 2 , 1 ) plt.plot(e_history_capacity, label=f'容量测试能量' ) plt.title('能量演化 (容量限制)' ) plt.xlabel('迭代次数' ) plt.ylabel('能量 (E)' ) plt.grid(True ) plt.subplot(1 , 2 , 2 ) plt.plot(overlaps_capacity, label=f'与测试模式重叠度' ) plt.title('重叠度演化 (容量限制)' ) plt.xlabel('迭代次数' ) plt.ylabel('重叠度' ) plt.grid(True ) plt.ylim(-1.1 , 1.1 ) plt.subplot(1 , 2 , 1 ) plt.legend() plt.subplot(1 , 2 , 2 ) plt.legend() plt.tight_layout() plt.show() ================================================== --- 实验2 : 赫布学习的反面情况 (存储容量限制) --- 尝试存储 20 个模式 (超过 10 神经元理论容量 1.38 ): 测试存储模式 1 (加5 %噪声): 初始重叠度: 0.80 最终状态与原始模式重叠度: 0.00 最终状态与存储模式 3 匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 print ("\n" + "=" * 50 )print ("--- 实验3: 赫布学习的反面情况 (收敛到虚假态) ---" )num_patterns_spurious = 3 np.random.seed(44 ) stored_patterns_spurious = [create_random_pattern(num_neurons) for _ in range (num_patterns_spurious)] print (f"存储了 {num_patterns_spurious} 个模式:" )for i, p in enumerate (stored_patterns_spurious): print (f" 模式 {i+1 } : {p} " ) w_spurious = train(stored_patterns_spurious) random_initial_state = create_random_pattern(num_neurons) print (f"\n使用随机初始状态:" )print (f" 随机初始状态: {random_initial_state} " )plt.figure(figsize=(14 , 6 )) final_state_spurious, e_history_spurious, state_history_spurious = work(random_initial_state, w_spurious, n=max_iterations) initial_overlaps_with_stored = [get_overlap(p, random_initial_state) for p in stored_patterns_spurious] closest_initial_idx = np.argmax(np.abs (initial_overlaps_with_stored)) target_pattern_for_overlap = stored_patterns_spurious[closest_initial_idx] overlaps_spurious = [get_overlap(target_pattern_for_overlap, s) for s in state_history_spurious] print (f" 与初始最接近的存储模式 {closest_initial_idx+1 } 的重叠度: {get_overlap(target_pattern_for_overlap, random_initial_state):.2 f} " )print (f" 最终状态: {final_state_spurious} " )is_stored_pattern = False for i, p in enumerate (stored_patterns_spurious): if np.array_equal(p, final_state_spurious) or np.array_equal(p, -final_state_spurious): print (f"最终状态收敛到存储模式 {i+1 } 或其反向。" ) is_stored_pattern = True break if not is_stored_pattern: print (" 最终状态不是任何存储模式,很可能是一个虚假态。" ) print (" 请注意,能量通常会下降到局部最小值。" ) plt.subplot(1 , 2 , 1 ) plt.plot(e_history_spurious, label=f'虚假态能量' ) plt.title('能量演化 (虚假态)' ) plt.xlabel('迭代次数' ) plt.ylabel('能量 (E)' ) plt.grid(True ) plt.legend() plt.subplot(1 , 2 , 2 ) plt.plot(overlaps_spurious, label=f'与最接近存储模式 {closest_initial_idx+1 } 的重叠度' ) plt.title('重叠度演化 (虚假态)' ) plt.xlabel('迭代次数' ) plt.ylabel('重叠度' ) plt.grid(True ) plt.ylim(-1.1 , 1.1 ) plt.legend() plt.tight_layout() plt.show() print ("-" * 40 )================================================== --- 实验3 : 赫布学习的反面情况 (收敛到虚假态) --- 存储了 3 个模式: 模式 1 : [-1 1 1 1 1 1 -1 -1 1 1 ] 模式 2 : [-1 -1 1 1 -1 1 1 1 1 1 ] 模式 3 : [-1 1 1 -1 1 1 1 -1 -1 1 ] 使用随机初始状态: 随机初始状态: [-1 1 1 1 -1 -1 -1 1 1 -1 ] 与初始最接近的存储模式 3 的重叠度: -0.40 最终状态: [ 1 -1 -1 1 -1 -1 -1 1 1 -1 ] 最终状态收敛到存储模式 3 或其反向。
在实验二中,我们创建了20个模式,这个数量远超了霍普菲尔德网络的理论存储上限 。因此,网络在学习过程中表现不佳,未能收敛到任何预期的模式。
而在实验三中,我们采用了完全随机生成 的初始状态,而非从已存储的模式中选取并施加噪声。在这种情况下,尽管输入与任何已知模式都不相似,但代码依然收敛到一个稳定的状态 。这个状态与我们预期的存储模式截然相反,这意味着网络进入了一个虚假态 。也体现了赫布学习不分正反。
本文参照:A Brain-Inspired Algorithm For Memory