从零构建深度信念网络用NumPy实现MNIST手写数字识别深度信念网络DBN作为深度学习发展史上的重要里程碑至今仍是理解神经网络分层特征提取的绝佳教学案例。本文将带您用纯NumPy实现一个可配置层数的DBN并在MNIST数据集上完成手写数字识别任务。不同于调用现成的深度学习框架这种造轮子的过程能让我们真正掌握DBN的核心机制——从RBM的对比散度训练到分层特征抽象的形成原理。1. 环境准备与数据加载在开始构建DBN之前我们需要准备Python环境和MNIST数据集。这里选择NumPy作为核心计算库它不仅提供了高效的矩阵运算能力还能让我们避开深度学习框架的黑箱亲手实现每个关键步骤。首先安装必要的库pip install numpy matplotlib scikit-learn加载和预处理MNIST数据的完整代码如下import numpy as np from sklearn.datasets import fetch_openml from sklearn.model_selection import train_test_split def load_mnist(binarizeTrue): mnist fetch_openml(mnist_784, version1) X mnist.data.astype(float32) / 255.0 y mnist.target.astype(int) if binarize: # 将像素值二值化更适合RBM处理 X (X 0.5).astype(np.float32) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42) return X_train, X_test, y_train, y_test注意MNIST原始像素值范围是0-255我们将其归一化到0-1之间。对于DBN的RBM层通常建议对输入进行二值化处理这符合RBM的二元随机神经元特性。数据加载后我们可以检查一下数据维度X_train, X_test, y_train, y_test load_mnist() print(f训练集形状: {X_train.shape}, 测试集形状: {X_test.shape})典型输出应该是训练集形状: (56000, 784), 测试集形状: (14000, 784)2. 实现受限玻尔兹曼机(RBM)RBM是DBN的基本构建块理解它的实现是掌握DBN的关键。一个RBM包含可见层和隐藏层层间全连接层内无连接。我们将实现对比散度(CD)算法来训练RBM。2.1 RBM类结构设计class RBM: def __init__(self, n_visible, n_hidden, learning_rate0.01, k1): self.n_visible n_visible self.n_hidden n_hidden self.lr learning_rate self.k k # CD-k算法中的k值 # 初始化权重和偏置 self.W np.random.normal(0, 0.01, size(n_visible, n_hidden)) self.h_bias np.zeros(n_hidden) self.v_bias np.zeros(n_visible) def sigmoid(self, x): return 1 / (1 np.exp(-x))2.2 对比散度训练过程CD-k算法的核心步骤如下正向传播计算隐藏层概率采样隐藏状态反向传播重构可见层重复Gibbs采样k次更新参数def contrastive_divergence(self, v0): # 正向传播 h0_prob self.sigmoid(np.dot(v0, self.W) self.h_bias) h0_sample (np.random.random(sizeh0_prob.shape) h0_prob).astype(np.float32) # Gibbs采样链 vk v0.copy() for _ in range(self.k): hk_prob self.sigmoid(np.dot(vk, self.W) self.h_bias) hk_sample (np.random.random(sizehk_prob.shape) hk_prob).astype(np.float32) vk_prob self.sigmoid(np.dot(hk_sample, self.W.T) self.v_bias) vk (np.random.random(sizevk_prob.shape) vk_prob).astype(np.float32) # 计算梯度 hk_prob self.sigmoid(np.dot(vk, self.W) self.h_bias) positive_grad np.dot(v0.T, h0_prob) negative_grad np.dot(vk.T, hk_prob) # 更新参数 self.W self.lr * (positive_grad - negative_grad) / v0.shape[0] self.v_bias self.lr * np.mean(v0 - vk, axis0) self.h_bias self.lr * np.mean(h0_prob - hk_prob, axis0) # 计算重构误差 reconstruction self.sigmoid(np.dot(h0_sample, self.W.T) self.v_bias) error np.mean(np.sum((v0 - reconstruction) ** 2, axis1)) return error2.3 RBM训练监控为了监控训练过程我们可以添加可视化功能def visualize_weights(self, n_rows10, n_cols10): import matplotlib.pyplot as plt fig, axes plt.subplots(n_rows, n_cols, figsize(10, 10)) for i, ax in enumerate(axes.flat): if i self.n_hidden: ax.imshow(self.W[:, i].reshape(28, 28), cmapgray) ax.axis(off) plt.show()训练RBM的示例代码rbm RBM(n_visible784, n_hidden256, learning_rate0.01, k1) n_epochs 20 batch_size 100 for epoch in range(n_epochs): np.random.shuffle(X_train) total_error 0 for i in range(0, X_train.shape[0], batch_size): batch X_train[i:ibatch_size] error rbm.contrastive_divergence(batch) total_error error print(fEpoch {epoch1}/{n_epochs}, Reconstruction Error: {total_error/(i1):.4f}) if epoch % 5 0: rbm.visualize_weights()3. 构建深度信念网络(DBN)DBN由多个堆叠的RBM组成通过逐层无监督预训练学习分层特征表示最后用有监督方法微调整个网络。3.1 DBN类结构设计class DBN: def __init__(self, layer_sizes): self.rbms [] for i in range(len(layer_sizes)-1): rbm RBM(n_visiblelayer_sizes[i], n_hiddenlayer_sizes[i1], learning_rate0.01) self.rbms.append(rbm) # 微调阶段的分类器权重 self.W np.random.normal(0, 0.01, size(layer_sizes[-1], 10)) self.b np.zeros(10) def pretrain(self, X, epochs10, batch_size100): input_data X.copy() for i, rbm in enumerate(self.rbms): print(fPre-training RBM layer {i1}/{len(self.rbms)}) for epoch in range(epochs): np.random.shuffle(input_data) for j in range(0, input_data.shape[0], batch_size): batch input_data[j:jbatch_size] rbm.contrastive_divergence(batch) # 获取当前RBM的隐藏表示作为下一层的输入 input_data rbm.sigmoid(np.dot(input_data, rbm.W) rbm.h_bias)3.2 前向传播与特征提取def transform(self, X): hidden_activation X.copy() for rbm in self.rbms: hidden_activation rbm.sigmoid( np.dot(hidden_activation, rbm.W) rbm.h_bias) return hidden_activation def predict_proba(self, X): hidden_activation self.transform(X) output np.dot(hidden_activation, self.W) self.b return self.softmax(output) def softmax(self, x): exp_x np.exp(x - np.max(x, axis1, keepdimsTrue)) return exp_x / np.sum(exp_x, axis1, keepdimsTrue)3.3 微调阶段实现微调阶段使用反向传播算法调整所有层的权重def finetune(self, X, y, epochs20, batch_size100, learning_rate0.1): y_onehot np.eye(10)[y] # 转为one-hot编码 for epoch in range(epochs): indices np.random.permutation(X.shape[0]) total_loss 0 correct 0 for i in range(0, X.shape[0], batch_size): # 获取当前batch idx indices[i:ibatch_size] X_batch X[idx] y_batch y_onehot[idx] # 前向传播 hidden_activations [X_batch] for rbm in self.rbms: hidden_activation rbm.sigmoid( np.dot(hidden_activations[-1], rbm.W) rbm.h_bias) hidden_activations.append(hidden_activation) output np.dot(hidden_activations[-1], self.W) self.b proba self.softmax(output) # 计算损失和准确率 loss -np.mean(np.sum(y_batch * np.log(proba 1e-10), axis1)) total_loss loss correct np.sum(np.argmax(proba, axis1) np.argmax(y_batch, axis1)) # 反向传播 output_error (proba - y_batch) / batch_size hidden_error [output_error.dot(self.W.T)] # 计算隐藏层误差 for j in range(len(self.rbms)-1, -1, -1): act hidden_activations[j1] error hidden_error[-1] * act * (1 - act) hidden_error.append(error.dot(self.rbms[j].W.T)) hidden_error.reverse() # 更新分类器权重 self.W - learning_rate * np.dot(hidden_activations[-1].T, output_error) self.b - learning_rate * np.sum(output_error, axis0) # 更新RBM权重 for j in range(len(self.rbms)): grad_W np.dot(hidden_activations[j].T, hidden_error[j1]) grad_h_bias np.sum(hidden_error[j1], axis0) self.rbms[j].W - learning_rate * grad_W self.rbms[j].h_bias - learning_rate * grad_h_bias accuracy correct / X.shape[0] print(fEpoch {epoch1}/{epochs}, Loss: {total_loss/(i1):.4f}, fAccuracy: {accuracy:.4f})4. 训练与评估完整DBN模型现在我们可以将所有这些组件组合起来训练一个完整的DBN模型# 定义DBN结构784-256-128-64 dbn DBN([784, 256, 128, 64]) # 预训练各层RBM print(Starting pre-training...) dbn.pretrain(X_train, epochs15) # 微调整个网络 print(Starting fine-tuning...) dbn.finetune(X_train, y_train, epochs30) # 评估测试集性能 test_proba dbn.predict_proba(X_test) test_pred np.argmax(test_proba, axis1) accuracy np.mean(test_pred y_test) print(fTest accuracy: {accuracy:.4f})4.1 参数调优建议在实践中有几个关键参数会影响DBN的性能网络结构通常从较宽的底层开始逐渐缩小隐藏层大小示例配置784-500-200-100 或 784-1024-512-256学习率预训练阶段0.01-0.1微调阶段0.001-0.01通常比预训练小批大小32-256之间较大的批大小通常更稳定CD-k中的k值通常k1就能工作得很好增加k可能提高质量但降低速度4.2 可视化中间特征理解DBN学习到的特征表示非常重要import matplotlib.pyplot as plt # 可视化第一层RBM的权重 plt.figure(figsize(10,10)) for i in range(25): plt.subplot(5,5,i1) plt.imshow(dbn.rbms[0].W[:,i].reshape(28,28), cmapgray) plt.axis(off) plt.suptitle(First Layer Learned Features, fontsize16) plt.show() # 可视化第二层隐藏激活 sample_idx np.random.choice(X_test.shape[0], 5) hidden_activations dbn.transform(X_test[sample_idx]) plt.figure(figsize(15,3)) for i in range(5): plt.subplot(1,5,i1) plt.imshow(hidden_activations[i].reshape(8,8), cmapviridis) plt.title(fLabel: {y_test[sample_idx[i]]}) plt.axis(off) plt.suptitle(Second Layer Hidden Activations, fontsize16) plt.show()4.3 常见问题排查在实现DBN时可能会遇到以下问题重构误差不下降降低学习率增加Gibbs采样的k值检查数据预处理是否正确微调阶段准确率低延长预训练时间尝试不同的网络结构调整微调阶段的学习率训练过程不稳定减小批大小添加权重衰减正则化对权重初始化使用更小的标准差# 添加权重衰减的RBM初始化示例 self.W np.random.normal(0, 0.001, size(n_visible, n_hidden))
别只盯着CNN/RNN了!手把手用Python和NumPy实现一个玩具级DBN(附完整代码)
发布时间:2026/5/31 19:46:15
从零构建深度信念网络用NumPy实现MNIST手写数字识别深度信念网络DBN作为深度学习发展史上的重要里程碑至今仍是理解神经网络分层特征提取的绝佳教学案例。本文将带您用纯NumPy实现一个可配置层数的DBN并在MNIST数据集上完成手写数字识别任务。不同于调用现成的深度学习框架这种造轮子的过程能让我们真正掌握DBN的核心机制——从RBM的对比散度训练到分层特征抽象的形成原理。1. 环境准备与数据加载在开始构建DBN之前我们需要准备Python环境和MNIST数据集。这里选择NumPy作为核心计算库它不仅提供了高效的矩阵运算能力还能让我们避开深度学习框架的黑箱亲手实现每个关键步骤。首先安装必要的库pip install numpy matplotlib scikit-learn加载和预处理MNIST数据的完整代码如下import numpy as np from sklearn.datasets import fetch_openml from sklearn.model_selection import train_test_split def load_mnist(binarizeTrue): mnist fetch_openml(mnist_784, version1) X mnist.data.astype(float32) / 255.0 y mnist.target.astype(int) if binarize: # 将像素值二值化更适合RBM处理 X (X 0.5).astype(np.float32) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42) return X_train, X_test, y_train, y_test注意MNIST原始像素值范围是0-255我们将其归一化到0-1之间。对于DBN的RBM层通常建议对输入进行二值化处理这符合RBM的二元随机神经元特性。数据加载后我们可以检查一下数据维度X_train, X_test, y_train, y_test load_mnist() print(f训练集形状: {X_train.shape}, 测试集形状: {X_test.shape})典型输出应该是训练集形状: (56000, 784), 测试集形状: (14000, 784)2. 实现受限玻尔兹曼机(RBM)RBM是DBN的基本构建块理解它的实现是掌握DBN的关键。一个RBM包含可见层和隐藏层层间全连接层内无连接。我们将实现对比散度(CD)算法来训练RBM。2.1 RBM类结构设计class RBM: def __init__(self, n_visible, n_hidden, learning_rate0.01, k1): self.n_visible n_visible self.n_hidden n_hidden self.lr learning_rate self.k k # CD-k算法中的k值 # 初始化权重和偏置 self.W np.random.normal(0, 0.01, size(n_visible, n_hidden)) self.h_bias np.zeros(n_hidden) self.v_bias np.zeros(n_visible) def sigmoid(self, x): return 1 / (1 np.exp(-x))2.2 对比散度训练过程CD-k算法的核心步骤如下正向传播计算隐藏层概率采样隐藏状态反向传播重构可见层重复Gibbs采样k次更新参数def contrastive_divergence(self, v0): # 正向传播 h0_prob self.sigmoid(np.dot(v0, self.W) self.h_bias) h0_sample (np.random.random(sizeh0_prob.shape) h0_prob).astype(np.float32) # Gibbs采样链 vk v0.copy() for _ in range(self.k): hk_prob self.sigmoid(np.dot(vk, self.W) self.h_bias) hk_sample (np.random.random(sizehk_prob.shape) hk_prob).astype(np.float32) vk_prob self.sigmoid(np.dot(hk_sample, self.W.T) self.v_bias) vk (np.random.random(sizevk_prob.shape) vk_prob).astype(np.float32) # 计算梯度 hk_prob self.sigmoid(np.dot(vk, self.W) self.h_bias) positive_grad np.dot(v0.T, h0_prob) negative_grad np.dot(vk.T, hk_prob) # 更新参数 self.W self.lr * (positive_grad - negative_grad) / v0.shape[0] self.v_bias self.lr * np.mean(v0 - vk, axis0) self.h_bias self.lr * np.mean(h0_prob - hk_prob, axis0) # 计算重构误差 reconstruction self.sigmoid(np.dot(h0_sample, self.W.T) self.v_bias) error np.mean(np.sum((v0 - reconstruction) ** 2, axis1)) return error2.3 RBM训练监控为了监控训练过程我们可以添加可视化功能def visualize_weights(self, n_rows10, n_cols10): import matplotlib.pyplot as plt fig, axes plt.subplots(n_rows, n_cols, figsize(10, 10)) for i, ax in enumerate(axes.flat): if i self.n_hidden: ax.imshow(self.W[:, i].reshape(28, 28), cmapgray) ax.axis(off) plt.show()训练RBM的示例代码rbm RBM(n_visible784, n_hidden256, learning_rate0.01, k1) n_epochs 20 batch_size 100 for epoch in range(n_epochs): np.random.shuffle(X_train) total_error 0 for i in range(0, X_train.shape[0], batch_size): batch X_train[i:ibatch_size] error rbm.contrastive_divergence(batch) total_error error print(fEpoch {epoch1}/{n_epochs}, Reconstruction Error: {total_error/(i1):.4f}) if epoch % 5 0: rbm.visualize_weights()3. 构建深度信念网络(DBN)DBN由多个堆叠的RBM组成通过逐层无监督预训练学习分层特征表示最后用有监督方法微调整个网络。3.1 DBN类结构设计class DBN: def __init__(self, layer_sizes): self.rbms [] for i in range(len(layer_sizes)-1): rbm RBM(n_visiblelayer_sizes[i], n_hiddenlayer_sizes[i1], learning_rate0.01) self.rbms.append(rbm) # 微调阶段的分类器权重 self.W np.random.normal(0, 0.01, size(layer_sizes[-1], 10)) self.b np.zeros(10) def pretrain(self, X, epochs10, batch_size100): input_data X.copy() for i, rbm in enumerate(self.rbms): print(fPre-training RBM layer {i1}/{len(self.rbms)}) for epoch in range(epochs): np.random.shuffle(input_data) for j in range(0, input_data.shape[0], batch_size): batch input_data[j:jbatch_size] rbm.contrastive_divergence(batch) # 获取当前RBM的隐藏表示作为下一层的输入 input_data rbm.sigmoid(np.dot(input_data, rbm.W) rbm.h_bias)3.2 前向传播与特征提取def transform(self, X): hidden_activation X.copy() for rbm in self.rbms: hidden_activation rbm.sigmoid( np.dot(hidden_activation, rbm.W) rbm.h_bias) return hidden_activation def predict_proba(self, X): hidden_activation self.transform(X) output np.dot(hidden_activation, self.W) self.b return self.softmax(output) def softmax(self, x): exp_x np.exp(x - np.max(x, axis1, keepdimsTrue)) return exp_x / np.sum(exp_x, axis1, keepdimsTrue)3.3 微调阶段实现微调阶段使用反向传播算法调整所有层的权重def finetune(self, X, y, epochs20, batch_size100, learning_rate0.1): y_onehot np.eye(10)[y] # 转为one-hot编码 for epoch in range(epochs): indices np.random.permutation(X.shape[0]) total_loss 0 correct 0 for i in range(0, X.shape[0], batch_size): # 获取当前batch idx indices[i:ibatch_size] X_batch X[idx] y_batch y_onehot[idx] # 前向传播 hidden_activations [X_batch] for rbm in self.rbms: hidden_activation rbm.sigmoid( np.dot(hidden_activations[-1], rbm.W) rbm.h_bias) hidden_activations.append(hidden_activation) output np.dot(hidden_activations[-1], self.W) self.b proba self.softmax(output) # 计算损失和准确率 loss -np.mean(np.sum(y_batch * np.log(proba 1e-10), axis1)) total_loss loss correct np.sum(np.argmax(proba, axis1) np.argmax(y_batch, axis1)) # 反向传播 output_error (proba - y_batch) / batch_size hidden_error [output_error.dot(self.W.T)] # 计算隐藏层误差 for j in range(len(self.rbms)-1, -1, -1): act hidden_activations[j1] error hidden_error[-1] * act * (1 - act) hidden_error.append(error.dot(self.rbms[j].W.T)) hidden_error.reverse() # 更新分类器权重 self.W - learning_rate * np.dot(hidden_activations[-1].T, output_error) self.b - learning_rate * np.sum(output_error, axis0) # 更新RBM权重 for j in range(len(self.rbms)): grad_W np.dot(hidden_activations[j].T, hidden_error[j1]) grad_h_bias np.sum(hidden_error[j1], axis0) self.rbms[j].W - learning_rate * grad_W self.rbms[j].h_bias - learning_rate * grad_h_bias accuracy correct / X.shape[0] print(fEpoch {epoch1}/{epochs}, Loss: {total_loss/(i1):.4f}, fAccuracy: {accuracy:.4f})4. 训练与评估完整DBN模型现在我们可以将所有这些组件组合起来训练一个完整的DBN模型# 定义DBN结构784-256-128-64 dbn DBN([784, 256, 128, 64]) # 预训练各层RBM print(Starting pre-training...) dbn.pretrain(X_train, epochs15) # 微调整个网络 print(Starting fine-tuning...) dbn.finetune(X_train, y_train, epochs30) # 评估测试集性能 test_proba dbn.predict_proba(X_test) test_pred np.argmax(test_proba, axis1) accuracy np.mean(test_pred y_test) print(fTest accuracy: {accuracy:.4f})4.1 参数调优建议在实践中有几个关键参数会影响DBN的性能网络结构通常从较宽的底层开始逐渐缩小隐藏层大小示例配置784-500-200-100 或 784-1024-512-256学习率预训练阶段0.01-0.1微调阶段0.001-0.01通常比预训练小批大小32-256之间较大的批大小通常更稳定CD-k中的k值通常k1就能工作得很好增加k可能提高质量但降低速度4.2 可视化中间特征理解DBN学习到的特征表示非常重要import matplotlib.pyplot as plt # 可视化第一层RBM的权重 plt.figure(figsize(10,10)) for i in range(25): plt.subplot(5,5,i1) plt.imshow(dbn.rbms[0].W[:,i].reshape(28,28), cmapgray) plt.axis(off) plt.suptitle(First Layer Learned Features, fontsize16) plt.show() # 可视化第二层隐藏激活 sample_idx np.random.choice(X_test.shape[0], 5) hidden_activations dbn.transform(X_test[sample_idx]) plt.figure(figsize(15,3)) for i in range(5): plt.subplot(1,5,i1) plt.imshow(hidden_activations[i].reshape(8,8), cmapviridis) plt.title(fLabel: {y_test[sample_idx[i]]}) plt.axis(off) plt.suptitle(Second Layer Hidden Activations, fontsize16) plt.show()4.3 常见问题排查在实现DBN时可能会遇到以下问题重构误差不下降降低学习率增加Gibbs采样的k值检查数据预处理是否正确微调阶段准确率低延长预训练时间尝试不同的网络结构调整微调阶段的学习率训练过程不稳定减小批大小添加权重衰减正则化对权重初始化使用更小的标准差# 添加权重衰减的RBM初始化示例 self.W np.random.normal(0, 0.001, size(n_visible, n_hidden))