Skip to content

实现并可视化这个算法.(goal) https://www.youtube.com/watch?v=l-OLgbdZ3kk

prediction

原理

  • 我们可以把一个多层神经网络当中,每一层的输出向量想象成多维空间的一个点.那么从某一层到下一层实际上就是一个点映射到另外一个多维空间当中的点. 或者说,可以写成这样:

  • PCN做到是:我们不再把x1映射到x2,而是映射到一个独立的变量,称之为v2.

x2 同样也是映射到新的变量v3 如何做prediction? 首先把\(x_1\)作为输入的值.也就是说 \(\(x_1 \leftarrow input\)\) 定义\(e_2\)为"能量",计算如下.也可以认为这是个"error": $$ e_2 = \frac{(v_2 - x_2)^2}{2} $$ 随后我们更新\(x_2\): $$ x_2 \leftarrow x_2 + \Delta{x_2} $$ 其中具体到我们这个图,其实就是 $$ \Delta{x_2} = lr * \frac{\partial{(e_2+e_3)}}{\partial{x_2}} $$ 如果我们后面加入了第四层的网络,由于我们这里只有\(e_2\)\(e_3\)是受到\(x_2\)影响的(e2好理解,而e3是因为e3受到v3影响,而v3是从x2更新出来的.从而上面的式子还是不变.更进一步的,其实上面完整的式子是这样的: $$ \Delta{x_2} = lr * \frac{\partial{(\sum_{i} x_i)}}{\partial{x_2}} $$ 但是由于后面的什么x4 之类的与x2没关系,因此就省略不写了. 上面的式子可以理解为:v2和x2之间有个初始长度为0弹簧,e2 e3其实就是弹性势能.每一次运行,都在试图减小这两个弹性势能的和. 上面这个是一个动态的过程.相当于是最终会慢慢x2靠近v2,x3靠近v2. 总的来说,还是很类似梯度下降,只不过我们尝试最小化的损失函数是"系统中弹性势能的总和".最终我们得到的是一个误差最小化之后的稳定状态.这个状态意味着,每个神经元已经处于对输入数据在不同抽象层次上的描述,这意味着这个网络可以用来进行提取特征,图像分类等任务了.

最初,只有第一层的v和x重合.慢慢的,后面的几层v和x都会越来越近,直到重合.是的,我确认是重合了.

更新时序

传统的神经网络,在计算时,想要得到x3,由于x3依赖于x2,那么就需要先计算x2;同理,x2依赖于x1,那么就需要计算x1. 而在pcn网络当中:\(\Delta{x_3} = f(v_3) = g(x_2)\) 这里关键的的一点是,在t+1时刻,实际上: \(\Delta{x_{3,t+1}} = f(v_3) = g(x_{2,t})\) 也就是说,t+1时刻\(\Delta{x_3}\)的计算只依赖于t时刻是x2的值.因此就可以并行的计算\(\Delta{x_3}\)\(\Delta{x_2}\). 注意,只有\(\Delta{}\)是可以并行的,而不是\(x_3\)可以并行计算.我们可以看到,实际上t时刻x1发生的改变,在t+1时刻才会传递到x2,t+2时刻才会传递到x3.

为什么是能量

实际上在生物当中,其实我们认为也确实是"降低分子动力学的势能",也是类似于此的. 树突注入的粒子就是我们说的v2,x2就是我们说的细胞体.由于正负电荷的吸引,也使得x2想要更接近v2. 更深一层,可能和free energy有关.

trainning/Inference

方便叙述,增加了x4,并且把x4固定到输出上.

推理 (Inference) 得到的是一个短暂的、针对当前输入的“状态稳定”。改变的是神经元的激活值 (State)。

训练 (Training) 得到的是一个长期的、适用于所有数据的“参数稳定”。改变的是神经元的连接权重 (Weight)。

在训练过程中,对于每一个训练样本,都必须先进行一次完整的“推理(Settling)”过程,让网络达到状态稳定,计算出最终的预测误差,然后利用这个误差去微调一下权重W。

这里我们的操作是,固定输入和输出: \(\(x_1 \leftarrow input,x_4 \leftarrow target\)\) 然后 loop 100 interations(有监督学习) 在每次loop里,我们还是更新x2 x3 然后结束之后,我们要update weights一次: $$ w_1 = w_1 + \Delta{w_1} , w_2 = w_2 + \Delta{w_2} $$ 其中: $$ \Delta w_1 = lr \times \frac{\delta E}{\delta w_1} $$ 注意这里的学习率和prediction的学习率是两个不同的超参数.

我们回到原本的图里,其实的意思就是更新w3,使得所有弹簧上能量能被最小化.我们从图上可以看到,修改w3只会更改v4 - x4这个弹簧上的能量.

我们更加优化上面的内容,实际上我们发现只是 $$ \Delta w_i = lr \times \frac{\delta e_{i+1}}{\delta w_i} = lr \times \frac{\delta(v_{i+1}-x_{i+1})^2}{\delta w_i} = lr \times \frac{\delta(w_i\cdot x_i-x_{i+1})^2}{\delta w_i} $$ 其实类似的,\(\Delta x_i\) 也可以像这样展开.

在训练前,应该在前面几层x和v都是重合的(可见下面的初始化部分),但是目标值的v和x有差距;在训练之后,原本最后的"差距"被分摊到各层之间,也就是各层的势能应该是相等的.随后,我们才会用这个误差来更新权重

效率问题

首先原本的训练,不论是反向传播还是PCN,都是这样的:(无监督学习)

# 伪代码:训练外循环
for epoch in range(num_epochs):  # 迭代很多轮
    for batch in dataset:        # 遍历所有数据
        # 核心计算步骤 (这里是BP和PCN产生区别的地方)
        gradients = calculate_gradients_for_batch(batch)
        update_weights(gradients) # 进行一小步梯度下降
对于当中的核心计算步骤,传统的神经网络只需要一次前向传播-反向传播(反向传播是吧所有权重的梯度计算出来,也就是\(\Delta w\)计算出来,方便后面梯度下降的时候使用.) 但是对于PCN:
# PCN的 calculate_gradients_for_batch() 内部伪代码
# ---- PCN的内循环开始 ----
for t in range(T): # T通常是几十上百
    # 步骤A: 自上而下预测
    generate_predictions()#也就是计算所有的能量的变化
    # 步骤B: 计算误差,更新神经元状态 (注意:这里更新的不是权重W,而是神经元激活值x)
    update_neuron_states()#根据能量的变化,计算x应该如何变化.
# ---- PCN的内循环结束 ----

# 在内循环T次,神经元状态稳定后,才得到了最终的误差
final_error = calculate_final_error()
# 用这个最终误差去计算权重的梯度
gradients = compute_gradients_from_error(final_error)
return gradients
课上的图:(这里则是有监督的) 这里的推断循环不是为了自由地预测输出,因为输出 x4 已经被强制设为了 target。相反,这个循环的目的是:在输入 x1 和目标 x4 都被固定的“约束”下,找到一组最协调、最一致的中间层状态 (x2, x3)。

你可以把这想象成网络在“反思”或“推理”:“如果我的输入是 x1,并且我必须得到 x4 这个结果,那么中间过程 x2 和 x3 应该是什么样子的?”

在这个循环中,x2 和 x3 会不断更新,以同时最小化来自底层的预测误差(x1 对 x2 的影响)和来自顶层的预测误差(x4 对 x3 的影响)。

学习阶段 (update weights): 当中间状态 x2 和 x3 在这种“两端夹击”下稳定后,网络才开始更新权重。权重更新的目的是减少在刚才那个被约束的推断过程中产生的预测误差。这样一来,下次网络只看到输入 x1 时,它自然而然产生的顶层预测就会更接近 target。

x的初始化问题

\(x_1\) \(x_2\) \(x_3\) 这些参数的shape一般就是(batch_size,InputDim/HiddenDim).这也就是说,在矩阵里,一条数据就是一行. prediction: xi可以都从0开始初始化,或者从正态分布噪声初始化都可以.经验上说这个是不重要的. train: 通常是prediction的输出是多少,我们就使用什么作为初始化. 在每一轮当中(每一轮是指经过一次prediction再train)都会进行一次初始化而且必须初始化.这是因为,上一轮的train时我们会把输出神经元固定在target上,如果回头来不进行初始化,那么prediction的loss会很不正常地小.

这也是我们为什么通常习惯于先prediction再train,因为我们往往喜欢使用prediction的结果作为train的初始化值.

特殊的损失函数

我们在输出层也可以使用任何我们认为可用的损失函数,比如softmax 交叉熵等等.具体来说是这样: 注:这里写的是二元交叉损失熵.毕竟总得来说,v4(我们的预测值)和x4的差距都可以用来表示损失函数. 那么这里我们可以说原本的可以改写成: $$ \Delta x_3 = \frac{\delta(e_3 + e_4)}{\delta x_3} = \frac{\delta e_3}{\delta x_3} +\frac{\delta e_4}{\delta x_3} $$ 然后我们再处理即可.但是问题是两者的量级可能不一样,比如 \(\frac{\delta e_3}{\delta x_3}\) 是零点几而后者是大几千,这样就会有问题.

因此在实践当中,这个损失函数还是没有验证过的.

开发与debug流程

我们同样需要观察loss曲线.Loss损失定义为:在prediction模式下给出的输出和目标的差距. - loss只受权重的影响,不受其余的影响. - 注意上面这个定义.这个定义的loss,是指:在prediction结束之后,我们计算最后一层的x,和我们目标的target之间的差距,而不是最后一层v和x之间的差距(因为这个差距必然是0) 我们做的结果应该是这样的:

和反向传播的区别

在传统的反向传播的过程当中,神经元的激活值是前向传播过程当中固定下来的. 但是在PCN当中,由于在train时是有一个inference,在更新w之前,每个神经元实际上是会与prediction过程结束的位置是不一样的: 神经元实际上会往target的方向稍微偏离一点.然后w的方向相当于是也试图偏离了一点(如图.) 我们会发现这和人脑的机制有点类似:看到一个target,我们先尝试把神经元改一下,试图更加符合target;然后才会改一下突触. 这个叫做pespective configuration --超前配置 另外,这里的反向传播:\(\Delta{w_3} = x_3 \times \epsilon_4\) 而PCN是:\(\Delta{w_3} = x_3 \times u_4\) 其中 \(u_4 = \frac{1}{2} \times \epsilon_4 ^2\)

基于能量的神经网络

上面这就属于一种.都会或多或少的有某种能量 开放问题:这里的能量应该如何设计才比较合理

变体 - zero divergence PCN(Z-PCN)

这个变种是完全等效于反向传播 prediction是不变的. 区别就在train的inference阶段 第一步的inference:只进行一步,也就是只会更新最后一个x,只有最后一个有error 然后就会立刻更新权重. 然后再进行一步inference.这样也就是说,没有等到inference收敛就更新全重. 这样如果inference的lr为1,那么最后会完全等于反向传播

变体 - PPCN

上面其实是走了两个极端--一个是完全收敛了.一个是完全不等收敛.其实也可以一边收敛一边进行训练,也即PPCN

实际上这是为了修正PCN的一个问题:PCN必须有一个外部的监督者来监督"什么时候应该收敛",这其实是不太符合神经元的现实的.

特性

完全可以更改input和target的位置.因此就是可以相互互换的. 也可以从"标签"得到图像.这也是最早使用的方式. 实际上上面这种叫做"判别式的使用方式"下面是"生成式的使用方式"

也就是说,固定一部分(固定1),就可以恢复-1.这实际上可以做到一种"关联记忆",可以类比到语言模型(可探索的方向)

当然,这样生成的东西是固定的.我们也可以在每个神经元上加噪声,增加随机性(MC-PCN)

另外,由于方向可换的特性,我们或许也可以在内部做一个信息的循环:(Arbitrary Graph)

生成模型

目前比较热门的方向就是生成模型,因为可以大规模利用没有标注的数据 目前在寻找新的生成范式

LLM的随机性是在model之外随机采样的,而我们MC-PCN的设计是在神经元层级的随机性.这种范式是比较有吸引力,(不知道应用的水平如何)

Target Propagation(TP)

prediction和BP是一样的. train时,根据target的值,反过来推导每个神经元的激活值应该是多少. 也就是算\(w_i ^ {-1}\)(当然,线性情况下就是逆,非线性的情况下就很难求逆了) 然后把反推出来的激活值与现有的激活值的差作为\(\epsilon\) 更新还是使用之前公式:

由于求逆很难,因此效果不是很好

联系

我们可以看到,如果我们把PCN的target固定而不固定输出的 实际上就是退化成了TP.

实际上可以看到,TP是把perspective 传播到了极致:直接到了最target的状态

Eq - Prop

稳定态传播

也就是把x3前置到clamp target 而不是target. 实际上还有个现象,当\(\alpha\) 逼近1的时候,w会逼近反向传播的w.

等效反向传播的情况

  • Z-PCN:first step method:当误差第一次到达的时候就更新权重
  • Eq Prop: weak supervision
  • train过程当中,给能量的变化加一个权重分配: 如果\(\beta\) 趋近于1,那么上面图里x2有更多的力去向上拉,因此可以有更多的数据. 这种情况叫做week feedback(feedback是前面e3传递回来的) 有论文就专门讲这个的,这也是research的一部分/一种形式

神经实现

三种层级

  • 电生物学: 研究跨膜蛋白的电路等等
  • 尖峰神经网络:建模出来一个:调节神经发射的信号\脉冲强度,从而影响发出的神经递质的含量,忽略生物的细节,而是通过数学来建模
  • rate-based model:基于连续的值.比如常见的ANN,\(x_1 \cdot w\)这样形式的.我们研究的都是这个层面上面了. 当然上面这种是不太符合现实.因为在反向传播当中,这里这里有个复制w2的过程,而这个在现实的神经元当中是没有的.

对神经实现的研究

不太可信的地方: - \(\epsilon\) 和本层的x是一对一链接,而对上一层则是一对多 - 上面\(\epsilon\)和x的定义不一样(上面的形式实际上还是\(\Delta x\)而不是x) - 我们会发现,从\(x_{l-1}\)\(\epsilon _l\)的链接是w,但是反过来还是w,这不符合现实相当于两个神经元之间必须有相互的链接,而且链接还是一致的.

bi -PCN

核心就是把PCN的两个方向融合到一起。

从一个方向就是分类,从另一个方向就是生成

这里注意,相当于两条弹簧拉它. 上面这个图上,由于有一个来回,由于本身就是双重的w,因此就是4条冗余

非线性

可以在两个地方加非线性,一般加一个就够了

ps:这里有张图,记得对齐一下符号的定义