news 2026/10/6 9:17:29

受限玻尔兹曼机原理与PyTorch实现:能量模型与对比散度详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
受限玻尔兹曼机原理与PyTorch实现:能量模型与对比散度详解

1. 为什么还要学Boltzmann机:从能量模型看另一个世界

1.1 深度学习之外的另一种思路

这几年不管是做CV还是NLP,主流方案基本绕不开卷积神经网络、循环神经网络、Transformer这些判别模型。大家习惯的套路是:拿数据喂进去,让网络学会从输入到输出的映射,最后用交叉熵或者MSE当损失函数一把梭。这种范式太深入人心了,以至于很多人学了几年神经网络,都不知道在深度学习火起来之前,还有一整套基于能量的生成模型体系。Boltzmann机就是这套体系里最有代表性的模型之一。

我第一次接触Boltzmann机是在读Hinton那篇用对比散度训练受限玻尔兹曼机的经典论文时,当时第一反应是:这玩意儿的思路和反向传播完全不一样,它不直接学输入到输出的映射,而是尝试拟合数据本身的分布。你可以这样理解:卷积神经网络像是在学“怎么把一张图正确分类”,而Boltzmann机是在学“这张图本身长什么样,像素和像素之间有什么规律”。这是一种生成式思维,和判别式思维有本质的区别。

写这篇笔记的动机很简单——市面上的深度学习教程对Boltzmann机的讲解往往一笔带过,或者一上来就甩公式,缺乏从零到一、从原理到代码的完整链路。我想用我这段时间踩坑总结的经验,把Boltzmann机的原理、受限玻尔兹曼机(RBM)的实现细节、训练过程中的常见问题好好梳理一遍。无论你是在校学生、算法工程师,还是对生成模型感兴趣的爱好者,这篇笔记应该都能帮你少走一些弯路。

1.2 Boltzmann机到底解决什么问题

先别急着看公式。我们要搞清楚一件事情的动机,才能有动力学下去。Boltzmann机本质上是想解决一个问题:怎么从一堆没有标签的数据中,自动学到数据内部隐藏的结构。

举个例子,假设你有一堆手写数字图片,每张图都是一个784维的向量(28x28像素)。在没有标签的情况下,普通神经网络不知道该干什么——没有监督信号,反向传播就没法用。但Boltzmann机可以。它做的事情是:把数据当成一个概率分布,然后让模型去拟合这个分布。一旦拟合成功,模型就有了“生成能力”,可以从分布中采样出新的手写数字图片,也可以把高维数据压缩成低维特征,供下游任务使用。

所以Boltzmann机的价值主要体现在三个方向:一是无监督特征学习,二是生成新样本,三是解决组合优化问题(比如旅行商问题这类需要寻找能量最低状态的场景)。在实际业务中,RBM被用在过协同过滤推荐、电影评分预测、图像特征提取等任务上,后来还被Hinton用来预训练深度置信网络(DBN),算是深度学习的祖师爷之一。

这里不得不提一下它与主流模型的关系。卷积神经网络擅长捕捉空间局部特征,循环神经网络擅长处理时序依赖,而Boltzmann机擅长的是无监督地刻画数据分布。GNN图神经网络虽然在图上做消息传递,但如果你想把图结构数据也纳入生成式建模,玻尔兹曼机那一套能量视角依然很有启发。理解了能量模型,再去看现在大火的扩散模型、基于能量的生成模型,你会发现它们在思想上是一脉相承的。

2. 波尔兹曼机原理拆解:能量、概率与状态

2.1 能量函数从哪来

Boltzmann机这个名字听着高大上,其实它借用的核心思想来自统计物理——能量。学过物理的朋友都知道,一个系统的状态分布与能量密切相关。在物理里,分子在高温下运动剧烈,低温下趋于有序;在Boltzmann机里,我们把“神经元的各种激活状态”看成系统的状态,给每种状态定义一个能量值,然后让模型学会往低能量的状态走。

具体怎么定义能量?先来个简单的版本。假设网络里有若干个神经元,每个神经元的状态可以是0或1(这也是Boltzmann机最初的形式——一切都是二值的,不要觉得简单,后面你会发现二值反而让推导清爽很多)。再假设每两个神经元之间有一条连接,连接有对应的权重。那么整个网络的能量可以写成:

