上承〔00 · 源起〕。这一篇是全系列的地基:先确定项目的意义。如今美国主流闭源大模型都到了 10 万亿参数量,开源的也到了万亿规模,Kimi K3 更是达到 2.8 万亿。那我们还要不要拆解一个 6400 万参数的模型?
一、先说说 ChatGPT
大家都知道,ChatGPT 是一个大语言模型 (LLM,Large Language Model)。但若再往深里追问它的内涵与外延,便有一大堆术语要解释,从费曼学习法的角度看,实在不友好。所以,不如先把它看成一个大黑盒:先搞清楚它怎么来的、输入输出是什么,再用一个最简类比,说清它------或者说 LLM------到底是什么。
首先,可以简单这么理解 LLM:它是一个用海量文本"喂"出来的、极其复杂的数学函数(计算机专业术语叫神经网络),这个函数上带着一大堆旋钮。你向它提供输入,通常是问问题;函数里这一大堆旋钮便开始一系列复杂运转,最后给出它认为的输出,看起来像是问题的回答。
那上千亿个旋钮的位置是谁定的?
这个问题先搁一搁。我们再凑近黑盒一步,只看输入和输出,还能发现一条稳定规律:给它的总是一段没说完的话,它回的总是接着往下说的一截,而且一小截一小截往外蹦。把这条规律说破,就是它的全部底层技术------
在海量文本上反复做一件事:盖住后面,猜下一个词。
拿一句没说完的话举例:「今天天气真___」。模型心里给词表上每个候选都打了一个分:"好"0.31,"冷"0.22,"热"0.15,"不错"0.09......6400 个候选,人人有份;然后按分数高低抽一个,比如"好",句子便成了"今天天气真好",接着再猜下一个。所谓"写一篇文章",不过就是这道题循环几百上千次。
这里插一个用词约定:我说的"词",准确讲叫 token,是模型词典里的最小单位------它比"字"碎,一个汉字可能被切开,几个汉字也可能并作一个。本篇行文为了顺口,一律叫"词"。
这套"猜词"练习有个正式名字,叫预训练 :拿海量文本当教材,把词盖住让模型猜;猜错了,就把所有旋钮各拧动一点点,再猜、再拧,如此天文数字次。GPT 这个名字,正是这套路数的缩写:G enerative,生成式,一个词一个词往外蹦;P re-trained,预训练,先海量阅读再上岗;Transformer,这套函数内部怎么接线的方案,2017 年谷歌一篇论文提出,论文名字就叫《Attention Is All You Need》。
所以回到那个问题:LLM 只是读过的文本足够多,多到把"语言接下来通常怎么说"这件事,磨进了上千亿个旋钮的位置里。这就是"它怎么来的"极简答案。至于怎么"磨",正是后面要拆解的核心。
至于 ChatGPT 多出来的那个"Chat",是在 GPT 之上又加了一轮"教它好好说话"的训练。预训练完的 GPT,像一个读完图书馆、却只会接话的人。你问它"法国首都是哪?",它未必答"巴黎",更可能接着写:"法国首都是哪?这是很多人关心的问题......"所以"Chat"这一轮,核心不是再喂知识,而是教它扮演助手。
第一轮,叫指令微调。人类写大量对话示范,格式大概是:
system: 你是一个有用的助手。
user: 法国首都是哪?
assistant: 巴黎。
训练时还是猜下一个词,但只在 assistant 该说话的位置上猜;猜错了,就拧旋钮。模型慢慢学会:看到 user,接下来应该由 assistant 回答;看到 system,要遵守规则。这一步用的数据比预训练小得多,但质量高、目的性强。
第二轮,叫偏好对齐 。同一个问题,让模型生成几个回答,人类按"有用、诚实、无害"排序。然后用这些排序训练一个"裁判模型",让它学会给回答打分。再拿裁判模型去拧 GPT 的旋钮:得分高的回答被鼓励,得分低的被抑制。这就是 RLHF ,人类反馈强化学习。后来也有 DPO 等更直接的方法,但思想一样:让模型更偏向人类喜欢的回答。
第三轮,才是产品包装。系统提示告诉它"你是 ChatGPT";对话历史被拼成一长串 token 喂进去;安全过滤拦住明显有害内容;工具接口让它能搜索、算数、写代码。多轮对话也一样,只是把前面所有轮次拼成前文,继续猜 assistant 的下一个 token。
所以 Chat 的简单公式是:
预训练:学会说话。
指令微调:学会回答问题。
偏好对齐:学会说得有用、诚实、无害。
产品包装:学会在对话框里当助手。
一句话:Chat 不是给 GPT 换了个新大脑,而是给同一个大脑做了岗前培训、礼仪培训、安全培训,再给它穿上制服、配上工牌和工具箱。
二、请 MiniMind 上台
00 篇里,MiniMind 已经亮过相:约 3500 行 Python,从 0 到 1 训出一个 LLM,词典、预训练、对话微调、偏好对齐、强化学习、蒸馏,一站不缺,连训练数据都备好了------作者把饭喂到了嘴边,从这篇起,我们开始动筷子。
先交代一句:MiniMind 有几个版本,我选的是 minimind-3。README 的版本表写得很清楚------主线当前的 Dense 模型是 minimind-3,64M 参数;旁边还有一个 minimind-3-moe,总参 198M、激活 64M;再往前是 minimind2-small,26M,2025 年 4 月的老版本。选 minimind-3,理由有三条:它是主线当前版本,作者维护最积极、文档最全;架构向 Qwen3 对齐,用的是 Transformer Decoder-Only,后面看源码时能跟主流模型对上号;26M 版虽然更小,但 00 篇那个"26M"本来就是历史版本的数,我既然要亲手验算,就从主线最新版开始,免得算完又要改。
好,先把它和 ChatGPT 摆到一张桌上:
| ChatGPT | MiniMind | |
|---|---|---|
| 参数量 | 官方止于 GPT-3 的 1750 亿,此后不再公布 | 64M(本文亲手验算) |
| 代码 | 闭源 | 约 3500 行,全部可读 |
| 训练数据 | 不公开 | 全部开源 |
跟 GPT-3 比,差三个半数量级;跟 Kimi K3 比,差四个半。README 里的说法是:主线最小版本体积约为 GPT-3 的 1/2700。量级悬殊至此,我却要先立一个判断:小,不等于另一个物种。它们走的是同一条技术路线------同一种函数、同一套练法,区别只是读书量。好比同一所学校出来的学生,一个读了万卷书,一个只扫过几行;脑筋的结构是一样的,墨水差得远。
要理解这个差距,得先弄懂参数量到底指什么。否则"差三个数量级"也只是听个响。而且 64M 这个数,我也是从 README 抄来的。抄来的不算数,自己数一遍。
三、64M 是怎么数出来的
参数,就是模型里所有可以被训练改动的数,也就是那一大堆旋钮。数法朴素得很:打开权重文件------就是那个 137MB 的文件------像账房先生点库一样,把每个零件的尺寸抄下来,做乘法,再做加法。
第一笔,词典。模型"认识"的词一共 6400 个(这份清单术语叫词表;为什么 6400 个就够用,今天先记账)。每个词在模型里不存成文字,而是对应一行 768 个数------好比给每个词发一张 768 格的身份卡,格子里填什么,决定了这个词在模型"眼"里的意味。这块零件的参数:6400 × 768 = 4,915,200。
第二笔,八层积木。词典之上,其余参数装在 8 层结构相同的积木里,可以想成 8 道流水线工序,文字的意思每过一层被加工一次。每层 11 个零件,抄下来是这张清单:
| 零件(术语叫张量,当"零件"读) | 形状 | 参数量 |
|---|---|---|
| q_proj | 768 × 768 | 589,824 |
| k_proj | 768 × 384 | 294,912 |
| v_proj | 768 × 384 | 294,912 |
| o_proj | 768 × 768 | 589,824 |
| q_norm、k_norm | 96 × 2 | 192 |
| gate、up、down | 768 × 2432,共 3 块 | 5,603,328 |
| 两个 norm | 768 × 2 | 1,536 |
| 每层合计 | 7,374,528 |
表里的"形状"就是乘法本身:768 × 768,意思是"吃进 768 个数、吐出 768 个数"的一组旋钮。零件名字今天不用懂,先混个脸熟;768、2432 这些尺寸怎么定的,也先记账。会乘法就够了。
合计。 4,915,200 + 8 × 7,374,528 + 768(收尾用的 768 个数)= 63,912,192,约 64M,和 README 对上了。HuggingFace 上的模型卡也写了同一个数:参数量 63.9M,8 层,hidden_size 768。
第三笔,文件对账。参数存成文件,实测 137,683,346 字节。账要两头对:存一个数,可以精打细算用 2 字节(术语叫 fp16,半精度------省一半地方,精度略降,存权重的常规选择;用 4 字节的 fp32 则翻倍,思考题里会再见)。63,912,192 × 2 = 127.8MB,可实测多了近 10MB,哪来的?
原来模型有个省参数的设计:词典除了负责"读入",还兼任"输出"------同一块矩阵两用,术语叫权重绑定。HuggingFace 的模型卡也确认了这一点:tie embeddings,embed_tokens 和 lm_head 共享权重。保存时这块矩阵被写了两份:68,827,392 个数 × 2 字节 = 137,654,784 字节,再加约 28KB 的文件格式开销,与实测分毫不差。每一字节都有了交代。
顺带修正 00 篇的一个数字:那里我写了"26M"------那是历史版本 minimind2-small(宽度 512)的参数量,本仓库主线 minimind-3 是 64M(宽度 768 × 8 层)。README 的版本表里,minimind2-small 的发布时间是 2025 年 4 月。文章已发在掘金,错误留在那里,这里郑重改正。往后本系列的数字,一律以亲手验算为准。
这个数代表什么?参数量是模型的容量口径,好比硬盘的标称容量:它框定了这个模型最多能装多少知识、多少语言规律;行业黑话"7B""70B",B 就是十亿参数。但有两层局限要看清。其一,标称容量不等于实际成色:同样 64M,数据好坏、练法对错,结果天差地别。HuggingFace 的模型卡也专门提醒:64M 级别的模型能力有限,主要用于学习 LLM 训练/推理全流程,不适合生产场景的复杂任务。其二,容量甚至不能完全决定行为------下一节的实验就会看到,参数一样、来源不同,"人格"都能整个换掉。
小模型还有一个好处:万亿模型的账,你只能听新闻稿念;64M 的账,你刚刚亲手算完了,一分不差。
四、实证:6400 万个数的两种人格
作者开源了两个权重文件,一个是只做过预训练的(叫它"甲"),一个是预训练之后又教过对话的(叫它"乙")。把甲乙轮流装进同一套代码------代码一行不动,只换盒里那 6400 万个数------问同一个问题。为保公平,随机种子固定,谁都可以复现(脚本:exp01_two_personas.py;输出里的 pretrain 即甲,full_sft 即乙):
代码块
💬 为什么天空是蓝色的 pretrain ?天空是蓝色的,这是由于光的折射和散射现象。在日光穿过大气层时,其散射现象 将太阳光中的各种颜色的光散射到各个方向,......但当阳光穿 ← 写满上限,断在半句 full_sft 天空之所以呈现蓝色,有以下几个关键原因: 1. 深蓝色的天空:这是因为蓝色的光线主要由蓝色颜料(如玻璃粉、水蒸气)......
💬 你有什么特长? pretrain 我是一台机器,无法真正"创造"人类,但我可以为你提供各种特长,......(70 词,停) full_sft 我是由jingyaogong创建的高效小参数AI模型,专注于提供精准、快速、简洁的交互 体验。......(59 词,答完收工)
三处细节值得盯住。
甲在续写,不在回答。 它的回答以一个孤零零的"?"开头------先接出一个问号,把标题"为什么天空是蓝色的?"补完整,再开始写正文。它眼里的任务从头到尾是"接着往下写"。
乙有身份,甲没有。 问"特长",乙一句话报出"由 jingyaogong 创建的小参数 AI 模型";甲绕到"我是一台机器,无法真正'创造'人类"上去,没有稳定的自我介绍。
甲并不胡言。 "天空是蓝色的,这是由于光的折射和散射现象"------不但通顺,甚至大体正确。它早就"会说话"了,缺的是别的东西。(顺带一记:两版的"天空"都写满了 100 词的上限才停------可见连"适可而止"都是练出来的。)
盒子没换,换的只是盒里 6400 万个数的摆法,"人格"就整个换了。一个连"人格"都能被参数解释的小模型,当教具是够格的。
五、从这个小家伙身上,能学到什么
回到开头的面子问题,正面回答。这套笔记说的"把玩",我给自己定了个意思:先看现象,再亲手算,最后对照源码。光读代码不算。按这个标准,小模型有三个好处。
第一,原理一比一可迁移。万亿模型的每个组件,64M 里一个不少。在小模型上弄懂的东西,到大模型上长得一模一样,只是旋钮更多。小是尺寸,不是物种。
第二,全程可复现。万亿模型你永远只能隔着网页用;64M 你能从词典到对齐全程亲手跑一遍。137MB 的文件,我的 Mac 上每秒能吐几十上百个词;训练环节,作者口径单张消费级显卡就能完成,这套笔记后面也会真的跑。知其所以然的唯一途径,是复现。
第三,代码读得完。训练词典是 train_tokenizer.py,预训练是 train_pretrain.py,对话微调是 train_full_sft.py,偏好对齐是 train_dpo.py......打开仓库,流水线的每一环都对得上号。核心约 3500 行------同等流程的项目动辄十万行起步。论文只给骨架,这里连血管都有。
还有第四个理由藏在方法论里:读一遍代码不算把玩。 本系列每篇固定三步------先看现象,再亲手算,最后对照源码;另配一套逐课动手的 notebook,每个机制先从零手写一遍,再对照作者的写法。
六、本篇概念清单
按 00 篇的约定,尽量不引入没讲过的术语。本篇用到的名词,清点如下:
| 概念 | 本篇交代到什么程度 |
|---|---|
| 函数 / 旋钮(参数) | 讲透:是什么、怎么数、64M 的账、文件怎么对 |
| 猜下一个词(预训练) | 讲透到直觉级:打分、抽一个、循环、猜错拧旋钮 |
| fp16 / fp32 | 讲清:一个数占 2 字节还是 4 字节 |
| 大语言模型(LLM)、神经网络 | 黑盒级定义,够用不展开 |
| 词表、token、结束符 | 借用了,没展开 |
| Transformer、架构 | 只点了个名:函数内部的接线方式 |
| 权重绑定 | 点名:词典两用,对账的钥匙 |
| 对话微调、偏好对齐、强化学习、蒸馏 | 只点了个名 |
| 张量 | 术语登记,当"零件"读 |
看完本篇,我们只需要先建立这样的心智模型:函数、参数、猜下一个词。其余的概念我们后面再一一拆解。
回到标题的问题:一个 64M 的模型,凭什么像 ChatGPT?凭的是同宗同源------同一种函数、同一套练法;不像的地方,只是读书量少了几万倍。 不过,这反而是好事:用书少的那个,一个人才翻得完。
留两道思考题,只用本篇的东西就够:
- 若把词表从 6400 扩到 12800(其余不变,权重绑定照旧),参数量变成多少?文件大约多大?
- 同样的参数改用 fp32(4 字节)保存,文件多大?为什么存权重一般不用 fp32?