在人工智能的浪潮中,深度学习已成为推动技术变革的核心动力。无论是最新的国产D20 AI加速卡宣称支持千亿参数大模型部署,还是我们日常使用的图像识别、语音助手,其底层都离不开一个基础而强大的计算模型——多层感知机(MLP)。理解MLP,关键在于掌握其两个核心的生命周期:前向传播(Forward Propagation)反向传播(Backpropagation)。本文将从数学原理入手,深入剖析这两个过程,并手把手教你使用PyTorch和TensorFlow构建你的第一个MLP模型,为你的深度学习之旅打下坚实基础。

1. 神经网络的计算核心:前向传播与反向传播概览

神经网络模仿人脑神经元的工作方式,通过层层连接对数据进行处理和分析。前向传播反向传播是神经网络训练过程中两个相互依存、循环往复的核心步骤。

简单来说,前向传播是“推理”的过程。输入数据从网络第一层(输入层)进入,经过每一层的加权求和与激活函数变换,逐步向前计算,最终得到输出层的预测结果。我们可以将其视为一个复杂的复合函数,输入数据x,通过函数f(即整个网络),得到输出f(x)

然而,网络的初始预测通常是错误的。反向传播则是“学习”的过程。它根据预测结果与真实标签之间的误差(损失),利用链式法则,从输出层开始反向逐层计算每个参数(权重和偏置)对总误差的“贡献度”(即梯度)。随后,优化算法(如梯度下降)利用这些梯度来更新参数,使得网络的预测下次能更接近真实值。

这两个过程紧密配合:前向传播为反向传播提供计算所需的中间变量;反向传播利用这些变量计算梯度来优化网络;参数更新后,下一次前向传播会产生更准确的输出。如此反复迭代,网络性能得以持续提升。

2. 深入原理:前向传播的数学推导

让我们以一个单隐藏层的MLP为例,详细拆解前向传播的数学计算。假设输入样本是一个向量 𝐱 ∈ ℝ^𝑑,隐藏层有 个神经元,输出层有 𝑞 个神经元。

第一步:从输入层到隐藏层

  1. 线性变换:隐藏层的输入是输入向量的加权和。设隐藏层权重矩阵为 𝐖^(1) ∈ ℝ^(ℎ×𝑑),则中间变量 𝐳 = 𝐖^(1) 𝐱。如果考虑偏置项 𝐛^(1),则 𝐳 = 𝐖^(1) 𝐱 + 𝐛^(1)
  2. 非线性激活:将线性变换的结果通过一个非线性激活函数 ϕ,得到隐藏层的激活值 𝐡 = ϕ(𝐳)。激活函数(如ReLU、Sigmoid)的引入至关重要,它使神经网络能够拟合复杂非线性关系,而不仅仅是线性组合。

第二步:从隐藏层到输出层

  1. 线性变换:将隐藏层的输出 𝐡 作为输出层的输入。设输出层权重矩阵为 𝐖^(2) ∈ ℝ^(𝑞×ℎ),偏置为 𝐛^(2),则输出层的未激活值 𝐨 = 𝐖^(2) 𝐡 + 𝐛^(2)
  2. 输出激活(可选):根据任务类型,可能对 𝐨 再施加一个激活函数。例如,多分类任务常用Softmax函数将输出转化为概率分布。

第三步:计算损失
前向传播的终点是计算损失函数 𝐿。损失函数衡量了网络预测值 𝐨 与真实标签 𝑦 之间的差距,例如均方误差或交叉熵损失。我们训练网络的终极目标就是最小化这个损失。

为了更直观地理解数据流动和计算依赖关系,我们可以绘制计算图。在计算图中,节点代表变量(输入、权重、中间结果)或操作(矩阵乘法、激活函数),边表示数据流向。前向传播就是沿着计算图从输入到输出的方向,顺序计算所有节点的值。

3. 理解学习:反向传播与链式法则

反向传播是训练神经网络的引擎,其核心是链式法则。我们的目标是求出损失函数 𝐿 对网络中每一个参数(如 𝐖^(1), 𝐖^(2))的梯度 ∂𝐿/∂𝐖,然后沿着梯度下降的方向更新参数。

由于网络是分层复合的,损失 𝐿 对底层参数 𝐖^(1) 的梯度无法直接计算。链式法则允许我们将这个梯度分解为一系列中间梯度相乘的形式。反向传播的过程,就是从损失 𝐿 开始,逆向遍历前向传播的计算图,逐层计算并传递梯度。

我们继续以上述单隐藏层MLP为例,忽略偏置和正则化项,简要展示梯度计算过程:

  1. 输出层梯度:首先计算损失 𝐿 对输出 𝐨 的梯度 ∂𝐿/∂𝐨,这取决于损失函数的形式。
  2. 隐藏层输出梯度:接着,计算 𝐿 对隐藏层输出 𝐡 的梯度:∂𝐿/∂𝐡 = (𝐖^(2))^⊤ (∂𝐿/∂𝐨)。这里 (𝐖^(2))^⊤ 是权重矩阵的转置,误差从输出层“反向”传播到了隐藏层。
  3. 隐藏层输入梯度:然后,计算 𝐿 对隐藏层输入 𝐳 的梯度:∂𝐿/∂𝐳 = (∂𝐿/∂𝐡) ⊙ ϕ‘(𝐳)。其中 表示按元素相乘,ϕ‘ 是激活函数的导数。这体现了非线性激活在梯度传播中的作用。
  4. 参数梯度:最后,我们可以得到损失对权重的梯度:
    • ∂𝐿/∂𝐖^(2) = (∂𝐿/∂𝐨) 𝐡^⊤
    • ∂𝐿/∂𝐖^(1) = (∂𝐿/∂𝐳) 𝐱^⊤