E = -Σ_i b_i * v_i - Σ_{i<j} w_ij * v_i * v_j

这里的v_i表示第i个神经元的状态(0或1),b_i是偏置(可以理解成神经元自身的倾向),w_ij是第i个和第j个神经元之间的权重。这个公式看着简单,意思却很直观:如果两个神经元之间的权重是正的,那它们同时激活(都为1)的时候,第二项贡献为负,能量会降低。系统总是倾向于低能量的状态,所以正权重意味着“这两个神经元倾向于同时亮”。负权重则相反。偏置同理,正的偏置让这个神经元更容易激活。

就这一句话,Boltzmann机把模型的行为和物理直觉打通了:学习的过程,本质上是找到一组权重和偏置,让真实数据对应的状态能量尽可能低,而随机状态的能量尽可能高。这样一来,数据分布就像是能量地形图上的“低谷”,模型采样的时候自然就会往低谷跑。

在完整的Boltzmann机里,神经元还会分成可见单元(visible units)和隐藏单元(hidden units)。可见单元用于接收外部数据,比如图像的像素值;隐藏单元则是模型的内部变量,用于捕捉数据背后的隐藏因素。如果你把可见单元看成“表象”,那隐藏单元可以理解为“本质规律”。比如一张人脸图片,可见单元是像素,隐藏单元可能就是“是否微笑”“是否戴眼镜”这类抽象属性。把隐藏单元加上之后,能量函数变成:

E(v, h) = -Σ_i a_i * v_i - Σ_j b_j * h_j - Σ_{i, j} w_ij * v_i * h_j

这里的v代表可见单元状态,h代表隐藏单元状态,a和b分别是两边的偏置,w_ij是可见单元i和隐藏单元j之间的权重。这个公式在标准RBM里会一次次出现,建议先把它盯住,后面所有推导都围绕它展开。

2.2 从能量到概率:Boltzmann分布

有了能量之后,怎么变成概率?答案是玻尔兹曼分布。这个分布原本是统计物理用来描述系统在不同能量状态上的概率分布的,长这样:

P(x) = (1/Z) * e^(-E(x))

其中Z叫做配分函数,是所有可能状态的能量之和:Z = Σ_x e^(-E(x)),用来保证所有概率加起来等于1。而e^(-E)这个指数形式意味着:能量越低的状态,概率越高;能量越高的状态,概率越低。这就把“低能量优先”的物理直觉转化成了数学上可操作的采样规则。

在Boltzmann机里,我们关心的分布是P(v, h),也就是可见单元和隐藏单元的联合分布。但由于Z涉及到对所有可能状态求和,当神经元数量上百的时候,Z根本算不出来——2的n次方种状态,即使n=100也已经是个天文数字。这也是Boltzmann机早期训练困难的核心原因,后面说的各种改进算法,绝大多数都是为了绕开对Z的直接计算。

还有一个值得一提的概念是温度T。如果把分布写成P(x) = (1/Z) * e^(-E(x)/T),T就是温度。T越大,分布越平緩,系统越随机;T越小,分布越尖锐,系统越倾向于落在能量最低点。实际训练中一般固定T=1,但如果你对模拟退火有了解,会发现调温度在采样中是个有用的技巧。我们通常不会在基础的RBM上折腾温度,不过理解它的含义有助于后续阅读更复杂的变体。

3. 从全连接到受限:RBM的出现

3.1 标准Boltzmann机为什么难搞

原始的Boltzmann机里,所有神经元之间都有连接,不管它是可见单元还是隐藏单元。这种全连接结构理论上是美的——它足够通用,理论上可以逼近任意分布。但在工程上这是噩梦,问题出在训练算法上。

回忆一下,训练任何概率模型,核心就是求极大似然,也就是让模型在训练数据上的概率最大化。对Boltzmann机做梯度推导,会得到一个非常优雅但极具讽刺意味的结果:参数更新量等于“数据分布下的期望”减去“模型分布下的期望”。第一项好算,数据就在手边;第二项要求我们从模型分布中采样——可模型分布恰恰就是那个Z算不出来的分布。你没法从一个未知分布中采样,所以梯度没法算。

