学习笔记(FNN前馈神经网络)
目录文章目录一、前馈神经网络的概念二、前馈神经网络的结构三、前馈神经网络的数学原理1、前馈神经网络的前向传播2、前馈神经网络的梯度下降法3、前馈神经网络的误差反向传播算法四、使用前馈神经网络对MNIST 手写数字进行分类参考一、前馈神经网络的概念前馈神经网络Feedforward Neural Network是最基础的神经网络架构信息从输入层单向传递经过若干隐藏层逐层变换最终到达输出层层内无连接、无反馈循环。每个神经元通过加权求和输入信号并施加非线性激活函数如ReLU、Sigmoid产生输出网络通过反向传播算法调整权重以最小化损失函数从而学习从输入到输出的复杂映射关系。它是深度学习的基础构件广泛应用于分类、回归等监督学习任务中。前馈网络是Transformer模型中的核心组件之一位于每个Transformer层的后半部分。它的经典结构是线性变换升维→ 激活函数 → 线性变换降维。二、前馈神经网络的结构1.输入层输入层是网络的第一层负责接收输入材料。每个神经元代表一个输入特征所有输入特征都会传递到下一层。2.隐藏层隐藏层是前馈神经网络的核心部分负责进行复杂的非线性变换。通常前馈神经网络包含一个或多个隐藏层。每个隐藏层的神经元接收上一层的输出经过加权求和后再利用激活函数生成输出。3.输出层网络的最后一层负责根据隐藏层的输出生成最终的预测结果。输出层的神经元数量和任务的性质相关例如在二分类任务中输出层通常只有一个神经元而在多分类任务中则有多个神经元。就是输出层三、前馈神经网络的数学原理1、前馈神经网络的前向传播下面的记号是来描述一个前馈神经网络前馈神经网络通过下面公式进行信息传播这样前馈神经网络通过逐层的信息传递得到网络最后的输出a(L)。整个网络可以看做是一个复合函数将向量x作为第1层的输入a(0)将第L层的输出a(L)作为整个函数的输出。2、前馈神经网络的梯度下降法神经网络具有极其强大的拟合能力可以作为一个万能函数来使用通过进行复杂的特征转换可以以任意精度来近似任何一个有界闭集函数。类似于其他机器学习算法求解参数的数值计算方法首先考虑用梯度下降法来进行参数学习。如果采用交叉熵损失函数对于样本(x,y)其损失函数为其中y∈{0, 1}C是标签y对应的one-hot向量表示C是类别的个数。给定训练集D{(x(1), y(1)),(x(2), y(2)),...,(x(N), y(N))}将每个样本x(n)输入给前馈神经网络得到神经网络的输出后其在训练集D上的结构化风险函数为其中W和b分别表示网络中所有的权重矩阵和偏置向量。是正则化项公式为然后用梯度下降法来进行学习。在梯度下降法的每次迭代中第l层的参数W(l)和b(l)的参数更新方式为梯度下降法需要计算损失函数对参数的偏导数如果用链式法则对每个参数逐一求偏导涉及到矩阵微分效率比较低。所以在神经网络中经常使用反向传播算法 来高效地计算梯度。3、前馈神经网络的误差反向传播算法假设给定一个样本x,y将其输入到神经网络模型中得到损失函数为如果要采用梯度下降法对神经网络的参数进行学习那么就要计算损失函数关于每个参数的导数。拿第l层中的参数矩阵W(l)和b(l)为例计算损失函数对参数矩阵的偏导数。但因为的计算涉及到矩阵微分非常繁琐于是应先计算W(l)中某个元素的偏导数。根据链式法则有上面两个公式中的第二项都是目标函数关于第l层的神经元z(l)的偏导数称为误差项。那么就需要计算三个偏导数和。1计算偏导数由于z(l)和Wij(l)的函数关系为所以偏导数为其中Wi:(l)为权重矩阵W(l)的第i行。2计算偏导数因为z(l)与b(l)的函数关系为因此偏导数是一个维度是m(l) × m(l) 的单位矩阵。3计算误差项用δ(l)来定义第l层神经元的误差项它用来表示第l层神经元对最终损失的影响也反映了最终损失对第l层神经元的敏感程度。根据链式法则第l层的误差项为首先根据其中fl(•)是按位计算的函数计算的偏导数如下。diag(•)表示对角矩阵。然后根据有于是第l层的误差项δ(l)最终表示为其中⊙表示向量的点积运算符表示每个元素相乘。上面这个公式就是误差的反向传播公式因为第l层的误差项可以通过第l1层的误差项计算得到。反向传播算法的含义是第l层的一个神经元的误差项等于该神经元激活函数的梯度再乘上所有与该神经元相连接的第l1层的神经元的误差项的权重和。再回到开头求两个参数的公式里面的三个偏导数都已经求出来了。于是可以求出进一步损失函数关于第l层权重W(l)梯度为而损失函数关于第l层偏置b(l)的梯度为于是在利用误差反向传播算法计算出每一层的误差项后就可以得到每一层参数的梯度。基于误差反向传播算法backpropagationBP 的前馈神经网络训练过程可以分为以下三步1、在前向传播时计算每一层的净输入z(l)和激活值a(l)直至最后一层2、用误差反向传播计算每一层的误差项 δ(l)3、计算每一层参数的偏导数并更新参数。四、使用前馈神经网络对MNIST 手写数字进行分类本任务是使用 PyTorch 搭建了一个三层前馈神经网络输入层784维→隐藏层128维→隐藏层128维→输出层10维通过 ReLU 激活函数和 Dropout 正则化在 MNIST 手写数字数据集上进行监督学习训练采用交叉熵损失函数和 Adam 优化器经过10轮迭代完成模型优化最终实现了对手写数字 0-9 的自动分类识别。import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pyplot as plt # 1. 超参数设置 INPUT_SIZE 28 * 28 # MNIST 图片尺寸 28x28 HIDDEN_SIZE 128 # 隐藏层神经元数 NUM_CLASSES 10 # 0-9 共10个数字 NUM_EPOCHS 10 # 训练轮数 BATCH_SIZE 64 # 批次大小 LEARNING_RATE 0.001 # 学习率 # 2. 数据预处理与加载 transform transforms.Compose([ transforms.ToTensor(), # 转为 [0,1] 的 Tensor transforms.Normalize((0.1307,), (0.3081,)) # MNIST 标准归一化 ]) # 下载并加载训练集和测试集 train_dataset datasets.MNIST( root./data, trainTrue, downloadTrue, transformtransform ) test_dataset datasets.MNIST( root./data, trainFalse, downloadTrue, transformtransform ) train_loader DataLoader(train_dataset, batch_sizeBATCH_SIZE, shuffleTrue) test_loader DataLoader(test_dataset, batch_sizeBATCH_SIZE, shuffleFalse) print(f训练集样本数: {len(train_dataset)}) print(f测试集样本数: {len(test_dataset)}) # 3. 定义前馈神经网络模型 class FeedforwardNN(nn.Module): def __init__(self, input_size, hidden_size, num_classes): super(FeedforwardNN, self).__init__() self.layer1 nn.Linear(input_size, hidden_size) self.relu nn.ReLU() self.dropout nn.Dropout(0.2) # 防止过拟合 self.layer2 nn.Linear(hidden_size, hidden_size) self.layer3 nn.Linear(hidden_size, num_classes) def forward(self, x): out self.layer1(x) out self.relu(out) out self.dropout(out) out self.layer2(out) out self.relu(out) out self.dropout(out) out self.layer3(out) return out # 实例化模型 device torch.device(cuda if torch.cuda.is_available() else cpu) model FeedforwardNN(INPUT_SIZE, HIDDEN_SIZE, NUM_CLASSES).to(device) # 4. 损失函数和优化器 criterion nn.CrossEntropyLoss() # 交叉熵损失内置 Softmax optimizer optim.Adam(model.parameters(), lrLEARNING_RATE) # 5. 训练模型 train_losses [] train_accuracies [] test_accuracies [] print(\n开始训练...) for epoch in range(NUM_EPOCHS): model.train() running_loss 0.0 correct 0 total 0 for batch_idx, (images, labels) in enumerate(train_loader): # 将图片展平为一维向量 [batch_size, 784] images images.view(-1, INPUT_SIZE).to(device) labels labels.to(device) # 前向传播 outputs model(images) loss criterion(outputs, labels) # 反向传播和优化 optimizer.zero_grad() loss.backward() optimizer.step() # 统计 running_loss loss.item() _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() # 每100个批次打印一次 if (batch_idx 1) % 100 0: print(fEpoch [{epoch1}/{NUM_EPOCHS}], fBatch [{batch_idx1}/{len(train_loader)}], fLoss: {loss.item():.4f}) # 计算本轮训练指标 epoch_loss running_loss / len(train_loader) epoch_acc 100 * correct / total train_losses.append(epoch_loss) train_accuracies.append(epoch_acc) # 测试集评估 model.eval() correct_test 0 total_test 0 with torch.no_grad(): for images, labels in test_loader: images images.view(-1, INPUT_SIZE).to(device) labels labels.to(device) outputs model(images) _, predicted torch.max(outputs.data, 1) total_test labels.size(0) correct_test (predicted labels).sum().item() test_acc 100 * correct_test / total_test test_accuracies.append(test_acc) print(fEpoch [{epoch1}/{NUM_EPOCHS}] 完成 | f训练损失: {epoch_loss:.4f} | f训练准确率: {epoch_acc:.2f}% | f测试准确率: {test_acc:.2f}%) print(\n训练完成!) # 6. 可视化训练过程 fig, axes plt.subplots(1, 2, figsize(14, 5)) # 损失曲线 axes[0].plot(range(1, NUM_EPOCHS 1), train_losses, b-o, linewidth2, markersize6) axes[0].set_xlabel(Epoch, fontsize12) axes[0].set_ylabel(Loss, fontsize12) axes[0].set_title(Training Loss Curve, fontsize14) axes[0].grid(True, alpha0.3) axes[0].set_xticks(range(1, NUM_EPOCHS 1)) # 准确率曲线 axes[1].plot(range(1, NUM_EPOCHS 1), train_accuracies, g-o, linewidth2, markersize6, labelTrain Accuracy) axes[1].plot(range(1, NUM_EPOCHS 1), test_accuracies, r-s, linewidth2, markersize6, labelTest Accuracy) axes[1].set_xlabel(Epoch, fontsize12) axes[1].set_ylabel(Accuracy (%), fontsize12) axes[1].set_title(Accuracy Curve, fontsize14) axes[1].legend(fontsize11) axes[1].grid(True, alpha0.3) axes[1].set_xticks(range(1, NUM_EPOCHS 1)) plt.tight_layout() plt.savefig(/mnt/agents/output/mnist_training_curves.png, dpi150, bbox_inchestight) plt.show() # 7. 最终测试集评估 model.eval() correct 0 total 0 all_predictions [] all_labels [] with torch.no_grad(): for images, labels in test_loader: images images.view(-1, INPUT_SIZE).to(device) labels labels.to(device) outputs model(images) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() all_predictions.extend(predicted.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) final_acc 100 * correct / total print(f\n最终测试集准确率: {final_acc:.2f}%) # 8. 可视化预测结果 fig, axes plt.subplots(3, 5, figsize(12, 8)) axes axes.flatten() # 获取测试集中的前15个样本 sample_images [] sample_labels [] sample_preds [] count 0 for images, labels in test_loader: for i in range(len(images)): if count 15: break sample_images.append(images[i]) sample_labels.append(labels[i].item()) with torch.no_grad(): img images[i].view(-1, INPUT_SIZE).to(device) output model(img) _, pred torch.max(output, 1) sample_preds.append(pred.item()) count 1 if count 15: break for i in range(15): axes[i].imshow(sample_images[i].squeeze(), cmapgray) color green if sample_preds[i] sample_labels[i] else red axes[i].set_title(fTrue: {sample_labels[i]}\nPred: {sample_preds[i]}, colorcolor, fontsize11, fontweightbold) axes[i].axis(off) plt.suptitle(MNIST Prediction Results (GreenCorrect, RedWrong), fontsize14, fontweightbold) plt.tight_layout() plt.savefig(mnist_training_curves.png, dpi150, bbox_inchestight) plt.show()输出结果样例总结任务结果显示该前馈神经网络在 MNIST 测试集上达到了 97.84% 的准确率训练过程中损失从 0.3223 持续下降至 0.0658训练准确率从 90.14% 逐步提升至 97.91%且训练准确率与测试准确率差距较小约0.07%说明模型拟合良好、未出现明显过拟合成功验证了多层感知机在处理图像分类任务上的有效性。参考https://zhuanlan.zhihu.com/p/1891081572305846495https://blog.csdn.net/colus_SEU/article/details/150601683https://www.cnblogs.com/slgkaifa/p/19118984https://nodejh.com/posts/a-simple-neural-network-with-python-and-keras/