从零训练一个 1B 参数的 LLM,过去听起来像是大厂算法团队才有资格做的事。但最近一个来自印度的两人团队,带着一个名为 AQ 的项目登上了 Hacker News 的 Show HN。它最吸引人的信息点不是模型跑分有多高,而是“两个人”和“from-scratch”这两个词同时出现。这意味着整个流程里没有从现成开源模型继续微调,而是从数据、词表、模型结构到训练脚本完整走了一遍。
这篇文章想和你认真聊的,不是要不要为 AQ 鼓掌,而是它背后更通用的问题:一个 1B 级别的学术 LLM,技术难点到底分布在哪里?两个人的团队怎么把工程量压缩下来?如果你也想做一次从零训练,应该怎么规划数据、精度、算力和评估?
我会按“项目价值 -> 核心组件 -> 选型思路 -> 工程拆解 -> 环境搭建 -> 代码示例 -> 验证 -> 排错 -> 工程建议”的顺序展开。整篇文章不假设你已经训练过大模型,但希望你对 Transformer 和 PyTorch 有基本了解。我们会以 AQ 项目作为讨论由头,不做任何虚构的“实测结论”,只基于公开信息做合理拆解。
1. 这件事为什么值得关注:1B 模型与“学术 LLM”的准确定位
很多人看到“1B”会本能地觉得小,因为现在头部模型的参数规模已经到几百 B,甚至上千 B。但评价一个从零训练的模型,不能只看参数数量。1B 模型在学术界和工程界都有非常明确的生态位:它足够小,小到个人开发者和高校实验室能够复现、运行和剖析;它又足够大,大到能承载完整的预训练流程,包括数据清洗、分词器训练、学习率调度、分布式训练、评估和推理优化。“1B”不是 AQ 的缺点,而是它作为研究载体最合适的分辨率。
AQ 的定位是“academic LLM”,这个词很关键。学术模型通常不以“一比一复刻 GPT-4”为目标,而是更看重方法公开、结果可复现、过程可解释。也就是说,团队把项目发在 Show HN 上,本质上是把训练经验和模型权重开放给社区,让更多人知道:从零训练一个语言模型,不再是大公司封闭基础设施里的黑盒操作。
这里需要区分三个很容易混淆的概念。第一,从零训练指的不是下载 llama 权重继续微调,而是从随机初始化参数开始做预训练。第二,学术模型不等于可以随意商用,它可能有自己的开源许可证约束。第三,1B 模型虽然“小”,但它的文本生成能力已经足够支撑很多实际任务,比如代码补全、摘要、分类、RAG 场景中的 generator,以及 Agent 系统里的轻量级“思考模块”。
对普通开发者来说,AQ 带来的真正价值判断只有一个:如果两个人能走通从零训练,那么一台像样的 GPU 服务器加上开源工具链,也能支撑小团队做模型研发。这件事真正降低的是“认知门槛”,而不是“物理门槛”。算力成本依然存在,但你已经知道该把钱花在哪个环节上了。
2. 从零训练一个 LLM,到底需要哪些基础组件
如果你只是用 API 调用大模型,你会觉得 LLM 是个“输入一句话,输出一句话”的黑盒。但一旦进入从零训练,黑盒就会被拆成一串非常具体的工程问题。这里我把最小组件清单列出来,方便你建立全局地图。
第一个组件是数据。模型不是靠代码“变聪明”的,而是靠从海量文本中学习下一个词的统计规律。数据通常来自开源语料、网页抓取、图书、论文、代码仓库等。但原始文本不能直接喂给模型,必须先经过清洗、去重、语言过滤、质量过滤、格式归一化等步骤。数据质量决定模型质量的上限,后面所有训练技巧都只是在逼近这个上限。
第二个组件是分词器。模型不能直接处理字符串,它要把文本切分成 token,再把 token 映射成 id。分词器可以选择 BPE、SentencePiece、WordPiece 等算法,具体词表大小通常从几千到十几万不等。分词器必须基于你的训练语料单独训练,这是“from-scratch”和“用现成模型继续预训练”之间一个非常明显的分水岭。
第三个组件是模型结构。现在绝大多数 LLM 都是 decoder-only 的 Transformer,核心是带因果掩码的多头自注意力机制。常见增强点包括旋转位置编码、SwiGLU 激活函数、RMSNorm、分组查询注意力等。对 1B 这种规模,结构选择不能太复杂,否则显存和训练时间会成倍上升。
第四个组件是训练循环。你要定义损失函数,通常就是交叉熵;要定义优化器,常见的是 AdamW;要设计学习率调度策略,比如 warmup 后线性衰减。训练过程不是“把数据塞进去”,而是需要考虑 batch size、梯度累积、混合精度、checkpoint 保存、日志记录、异常恢复等一整套工程机制。
第五个组件是评估体系。训练完模型后,要回答“它到底行不行”。你可以算困惑度,也可以在下游任务集上测准确率,还可以直接写提示词做人工评测。没有评估体系的训练项目,最后只能得到一堆“看起来能说人话”但无法量化进步的权重文件。
这五个组件彼此依赖,任何一环缺失都会导致整个项目返工。AQ 作为两人团队能跑通,本质上就是把这五条链路全部打通了。很多大厂之外的团队第一次从零训练失败,往往不是因为模型结构不会写,而是低估了数据清洗和训练稳定性这两个“看不见的工程”。
3. 1B 参数模型的核心设计与选型思路
参数规模定在 1B,意味着你要做的不是简单地把大模型“缩小”,而是要按照这个量级重新做设计。核心选型可以拆成四个方面。
第一是模型结构。对于 1B 规模,最常见的做法是采用类似 GPT-2/GPT-3 的 decoder-only Transformer,层数在 12 到 24 层之间,隐藏层维度在 1024 到 2048 之间。当你从零开始设计时,不需要追求最新论文里的所有技巧,而应该优先选择稳定、有大量开源实现验证过的配置。比如旋转位置编码(RoPE)和 RMSNorm,都是实现成本低、训练稳定性好的选择。分组查询注意力(GQA)和滑动窗口注意力这类优化项,可以在基础版本跑通后再考虑加入。
第二是上下文长度。1B 模型的训练上下文一般不会设得太长,因为注意力计算量随序列长度平方增长。材料中提到 AQ 时没有给出详细上下文长度,但从技术经验看,1B 学术模型常见的训练长度是 2048 或 4096。更长的上下文需要更多数据、更大显存和更复杂的并行策略,对两人团队来说并不是优先追求的目标。
第三是精度选择。这是很多人第一次训练时最容易踩坑的地方。FP32 训练稳定但太慢、太占显存;FP16 在反向传播时容易出现梯度下溢或溢出;BF16 因为和 FP32 有相同的指数位,动态范围更大,所以在大模型训练中逐渐成为主流。如果你的 GPU 支持 BF16,建议优先使用 BF16 混合精度。真要做 FP16 训练,必须配合 loss scaling,并且密切关注训练日志里是否出现 NaN 或 Inf。
第四是训练数据规模。1B 模型不需要喂几百 B 的 token,但数据量太少又会导致欠拟合。业界常用的一个参考经验是“token 数大约是参数量的 20 倍”,也就是 1B 参数配合 20B token 左右。但这不是硬性规定,很多轻量级模型会为了控制成本减少到 10B token。真正的判断标准是:观察验证集 loss 是否还在下降,如果模型还有明显欠拟合,就继续训练;如果 loss 已经进入平台期,再加数据也不一定能带来本质提升。
从 AQ 这类项目的角度看,1B 模型最大的设计优势是“试错成本低”。你可以在 8 张 GPU 上完成整个训练,也可以接受“跑了一晚上发现学习率不对,第二天调整重来”。这种快速迭代能力,是 10B、100B 模型无法提供的。
4. 两人团队如何压缩工程成本:关键路径拆解
一个常规的大模型预训练项目,放在大厂里可能是数据组、训练组、评估组、基础设施组等多团队协作。两人团队能跑通,说明他们把工程量压缩到了极致。从技术路径来看,最关键的是以下几条。
数据环节要尽量自动化。不要用人工去“看”每一篇文档,而是写清洗脚本,用规则和分类器处理重复项、低质量文本、代码块、HTML 标签等。两人团队通常还会选择公开的、已经做过基本清洗的数据集作为起点,而不是自己从零爬取全网。这样能省下大量时间和法律风险。
训练环节要建立在成熟开源框架上。从零训练模型结构和训练循环当然可以自己写,但如果没有特殊需求,优先使用 PyTorch 加 Hugging Face Transformers,以及 Accelerate、DeepSpeed 这类工具。两人团队不需要重复造轮子,他们需要的是把有限精力集中在数据、实验设计和结果分析上。
分工要按“阻塞点”划分,而不是按“模块”划分。大厂的算法、工程、数据角色可以分得很细,但两人团队必须让每个人都具备端到端能力。一个人负责数据管线时,另一个人负责模型训练和监控;遇到训练崩溃时,两个人一起解决。这种工作方式不适合规模化复制,但非常适合“从零验证可行性”的学术项目。
可复现性要提前设计。两人团队最怕的不是跑得慢,而是跑完一个实验后忘了当时用了哪组超参数。建议从第一天起就使用配置文件管理实验参数,把数据版本、模型结构、学习率、batch size、精度设置全部固化下来。这样即使团队只有两个人,也能同时管理多个实验。
从 AQ 的公开信息看,它呈现的是一个“完成时”的项目。能走到 Show HN 这一步,意味着团队已经把模型训练、结果评估、权重发布和文档整理都做完了。对旁观者而言,最值得学习的不是它训练了多大模型,而是它用极小的团队规模走通了完整的项目闭环。
5. 环境准备与基础设施建议
如果你也想从零训练一个 1B 级别的 LLM,环境准备不需要一步到位,但必须想清楚目标。下面是一套比较稳妥的起步配置思路。
操作系统建议使用 Linux,常见选择是 Ubuntu 22.04 或更新的 LTS 版本。深度学习生态对 Linux 的支持最完整,驱动和 CUDA 环境问题也最少。如果只有 Windows 电脑,可以优先考虑 WSL2,但生产级的长时间训练任务不建议在 WSL2 里运行,因为 GPU 直通和稳定性仍然会有额外的变数。
Python 环境建议使用 Python 3.10 或更高版本,通过 conda 或 venv 管理。核心依赖包括 PyTorch、Transformers、Datasets、Tokenizers、Accelerate、DeepSpeed、TensorBoard 或 Weights & Biases。对于 1B 模型,PyTorch 2.x 的torch.compile也值得尝试,因为它可以在不改动大量代码的情况下提升训练吞吐。
GPU 方面,1B 模型的训练需要多少显存取决于很多因素。权重本身大约 2GB,但优化器状态、梯度、激活值会叠加起来。如果只做推理,一张 16GB 或 24GB 显存的显卡就足够部署;如果要做从零预训练,理想情况是至少一张 48GB 或 80GB 的专业卡,或者多张 24GB 卡通过数据并行训练。用消费级显卡也能启动训练,但 batch size 和训练时间会受到明显限制。
需要提醒的是,基础设施规划要和你手里的预算强绑定。不要一上来就追求“和某大厂一样”的并行策略。对 1B 模型来说,单机多卡的数据并行通常已经够用,张量并行和流水线并行是模型更大以后才必要的优化手段。
环境准备阶段最容易犯的错误是:花大量时间折腾最新的 CUDA 版本和驱动,结果训练脚本还没跑通。更推荐的做法是先安装一个经过完整验证的稳定组合,比如 PyTorch 官方推荐的 CUDA 版本,先把最小训练示例跑起来,再考虑性能优化。
6. 一个从零训练最小示例(基于开源工具链)
下面这段内容不是 AQ 项目的源码,而是用开源生态里的常见接口,演示从零训练 1B 模型需要写哪些核心代码。你可以把它看作一张“技术地图”,用来理解每个组件在工程上长什么样。
6.1 训练 BPE 分词器
分词器是文本到 token 的第一道关卡。这里我们使用tokenizers库训练一个 BPE 词表,输出到本地文件。
# 文件路径:train_tokenizer.py from tokenizers import Tokenizer, models, pre_tokenizers, trainers tokenizer = Tokenizer(models.BPE()) tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel.new_alphabet() trainer = trainers.BpeTrainer( vocab_size=32768, min_frequency=2, special_tokens=["<pad>", "<unk>", "<s>", "</s>"], show_progress=True, ) files = [f"data/corpus_{i}.txt" for i in range(10)] tokenizer.train(files, trainer) tokenizer.save("tokenizer.json")这段代码的关键点有两个。第一,词表大小决定了嵌入层参数量和文本切分的粒度,1B 模型用 32K 到 64K 是比较常见的区间。第二,特殊 token 一定要在训练开始前定义好,否则后续加载模型时会出现 id 不一致。如果你的语料是全英文和代码,ByteLevel 预分词是默认的可靠选择;如果包含大量中文,需要额外考虑是否用混合分词方案。
6.2 数据预处理与样本打包
分词器训练完成后,要把原始文本转换为固定长度的 token 序列。实际训练中,我们通常把多个文档拼接成 block,并在文档之间插入分隔符,然后切成固定长度的样本。
# 文件路径:prepare_dataset.py from datasets import load_dataset from tokenizers import Tokenizer tokenizer = Tokenizer.from_file("tokenizer.json") dataset = load_dataset("json", data_files="data/corpus.jsonl", split="train") def tokenize_function(examples): text = examples["text"] encoded = tokenizer.encode(text).ids return {"input_ids": encoded} tokenized = dataset.map(tokenize_function, remove_columns=["text"]) def group_texts(examples, block_size=2048): all_ids = [token_id for ids in examples["input_ids"] for token_id in ids] result = [] for i in range(0, len(all_ids) - block_size + 1, block_size): result.append(all_ids[i : i + block_size]) return {"input_ids": result} dataset = tokenized.map(group_texts, batched=True, remove_columns=["input_ids"]) dataset.save_to_disk("data/train_blocks")这段代码把不定长的文本“切块”成固定 2048 长度的样本。实际项目中你还需要考虑多文档拼接时的 padding 和掩码,避免模型跨文档边界无意义地建模。更严谨的做法是在拼接时记录每个样本属于哪篇文档,训练时不计算跨文档位置的损失,但最小示例里可以先不做这层处理。
6.3 训练配置与启动脚本
模型结构和训练循环可以用 Transformers 的Trainer封装,把更多精力放在配置上。下面是一个基础的训练配置。
# 文件路径:config/1b_config.yaml model_name_or_path: "none" output_dir: "./checkpoints" num_train_epochs: 3 per_device_train_batch_size: 8 gradient_accumulation_steps: 16 learning_rate: 3e-4 warmup_steps: 2000 weight_decay: 0.1 bf16: true logging_steps: 50 save_steps: 500 save_total_limit: 3 report_to: "tensorboard" block_size: 2048这里的gradient_accumulation_steps值得特别解释:当单卡显存放不下太大的 batch 时,可以把一个小 batch 的梯度累积多次后再更新参数,等效放大 batch size。1B 模型训练时,常见的有效 batch size 可能达到几十万 token,但这一步不需要在第一天就追求,先用一个能稳定训练的配置跑起来更重要。
训练启动脚本可以用 Accelerate 的 launch 命令实现多卡训练:
accelerate launch \ --multi_gpu \ --num_machines 1 \ --num_processes 8 \ train.py --config config/1b_config.yaml这里假设你有 8 张 GPU。如果只有单卡,去掉--multi_gpu参数即可。真正的train.py里需要定义模型结构、加载数据集、设置 optimizer 和 lr scheduler,再传入Trainer。由于这些代码在不同项目里差异较大,我这里只给出配置层面的骨架,避免误导你把它当作 AQ 仓库里的真实实现。
7. 运行结果与效果验证
训练不是启动脚本就结束了,还要有清晰的验证方法。这一步最基础也最重要的信号是训练 loss 和验证 loss。
运行训练后,你会看到类似下面的日志:
Step 100 | loss: 9.82 | lr: 1.2e-4 | throughput: 1800 tokens/s Step 200 | loss: 8.91 | lr: 2.1e-4 | throughput: 1820 tokens/s Step 500 | loss: 7.23 | lr: 3.0e-4 | throughput: 1790 tokens/sloss 是否下降,是判断模型是否学起来的首要指标。1B 模型的初始 loss 会因为词表大小和数据分布不同而有差异,但趋势比绝对值更重要。如果 loss 在稳步下降,说明数据管道、模型结构、优化器设置基本正常。如果 loss 不降或者出现剧烈震荡,要先检查数据是否重复、学习率是否过高、梯度是否异常。
模型训练完成后,先做一个静态评估,也就是计算模型在保留验证集上的困惑度。困惑度越低,代表模型对文本分布的建模越好。但困惑度本身不能完全代表生成质量,所以还要做生成测试和下游任务测试。生成测试很简单,给定一个提示词,让模型自回归地生成若干 token,人工判断句子是否连贯、是否重复、是否符合常识。
# 文件路径:generate_sample.py from transformers import AutoModelForCausalLM, AutoTokenizer model = AutoModelForCausalLM.from_pretrained("./checkpoints/final") tokenizer = AutoTokenizer.from_pretrained("tokenizer.json") prompt = "The future of AI is" inputs = tokenizer(prompt, return_tensors="pt") output = model.generate(**inputs, max_new_tokens=50, do_sample=True, temperature=0.8) print(tokenizer.decode(output[0], skip_special_tokens=True))如果模型是纯从零训练且只训了很短时间,生成结果很可能不通顺。这是正常的,评估的目的不是“显得厉害”,而是知道当前模型正处于哪个阶段。如果生成文本很快就陷入重复,通常意味着训练步数不足,或者采样参数设置不当。如果完全生成是一堆乱码,则要检查 tokenizer 和模型加载是否匹配。
对于学术项目,还应该选几个常用的公开下游评测任务,比如常识推理、问答、代码生成或阅读理解,看看模型在不同能力维度上的表现。AQ 作为一个学术项目,在发布时通常也会附带类似的评测说明,社区看到的不只是一个权重文件,而是一套“它为什么值得信任”的证据链。
8. 常见问题与排查思路
从零训练 LLM 和微调现成模型最大的区别在于:你几乎没有“已经能用的起点”,很多问题只能靠日志和实验记录判断。下面列几个高频问题。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 训练启动后直接 OOM | batch size 太大或激活值占用过高 | 查看错误日志中的 CUDA out of memory 位置 | 调低 batch size,开启梯度累积,或使用更小的序列长度 |
| loss 不下降 | 数据重复、学习率过低、数据管道没对齐 | 检查数据样本和 tokenizer 输出,观察 loss 曲线 | 清理数据,调高学习率,先用小数据集验证过拟合能力 |
| loss 出现 NaN 或 Inf | FP16 精度下溢出、学习率过高等 | 查看具体 Step 的日志和梯度范数 | 切换 BF16,降低学习率,启用梯度裁剪,检查数据是否存在异常值 |
| 多卡训练吞吐不升反降 | 数据加载瓶颈或卡间通信开销大 | 观察 GPU 利用率,检查 dataloader 的 num_workers | 开启预取和多进程加载,优化数据读取,或者改用更大 batch size 减少通信频率 |
| 生成结果全是重复文本 | 训练不足、温度设置太低、上下文长度不足 | 检查生成参数并查看验证 loss | 继续训练,调高 temperature,使用 top-p 采样,增加训练数据多样性 |
这些问题的核心排查思路是一致的:先缩小范围,确认是数据问题、模型问题,还是训练策略问题。不要同时改五个超参数,那样你根本不知道哪一个让 loss 恢复了正常。保险的做法是每次只改一个变量,并保留完整日志。
还有一个很容易被忽略的问题是 checkpoint 与 tokenizer 不匹配。如果你重新训练了 tokenizer,但没有同步更新模型配置文件,加载权重时会出现 embedding 维度不一致,报错信息可能非常隐晦。解决方式是在项目里统一管理 tokenizer 版本,记录词表大小和特殊 token 顺序,任何一次变更都走配置变更流程。
9. 对个人和小团队的工程建议与最佳实践
从 AQ 这个项目身上,个人开发者和小团队能提炼出几条真正有用的工程经验。
第一,先用最小实验验证全流程。不要一开始就想训练 10B token,先拿 1B token 和非常小的模型把数据管道、训练脚本、评估流程全部跑通。哪怕这个模型结果很差,但你已经验证了每个环节是通的,后面扩大规模只是时间和成本问题。
第二,训练和评估指标要全部落地为文件。从零训练项目的失败大多不是“模型没学好”,而是“不知道当前在哪个阶段”。建议每一步都记录:训练 loss、验证 loss、学习率、GPU 吞吐、checkpoint 路径、数据版本、代码 commit 号。这样任何一次实验结果都可以被完整复盘。
第三,开源许可证和数据合规要提前确认。学术模型通常涉及公开数据集、代码库和模型权重,不同数据集的使用条款可能完全不同。两人团队尤其容易因为“赶进度”而忽略这一点,但一旦模型公开,版权问题会被放大。发布权重前,必须逐一确认训练数据、tokenizer、模型代码和权重各自的许可证。
第四,考虑推理场景的优化。1B 模型最大的优势是部署门槛低。训练完成后,可以通过量化、剪枝、vLLM 或 llama.cpp 等方式让它在普通 CPU 或消费级 GPU 上运行。如果你后续要做 RAG 或 Agent 应用,1B 模型往往比云端大模型更适合做本地私有化推理,既减少 API 调用成本,也能保护数据隐私。
第五,注意从零训练的资源边界。对个人开发者来说,最稳妥的路径是先跑通一个小规模 demo,再决定是否投入更大的算力预算。不要因为“别人用从零训练发布项目”就冲动地在云上开几十张卡跑一周,结果只得到一堆无法解释的日志。理性做法是像 AQ 团队一样,先定义清楚“我到底要验证什么”,再为这个验证目标分配资源。
第六,安全与隐私不能因为团队小就省略。如果模型会被用于对话、代码生成或企业数据场景,必须做输入输出过滤,尤其是防止模型被提示注入、生成有害内容,或在未授权数据上泄露敏感信息。涉及外部数据接入和工具调用时,坚持最小权限原则,开发环境、测试环境、生产环境互相隔离。
10. 总结与后续学习方向
AQ 这个项目用两个人证明了一件事:从零训练 LLM 的完整链路已经可以被小团队触达。它需要的不是“天才灵感”,而是对数据、模型、训练、评估、工程配置的系统性掌控。1B 不是个小数字,它足够承载完整的预训练方法;1B 也不是个遥不可及的数字,它让个人团队有机会亲手跑通每一个环节。
如果你想沿着这个方向继续深入,我建议按下面的路径走。第一,用开源工具复现一个更小的从零训练项目,比如 100M 参数加少量语料,先建立“训练闭环”的真实感觉。第二,研究混合精度训练的原理,把 FP16、BF16、loss scaling 这些概念彻底弄懂。第三,尝试把训练好的 1B 模型接进一个真实的 RAG 或 Agent 场景,看它在推理延迟、内存占用、输出质量上的实际表现。第四,阅读大模型训练框架的源码,尤其是数据加载、梯度累积、分布式通信三个部分。这些知识组合起来,才是从“调用 API”进阶到“生产模型”的真正分水岭。
从零训练的价值,从来不只是把 loss 降下去。它逼着你重新理解语言模型底层的一切,也会让你在以后面对任何预训练模型时,不再把它当成黑盒。