为了解决这个问题,早期研究者用马尔可夫链蒙特卡洛(MCMC)方法,让网络在数据中初始化,然后反复迭代直到达到平衡态,再采样获取近似梯度。但问题是:MCMC需要非常多的迭代步才能收敛,每一轮参数更新都要跑一遍MCMC,整个训练过程慢到让人绝望。在算力有限的年代,标准Boltzmann机基本是个理论玩具。

3.2 受限Boltzmann机(RBM)的约束

Hinton在2002年左右明确提出用对比散度(Contrastive Divergence,CD)算法来训练RBM,后面又和合作者一起完善了这套方案。所谓“受限”(Restricted),就是在结构上加了一条硬性约束:可见单元内部不允许有连接,隐藏单元内部也不允许有连接,所有的边只能存在于可见单元和隐藏单元之间。

这个约束带来的好处是巨大的。因为层内没有连接,给定可见单元状态v时,每个隐藏单元的条件概率是独立的,可以并行计算:

P(h_j=1|v) = sigmoid(b_j + Σ_i w_ij * v_i)

反过来,给定隐藏单元状态h时,每个可见单元的条件概率也是独立的:

P(v_i=1|h) = sigmoid(a_i + Σ_j w_ij * h_j)

这两个条件概率公式是RBM所有算法的基石。它们让吉布斯采样变得极其简单——你可以一次性更新所有隐藏单元,再一次性更新所有可见单元,完全不需要逐神经元迭代。打个比方,全连接的Boltzmann机像是在一群人里两两握过手,关系错综复杂;RBM则把人们分成了两组,同组的人互不相识,只有跨组才有联系,整个社交结构清爽了许多。

从另一个角度说,RBM把原先无法处理的联合分布P(v, h),拆解成了两个可以高效计算的条件分布的交替。这正是它能在实际任务中跑起来的关键。不要小看这个结构改动,它把“理论上优美但实践上跑不动”变成了“理论依然优美且实践可行”,这本身就是工程思维的胜利。

4. 实战:用Python从零实现一个RBM

4.1 数据准备与任务定义

说了半天理论,终于到了动手环节。我建议你不要急着往MNIST上冲,先用一个小到能在笔记本上跑动的数据集来验证理解。我这次用的是经典的MNIST,但只取了一小部分(1000张),二值化处理,方便和RBM的0/1状态匹配。

二值化这一步很关键。原始MNIST是0到255的灰度值,虽然也有用实数值RBM处理的方案,但对初学者来说,最稳妥的做法是设定阈值(比如128),大于阈值的像素置1,小于等于的置0。这样一来,每张图就是一个0/1向量,正好对应RBM可见单元的状态。用这种方法训练出来的RBM,学到的特征非常直观,方便调试。

代码整体的结构分成三块:RBM类定义、训练循环、可视化验证。我下面把每一块的思路和关键代码都贴出来解释一遍。

4.2 核心代码拆解:从能量到采样

先看RBM类的初始化部分:

import numpy as np class RBM: def __init__(self, n_visible, n_hidden, lr=0.01, momentum=0.5): self.n_visible = n_visible self.n_hidden = n_hidden # 权重和偏置初始化:小随机值有利于打破对称 self.W = np.random.normal(0, 0.01, (n_visible, n_hidden)) self.bv = np.zeros(n_visible) # 可见单元偏置 self.bh = np.zeros(n_hidden) # 隐藏单元偏置 # 动量缓存:记录上一次梯度更新量 self.vW = np.zeros_like(self.W) self.vbv = np.zeros_like(self.bv) self.vbh = np.zeros_like(self.bh) self.lr = lr self.momentum = momentum

权重的初始化我用了均值为0、标准差0.01的正态分布。不要用全零初始化,否则所有隐藏单元学到的东西都会一样,根本没有对称性破缺。偏置初始化为0没问题,但后面你会在实验中发现,偏置的更新其实也很有讲究。

接下来是采样函数。RBM最核心的操作有两个:给定隐藏单元概率计算可见单元,以及反过来。在实现上,它们是一对镜像函数:

