目录
文章目录
一、前馈神经网络的概念
二、前馈神经网络的结构
三、前馈神经网络的数学原理
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层的误差项可以通过第l+1层的误差项计算得到。反向传播算法的含义是:第l层的一个神经元的误差项等于该神经元激活函数的梯度,再乘上所有与该神经元相连接的第l+1层的神经元的误差项的权重和。
再回到开头求两个参数的公式:
里面的三个偏导数都已经求出来了。于是可以求出:
进一步,损失函数关于第l层权重W(l)梯度为:
而损失函数关于第l层偏置b(l)的梯度为:
于是在利用误差反向传播算法计算出每一层的误差项后,就可以得到每一层参数的梯度。
基于误差反向传播算法(backpropagation,BP )的前馈神经网络训练过程可以分为以下三步:
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', train=True, download=True, transform=transform ) test_dataset = datasets.MNIST( root='./data', train=False, download=True, transform=transform ) train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False) 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(), lr=LEARNING_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(f'Epoch [{epoch+1}/{NUM_EPOCHS}], ' f'Batch [{batch_idx+1}/{len(train_loader)}], ' f'Loss: {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(f'Epoch [{epoch+1}/{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', linewidth=2, markersize=6) axes[0].set_xlabel('Epoch', fontsize=12) axes[0].set_ylabel('Loss', fontsize=12) axes[0].set_title('Training Loss Curve', fontsize=14) axes[0].grid(True, alpha=0.3) axes[0].set_xticks(range(1, NUM_EPOCHS + 1)) # 准确率曲线 axes[1].plot(range(1, NUM_EPOCHS + 1), train_accuracies, 'g-o', linewidth=2, markersize=6, label='Train Accuracy') axes[1].plot(range(1, NUM_EPOCHS + 1), test_accuracies, 'r-s', linewidth=2, markersize=6, label='Test Accuracy') axes[1].set_xlabel('Epoch', fontsize=12) axes[1].set_ylabel('Accuracy (%)', fontsize=12) axes[1].set_title('Accuracy Curve', fontsize=14) axes[1].legend(fontsize=11) axes[1].grid(True, alpha=0.3) axes[1].set_xticks(range(1, NUM_EPOCHS + 1)) plt.tight_layout() plt.savefig('/mnt/agents/output/mnist_training_curves.png', dpi=150, bbox_inches='tight') 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(), cmap='gray') color = 'green' if sample_preds[i] == sample_labels[i] else 'red' axes[i].set_title(f'True: {sample_labels[i]}\nPred: {sample_preds[i]}', color=color, fontsize=11, fontweight='bold') axes[i].axis('off') plt.suptitle('MNIST Prediction Results (Green=Correct, Red=Wrong)', fontsize=14, fontweight='bold') plt.tight_layout() plt.savefig('mnist_training_curves.png', dpi=150, bbox_inches='tight') plt.show()输出结果:
样例总结
任务结果显示,该前馈神经网络在 MNIST 测试集上达到了 97.84% 的准确率,训练过程中损失从 0.3223 持续下降至 0.0658,训练准确率从 90.14% 逐步提升至 97.91%,且训练准确率与测试准确率差距较小(约0.07%),说明模型拟合良好、未出现明显过拟合,成功验证了多层感知机在处理图像分类任务上的有效性。
参考
https://zhuanlan.zhihu.com/p/1891081572305846495
https://blog.csdn.net/colus_SEU/article/details/150601683
https://www.cnblogs.com/slgkaifa/p/19118984
https://nodejh.com/posts/a-simple-neural-network-with-python-and-keras/