矩阵分解与表示学习
矩阵分解是现代机器学习和深度学习的核心工具。从主成分分析(PCA)到词嵌入(Word2Vec),矩阵分解的思想贯穿始终。本章系统介绍矩阵分解的数学原理及其在深度学习中的应用。
奇异值分解(SVD)
定义
任意实矩阵 可以分解为:
其中:
- :左奇异向量矩阵()
- :右奇异向量矩阵()
- :奇异值对角矩阵,
几何意义
SVD将线性变换分解为三个基本操作:
- 旋转 :在输入空间中旋转到主轴方向
- 缩放 :沿主轴缩放(奇异值即为缩放因子)
- 旋转 :在输出空间中旋转
与特征值分解的关系
对于对称矩阵 :
其中特征值可以是负数。SVD可看作对任意矩阵的”特征值分解”:
奇异值 是 特征值的平方根。
最佳低秩近似
Eckart-Young-Mirsky定理
设 ,秩为 ,其SVD为 。
则秩为 的最佳近似矩阵为:
其中 取前 个奇异分量。
最优性: 是Frobenius范数意义下的最优近似:
截断SVD的应用
import numpy as np
def truncated_svd(A, k):
"""
截断SVD:返回秩为k的最佳近似
A: (m, n)
k: 目标秩
"""
U, s, Vt = np.linalg.svd(A, full_matrices=False)
return U[:, :k] @ np.diag(s[:k]) @ Vt[:k, :]主成分分析(PCA)
PCA的矩阵推导
设数据矩阵 ,每行一个样本。
数据中心化:
协方差矩阵:
PCA目标:找到正交矩阵 ,使得:
最大化 。
闭式解
协方差矩阵的特征值分解:
主成分为对应最大 个特征值的特征向量(列)。
PCA与SVD的关系
设数据中心化矩阵为 ,其SVD为 。
则:
- 主成分方向:(右奇异向量)
- 投影坐标:(左奇异向量乘以奇异值)
- 解释的方差:
import numpy as np
from sklearn.decomposition import PCA
def pca_manual(X, n_components):
"""手动实现PCA"""
# 中心化
X_centered = X - X.mean(axis=0)
# SVD分解
U, s, Vt = np.linalg.svd(X_centered, full_matrices=False)
# 取前n_components个主成分
components = Vt[:n_components]
explained_variance = s[:n_components]**2 / (len(s) - 1)
# 投影
X_transformed = X_centered @ components.T
return X_transformed, components, explained_variancePCA在深度学习中的应用
| 应用 | 说明 |
|---|---|
| 数据降维 | 可视化、压缩存储 |
| 特征提取 | 预处理步骤,减少维度 |
| 初始化 | 预训练权重的PCA初始化 |
| 正则化 | 去相关、降低过拟合 |
矩阵分解与嵌入学习
词共现矩阵分解
设词共现矩阵 ,其中 表示词 和词 的共现次数。
目标:找到词嵌入 ,使得:
Word2Vec与矩阵分解的等价性
GloVe (Pennington et al., 2014) 证明了Word2Vec Skip-gram与矩阵分解的等价性:
这解释了为什么基于共现矩阵的GloVe和基于上下文预测的Skip-gram能学到相似的词向量。
推荐系统中的矩阵分解
设用户-物品交互矩阵 :
其中 为用户嵌入, 为物品嵌入。
损失函数:
其中 为观测到的交互集合。
import torch
import torch.nn as nn
class MatrixFactorization(nn.Module):
"""矩阵分解推荐模型"""
def __init__(self, num_users, num_items, embedding_dim):
super().__init__()
self.user_embedding = nn.Embedding(num_users, embedding_dim)
self.item_embedding = nn.Embedding(num_items, embedding_dim)
# 偏置项
self.user_bias = nn.Embedding(num_users, 1)
self.item_bias = nn.Embedding(num_items, 1)
self.global_bias = nn.Parameter(torch.zeros(1))
def forward(self, user_ids, item_ids):
user_emb = self.user_embedding(user_ids)
item_emb = self.item_embedding(item_ids)
# 点积 + 偏置
interaction = (user_emb * item_emb).sum(dim=-1)
user_b = self.user_bias(user_ids).squeeze()
item_b = self.item_bias(item_ids).squeeze()
return interaction + user_b + item_b + self.global_bias神经网络的矩阵视角
权重矩阵的秩
神经网络的表达能力与其权重矩阵的秩密切相关:
- 低秩瓶颈:某些层可能存在低秩结构
- 权重空间维度:有效参数维度可能小于名义参数维度
- 信息瓶颈:神经网络可能在某层丢失信息
低秩适应(LoRA)
低秩适应是一种高效的微调方法。设预训练权重 ,微调时冻结 ,只更新低秩分解:
其中 为秩。
class LoRALinear(nn.Module):
"""LoRA线性层"""
def __init__(self, in_features, out_features, rank=4, alpha=1.0):
super().__init__()
self.rank = rank
self.alpha = alpha
self.scaling = alpha / rank
# 冻结原始权重
self.weight = nn.Parameter(torch.zeros(out_features, in_features))
self.weight.requires_grad = False
# 可训练的低秩分解
self.lora_A = nn.Parameter(torch.randn(in_features, rank) * 0.01)
self.lora_B = nn.Parameter(torch.zeros(rank, out_features))
def forward(self, x):
# W_0 + A * B
return x @ (self.weight + self.lora_A @ self.lora_B * self.scaling)SVD在模型压缩中的应用
权重分解
设卷积核 ,可分解为:
近似为两个小卷积的组合:
- 第一个: 卷积()
- 第二个: 卷积()
剪枝与重构
网络剪枝后,权重矩阵可能接近低秩。SVD可以找到最佳重构:
def svd_prune(model, sparsity=0.5):
"""基于SVD的权重剪枝"""
for name, param in model.named_parameters():
if 'weight' in name and param.dim() >= 2:
# SVD分解
U, s, Vt = torch.linalg.svd(param.data, full_matrices=False)
# 保留前k个奇异值
k = int(len(s) * (1 - sparsity))
param.data = U[:, :k] @ torch.diag(s[:k]) @ Vt[:k, :]自编码器与表示学习
自编码器架构
自编码器通过编码器 和解码器 学习数据的紧凑表示。
PCA自编码器
当编码器是线性层、解码器也是线性层,且损失为MSE时,自编码器学到的表示正好是PCA:
最优 的列空间与PCA主成分一致。
变分自编码器(VAE)
VAE引入概率建模:
损失函数:
class VAE(nn.Module):
"""变分自编码器"""
def __init__(self, input_dim, latent_dim):
super().__init__()
# 编码器
self.encoder = nn.Sequential(
nn.Linear(input_dim, 256),
nn.ReLU(),
nn.Linear(256, 128),
nn.ReLU()
)
self.fc_mu = nn.Linear(128, latent_dim)
self.fc_logvar = nn.Linear(128, latent_dim)
# 解码器
self.decoder = nn.Sequential(
nn.Linear(latent_dim, 128),
nn.ReLU(),
nn.Linear(128, 256),
nn.ReLU(),
nn.Linear(256, input_dim)
)
def reparameterize(self, mu, logvar):
std = torch.exp(0.5 * logvar)
eps = torch.randn_like(std)
return mu + eps * std
def forward(self, x):
h = self.encoder(x)
mu = self.fc_mu(h)
logvar = self.fc_logvar(h)
z = self.reparameterize(mu, logvar)
return self.decoder(z), mu, logvar谱范数与神经网络稳定性
谱范数定义
矩阵 的谱范数(2-范数):
谱归一化
谱归一化 (Spectral Normalization) 强制权重矩阵的谱范数为1:
作用:
- 满足Lipschitz约束(GAN判别器)
- 训练更稳定
- 有正则化效果
class SpectralNorm(nn.Module):
"""谱归一化包装器"""
def __init__(self, layer, n_iter=1):
super().__init__()
self.layer = layer
self.n_iter = n_iter
self.u = None
self._init_u()
def _init_u(self):
# 初始化随机向量
w = self.layer.weight.data
self.u = nn.Parameter(w.new_empty(w.size(0)).normal_(0, 1))
def _compute_spectral_norm(self):
w = self.layer.weight.data
for _ in range(self.n_iter):
# 幂迭代
v = torch.nn.functional.normalize(w @ self.u, dim=1)
self.u.data = torch.nn.functional.normalize(w.T @ v, dim=0)
return torch.dot(self.u, w @ v)
def forward(self, x):
w = self.layer.weight.data / self._compute_spectral_norm()
return nn.functional.linear(x, w, self.layer.bias)