神经网络代码实战:BP、CNN、RNN与PyTorch实现指南

发布时间:2026/9/7 13:49:26
神经网络代码实战:BP、CNN、RNN与PyTorch实现指南 神经网络这门课很多人是“理论会背代码不会写”。看了几天反向传播公式觉得自己懂了打开 PyTorch 准备跑个模型结果连DataLoader都报错。这次我们直接换个思路不按教材目录走而是挑几个最常见、最容易上手的神经网络配可运行的 Python 代码一行行解释输入输出和训练流程。看完这篇文章你应该能自己跑通 BP 神经网络、卷积神经网络、循环神经网络并且能判断不同任务应该优先选哪种网络结构。本文覆盖的内容不是某个开源项目的一键部署而是“神经网络代码实操”。我会用 PyTorch 写 4 组完整示例第一个是 BP 前馈网络解决异或问题用来理解反向传播到底在做什么第二个是 CNN 手写数字识别对应网上常见的“基于卷积神经网络的手写数字识别”教程但用 PyTorch 重新实现第三个是循环神经网络直接从核心公式开始手写一个 vanilla RNN用来理解时间步和隐藏状态最后补充图神经网络、物理信息神经网络、强化学习 TD3 等进阶方向的代码入口。环境上只需要 Python 加 PyTorchCPU 也能跑通有 NVIDIA 显卡就顺手开 CUDA 加速门槛不高适合作为神经网络入门和代码巩固的第一篇实操笔记。1. 核心能力速览能力项说明覆盖网络类型BP/前馈神经网络、CNN 卷积神经网络、RNN 循环神经网络、图神经网络、物理信息神经网络、TD3 强化学习入门代码开发语言Python 3.8深度学习框架PyTorch 1.10 及以上CPU 版本即可运行硬件需求CPU 可跑通全部示例CNN 的 MNIST 训练集建议使用 GPU 加速显存 4G 以上即可实际占用随 batch size 和模型结构调整主要代码库torch、torchvision、matplotlib、numpy学习收益理解反向传播过程、卷积特征提取流程、RNN 隐藏状态更新机制以及进阶网络的设计思想适合人群学过神经网络理论、但缺少代码经验的学生准备做论文实验的入门研究者想从分类任务转向序列建模的开发者从材料中的热词可以看到神经网络相关的高频搜索包括“卷积神经网络”“循环神经网络”“BP神经网络结构图”“物理信息神经网络”“hopfield神经网络”“td3代码pytorch”等。下面我会尽量覆盖这些方向并把重点放在能直接运行的代码上。2. 神经网络分类与适用场景很多初学者会混淆“神经网络”这个大概念下的不同分支。实际工程中选择哪种网络结构取决于输入数据的形态和任务目标。从结构上划分神经网络大致可以分为 5 类前馈神经网络 / BP 神经网络数据从输入层向输出层单向流动常见结构为多层感知机。适用于结构化数据分类、回归任务是理解反向传播算法的基础。卷积神经网络 CNN通过卷积核提取局部特征适合图像、视频帧、语音频谱等多维数据。典型任务包括手写数字识别、图像分类、目标检测。循环神经网络 RNN在时间步之间传递隐藏状态适合文本、语音、股票序列等时间序列数据。vanilla RNN 是理解 LSTM、GRU 的起点。图神经网络 GNN处理非欧几里得数据例如社交网络、分子结构、知识图谱核心操作是节点特征聚合。物理信息神经网络 PINN把物理方程作为损失约束嵌入神经网络训练过程中用于偏微分方程求解、流体模拟等领域。此外还有 Hopfield 神经网络这类早期的联想记忆模型、贝叶斯神经网络这类不确定性建模模型以及 TD3、PPO 等强化学习中使用的 Actor-Critic 网络。选择建议可以简化为三句话图像任务优先 CNN序列任务优先 RNN/LSTM/Transformer表格数据任务优先 XGBoost 或带正则化的 MLP。神经网络不是越复杂越好而是越匹配数据形态越好。3. 环境准备与前置条件3.1 安装 Python 与 PyTorch示例代码全部基于 Python 3.8 以上版本推荐使用 3.9 或 3.10兼容性更稳。PyTorch 安装建议直接走官方网站的镜像命令CPU 用户可以这样做pip install torch torchvision matplotlib numpy如果你有 NVIDIA 显卡并且已经安装好对应版本的 CUDA 驱动可以先到 PyTorch 官网复制适合本机 CUDA 版本的安装命令例如# 示例CUDA 12.1 版本实际命令以官网为准 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu1213.2 验证 PyTorch 是否可用安装完成后先跑一段验证代码确保torch能正常导入同时确认 CUDA 是否可用import torch print(PyTorch 版本:, torch.__version__) print(CUDA 是否可用:, torch.cuda.is_available()) print(CUDA 设备名称:, torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU)如果你用的是 CPU 环境torch.cuda.is_available()会返回False这是正常现象不影响本文代码运行。3.3 检查磁盘与内存MNIST 数据集会自动下载占用约 60MB 磁盘空间。模型文件都很小整个项目在磁盘上占用不到 500MB。内存方面4G 以上内存可以流畅运行所有示例。4. BP 神经网络代码实操从异或问题理解反向传播4.1 为什么选异或问题异或XOR问题是神经网络入门最经典的实验。单层感知机无法解决异或问题因为这个数据集不是线性可分的而带一个隐藏层的 BP 神经网络可以轻松拟合。通过观察 loss 从大到小下降的过程你能直观理解反向传播在做什么。4.2 完整训练代码import torch import torch.nn as nn import torch.optim as optim # 1. 构造异或数据集 X torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]) y torch.tensor([[0.0], [1.0], [1.0], [0.0]]) # 2. 定义两层 BP 神经网络 class BPNet(nn.Module): def __init__(self): super(BPNet, self).__init__() self.fc1 nn.Linear(2, 4) # 输入层到隐藏层隐藏层 4 个神经元 self.fc2 nn.Linear(4, 1) # 隐藏层到输出层输出 1 个值 self.sigmoid nn.Sigmoid() def forward(self, x): x self.sigmoid(self.fc1(x)) x self.sigmoid(self.fc2(x)) return x model BPNet() criterion nn.MSELoss() # 回归任务用均方误差 optimizer optim.SGD(model.parameters(), lr0.5) # 3. 训练 5000 轮 for epoch in range(5000): optimizer.zero_grad() outputs model(X) loss criterion(outputs, y) loss.backward() optimizer.step() if epoch % 500 0: print(fEpoch [{epoch}/5000], Loss: {loss.item():.6f}) # 4. 测试推理结果 print(\n训练后的预测结果) with torch.no_grad(): for i in range(4): pred model(X[i]).item() print(f输入 {X[i].tolist()} - 预测 {pred:.4f} - 四舍五入 {round(pred)})4.3 代码逐段解释第一段构造异或数据四条数据分别对应 0^00、0^11、1^01、1^10。第二段定义网络结构nn.Linear(2, 4)表示输入维度是 2输出维度是 4也就是隐藏层有 4 个神经元nn.Linear(4, 1)输出单个值。激活函数使用 Sigmoid把输出压缩到 0 到 1 之间。训练循环的核心是loss.backward()。PyTorch 会自动计算每个参数对 loss 的梯度然后optimizer.step()用梯度更新权重。这个过程就是反向传播加梯度下降。训练结束后用model(X[i])验证预测结果。预期输出是一个接近 0 和 1 的浮动值例如0.021和0.978说明模型已经学到了异或规则。如果 loss 一直不下降检查学习率是否过大或过小通常0.1到1.0之间的学习率对这个任务都有效。5. 卷积神经网络代码实操基于 PyTorch 的手写数字识别5.1 网络设计思路这一节对应很多同学在搜索的“基于卷积神经网络的手写数字识别”。MNIST 数据集包含 0 到 9 的灰度手写数字图片每张图片尺寸是28x28。CNN 的处理流程是卷积层提取边缘和纹理特征池化层压缩特征尺寸展平后接全连接层完成分类。5.2 完整代码import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transforms # 1. 数据预处理 transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset datasets.MNIST( root./data, trainTrue, downloadTrue, transformtransform ) test_dataset datasets.MNIST( root./data, trainFalse, downloadTrue, transformtransform ) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size256, shuffleFalse) # 2. 定义 CNN 模型 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 nn.Conv2d(1, 32, kernel_size3, padding1) # 输入通道1输出32 self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) # 输入32输出64 self.pool nn.MaxPool2d(2, 2) self.fc1 nn.Linear(64 * 7 * 7, 128) self.fc2 nn.Linear(128, 10) self.relu nn.ReLU() self.dropout nn.Dropout(0.25) def forward(self, x): x self.pool(self.relu(self.conv1(x))) # 28x28 - 14x14 x self.pool(self.relu(self.conv2(x))) # 14x14 - 7x7 x x.view(-1, 64 * 7 * 7) # 展平 x self.relu(self.fc1(x)) x self.dropout(x) x self.fc2(x) return x model CNN() criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) # 3. 训练函数 def train_one_epoch(epoch): model.train() total_loss 0 for batch_idx, (images, labels) in enumerate(train_loader): optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() print(fEpoch {epoch}, 平均 Loss: {total_loss / len(train_loader):.6f}) # 4. 测试函数 def evaluate(): model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: outputs model(images) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() print(f测试集准确率: {100.0 * correct / total:.2f}%) # 5. 训练 5 个 epoch 并评估 for epoch in range(1, 6): train_one_epoch(epoch) evaluate()5.3 结果判断在 CPU 环境下一个 epoch 大约需要几十秒5 个 epoch 能跑到 98% 以上的测试准确率。如果使用 GPU比如 4G 显存的入门显卡训练时间能明显缩短。这组代码有几个值得关注的点第一Conv2d(1, 32, kernel_size3, padding1)把单通道灰度图转成 32 个特征图第二MaxPool2d(2, 2)把特征图尺寸减半第三x.view(-1, 64*7*7)是把多维特征图展平成一维向量方便送入全连接层。如果报维度错误问题通常出在这一步需要根据输入尺寸重新计算。6. 循环神经网络代码实操从核心公式理解时间步6.1 vanilla RNN 的核心公式标准循环神经网络在每个时间步t的隐藏状态更新公式如下h_t tanh(W_hh * h_{t-1} W_xh * x_t b_h) y_t W_hy * h_t b_y其中h_{t-1}是上一个时间步的隐藏状态x_t是当前时间步的输入W_hh是隐藏状态到隐藏状态的权重W_xh是输入到隐藏状态的权重。循环神经网络的工作方式类似于一个人逐字阅读文本每读一个字都会带着上一个字的记忆去理解当前字。下面我用 NumPy 手写这个公式方便你直观理解循环网络内部发生了什么。6.2 手写 vanilla RNN 前向传播import numpy as np def vanilla_rnn_forward(input_seq, hidden_state, W_xh, W_hh, W_hy, b_h, b_y): input_seq: shape (seq_len, input_size) hidden_state: shape (hidden_size,) W_xh: shape (input_size, hidden_size) W_hh: shape (hidden_size, hidden_size) W_hy: shape (hidden_size, output_size) seq_len input_seq.shape[0] hidden_states [] outputs [] for t in range(seq_len): x_t input_seq[t] # 当前时间步输入 hidden_state np.tanh( np.dot(hidden_state, W_hh) np.dot(x_t, W_xh) b_h ) y_t np.dot(hidden_state, W_hy) b_y hidden_states.append(hidden_state.copy()) outputs.append(y_t) return np.array(hidden_states), np.array(outputs) input_size 1 hidden_size 8 output_size 1 np.random.seed(0) W_xh np.random.randn(input_size, hidden_size) * 0.1 W_hh np.random.randn(hidden_size, hidden_size) * 0.1 W_hy np.random.randn(hidden_size, output_size) * 0.1 b_h np.zeros(hidden_size) b_y np.zeros(output_size) # 模拟 10 个时间步的正弦序列输入 t np.linspace(0, 1, 10) input_seq np.sin(2 * np.pi * t).reshape(-1, 1) hidden_state np.zeros(hidden_size) hidden_states, outputs vanilla_rnn_forward( input_seq, hidden_state, W_xh, W_hh, W_hy, b_h, b_y ) print(输入序列长度:, input_seq.shape) print(隐藏状态序列形状:, hidden_states.shape) print(输出序列形状:, outputs.shape)6.3 用 PyTorch 调用内置 RNN实际开发中不需要手写公式直接使用nn.RNN即可。下面是一个用 PyTorch 内置 RNN 层拟合正弦序列下一个值的示例import torch import torch.nn as nn import numpy as np # 生成正弦序列数据 X [] y [] sequence np.sin(np.linspace(0, 20 * np.pi, 500)) seq_len 10 for i in range(len(sequence) - seq_len - 1): X.append(sequence[i:i seq_len]) y.append(sequence[i seq_len]) X torch.tensor(X, dtypetorch.float32).unsqueeze(-1) # shape: (样本数, seq_len, 1) y torch.tensor(y, dtypetorch.float32).unsqueeze(-1) class SimpleRNN(nn.Module): def __init__(self): super(SimpleRNN, self).__init__() self.rnn nn.RNN(input_size1, hidden_size16, batch_firstTrue) self.fc nn.Linear(16, 1) def forward(self, x): out, _ self.rnn(x) # out shape: (batch, seq_len, hidden_size) out self.fc(out[:, -1, :]) # 取最后一个时间步的输出 return out model SimpleRNN() criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr0.01) for epoch in range(100): seq_len min(64, X.shape[0]) idx np.random.choice(X.shape[0], seq_len, replaceFalse) inputs X[idx] targets y[idx] optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, targets) loss.backward() optimizer.step() if epoch % 20 0: print(fEpoch {epoch}, Loss: {loss.item():.6f})batch_firstTrue的含义是输入张量的维度顺序为(batch, seq_len, input_size)。最后取out[:, -1, :]是因为我们只关心最后一个时间步的预测如果做序列到序列的翻译任务通常需要返回完整输出序列。7. 进阶方向代码入口图神经网络、PINN 与 TD37.1 图神经网络 GNN图神经网络处理的是节点和边的数据最常用的库是 PyTorch GeometricPyG。安装命令如下pip install torch_geometric一个简单的图分类或节点分类模型核心步骤包括定义GCNConv卷积层、传递节点特征和边索引。入门时建议先跑官方提供的 Cora 数据集节点分类示例它展示了图数据的基本格式节点特征矩阵x、邻接边索引edge_index、节点标签y。GNN 与 CNN 的最大区别在于CNN 卷积核作用在固定网格区域GNN 的卷积作用在任意拓扑结构的邻居节点上。7.2 物理信息神经网络 PINN物理信息神经网络把科学原理嵌入 AI 模型的设计与实践中核心思想是在损失函数里加入物理方程的残差项。以最简单的一维热传导方程为例网络不仅要拟合初始条件和边界条件还要让模型输出满足方程本身。损失函数通常写成L L_data λ * L_pde这里L_data是数据拟合误差L_pde是控制方程残差λ 是权重系数。实现时使用自动微分计算输出对输入空间坐标的导数把导数代入 PDE 计算残差。PINN 的代码难点不在网络结构而在于损失函数设计和自动微分的正确使用。建议新手先用常微分方程练手再切换到偏微分方程。7.3 TD3 强化学习网络TD3 是强化学习中面向连续动作控制的经典算法它的网络中包含了 Actor 网络和两个 Critic 网络。如果你在搜索“td3代码pytorch”可以先不急着看完整训练代码先理解三个网络各自的作用Actor输入状态输出动作。Critic 1 和 Critic 2输入状态加动作输出 Q 值取两者较小值来避免过度估计。目标网络延迟更新提升训练稳定性。在 PyTorch 中定义 TD3 的 Actor 网络通常就是一个多层感知机输入维度是观测空间维度隐藏层用 ReLU输出层用 Tanh 把动作限制在 [-1, 1]。建议把 TD3 放在 BP 网络代码跑通之后再学习因为它的核心仍然是最简单的全连接网络只是多了目标网络和延迟更新机制。7.4 其他值得一提的网络Hopfield 神经网络是早期用于联想记忆的模型核心公式是状态更新适合理解能量函数概念。贝叶斯神经网络通过给权重加先验分布来估计不确定性在 PyTorch 中可以用torch.distributions实现变分推断的简化版。小波 Elman 神经网络则是把 Elman 循环网络和小波变换结合常用于时间序列预测和故障诊断。这些方向都有各自的代码库和论文复现仓库初学者不建议一开始就扎进太偏门的模型。8. 神经网络代码常见问题与排查方法写神经网络代码时遇到的问题90% 集中在依赖、维度、数据、训练效果四个方面。下面按优先级整理一份排查清单。8.1 依赖与运行环境问题问题现象可能原因排查方式解决方案ModuleNotFoundError: No module named torchPyTorch 未安装执行python -c import torch按 3.1 节命令安装CUDA 不可用显卡驱动较旧或安装的是 CPU 版 PyTorch打印torch.cuda.is_available()更新驱动或重新安装对应 CUDA 版本 PyTorch下载 MNIST 数据集失败网络受限检查网络看./data目录是否生成缓存手动下载数据集并放入指定目录8.2 维度不匹配这是最常见的问题。PyTorch 报错信息中会明确写出 Upsert 维度例如RuntimeError: mat1 and mat2 shapes cannot be multiplied排查顺序是先看输入张量的 shape再看nn.Linear的第一个参数是否等于输入维度。CNN 模型展平之前的维度计算尤其容易出错。一个通用办法是在forward里临时加一行print(x.shape)确认维度后再删掉。8.3 Loss 不下降Loss 不下降的原因通常有几个学习率过大或过小。SGD 在 XOR 问题上建议用 0.1 到 1.0Adam 在分类任务上建议 0.0001 到 0.001。数据没有归一化。MNIST 使用transforms.Normalize后收敛速度明显提升。激活函数选错。分类任务输出层不要用 Sigmoid应该用 Softmax 加 CrossEntropyLoss。标签和数据不匹配。检查dataloader中images和labels是否一一对应shuffleTrue时尤其注意。8.4 过拟合训练集准确率高、测试集准确率低说明模型过拟合。常用解法有三个增加Dropout层、减小模型容量、加入数据增强。对 MNIST 这类简单数据集两层卷积加一个 Dropout 通常不会出现过拟合。8.5 训练速度慢CPU 上训练 MNIST 可能需要几十秒一个 epoch这很正常。如果速度慢到无法接受优先检查 batch size 是否太小batch size 从 32 调到 128训练时间能下降不少。如果安装了 PyTorch GPU 版运行前用torch.cuda.is_available()确认已经在使用 GPU。8.6 显存不足如果你在训练更复杂的模型时遇到CUDA out of memory处理方法按优先级排列减小 batch size降低输入图像分辨率使用torch.no_grad()包裹推理流程在with torch.no_grad()中不要调用loss.backward()。本文的 MNIST 示例显存占用很低4G 显存完全够用实际占用会随 batch size 变化而变化。9. 神经网络代码训练最佳实践9.1 固定随机种子神经网络训练涉及随机初始化同一个代码跑两次结果可能不同。做实验对比时在代码开头固定随机种子import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) set_seed(42)9.2 数据、模型、输出分目录管理写项目时不要把所有文件堆在同一个目录。建议按下面的结构组织project/ ├── data/ # 数据集存放位置 ├── models/ # 模型结构定义 ├── checkpoints/ # 训练得到的模型权重 ├── logs/ # 训练日志和 loss 曲线 └── outputs/ # 预测结果和可视化图片这样做的好处是模型文件可以独立保存后续做批量推理时只需要加载 checkpoint不需要重新训练。9.3 保存与加载模型训练完成后用torch.save保存参数推理时用torch.load加载torch.save(model.state_dict(), ./checkpoints/cnn_mnist.pth) # 推理时 model CNN() model.load_state_dict(torch.load(./checkpoints/cnn_mnist.pth)) model.eval()注意model.eval()不能省略它会关闭 Dropout 层和 BatchNorm 层的训练行为保证推理结果稳定。9.4 第一次训练用最小配置任何新数据集第一次跑通代码比跑出好效果更重要。先把 batch size 调小、epoch 调小、网络层数调浅确认代码没有报错、loss 能下降再逐步增加参数。这个习惯能帮你节省大量排错时间。9.5 数据版权与隐私合规提醒使用公开数据集训练模型时注意数据集本身的授权协议。MNIST、CIFAR 这类公开数据集可以用于学习和研究。如果自己采集图像、声音、人脸数据训练神经网络必须确保数据来源合法获得相关人员的授权并遵守所在地区对个人信息保护的法律要求。涉及他人肖像、声音、隐私数据时不能随意公开或商用训练出来的模型。工程应用前建议对训练数据做匿名化处理。10. 从代码理解神经网络的关键先跑通再深挖神经网络代码实操的最终目的不是把所有模型背下来而是建立“结构 - 输入 - 输出 - 损失函数”的直觉。先用 BP 网络跑通异或问题你就能理解反向传播和梯度下降的关系再用 CNN 跑通 MNIST你就能理解卷积层为什么适合图像接着手写一遍 RNN 核心公式你就能明白时间步和隐藏状态的工作机制。这篇代码教程覆盖了入门阶段最值得花时间的几个方向BP 前馈网络、CNN 卷积网络、RNN 循环网络以及 GNN、PINN、TD3 的进阶入口。建议你把代码复制到本地按照 3.1 节安装环境后逐段运行。遇到维度报错就打印 shape遇到 loss 不下降就调学习率固定随机种子后再看效果。先跑通全部示例再考虑把网络结构加深、换数据集、接入自己的项目。后面如果继续深入学习可以沿着“CNN 变体ResNet、EfficientNet”“RNN 变体LSTM、GRU、Transformer”“物理信息神经网络论文复现”这三条主线走每一步都配代码实验。这套代码跑完你不仅会说神经网络还能真正写出来。