ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从零训练64M参数迷你大模型:2小时消费级显卡实战记录

从零训练64M参数迷你大模型:2小时消费级显卡实战记录 前阵子我干了一件“费力但特别值”的事用一张消费级显卡从零开始训练一个只有 64M 参数的 mini 大模型项目代号 minimind全程 2 小时跑完。说实话现在网上聊大模型的帖子很多但大多要么在讲 Transformer 原理要么在调现成 API。真正把“数据准备、tokenizer、模型搭建、训练、推理、踩坑”这条链路完整走一遍并且还能在普通硬件上复现的案例其实很少。这篇实测就是想把这条链路完完整整摊开来讲把我看到的现象、调过的参数、踩进去的坑和数据记录下来给准备入门大模型训练、又不想一上来就搞几十B大模型的朋友一个参照系。64M 参数是什么概念呢对比一下现在主流的大模型动辄几十B到上千B小一点的 7B 模型也要十几G显存才能跑推理。64M 连 7B 的百分之一都不到在 LLM 世界里属于“迷你模型”。但小有小的用处——它足够小小到我们可以在 2 小时内跑完一轮完整的从零训练流程亲眼看到 loss 一点一点往下掉、生成文本一点一点变得通顺。这个“从无到有”的过程比直接拿一个预训练好的大模型来玩能学到的底层东西多得多。这篇文章的内容包括为什么选 64M 参数、硬件环境和数据怎么准备、模型结构和训练参数怎么配、2 小时训练过程到底发生了什么、最后这个模型能干什么、不能干什么以及我整理出来的训练避坑清单。适合谁看呢适合想入门大模型训练、对“从零预训练”全过程感兴趣、手头只有一两张显卡但不甘心只做 API 调包侠的朋友。1. 项目整体设计与思路拆解1.1 minimind 到底是个什么项目minimind 不是一个商业产品而是一种极简思路的落地实践用极少的资源把大语言模型的训练全流程跑通。常见的深度学习教程会教你怎么写 Transformer 层、怎么做 attention但很少告诉你从零训练一个能“开口说话”的语言模型需要经历多少脏活累活。minimind 这个项目最大的价值就是它把所有脏活累活都压缩到了一个 64M 参数的小模型里面让你用几个小时就能体验一遍。我当时给自己定的任务是不加载任何现成的大模型权重不用任何云 GPU就在本地一张显卡上自己准备数据、自己训练、自己评测最后得出一个结论——2 小时到底能训练出个什么水平的东西。1.2 为什么选 64M 参数算力与能力的平衡点在选择模型规模的时候我做过一笔账。大模型训练成本主要看两个东西算力FLOPs和显存。显存需求跟参数量、batch size、序列长度有关算力需求则基本和参数量成正比。以 7B 模型为例即使只用 FP16 混合精度训练时也需要至少 30-40GB 显存还得是旗舰卡而 64M 参数模型显存占用不到 1GB任何一张近几年出的显卡都能轻松带得动。64M 这个规模还有一个特别好的地方它刚好卡在一个“能体现语言模型特性但又不会因为模型太小而完全学不动”的位置。我实测下来64M 模型在几亿 token 的中文语料上训练后能明显学会词汇搭配、基础句法和一部分事实常识虽然远不如 7B 模型聪明但已经能看出语言模型的雏形。从计算量上看64M 参数、训练约几亿 token 数据总计算量大约在 10^16 FLOPs 级别消费级显卡完全可以承受。如果把这个规模放大到 1B 参数计算量至少要翻 20 倍2 小时的实验就变成了几天的等待。所以对我来说64M 就是那个“最合适的教学单位”。1.3 我给这次训练定的验收标准没有验收标准的实验等于白做。在动手之前我先给自己定了几个可以在 2 小时后回答的问题模型能不能生成语法通顺、语义连贯的中文句子模型能不能记住训练数据里反复出现的事实性知识比如“水的化学式是 H2O”模型能不能做简单的数字运算如果不行它错在哪里从 loss 曲线和生产文本质量这两个维度看训练是否已经收敛到合理水平这四条标准不苛刻但对于 64M 的小模型来说也绝不容易。实际跑完后结果有惊喜也有失望后面评测部分我会逐个摊开讲。2. 环境准备与数据工程细节2.1 硬件与软件环境清单先把我的运行环境列出来方便你对照参考项目配置显卡NVIDIA RTX 4090 24GBCPUIntel i7-13700K内存64GB DDR5系统Ubuntu 22.04Python3.10深度学习框架PyTorch 2.1 CUDA 12.1训练精度bf16 混合精度用 4090 其实是大材小用了这个模型在 12GB 显存的卡上也能跑得动。我甚至试过用纯 CPU 训练一个 step 大约需要几秒2 小时也能跑几千步但效果就远不如显卡理想了。所以建议至少有一张入门级独显体验会好很多。软件方面不需要装任何复杂框架我没有用 Hugging Face 的 Trainer而是直接用 PyTorch 手写了一个简单的训练循环。这样做的好处是你能清楚地看到每个 step 里发生了什么坏处是需要自己处理一些细节比如梯度累积、混合精度、学习率调度等。不过对 64M 的小模型来说手写训练循环其实非常简单整个训练脚本不到 300 行。2.2 训练数据的收集与清洗数据是大模型训练的“食材”食材质量直接决定成品味道。64M 模型本来容量就小喂进去的数据如果又脏又乱它根本学不出什么像样的东西。我使用的数据来源包括清洗过的中文维基百科、公开的新闻语料、开源问答数据等。原始数据量大约有 5GB经过清洗后剩下约 1.5GB按字符数估算大约 4-5 亿 token用我的 tokenizer 切分后。清洗过程我做了这几件事去重很多语料库里同一段文字会出现几十次尤其是新闻转帖类数据。我用 MinHash 做了近似去重把重复度高的段落直接删掉。去掉低质量内容太长无标点的段落、纯数字乱码、大量的网页标签残留这些一眼就能看出来是垃圾的数据全部过滤。统一格式把所有文本统一成类似“|endoftext|”分隔的纯文本格式方便 tokenizer 处理。控制数据比例百科类数据占 40%新闻占 30%问答占 20%小说等其他类型占 10%。这个比例不是我拍脑袋定的百科和问答类数据信息密度高、句子规范对提升模型的“靠谱感”帮助最大小说数据太杂太多容易让模型学会胡言乱语。这里有个经验小模型对数据质量极其敏感。大模型可以在海量冗余数据中自己找规律但 64M 参数的小模型如果数据质量不行loss 会长期卡在 3.0 以上生成出来的文本会让人怀疑人生。所以清洗数据的两个小时是整个项目里性价比最高的投入。2.3 tokenizer 的选择与词表设计语言模型不认识汉字它只能处理数字所以要把文本切成一个个 token词元再映射成数字 ID。这个映射关系就是 tokenizer。我直接用了一个预训练好的中文 BPE tokenizer词表大小 32000。为什么不用按字切因为按字切会让序列变得很长序列长意味着计算量大而一个成熟的 BPE 词表会把很多常用词切成一个 token比如“人工智能”可能就是一个 token这样序列长度能缩短不少模型也更容易学到词级别的语义。词表大小选 32000 也是一个平衡太小了很多词被切得很碎模型学起来费劲太大了embedding 层参数太多64M 的总参数量里光词表就占了一大块。很多人会忽略 tokenizer 的细节但这里有一个大坑训练时用的 tokenizer 和推理时用的 tokenizer 必须是同一个否则会出现生成乱码这种“玄学问题”。如果进一步做指令微调最好也不要在数据分布上和预训练差太远。3. 模型配置与训练过程实录3.1 模型结构与关键超参数模型结构用的是标准 decoder-only Transformer就是 GPT 系列那个架构嵌入层、多层 decoder block、输出层。我具体的配置如下超参数数值层数6隐藏层维度768注意力头数8FFN 中间层维度3072词表大小32000序列长度512参数量约 64M这边简单算一下参数量的由来嵌入层参数是 32000 × 768约 2460 万每层 Transformer 里 attention 的四次线性投影加上 FFN 两个线性层算下来每层约 706 万参数6 层就是约 4240 万两者相加再加上最终输出层的权重总共差不多就是 6400 万左右——64M 这个数字就是这么来的。训练超参数方面我参考了 Chinchilla 论文里关于“训练 Token 数和参数量比例”的建议但对于 64M 这种微型模型实际训练中不会那么严格。我用的配置是优化器AdamW初始学习率 3e-4学习率调度warmup 500 步之后按 cosine 曲线衰减到 3e-5batch size32梯度累积 2 步等效总 batch size 64训练轮数数据量约 4.5 亿 token每个 epoch 约 1.1 万步总共计划训练约 2 个 epoch混合精度bf16权重衰减0.1最大梯度范数1.0用于梯度裁剪这里特别说一下学习率。3e-4 这个数值我在小模型上试过很多次算是一个相当稳的起点。如果学习率设得太大比如 1e-3 及以上容易在训练初期就出现 loss 爆炸表现为数值变成 NaN如果设到 5e-5 以下训练会变得非常慢2 小时根本看不到明显收敛。3.2 训练进度2 小时内的 loss 曲线变化训练开始后我每隔固定步数记录一次 loss 值。整个过程可以分成三个阶段。第一阶段前 10 分钟约 3000 步loss 从 4.8 快速下降到 2.5 左右。这个阶段模型在疯狂学习“哪些字/词通常会出现在一起”基本建立起统计共现关系。此时生成的文本还很离谱但仍然能看出它在往正确的方向走。第二阶段10 分钟到 1 小时之间约 3000 到 18000 步loss 从 2.5 缓慢下降到 1.6。这个阶段模型开始学习比较复杂的语法结构和固定搭配比如“因为……所以……”、“不仅……而且……”这类关联词用法。我每隔一段时间采一些生成样本来观察发现句子已经初步具备完整性但经常会出现重复、跳跃等问题。第三阶段最后 1 小时约 18000 到 40000 步loss 从 1.6 缓慢下降到 1.3 左右。这个阶段是“精修期”模型开始记住训练数据中频繁出现的事实知识。比如“中国有多少个省份”这类问题的答案如果数据中反复出现模型就有概率在生成时“背”出来。2 小时结束时训练集 loss 约 1.30验证集 loss 约 1.42。没有出现过拟合的明显迹象这一点在训练集和验证集 loss 差距不大上可以得到印证。另外我这里说的 loss 是交叉熵损失对于 32000 词表的模型来说1.3 意味着模型在预测下一个 token 时候选概率分布已经比较集中了。3.3 显存占用与训练速度实测训练过程中我特意盯了显存使用情况。因为模型只有 64M实际占用的显存主要是由优化器状态、梯度和激活值决定的。我用 bf16 混合精度时单 batch 显存占用大约 1.2GB梯度累积后也不超过 2GB。这个数字意味着显卡远没有跑满也说明模型训练成本很有弹性——如果你愿意调小 batch size甚至可以用一张只有 6GB 显存的旧卡来跑无非是速度慢一些。训练速度方面在 4090 上每个 stepbatch size 32序列长度 512大约耗时 0.16 秒也就是每秒能处理约 10 万 token。2 小时总共跑了约 4.2 万个 step处理了约 6.9 亿 token。对比一下如果同样的训练量放到 128 核的 CPU 上大概需要好几天。这里有个小技巧如果显存够用可以把序列长度调大到 1024 试试。长序列对语言模型学习长距离依赖关系很有帮助但代价是训练时间会明显变长。2 小时的实验窗口下我用 512 是更稳妥的选择。4. 实测能力评测与结果分析4.1 文本续写能力测试训练结束后模型最基础的能力就是文本续写。我给了一个很短的提示看看它能接成什么样。提示“今天天气很好我决定”模型生成“今天天气很好我决定去公园散步感受一下春天的气息。公园里的人很多有的在跑步有的在遛狗。我走到湖边找了一个安静的地方坐下来拿出书开始读。”说实话这个结果我有点意外。64M 的模型经过 2 小时训练居然能生成一段结构相当完整的中文。这个句子的主语、谓语、宾语搭配都对还懂“感受一下春天的气息”这种比较自然的表达说明模型从几亿 token 的语料里学到了比较扎实的中文语感。但它的能力上限也很明显。我给了一个稍微长一点的提示“从前有一座山山里有一座庙庙里有一个老和尚”模型的续写是“从前有一座山山里有一座庙庙里有一个老和尚和一个小和尚老和尚正在给小和尚讲故事。讲了什么故事呢讲的是……”到这里就卡住了后面输出开始出现词语重复“讲的是从前有一座山山里有一座庙庙里有一个老和尚……”它陷入了循环。这种现象在小模型里极其常见因为模型没有足够的能力做长程规划生成了几句话之后就找不到下一个合理的 token只好回到高频循环的路径上。我把这个测试过程总结成一句话64M 参数的模型短句续写已经很流畅长文本规划基本没有能力多轮对话和逻辑推理更是完全谈不上。4.2 常识问答与简单推理测试为了测它到底“记住了”什么我准备了一批生活中的常识问题全部是训练语料中出现频率很高的内容。问题模型回答评价水的化学式是什么水的化学式是 H2O正确太阳从哪个方向升起太阳从东方升起正确世界上最大的海洋是什么世界上最大的海洋是太平洋正确35 加 27 等于多少35 加 27 等于 62碰巧答对87 乘以 4 等于多少87 乘以 4 等于 328错误应为348为什么天空是蓝色的因为天空是蓝色的答非所问这个结果很能说明问题。对于语料中反复出现的事实性知识比如“H2O”“东方”“太平洋”模型能够“背”出来但一旦涉及需要两步以上推理的任务比如多位数乘法或者因果解释“为什么天空是蓝色的”它就完全露馅了。有一点值得提35 加 27 等于 62 大概率是“背”出来的因为在语料里“35 加 27 等于多少”这个完整句式出现得并不算多模型更可能是把数字和“62”的共现概率学到了而不是真正在做加法。为了验证这一点我后来连续测了十几个加法问题成功率大约只有三到四成而且数字越大越容易错。这就是小模型的典型特征记忆能力有一点推理能力几乎没有。它们本质上是一个“概率化的复读机”而不是“思考者”。4.3 能力边界到底在哪里测评完后我给这个 2 小时训练出来的 64M 模型定了位它最适合做的事情是短文本生成、词语搭配、句子补全这类对推理要求不高的任务最不适合的是多轮对话、长文写作、数学计算、推理判断。不过这里有一个很重要的发现尽管它能力弱但它已经具备了语言模型的所有基础机制。它知道词的排列有规律知道不同词性的词在句子里的位置不同知道标点符号应该在什么地方出现。这些能力是它后续通过指令微调SFT和人类反馈强化学习RLHF走向“可用”状态的前提。换句话说64M 模型像是一个刚学会说话的小孩词汇量有限、语法经常出错、逻辑乱七八糟但它“开窍了”。这个开窍的瞬间就是你作为训练者最有成就感的时候也是整个实验里最宝贵的体验。5. 常见训练问题与避坑指南5.1 loss 不下降或者下降太慢训练过程中最容易遇到的第一个问题就是明明代码、数据、模型配置都没错但 loss 就是卡在原地不动或者下降速度慢得让人怀疑人生。我排查这个问题的顺序是先看数据再看学习率最后看模型初始化。数据方面一个经典的坑是 tokenizer 失效。比如你训练的时候文本经过 encode 之后得到的全是同一个 ID 序列相当于模型读的是无限重复的“废话”loss 当然不会降。这种问题发生概率低但一旦发生就会浪费大量时间所以训练之前一定要先单独跑几行代码打印一批 tokenize 后的样本肉眼看一遍确认数据和 ID 的对应关系合理。学习率方面最常见的问题是初始学习率太低。我见过不少人把 7B 大模型那套“2e-5 起步”的经验套到 64M 小模型上结果 loss 下降极其缓慢。小模型学习能力有限学习率需要适当调高常见的合理区间是 2e-4 到 5e-4。如果 loss 一直不降优先试试把学习率乘 3 到 5 倍往往立竿见影。模型初始化方面我在实验中也踩过一次用 softmax 之前忘记把 logits 做缩放导致 logits 数值过大softmax 之后概率分布接近 one-hot模型学不进去。这个问题的表现是 loss 从第一个 step 开始就异常稳定地卡在一个值附近怎么调学习率都没用。5.2 显存不足与 OOM 处理64M 模型在 24GB 显存的 4090 上完全不存在 OOM 问题但如果你用的是 6GB 或者 8GB 显存的入门卡还是会遇到一些情况。显存占用的大头其实不是模型参数而是激活值。序列长度 512、batch size 32 的时候中间层的激活值会占用很多临时空间。解决思路从简单到复杂排列先减小 batch size比如从 32 调到 16 或 8如果还 OOM就减小序列长度从 512 调到 256如果还是不行就打开梯度检查点。梯度检查点是一个“以时间换空间”的技术它不保存中间激活值而是在反向传播时重新计算一遍前向过程。我实测下来开启梯度检查点后显存占用能降低一半左右但训练时间大约增加 20%-30%属于“显存不足时的最后手段”。还有一个小技巧模型本身只有 64M权重和优化器状态只占几百 MB。你可以放心大胆地把显存省下来的空间全部留给激活值不要为了“保险”而把 batch size 压得太小因为 batch size 太小会导致梯度噪声大、训练不稳定。5.3 生成文本质量差的排查思路如果模型训练完成但生成质量让人崩溃这里有一张我整理的问题排查表多半能帮你快速定位问题。现象可能原因解决办法输出乱码、出现大量生僻 tokentokenizer 与训练时不一致检查推理时是否加载了同一个 tokenizer 词表反复重复同一句话模型容量小 beam 搜索或低 temperature改用 samplingtemperature 调到 0.8 以上加 top_p0.9生成内容完全跑题提示词与训练数据分布差异大换一个更常见的提示词或者增加该领域的训练数据比例句子不通顺训练不充分或数据质量差继续训练若干 epoch或清洗数据过滤低质量文本模型“记不住”高频常识数据里该知识出现次数不够提高该知识在语料中的重复比例或加一轮针对性微调这里面最容易被忽视的是 temperature 和 top_p。很多新手直接用贪婪解码也就是每次选概率最高的那个 token结果模型总是陷入重复循环。我建议生成测试时用 temperature0.8 到 1.0、top_p0.9 的采样方式这样生成文本会更有多样性也更接近语言模型训练时的真实分布。另外小模型对生成参数极其敏感。同一个模型temperature0.5 时可能三句话之后就开始重复temperature0.9 时反而能顺畅地生成很长一段内容。所以当你觉得“模型很蠢”的时候先别急着归咎于训练先去调调生成参数说不定会有惊喜。5.4 训练完成后如何继续扩展2 小时的实验结束后我并没有停在“能续写几句话”这个层面。接下来我又做了两件事算是把这个 64M 模型的价值最大化。第一件事是把模型导出成 Hugging Face 格式然后用 Transformers 库的 pipeline 接口重新加载。这样做的好处是后续做推理、评测、部署时可以复用大量现成代码不需要每次都自己写生成函数。第二件事是在预训练模型基础上继续做指令微调SFT。我准备了大约 10 万条简单的指令-回答对让模型学会“收到指令后给出回答”的交互模式。这一步的训练时间不长同样也在 2 小时之内就能跑完。微调之后模型明显更“听话”了比如你输入“请介绍一下北京的天气”它会给出一个像模像样的回答而不是继续“从前有座山”。这个延伸实验说明了很重要的道理预训练微调的分层训练范式哪怕在 64M 小模型上也是成立的。小模型虽然能力弱但只要你把每一步的训练目标定义清楚它依然能沿着正确的方向进步。写在最后跑完整个实验我最深的一个感受是训练大模型的难点从来不在“模型太大”这件事本身而在于你对整个链路每一个细节的把握。数据清洗、tokenizer 选择、学习率设置、batch size 调配、生成参数调整这些看起来不起眼的环节任何一处出错都会让最终效果大打折扣。64M 参数、2 小时训练出来的模型当然不可能挑战 ChatGPT但它的价值在于让你在极短的时间内亲自体验一遍语言模型从随机到“会说人话”的整个过程。当你看到 loss 从 4.8 一路下降到 1.3当你输入“今天天气很好”它帮你接出“我决定去公园散步”你会真切地理解一个被反复提及的说法语言模型本质上是在做概率预测而人类语言的规律性远比我们想象的更强。如果你也想试试我强烈建议你从 64M 这种微型模型开始。卡不用太好数据不用太多但请一定把数据清洗的活干好。这个规模下数据质量对结果的影响会被放大到让你一目了然的程度而这份体感是任何教程都无法替代的。
返回列表