跑通 nanochat 的最小闭环,判断标准不是「没报错」,而是每一步都留下可检查的产物:分词结果能编码也能解码回原文、训练日志里损失在持续变化、刚保存的检查点能被采样脚本正确加载并生成连贯文本。卡住时按「数据 → 显存 → 采样」的顺序切分问题,先确认产物是否存在、是否被正确读取,再考虑改模型或调参。下面按五段走,每段跑通再进下一段,避免把数据问题和训练问题混在一起排查。
先跑通链路,再谈效果。通常路径是:读目录锁定入口脚本 → 用小体量文本产出分词文件并做编码/解码自检 → 用最小配置启动训练,只盯损失是否正常变化 → 用刚保存的检查点做一次对话采样。每步都要有文件或日志作为通过依据,「没有报错」不等于跑通。目录约定、参数名与默认值以仓库帮助输出和实际文件为准,不同版本可能有差异。
读仓库目录,锁定训练、分词、采样各自的入口脚本
拿到仓库先别急着运行。目录阅读的顺序建议是:先看 README 和依赖清单(requirements.txt、pyproject.toml 或 environment.yml 之一),确认 Python 版本与需要装的包;再看根目录下的 *.py 与 scripts/、train/、data/ 一类子目录,把脚本按用途分成三类——数据准备与分词、训练、采样或推理。多数实现的这三类入口是互相独立的脚本,训练脚本不会顺手帮你分词,采样脚本也不会帮你训练。
依赖安装骨架可以先照这个形式走,具体文件以仓库实际存在的为准:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 若仓库用 pyproject.toml:pip install -e .
# GPU 环境需先按 CUDA 版本装好对应 torch,再装其余依赖
确认脚本用途最省事的办法是用它自带的帮助参数,而不是猜:
python <某个入口脚本>.py `--help`
# 若脚本不用 argparse,就直接读文件末尾 if __name__ == "__main__" 那一段
通过的依据是:命令能打印出参数说明或正常退出,而不是抛 ImportError 或找不到模块。如果 import 失败,先解决依赖版本问题,不要继续往下跑。
准备一份小体量文本并跑出分词产物
数据环节单独跑,是为了让后面训练出错时能排除掉数据格式的原因。通常的做法是在仓库约定好的数据目录(常见是 data/ 或 dataset/)下放原始文本,训练集和验证集分开放,文件名和层级按脚本默认读取的路径来放,不要自己重命名后再去改脚本。
分词阶段一般会产出两类东西:一是词表或分词器模型文件,二是把原始文本编码后的 token 序列文件。查看方式就是直接列目录看文件是否生成、大小是否合理:
ls -lh data/tokenizer/ 2>/dev/null || ls -lh data/
# 产物文件应为非空;token 文件数量应与切分后的分片数吻合
判断分词是否成功的检查点有三个:产物文件存在且非空;词表大小落在合理区间(不是 0,也不是和字符数一样多,那通常意味着没做真正的子词切分);随机抽一段原始文本做编码再解码,能大致还原原文。若解码结果是大量重复符号或空串,说明分词配置有问题,先修这一步再进训练。
用最小配置启动一次训练并盯住损失输出
最小配置的目的不是训出好模型,而是确认训练循环能转起来。需要显式确认的配置项通常包括:数据路径指向刚生成的分词产物、批大小、序列长度、模型层数与隐藏维度、训练步数或轮数、学习率、运行设备、检查点输出目录。这些字段的具体名称各版本不一,先看 `--help` 或配置样例文件再填。
启动命令骨架大致如下,参数名请替换为仓库实际支持的写法:
python <训练入口>.py \
`--data-dir` data \
`--out-dir` runs/minimal \
`--device` cuda \
# 其余按最小配置填写:批大小、序列长度、步数等
日志中应重点关注几行:当前步数、损失值、学习率、吞吐或每步耗时。正常表现是损失从一个较高的初始值开始,随步数有波动但整体往下走。异常信号包括:损失变成 nan 或 inf(多半是学习率过大或数据里有异常值)、损失长时间几乎不动(配置没生效或数据没被读到)、显存不足报错、步数长时间不推进(数据加载阻塞)。看到若干个 step 的损失打印、并且输出目录里出现中间检查点文件,才算这一步通过。
从保存的检查点做一次对话采样
采样环节验证的是训练产物能不能被正确读取。先做路径检查:采样脚本传入的检查点路径,要和训练时输出目录里的实际文件名一致,注意是选最新的那个还是最终的那个;确认文件存在且大小不为 0。如果检查点里只存了权重、没存模型结构配置,采样侧就要传入与训练时完全相同的层数、维度、词表大小,否则会出现键名或形状不匹配。
采样命令骨架:
python <采样入口>.py \
`--checkpoint` runs/minimal/<检查点文件> \
`--prompt` "你好" \
# 温度、最大生成长度等按仓库支持项填写
采样结果异常的排查顺序建议是:先确认加载的确实是训练后期的检查点,而不是随机初始化权重;再确认采样用的分词器与训练时是同一份词表和同一套编码逻辑;然后确认 prompt 里的字符都在词表覆盖范围内;接着调生成参数,比如提高或降低温度、增加最大长度;最后再考虑模型本身训练是否充分。顺序反过来做,容易在模型质量上浪费时间。
对照日志判断卡在数据、显存还是采样阶段
三类问题在日志上有比较明显的区分,可以按下面的特征对号入座。
- 数据阶段卡住:进程在跑但没有 step 输出,CPU 占用偏高或磁盘 IO 明显,日志停在扫描、加载或预处理字样。下一步动作是缩小数据量先跑通、检查路径与文件格式是否和脚本预期一致、把多进程加载先关掉单进程试。
- 显存不足:出现 CUDA out of memory 一类报错,或者显存占用爬升后停住不动。下一步动作是减小批大小或序列长度、启用梯度累积、确认没有多个进程同时占满同一张卡。
- 采样阶段异常:检查点加载时报键缺失或形状不匹配,或者加载成功但输出乱码、空串、反复重复同一片段。下一步动作是核对采样侧传入的模型结构参数与词表是否和训练时一致,再回到生成参数。
定位顺序上,建议先看「有没有产物」,再看「产物有没有被读到」,最后才看「读到之后结果对不对」。这三层依次排除,比一上来就改模型配置要省时间;每次只改一个变量,改完重跑同一段流程,才能判断到底是哪一步在起作用。