# Karpathy GPT 教程代码常见问题排查:这些坑让你的训练白跑
作为曾经在华强北柜台前调过无数开发板的工程师,我见过太多人跑 Karpathy 的 GPT 教程代码时踩坑踩到心态爆炸。今天就把几个最常见的实际问题讲清楚——不是教程不好,而是踩进去代价很高。
—
## 一、安全漏洞:pickle 反序列化让你服务器变肉鸡
这是最容易被忽略但后果最严重的一个问题。
nanoGPT 在 `train.py` 第 142 行直接用 `pickle.load()` 加载模型权重,GitHub Issue #661 明确标注了 [Security] Code Execution via unsafe deserialization。攻击者只要构造一个恶意的 pickle 文件,就能在你服务器上执行任意命令。nanoGPT 后续的分支 nanochat 更是直接爆出了 Critical Sandbox Escape Vulnerability (Issue #717),沙箱直接被穿透。
如果你在生产环境跑这段代码,赶紧停掉。这不是调参能解决的问题,是架构级缺陷。
从技术原理上讲,Python 的 `pickle` 模块可以序列化任意对象,包括可执行代码。当模型权重文件被替换成恶意 pickle 文件时,`pickle.load()` 会反序列化出被嵌入的代码并在加载进程中立即执行。这意味着攻击者只需要让目标机器加载一次恶意文件,就能获得与 Python 进程同等权限的 shell——在服务器层面就是 root 权限。2024 年已有多个挖矿木马通过这种路径渗透进 GPU 集群。
正确的做法是使用 `torch.load()` 并配合 `weights_only=True` 参数,或者将权重转换为安全格式如 SafeTensors。SafeTensors 由 Hugging Face 主导开发,已被大多数主流模型托管平台采用,其设计目标就是避免任意代码执行。
—
## 二、Loss 变成 NaN:训练到一半模型直接崩溃
这是 Reddit 上被反复提起的高频问题(r/learnmachinelearning,2023年9月)。具体表现:训练到 step 10k 左右,train loss 和模型输出直接变成 NaN,三角形图标在图表上刺眼地出现。
有人尝试了各种学习率,从极低到极高都试过,grad_clip 也调了,依然解决不了。这不是超参数问题,是代码实现层面存在数值不稳定。
核心原因在于 nanoGPT 的实现极度精简,省略了大量生产级训练框架中的保护机制。当你的数据分布稍有异常,或者梯度累积到一定程度,缺乏 safeguard 的代码会直接崩溃。
更深层地看,NaN 通常源于以下几种情况:除零操作(loss 计算时分母为 0)、指数溢出(softmax 或 layer norm 的输入值过大)、梯度爆炸(连乘过程中数值超出 float32 表示范围)。在 nanoGPT 的原始实现中,虽然设置了 `max_grad_norm`,但缺少对中间层激活值的监控机制。当模型深到 12 层以上、batch size 稍大时,hidden states 的数值范围很容易超出安全边界。
此外,Adam 优化器的 epsilon 参数(默认 1e-8)在处理极小梯度时也可能导致 0 除。某些极端 batch(比如 tokenizer 遇到罕见词元组合)会产生极小梯度,此时 1e-8 的 epsilon 反而不够稳定。
建议:不要直接跑完整训练。先用极小数据集(几百行文本)验证代码能正常收敛,再逐步放大。如果出现 NaN,第一步是检查数据清洗是否彻底——华强北常见的「数据脏了」问题包括:缺失值未处理、异常值未截断、特殊字符未转义。确认数据无误后,再考虑是否为架构层问题。
—
## 三、MacBook M2 MPS 后端:`top_k` 直接爆掉
这不是 Windows 和 Linux 的差异,是 Apple Silicon 特有的坑。Simon Willison 在他的笔记中记录了一个明确报错:
“`
RuntimeError: Currently topk on mps works only for k <= 16
```
nanoGPT 默认 `top_k=50`,在 M2 Mac 上推理直接崩溃。必须手动改成 `top_k<=16`,否则跑不起来。
这个坑的危险之处在于:你本地 Mac 调通了,放到 Linux 服务器上跑又是另一套行为,因为 MPS 和 CUDA 的数值行为不一致。很多初学者在这里浪费大量时间排查"为什么代码在我机器上能用,在服务器上不行"。
MPS(Metal Performance Shaders)是 Apple Silicon 的 GPU 加速框架,其算子支持度与 CUDA 存在显著差距。`top_k` 在 CUDA 上实现时没有上限限制,但 MPS 后端为了兼容 Metal API 的某些约束,将 k 值硬性限制在 16 以内。这个差异并非 bug,而是两个计算平台的设计哲学不同——CUDA 偏向灵活性,MPS 偏向一致性。
解决方案分为三个层次:短期——改 `top_k=16` 或直接设为 None(让模型自己决定);中期——通过 `torch.nn.functional.topk` 手动实现并手动截断;长期——用 `mps_issue_1073` 补丁或等待 PyTorch 官方修复。值得注意的是,这个 MPS 限制在 PyTorch 2.1+ 仍存在,不要期待版本升级会自动解决。
---
## 四、数据集下载卡死:openwebtext 是个老大难问题
Issue #89 记录了这个问题:`prepare.py` 下载 openwebtext 数据集时,下载和提取都成功,但在生成 train split 时直接报错中止。
更深层的问题是:openwebtext 的数据源本身就不稳定,经常遇到 404 或者链接失效。Karpathy 的代码没有对数据源做降级处理,一旦原始链接失效,整个流程直接挂掉。
相关阅读:国行Thinkpad笔记本_深圳报价