跳转到内容

8:Transformer

机器学习Banner

🎯 欢迎进入AI的”核心反应堆”!

Section titled “🎯 欢迎进入AI的”核心反应堆”!”

恭喜你!🎉 经过前面所有题目的历练,你已经从AI小白成功进阶。现在,是时候挑战现代AI的核心引擎——Transformer了!

这不仅仅是一道题目,更是一次从入门到精通的华丽蜕变!从GPT到ChatGPT,从BERT到Stable Diffusion,几乎所有震撼世界的AI模型都离不开这个神奇的架构。

🚀 激动人心的事实: 你即将亲手实现支撑整个大语言模型时代的核心技术!

深入理解 Transformer架构的设计哲学 ✅ 掌握注意力机制的数学原理 ✅ MiniGPT实战:理论转化为代码能力 ✅ 培养独立研究论文的能力

  1. 理论先行: 在写代码之前,确保脑海中有清晰的架构图

  2. 循序渐进: 从单头注意力到多头,从简单到复杂

  3. 善用调试: 每一个tensor的shape变化都要心中有数

  4. 对比分析: 调参数,看变化,做一个”超参数猎人”

  5. 延伸思考: 想想如何让你的模型更强更快

⚠️ 重要提醒: 虽然这题很硬核,但完成后你将从入门直达精通!这可能是你编程生涯中最有成就感的时刻之一!同时也是面试中的重点题目喔~

在深入代码之前,让我们先”拷问”一下Transformer的灵魂!

** 必答题清单:**

  1. Transformer的特点是什么?主要解决什么问题?缺点又是什么?

    • 🔍 任务: 在原论文中找到对应位置并标注
  2. Layer Norm vs Batch Norm

    • 🤔 这两个”标准化兄弟”有什么恩怨情仇?
  3. 位置编码 (Positional Encoding)

    • 深度解析: 什么是位置编码?

    • Transformer用了怎样的位置编码?

    • 这种设计的优势在哪里?

  4. Encoder & Decoder 架构解析

    • 画图 + 文字: 详细说明两个模块的结构

    • 数据是如何在其中流动的?

    • 🎯 面试重点: 务必搞懂每个细节!

  5. 注意力机制核心问题

    • K, Q, V分别代表什么?如何变化?

    • Q×K^T的数学内涵是什么?

    • Scaled Dot-Product Attention vs Additive Attention

  6. 自注意力机制的”自”

    • 这个”自”字体现在哪里?为什么这么设计?
  7. 可学习参数分析

    • 注意力机制中有需要学习的参数吗?

    • 多头注意力中呢?

    • 我们希望这些参数学到什么?

  8. 数据流动全解析

    • 文本输入后,各个维度是如何变化的?

    • 从token到embedding到输出的完整过程

  9. BLEU评估指标

    • BLEU是什么?在NLP中扮演什么角色?

“纸上得来终觉浅,绝知此事要躬行”

准备好撸起袖子,手搓一个迷你版GPT了吗?

硬件需求: 强烈建议使用云端GPU(参考第0题白嫖攻略)

工具: Jupyter Lab

tinyshakespeare数据集:

Terminal window
wget https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt

💡 偷懒指南: 数据集很小,直接复制粘贴到txt文件也OK!

import torch
import torch.nn as nn
from torch.nn import functional as F
import matplotlib.pyplot as plt
## 超参设置(每个参数都有讲究!)
batch_size = 16 # 批处理大小
block_size = 32 # 上下文长度
max_iters = 5000 # 训练轮数
learning_rate = 1e-3 # 学习率
device = 'cuda' if torch.cuda.is_available() else 'cpu'
n_embd = 64 # embedding维度
n_head = 4 # 多头注意力头数
n_layer = 4 # transformer层数
dropout = 0.0 # dropout率
torch.manual_seed(42) # 随机种子,保证可复现
# 读取莎士比亚文本
with open('input.txt', 'r', encoding='utf-8') as f:
text = f.read()
print(f"数据集总字符数: {len(text):,}")
print(f"前300个字符预览:\n{text[:300]}")
# 构建词汇表
chars = sorted(list(set(text)))
vocab_size = len(chars)
print(f"词汇表: {''.join(chars)}")
print(f"词汇表大小: {vocab_size}")
# 简单tokenizer实现
stoi = {ch: i for i, ch in enumerate(chars)}
itos = {i: ch for i, ch in enumerate(chars)}
encode = lambda s: [stoi[c] for c in s]
decode = lambda l: ''.join([itos[i] for i in l])
# 测试编解码
print(f"编码测试: {encode('Hello World!')}")
print(f"解码测试: {decode(encode('Hello World!'))}")
def get_batch(split):
"""
生成训练批次数据
你需要实现:
1. 从训练/验证集中随机采样
2. 生成输入序列x和目标序列y
3. 返回正确维度的tensor
"""
###################
# 你的代码在这里
###################
pass

🔥 这是整个项目的核心!必须手工实现!

