markdown复制## 1. 项目概述
最近在整理技术栈时,发现很多想入门大模型的朋友都被复杂的框架和数学公式劝退。作为过来人,我决定用最直白的方式带大家实现一个迷你版GPT模型。这个项目特别适合:
- 想了解Transformer架构但被论文公式吓退的初学者
- 需要快速验证创意的产品经理
- 考虑转行AI开发的程序员
- 任何想亲手训练文本生成模型的Python用户
我们将用不到500行代码实现完整训练流程,包含这些实用功能:
- 中文文本预处理流水线(清洗→分词→词表构建)
- 可解释的模型架构(每行代码都有注释)
- 训练过程可视化与早停机制
- 交互式文本生成演示
> 提示:本教程所有代码已在Colab和本地机器验证通过,建议边阅读边实操。
## 2. 核心模块解析
### 2.1 数据预处理设计
中文文本处理需要特别注意字符编码和分词问题。我们的清洗流程包含三个关键步骤:
1. **字符级清洗**:
```python
def clean_text(text):
# 保留中文、英文、数字和基础标点
text = re.sub(r'[^一-龥a-zA-Z0-9,。!?;,.!?;]', '', text)
# 合并连续空白符
return re.sub(r'\s+', ' ', text)
这里使用Unicode范围"一-龥"匹配所有中文字符,比常用的"\u4e00-\u9fa5"更全面。
python复制words = jieba.lcut(cleaned_text) # 结巴分词
token_text = ''.join(words) # 字级建模
虽然使用分词工具,但最终采用字级建模。这是因为:
python复制chars = sorted(list(set(token_text)))
stoi = {ch:i for i,ch in enumerate(chars)}
itos = {i:ch for i,ch in enumerate(chars)}
维护双向映射字典(stoi/itos)时,建议:
我们的TinyDecoderTransformer包含这些核心组件:
python复制class SelfAttention(nn.Module):
def forward(self, x):
# 计算Q,K,V
qkv = self.qkv(x).reshape(N, T, 3, self.heads, self.head_dim)
# 缩放点积注意力
scores = torch.einsum("nthe,nshe->nths", q, k) / math.sqrt(self.head_dim)
# 因果掩码(防止看到未来信息)
mask = torch.tril(torch.ones(T, T, device=x.device))
scores = scores.masked_fill(mask == 0, float("-inf"))
# 加权求和
attn = F.softmax(scores, dim=-1)
out = torch.einsum("nths,nshe->nthe", attn, v)
return self.fc_out(out.reshape(N, T, C))
关键点说明:
python复制self.ff = nn.Sequential(
nn.Linear(embed_size, embed_size * 2), # 扩展维度
nn.ReLU(),
nn.Linear(embed_size * 2, embed_size) # 恢复维度
)
采用经典的"扩展→压缩"结构:
python复制optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
对于小模型:
python复制if avg_val_loss < best_val_loss - 1e-6:
best_val_loss = avg_val_loss
no_improve = 0
else:
no_improve += 1
if no_improve >= patience:
break
设置patience=3表示:
python复制raw_text = load_raw_text('data/西游记.txt')
cleaned = preprocess_text(raw_text) # 长度约100万字符
token_text = tokenize_text(cleaned) # 分词后长度变化
python复制样例1:
输入序列 (X): 却说那大圣虽被唐僧逐赶
目标序列 (Y): 说那大圣虽被唐僧逐赶,
启动训练后关注这些指标:
code复制Epoch 1/50 [██████████████████████████████] 623/623
train_loss=2.3147 val_loss=2.1012 time=45.3s
使用不同温度参数对比:
python复制# 保守生成(temperature=0.5)
generate_text(model, stoi, itos, "孙悟空", temperature=0.5)
# 随机生成(temperature=1.2)
generate_text(model, stoi, itos, "唐僧", temperature=1.2)
实际效果对比:
当出现CUDA out of memory时:
python复制optimizer.zero_grad()
for micro_step in range(grad_accum_steps):
loss.backward(retain_graph=True)
optimizer.step()
如果模型总是重复相同片段:
确保整个流程编码一致:
想让模型更实用可以:
python复制# 旋转位置编码(RoPE)
# 分组查询注意力(GQA)
# 滑动窗口注意力
这个迷你项目已经包含了GPT的核心思想,后续我会继续分享如何:
训练过程中遇到任何问题,欢迎在评论区交流具体报错信息。