从零开始深度学习:全连接层、损失函数与梯度下降的详尽指南
引言
在深度学习的领域,全连接层、损失函数与梯度下降是三块重要的基石。如果你正在踏上深度学习的旅程,理解它们是迈向成功的第一步。这篇文章将从概念到代码、从基础到进阶,详细剖析这三个主题,帮助你从小白成长为能够解决实际问题的开发者。
第一部分:全连接层——神经网络的基础单元
1.1 什么是全连接层?
全连接层(Fully Connected Layer,简称FC层)是神经网络中最基本的组件之一。它的核心任务是将输入特征映射到输出空间,并在这个过程中学习特征之间的复杂关系。
公式定义:
全连接层的数学表达式如下:
y = f ( W x + b ) y = f(Wx + b) y=f(Wx+b)
(x):输入向量,表示当前层的输入特征。
(W):权重矩阵,表示每个输入特征对输出特征的影响权重。
(b):偏置向量,为网络提供更大的表达能力。
(f):激活函数,为模型引入非线性。
全连接层的核心是通过权重矩阵和偏置向量的线性变换学习输入和输出之间的映射关系。最终,通过激活函数完成非线性变换,使得网络能够处理复杂的任务。
1.2 为什么需要全连接层?
全连接层的主要作用是:
特征融合:将不同的特征组合起来,捕捉全局信息。
非线性表达:通过激活函数,使网络能够学习复杂的非线性映射关系。
分类和回归任务:在任务的最后几层,全连接层常用于将特征映射为目标类别或回归值。
在图像分类任务中,全连接层负责将卷积层提取的特征映射到最终的分类结果。例如:
输入:卷积层输出的特征(如512维向量)。
输出:分类结果(如10类)。
1.3 全连接层的实现与代码示例
以下是一个简单的全连接网络,用于对MNIST手写数字进行分类:
import torch
import torch.nn as nn
定义全连接神经网络
class FullyConnectedNet(nn.Module):
def __init__(self):
super(FullyConnectedNet, self).__init__()
self.fc1 = nn.Linear(28 * 28, 128) # 输入层到隐藏层
self.fc2 = nn.Linear(128, 64) # 隐藏层到另一个隐藏层
self.fc3 = nn.Linear(64, 10) # 隐藏层到输出层
def forward(self, x):
x = x.view(x.size(0), -1) # 将二维输入展平
x = torch.relu(self.fc1(x)) # 激活函数ReLU
x = torch.relu(self.fc2(x))
x = self.fc3(x) # 输出分类
return x
测试网络
model = FullyConnectedNet()
sample_input = torch.randn(1, 28, 28) # 模拟一个MNIST样本
output = model(sample_input)
print(output)
代码解读:
nn.Linear 创建全连接层,定义输入和输出的维度。
torch.relu 使用 ReLU 激活函数引入非线性。
x.view 展平输入张量,为全连接层提供一维向量形式的数据。
1.4 全连接层的局限性
尽管全连接层功能强大,但也有一定局限性:
参数量大:全连接层需要存储和计算大量的权重和偏置,容易导致过拟合。
空间感缺失:无法有效利用输入数据的空间信息(如图像的像素结构),这也是卷积层的用武之地。
计算复杂度高:大规模网络可能导致训练和推理的计算开销过大。
第二部分:损失函数——模型的学习目标
2.1 什么是损失函数?
损失函数是衡量模型预测值与真实值之间差距的数学函数。深度学习的目标是通过优化算法(如梯度下降),不断调整模型参数,以最小化损失函数的值。
损失函数的两种主要类型:
回归问题:预测连续值,常用的损失函数包括均方误差(MSE)和平均绝对误差(MAE)。
分类问题:预测离散值,常用的损失函数是交叉熵损失。
2.2 常见损失函数
- 均方误差(MSE)
M S E = 1 n ∑ i = 1 n ( y ^ i − y i ) 2 MSE = \frac{1}{n} \sum_{i=1}^n (\hat{y}_i - y_i)^2 MSE=n1i=1∑n(y^i−yi)2
适用于回归问题,计算预测值与真实值的平方差。
- 交叉熵损失(Cross Entropy Loss)
用于分类问题,衡量预测分布与真实分布之间的差异:
L = − ∑ i = 1 n y i log ( y ^ i ) L = -\sum_{i=1}^n y_i \log(\hat{y}_i) L=−i=1∑nyilog(y^i)
- 二元交叉熵损失(Binary Cross Entropy)
适用于二分类问题,公式为:
B C E = − 1 n ∑ i = 1 n [ y i log ( y ^ i ) + ( 1 − y i ) log ( 1 − y ^ i ) ] BCE = -\frac{1}{n} \sum_{i=1}^n \left[y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i)\right] BCE=−n1i=1∑n[yilog(y^i)+(1−yi)log(1−y^i)]
2.3 损失函数的代码实现
以下代码展示了如何使用 PyTorch 计算交叉熵损失:
import torch
import torch.nn as nn
模拟模型输出和真实标签
output = torch.tensor([[0.1, 0.8, 0.1], [0.7, 0.2, 0.1]]) # 模型预测
target = torch.tensor([1, 0]) # 真实标签
定义交叉熵损失
criterion = nn.CrossEntropyLoss()
loss = criterion(output, target)
print(f"Loss: {loss.item()}")
说明:
模型的输出是未经过 softmax 的原始分数(logits),nn.CrossEntropyLoss 会自动应用 softmax。
2.4 如何选择合适的损失函数?
回归问题:MSE 是默认选择,但 MAE 在对异常值敏感的场景中表现更好。
分类问题:交叉熵是首选,尤其是多分类任务。
概率分布建模:使用 Kullback-Leibler 散度(KL 散度)来衡量分布之间的差异。
第三部分:梯度下降——优化的利器
3.1 梯度下降的原理
梯度下降是一种迭代优化算法,通过最小化损失函数来寻找最优参数。它的核心思想是:沿着损失函数的负梯度方向调整参数,直到损失值最小。
参数更新公式:
θ
=
θ
−
α
∇
θ
J
(
θ
)
\theta = \theta - \alpha \nabla_\theta J(\theta)
θ=θ−α∇θJ(θ)
(\theta):模型参数。
(\alpha):学习率,控制步长大小。
(\nabla_\theta J(\theta)):损失函数对参数的梯度。
3.2 梯度下降的三种变体
批量梯度下降(Batch Gradient Descent):
对整个数据集计算梯度。
优点:稳定。
缺点:计算开销大,尤其在大数据集上。
随机梯度下降(SGD, Stochastic Gradient Descent):
每次使用一个样本计算梯度。
优点:更新速度快。
缺点:收敛不稳定。
小批量梯度下降(Mini-batch Gradient Descent):
每次使用一小部分样本计算梯度。
优点:折中方案,常用于实际深度学习任务。
3.3 梯度下降的代码实现
以下是一个结合 PyTorch 优化器的完整训练过程:
import torch.optim as optim
定义模型、损失函数和优化器
model = FullyConnectedNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
模拟训练过程
for epoch in range(5):
optimizer.zero_grad() # 清除上一轮的梯度
output = model(sample_input) # 前向传播
target = torch.tensor([3]) # 假设真实标签
loss = criterion(output, target) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新参数
print(f"Epoch {epoch+1}, Loss: {loss.item()}")
优化策略与进阶技巧
动态学习率
在训练过程中,动态调整学习率有助于模型更快地收敛。例如:
from torch.optim.lr_scheduler import StepLR
scheduler = StepLR(optimizer, step_size=2, gamma=0.1)
for epoch in range(5):
train() # 假设有训练逻辑
scheduler.step()
动量优化
动量方法通过加速梯度下降并减少波动,提高收敛速度:
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
Adam优化器
Adam 是一种自适应学习率的优化算法,结合了动量和 RMSProp 的优点,适合大多数任务:
optimizer = optim.Adam(model.parameters(), lr=0.001)
总结
全连接层、损失函数与梯度下降是深度学习的基石。通过本文的详细解析,你不仅理解了它们的理论,还掌握了代码实现和优化技巧。在深度学习的道路上,这三块知识将帮助你构建强大的模型,解决实际问题。