霍普菲尔德(Hopfield Network)网络

背景介绍

Hopfield网络,由美国物理学家约翰·霍普菲尔德(John Hopfield)于1982年提出,它是早期人工神经网络的重要代表,对后续神经网络的发展产生了深远影响。

约翰·霍普菲尔德他的研究领域很广阔,包括物理学、生物分子学、神经科学,他的个人荣誉也很广阔,包括美国科学院院士、美国文理学院院士、美国哲学学会会士、美国物理学会会长等。

神经网络的研究最早可以追溯到由麦卡洛克(McCulloch),皮茨(Pitts)在1943年提出的麦卡洛克-皮茨模型(McCulloch-Pitts Model,简称MP模型),MP模型虽然展示了神经元可以通过简单的数学运算实现逻辑功能,但它缺乏学习机制,无法通过训练调整权重。

在1958年,美国科学家弗兰克·罗森布拉特(Frank Rosenblatt)在MP模型的基础上提出了感知机模型。但是当时的感知机还是太简单了,例如:单层的感知机还无法之下异或运算,多层感知机则类似一个黑盒,很多东西都不透明,模型的解释性不强,参数过多容易出错,过度拟合无法保证出现全局最优解等等问题的存在。这使得它没有地方可以很好的运用,因此神经网络的研究进入了长达几十年的低潮期。

神经网络研究的低潮在1982年Hopfield网络的提出之后,被彻底打破!Hopfield网络在联想记忆、数据恢复、优化问题求解等地方有了实际的应用价值。而且它为神经网络的研究提供了新的视角和方法,推动了神经网络技术从简单的线性模型向复杂的非线性动态系统发展。霍普菲尔德网络的成功也体现了跨学科研究的力量,为后续的人工智能和神经科学的发展开辟了新的道路。

基本原理引入

霍普菲尔德网络是一种联想记忆网络,它通过网络的动态演化(神经元状态迭代更新)收敛到稳定状态,将存储的模式作为这些稳定状态来实现模式的存储和回忆。

这样可能很笼统很难理解,下面我会具体直观的讲解。

先来从他的作用说起,简单来说,霍普菲尔德网络是一种查询方式,在无需逐个检查的方式下将,现有记忆和新输入之间建立联系。这看似不可能,但我们能从看似无关的生物学领域汲取灵感。

蛋白质是由氨基酸长链折叠成特定的三维结构的,蛋白质在三维空间中的排列方式难以计数,有研究证明,即使蛋白质在纳秒尺度采样不同的构想,达到正确配置的时间仍然超过宇宙年龄的时间。但实际上通常在毫米级别的时间就能快速的完成折叠。

有些人可能不太理解,会提问道:“蛋白质折叠成特定形状为什么不是蛋白质记忆了这种形状,直接按照特定形状折叠呢?”

将蛋白质折叠联想到“记忆”是很自然的,因为它确实展现出一种非随机的、有方向性的特性,似乎“知道”如何快速找到正确的终点,但是蛋白质的折叠不用记忆来描述。先来解释记忆:记忆是需要储存的,然后在需要的时候检索,但是蛋白质的折叠,没有检索这一个过程。

实际上,蛋白质的折叠只是落入最稳定有力的配置,就好似,石头从山谷滚落落入谷底的状态。让我们引入能量的概念,对于蛋白质,我们将对蛋白质链中原子相互作用储存的势能做研究。蛋白质的每种形态都会有不同的势能水平,这由这些原子相互作用的总和决定。我们可以抽象的将其视为只有二维,那么能量函数可以视为一个平面,每一个点,都代表一个蛋白质。

image

点的海拔代表该配置的势能,这就是所谓的蛋白质能量景观

image

蛋白质分子,倾向于最小化他的势能。受热力学第二定律的指导,自然的寻求尽可能低的能量水平,因为这代表着其原子的最佳排列。在折叠的过程中,它本质上是在能量景观上面,沿最陡路径向山谷滑落。

image

这就是蛋白质为什么折叠这么快的原因,它不需遍历所有的情况,他们只需要沿着物理系统的自然趋势,将其势能最小化,就能得到目标结构。现在让我们将这种思想关联到记忆,来实现类似的效果。

假设我们有一个系统,它可以通过T[1]与其状态[2]之间的相互作用来编码信息,并且每种配置都具有特定的势能。首先我们需要创建能量景观,来将我们想要储存的记忆或者状态模式对应能量表面的中的最小值。其次我们需要有扮演热力学第二定律作用的东西,来驱动状态的变化,引导系统趋向局部最小值。即实现这两个目标,检索与模式最相近的记忆是通过,配置系统对最初输入模式编码,并让他由于运行到平衡状态,达到能量井[3],我们就可以从中读出原记忆。接下来让我们探索如何构建这种模式。

