一、大语言模型简介
大语言模型(如GPT、BERT、LLaMA、Grok等)是基于深度神经网络(主要是Transformer架构)的模型,通过在大规模文本数据上训练,学习语言的统计规律、语义和上下文关系。它们可以完成多种任务,包括文本生成、分类、翻译、问答等。训练和推理是模型生命周期的两个核心阶段:
- 训练:从海量数据中学习语言模式,优化模型参数。
- 推理:使用训练好的模型处理新输入,生成输出。
二、大语言模型的训练过程
训练一个大语言模型是一个复杂的过程,涉及数据准备、模型架构设计、优化算法、分布式计算和微调等多个阶段。以下是详细分解:
1. 数据准备
数据是LLM训练的基础,其质量和规模直接影响模型性能。
(1) 数据收集
- 来源:
- 网络爬取:如Common Crawl(数十TB的网页数据,包含论坛、博客、新闻等)。
- 公开数据集:Wikipedia(多语言百科全书)、BooksCorpus(免费电子书)、ArXiv(学术论文)。
- 专有数据:企业可能使用内部数据(如社交媒体帖子、客服对话)。
- 多模态数据(新兴趋势):如图像-文本对(用于多模态LLM,如CLIP)。
- 规模:
- GPT-3训练数据约45TB(压缩后约570GB),包含约5000亿个token(假设平均token长度为4字节)。
- LLaMA-3(假设)可能使用更大规模数据,如100TB+。
- 多样性:
- 覆盖多种语言(英语、汉语、西班牙语等)。
- 包含多种领域(科技、文学、法律、日常对话)。
- 风格多样(正式、口语、创意写作)。
- 细节:
- 数据通常以纯文本或JSON格式存储。
- 收集需要遵守版权和隐私法规(如GDPR)。
- 工具:如
wget
、BeautifulSoup
(Python爬虫)、Apache Nutch。
(2) 数据清洗
- 去噪:
- 移除广告、导航菜单、HTML标签、乱码。
- 示例:使用正则表达式过滤HTML标签(
<.*?>
)。 - 工具:
lxml
、html2text
。
- 去重:
- 使用哈希算法(如MinHash)或精确匹配检测重复文本。
- 工具:
datasketch
(MinHash实现)。 - 目的:避免模型记住重复模式,减少过拟合。
- 过滤低质量内容:
- 移除语法错误严重的文本、过短的句子。
- 使用语言模型(如BERT)评分文本质量,过滤低分内容。
- 隐私保护:
- 移除个人信息(如姓名、电话号码)。
- 工具:正则表达式、NER(命名实体识别)模型。
- 示例:
- 原始文本:
<p>Buy now for $99! Visit www.example.com</p>
- 清洗后:
Buy now for $99!
- 原始文本:
(3) 数据预处理
- 分词(Tokenization):
- 目标:将文本分割成token(单词、子词或字符)。
- 算法:
- Byte-Pair Encoding (BPE):
- 初始将文本拆分为字符,迭代合并高频字符对。
- 示例:单词“unhappiness”可能拆为“un”+“happi”+“ness”。
- 实现:
sentencepiece
库、tokenizers
(Hugging Face)。
- WordPiece(BERT使用):
- 类似BPE,但优化交叉熵损失。
- Unigram(SentencePiece使用):
- 从大词汇表开始,逐步删除低概率token。
- Byte-Pair Encoding (BPE):
- 词汇表:
- 大小:通常为3万到10万(GPT-3约5万,BERT约3万)。
- 包含特殊token,如
[CLS]
(分类)、[SEP]
(分隔)、<|start|>
(开始)。
- 代码示例(使用Hugging Face
tokenizers
):from tokenizers import Tokenizer from tokenizers.models import BPE from tokenizers.trainers import BpeTrainertokenizer = Tokenizer(BPE()) trainer = BpeTrainer(vocab_size=30000, special_tokens=["<|start|>", "<|end|>"]) tokenizer.train(files=["corpus.txt"], trainer=trainer) encoded = tokenizer.encode("Hello, world!") print(encoded.tokens) # ['Hel', 'lo', ',', 'wor', 'ld', '!']
- 序列化:
- 将token映射为数字ID。
- 示例:
Hello
→ 1001,,
→ 1002。 - 存储为张量(如PyTorch的
torch.tensor
)。
- 分组:
- 将文本切分为固定长度序列(最大长度如512或2048 token)。
- 填充(Padding):短序列补齐到最大长度,使用
[PAD]
。 - 截断(Truncation):长序列截断,保留关键部分。
- 注意力掩码(Attention Mask):
- 生成掩码张量,标记有效token(1)和填充token(0)。
- 示例:输入
[Hello, world, [PAD]]
,掩码为[1, 1, 0]
。
- 代码示例:
import torch tokens = [1001, 1002, 1003] # Hello, world padded = tokens + [0] * (512 - len(tokens)) # 填充到512 attention_mask = [1] * len(tokens) + [0] * (512 - len(tokens)) input_tensor = torch.tensor([padded]) mask_tensor = torch.tensor([attention_mask])
2. 模型架构
LLM通常基于Transformer架构,以下是详细分解。
(1) Transformer核心组件
- 自注意力机制(Self-Attention):
- 原理:
- 每个token的表示通过查询(Query)、键(Key)、值(Value)向量计算。
- 注意力分数衡量token间的相关性。
- 数学公式:
- 输入:嵌入矩阵
X ∈ ℝ^{n×d}
,n为序列长度,d为嵌入维度。 - 计算Q、K、V:
Q = XW_Q
,K = XW_K
,V = XW_V
,其中W_Q, W_K, W_V ∈ ℝ^{d×d_k}
。
- 注意力权重:
Attention(Q, K, V) = softmax(QK^T / √d_k)V
。√d_k
为缩放因子,防止数值过大。
- 输入:嵌入矩阵
- 代码示例(简化的自注意力):
import torch import torch.nn as nnclass SelfAttention(nn.Module):def __init__(self, d_model, d_k):super().__init__()self.d_k = d_kself.W_q = nn.Linear(d_model, d_k)self.W_k = nn.Linear(d_model, d_k)self.W_v = nn.Linear(d_model, d_k)def forward(self, x):Q = self.W_q(x) # [batch, seq_len, d_k]K = self.W_k(x)V = self.W_v(x)scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5)attn_weights = torch.softmax(scores, dim=-1)output = torch.matmul(attn_weights, V)return output
- 原理:
- 多头注意力(Multi-Head Attention):
- 将注意力分为h个头(head),每头独立计算:
head_i = Attention(QW_Q^i, KW_K^i, VW_V^i)
。- 合并:
MultiHead(Q, K, V) = Concat(head_1, ..., head_h)W_O
。
- 参数:
- 头数h:如12(BERT)或96(GPT-3)。
- 每头维度:
d_k = d_model / h
。
- 优势:捕捉不同的语义关系(如语法、语义)。
- 将注意力分为h个头(head),每头独立计算:
- 前馈神经网络(FFN):
- 逐位置应用:
FFN(x) = max(0, xW_1 + b_1)W_2 + b_2
。
- 参数:
- 中间维度通常为
4 * d_model
(如BERT的3072)。
- 中间维度通常为
- 实现:
class FeedForward(nn.Module):def __init__(self, d_model, d_ff):super().__init__()self.linear1 = nn.Linear(d_model, d_ff)self.linear2 = nn.Linear(d_ff, d_model)self.relu = nn.ReLU()def forward(self, x):return self.linear2(self.relu(self.linear1(x)))
- 逐位置应用:
- 层归一化(Layer Normalization):
- 稳定训练:
LayerNorm(x) = γ * (x - μ) / σ + β
,其中μ、σ为均值和标准差,γ、β为可学习参数。
- 位置:通常在注意力后和FFN后。
- 稳定训练:
- 残差连接(Residual Connection):
x = x + Sublayer(x)
,避免梯度消失。
- 位置编码(Positional Encoding):
- 固定正弦/余弦编码:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
。PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
。
- 可学习编码:直接优化嵌入向量。
- 实现:
def get_positional_encoding(seq_len, d_model):pos = torch.arange(seq_len).unsqueeze(1)div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))pe = torch.zeros(seq_len, d_model)pe[:, 0::2] = torch.sin(pos * div_term)pe[:, 1::2] = torch.cos(pos * div_term)return pe
- 固定正弦/余弦编码:
(2) 模型类型
- 仅解码器模型(如GPT):
- 单向自回归,适合生成任务。
- 注意力掩码:因果掩码(Causal Mask),只关注前文。
- 仅编码器模型(如BERT):
- 双向建模,适合理解任务。
- 注意力掩码:全连接(无掩码)。
- 编码器-解码器模型(如T5):
- 适合序列到序列任务。
- 编码器:双向;解码器:自回归。
(3) 模型规模
- 参数量:
- GPT-3:1750亿。
- LLaMA-3-70B:700亿。
- 参数分布:嵌入层约20%,注意力层约40%,FFN约40%。
- 层数与维度:
- GPT-3:96层,
d_model=12288
,h=96
。 - BERT:12层(Base)或24层(Large),
d_model=768
或1024。
- GPT-3:96层,
- 计算复杂度:
- 自注意力:
O(n^2 * d)
,n为序列长度。 - FFN:
O(n * d * d_ff)
。 - 序列长度增加会显著提高成本。
- 自注意力:
3. 训练目标
训练目标定义了模型优化的方向。以下是详细分析:
(1) 自回归语言建模(Causal Language Modeling)
- 目标:
- 预测下一个token,给定前文:
P(x_t | x_1, ..., x_{t-1})
。
- 预测下一个token,给定前文:
- 损失函数:
- 交叉熵损失:
L = -∑_{t=1}^T log P(x_t | x_1, ..., x_{t-1})
。
- 对每个token计算softmax概率:
P(x_t) = softmax(W_o * h_t)
,其中h_t
为Transformer输出。
- 交叉熵损失:
- 实现:
class LanguageModel(nn.Module):def __init__(self, vocab_size, d_model):super().__init__()self.transformer = TransformerDecoder(...) # 简化的Transformerself.output = nn.Linear(d_model, vocab_size)def forward(self, x):h = self.transformer(x)logits = self.output(h)return logitscriterion = nn.CrossEntropyLoss() logits = model(input_ids) # [batch, seq_len, vocab_size] loss = criterion(logits.view(-1, vocab_size), target_ids.view(-1))
- 细节:
- 输入和目标偏移一位(
target_ids = input_ids[1:]
)。 - 因果掩码确保只关注前文。
- 输入和目标偏移一位(
(2) 掩码语言建模(Masked Language Modeling)
- 目标:
- 随机掩盖15%的token,预测原始token。
- 示例:输入
The cat [MASK] on the mat.
,预测sat
。
- 损失函数:
- 仅对掩码token计算交叉熵:
L = -∑_{i∈masked} log P(x_i | x)
。
- 仅对掩码token计算交叉熵:
- 细节:
- 掩码策略:
- 80%替换为
[MASK]
。 - 10%替换为随机token。
- 10%保持不变。
- 80%替换为
- 实现:
def mask_tokens(inputs, tokenizer, mlm_prob=0.15):labels = inputs.clone()mask = torch.rand(inputs.shape) < mlm_probinputs[mask] = tokenizer.mask_token_idreturn inputs, labels
- 掩码策略:
(3) 序列到序列建模
- 目标:
- 将输入序列映射到输出序列:
P(y_1, ..., y_m | x_1, ..., x_n)
。
- 将输入序列映射到输出序列:
- 损失函数:
- 交叉熵:
L = -∑_{t=1}^m log P(y_t | y_1, ..., y_{t-1}, x)
。
- 交叉熵:
- 实现:
- 编码器处理输入,解码器生成输出。
- 使用
teacher forcing
:训练时输入真实目标序列。
4. 优化算法
(1) 随机梯度下降(SGD)与Adam
- Adam:
- 结合一阶动量(均值)和二阶动量(方差)。
- 更新规则:
m_t = β_1 m_{t-1} + (1 - β_1) g_t
。v_t = β_2 v_{t-1} + (1 - β_2) g_t^2
。θ_t = θ_{t-1} - η * m_t / (√v_t + ε)
。
- 参数:
β_1=0.9
,β_2=0.999
,ε=1e-8
。
- 实现:
optimizer = torch.optim.Adam(model.parameters(), lr=6e-4, betas=(0.9, 0.999))
(2) 学习率调度
- Warm-up:
- 初始阶段线性增加学习率:
lr_t = lr_max * t / T_warmup
。
- 示例:前10%步数从0到6e-4。
- 初始阶段线性增加学习率:
- 衰减:
- 余弦退火:
lr_t = lr_min + 0.5 * (lr_max - lr_min) * (1 + cos(π * t / T))
。
- 实现:
from torch.optim.lr_scheduler import CosineAnnealingLRscheduler = CosineAnnealingLR(optimizer, T_max=100000, eta_min=1e-6)
- 余弦退火:
(3) 批量大小
- 大批量:
- GPT-3使用约3.2M token/batch。
- 提高并行效率,稳定梯度。
- 梯度累积:
- 显存不足时,分多次计算小批量:
optimizer.zero_grad() for i, batch in enumerate(data):loss = model(batch).lossloss.backward()if (i + 1) % accumulation_steps == 0:optimizer.step()optimizer.zero_grad()
- 显存不足时,分多次计算小批量:
(4) 混合精度训练
- 原理:
- 使用FP16/BF16计算,FP32存储权重。
- 减少显存,加速计算。
- 实现:
from torch.cuda.amp import autocast, GradScalerscaler = GradScaler() for batch in data:optimizer.zero_grad()with autocast():loss = model(batch).lossscaler.scale(loss).backward()scaler.step(optimizer)scaler.update()
5. 分布式训练
(1) 数据并行
- 每个GPU处理不同数据子集,同步梯度:
All-Reduce
聚合梯度。
- 实现:PyTorch DDP:
import torch.distributed as dist from torch.nn.parallel import DistributedDataParallel as DDPdist.init_process_group(backend="nccl") model = DDP(model)
(2) 模型并行
- 流水线并行:
- 将层分配到不同设备:
- GPU 1:层1-10,GPU 2:层11-20。
- 实现:DeepSpeed Pipe。
- 将层分配到不同设备:
- 张量并行:
- 将矩阵运算分割:
- 注意力头的W_Q、W_K分割到不同GPU。
- 实现:Megatron-LM。
- 将矩阵运算分割:
(3) ZeRO(Zero Redundancy Optimizer)
- 分片优化器状态、梯度和参数:
- ZeRO-1:分片优化器状态。
- ZeRO-2:分片梯度。
- ZeRO-3:分片参数。
- 实现:DeepSpeed:
from deepspeed import init_distributed import deepspeedmodel_engine, optimizer, _, _ = deepspeed.initialize(model=model, config=ds_config)
(4) 通信优化
- Ring All-Reduce:
- 环形拓扑减少通信开销。
- NVLink:
- NVIDIA GPU间高速互联。
- InfiniBand:
- 集群间高速网络。
6. 训练中的挑战与解决方案
(1) 计算资源
- 硬件:
- GPT-3训练可能使用8000块A100 GPU,耗时约1个月。
- 成本:数百万美元。
- 解决方案:
- 云服务:AWS(EC2 P4d)、Azure(NDv4)。
- 高效算法:ZeRO、混合精度。
(2) 过拟合
- Dropout:
- 随机丢弃神经元(概率如0.1)。
- 权重衰减:
- L2正则化,惩罚大权重。
- 数据增强:
- 动态掩码、文本替换。
(3) 稳定性
- 梯度裁剪:
- 限制梯度范数(如1.0)。
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
- 初始化:
- Xavier或He初始化,稳定初始梯度。
7. 微调
(1) 监督微调(SFT)
- 数据:标注的问答对、翻译对。
- 实现:
from transformers import Trainer, TrainingArgumentstraining_args = TrainingArguments(output_dir="./sft",per_device_train_batch_size=8,num_train_epochs=3, ) trainer = Trainer(model=model, args=training_args, train_dataset=dataset) trainer.train()
(2) 强化学习微调(RLHF)
- 步骤:
- 收集人类偏好数据(比较模型输出)。
- 训练奖励模型(Reward Model):
- 输入:输出对,输出:偏好分数。
- 使用PPO优化:
- 最大化奖励,同时约束KL散度。
- 实现:TRL库:
from trl import PPOTrainerppo_trainer = PPOTrainer(model=model, ref_model=ref_model, config=ppo_config) ppo_trainer.train()
(3) 参数高效微调(PEFT)
- LoRA:
- 冻结预训练权重,添加低秩矩阵:
W = W_0 + BA
,B、A为小矩阵。
- 实现:PEFT库:
from peft import LoraConfig, get_peft_modellora_config = LoraConfig(r=8, lora_alpha=16, target_modules=["q", "v"]) model = get_peft_model(model, lora_config)
- 冻结预训练权重,添加低秩矩阵:
三、大语言模型的推理过程
推理是使用训练好的模型处理输入,生成输出的过程。以下是详细分解:
1. 输入处理
(1) 分词
- 使用训练时相同的分词器。
- 示例:
- 输入:“我爱学习人工智能”
- 输出:
["我", "爱", "学", "习", "人", "工", "智", "能"]
。
(2) 序列化
- 转换为ID:
token_ids = tokenizer.convert_tokens_to_ids(tokens)
。
(3) 填充与截断
- 固定长度(如2048):
- 填充:
[PAD]
。 - 截断:保留前2048 token。
- 填充:
- 注意力掩码:
attention_mask = [1, 1, ..., 0, 0]
。
2. 模型前向传播
(1) 嵌入层
- 词嵌入:
x = embedding(token_ids)
,x ∈ ℝ^{seq_len×d_model}
。
- 位置嵌入:
x = x + positional_encoding(seq_len, d_model)
。
(2) Transformer层
- 每层:
- 自注意力:
h = MultiHeadAttention(x)
。 - 残差:
x = x + h
。 - 层归一化:
x = LayerNorm(x)
。 - FFN:
h = FeedForward(x)
。 - 残差与归一化:
x = LayerNorm(x + h)
。
- 自注意力:
(3) 输出层
- 线性层+softmax:
logits = W_o * h
,probs = softmax(logits)
。
- 输出:词汇表概率分布。
3. 输出生成
(1) 自回归生成
- 过程:
- 初始输入:
x_1, ..., x_t
。 - 预测:
x_{t+1} ~ P(x_{t+1} | x_1, ..., x_t)
。 - 加入新token,重复。
- 初始输入:
- 解码策略:
- 贪心搜索:
x_{t+1} = argmax(P(x_{t+1}))
。
- 束搜索:
- 保留k个候选序列:
score = ∑ log P(x_t)
。
- 参数:
beam_size=5
。
- 保留k个候选序列:
- 采样:
- 随机采样:
x_{t+1} ~ P(x_{t+1})
。 - Top-k:从前k个token采样。
- Top-p:从累计概率p的token采样。
- 随机采样:
- 温度:
P'(x) = P(x)^(1/T) / ∑ P(x)^(1/T)
。- T<1:更确定,T>1:更随机。
- 实现:
from transformers import GPT2LMHeadModel, GPT2Tokenizermodel = GPT2LMHeadModel.from_pretrained("gpt2") tokenizer = GPT2Tokenizer.from_pretrained("gpt2") inputs = tokenizer("Hello, world!", return_tensors="pt") outputs = model.generate(inputs["input_ids"],max_length=50,do_sample=True,top_k=50,top_p=0.95,temperature=0.7, ) print(tokenizer.decode(outputs[0]))
- 贪心搜索:
(2) 非自回归生成
- 一次性输出:
- 示例:BERT预测
[MASK]
。
- 示例:BERT预测
- 实现:
from transformers import BertForMaskedLMmodel = BertForMaskedLM.from_pretrained("bert-base-uncased") inputs = tokenizer("The cat [MASK] on the mat.", return_tensors="pt") logits = model(**inputs).logits masked_idx = (inputs["input_ids"] == tokenizer.mask_token_id).nonzero() predicted_token = logits[0, masked_idx, :].argmax(dim=-1)
4. 推理优化
(1) 量化
- INT8量化:
- 将FP32权重转换为INT8:
w_int8 = round(w_fp32 / scale)
。
- 将FP32权重转换为INT8:
- 实现:
torch.quantization
。
(2) 剪枝
- 结构化剪枝:
- 删除整个注意力头或FFN单元。
- 非结构化剪枝:
- 删除小权重。
- 实现:
torch.nn.utils.prune
。
(3) 蒸馏
- 知识蒸馏:
- 学生模型优化:
L = α * L_CE + (1 - α) * L_KL(teacher_logits, student_logits)
。
- 学生模型优化:
- 实现:Hugging Face DistilBERT。
(4) KV缓存
- 原理:
- 缓存自注意力的K、V:
K_t = [K_1, ..., K_t]
,V_t = [V_1, ..., V_t]
。
- 缓存自注意力的K、V:
- 实现:
class TransformerDecoderWithCache(nn.Module):def __init__(self):super().__init__()self.k_cache = []self.v_cache = []def forward(self, x, use_cache=True):if use_cache:k, v = self.attention(x)self.k_cache.append(k)self.v_cache.append(v)return self.attention(x, k_cache=self.k_cache, v_cache=self.v_cache)
(5) 硬件加速
- TensorRT:
- 优化推理图,融合算子。
- ONNX:
- 跨平台优化。
5. 推理中的挑战
(1) 计算成本
- 显存:
- GPT-3推理需要约350GB(FP16)。
- 解决方案:
- 模型并行、量化。
(2) 延迟
- 实时要求:
- 对话系统需<200ms。
- 解决方案:
- 高效解码、缓存。
(3) 输出质量
- 幻觉(Hallucination):
- 生成错误事实。
- 解决方案:
- 微调、后处理。
四、案例分析:以LLaMA-3为例
1. 训练
- 数据:
- 假设100TB,包含Common Crawl、Wikipedia、C4。
- 清洗:去重(MinHash)、去噪(正则表达式)。
- 分词:SentencePiece,词汇表约5万。
- 架构:
- 仅解码器,70B参数,80层,
d_model=8192
,h=64
。
- 仅解码器,70B参数,80层,
- 目标:
- 自回归建模,交叉熵损失。
- 优化:
- AdamW,
lr=3e-4
,余弦衰减。 - 混合精度(BF16)。
- 批量大小:4M token。
- AdamW,
- 分布式:
- 4096块H100 GPU,ZeRO-3。
- 训练约2个月。
2. 推理
- 输入:
- “请解释量子力学”
- 分词:
["请", "解", "释", "量", "子", "力", "学"]
。
- 前向传播:
- 嵌入:
8192
维向量。 - Transformer:80层,KV缓存加速。
- 嵌入:
- 生成:
- Top-p采样,
p=0.9
,temperature=0.8
。 - 输出:逐token生成,约100 token。
- Top-p采样,
- 优化:
- INT8量化,TensorRT加速。
五、学习建议
1. 理论
- 书籍:
- 《Deep Learning》:数学基础。
- 《Transformers for Natural Language Processing》:Transformer详解。
- 论文:
- 《Scaling Laws for Neural Language Models》:规模效应。
- 《LoRA: Low-Rank Adaptation》:高效微调。
- 课程:
- CS231n(Stanford,CNN与Transformer)。
- fast.ai NLP课程。
2. 实践
- 框架:PyTorch(推荐)、JAX(高性能)。
- 项目:
- 实现小型Transformer:
class Transformer(nn.Module):def __init__(self, d_model, nhead, num_layers):super().__init__()self.encoder = nn.TransformerEncoder(nn.TransformerEncoderLayer(d_model, nhead), num_layers)def forward(self, src):return self.encoder(src)
- 微调LLaMA:
- 使用Hugging Face PEFT。
- 推理优化:
- 实现KV缓存。
- 实现小型Transformer:
- 数据集:
- WikiText、C4。
- 工具:
- Hugging Face、DeepSpeed。
3. 资源
- 免费:
- Colab Pro(A100 GPU)。
- Kaggle(TPU)。
- 社区:
- CSDN平台(#NLP、#LLM)。
- GitHub、Hugging Face、Meta AI。
六、总结
- 训练:
- 数据:收集、清洗、分词(BPE)。
- 架构:Transformer(自注意力、FFN)。
- 目标:自回归、掩码建模。
- 优化:Adam、混合精度、分布式(ZeRO)。
- 微调:SFT、RLHF、LoRA。
- 推理:
- 输入:分词、序列化。
- 前向:嵌入、Transformer、输出。
- 生成:贪心、束搜索、采样。
- 优化:量化、KV缓存、TensorRT。