线性代数视角下的神经网络层
神经网络可以看作是一系列线性变换与非线性激活的交织。本章从线性代数的视角,分析各类神经网络层的数学本质,揭示它们之间的内在联系。
全连接层
数学定义
全连接层(Fully Connected Layer / Dense Layer)是最基础的神经网络层:
其中:
- :输入向量
- :权重矩阵
- :偏置向量
- :逐元素激活函数
几何解释
全连接层实现了仿射变换(Affine Transformation):
- 线性变换: 将输入向量旋转、反射、缩放、投影
- 平移: 将原点移动
几何变换示例:
| 权重矩阵类型 | 几何效果 |
|---|---|
| 正交矩阵 | 纯旋转/反射 |
| 对角矩阵 | 沿坐标轴缩放 |
| 稀疏矩阵 | 选择性丢弃某些维度 |
PyTorch实现
import torch
import torch.nn as nn
# 手动实现
def fc_layer(x, W, b, sigma):
"""
x: (batch, d_in)
W: (d_in, d_out)
b: (d_out,)
"""
return sigma(x @ W + b)
# nn.Module实现
class FullyConnected(nn.Module):
def __init__(self, d_in, d_out):
super().__init__()
self.weight = nn.Parameter(torch.randn(d_in, d_out) * 0.01)
self.bias = nn.Parameter(torch.zeros(d_out))
def forward(self, x):
return torch.relu(x @ self.weight + self.bias)卷积层
卷积的矩阵形式
卷积操作可以通过Toeplitz矩阵转化为矩阵乘法。
一维卷积
对于输入 和卷积核 :
Toeplitz矩阵 :
则卷积等价于:
二维卷积(im2col)
对于图像 和卷积核 :
im2col将每个滑动窗口展开为一行:
卷积核展成列向量:
则输出为:
卷积的几何意义
| 视角 | 解释 |
|---|---|
| 线性代数 | 输入与卷积核的稀疏矩阵乘法 |
| 信号处理 | 相关性/卷积运算 |
| 计算机视觉 | 局部特征提取器 |
| 群论 | 等变变换(Equivariant Transformation) |
PyTorch卷积实现
import torch
import torch.nn as nn
# 标准2D卷积
conv2d = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, padding=1)
# im2col实现示意
def im2col(x, kernel_size, stride, padding):
"""
将2D图像转换为矩阵列
x: (B, C, H, W)
"""
x = torch.nn.functional.pad(x, (padding,)*4)
B, C, H, W = x.shape
# 计算输出尺寸
out_H = (H - kernel_size) // stride + 1
out_W = (W - kernel_size) // stride + 1
# 使用unfold进行im2col
cols = x.unfold(2, kernel_size, stride).unfold(3, kernel_size, stride)
cols = cols.contiguous().view(B, C * kernel_size * kernel_size, -1)
return cols, (out_H, out_W)
# 手写卷积(用于理解)
def conv2d_manual(x, weight, bias, stride=1, padding=0):
B, C, H, W = x.shape
K, _, kH, kW = weight.shape
x_padded = torch.nn.functional.pad(x, (padding,)*4)
out_H = (H + 2*padding - kH) // stride + 1
out_W = (W + 2*padding - kW) // stride + 1
out = torch.zeros(B, K, out_H, out_W)
for i in range(out_H):
for j in range(out_W):
h_start = i * stride
w_start = j * stride
window = x_padded[:, :, h_start:h_start+kH, w_start:w_start+kW]
out[:, :, i, j] = (window.unsqueeze(1) * weight).sum(dim=(2,3,4)) + bias
return out注意力层
Self-Attention的矩阵形式
Self-Attention是Transformer的核心组件:
设 为输入序列,则:
矩阵形式分解:
- 投影: 是 的三个不同线性投影
- 相似度矩阵:
- 注意力权重:
- 加权聚合:
注意力的线性代数视角
| 视角 | 解释 |
|---|---|
| 矩阵分解 | |
| 图神经网络 | 每个token是从所有其他token聚合信息 |
| 核方法 | softmax是径向基函数核的归一化形式 |
| 信息检索 | Query-Key-Value对应检索系统 |
多头注意力的矩阵拼接
其中每个头的输出:
PyTorch实现
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class SelfAttention(nn.Module):
def __init__(self, d_model, num_heads):
super().__init__()
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def forward(self, x, mask=None):
B, N, C = x.shape
# 线性投影并分头
Q = self.W_q(x).view(B, N, self.num_heads, self.d_k).transpose(1, 2)
K = self.W_k(x).view(B, N, self.num_heads, self.d_k).transpose(1, 2)
V = self.W_v(x).view(B, N, self.num_heads, self.d_k).transpose(1, 2)
# 缩放点积注意力
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attn_weights = F.softmax(scores, dim=-1)
context = torch.matmul(attn_weights, V)
# 合并多头
context = context.transpose(1, 2).contiguous().view(B, N, C)
return self.W_o(context)Embedding层
数学定义
Embedding层本质上是一个查表操作:
给定词索引 ,输出对应词向量:
矩阵视角
Embedding可以看作是一个稀疏矩阵乘法:
其中 是one-hot编码矩阵。
实际实现优化:直接用索引查表,避免稀疏矩阵运算。
几何解释
- 语义相似的词在向量空间中距离更近
- 词向量之间的夹角反映语义关系
- 线性关系(如 )体现语言规律
PyTorch实现
import torch
import torch.nn as nn
class TokenEmbedding(nn.Module):
def __init__(self, vocab_size, d_model):
super().__init__()
self.embedding = nn.Embedding(vocab_size, d_model)
self.scale = math.sqrt(d_model)
def forward(self, x):
# x: (batch_size, seq_len) 整数索引
return self.embedding(x) * self.scale
# 位置编码也可以看作是一种embedding
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1).float()
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
self.register_buffer('pe', pe.unsqueeze(0))
def forward(self, x):
# x: (batch_size, seq_len, d_model)
return x + self.pe[:, :x.size(1)]归一化层
BatchNorm的矩阵形式
对于形状 的张量:
LayerNorm的矩阵形式
对于形状 的张量(Transformer采用):
矩阵视角:LayerNorm对每个样本的嵌入维度进行归一化,相当于在特征空间做单位球面投影。
RMSNorm
去掉均值中心化,保留RMS归一化,计算更高效。
Dropout与矩阵稀疏化
Dropout的矩阵形式
训练时以概率 丢弃神经元:
其中 是掩码矩阵。
Dropout的期望
因此推理时使用 的缩放,保持期望一致。
Dropout的几何解释
- 强制网络不依赖单个神经元
- 在高维空间中进行稀疏采样
- 近似贝叶斯推断中的模型集成
层之间的联系
全连接 vs 卷积
| 特性 | 全连接层 | 卷积层 |
|---|---|---|
| 权重共享 | 无 | 局部权重共享 |
| 稀疏性 | 密集连接 | 稀疏连接 |
| 等变性 | 不具备 | 具有平移等变性 |
| 参数量 |
卷积 vs 注意力
| 特性 | 卷积层 | 注意力层 |
|---|---|---|
| 感受野 | 局部(kernel大小) | 全局(整个序列) |
| 连接模式 | 固定(局部邻域) | 自适应(数据驱动) |
| 计算复杂度 |
现代架构的统一视图
MetaFormer(Yu et al., 2022)提出:Transformer = Token Mixer + Channel Mixer
| 组件 | Token Mixer | Channel Mixer |
|---|---|---|
| Transformer | Self-Attention | FFN |
| CNN | Convolution | 1x1 Conv |
| MLP-Mixer | 线性层(跨通道) | 线性层(跨Token) |