class Head(nn.Module):
"""单头自注意力"""
def __init__(self, head_size):
super().__init__()
# QKV投影矩阵
self.key = nn.Linear(n_embd, head_size, bias=False)
self.query = nn.Linear(n_embd, head_size, bias=False)
self.value = nn.Linear(n_embd, head_size, bias=False)
# 下三角mask矩阵(防止看到未来)
self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
self.dropout = nn.Dropout(dropout)
def forward(self, x):
B, T, C = x.shape
###################
# 注意力计算逻辑
# 1. 计算Q, K, V
# 2. 计算注意力分数
# 3. 应用mask
# 4. 加权求和
###################
pass
class MultiHeadAttention(nn.Module):
"""多头注意力:并行处理多个注意力头"""
def __init__(self, num_heads, head_size):
super().__init__()
self.heads = nn.ModuleList([Head(head_size) for _ in range(num_heads)])
self.proj = nn.Linear(n_embd, n_embd)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
###################
# 多头并行计算与合并
###################
pass
class FeedForward(nn.Module):
"""前馈神经网络:简单而强大"""
###################
# 实现两层线性变换+激活
###################
pass
class Block(nn.Module):
"""Transformer Block:communication + computation"""
###################
# 组装完整的transformer块
# LayerNorm + MultiHeadAttention + LayerNorm + FeedForward
###################
pass
class BigramLanguageModel(nn.Module):
"""完整的语言模型"""
def __init__(self):
super().__init__()
# embedding表
self.token_embedding_table = nn.Embedding(vocab_size, n_embd)
self.position_embedding_table = nn.Embedding(block_size, n_embd)
# 堆叠transformer块
self.blocks = nn.Sequential(*[Block(n_embd, n_head=n_head) for _ in range(n_layer)])
self.ln_f = nn.LayerNorm(n_embd)
self.lm_head = nn.Linear(n_embd, vocab_size)
def forward(self, idx, targets=None):
###################
# 完整前向传播
# 1. token + position embedding
# 2. 通过transformer块
# 3. 输出预测
# 4. 计算损失(如果有target)
###################
pass
def generate(self, idx, max_new_tokens):
"""文本生成:让AI说话!"""
for _ in range(max_new_tokens):
# 裁剪到block_size
idx_cond = idx[:, -block_size:]
# 预测下一个token
logits, _ = self(idx_cond)
logits = logits[:, -1, :] # 只要最后一个时间步
probs = F.softmax(logits, dim=-1)
# 采样下一个token
idx_next = torch.multinomial(probs, num_samples=1)
idx = torch.cat((idx, idx_next), dim=1)
return idx
@torch.no_grad()
def estimate_loss():
"""评估训练和验证损失"""
out = {}
model.eval()
###################
# 计算平均损失
###################
model.train()
return out
# 实例化模型
model = BigramLanguageModel().to(device)
print(f"模型参数量: {sum(p.numel() for p in model.parameters())/1e6:.2f}M")
# 训练循环
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
train_losses, val_losses = [], []
for iter in range(max_iters):
###################
# 训练循环实现
# 1. 获取batch
# 2. 前向传播
# 3. 计算损失
# 4. 反向传播
# 5. 参数更新
# 6. 定期评估
###################
pass
# 绘制损失曲线
plt.figure(figsize=(10, 5))
plt.plot(train_losses, label='Train Loss', color='#FF6B6B')
plt.plot(val_losses, label='Validation Loss', color='#4ECDC4')
plt.xlabel('Iteration')
plt.ylabel('Loss')
plt.title('Training Progress: Loss Curves')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('loss_plot.png', dpi=300, bbox_inches='tight')
# 生成莎士比亚风格文本
print("AI莎士比亚开始创作...")
context = torch.zeros((1, 1), dtype=torch.long, device=device)
generated_text = decode(model.generate(context, max_new_tokens=2000)[0].tolist())
print(generated_text)

回答以下问题:

  1. Validation Loss收敛值是多少?

  2. MiniGPT参数量统计(应该<1M)

  3. 显存占用情况(应该<1G)

  1. RoPE vs 正弦位置编码

    • 为什么现代GPT使用Rotary Position Embedding

    • 尝试在MiniGPT中实现RoPE并对比性能!

  2. LayerNorm位置之争

    • Pre-LN vs Post-LN:为什么前者更稳定?

    • 修改代码实现Pre-LN架构并分析训练稳定性!

  3. 计算复杂度分析

    • 当block_size增加到128时会发生什么?

    • 尝试**梯度检查点(Gradient Checkpointing)**减少显存占用!

  4. FlashAttention算法

    • 了解这个让Transformer飞起来的神奇算法!

AI工具使用: 本题强烈建议纯手工实现!这可能是你最后一次有机会完全从零手撸这么核心的算法了,好好珍惜这个过程吧!


  • 代码文件: ml-8-姓名-学号.py

  • 理论答案: ml-8-姓名-学号.md

  • 压缩包提交

  • 邮箱: gimmerml401@163.com

  • 主题: 8-姓名-学号

出题人:百事可乐

QQ:2465800571