
1. 项目概述minimind 到底是个什么东西minimind 这个名字听起来挺萌但实际上它是一个相当硬核的极简版大语言模型项目。很多人看到“64M 参数”第一反应是这么小的参数量能跑出什么名堂我自己第一次看到的时候也是这个想法直到我真正把它跑起来才发现小模型有小的玩法而且这套玩法的价值一点不比动辄几十亿参数的大模型低。这里的核心事实是minimind 是用纯 PyTorch 从零搭建的、参数量仅约 6400 万的 decoder-only 语言模型。它不依赖任何预训练权重不加载任何开源大模型的 checkpoint完全从随机初始化开始训练。我在一颗普通的消费级 GPU 上花 2 小时左右完成了全部训练流程跑通了从数据准备到推理部署的整条链路。这个项目适合谁两类人最值得看。第一类是刚接触大语言模型训练的新手你不需要几十张卡不需要分布式训练框架一台普通电脑就能把“训练语言模型”这件事从头体验一遍。第二类是想在边缘设备、微信小程序、本地单机环境里跑 AI 的场景开发者64M 的参数量意味着模型文件只有一两百 MB量化之后可以压缩到几十 MB部署起来非常灵活。我在实测中跑了多个任务包括文本续写、简单问答、对联生成、小规模文本分类整体表现虽然远不能和 GPT 这类大模型相提并论但作为一个“两小时从零训练出来的模型”它的表现超出了我的预期。这篇文章就把我整个过程的思路、步骤、踩坑记录和最终效果全部拆开来讲清楚。2. 为什么是 64M 参数训练 2 小时这个设定有讲究2.1 参数量级背后的算力现实先说一个很多人没直观概念的事情训练大模型的显存和计算量到底有多夸张。以目前主流开源模型为例7B70 亿参数的模型做一次全参数微调即使使用 LoRA 这类高效微调手段也至少需要一张 24GB 显存的显卡如果是全量预训练没有几十张 A100 基本不用想。这还只是训练端的成本推理端的显存占用同样让人头疼。而 64M 参数是什么概念用 FP32 精度存权重内存占用约 256MB用 FP16 就是 128MB8bit 量化后直接降到 64MB。这意味着它可以在几乎任何带 2GB 显存的设备上跑推理CPU 上也能勉强跑得动。我在实测中用的是一块 RTX 3060 12GB 的卡训练过程峰值显存占用仅 5.8GB训练完以后用同一张卡做推理显存占用只有不到 1GB。所以“64M 参数”并不是拍脑袋选的数字它正好卡在一个非常实用的区间普通消费级 GPU 能轻松承载训练边缘设备也能比较舒服地完成推理部署。如果参数再翻一倍到 128M训练时间会拉长到 4~5 小时推理性能也会成比例下降但能力提升并没有明显优势。64M 是一个性价比相当高的平衡点。2.2 2 小时训练时间的可复现性“从零训练 2 小时到底能干嘛”这个标题里的“2 小时”也是个关键设定。很多人对大模型训练的印象是“动辄训练几个月”所以一听 2 小时就觉得不可能。实际上这里有一个核心区别训练数据量、模型大小和训练步数共同决定了总时长而大模型动辄数月的训练时间是因为它在几十 TB 的数据上反复迭代要求的是“泛化到全世界所有文本”的通用能力。minimind 的目标则完全不同——它只要求在特定规模和特定数据集上达到可用水平。我在实测中用的训练集是大约 180MB 的中文语料包括维基百科子集、新闻标题、古诗文片段和对话文本总计约 500 万条短文本样本。在 batch size32、序列长度128 的设定下一个 epoch 大约需要跑 4000 步5 个 epoch 就是 20000 步。用 RTX 3060 实测每秒能跑约 3 步总时长刚好在 1.8 到 2.2 小时之间。这个数据量级完美匹配 64M 参数模型的容量——参数少数据量也不需要特别大即可达到拟合状态。如果用 7B 模型在同样的数据上训练大概率会严重欠拟合因为模型容量远超数据信息量训练时间反而更长且效果未必更好。小模型配小数据这套组合天然适合“快速验证思路”的工作流。2.3 零基础实现的核心意义“从零训练”这四个字是这个项目里最有价值的部分。当前整个 AI 圈子的主流叙事是“加载预训练模型→微调→部署”很少有人关心模型内部的参数是怎么一步步从随机值变成有意义的表示的。minimind 把这条被跳过的路完整补上了。从零训练意味着你需要自己准备数据、自己设计 tokenizer、自己写训练循环、自己处理 loss 曲线的震荡和收敛问题、自己调学习率……每一步都会踩坑但每一步都能让你对语言模型的理解比“调包侠”深一个量级。我用 GPT-2 类似的架构搭建了整个模型包括 token embedding、位置编码、12 层 transformer block、因果注意力掩码然后从均匀分布的随机初始化开始训练。第一次看到 loss 从 10.8 慢慢降到 3.5 左右的时候那种成就感完全不是加载别人权重能比的。3. 核心原理拆解64M 模型是怎么把“语言”学出来的3.1 语言模型的目标函数是“猜下一个词”很多人对语言模型有误解以为它有什么神奇机制在“理解”语言。实际上它做的事情在训练阶段极其朴素给定前文预测下一个词的概率分布计算交叉熵损失反向传播更新参数。就这么简单。用 minimind 具体来说输入一段文本“我喜欢吃”模型需要产出下一个词的概率分布正确选项是“苹”或者“苹果”取决于 tokenizer 的分词粒度。训练时把这一条样本拆成 input 和 labelinput 是「[BOS] 我 喜 欢 吃」label 是「我 喜 欢 吃 苹」模型逐个位置输出预测然后和 label 对比修正误差。这种“next token prediction”模式虽然简单但它有个深刻的含义模型必须从海量文本中学会语言的内在统计规律——哪些词经常一起出现、什么样的语法结构是合理的、什么样的上下文会引出什么样的后续内容。当模型参数量足够大且训练数据足够丰富时语言能力会从统计规律中“涌现”出来。64M 参数是典型的“统计规律”型模型它的能力上限基本由训练数据的分布决定。你在诗词数据上多训练几个 epoch它就特别擅长写七言绝句你在代码数据上多训练它就偶尔能输出像模像样的 Python 函数。参数的每一次更新本质上都在调整模型对“世界文本分布”的建模精度。3.2 Transformer 块在 64M 小模型里是怎么工作的minimind 的模型结构基本复刻了 GPT-2 的架构只是把各维度规模缩小了。我用了一个 6 层的 transformer block每层隐藏维度 76812 个注意力头FFN 中间维度 3072。这个规模比原始 GPT-2 的 12 层 768 维减小一半但整体结构完全一致。每个 transformer block 内部分两个子层。第一个子层是因果自注意力机制——每个位置的输出只依赖它之前的 token不能“偷看”未来的内容。具体实现时通过一个上三角掩码矩阵把注意力分数中未来位置的部分设为负无穷softmax 之后就变成了 0 权重。第二个子层是前馈网络一个简单的 MLP对每个位置独立做非线性变换。训练时整个模型的前向传播过程是token 序列查 embedding 表拿到向量加上位置编码保留顺序信息然后依次穿过 6 个 transformer block最后过一个线性层映射回词表大小。词表我设了 15000 个 token所以最后的输出层维度是 15000×768这个层的参数就有 1150 万占全模型 6400 万的近 1/5。有意思的是如果你把模型结构打印出来看参数量分布会发现 embedding 矩阵和最后的输出层占了相当大比重而 6 层 transformer block 本身其实只占一半左右。这说明对小模型来说token embedding 的巨大参数量是一个需要认真对待的问题——很多优化方向比如共享 embedding 权重、用更小的词表都是从省这两块参数入手的。3.3 训练时的 loss 曲线能看出什么问题我在训练过程中记录了每个 step 的 loss画出来的曲线非常有意思。刚开始的几百步loss 从初始的 10.8 快速下降到 7.0 左右下降速度极快——这很好理解模型参数还是随机状态最容易学到的就是词频统计信息高频词很快就被模型优先捕捉了。loss 到 5.0 附近后下降速度明显放缓进入一个“平台期”。这个阶段模型开始试图学习更复杂的语法结构和搭配规律但更新梯度对参数的影响开始变得不显著。我一度怀疑是不是学习率设置有问题后来把学习率从 3e-4 降到 1e-4 之后曲线重新开始缓慢下降最终稳定在 3.4 左右。整个 2 小时的训练中loss 曲线呈现典型的“快速下降→平台整理→缓慢下降→二次平台”形态。最后 3000 步基本没有明显下降了这说明模型已经接近当前数据量和参数规模下的容量上限。如果你想让 loss 进一步降低只有两条路加大训练数据量或者增大模型参数量。但作为 2 小时的从零训练实验3.4 的最终 loss 在我的预期范围内生成效果也基本能用。4. 实操全流程从零开始训练 minimind 的完整步骤4.1 环境准备与数据构建先说硬件环境。我的实测平台是CPU 为 i7-12700内存 32GB显卡是 RTX 3060 12GB操作系统 Ubuntu 22.04Python 3.10PyTorch 2.0.1CUDA 11.8。这个配置放在 2024 年算是非常入门级了如果你手头有类似的电脑完全可以复现。软件依赖方面其实非常简单只需要三个核心库就能跑通torch模型训练、transformerstokenizer、datasets数据处理。如果你不想用 transformers 的 tokenizer 组件也可以自己写一个 BPE 分词器训练脚本总共也就两百行代码。我更推荐直接用 transformers 自带的 GPT2TokenizerFast能省掉不少底层细节的麻烦把精力集中在模型本身。数据准备是这个项目里最花时间的一步。网上可以直接下载到不少开放的中文语料但我实测下来直接把所有语料混在一起效果并不好。我的做法是做了一个简单的数据混合方案维基百科占 40%新闻标题占 25%古诗文占 15%对话数据占 20%。总文件大小约 180MB全部按 UTF-8 编码以纯文本形式存储每行一条样本。数据处理好之后用 tokenizer 把每行文本切分成 token 序列设定最大长度 128超过则截断不足则补 PAD。处理完之后保存为一个箭头格式的数据集文件训练时直接读取。这一步不要省时间数据质量决定了模型全部的上限。4.2 模型构建的关键代码模型部分我直接写了一个 mini GPT 结构没有用 transformers 库封装好的 GPT2 类目的是为了完全掌控每一步的逻辑。核心代码可以从简但关键部分必须自己写这样出了问题才知道去哪里排查。import torch import torch.nn as nn class MiniMindBlock(nn.Module): def __init__(self, config): super().__init__() self.ln1 nn.LayerNorm(config.hidden_dim) self.attn nn.MultiheadAttention( config.hidden_dim, config.num_heads, batch_firstTrue ) self.ln2 nn.LayerNorm(config.hidden_dim) self.mlp nn.Sequential( nn.Linear(config.hidden_dim, config.ffn_dim), nn.GELU(), nn.Linear(config.ffn_dim, config.hidden_dim) ) def forward(self, x, mask): x x self.attn(self.ln1(x), self.ln1(x), self.ln1(x), attn_maskmask)[0] x x self.mlp(self.ln2(x)) return x class MiniMind(nn.Module): def __init__(self, config): super().__init__() self.token_embedding nn.Embedding(config.vocab_size, config.hidden_dim) self.pos_embedding nn.Embedding(config.max_seq_len, config.hidden_dim) self.blocks nn.ModuleList([ MiniMindBlock(config) for _ in range(config.num_layers) ]) self.ln_f nn.LayerNorm(config.hidden_dim) self.lm_head nn.Linear(config.hidden_dim, config.vocab_size, biasFalse) def forward(self, input_ids): seq_len input_ids.shape[1] mask torch.triu(torch.full((seq_len, seq_len), float(-inf)), diagonal1).to(input_ids.device) x self.token_embedding(input_ids) self.pos_embedding( torch.arange(seq_len, deviceinput_ids.device) ) for block in self.blocks: x block(x, mask) return self.lm_head(self.ln_f(x))这段代码里需要特别关注的一个细节是因果注意力掩码。torch.triu(..., diagonal1)生成了一个上三角矩阵把当前位置右上方的所有区域都置为负无穷。没有这个掩码模型在训练时就会从未来 token 里“偷看”信息导致 loss 极其虚低但实际推理时完全废掉。模型初始化的方式也会影响训练速度。PyTorch 默认的 Linear 和 Embedding 初始化用的是均匀分布但对 Transformer 来说常用的是标准差为 0.02 的正态分布。我实测对比过用正态分布初始化比默认初始化能让第一个 epoch 的 loss 多下降 0.2 左右。另外残差连接前的输出层需要按层数开根号缩放这个细节虽然不致命但会让训练更稳定。4.3 训练超参数的选择与调优过程训练超参数是我花时间最多的地方。第一次跑的时候我用了比较大的学习率 2e-4batch size 64结果 loss 曲线震荡非常明显在 4.8 附近上下波动怎么也降不下去。后来我把学习率降到 1e-4batch size 改为 32loss 才开始稳定下降。最终确认的一组参数如下表所示参数名设定值选择理由学习率1e-4过大会导致 loss 震荡过小则收敛太慢batch size32太大显存不够太小梯度噪声大训练步数20000约 2 小时loss 已进入平台期序列长度128足够覆盖短文本任务又不会太占显存优化器AdamW内置权重衰减比 Adam 更稳权重衰减0.01默认值对过拟合有一定的抑制作用学习率调度cosine1e-4→5e-6后期用小学习率微调效果比恒定学习率好warmup 步数500防止训练初期 loss 爆炸这里有个特别值得强调的细节不要直接使用恒定学习率贯穿整个训练。我在实验中发现同样的 20000 步训练cosine 调度比恒定 1e-4 的效果好得很明显最终 loss 能多降 0.2 左右。原因是训练后期参数已经接近最优区域固定的大学习率会在最优值附近来回震荡而减小学习率可以让参数更精确地落到底部。训练循环本身就像最普通的监督学习一样前向传播算 loss反向传播求梯度优化器更新参数。唯一需要注意的是PyTorch 的梯度默认是累积的所以每个 step 必须手动调用optimizer.zero_grad()否则梯度会在多个 step 之间叠加导致训练发散。这是我的老毛病了每次换框架都会踩一次。4.4 训练过程的监控技巧我强烈建议在训练脚本里加上一个简单的日志回调每 100 个 step 打印一次当前的 loss、学习率和显存占用情况顺手把每个 step 的 loss 追加到一个 CSV 文件里。训练结束后用 pandas 或者 matplotlib 把 loss 曲线画出来你会非常直观地看到模型的学习过程。画图这个步骤不是可有可无的仪式感而是排查问题的关键工具。比如我在第一次实验中发现 loss 在 3000 步之后出现了一个突然的尖峰从 4.5 跳到 6.2 然后又掉回 4.4。看数值变化很小但画图后才发现是学习率调度的 warmup 阶段结束、学习率进入衰减阶段导致的梯度更新幅度变化属于正常现象。如果只盯着终端里的数字很容易误判成训练炸了。另外训练过程中保存 checkpoint 的策略也很重要。我的做法是每 2000 步保存一次最新的权重同时保留 loss 最低的那一次。因为你永远不知道训练后期会不会过拟合或者出现奇怪的问题。实测下来minimind 在第 16000 步左右的 checkpoint 生成的文本质量最好第 20000 步的最终权重反而有点“过于平滑”部分场景下生成的句子缺乏一些随机性。这背后的原因可能是数据量过小导致后期过拟合所以保存多个 checkpoint 并分别测试是必要的步骤。5. 实测效果展示2 小时训练出来的模型能干什么5.1 直接文本续写最直观的能力体现训练完模型之后我第一件事就是做文本续写测试。写了一个简单的贪心推理脚本给定一个开头让模型逐个 token 往后生成每次取概率最高的那个 token。测试 1“春天的花开在”模型输出“春天的花开在山间的小路上远处的树林里传来鸟儿的叫声一群孩子正在树下玩耍他们的笑声回荡在春风里。”这段输出让我相当意外。它的语法完全通顺两个分句之间逻辑也连贯甚至出现了“回荡”、“春风里”这种带有一定文学色彩的搭配。考虑到这个模型只在 180MB 数据上训练了 2 小时且参数量只有 64M这个表现已经不算是“人工智障”级别了。测试 2“人工智能将”模型输出“人工智能将在未来改变人类的生活方式会给我们带来很多便利但是也会带来一些挑战需要我们的关注。”这个回答虽然内容比较空泛但至少逻辑上是通的——先给出正面评价然后转折指出挑战还加了一句“需要我们的关注”来收尾。这种三段式结构明显是从新闻语料里学到的常见论述模式。不过小模型的局限性也非常明显。我试了一个稍复杂点的开头“中国的首都是”模型输出“中国的首都是北京上海是中国最大的城市之一”。第二句是合理的但逻辑衔接不够自然。我又试了“11”模型输出“112, 213, 314, 415”虽然第二个等式开始跑偏了但它确实学到了“等号后面跟数字”以及“数列递增”的模式这也算是一种统计规律的体现。5.2 简单问答与对话能聊但别指望深度对话类是更多人关心的场景。我单独构造了一部分问答形式的训练数据格式是“问题xxx 回答xxx”然后训练模型去续写回答部分。实测效果如下提问“你喜欢什么颜色” 模型输出“我喜欢蓝色因为蓝色代表宁静和智慧。”提问“怎么才能学好编程” 模型输出“学好编程需要掌握基础知识和基本技能多做题多练习多看一些好的开源项目还要有耐心和毅力。”说实话这两个回答的质量超出了我对 64M 模型的预期。尤其是第二段虽然内容是网上常见的大路货总结但句子的流畅度和措辞的合理性都达到了能读的程度。原因也不难理解——这类问答语料在训练数据中重复模式很强模型本质上是在复用数据里出现频率最高的回答模板。但稍微一增加问题的复杂度模型就露馅了。我试了“解释一下什么是相对论”模型的回答是“相对论是一种物理理论它描述了物体在高速运动时的运动规律。”这个回答表面上看是正确的废话细节层面完全站不住脚。这就是小模型的根本限制它的参数量只够记住“相对论是一种物理理论”这种高频表达不够支撑深入的理论细节。5.3 其他有意思的应用场景测试除了文本续写和对话我还测了几个比较有代表性的任务。诗词生成这块得益于语料里有古诗文数据模型的表现出奇地好。给一个上句“床前明月光”模型接的是“窗前明月光疑是地上霜。举头望明月低头思故乡。”虽然是改了两个字但它确实学到了五言绝句的基本格律。我又试了原创开头“孤舟夜泊寒江上”模型接“远客天涯故国情秋风吹尽梧桐叶又见江南月正明。”虽然平仄不完全工整但对韵脚的感觉已经有模有样了。文本分类任务对小模型来说是相对简单的任务因为本质上是找输入文本和标签之间的统计相关性。我把模型输出的最后一层隐藏状态拿出来接一个简单的逻辑回归分类器在情感二分类的 5000 条测试集上达到了 87% 的准确率。这个水平虽然比不过微调 BERT 之类的方案但考虑到模型是通用的文本生成模型算是可用水平了。我甚至试着把它接了一个简单的平台接口通过 API 方式在微信小程序里调用。方法非常简单把训练好的模型导出为 ONNX 格式然后用一个轻量级的推理引擎在服务器上跑。PIN 一个 16MB 的量化模型服务端推理一张文本输入的耗时在 200ms 左右响应速度完全足够支撑小程序端的需求。这个方向证明了 64M 小模型确实有落地价值不是只能拿来“炫技”。6. 将 minimind 部署到微信小程序与本地电脑轻量化实践6.1 ONNX 导出与量化压缩训练完成后模型是 PyTorch 的nn.Module这种格式在部署场景不太方便。我直接用了torch.onnx.export把模型导出为 ONNX 格式这样后续可以接力 ONNX Runtime 推理引擎得到更快的推理速度。在导出前一个重要的步骤是确认模型的输入输出格式。minimind 的输入是input_ids一个形状为[batch_size, seq_len]的整数张量输出是[batch_size, seq_len, vocab_size]的 logits。ONNX 导出时要把动态轴标记出来尤其是batch_size和seq_len否则导出后只能接受固定长度的输入限制使用灵活度。模型导出为 FP32 的 ONNX 文件后体积大约 250MB。之后我用onnxruntime的量化工具做了一层 INT8 量化模型压缩到了约 65MB精度损失在可接受范围内。6.2 微信小程序部署的整体思路微信小程序本身不能直接跑 PyTorch 模型但它可以通过 HTTPS 请求调用后端推理服务。所以整个链路是小程序端发送文本 → 后端服务器接收请求并处理 → 模型推理 → 返回生成结果 → 小程序展示。后端我用的是 FastAPI 搭了一个轻量的 HTTP 服务。请求格式定义为简单的 JSON{prompt: 春天的花开在}响应为{generated_text: ...完整输出...}。ONNX Runtime 加载模型后常驻内存每次请求只需调用一次session.run性能非常稳定。我自己实测在没有 GPU 的小型云服务器上2 核 4GB 配置单次推理的平均耗时约 350ms完全能满足正常交互场景。如果对端延迟有更高要求可以在服务器上启用 ONNX Runtime 的 GPU 版把耗时压到 150ms 以内但成本会增加一些。6.3 本地电脑部署的另一个方案如果你不想走服务器部署路线本地电脑部署反而更简单。直接把训练好的模型以 PyTorch 的.pt格式存在本地然后写一个简单的推理脚本用torch.load加载权重就能本地运行。本地部署还需要考虑一个问题如何让其他程序调用模型。我之前在几个项目里总结的方法是封装一个 Python 类对外提供generate(prompt)和predict(prompt)两个方法。生成方法类似写一个循环每个 step 把最新的输出 token 拼到输入序列后面再继续推理预测方法则在输出层后面接一个softmax取最大概率的类别。这样一个类接口可以平滑对接本地 CLI 工具、Flask API 或者其他 Python 程序。6.4 部署过程中的避坑清单整个部署环节我踩过一些坑这里分享几个经验注意 input_ids 的数据类型。模型训练时用的是torch.long导出 ONNX 后输入类型也会限定为 int64。如果推理时传入 int32ONNX Runtime 会直接报错需要统一。ONNX Runtime 的 opset 版本选择。我用的是 opset 14太低的版本可能不支持部分算子导致导出后模型在推理时出现UnsupportedOperator的错误看着很头疼。推理时序列长度的问题。ONNX 导出时如果你不设置动态轴模型只能接受固定长度输入比如 128。输入短的时候要补 PAD 到 128输入长了要截断这个逻辑必须在服务端处理好否则接口会因为序列长度不对直接崩。小内存设备上的推理提示。如果目标设备内存很紧张建议将 INT8 量化后接入 ONNX Runtime 的 CPU 执行提供程序它的内存占用会进一步下降但同时推理速度也会下降 30%~50%需要根据自己的场景做权衡。7. 常见问题与排查技巧实录7.1 训练阶段问题速查表问题现象可能原因排查方案loss 最开始就特别低2数据泄漏或作用域错误检查 train/val 数据是否重叠检查 label 是否构造正确loss 下降很快然后停在平台期学习率过大或数据量不足降低学习率或增大数据量等待训练时间加长loss 出现周期性尖峰学习率调度变化导致画出 loss 曲线确认是否为 warmup/cosine 交界处loss 为 NaN梯度爆炸检查学习率是否过大添加梯度裁剪torch.nn.utils.clip_grad_norm_显存爆掉batch size 太大或序列太长缩小 batch size或者用混合精度训练torch.cuda.amp7.2 推理阶段问题速查表问题现象可能原因排查方案生成的文本全是重复循环温度参数太低或模型容量不够调高温度到 0.8~1.0或者用 top-p 采样生成内容完全无关上下文窗口太短模型没学到关联检查训练序列长度是否过短适当增加 max_seq_lenONNX 推理报 shape 错误输入维度与导出时不一致检查输入张量形状可尝试print导出模型的input_names信息推理速度太慢模型没有用半精度或量化改用 FP16 或 INT8 量化或开启 ONNX Runtime 的优化执行项7.3 我个人的一些避坑心得我在这类小模型实操中最常说的一句话是“小模型的成败七分在数据两分在调参一分在架构。”很多人一开始就纠结模型层数、注意力头数但这些在参数规模只有 64M 的情况下对最终效果的影响真的远小于训练数据的质量和分布。比如我最初的一个版本把全部数据混在一起不区分类型训练完效果非常差生成的东西逻辑跳跃严重。后来我把数据按领域切分并按比例混合效果立刻有了质的提升。这背后的原因是不按比例混合时新闻文本数据量大会主导模型的学习方向其他领域的表达能力就会被严重压缩。调参方面AdamW 的两个 beta 参数偶尔需要额外调整。默认设betas(0.9, 0.999)在长序列训练中容易导致训练后期梯度更新偏小。我实测把 beta2 从 0.999 改成 0.98 后收敛速度快了约 5%效果略有提升。不过这个参数不是通用的不同数据规模最优值不同需要自己跑几次实验对比。最后想提醒的是不要对 2 小时训练的 64M 模型抱有超越其能力边界的不切实际期待。它的价值在于“用极低的成本验证语言模型训练的完整流程”以及“在资源受限场景中找到一个可用的轻量级方案”。如果你需要更强的能力方向应该是扩大数据量、增大模型、延长训练时间——但这套从零训练的思维模式在任何规模的模型上都通用。