def sample_h(self, v): # 计算隐藏单元激活概率,然后按概率采样二值状态 h_prob = 1.0 / (1.0 + np.exp(-(v @ self.W + self.bh))) h_sample = (h_prob > np.random.rand(h_prob.shape[0], h_prob.shape[1])).astype(np.float32) return h_prob, h_sample def sample_v(self, h): # 计算可见单元重构概率,同样按概率采样 v_prob = 1.0 / (1.0 + np.exp(-(h @ self.W.T + self.bv))) v_sample = (v_prob > np.random.rand(v_prob.shape[0], v_prob.shape[1])).astype(np.float32) return v_prob, v_sample

这里有个小细节值得注意:我们同时返回了概率和采样结果。对比散度算法里,训练时有时候用概率,有时候用采样值,具体怎么选要看是哪个阶段。如果你刚开始写RBM,最容易犯的错误就是全用概率或者全用采样值,这会导致训练行为和你预想的不一致。下面训练部分会讲清楚每个阶段该用哪个。

4.3 训练流程:对比散度CD-k算法

对比散度是Hinton提出的聪明办法。标准的极大似然需要从模型分布中采样来计算梯度,这很慢;CD算法给出的近似方案简单粗暴:用数据作为起点,只跑k步吉布斯采样(通常k=1就够用),然后用采样结果近似模型分布的期望。

具体流程是这样的:

def cd_k(self, v_input, k=1): # v_input 是原始输入数据,形状为 (batch_size, n_visible) v_positive = v_input # 正向阶段:从输入数据出发,采样隐藏状态 h_prob, h_sample = self.sample_h(v_positive) # 反向-重构-正向的交替采样 v_negative = v_input.copy() for step in range(k): h_prob_neg, h_sample_neg = self.sample_h(v_negative) v_prob_neg, v_negative = self.sample_v(h_sample_neg) # 最后再采样一次隐藏状态,用于计算负向统计量 h_prob_neg, h_sample_neg = self.sample_h(v_negative) # 梯度计算:正向期望 - 负向期望 positive_grad = v_positive.T @ h_prob # 注意这里用的是概率 negative_grad = v_negative.T @ h_prob_neg # 更新权重和偏置 grad_w = (positive_grad - negative_grad) / v_input.shape[0] grad_bv = np.mean(v_positive - v_negative, axis=0) grad_bh = np.mean(h_prob - h_prob_neg, axis=0) # 加动量并更新 self.vW = self.momentum * self.vW + self.lr * grad_w self.vbv = self.momentum * self.vbv + self.lr * grad_bv self.vbh = self.momentum * self.vbh + self.lr * grad_bh self.W += self.vW self.bv += self.vbv self.bh += self.vbh # 返回重构误差,用于监控训练过程 err = np.mean((v_positive - v_negative) ** 2) return err

这段代码是RBM训练的核心,我解释几个关键点。

第一,正向阶段为什么用h_prob而不是h_sample?在计算正梯度时,我们用数据的期望来近似。理论上应该用采样得到的二值状态,但Hinton在实践中发现,直接用概率期望效果更好,方差更小。这也是RBM实现中最常见的“软性”细节之一。

第二,负向阶段我们跑了k步吉布斯采样,起始点是原始数据。标准的CD-k算法里,k=1已经能取得不错的效果,k越大越接近真实的模型分布,但计算量也越大。实际训练中k=1是个不错的选择,我后来试过k=2、k=3,效果提升很有限,但训练时间成倍增加。

第三,权重更新公式用的是正负梯度的差值。这个差值的含义很直观:我们希望增加数据对应的那部分概率(正梯度方向),减少模型自发生成的那部分概率(负梯度方向)。正负之间互相拉扯,最终达到平衡。

4.4 训练循环与全程监控

主体循环写起来比较直接。我每次迭代随机取一小批数据,然后用CD-k更新参数。这里有个非常关键的工程点:训练过程中一定要监控重构误差,也就是输入数据和经过一轮正负采样后重构出的数据之间的差异。重构误差下降,说明模型在逐渐记住数据的规律;如果重构误差震荡不降,或者前期先降后升,说明学习率或者动量设置有问题。