image

Hopfield 网络架构

构建能量景观

考虑一组神经元,他们可以为“+1”,“-1”两种状态,每一个神经元都和其他的神经元连接(全连接),他们中间都有相对应的权重,这里只考虑对称的权重(即神经元i到神经元j的权重和神经元j到神经元i的权重相等),之后在来解释原因。

image

我们先来解释一下权重(Wij)的概念。如果Wij>0 那么两个神经元具有兴奋性。例如Wij是一个正数,那么意味着神经元i和j都紧密耦合,一个神经元会激发另一个神经元。在这种情况下,一个神经元处于激活状态,那么另一个神经元也倾向于激活状态,同样,一个神经元处于抑制状态,那么另一个神经元也倾向于抑制状态,即促进它们状态上的一致性。相反,如果Wij<0,那么神经元 i 和神经元 j 之间的连接会促进它们状态上的不一致性

如果连接权重 Wij期望神经元 i 和 j 的状态相同(即 Wij>0),但实际上它们的当前状态是相反的(sisj=−1),那么这就产生了冲突。相反,如果 Wij 期望它们状态相反(即 Wij<0),但实际上它们的当前状态是相同的(sisj=+1),这也产生了冲突

将这个概念拓展到整个网络,网络中所有神经元对之间的冲突累加起来的总量定义为总冲突,这个总量越大,说明网络当前的状态离它“想要”的记忆模式越远,或者说,网络越不稳定。而冲突越少,连接权重和神经元的成对状态之间的整体一致性越高,即距离我们“想要”的记忆模式越近。

我们可以总结为:Wijxixj越大(越接近正无穷)表明当前神经元对的状态与网络所存储的记忆模式之间的契合度越高,从而使系统更接近其稳定态(目标结果)。我们同样将这个概念拓展到整个网络,我们要寻求的是最大的“$\sum_{i \neq j} W_{ij} s_i s_j$”。那么显而易见,我们可以转换成求最小的“-$\sum_{i \neq j} W_{ij} s_i s_j$”。我们将其($\sum_{i \neq j} W_{ij} s_i s_j$)定义为构建能量景观的能量,以此来构建能量景观。

image

构建热力学第二定律的模式

由上文可知,我们希望霍普菲尔德网络能逐渐向能量最小值演化,但是观察公式,我们可以观察出,能量的大小取决于神经元的状态和权重两个部分。

我们先来解释一下权重在霍普菲尔德网络中代表着什么:霍普菲尔德网络的工作原理是将要存储的记忆模式(或称为原型模式)编码到这些连接权重中。通常,这是通过赫布学习规则(Hebbian learning rule)的变体来实现的。在霍普菲尔德网络中的具体体现为。

  • 如果一对神经元 i 和 j 在许多被存储的模式中都处于相同的状态(比如在模式A中都是+1,在模式B中都是-1),那么它们之间的权重 Wij 就会被加强为正值
  • 如果它们在许多被存储的模式中都处于相反的状态(比如在模式A中是+1/-1,在模式B中是-1/+1),那么它们之间的权重 Wij 就会被加强为负值

整个权重矩阵 W 实际上就包含了网络所有学习到的、期望的神经元状态配置,这些配置对应着能量景观中的局部最小值,也就是网络的记忆。

假设我们已经通过学习确定了权重的大小,但是神经元的静默状态是完全未知的,那么问题变成了,如何找到可以最小化整个网络的能量的状态模式。我们当然不会遍历所有的情况,我们会从其中一个记忆的部分或者噪声版本开始,或者是完全随机的配置,随后我们有两种方式来找到最小能量,同步更新和异步更新。

异步更新:在异步更新模式下,在每个时间步,只选择一个神经元进行更新。我们可以随机的选择或者顺序的选择要更新的神经元,把更新的神经元设置为“i”,我们将会计算所有其他神经元对神经元 i 的加权影响之和$\sum_{i \neq j} W_{ij} s_j$,将这个结果表示为hi(净输入)。如果hi>0那么表示其他神经元赞成这个hi为“+1”,反之,如果hi<0,那么,其他神经元赞成这个hi为“-1“。如果hi=0,会保持当前状态或者随机选择状态。

同步更新:在同步更新模式下,在每个时间步,所有神经元都根据它们在上一时刻的状态同时计算净输入,并同时更新自己的状态。更新规则与异步更新一致。

