全连接层(Fully Connected Layer)是神经网络架构中最基础也最重要的组成部分之一,在PyTorch中通过torch.nn.Linear类实现。这个看似简单的结构实际上承载着深度学习模型中最核心的特征变换功能。
全连接层的核心数学操作是矩阵乘法加偏置。对于一个输入向量x ∈ ℝⁿ,经过全连接层后会得到输出y ∈ ℝᵐ:
y = f(Wx + b)
其中:
这个计算过程可以分解为三个关键步骤:
注意:当bias=False时,全连接层将不包含偏置项,公式简化为y = f(Wx)。这在某些特定场景下可能有用,但大多数情况下建议保留偏置项。
理解维度变换是掌握全连接层的关键。以一个具体例子说明:
python复制import torch
import torch.nn as nn
# 输入特征维度3,输出特征维度4
fc_layer = nn.Linear(in_features=3, out_features=4)
# 模拟一个batch_size=2的输入
x = torch.randn(2, 3) # shape: [2, 3]
output = fc_layer(x) # shape: [2, 4]
维度变换过程:
nn.Linear的构造函数包含三个关键参数:
python复制torch.nn.Linear(
in_features, # 输入特征维度
out_features, # 输出特征维度
bias=True # 是否使用偏置项
)
实际开发中需要注意:
in_features必须与输入张量的最后一维匹配out_features决定了该层的输出维度bias默认启用,除非有特殊需求否则不应关闭PyTorch默认使用Kaiming初始化(针对ReLU激活函数优化)来初始化权重:
python复制# 查看初始化后的权重
fc = nn.Linear(3, 4)
print(fc.weight) # 形状[4,3],使用Kaiming均匀分布初始化
print(fc.bias) # 形状[4],从均匀分布U(-sqrt(k), sqrt(k))采样
自定义初始化示例:
python复制# 自定义初始化
nn.init.xavier_uniform_(fc.weight)
nn.init.constant_(fc.bias, 0.1)
PyTorch的nn.Linear底层实际上调用的是torch.addmm或torch.matmul:
python复制# 等价的手动实现
output = torch.matmul(input, weight.t()) + bias
性能优化提示:
全连接层不仅可以用于传统神经网络,还可以实现各种矩阵变换:
python复制# 实现一个可学习的投影矩阵
projection = nn.Linear(256, 128) # 将256维空间投影到128维
# 实现一个可学习的嵌入层
embedding = nn.Linear(1000, 256) # 将1000维one-hot变为256维嵌入
全连接层常与其他层组合使用:
python复制# CNN分类头典型结构
self.classifier = nn.Sequential(
nn.Linear(512*7*7, 4096),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(4096, 1000) # 假设是1000类分类
)
通过继承nn.Module实现自定义变体:
python复制class SparseLinear(nn.Module):
def __init__(self, in_features, out_features, sparsity=0.5):
super().__init__()
self.weight = nn.Parameter(torch.Tensor(out_features, in_features))
self.mask = (torch.rand(out_features, in_features) > sparsity).float()
nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
def forward(self, x):
return F.linear(x, self.weight * self.mask)
全连接层的计算复杂度为O(in_features × out_features),当这两个维度很大时(如BERT的FFN层有3072维),会成为计算瓶颈。
优化策略:
torch.cuda.amp)nn.utils.prune进行剪枝维度不匹配错误:
python复制# 错误示例:输入最后一维与in_features不匹配
fc = nn.Linear(256, 10)
x = torch.randn(32, 128) # 期望[*,256],得到[32,128]
output = fc(x) # RuntimeError
解决方案:
python复制# 确保输入形状匹配
assert x.size(-1) == fc.in_features
梯度消失/爆炸:
nn.utils.clip_grad_norm_)全连接层的内存占用主要来自权重矩阵:
nn.LazyLinear延迟初始化nn.utils.parametrizations.spectral_norm控制参数规模nn.parallel.DistributedDataParallel分割大矩阵虽然Transformer等新架构崛起,全连接层仍以新形式存在:
Transformer中的前馈网络本质上是两个全连接层的组合:
python复制# Transformer的FFN实现示例
class FeedForward(nn.Module):
def __init__(self, d_model, d_ff, dropout=0.1):
super().__init__()
self.w1 = nn.Linear(d_model, d_ff)
self.w2 = nn.Linear(d_ff, d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
return self.w2(self.dropout(F.gelu(self.w1(x))))
为减少参数量的替代设计:
低秩分解:将大矩阵分解为两个小矩阵乘积
python复制# 低秩全连接层
class LowRankLinear(nn.Module):
def __init__(self, in_features, out_features, rank=64):
super().__init__()
self.A = nn.Linear(in_features, rank, bias=False)
self.B = nn.Linear(rank, out_features)
def forward(self, x):
return self.B(self.A(x))
深度可分离全连接:类似深度可分离卷积的思想
现代架构中全连接层常与其他操作融合:
python复制# 线性层+LayerNorm常见组合
self.linear = nn.Linear(256, 512)
self.norm = nn.LayerNorm(512)
x = self.norm(self.linear(x))
在实际模型设计中,全连接层的灵活运用仍然是构建高效深度学习模型的基础技能。理解其数学原理和实现细节,能够帮助开发者更好地调试模型、优化性能并创新网络架构。