可以看到,在计算 ∂𝐿/∂𝐖^(1) 时,我们用到了前向传播中存储的中间变量 𝐱𝐳。这正是反向传播需要额外内存的原因:它必须保留前向传播的中间结果以供反向计算使用,其内存占用量与网络层数和批量大小成正比。

4. 实践出真知:用PyTorch构建MLP

理解了理论之后,我们使用PyTorch框架快速构建一个MLP模型。这里我们以经典的Fashion-MNIST图像分类数据集为例。

第一步:导入库并定义网络架构
PyTorch的nn.Module提供了构建网络所需的所有模块。

import torch
from torch import nn
from torch.nn import functional as F

class MLP(nn.Module):
    def __init__(self, input_size=784, hidden_size=256, num_classes=10):
        super(MLP, self).__init__()
        # 展平层:将28x28的图像展平为784维向量
        self.flatten = nn.Flatten()
        # 隐藏层:全连接层 + ReLU激活函数
        self.hidden = nn.Linear(input_size, hidden_size)
        # 输出层:全连接层(未激活),输出10个类别的分数
        self.output = nn.Linear(hidden_size, num_classes)

    def forward(self, x):
        # 前向传播定义
        x = self.flatten(x)
        x = self.hidden(x)
        x = F.relu(x)  # 应用ReLU激活函数
        x = self.output(x)
        return x

第二步:初始化模型、损失函数和优化器

# 超参数
batch_size = 256
learning_rate = 0.1
num_epochs = 10

# 实例化模型
net = MLP()
# 定义交叉熵损失函数(内部包含Softmax)
criterion = nn.CrossEntropyLoss()
# 定义随机梯度下降优化器,并将网络参数传递给它
optimizer = torch.optim.SGD(net.parameters(), lr=learning_rate)

第三步:训练循环(整合前向与反向传播)
训练循环清晰地展示了前向传播、反向传播和参数更新的交替进行。

for epoch in range(num_epochs):
    for data, labels in train_loader: # 假设train_loader已定义
        # 前向传播
        predictions = net(data)
        loss = criterion(predictions, labels)

        # 反向传播
        optimizer.zero_grad() # 清空上一轮的梯度
        loss.backward()       # 自动计算梯度(反向传播)

        # 参数更新
        optimizer.step()      # 根据梯度更新网络参数

    print(f‘Epoch {epoch+1}, Loss: {loss.item():.4f})

loss.backward()这一步,PyTorch的自动微分(Autograd)系统自动为我们完成了链式法则的所有计算,这正是深度学习框架的强大之处。

5. 双框架对比:用TensorFlow/Keras构建MLP

为了展示不同框架的灵活性,我们同样使用TensorFlow的高级API Keras来实现功能相同的MLP。

第一步:使用Sequential API定义模型
Keras的Sequential API允许我们以层堆叠的方式直观定义模型。

import tensorflow as tf
from tensorflow.keras import layers, models

# 使用Sequential API定义模型
model = models.Sequential([
    layers.Flatten(input_shape=(28, 28)),   # 输入展平层
    layers.Dense(256, activation=‘relu‘),   # 全连接隐藏层,使用ReLU激活
    layers.Dense(10)                        # 输出层,10个神经元
])

# 打印模型概要
model.summary()

第二步:编译模型
在Keras中,编译步骤需要指定优化器、损失函数和评估指标。

model.compile(
    optimizer=tf.keras.optimizers.SGD(learning_rate=0.1), # 随机梯度下降优化器
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), # 损失函数
    metrics=[‘accuracy‘] # 评估指标为准确率
)

第三步:训练模型
Keras的fit方法将训练循环高度抽象化,只需一行代码。

# 假设 (train_images, train_labels), (test_images, test_labels) 已加载
history = model.fit(
    train_images, train_labels,
    epochs=10,
    batch_size=256,
    validation_data=(test_images, test_labels) # 每轮训练后在验证集上评估
)

TensorFlow/Keras的设计哲学是提供更高层次的抽象,让开发者能更专注于模型结构而非训练细节。model.fit()内部同样完整执行了前向传播、损失计算、反向传播和参数更新的循环。

6. 总结与进阶思考

通过本文,我们系统地探讨了深度学习的基石:前向传播与反向传播的数学原理,并亲手使用PyTorch和TensorFlow两个主流框架构建了MLP模型。理解前向传播如何将输入映射为输出,以及反向传播如何利用梯度下降和链式法则来优化网络参数,是掌握深度学习的关键。

尽管我们构建的MLP结构简单,但它包含了现代深度神经网络的所有核心要素。MLP作为基础构建块,其思想和原理贯穿于更复杂的卷积神经网络(CNN)、循环神经网络(RNN)和Transformer架构中。

进一步探索的方向:

  • 深度与宽度:尝试增加隐藏层的数量(深度)或每层的神经元数量(宽度),观察模型性能的变化。在一定范围内,更深的网络具有更强的表达能力。
  • 激活函数:将ReLU替换为Sigmoid、Tanh或LeakyReLU等,比较它们对训练速度和效果的影响。
  • 优化器:使用Adam、RMSprop等更先进的优化器替代基础的SGD,它们通常能带来更快的收敛速度。
  • 正则化:为了防止过拟合,可以在模型中添加Dropout层、L2权重正则化(如我们在数学推导中提到的 𝑠 项)或批归一化(Batch Normalization)层。
  • 硬件考量:随着模型加深加大,对显存和算力的需求急剧增加。这催生了如国产D20 AI加速卡这类高性能、低功耗的专用AI芯片,它们通过优化底层计算和存储访问,为训练和部署大规模模型提供了硬件基础。

深度学习的世界广阔而深邃。希望本文为你打开了这扇大门,助你在AI的探索之路上稳步前行。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