最后交付什么,怎样算是完成
本章不再换一个孤立的小例子。最终交付物就是前十一章持续构建的同一台字符Transformer:从文档编号与编码开始,经自写解码块、全参数训练、保存恢复,到独立评分和缓存生成。你应能从一份新解压的目录重复跑通,解释每个主要张量的含义,并用失败检查界定结果。如果只能展示一段生成文字,或者只能运行别人保存的权重,却无法重建训练与评价,就尚未完成这门实战课的项目。
最终包分两部分:自己的实现与一份实验报告。实现应包含数据、配置、源码、测试和必要说明;报告应包含原始指标、失败样例与对应检查点的身份信息。课程提供完整参考工程,帮助你对照公式和接口,但参考实现能跑不等于你已经理解。建议先按章节逐个补齐自己的模块,再把同一输入交给参考版对照,保存首次失败的中间量,而不是只在最后粘贴一份能通过的结果。
本课默认读者已经会写Python函数、列表、字典和运行本地程序;线性代数、概率与导数的本地用法已在前面展开。项目依赖PyTorch的张量运算和自动求导引擎,但注意力、因果遮罩、解码块与训练组织由我们自己编写,没有使用预训练模型或nn.Transformer。这是完整架构与工程链的小规模实现,不是用几千字语料训练出了预训练级中文模型。实现完整与能力规模是两条不同的边界。
先检查环境,再确认资料从哪里进入系统
解压项目包,进入README所在目录。首先确认解释器版本至少为Python3.10;课程实际验证使用Python3.12.14与PyTorch2.6.0。若系统默认python3版本太旧,改用已有符合要求的解释器创建环境,后续统一使用环境内的解释器。不要一部分命令用系统Python、另一部分用新环境,再把缺包错误误判成模型问题。下列命令不会下载预训练权重,依赖安装完成后,示例数据和全部计算都在本地。
python3 -c 'import sys; assert sys.version_info >= (3,10), sys.version'
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python -m tests.run_tests --output evidence/my-tests.json
项目示例包含二十四篇为本课程新写的短中文文档,稳定编号从doc001到doc024;标题只作说明,正文才参与训练。划分文件指定十八篇训练、三篇验证、三篇测试,然后只由训练正文构建排序字符词表。不要把“先建立全部词表,再分割文档”当作相同操作。对新语料,先写出来源和可用范围,再固定文档身份、切分与重复检查;文件名不同,并不能排除正文重复。
读取数据后,检查清单中的文档数量、内容哈希、词表与未知字符比例。每篇编码为BOS、真实字符、EOS;文档内分窗,绝不把上一文档的结尾当作下一文档的开头。每个真实字符和每个EOS在完整评价中计分一次,窗口开头的可用上下文可能较短。分窗长度和重置位置属于协议,不能在挑选最好结果后悄悄更换。第八章和第九章的同一约定在最终项目中仍有效。
| 阶段 | 必须读到的输入 | 必须产出的证据 |
|---|---|---|
| 数据 | 文档、split、编码约定 | ID隔离、重复检查、词表、未知率 |
| 模型 | 冻结Config与参数初始化 | 形状、因果性、参数账本、梯度 |
| 训练 | 训练窗口与采样器状态 | loss记录、更新次数、完整checkpoint |
| 评价 | 保存的词表与未更新文档 | 有效token总量、损失和、评分协议 |
| 生成 | 同一checkpoint与提示 | 编号序列、停止规则、cache对照 |
| 报告 | 同一版本的上述文件 | 命令、环境、哈希、原始结果与失败 |
第一道验收:每个部件真的完成了它声称的计算
先运行数值与行为测试,再启动较长训练。注意力测试不仅检查输出形状,还用手算分数核对权重行和、未来权重为零以及值汇总;因果测试改变未来编号,要求前缀输出不变;梯度测试检查每层与每个参数张量都收到有限梯度,并选定参数用float64中心有限差分对照。这里的“收到梯度”只证明接线的一部分,优化器是否真正更新它们还需要额外检查。
有限差分的步长、精度和容差都应记录。步长过大包含明显截断误差,过小则放大相减的舍入影响;看几个步长的误差趋势,比只选一个恰好通过的数更可靠。若前向已经把整个错误目标计算得很一致,自动求导和有限差分也可能一起通过。因此还要把它们接回第二章的正确目标与第三章的手算注意力,不让“两个程序相同”替代“它们都在解决正确的问题”。
缓存测试比较同一前缀的每一步logits,覆盖单词元、多词元块、位置偏移、窗口上限与滑窗重算。保存恢复测试则比较连续训练与中途保存后继续训练:不仅看最终loss,还比较参数、优化器矩、采样排列、采样器位置、torch随机状态和逐步loss。项目额外开启dropout检查恢复,防止只在没有随机分支时偶然相等。这两类测试检验不同的状态机,不应合成一个不说明覆盖范围的绿色勾号。
读测试报告时,确认每个用例实际执行、没有被跳过,并查看失败信息和源码哈希。测试通过只覆盖已列出的输入和性质,不能推导任意数据、设备或精度都正确。一个实用的反驳步骤是故意引入已知故障,例如去掉未来遮罩、重置缓存位置、漏保存采样器状态,确认相应测试会失败,再恢复实现。这能检验测试是否有能力发现它声称检测的错误。
第二道验收:在同一套数据与参数上完成训练
参考命令使用CPU、单线程、宽度三十二、两层、四头、窗口十六、前馈宽度一百二十八、批量八。小配置降低等待成本,但注意力、前馈、嵌入、归一化和输出头的所有参数都参与优化。优化器为AdamW,学习率零点零零三、权重衰减零点零一,梯度范数裁剪到一;没有把固定注意力特征上的线性训练改名为完整Transformer训练。
.venv/bin/python -m hero_transformer.train --output-dir runs/my-model --steps 160 --batch-size 8 --block-size 16 --d-model 32 --n-heads 4 --n-layers 2 --d-ff 128 --dropout 0 --lr 0.003 --seed 7 --threads 1 --eval-every 40
训练目录会得到checkpoint.pt、metrics.json和run-manifest.json。先对照参数账本:示例词表大小三百八十五时,总参数五万零三百六十八,均可训练。然后查看从初始模型到最终模型的全训练集与验证集分数,而不是只看最后一个随机批次的loss。采样批次的难易程度会变化,所以单步下降或上升都不能直接替代同一固定集合上的前后评价。
本课程保留的一次真实参考运行中,训练集loss从5.962669下降到2.026304,验证集从5.964481下降到5.435705。二者下降幅度差异很大,提示模型对这份小训练资料的拟合远强于未见资料上的改善。这是需要解释的结果,不是应被删掉的“不好看”数字。训练曲线、文档划分和未知率一起看,才能判断下一步该改数据、表示、容量还是优化协议。
第三道验收:独立评分、生成与恢复
.venv/bin/python -m hero_transformer.evaluate --checkpoint runs/my-model/checkpoint.pt --split validation --batch-size 8 --threads 1 --output runs/my-model/validation.json
.venv/bin/python -m hero_transformer.evaluate --checkpoint runs/my-model/checkpoint.pt --split test --batch-size 8 --threads 1 --output runs/my-model/test.json
参考运行的独立测试loss为5.460169,困惑度约235.137101;测试集有二百二十八个原始字符,加三篇EOS,共二百三十一个计分目标。验证未知字符比例约13.14%,测试约17.54%。它们被折叠为UNK,评分并不保留原文全部字符身份;因此不能把这份困惑度拿去与其他分词器或开放词表大模型直接排名。具体数字只指向包内对应检查点与评分协议,不是每台机器每次运行都应得到的通用常数。
.venv/bin/python -m hero_transformer.generate --checkpoint runs/my-model/checkpoint.pt --prompt 小禾 --max-new-tokens 60 --temperature 0 --threads 1 --output runs/my-model/greedy.json
.venv/bin/python -m hero_transformer.generate --checkpoint runs/my-model/checkpoint.pt --prompt 小禾 --max-new-tokens 60 --temperature 0 --threads 1 --no-cache --output runs/my-model/greedy-reference.json
这次参考模型对提示“小禾”的贪心输出为“小禾给小禾给小禾检查步。”,随后EOS。它有明显重复,也不够通顺。保留这个样例是项目报告的一部分:模型可以正确执行完整训练与推理算法,同时仍缺少足够数据和能力。把重复样例换成手工写好的流畅句子,会把软件行为和编辑产物混在一起,使前面的验收失去意义。
.venv/bin/python -m hero_transformer.checkpoint --checkpoint runs/my-model/checkpoint.pt --output runs/my-model/checkpoint-summary.json
.venv/bin/python -m hero_transformer.train --resume runs/my-model/checkpoint.pt --output-dir runs/my-model-continued --steps 164 --threads 1 --eval-every 4
要检验这次继续是否精确,在另一独立目录用同一初始设置直接训练到一百六十四步,再比较两边状态。包内保存了这种真实CLI对照,参数最大差为零,优化器、采样器、随机状态和清单一致。这个结论只覆盖相同CPU环境、固定线程和所记录版本,不是跨GPU、跨框架版本逐位相同的承诺。前面一百六十步模型的质量表,与这里一百六十四步的恢复比较,应分别注明检查点,不拼成一份模糊的“最好模型”成绩。
第四道验收:让另一个人知道你究竟跑了什么
报告首页应写清任务、读者可复跑的命令、模型配置、数据来源与划分、环境、检查点身份以及主要结果。随后提供三类材料:正确性检查的原始输出,固定数据上的训练与评价结果,包含失败的生成样例。至少保留一个没有被优化掉的失败案例,并解释它支持什么结论、还不能支持什么。完整报告可以有失败结果;它不能有被隐藏的协议变化。
哈希用于把这些记录绑定到明确文件,不能证明算法正确或素材授权。对配置清单采用固定排序与编码再计算SHA256,可以避免仅因字典键顺序不同就得到另一身份;修改种子、词表、文档内容或架构时则应产生不同身份。模型权重、源码和数据各有自己的哈希,把一张截图的哈希称为“模型收据”没有关联作用。下例只展示清单规范化,不代替对实际项目文件的检查。
import json,hashlib
def digest(manifest):
encoded = json.dumps(manifest,sort_keys=True,ensure_ascii=False,separators=(',',':')).encode('utf-8')
return hashlib.sha256(encoded).hexdigest()
a = {'seed':7,'config':{'width':32,'layers':2},'data_hash':'example-data'}
b = {'data_hash':'example-data','config':{'layers':2,'width':32},'seed':7}
changed = {**a,'seed':8}
assert digest(a)==digest(b)
assert digest(a)!=digest(changed)
print({'manifest':digest(a),'changed_seed':digest(changed)})
| 验收线 | 必须展示的结果 | 不能替代它的材料 |
|---|---|---|
| 数学与模块 | 手算、梯度差分、因果/形状检查 | 只看输出形状或一段文字 |
| 完整训练 | 所有参数接入优化且实际更新 | 仅输出层拟合或预训练权重演示 |
| 恢复与缓存 | 同一输入/轨迹的状态与分数对照 | 只看最后文本相似 |
| 任务评价 | 冻结split、有效目标、未知率和失败 | 只报训练loss或挑选样例 |
| 可复跑交付 | 环境、命令、源码/数据/权重身份 | 没有对应文件的成绩表 |
自评时可以给推导、实现、实验和交付分别打分,但先满足不可补偿的门槛:因果性失败、标签错位、测试资料参与训练、保存恢复不一致、报告与实际检查点不对应,任何一项都应先修复,再讨论总分。课程页面的“已完成”按钮只是你在当前浏览器的阅读记录,不是自动证明通过项目验收,也不授予外部课程机构的学分或证书。
到这里,十二章形成了一条可重构的闭环:任务定义决定标签,标签决定目标,注意力与位置决定信息路径,解码块与反传决定可训练函数,训练协议决定参数轨迹,评价与生成检验最终行为。后续扩大模型或进入从零语言模型、后训练、高效推理等课程时,可以更换一个明确模块,并沿同一链条重新验收,而不必靠新的演示重新相信一遍。
TRY IT YOURSELF
练习一|梯度存在,却没有真正训练
所有参数都得到非零有限梯度,但训练后只有输出头变化。至少给出两种可能原因、每种如何定位,以及为什么“梯度检查通过”还不够。
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习二|给复跑失败定位边界
甲连续训练16步,乙训练7步后保存再恢复9步。两边模型与优化器初值相同,最终参数却不同。按由输入到输出的顺序列出应比较的中间状态,不要只建议“换一个种子”。
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习三|写一段边界正确的最终结论
参考模型训练loss显著下降、独立测试仍高、未知字符比例17.54%、生成重复,但数值与恢复检查通过。写出一段既不隐瞒进展,也不夸大能力的项目结论。