LLM学习笔记(六)线性代数
公式速查表
1. 向量与矩阵:表示、转换与知识存储的基础
向量表示 (Vectors): 语义的载体
在LLM中,向量 x ∈ R d \mathbf{x}\in\mathbb{R}^d x∈Rd 是信息的基本单元,承载着丰富的语义信息:
- 词嵌入向量 (Word Embeddings):将离散的token(如单词"北京"或子词"思")映射为一个稠密的高维向量(例如,BERT使用768维,GPT-3使用数千维)。这些向量是学习得到的,目标是使语义上相似的词在向量空间中彼此靠近。例如,"国王"和"女王"的向量会比"国王"和"香蕉"的向量更接近。它们是模型处理语言的原子输入。
- 位置编码向量 (Positional Encodings):由于Transformer的自注意力机制本身不感知序列顺序,位置编码向量为模型提供了token在序列中的位置信息。这些向量通过固定函数(如正弦/余弦函数)生成或通过学习得到,并加到词嵌入向量上,使得模型能够区分"我爱北京"和"北京爱我"。
- 隐藏状态向量 (Hidden State Vectors):在Transformer的每一层处理后,每个token的表示都会更新为一个新的隐藏状态向量。这个向量是该token在当前上下文中的动态、情境化表示,融合了词本身的意义、位置信息以及通过注意力机制从序列中其他token获取的相关信息。
矩阵变换 (Matrix Transformations): 知识的编码与特征提取
权重矩阵 W ∈ R m × n \mathbf{W}\in\mathbb{R}^{m\times n} W∈Rm×n 是模型学习和存储知识的主体。通过线性变换 y = W x + b \mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b} y=Wx+b,模型将输入向量 x \mathbf{x} x (n维) 投影或转换为输出向量 y \mathbf{y} y (m维)。偏置项 b \mathbf{b} b 则允许对变换后的空间进行平移,增加了模型的表达能力。
import torch
import torch.nn.functional as F
import math# 线性变换的简化PyTorch实现
def linear_transform(x_batch, W, b):# x_batch: [batch_size, in_features]# W: [out_features, in_features] (PyTorch nn.Linear stores W this way)# b: [out_features]# torch.matmul(x, W.t()) or F.linear(x, W, b)return F.linear(x_batch, W, b)# 示例:
# x = torch.randn(32, 128) # Batch of 32 vectors, each 128-dim
# W_example = torch.randn(64, 128) # Projects from 128-dim to 64-dim
# b_example = torch.randn(64)
# y = linear_transform(x, W_example, b_example) # y will be [32, 64]
在LLM的核心组件——自注意力机制中,同一个输入序列的隐藏状态向量 x \mathbf{x} x 会通过三个不同的学习到的权重矩阵 W q , W k , W v \mathbf{W_q}, \mathbf{W_k}, \mathbf{W_v} Wq,Wk,Wv 映射,生成查询 (Query, Q)、键 (Key, K) 和值 (Value, V) 向量:
# 假设 x 是一个批次的序列表示: [batch_size, seq_len, d_model]
# W_q, W_k, W_v 是权重矩阵: [d_model, d_k] 或 [d_model, d_v]
# (实际实现中,这些权重通常合并为一个大矩阵进行计算,然后分割/重塑)# x_token: [d_model] (单个token的表示)
# W_q_matrix: [d_model, d_k] (假设d_q = d_k)# Q_token = torch.matmul(x_token, W_q_matrix) # 查询向量 [d_k]
# K_token = torch.matmul(x_token, W_k_matrix) # 键向量 [d_k]
# V_token = torch.matmul(x_token, W_v_matrix) # 值向量 [d_v]# 对于整个序列 (batch_size, seq_len, d_model) -> (batch_size, seq_len, d_k or d_v)
# Q = F.linear(x, W_q_full) # W_q_full: [d_k_total, d_model] if considering multi-head
# K = F.linear(x, W_k_full)
# V = F.linear(x, W_v_full)
深层意义:学习特征的层次化抽象
这些线性变换在高维语义空间中执行着复杂的操作。每个权重矩阵可以被看作是学习到的一个特定"视角"或"投影仪",它将输入信息投影到能揭示某些特定关系或特征的子空间。例如,一个 W \mathbf{W} W 可能学会提取与情感相关的特征,另一个则可能关注语法结构。在深度网络中,这些变换层叠进行,使得模型能够学习从低级特征(如词形)到高级抽象概念(如主题、意图)的层次化表示。模型的学习过程本质上就是在调整这些矩阵的参数,以最小化预测任务(如预测下一个词)的损失。
2. 张量 (Tensors): 多维数据的结构化处理
张量是向量(一阶张量)和矩阵(二阶张量)向更高维度的推广。在LLM中,数据通常以三阶或更高阶张量的形式存在,例如 T ∈ R B × L × d \mathcal{T}\in\mathbb{R}^{B\times L\times d} T∈RB×L×d,其中:
- B (Batch Size): 批量大小,即模型一次并行处理的独立序列数量,利用GPU并行计算能力提高训练效率。
- L (Sequence Length): 序列长度,即每个输入序列包含的token数量。
- d (Hidden Dimension / Embedding Dimension): 隐藏维度或嵌入维度,即表示每个token的向量的特征数量。
实际应用与并行计算
Transformer模型中的操作,如多头注意力、层归一化、前馈网络等,都高度依赖于高效的张量运算。
# 输入张量形状变化示例(简化)
batch_size = 32
seq_len = 512
d_model = 768
num_heads = 12
d_head = d_model // num_heads # 64, 每个头的维度inputs = torch.randn(batch_size, seq_len, d_model)# 在多头注意力中,Q, K, V 会被计算并重塑以分离各个头
# 假设 W_q projects d_model to d_model (num_heads * d_head)
# Q_projected = F.linear(inputs, W_q_weight) # [batch_size, seq_len, d_model]
# Q_reshaped = Q_projected.view(batch_size, seq_len, num_heads, d_head)
# Q_transposed = Q_reshaped.permute(0, 2, 1, 3) # [batch_size, num_heads, seq_len, d_head]
# K 和 V 类似处理
# K_transposed: [batch_size, num_heads, seq_len, d_head]
# V_transposed: [batch_size, num_heads, seq_len, d_head]
维度操作的重要性:效率与模块化
张量的转置 (permute)、重塑 (view/reshape) 和广播 (broadcasting) 是实现高效并行计算,尤其是多头注意力机制的关键。多头注意力机制允许模型同时从输入的不同表示子空间中学习信息,每个"头"可以独立地关注输入序列的不同方面。张量操作使得这些独立的计算可以被优雅地表示和高效地在现代硬件(如GPU/TPU,它们就是为大规模并行浮点运算设计的)上执行。
3. 内积 (Inner Product) 与相似度计算:注意力的核心机制
两个向量 q \mathbf{q} q 和 k \mathbf{k} k(通常是查询向量和键向量)的内积(或点积) ⟨ q , k ⟩ = q ⊤ k = ∑ i q i k i \langle \mathbf{q},\mathbf{k} \rangle = \mathbf{q}^\top \mathbf{k} = \sum_i q_i k_i ⟨q,k⟩=q⊤k=∑iqiki 是注意力机制计算相似度的基础。
几何理解:对齐度的量化
内积可以表示为: q ⊤ k = ∥ q ∥ ⋅ ∥ k ∥ ⋅ cos ( θ ) \mathbf{q}^\top \mathbf{k} = \|\mathbf{q}\| \cdot \|\mathbf{k}\| \cdot \cos(\theta) q⊤k=∥q∥⋅∥k∥⋅cos(θ),其中 θ \theta θ 是 q \mathbf{q} q 和 k \mathbf{k} k 之间的夹角。
- 当两个向量方向一致时( θ = 0 , cos ( θ ) = 1 \theta=0, \cos(\theta)=1 θ=0,cos(θ)=1),内积达到最大正值(给定范数)。这表示两者高度相关或相似。
- 当两个向量正交( θ = π / 2 , cos ( θ ) = 0 \theta=\pi/2, \cos(\theta)=0 θ=π/2,cos(θ)=0),内积为0。表示两者线性无关。
- 当两个向量方向相反时( θ = π , cos ( θ ) = − 1 \theta=\pi, \cos(\theta)=-1 θ=π,cos(θ)=−1),内积达到最大负值。表示两者负相关。
在注意力机制中,通常更关心方向的一致性,即 cos ( θ ) \cos(\theta) cos(θ)。缩放点积注意力 (Scaled Dot-Product Attention) 中除以 d k \sqrt{d_k} dk (键向量维度的平方根) 是为了在梯度计算时稳定数值,防止内积过大导致softmax函数进入饱和区。
在注意力中的应用:信息选择的权重
自注意力机制通过计算一个序列中每个查询向量与所有键向量的相似度(内积)来决定每个词应该“关注”序列中其他词的程度:
# 假设 Q, K, V 已经是 [batch_size, num_heads, seq_len, d_head]
# Q: [..., seq_len_q, d_head]
# K: [..., seq_len_k, d_head]
# V: [..., seq_len_k, d_head] (seq_len_k 和 seq_len_v 是一样的)# d_k = Q.size(-1) # d_head
# attention_scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
# # attention_scores: [batch_size, num_heads, seq_len_q, seq_len_k]# attention_weights = F.softmax(attention_scores, dim=-1)
# # attention_weights: [batch_size, num_heads, seq_len_q, seq_len_k]
# # 表示对于每个查询token,它对所有键token的注意力分布# context_vectors = torch.matmul(attention_weights, V)
# # context_vectors: [batch_size, num_heads, seq_len_q, d_head]
# # 这是通过注意力加权聚合V得到的新表示
例如,当计算句子 “The cat sat on the mat” 中 “sat” 作为查询词时,它与 “cat” 和 “mat” (作为键) 的内积可能会比较高,表明这些词与 “sat” 的动作在语义上或句法上高度相关,因此在计算 “sat” 的新表示时,“cat” 和 “mat” 的值向量会获得更高的注意力权重。这实现了所谓的内容寻址记忆(content-addressable memory)的特性。
4. 正交性 (Orthogonality) 与归一化 (Normalization): 训练稳定性与表达力的保障
正交矩阵的优良特性
一个实方阵 Q \mathbf{Q} Q 如果是正交的,则满足 Q ⊤ Q = Q Q ⊤ = I \mathbf{Q}^\top\mathbf{Q} = \mathbf{Q}\mathbf{Q}^\top = \mathbf{I} Q⊤Q=QQ⊤=I (单位矩阵)。正交变换保持向量的L2范数(长度)不变: ∥ Q x ∥ 2 = ∥ x ∥ 2 \|\mathbf{Q}\mathbf{x}\|_2 = \|\mathbf{x}\|_2 ∥Qx∥2=∥x∥2,并且保持向量间的内积和角度不变。
在LLM中:
- 权重初始化 (Weight Initialization):使用接近正交的矩阵(如通过Xavier或Kaiming初始化的变体,或者直接进行正交初始化)进行权重初始化,有助于在训练初期维持信号的方差,防止梯度在深层网络中消失或爆炸,从而加速收敛。
- 潜在的正则化效应:某些研究表明,鼓励权重矩阵接近正交性可以作为一种正则化手段,提高泛化能力。
层归一化 (Layer Normalization)
层归一化是Transformer模型中至关重要的组件,它对每个样本的每一层的激活值(在一个层内的所有隐藏单元上)进行归一化,使其均值为0,方差为1,然后再通过可学习的缩放因子 γ \gamma γ 和平移因子 β \beta β 进行调整。
# LayerNorm实现示例 (PyTorch自带 nn.LayerNorm)
# def layer_norm(x, gamma, beta, eps=1e-5):
# # x: [batch_size, seq_len, hidden_dim]
# mean = x.mean(dim=-1, keepdim=True) # 在最后一个维度(hidden_dim)上计算均值
# var = x.var(dim=-1, keepdim=True, unbiased=False) # 方差
# normalized_x = (x - mean) / torch.sqrt(var + eps) # 归一化
# return gamma * normalized_x + beta # 应用可学习的仿射变换# # PyTorch usage:
# d_model = 768
# layer_norm_module = torch.nn.LayerNorm(d_model)
# x = torch.randn(32, 512, d_model)
# normalized_x_pytorch = layer_norm_module(x)
为什么需要归一化:稳定信息流
在深层网络中,每一层的计算都可能改变其输出激活值的分布。如果没有归一化,这些分布可能会在层间传递时发生剧烈变化(称为内部协变量偏移,internal covariate shift),导致信号逐渐衰减至0或膨胀至极大值,使得网络难以训练。层归一化通过在每个层级重新调整激活值的尺度,确保了梯度能够稳定地反向传播,并使得模型对权重初始化和学习率的选择不那么敏感,从而显著提高了训练的稳定性和速度。与批量归一化(Batch Normalization)不同,层归一化独立于批量大小,并且对序列数据(其长度可变)特别有效。
5. 矩阵分解 (Matrix Factorization): 理解模型、压缩与效率提升
矩阵分解是将一个矩阵表示为多个(通常更简单或具有特定结构的)矩阵的乘积。
奇异值分解 (SVD)
任何实矩阵 A ∈ R m × n \mathbf{A}\in\mathbb{R}^{m\times n} A∈Rm×n 都可以分解为 A = U Σ V ⊤ \mathbf{A}=\mathbf{U}\mathbf{\Sigma}\mathbf{V}^\top A=UΣV⊤,其中:
- U ∈ R m × m \mathbf{U}\in\mathbb{R}^{m\times m} U∈Rm×m 和 V ∈ R n × n \mathbf{V}\in\mathbb{R}^{n\times n} V∈Rn×n 是正交矩阵。 U \mathbf{U} U的列是 A A ⊤ \mathbf{A}\mathbf{A}^\top AA⊤的特征向量(左奇异向量), V \mathbf{V} V的列是 A ⊤ A \mathbf{A}^\top\mathbf{A} A⊤A的特征向量(右奇异向量)。
- Σ ∈ R m × n \mathbf{\Sigma}\in\mathbb{R}^{m\times n} Σ∈Rm×n 是一个对角矩阵(或对角块矩阵),其对角线上的元素 σ i \sigma_i σi 称为奇异值,它们是非负的并按降序排列。奇异值表示了矩阵在对应奇异向量方向上的“拉伸”程度。
在LLM优化与分析中的应用
SVD及其相关的特征分解(Eigen-decomposition, A = P D P − 1 \mathbf{A} = \mathbf{P}\mathbf{D}\mathbf{P}^{-1} A=PDP−1 for diagonalizable square matrices, where D is diagonal with eigenvalues)是强大的分析工具和优化手段:
- 模型压缩 (Model Compression) via Low-Rank Approximation: 通过保留最大的k个奇异值及其对应的奇异向量,可以得到原矩阵的最佳低秩近似 A k = U k Σ k V k ⊤ \mathbf{A}_k = \mathbf{U}_k\mathbf{\Sigma}_k\mathbf{V}_k^\top Ak=UkΣkVk⊤。这常用于压缩LLM中的权重矩阵,减少参数量和计算量,同时尽量保持模型性能。
# 使用SVD压缩权重矩阵 (概念性) # weight_matrix: [out_features, in_features] # U, S, Vt = torch.linalg.svd(weight_matrix) # S is a vector of singular values # k = 100 # 保留的奇异值数量,压缩比例取决于需求 # U_k = U[:, :k] # S_k_diag = torch.diag(S[:k]) # Vt_k = Vt[:k, :] # Vt is already V.T # compressed_weight = torch.matmul(U_k, torch.matmul(S_k_diag, Vt_k))
- 理解模型内部机制: 分析权重矩阵的奇异值谱(奇异值的分布)可以揭示矩阵的有效秩和信息容量。分析奇异向量可以帮助理解矩阵变换的主要方向和模型学习到的关键特征。例如,分析注意力头输出投影矩阵的奇异向量,可能揭示该头关注的特定语义或句法模式。
- 降噪与正则化: 截断SVD可以去除由较小奇异值表示的噪声成分,有时能提高模型的泛化能力。
6. 秩 (Rank) 与低秩近似 (Low-Rank Approximation): 参数效率与模型适应
矩阵的秩是指其线性独立的行向量或列向量的最大数目。它反映了矩阵所代表的线性变换能将输入空间映射到的输出空间的维度,即变换的“有效维度”。
低秩近似在LLM中的核心价值:
许多在大型模型中出现的权重矩阵,或者在微调过程中产生的权重更新矩阵,实际上可能是“过参数化”的,即它们的内在秩远小于其完整维度。利用这一特性进行低秩近似,可以在不显著牺牲性能的前提下,大幅提高参数效率。
- 模型微调 (Fine-tuning) - LoRA (Low-Rank Adaptation): LoRA是一种非常流行的参数高效微调技术。它假设预训练模型的权重更新矩阵 Δ W \Delta \mathbf{W} ΔW 是低秩的。因此,它不直接更新原始权重 W 0 \mathbf{W}_0 W0,而是学习两个较小的低秩矩阵 A ∈ R m × r \mathbf{A} \in \mathbb{R}^{m \times r} A∈Rm×r 和 B ∈ R r × n \mathbf{B} \in \mathbb{R}^{r \times n} B∈Rr×n (其中 r ≪ min ( m , n ) r \ll \min(m,n) r≪min(m,n) 是秩),使得 Δ W ≈ A B \Delta \mathbf{W} \approx \mathbf{A}\mathbf{B} ΔW≈AB。在推理时,等效的权重是 W 0 + A B \mathbf{W}_0 + \mathbf{A}\mathbf{B} W0+AB。这极大地减少了微调所需的训练参数数量。
# 低秩参数化 (LoRA 核心思想) # 原始权重 W0 ∈ R^(m×n) (冻结) # 低秩更新 A ∈ R^(m×r), B ∈ R^(r×n) (可训练) # r = 8 # 通常是一个很小的值,如4, 8, 16, 32# class LoRALayer(torch.nn.Module): # def __init__(self, W0, r): # super().__init__() # self.m, self.n = W0.shape # self.r = r # self.A = torch.nn.Parameter(torch.randn(self.m, r)) # self.B = torch.nn.Parameter(torch.zeros(r, self.n)) # B通常初始化为0 # self.W0 = W0 # 冻结的原始权重 # # def forward(self, input_tensor): # delta_W = self.A @ self.B # output = F.linear(input_tensor, self.W0 + delta_W) # return output
- 模型量化与结构化剪枝: 一些量化技术也借鉴了低秩分解的思想,或者通过分析秩来指导剪枝策略。
- 知识蒸馏: 在知识蒸馏中,学生模型可能被设计为具有低秩结构的层,以更有效地从教师模型中学习。
- 推理加速: 使用分解后的小矩阵进行计算通常比使用原始大矩阵更快。
7. 范数 (Norms) 与距离 (Distances): 约束、正则化与优化稳定性
范数是衡量向量或矩阵“大小”或“长度”的函数。距离则是衡量两个向量或点之间“远近”的度量,通常基于范数定义。
常用范数
- L2范数 (Euclidean Norm): ∥ x ∥ 2 = ∑ i x i 2 \|\mathbf{x}\|_2 = \sqrt{\sum_i x_i^2} ∥x∥2=∑ixi2。衡量向量在欧几里得空间中的长度。
- L1范数 (Manhattan Norm): ∥ x ∥ 1 = ∑ i ∣ x i ∣ \|\mathbf{x}\|_1 = \sum_i |x_i| ∥x∥1=∑i∣xi∣。衡量向量各元素绝对值之和。
- Frobenius范数 (For Matrices): ∥ A ∥ F = ∑ i , j a i j 2 \|\mathbf{A}\|_F = \sqrt{\sum_{i,j} a_{ij}^2} ∥A∥F=∑i,jaij2。矩阵所有元素的平方和的平方根,相当于将矩阵视为一个长向量后计算其L2范数。
在LLM训练与优化中的应用
范数在LLM训练中扮演多重角色:
- 权重衰减 (Weight Decay / L2 Regularization): 在损失函数中加入权重的L2范数平方项( λ ∥ W ∥ F 2 \lambda \|\mathbf{W}\|_F^2 λ∥W∥F2)。这会惩罚过大的权重值,倾向于使权重分布更平滑,有助于防止过拟合,提高模型泛化能力。几何上,它将权重向原点“拉回”。
# L2权重衰减(通常由优化器实现,如AdamW) # loss = task_loss + weight_decay_lambda * torch.sum(torch.square(weights_parameter))
- L1 正则化: 在损失函数中加入权重的L1范数项( λ ∥ W ∥ 1 \lambda \|\mathbf{W}\|_1 λ∥W∥1)。L1正则化倾向于产生稀疏权重(即许多权重为零),可以用于特征选择或模型剪枝。
# l1_penalty = torch.sum(torch.abs(weights_parameter)) # loss = task_loss + l1_lambda * l1_penalty
- 梯度裁剪 (Gradient Clipping): 为了防止在训练过程中梯度因某些样本或特定网络结构而变得过大(梯度爆炸),导致训练不稳定,通常会裁剪梯度的范数。如果梯度的L2范数超过一个阈值,就将其缩放到该阈值。
# max_grad_norm = 1.0 # torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=max_grad_norm)
- 相似度度量: 余弦相似度 cos ( θ ) = a ⋅ b ∥ a ∥ 2 ∥ b ∥ 2 \cos(\theta) = \frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\|_2 \|\mathbf{b}\|_2} cos(θ)=∥a∥2∥b∥2a⋅b,是基于L2范数和内积定义的,广泛用于衡量词嵌入向量之间的语义相似性。
8. 投影 (Projections): 子空间映射与信息分离
投影是将一个向量映射到某个特定子空间(如由一组基向量张成的空间)的操作。在线性代数中,投影矩阵 P \mathbf{P} P 满足 P 2 = P \mathbf{P}^2 = \mathbf{P} P2=P (幂等性)。
LLM中的实际应用:特征解耦与并行处理
- 多头注意力 (Multi-Head Attention): 这是投影概念最显著的应用之一。输入序列的表示(词嵌入+位置编码)首先被线性投影到多个不同的、低维的查询(Q)、键(K)和值(V)子空间。每个“头”在各自的子空间中独立执行注意力计算。这使得模型能够同时关注来自输入的不同方面的信息。例如,一个头可能关注句法依赖,另一个头可能关注语义关联,还有一个头可能关注指代关系。
# 多头注意力投影示例(简化版,W_q, W_k, W_v 包含了所有头的投影权重) # def multi_head_projection(x, W_q_all_heads, W_k_all_heads, W_v_all_heads, num_heads): # # x: [batch_size, seq_len, d_model] # batch_size, seq_len, d_model = x.shape # d_head = d_model // num_heads # # # 投影到Q, K, V空间,并为多头重塑 # # W_q_all_heads: [d_model, d_model] (d_model = num_heads * d_head) # q_projected = F.linear(x, W_q_all_heads) # [batch_size, seq_len, d_model] # q = q_projected.view(batch_size, seq_len, num_heads, d_head).transpose(1, 2) # # q: [batch_size, num_heads, seq_len, d_head] # # # k 和 v 类似处理 # # ... # return q, k, v
- 输出层: 最终的Transformer层输出的隐藏状态向量会被投影回词汇表大小的维度,然后通过Softmax函数得到下一个词的概率分布。这个投影矩阵(通常与输入词嵌入矩阵共享或绑定权重)将高维的上下文表示映射到具体的词选择上。
- 残差连接中的投影: 在残差连接中,如果输入和输出的维度不匹配(例如,在某些网络架构的下采样层),可能需要一个线性投影(通常是一个1x1卷积或一个线性层)来使它们的维度一致,以便相加。
9. 仿射变换 (Affine Transformations) 与线性子空间:模型的几何解释
仿射变换是线性变换与平移(向量加法)的结合: y = W x + b \mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b} y=Wx+b。Transformer模型中的几乎所有参数化层(如注意力机制中的QKV投影、输出投影,以及前馈网络中的线性层)都是仿射变换。
LLM的几何之旅:在高维空间中塑造语义
Transformer的整个工作流程可以被看作是一系列在高维向量空间中进行的复杂几何操作:
- 嵌入与注入: 输入的离散token首先被嵌入(投影)到高维语义空间,并与位置信息结合。
- 序列变换与信息融合: 每一层Transformer块通过自注意力机制和前馈网络对这些向量表示进行迭代式的仿射变换和非线性激活 (如GELU, ReLU)。
- 自注意力可以被视为一种动态的、内容敏感的加权平均过程,它根据向量间的相似性(通过内积度量)在不同子空间中重新组合和传播信息。
- 前馈网络 (FFN),通常是两个仿射变换夹一个非线性激活函数(如 FFN ( x ) = GELU ( x W 1 + b 1 ) W 2 + b 2 \text{FFN}(\mathbf{x}) = \text{GELU}(\mathbf{x}\mathbf{W}_1 + \mathbf{b}_1)\mathbf{W}_2 + \mathbf{b}_2 FFN(x)=GELU(xW1+b1)W2+b2),对每个位置的表示进行独立的、更深层次的特征提取和转换。这个非线性是至关重要的,它使得模型能够学习远比单纯线性变换复杂得多的函数。
- 空间扭曲与分离: 经过多层这样的处理,输入序列的表示在语义空间中被不断地“扭曲”、“拉伸”和“折叠”,使得原本难以区分的语义模式变得线性可分或易于处理。
- 输出投影: 最终,经过充分变换的顶层隐藏状态被投影回词汇表空间,模型在此空间中选择概率最高的下一个token。
每一层Transformer实际上都在学习如何将输入表示映射到新的特征子空间,这些子空间能够更好地揭示与最终任务(如语言建模、翻译、问答)相关的特定模式或关系。残差连接确保了信息可以直接流过层,使得模型可以学习对恒等映射的修正,极大地帮助了深层网络的训练。
10. 迹 (Trace) 与谱特性 (Spectral Properties): 模型内部分析与正则化工具
矩阵的迹 (Trace) 是其主对角线上元素之和: Tr ( A ) = ∑ i a i i \text{Tr}(\mathbf{A}) = \sum_i a_{ii} Tr(A)=∑iaii。对于方阵,迹等于其所有特征值之和。谱特性主要指矩阵的特征值 (eigenvalues) 和奇异值 (singular values) 及其分布。
在LLM分析与设计中的应用
- 理解信息流与表示能力:
- 信息瓶颈理论: 某些理论工作使用互信息(与熵和条件熵相关,间接联系到概率分布的“形状”和矩阵变换如何改变它们)来分析信息在网络层间的流动。迹和谱有时在这些分析的数学推导中出现。
- 协方差矩阵的谱: 分析数据表示的协方差矩阵(如 X ⊤ X \mathbf{X}^\top\mathbf{X} X⊤X)的特征值谱,可以揭示数据的主要变化方向和表示的“有效维度”或“各向异性”。
- 谱归一化 (Spectral Normalization): 一种正则化技术,通过将权重矩阵 W \mathbf{W} W 除以其最大的奇异值(谱范数)来约束其Lipschitz常数。即 W sn = W / σ max ( W ) \mathbf{W}_{\text{sn}} = \mathbf{W} / \sigma_{\max}(\mathbf{W}) Wsn=W/σmax(W)。这有助于稳定训练过程,尤其是在生成对抗网络(GANs)中常用,但其原理对任何深度网络都有借鉴意义,可以防止层输出的尺度爆炸。
# 谱归一化 (概念性,PyTorch有 torch.nn.utils.spectral_norm) # def spectral_norm_regularize(weight_matrix): # # 计算最大奇异值 (通常通过幂迭代法近似) # _, S, _ = torch.linalg.svd(weight_matrix.data) # .data to avoid autograd issues if applying in place # max_singular_value = S[0] # # 归一化权重 # normalized_weight = weight_matrix / max_singular_value # return normalized_weight # 在实践中,优化器会作用于原始权重,而归一化在每次前向时进行
- 注意力机制分析:
- 分析注意力矩阵 A \mathbf{A} A (softmax后的权重)的谱特性。例如,其主特征向量可能揭示了注意力机制倾向于关注的“模式”或“主题”。
- 研究者有时会观察权重矩阵(如 W q , W k , W v , W o \mathbf{W_q}, \mathbf{W_k}, \mathbf{W_v}, \mathbf{W_o} Wq,Wk,Wv,Wo)的奇异值衰减情况,以判断其是否接近低秩,或是否存在某些主导的语义转换方向。
- 模型可解释性: 通过分析特定层权重矩阵的奇异值和奇异向量,可以尝试理解模型学到的变换的本质,哪些输入特征被放大,哪些被抑制。
11. 实际应用集成:一个简化的Transformer层
大型语言模型的惊人能力源于上述所有线性代数组件的复杂而精妙的协同作用。以下是一个极其简化的Transformer编码器层(不含dropout、精确的权重初始化等细节),以展示这些概念如何结合:
# 假设我们已经有了模型参数:
# attn_weights = {'Wq': ..., 'Wk': ..., 'Wv': ..., 'Wo': ...} (线性层权重和偏置)
# ffn_weights = {'W1': ..., 'W2': ...} (线性层权重和偏置)
# norm_params = {'gamma1':..., 'beta1':..., 'gamma2':..., 'beta2':...} (LayerNorm参数)class SimplifiedTransformerLayer(torch.nn.Module):def __init__(self, d_model, num_heads, d_ffn, dropout_rate=0.1):super().__init__()self.d_model = d_modelself.num_heads = num_headsassert d_model % num_heads == 0, "d_model must be divisible by num_heads"self.d_head = d_model // num_heads# 1. 多头自注意力组件self.W_q = torch.nn.Linear(d_model, d_model) # Projects to Q_total (all heads)self.W_k = torch.nn.Linear(d_model, d_model) # Projects to K_totalself.W_v = torch.nn.Linear(d_model, d_model) # Projects to V_totalself.W_o = torch.nn.Linear(d_model, d_model) # Output projection# 2. 前馈网络组件self.linear1_ffn = torch.nn.Linear(d_model, d_ffn)self.linear2_ffn = torch.nn.Linear(d_ffn, d_model)self.activation_ffn = torch.nn.GELU()# 3. 层归一化self.norm1 = torch.nn.LayerNorm(d_model)self.norm2 = torch.nn.LayerNorm(d_model)# (Dropout layers would also be here)def scaled_dot_product_attention(self, Q, K, V, mask=None):# Q, K, V: [batch_size, num_heads, seq_len, d_head]attention_scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_head)if mask is not None:attention_scores = attention_scores.masked_fill(mask == 0, -1e9) # Apply maskattention_weights = F.softmax(attention_scores, dim=-1) # Softmax over keys for each query# (Dropout on attention_weights can be applied here)context = torch.matmul(attention_weights, V) # Weighted sum of Vreturn context, attention_weightsdef forward(self, x, src_mask=None): # x: [batch_size, seq_len, d_model]batch_size, seq_len, _ = x.shape# --- 1. 多头自注意力子层 ---# a. 残差连接的输入residual1 = x# b. 第一个层归一化 (LN前置,Pre-LN)x_norm1 = self.norm1(x)# c. 线性投影到Q, K, V并重塑 (向量投影、张量操作)Q = self.W_q(x_norm1).view(batch_size, seq_len, self.num_heads, self.d_head).transpose(1, 2)K = self.W_k(x_norm1).view(batch_size, seq_len, self.num_heads, self.d_head).transpose(1, 2)V = self.W_v(x_norm1).view(batch_size, seq_len, self.num_heads, self.d_head).transpose(1, 2)# Q, K, V: [batch_size, num_heads, seq_len, d_head]# d. 计算注意力 (内积、相似度、加权平均)context_vectors, attn_weights_debug = self.scaled_dot_product_attention(Q, K, V, src_mask)# e. 合并多头输出并进行最终投影 (张量操作、线性变换)context_vectors = context_vectors.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)attn_output = self.W_o(context_vectors)# f. 残差连接 (向量加法,促进梯度流动)# (Dropout on attn_output can be applied here)x = residual1 + attn_output# --- 2. 前馈网络子层 ---# a. 残差连接的输入residual2 = x# b. 第二个层归一化x_norm2 = self.norm2(x)# c. 前馈网络计算 (仿射变换、非线性激活)ffn_hidden = self.linear1_ffn(x_norm2)ffn_activated = self.activation_ffn(ffn_hidden)# (Dropout on ffn_activated can be applied here)ffn_output = self.linear2_ffn(ffn_activated)# d. 残差连接# (Dropout on ffn_output can be applied here)output = residual2 + ffn_outputreturn output #, attn_weights_debug (if needed for analysis)
在这段代码中,清晰可见:
- 向量和矩阵是数据表示和参数存储的基本形式。
- 张量操作(如
view
,transpose
)对于实现多头并行至关重要。 - 内积(在
torch.matmul(Q, K.transpose(...))
中)用于计算查询和键之间的相似度,构成注意力的核心。 - 层归一化在子层输入前应用,以稳定数值和加速训练。
- 线性变换(
torch.nn.Linear
,内部是矩阵乘法和偏置加法)用于投影、特征提取和输出组合。 - 残差连接(向量加法)允许梯度直接传播,并使模型易于学习恒等映射的微小调整。