Skip to content

9olqLl7-x6XtWyNUssHvLGkRzM5cj2_KAPAYU4vaHIw

8:Transformer

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

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

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

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

📚 学习目标

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

💡 学习建议

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

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

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

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

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

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

🔥 第一部分:理论基础问答

Attention is all you need!

在深入代码之前,让我们先"拷问"一下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中扮演什么角色?

第二部分:MiniGPT实战挑战

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

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

🎮 实战环境准备

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

工具: Jupyter Lab

1️⃣ 数据集获取

tinyshakespeare数据集:

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

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

2️⃣ 超参数配置

python
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) # 随机种子,保证可复现
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) # 随机种子,保证可复现

3️⃣ 数据预处理

python
# 读取莎士比亚文本
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!'))}")
# 读取莎士比亚文本
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!'))}")

4️⃣ 数据分批处理

python
def get_batch(split):
    """
    生成训练批次数据
    
    你需要实现:
    1. 从训练/验证集中随机采样
    2. 生成输入序列x和目标序列y  
    3. 返回正确维度的tensor
    """
    ###################
    # 你的代码在这里
    ###################
    pass
def get_batch(split):
    """
    生成训练批次数据
    
    你需要实现:
    1. 从训练/验证集中随机采样
    2. 生成输入序列x和目标序列y  
    3. 返回正确维度的tensor
    """
    ###################
    # 你的代码在这里
    ###################
    pass

5️⃣ 模型架构实现

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

python
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
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

6️⃣ 模型评估 📊

python
@torch.no_grad()
def estimate_loss():
    """评估训练和验证损失"""
    out = {}
    model.eval()
    ###################
    # 计算平均损失
    ###################
    model.train()
    return out
@torch.no_grad()
def estimate_loss():
    """评估训练和验证损失"""
    out = {}
    model.eval()
    ###################
    # 计算平均损失
    ###################
    model.train()
    return out

7️⃣ 模型训练

python
# 实例化模型
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)
# 实例化模型
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)

8️⃣ 结果分析

回答以下问题:

  1. Validation Loss收敛值是多少?

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

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

9️⃣ 挑战性进阶问题

  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