最终我们会到达一种配置,反转任何神经元都会导致,整体能量的上升。这时候我们认为网络已经收敛到稳定状态,也就是整体能量的最小值。我们用这种更新模式,来代替热力学第二定律。

注释:尽管同步更新在并行计算中看似更高效,但在霍普菲尔德网络的理论分析和实际应用中,异步更新更为常见和推荐。这是因为它能够严格保证网络的收敛性,确保网络总会找到一个稳定的记忆模式,而不会陷入无限循环或不可预测的振荡。这种确定性的收敛行为对于联想记忆的任务至关重要。

公式解释

$E = -\frac{1}{2} \sum_{i \neq j} W_{ij} s_i s_j$ 这是霍普菲尔德网络的能量公式,让我们来解释以下几个问题。

$-\frac{1}{2}$的由来

  • 抵消重复计数

$- \sum_{i \neq j} W_{ij} s_i s_j$ 这个这部分的含义在上文中讲解过,不多做赘述,其中Wij代表权重,si和sj代表神经状态,$\sum_{i \neq j} $,表示我们对所有不同的神经元对 (i,j) 进行求和,且 i不等于j。例如:我们在求和的时候,分别加了$W_{ij} s_i s_j$ 和 $W_{ji} s_j s_i$,换句话说,求和 $\sum_{i \neq j} $实际上会把每对神经元之间的相互作用计算两次:一次是 (i,j),另一次是 (j,i)。这两项的结果是完全相同的,为了对每一个神经元都只作用一次,所以我们要乘1/2来抵消这个重复的计数。

  • 能量函数中 $-\frac{1}{2}$ 的存在使数学推导变得更加简洁和优雅

在异步更新中,我们随机选择一个神经元 k,并假设只有它的状态从 $s_k$变为 $s_k’$,而其他所有神经元 $j \neq k$ 的状态 $s_j$ 保持不变。

能量变化 $\Delta E$ 可以表示为新能量 $E’$ 减去旧能量 $E$:

$$\Delta E = E’ - E$$

我们来分析能量函数中哪些项会发生变化。只有包含 $s_k$ 的项会改变。

旧能量 E 为:

$$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’ 为:

$$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)$$

注意,最后一个求和项 $\sum_{i \neq k, j \neq k} W_{ij} s_i s_j$在 $E$ 和 $E’$ 中是相同的,因为它不包含 $s_k$。所以它在相减时会被抵消。

那么 $\Delta E$ 就简化为:

$$\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)$$

由于权重的对称性 $W_{ik} = W_{ki}$,我们可以将第二个求和项中的 $W_{ik} s_i s_k$ 改写为 $W_{ki} s_i 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)$$

合并相同的项:

$$\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)$$

这里的 $-\frac{1}{2}$ 和 2 相乘,就变成了 -1 和 +1。

$$\Delta E = - \sum_{j \neq k} W_{kj} s_k’ s_j + \sum_{j \neq k} W_{kj} 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$$
$$\Delta E = \left( \sum_{j \neq k} W_{kj} s_j \right) (s_k - s_k’)$$

我们定义神经元 k 的净输入为 $h_k = \sum_{j \neq k} W_{kj} s_j$。 所以,最终得到:

$$\Delta E = h_k (s_k - s_k’)$$

如果没有$-\frac{1}{2}$ 那么最终的结果是:

$\Delta E^* = 2 h_k (s_k - s_k’)$

有了$-\frac{1}{2}$,能量变化 $\Delta E$ 的表达式直接地简洁地呈现为 $h_k (s_k - s_k’)$。这使得能量变化与神经元 k 的净输入 $h_k$ 和其状态变化 $(s_k - s_k’)$ 之间存在一对一的、没有额外常数因子的关系。

为什么权重要对称

保持权重的对称性其实主要是为了避免霍普菲尔德网络陷入周期性振荡,而不是收敛到单一稳定模式。

这其中涉及到的是能量函数(或李亚普诺夫函数)的存在性。简单的理解能量函数,你可以认为能量的最低点就是稳定点(正定性),而整个系统,在演化过程中,总会保持能量的不变或减少(负半定性)完美符合蛋白质的折叠过程不是吗?,这两个性质在霍普菲尔德网络中其中决定性的作用。

异步更新权重对称的情况下,霍普菲尔德定义的能量函数 $E = -\frac{1}{2} \sum_{i \neq j} W_{ij} s_i s_j$ 就符合了李亚普诺夫函数的关键性质。

由于满足了这些条件,我们就可以确信,霍普菲尔德网络在回忆阶段总会收敛到一个能量的局部最小值,而不会陷入无限循环或混沌状态。这些局部最小值正是网络预先存储的记忆模式。

