最近 Show HN 上出现了一个名为 AQ 的项目:两位开发者在印度从零训练了一个 10 亿参数规模的学术大语言模型。这个项目真正值得关注的地方,不是参数数量,而是“从零”这两个字。从零意味着分词器要自己训练、语料要自己清洗、模型结构要自己搭建、训练脚本要自己调通、评测要自己设计,整条链路没有任何现成模型权重可以依赖。
对大多数开发者来说,1B 是一个很合适的观察窗口。它足够大,能完整暴露大模型训练里的数据、显存、精度、稳定性等工程问题;又足够小,单张 24GB 显存显卡配合梯度累积和激活检查点就有机会跑通。本文不介绍 AQ 模型的内部细节,那要以官方发布为准,而是以这类项目为背景,梳理从零训练一个 1B 学术模型必须处理的数据、分词器、模型结构、训练稳定性、评测和排错问题,并给出一套可以直接参考的工程路径。
1. 1B 学术大模型到底“从零训练”了什么
1.1 从零训练不是只训练网络
很多第一次接触大模型训练的开发者,会把“从零训练”理解为写一个 Transformer 模型,然后喂语料跑反向传播。真正进入项目后才发现,训练网络只占整条链路的一小部分。
一个完整的小规模 LLM 训练链路至少包括:
- 语料采集、清洗、去重、质量过滤和配比设计
- 分词器训练与特殊符号设计
- 模型架构选择和超参数配置
- 训练脚本、数据加载、精度管理、断点续训
- 训练过程中的 loss、梯度、吞吐量监控
- 困惑度、生成样例、下游任务和人工评估
- 每次实验的数据版本、配置版本、权重版本管理
任何一个环节出错,模型最终都会以很难排查的方式表现出来。比如语料里混入了大量重复文本,loss 会一直降但生成结果空洞;分词器词表与实际模型配置不一致,推理时会出现大量未知词;评测数据没做污染检查,指标虚高但真实能力不足。
1.2 1B 规模为什么适合小团队和学术研究
学术研究需要的是“可控性”。1B 模型可以在有限算力下做多组对照实验,验证数据配比、学习率、上下文长度、分层结构等因素的影响,再把结论外推到更大规模。
从计算量上看,可以做一个粗略估算。参考 Chinchilla 论文给出的约 20 tokens/parameter 的经验值,1B 模型需要大约 20B token 的语料。训练计算量约为 6 × 参数 × token 数,也就是 6 × 1B × 20B = 1.2 × 10^20 FLOPs。单张 A100 的实用吞吐量大约在每秒 3 × 10^14 FLOPs 量级,8 卡并行也需要数天时间。所以小团队通常会把一个实验版本的 token 数降到 5B 到 20B 之间,先把模型从 0 跑到 1,再决定是否扩展。
这个规模还意味着可以在 3090、4090 这类消费级显卡上做小批量调试。很多超参数问题在 1B 规模上就能暴露,不需要等到百亿甚至千亿参数才发现。
1.3 从 AQ 这类项目看小团队的工程取舍
AQ 这类由两人团队完成的项目,工程上通常有三个明显取舍。
第一,依赖公开数据集,但在领域比例上做取舍。学术模型一般会混入论文、教材、百科、代码等数据,其中论文类数据的比例会被刻意调高。第二,训练目标不追求“全能”,而是追求可评测、可复现。学术模型更关注在公开 benchmark 上的表现和实验记录是否完整。第三,用更频繁的中间评测替代一次性的最终评测,因为小团队的容错空间小,等到训练结束再发现问题,重跑成本很高。
这些取舍对普通开发者同样有参考价值:预算有限时,先把一条完整链路跑通,再逐步提升数据质量,比一开始就堆算力更有效。
2. 数据与分词器是第一个真正的大工程
2.1 数据来源、清洗与去重
学术 LLM 的语料以论文、教材和技术文档为主,常见公开来源包括 arXiv、PubMed、S2ORC、Wikipedia、The Pile 和 Common Crawl 的子集。不要直接使用原始爬虫文本,里面包含大量导航、广告、HTML 标签和重复段落。
清洗流程可以按这个顺序执行:
- 用 trafilatura 或 readability 提取正文,去掉 HTML 噪音
- 用 fastText 语言检测过滤掉无关语言
- 过滤过短、符号占比过高、连续重复行过多的文档
- 用精确哈希去掉完全重复的文档
- 用 MinHash 去掉近似重复的文档
- 用域名和关键词黑名单去掉垃圾内容
- 对学术文档做公式、表格、引用的噪音处理
import trafilatura # 从 URL 抓取并提取正文,替换原始 HTML downloaded = trafilatura.fetch_url("https://example.com/paper.html") text = trafilatura.extract(downloaded)from datasketch import MinHash def build_minhash(text: str, num_perm: int = 128): m = MinHash(num_perm=num_perm) for token in text.split(): m.update(token.encode("utf-8")) return m # 对每个文档生成 MinHash,再放入 MinHashLSH 做近似去重这里要特别注意:近似去重对学术语料非常重要。同一个论文主题会被不同课程笔记反复改写,不做去重,模型会反复记忆同一批观点,生成时出现严重重复。
2.2 训练自己的 BPE 分词器
为什么不能直接拿其他开源模型的分词器用?学术领域有大量领域词汇,直接使用通用分词器会把一个术语切得支离破碎,还可能因为词表 ID 不一致导致模型配置和分词器不匹配。训练自己的分词器是“从零训练”的一部分。
推荐使用 SentencePiece 训练 BPE 分词器,设置好特殊符号和词表大小:
import sentencepiece as spm spm.SentencePieceTrainer.Train( input="corpus_clean.txt", model_prefix="aq_tokenizer", vocab_size=32768, model_type="bpe", character_coverage=0.9995, bos_id=1, eos_id=2, pad_id=0, unk_id=3, bos_piece="<s>", eos_piece="</s>", pad_piece="<pad>", unk_piece="<unk>", user_defined_symbols=["[MATH]", "[CODE]", "[TABLE]"], )训练完成后必须做一次编码解码回环测试:
python -c " import sentencepiece as spm sp = spm.SentencePieceProcessor(model_file='aq_tokenizer.model') ids = sp.encode('transformer is a deep learning model') print(ids) print(sp.decode(ids)) "如果 decode 结果和原文不一致,说明词表或特殊符号配置有问题,需要重新训练。
2.3 数据混合比例与质量过滤
数据混合比例没有统一答案,但可以从领域目标反推。以学术模型为例,论文类数据比例会明显高于通用模型。下面是一个示例配置,实际项目要按语料可用量调整:
| 数据源 | 类型 | 参考比例 | 清洗重点 |
|---|---|---|---|
| 学术论文 | 论文、预印本 | 30%-40% | 去公式噪音、去模板重复、去引用列表 |
| Wikipedia | 百科 | 10% | 去维护模板、去分类导航 |
| 书籍 | 教材、专著 | 10%-15% | OCR 修复、版权过滤 |
| 代码 | 开源代码 | 5%-10% | 去自动生成文件、去敏感配置 |
| 网页文本 | 通用网页 | 20%-30% | 去广告、去导航、去重复段落 |
质量过滤时,可以用几个简单规则快速排除垃圾文本:文本长度小于 200 字符的丢弃;连续重复行占比超过 30% 的丢弃;标点符号占比异常的丢弃。也可以用一个小的语言模型计算语料困惑度,把困惑度明显偏高的文档视为低质量文本。
3. 模型结构和训练配置要围绕 1B 参数做减法
3.1 架构选择:RMSNorm、RoPE、SwiGLU、GQA
1B 模型的基本架构通常采用 decoder-only 风格,但会在细节上做工程取舍。常用组件可以整理成一张表:
| 组件 | 作用 | 为什么适合小团队 |
|---|---|---|
| RMSNorm | 替代 LayerNorm,去掉均值中心化 | 计算更快,数值稳定性接近 |
| RoPE 旋转位置编码 | 给注意力注入位置信息 | 支持上下文长度外推,训练稳定 |
| SwiGLU 激活函数 | 提升表达能力 | 效果更好,但参数量略高 |
| GQA 分组查询注意力 | 减少 KV 头数量 | 降低显存和推理开销 |
| 共享词嵌入 | 输入输出词表共享权重 | 显著减少参数量 |
GQA 是 1B 模型里很值得加的一个设计。全量多头注意力在推理时每个头都要保存 KV 缓存,用 GQA 后 KV 头可以降到 4 个甚至更少,推理阶段的内存和带宽压力会明显下降。
3.2 一份可落地的 1B 模型配置
下面是一份接近 1B 参数的示例配置,列表里的数值用于说明思路,落地时要根据自己分词器词表和显存情况调整:
config = { "vocab_size": 32768, "hidden_size": 2048, "intermediate_size": 5632, "num_hidden_layers": 24, "num_attention_heads": 16, "num_key_value_heads": 4, "max_position_embeddings": 4096, "rms_norm_eps": 1e-6, "rope_theta": 500000, "use_bias": False, "use_swiglu": True, "tie_word_embeddings": True, }用这个配置估算参数量:词嵌入部分为 32768 × 2048 ≈ 6700 万;每一层 Transformer 中,注意力约 1000 万,MLP 约 3400 万;24 层合计约 11.3 亿,加上共享词嵌入总计约 12 亿。MLP 的 intermediate_size 一般取 hidden_size 的 2.7 到 3 倍,SwiGLU 因为有三个线性层,实际计算量和参数量要单独核算。
3.3 fp32、fp16、bf16 怎么选
训练大模型时,精度选择直接影响收敛速度和稳定性。这是网上讨论非常多的问题,这里给出一个实用结论:
| 格式 | 位宽 | 指数位 | 尾数位 | 适用场景 |
|---|---|---|---|---|
| fp32 | 32 | 8 | 23 | 基线计算、优化器状态 |
| fp16 | 16 | 5 | 10 | 早期加速方案,需要 loss scaling |
| bf16 | 16 | 8 | 7 | LLM 预训练推荐,动态范围与 fp32 一致 |
| fp8 | 8 | 5 | 2 | 推理加速和部分训练场景,精度风险更高 |
fp16 的问题在于尾数位只有 10 位,小数值很容易溢出。bf16 牺牲了尾数精度,但保留了与 fp32 相同的动态范围,训练中不易出现损失溢出,所以成为当前预训练的主流选择。
在 PyTorch 中,bf16 的使用非常简单:
with torch.autocast(device_type="cuda", dtype=torch.bfloat16): logits = model(input_ids)如果必须使用 fp16,则需要引入 GradScaler 处理梯度缩放:
from torch.cuda.amp import GradScaler scaler = GradScaler() with torch.autocast(device_type="cuda", dtype=torch.float16): loss = model(input_ids) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()实际项目里,通常让模型权重和优化器状态保持 fp32,前向和反向计算使用 bf16,这样可以兼顾数值稳定性和显存占用。
4. 训练工程:显存、断点续训和日志监控
4.1 先估算显存,再决定并行方案
训练 1B 模型前,建议先做显存估算,不要直接开跑。
| 显存项目 | 1B 模型约占用 |
|---|---|
| 模型权重 fp32 | 4GB |
| 模型权重 bf16 副本 | 2GB |
| Adam 一阶动量 fp32 | 4GB |
| Adam 二阶动量 fp32 | 4GB |
| 梯度 bf16 或 fp32 | 2GB 到 4GB |
| 激活值 | 取决于 batch size 和序列长度,可能数 GB |
也就是说,仅模型、梯度和优化器状态就可能在 16GB 到 18GB 之间。24GB 显存能跑,但很紧张,必须配合激活检查点、梯度累积和序列长度控制。
如果只有单张 24GB 显卡,推荐组合是:bf16 混合精度 + 激活检查点 + 梯度累积 + DeepSpeed ZeRO-1 或关闭分片的 FSDP。如果有多张卡,可以使用 FSDP 或 DeepSpeed ZeRO-2 把模型分片到多卡。
4.2 一个最小训练循环怎么写
训练循环的关键点在于梯度累积、梯度裁剪和学习率调度。下面是一个可以直接作为起点的示例:
import torch model.train() optimizer = torch.optim.AdamW( model.parameters(), lr=3e-4, weight_decay=0.1, betas=(0.9, 0.95), ) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_max=steps_total ) grad_accum_steps = 8 optimizer.zero_grad() for step, batch in enumerate(train_loader): input_ids = batch["input_ids"].to(device) labels = input_ids[:, 1:].contiguous() with torch.autocast(device_type="cuda", dtype=torch.bfloat16): logits = model(input_ids[:, :-1]) loss = torch.nn.functional.cross_entropy( logits.view(-1, config["vocab_size"]), labels.view(-1), ignore_index=-100, ) loss = loss / grad_accum_steps loss.backward() if (step + 1) % grad_accum_steps == 0: torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() scheduler.step() optimizer.zero_grad() if step % 100 == 0: print(f"step {step} loss {loss.item():.4f} lr {scheduler.get_last_lr()[0]}")这里有两个容易忽略的细节。第一,loss 除以 grad_accum_steps 是为了把多次微批次的梯度平均到同一量级,否则有效学习率会随累积步数放大。第二,ignore_index=-100 是为了让填充位置不参与损失计算,否则模型会花费精力去学习预测无意义的 pad token。
4.3 断点续训和 loss spike 自动保护
从零训练的周期长,断点续训不是可选项。每个 checkpoint 至少保存模型、优化器、学习率调度器、步数和随机数状态:
checkpoint = { "model": model.state_dict(), "optimizer": optimizer.state_dict(), "scheduler": scheduler.state_dict(), "step": step, "config": config, "rng_state": torch.get_rng_state(), } torch.save(checkpoint, f"ckpt_{step}.pt")训练稳定性监控要盯三个指标:loss、梯度范数、当前学习率。更稳妥的做法是加一道 loss spike 保护:维护最近若干步的滑动平均 loss,当前步 loss 如果超过滑动平均的若干倍,就回退到上一个 checkpoint,降低学习率后继续训练。
if loss > loss_avg * 3: print(f"loss spike at step {step}: {loss.item():.4f}") restore_from_checkpoint(last_ckpt) optimizer.param_groups[0]["lr"] *= 0.5 continue日志建议同时输出到本地文件和 wandb 或 TensorBoard,因为训练结束后复盘实验时,loss 曲线和梯度范数曲线是定位问题最有用的证据。
5. 评测不能只看 loss,要把“学术能力”拆开看
5.1 困惑度、生成样例和下游任务评测
预训练阶段最常用的在线指标是评估集困惑度(perplexity),但它只能反映模型对语料分布的拟合程度,不能反映问答、推理和生成质量。
完整的评测至少分三层:
- 困惑度评估:在留出的、与训练集同分布的语料上计算 PPL
- 生成样例:用人工挑选的学术类 prompt 查看模型输出质量
- 下游任务:用公开 benchmark 评估常识、推理、知识能力
下游任务可以用 lm-evaluation-harness 快速跑起来:
pip install lm-evaluation-harness lm_eval --model hf \ --model_args pretrained=./aq-1b \ --tasks hellaswag,arc_easy,piqa,winogrande,openbookqa \ --device cuda:0 \ --batch_size 8需要注意,小模型的 benchmark 分数波动很大,单次评测结果不能说明问题。建议固定 prompt 模板,多次运行取均值,并且把评测代码和版本一起记录。
5.2 数据污染检查:训练集和评测集必须隔离
如果训练语料里混入了评测集样本,模型等于“背过答案”,指标会虚高。这个问题在小模型上尤其容易发生,因为训练语料往往来自公开爬虫,而公开 benchmark 的题目也来自网页。
至少做三件事:
- 训练前把评测集的所有 prompt 和答案从语料中精确删除
- 用 MinHash 对评测集和训练集做近似重叠检查
- 在训练过程中定时计算评估集困惑度,观察是否提前出现剧烈下降
写成脚本可以这样:
python filter_overlap.py \ --train corpus_clean.txt \ --eval eval_prompts.jsonl \ --n 8n 表示 n-gram 重叠长度。如果训练语料和评测数据存在 8-gram 级别的连续重叠,基本可以判定污染。
5.3 多次小步迭代比一次大训练更可靠
小团队最常见的浪费是直接启动大规模训练,跑了一周后发现模型生成质量糟糕,却不知道问题在数据还是架构。
推荐的做法是先跑小规模实验:
- 用 300M 左右的小模型,在 1B 到 2B token 上做 10 到 20 步的快速验证
- 检查 loss 是否按预期下降、生成样例是否可读
- 确认没有 NaN、分词错误、数据加载错位等问题
- 再放到 1B 模型上启动正式训练
在正式训练过程中,每 1000 步保存一个中间 checkpoint,每遇到一个 checkpoint 就跑一次轻量评测。这样即使最终版本不理想,也能从中间 checkpoint 里挑选出可用的版本。
6. 从零训练最常见的坑和排查链路
小团队训练 LLM 时,问题往往集中在训练稳定性、数据质量和评测可信度三个方向。下面整理了一张常用排查表:
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| loss 变 NaN | 学习率过高、fp16 溢出、数据含异常值 | 检查梯度范数、输入数据、logits 是否包含 NaN | 使用 bf16、降低学习率、开启梯度裁剪 |
| loss 突然飙升 | 学习率调度异常、数据批次重复、梯度累积归一化错误 | 回放 loss 曲线和梯度范数曲线 | 回退最近 checkpoint,降低学习率继续训练 |
| 生成结果大量重复 | 语料去重不彻底、训练步数过长 | 检查语料重复率、生成测试 | 加强 MinHash 去重,提前停止或增加数据 |
| 解码出现大量 unk | 分词器词表与模型配置不一致 | 做 encode/decode 回环测试 | 重新训练或统一词表 ID 映射 |
| PPL 很低但任务分数低 | 评测数据污染、数据分布偏差 | 检查训练集与评测集重叠度 | 清洗评测数据,增加领域数据比例 |
| 训练吞吐量低 | 数据加载瓶颈、序列 padding 浪费 | 观察 GPU 利用率和 dataloader 耗时 | 用序列打包、加大 num_workers、开启 prefetch |
6.1 loss 变 NaN 或突然飙升
这是最常见也是最紧急的问题。第一反应不是改代码,而是先确认是哪个环节出现 NaN。
排查顺序:
- 输入数据是否有空文档或异常 token id
- 前向 logits 是否包含 NaN
- 梯度范数是否在某一层爆炸
- 当前学习率和 warmup 步数是否合理
- 是否开启了 fp16 而没有配置 loss scaling
如果是 fp16 问题,直接换 bf16 通常能解决。如果是学习率问题,把峰值学习率从 3e-4 降到 1e-4 再试。如果是数据问题,定位到具体 batch 后从数据管线里过滤掉。
6.2 分词器出现未登录词或词表不一致
症状是训练正常,但生成时出现大量[UNK]或[PAD]。原因一般是分词器训练时设置的特殊 token 顺序,和模型初始化时的 pad_token_id、bos_token_id、eos_token_id 不一致。
解决办法是建立一次全链路校验:加载分词器和模型配置后,打印所有特殊 token 的 ID,确认与训练时一致,并跑一轮编码解码回环测试。
sp = spm.SentencePieceProcessor(model_file="aq_tokenizer.model") for token in ["<s>", "</s>", "<pad>", "<unk>", "[MATH]"]: print(token, sp.piece_to_id(token))6.3 模型“死记”训练集,下游任务不涨
如果训练 loss 不断下降,而评测集指标停滞,首先要怀疑评测数据污染,其次要怀疑数据分布过于狭窄。学术语料如果只来自单一期刊或单一爬虫源,模型会记忆文章片段,但无法泛化到问答和推理场景。
建议在数据配比中保留 10% 以上的通用文本,例如 Wikipedia 和书籍,并定期生成一批与训练数据风格不同的 prompt 做人工评测。
7. 学习环境、单卡环境和生产环境的边界
小团队从零训练模型,必须区分清楚自己是在哪个环境里工作,不同环境的工程要求完全不同。
| 环境类型 | 硬件参考 | 数据规模 | 目标 | 核心任务 |
|---|---|---|---|---|
| 学习环境 | CPU、Colab、小显存显卡 | 100M 到 1B token | 跑通代码链路 | 理解训练循环、分词器、评测流程 |
| 单卡开发环境 | 24GB 显存显卡 | 5B 到 20B token | 小规模实验验证 | 调超参、测数据、排 bug |
| 生产训练环境 | 多卡集群、多节点 | 50B token 以上 | 发布模型权重 | 监控、断点续训、数据版本管理、模型评估 |
7.1 本地学习怎么跑通最小案例
如果是学习目的,不需要从 1B 开始。可以先建一个 10M 到 50M 参数的小模型,用几万条文本跑几十步,确认数据管线、训练循环、评测脚本都能跑通。
这个阶段要重点关注过程而不是结果:
- 每次运行后检查 loss 是否稳定下降
- 观察日志里是否出现 NaN 或异常梯度
- 用固定 prompt 看生成结果是否有变化
建议把整个项目放在一个仓库里,使用固定版本的依赖。llm、tokenizers、datasets、transformers 这四类库的版本变化都会导致行为差异。
7.2 单卡 24GB 训练 1B 的可行方案
单卡 24GB 训练 1B 是可行的,但要把很多设置在“勉强可用”的边界上:
- 使用 bf16,不单独保存 fp32 权重副本
- 开启激活检查点,牺牲约 30% 的吞吐量换取显存
- 微批次大小设为 1,梯度累积设为 16 到 32
- 序列长度从 4096 降到 2048,先保证能跑
- 使用 DeepSpeed ZeRO-1 把优化器状态分片并可选 offload
一个参考命令:
deepspeed train.py \ --deepspeed ds_config.json \ --model config.json \ --batch_size 1 \ --grad_accum_steps 32 \ --seq_len 2048 \ --bf16这种配置的吞吐量不会太高,但它能让小团队在不依赖集群的情况下完成 1B 模型的完整训练链路,这对验证数据方法和评测流程已经足够。
7.3 生产训练还必须补哪些模块
进入生产环境后,单机脚本要扩展成一套工程系统:
- 配置外置:训练参数、数据路径、模型配置全部用配置文件注入
- 日志监控:loss、梯度范数、吞吐量、显存、温度全部上报
- 数据版本管理:每次实验记录语料来源、清洗脚本和版本哈希
- 模型注册:每个 checkpoint 记录评测结果和训练配置
- 回滚机制:loss 异常时自动回退 checkpoint
- 权限与安全:训练数据、代码、权重按角色隔离
很多开源项目失败不是因为模型架构不对,而是因为实验不可复现。配置、数据、代码、评测结果和权重必须捆绑记录,这是小团队最值得投入的工程习惯。
8. 小团队训练完模型之后:评测、落地和扩展
8.1 模型发布前要做的检查清单
无论模型是发到 Hugging Face 还是内部使用,发布前建议按清单逐项检查:
- 模型 config 与训练配置一致,隐藏层、头数、词表大小无偏差
- 分词器文件与模型 config 里的 vocab_size 一致
- 特殊 token ID 在加载后能正确对应
- 评测报告包含任务名称、prompt 模板、运行次数、均值与方差
- 训练数据和评测数据无重叠
- 生成样例覆盖多个领域,不只有训练集风格
- 已知限制要写明,例如上下文长度上限、领域外表现差、存在幻觉可能
- 数据来源和训练资源要说明,便于他人判断可复现性
8.2 从基座模型走向应用:SFT、RAG、Agent 与 MCP
预训练得到的 1B 模型只是基座,不能直接作为产品使用。常见扩展路径是:
- SFT 微调:用问答数据让模型学会回复格式
- DPO 或 RLHF:让模型对齐偏好,减少有害和空洞输出
- RAG 检索增强:把学术论文、知识库的检索结果拼到 prompt 里,缓解幻觉
- Agent 工具调用:让模型按格式输出调用参数
- MCP 协议:通过标准化接口连接外部工具和数据源
- 量化与推理服务:用 vLLM、TGI 或 GGUF 部署,降低显存和延迟
对 1B 模型来说,RAG 尤其重要。它本身知识容量有限,无法记住整个学术领域的事实,但通过检索可以把外部知识带进上下文,在算力受限的场景里性价比很高。
8.3 对小团队和学术研究者的建议
从零训练 1B 模型,最重要的不是模型结构,而是流程纪律。每一次实验都记录数据版本、配置版本、日志路径和评测结果;每次改动只动一个变量;每个 checkpoint 都能独立加载和评测。
对新手来说,最值得做的一个练习是:在单卡环境下,用公开语料训练一个 100M 到 300M 的小模型,完整走一遍清洗数据、训练分词器、搭建模型、训练、评测、发布检查清单的流程。这个小模型的表现并不重要,真正收获的是理解整条链路里每个环节的失败模式。
AQ 这类项目给行业的启示在于,小团队也能做有意义的基础模型工作,前提是把工程边界控制好。1B 规模既是算力限制下的务实选择,也是研究训练方法和数据策略的合适实验场。下一步无论往更大规模走,还是往垂直领域微调走,这段从零训练的经验都会成为最踏实的底子。