def train_rbm(rbm, data, epochs=20, batch_size=64, k=1): n_samples = data.shape[0] history = [] for epoch in range(epochs): # 每个epoch打乱数据顺序 indices = np.random.permutation(n_samples) data_shuffled = data[indices] epoch_err = 0.0 n_batch = 0 for i in range(0, n_samples, batch_size): batch = data_shuffled[i:i+batch_size] err = rbm.cd_k(batch, k) epoch_err += err n_batch += 1 avg_err = epoch_err / n_batch history.append(avg_err) print(f"Epoch {epoch+1}/{epochs}, avg reconstruction error: {avg_err:.4f}") return history

训练过程的监控信息除了重构误差,我强烈建议把权重可视化出来看。因为RBM学到的权重矩阵每一列(对应一个隐藏单元)可以reshape成图像,直接展示这个隐藏单元在“看”什么样的模式。这一步比任何指标都直观,效果非常震撼。

import matplotlib.pyplot as plt def visualize_weights(rbm, grid_shape=(10, 10), image_shape=(28, 28)): fig, axes = plt.subplots(grid_shape[0], grid_shape[1], figsize=(10, 10)) n_hidden = rbm.W.shape[1] for i in range(n_hidden): ax = axes[i // grid_shape[1], i % grid_shape[1]] w = rbm.W[:, i].reshape(image_shape) ax.imshow(w, cmap='gray') ax.axis('off') plt.show()

我第一次跑通可视化的时候,看到的是一排排类似笔画片段、边缘方向的特征图,那种感觉很难用语言形容——原来没人告诉模型“数字长什么样”,它就是自己从1000张二值图里提炼出了这些模式。这就是无监督特征学习的魅力,也是Boltzmann机最让我着迷的地方。

5. 训练过程踩过的坑:从发散到收敛的排查实录

5.1 学习率的选择:不是越大越快

学习率这个超参数,在RBM里比在普通前馈神经网络里更敏感。我刚开始训练的时候图省事,直接设了0.1,结果重构误差不仅不降,反而一路狂飙,从0.2涨到0.7。后来才明白原因:RBM的梯度本身就带噪声,学习率太大的话,参数会在最优解附近剧烈震荡,甚至越震越远。

通过反复实验,我发现对于二值RBM和MNIST数据,学习率在0.01到0.05之间比较稳。如果你用的是实值可见单元(即输入是连续值),学习率还要调得更小一些,比如0.005。一个实用的技巧是:如果你发现重构误差在早期不降反升,先把学习率除以10再试;如果误差降得很慢但很稳,可以尝试乘以2,但每次调整后都要观察几个epoch再下结论。

另一个相关问题是动量(momentum)的设置。动量可以让梯度更新更平滑,抑制震荡。我的经验是前5个epoch用0.5的动量,后面提升到0.9。这样的“动量预热”策略在很多深度学习任务里都有效,但在RBM里尤其重要——因为RBM的梯度噪声大,高动量前期容易让参数跑偏。

5.2 权重初始化与偏置的细节

初始化这件事我前面提过一句,但值得展开说。权重的标准差如果太大,比如0.1,那么隐藏单元的输入加权和会分布得很散,sigmoid函数非常容易饱和。饱和意味着梯度几乎为零,模型学不动。反之,标准差太小(如0.001)会让所有单元一开始都近似对半分,学习速度偏慢但比较稳。0.01是一个折中的选择,实测效果不错。

偏置初始化有一个经验法则:可见单元偏置可以设置为训练数据中对应特征的边缘概率(也就是每个像素为1的频率)的对数几率,即log(p/(1-p))。这样模型一开始就能重建出数据的平均状态,而不是从全零状态开始挣扎。隐藏单元偏置一般初始化为0就行。我踩过这个坑——初始化全零偏置的时候,前几个epoch重构误差虽然也降,但速度明显比用边缘概率初始化慢很多。

5.3 批量大小的取舍

批量大小对RBM训练的影响也很微妙。批量太小(比如1),梯度噪声极大,训练不稳定;批量太大(比如512),每个epoch更新次数太少,收敛偏慢,而且大batch会让CD-k算法中的负采样偏差更大。我在这份笔记里用的64是一个比较平衡的选择。

如果数据量很大,还有一个实用建议:宁可多跑几个epoch,也别把batch开太大。RBM的CD算法对batch大小没有普通神经网络那么敏感,但实验中我发现64到128区间表现最稳。超过256之后,重构误差下降速度明显变慢,原因可能在于大batch减少了参数更新的频次,随机性降低,模型更容易陷入局部最优。

5.4 过拟合怎么判断

RBM的参数数量和隐藏单元数量直接相关,隐藏单元太多,模型容易把训练数据的细节死记硬背下来,表现出来就是在训练集上重构误差很低,但拿到测试集上生成的样本质量很差。判断过拟合的一个方法:训练到一半时,把模型生成的样本画出来看看。如果你发现生成样本几乎就是训练样本的简单复制,说明模型容量已经超出需求,应该减少隐藏单元数量,或者加入权重衰减项。

权重衰减(L2正则)在RBM里的实现很简单,就是在权重更新时减去一个小比例(比如0.0001)的权重值。但要注意,RBM的权重衰减系数一般比普通神经网络小很多,太大了会压制模型的学习能力。我目前用的是1e-4,效果还不错。

6. 常见错误与调试速查表

训练RBM的过程中,很多问题看起来五花八门,实际上归结起来就那么几类。我把这段时间遇到过的典型问题整理成一个速查表,方便你做实验时快速定位。

症状可能原因排查与解决
重构误差一开始就很高(0.5以上),而且不下降学习率过大,参数震荡发散把学习率调小10倍再试,确认权重初始化标准差在0.01左右
重构误差缓慢下降,但生成的样本模糊成一团隐藏单元数量太少,模型容量不足增加隐藏单元数量,或者减少批量大小以增强随机性
训练集重构误差很好,但采样出的新样本完全不像原始数据过拟合,模型记住训练样本的分部模式增加权重衰减,减少训练epoch,或者增大测试集和训练集的差异
训练过程正常,但不同隐藏单元学到的权重完全相同权重初始化对称,没有打破对称性使用随机初始化,确保每个隐藏单元的初始权重不一致
生成样本噪声很大,像是黑白电视雪花采样步数k不够,或者数据预处理没做二值化尝试CD-2或CD-3,检查输入数据是否已经转换为正确的0/1格式
训练时间极长,每轮epoch要跑几分钟隐层单元数量过大,或数据没有做批量处理减少隐藏单元数,确保用了批量采样而非逐样本循环

这张表只覆盖了最常见的几种。实际操作中你可能会碰到更诡异的问题,我的建议是:先从最基础的组件开始检查——输入数据的形状和值域、sigmoid计算有没有溢出、采样逻辑是否正确。很多所谓的问题,最后查出来只是代码里一个形状不匹配或者类型错误。

另外一个独家的调试技巧:把训练过程可视化。我在代码里加了实时画重构误差曲线和权重图像的功能,每隔几个epoch就输出一次。这样做的好处是,任何异常都能很早发现,而不是等训练结束才在一堆数字里找线索。机器学习这个领域,可视化永远是排在第一位的调试工具。

7. 下一步往哪儿走:从RBM到更广阔的能量模型世界

7.1 深度置信网络与深度玻尔兹曼机

RBM单层能学的特征有限,但它有一个非常优雅的扩展方式——堆叠。把第一个RBM的隐藏层输出当成第二个RBM的输入,再叠加一层隐藏单元,就构成了深度置信网络(DBN)。Hinton最早提出DBN的时候,就是用这种逐层预训练的方式初始化一个深层神经网络,然后再用反向传播微调。在当时,这是解决深度网络训练困难的主要方案,后来才被ReLU、BatchNorm等技巧部分替代,但它依然是一个重要的历史坐标。

深度玻尔兹曼机(DBM)则更进一步,它允许多个隐藏层之间也存在连接(不只是相邻层的单向连接),训练方式也比DBN复杂。如果你已经理解了RBM的CD算法,再去看DBM会容易很多——它就是一个多层的能量模型,采样过程从单层交替变成了多层交替,梯度推导的核心思想完全一致。我个人觉得,DBM是理解深度生成模型的最佳跳板,因为它把“层间交互”和“联合学习”的概念融合在一起,和现在扩散模型中的去噪过程有异曲同工之妙。

7.2 能量视角在今天的技术里还重要吗

你可能会有疑问:都2025年了,RBM这些东西还有实际价值吗?我的看法是:直接在生产环境里用RBM的场景确实少了,但它的思想遗产无处不在。现在大火的扩散模型,本质上就是在学习一个能量景观的梯度;基于能量的模型(EBM)也仍然是生成建模的重要流派。

更实际的一点是,理解RBM能帮你理解所有生成模型的底层困难——近似推断、采样、对配分函数的逃避。这些问题在RBM里以最清晰、最无修饰的形式呈现出来,一旦你搞懂了RBM里的CD、吉布斯采样是怎么运作的,再去看变分自编码器、生成对抗网络甚至扩散模型,都会有一种“原来都是一家人”的通透感。

从技能树的角度看,RBM涉及的数学(概率图模型、能量函数、采样方法)和工程(二值数据处理、动量优化、正则化)都是通用的。即使你的研究方向是图神经网络或者卷积神经网络,这些基础能力也不会浪费。

最后分享一个小经验:如果你打算深入理解RBM,请一定不要只看公式或者只调别人的库,亲手从零把这个模型用numpy写一遍,哪怕只是处理一个简单的玩具数据集,收获也会远超预期。很多模型,你以为自己懂了,直到自己调试才会发现,各种细节上的坑远比想象得多。正是因为写这篇笔记,我才真正把这些细节全部串起来。学习一个模型最好的方式,就是把它彻底拆开,再一步步装回去。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 9:16:45

Agent Skills 模块化实战:从设计到 GKE 部署的避坑指南

1. 从"skills"这个热词说起&#xff1a;它到底在解决什么问题最近一段时间&#xff0c;"skills"这个词在技术社区里出现的频率高得离谱。不管是在讨论 Google Cloud 上的 Agent 构建&#xff0c;还是在聊 GKE 集群里的自动化运维&#xff0c;甚至是在 Genk…

作者头像 李华
网站建设 2026/10/6 9:16:28

破解卡文迪什猎豹冲刺:空气动力学与终点线统治术

“曼岛飞弹”听起来像是营销号给马克卡文迪什起的中二外号&#xff0c;但只要你见过一次他在最后一公里的表现&#xff0c;就会觉得这个外号其实还挺克制的。这位车手从身形上看并不像一个标准的冲刺机器——他比很多冲刺主将矮&#xff0c;体重也不算重&#xff0c;但他的获胜…

作者头像 李华
网站建设 2026/10/6 9:15:25

汇编实现内存驻留时钟:TSR中断向量与热键卸载全解析

先说点大实话&#xff1a;TSR内存驻留程序这六个字&#xff0c;放在今天依然能劝退一堆人。但如果你在DOS/VGA时代写过几行汇编&#xff0c;或者现在想弄明白中断向量、内存控制块、驻留退出这些底层机制是怎么协同工作的&#xff0c;那这个主题绝对是性价比最高的一条路。这篇…

作者头像 李华
网站建设 2026/10/6 9:15:25

用Python分析Spotify听歌数据:从API授权到可视化完整实战

年底各大平台都会给你端出年度报告&#xff0c;卡片做得一年比一年精致&#xff0c;可数字背后到底是怎么算出来的&#xff0c;平台基本不会告诉你&#xff0c;也不会把原始数据交给你。作为一个把耳机当器官的人&#xff0c;我对Spotify这些年攒下的听歌数据一直充满好奇&…

作者头像 李华
网站建设 2026/10/6 9:14:37

商品单位设计:多单位换算、精度规则与库存对账实战解析

做进销存和电商后台这些年&#xff0c;我一直觉得“商品单位”是被低估最狠的一个基础功能。外面看就是一个下拉框&#xff0c;选“件”还是“箱”&#xff0c;但真正深入进去你会发现&#xff0c;它是整个库存、价格、采购、财务对账体系的地基。地基没打好&#xff0c;后面盖…

作者头像 李华
网站建设 2026/10/6 9:14:37

手机越用越卡?拆解卡顿根因与完整优化方案

我这些年被问得最多的一个问题&#xff0c;不是“买什么手机好”&#xff0c;而是“手机为什么越用越卡”。刚买回来那会儿&#xff0c;App秒开、页面跟手&#xff0c;用了一两年之后&#xff0c;打开微信转圈、切后台重载、扫码付款都要等半天。很多人第一反应是“手机老了该换…

作者头像 李华