拓展探讨

1.为什么霍普菲尔德网络不能处理异或问题?

霍普菲尔德网络不能处理异或问题是因为异或是一个非线性可分问题,而霍普菲尔德网络本质上是一种线性模型,无法通过简单的线性组合来区分异或问题中不同的输入组合所对应的输出类别。

注释

  • 什么是异或

异或(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

image

绿线将两类点完全分开。

线性不可分:如果在一个特征空间中,无法找到一个超平面将不同类别的样本点完全地分开,那么这些样本就是线性不可分的。

例如:

异或(XOR)问题:

  • (0,0) -> 0
  • (0,1) -> 1
  • (1,0) -> 1
  • (1,1) -> 0

image

没有线可以将两类点分开。

  • 为什么无法处理异或问题

霍普菲尔德网络中单个神经元的状态更新依据其净输入:$h_i = \sum_{i \neq j} W_{ij} s_j$ 。神经元 i 的新状态将根据 hi 的正负号(例如,若 hi≥0 则为 +1,若 hi<0 则为 −1)来确定。但是当hi=0的时候,这个方程定义了一个线性决策边界(在多维输入空间中表现为一个超平面)。这意味着,无论网络如何通过学习调整权重 Wij,每个神经元在决定自身状态时,都只能通过一个线性的“切割”来区分其输入模式。然而,异或 (XOR) 问题本质上是线性不可分的。它的输入-输出模式无法通过单一的直线(或更高维度的超平面)在原始输入空间中被完全区分开来。

2.如何在霍普菲尔德网络中存储多个模式

  • 我们可以简单的将每个模式叠加在一起,根据以下等式计算权重。

image

这将把每一个模式都变成局部最小值,具体的内容可以阅读文献来获取答案。

Hopfield Networks: Neural Memory Machines | Towards Data Science

Hopfield Networks is All You Need | hopfield-layers

image

  • 为每个模式独立的挖掘能量井,然后将能量景观叠加在一起。

image

这进而引出了或普菲尔德网络的局限性

在网络中,我们能塑造的能量谷是有限的,如果我们存储过多的模式,那么我们将无法可靠的收敛到储存的模式,甚至出现奇怪的中间记忆,这个模式的数量取决于神经网络的数量,大约是神经原网络的0.14倍。

image

但是如果两个模式有很强的相似性或者相关性,那么他们会在达到最大模式存储数量之前,就会产生干扰。后面我会实际演示证明。

image

3.赫布学习法不分正反

赫布学习(Hebbian learning)的核心思想是“一同激发的神经元,会连接在一起”(“Neurons that fire together, wire together”)。它会无差别地强化那些同时活跃的神经元之间的连接,而不会“判断”这些连接是好是坏,或者学习到的模式本身是否“理想”

赫布学习不分正反意味着赫布学习规则在更新权重时,不会区分模式的方向。

这意味着在如果模式P被存储,那么模式-P也会被存储,而且,在赫布学习下,这两个模式在权重更新时是等价的。体现在霍普菲尔德网络中,实际的结果就会产生,生成与目标模式完全相反的情况。

下面通过代码来展现

1
2
import numpy as np
import 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] # 如果 net_input 为 0,保持状态不变

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: # 确保如果噪声水平 > 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:.2f} 个模式")
--- 霍普菲尔德网络可视化 (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
# --- 实验1: 赫布学习的正面情况 (成功召回) ---
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]

No description has been provided for this image

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
# --- 实验2: 赫布学习的反面情况 (存储容量限制) ---
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:.2f}):")

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):.2f}")


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):.2f}")
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 匹配。

No description has been provided for this image

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
# --- 实验3: 赫布学习的反面情况 (收敛到虚假态) ---
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):.2f}")
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 或其反向。

No description has been provided for this image


在实验二中,我们创建了20个模式,这个数量远超了霍普菲尔德网络的理论存储上限。因此,网络在学习过程中表现不佳,未能收敛到任何预期的模式。

而在实验三中,我们采用了完全随机生成的初始状态,而非从已存储的模式中选取并施加噪声。在这种情况下,尽管输入与任何已知模式都不相似,但代码依然收敛到一个稳定的状态。这个状态与我们预期的存储模式截然相反,这意味着网络进入了一个虚假态。也体现了赫布学习不分正反。

本文参照:A Brain-Inspired Algorithm For Memory


  1. 连接权重 ↩︎

  2. 指每个神经元是处于 +1 还是 -1 的“开”或“关”状态 ↩︎

  3. “能量井”是霍普菲尔德网络中存储记忆的稳定状态的视觉化比喻 ↩︎