6GB显卡预训练11亿参数模型:全是已发表技术,实测需375天
核心数据(RTX 4050 6GB实测)
| 指标 | 数值 |
|---|---|
| 模型规模 | 11.1亿参数(预训练,非推理) |
| 峰值显存 | 4.51 GB(还剩1.5 GB) |
| 吞吐量 | 4096上下文下每秒1579个token |
| 预训练时间(Chinchilla最优) | 360亿token约需375天 |
让预训练成为可能的5项技术组合
没有任何新算法——每一个组件都是已发表的工作并注明出处。真正的工作在于让它们协同运行,并测量其代价。
| 技术 | 省掉了什么 | 节省幅度 |
|---|---|---|
| 块坐标下降(BAdam) | 除当前块外所有块的优化器状态 | 优化器状态约减小8倍 |
| CPU权重卸载 | 非活跃块完全离开GPU | 随模型深度扩展 |
| 三值权重(BitNet b1.58) | 32位权重变成 -1/0/+1 | 权重压缩2.33倍 |
| 共享词嵌入 | 用一张词表代替两张 | 1B规模下节省1.03亿参数 |
| 梯度检查点 | 不再存储激活值,反向时重算 | 激活值降到约50 MB |
| 分块交叉熵 | logits按切片逐块生成,反向时重算 | 4096×5万词表下节省约1.5 GB |
其中两项此前无法同时工作。共享词嵌入在输入查表和输出层之间共用同一个张量,而卸载系统直接拒绝这种用法——它要求每个参数严格属于某一个块,而这个张量同时属于两个块。该组合被标记为不支持。
解决办法(Issue #157):固定共享词表——一个可从两个模块访问的参数绝不会被换出,因为前向传播在两端都要用到它,而任何一个块的驻留窗口都覆盖不了这两次使用。代价:一张词表常驻显存(1B规模约250 MB)。结合分块交叉熵(--chunked-loss 512)后,之前会OOM的组合(共享词嵌入 + CPU卸载)现在可以工作了——1.11B参数、fp32、5步测试运行在6 GB的79% = 4.85 GB,没有OOM。固定的共享词表(206 MB bf16)加上分块损失(节省约1.5 GB)使得四项技术组合(BCD + 卸载 + 共享 + 分块)完全可组合。
架构
模型是一个门控DeltaNet混合体(10个DeltaNet块 + 第3层和第10层的2个滑窗注意力层)。该架构是对已发表工作的重新实现:
- 经典门控DeltaNet更新(GLA / Mamba-2风格衰减门)
- 每头输出RMSNorm归一化(正确的加权平均读出)
- 输出门 + 短因果深度卷积(Mamba / RWKV-7风格)
常量内存推理——无KV缓存、状态固定——是整个模型家族(Mamba、RWKV、RetNet)的定义性属性,不是本项目发明的。
为什么要混合注意力?推理时的精确召回
纯循环模型(DeltaNet、Mamba、RWKV)把全部历史压缩进一个固定状态——对吞吐量很好,但无法从上下文中精确回忆出原始token。混合架构加入2个滑窗注意力层(第3层和第10层,窗口=128)正是为了解决这个问题。
在200次试验的合成召回探针上实测(虚构实体,零知识混淆):
| 模型 | 近窗召回(≤128 token) | 远窗召回(>128 token) |
|---|---|---|
| 纯DeltaNet主干(10层) | 4–5% | 0.5%(噪声水平) |
| 混合(+2注意力层,8K步) | 35.5%(主干的9倍) | 8.5%(主干的17倍) |
关键洞察:注意力窗口(128 token)可以在窗口内精确召回token——这是循环状态在数学上无法提供的。超出窗口后,循环模型和窗口注意力都回落到噪声底线(约0.5%)。
这意味着在推理时:混合模型可以读取并引用上下文最后约128个token的精确段落,而纯循环模型只能给出有损的语义梗概。对于编码代理、工具调用和基于事实的问答,这正是“它幻觉出一个函数签名”和“它从上下文里精确复制签名”的区别。
注意力层只增加约13%的显存(2个TransformerBlock,窗口化KV缓存各限制为128个key)和约7%的预训练步耗时——为最关键处的精确召回付出的小代价。
没有这套组合会怎样(同一张卡,标准脚本——预训练)
| 模型 | 标准脚本 | 这套组合 |
|---|---|---|
| 157M | 3.38 GB — 放得下 | 放得下 |
| 243M | 4.80 GB — 放得下 | 放得下 |
| 434M | 9.04 GB — 溢出 | 放得下 |
| 1.07B(循环) | 远超显卡容量 | 4.00 GB |
| 1.07B(普通Transformer) | 远超显卡容量 | 4.04 GB |
| 1.58B | 远超显卡容量 | 5.92 GB — 天花板 |
标准脚本约能跑243M,用这套组合约能跑1.58B——约6.5倍。两个数字都在同一张6 GB卡、4096上下文下实测,在首次溢出到系统内存处停止(Windows/WDDM下超出显存不会报错——它会在4–15倍的减速下悄悄溢出)。
表格没有显示的是:Transformer和循环混合模型在这些规模下需要几乎相同的内存(243M时4.80 GB vs 5.09 GB),而一个普通的1.07B Transformer在同一套组合下只占4.04 GB。6.5倍来自预训练技术组合,而不是架构——所以它适用于你正在预训练的任何模型,包括普通Transformer。
这对你的GPU意味着什么(投影显存上限——外推值,非实测)
⚠️ 只有6 GB行是实测的。其他行都是根据6 GB上限(约1.58B)做线性外推,假设节省比例固定。真实的显存上限会因GPU架构、WDDM行为和批量大小而异。请视为上界估计。
这套组合随显存扩展。在6 GB卡上我们实测上限约1.58B。以下是同一组合对更大显卡的投影(从实测点线性外推,在首次WDDM溢出处停止):
| GPU显存 | 最大模型(循环混合) | 最大模型(普通Transformer) | 预训练时间(Chinchilla) |
|---|---|---|---|
| 6 GB(RTX 4050笔记本) | 约1.58B | 约1.58B | 1.1B约375天 |
| 8 GB(RTX 3070/4060笔记本) | 约2.2B | 约2.2B | 1.5B约160天 |
| 12 GB(RTX 3060/4070) | 约3.5B | 约3.5B | 2.5B约75天 |
| 16 GB(RTX 4080) | 约4.8B | 约4.8B | 3.5B约40天 |
| 24 GB(RTX 3090/4090) | 约7.5B | 约7.5B | 5B约18天 |
关键洞察:6.5倍乘数(标准脚本 → 这套组合)适用于每一档显存,因为节省是按比例的:
- BCD:优化器状态节省约8倍(随参数量扩展)
- 卸载:非活跃块到CPU(随深度扩展)
- 三值:权重压缩2.33倍(固定比例)
- 共享词嵌入:1B规模节省1.03亿参数(固定绝对值)
- 分块损失:4K上下文节省约1.5 GB(固定绝对值)
上限随显存线性增长,因为主导项(权重 + 优化器状态)随模型规模扩展,而我们的技术按固定比例压缩它们。24 GB卡不只是“多放一点”——它能放约5倍于6 GB卡的参数量。
更大显存时发生的变化:
- 可以去掉 --offload(没有PCIe瓶颈)→ 约20%加速
- 可以增大 --batch-size → 更好的GPU利用率
- 更早撞上算力墙(矩阵乘在100%上限运行)
- 24 GB下,7B预训练约18天——真的能跑完
系统内存需求(常被忽略)
| GPU显存 | 最低系统内存 | 推荐 | 原因 |
|---|---|---|---|
| 6 GB | 16 GB | 32 GB | 卸载的权重 + 数据集 + 操作系统 |
| 8 GB | 16 GB | 32 GB | 卸载的权重随模型深度增长 |
| 12 GB | 24 GB | 48 GB | 需要卸载的更少,但数据集更大 |
| 16 GB | 32 GB | 64 GB | 可以不卸载,但数据集更大 |
| 24 GB | 48 GB | 96 GB | 全模型进显存,但数据集 + 操作系统 |
系统内存重要的两个原因:
1. CPU卸载(--offload):非活跃块的权重被换出到系统内存。1.11B带卸载时,约3–4 GB权重驻留在系统内存。16 GB系统内存 + 6 GB显存没问题;8 GB系统内存会在主机侧OOM。
2. WDDM静默溢出(Windows):Windows上超出显存不报OOM——驱动通过PCIe以4–15倍减速悄悄溢出到系统内存。我们实测2.45B模型溢出时每个micro-batch耗时82秒。如果系统内存也满了,溢出的数据无处可去 → 直接崩溃。
经验法则:带卸载时系统内存 ≥ 2× GPU显存;不带卸载时 ≥ 1.5×。我笔记本里的16 GB是6 GB显存 + 卸载的最低要求。
内存实际花在哪里(48M、4096上下文实测)
| 项目 | 大小 |
|---|---|
| 计算损失——4096个位置 × 50257词表 | 约1500 MB |
| CUDA上下文、内核、工作区 | 约400 MB |
| 权重(三值) | 约960 MB |
| 优化器状态(BCD:一次一块) | 约200 MB |
| 层激活(梯度检查点) | 约50 MB |
| 固定共享词表(如启用) | 约250 MB |
最大的消耗者不是模型——而是词表上的交叉熵(约权重的3倍)。分块损失(每次512个位置)在反向时重算logits而不是保存它们,节省约1.5 GB。这正是让之前OOM的组合(共享 + 卸载)得以工作的原因。
权衡:共享词嵌入 vs 卸载
| 配置 | 参数量 | 共享 | 显存 | 状态 |
|---|---|---|---|---|
| 博客声称(无BCD卸载) | 1.07B | ✓ | 4.00 GB | 目标 |
| 1.11B运行(不共享) | 1.11B | ✗ | 3.48/4.51 GB | 1350步,正常 |
| 共享 + 卸载 + 分块损失 | 1.11B | ✓ | 4.85 GB(79%) | 5步fp32,正常(已修复) |
| 共享 + 卸载(无分块) | 1.07B | ✓ | >6 GB | OOM(固定250 MB + 开销) |
| 57M测试 | 57M | ✓ | 正常 | 全部5项技术集成 |
残酷的现实:放得下 ≠ 训得完
1B模型在这里跑约1100 token/秒,而正确预训练一个需要约360亿token——大约375天。这套组合抬高的是你能装下多少;它没碰到的墙是算力。
为什么是375天?
首先对这卡最擅长的一件事(GEMM)计时,找出真实速度——不是包装盒上的数字。然后把一个预训练步骤拆成几部分:
| 一个训练步骤的组成部分 | 占比 | 离极限有多近? |
|---|---|---|
| 矩阵乘法(真正的数学) | 33% | 100.6% — 已到天花板 |
| 大操作之间的小操作 | 36% | 受内存速度限制 |
| 逐步遍历序列 | 15% | 在等待,而非计算 |
| 搬运数据 | 15% | 装进6 GB的代价 |
数学运算跑到了这张GPU实测最快速度的100.6%。那里已经没有余地了。剩下的所有改进乐观相加也只有20–25%,把375天变成约300天。这一年不是代码烂——是360亿token。
为什么是30 token/参数,不是20?
Chinchilla论文(Hoffmann et al., 2022)建议Transformer约20 token/参数。但本架构表现不同:
在三个模型规模、恒定学习率下实测(每个检查点都是一个模型训练到那个token数,不是衰减调度中途的快照):
| Token/参数 | 留出损失改善 | 计算开销 |
|---|---|---|
| 20 → 30 | 提升1.9–3.3% | +50% token |
| 30 → 40 | 提升约1.1% | +33% token |
30是收益转折点。从20到30用50%额外计算换显著质量提升;从30到40用33%额外计算只换边际收益。
为什么这个架构需要更多数据:
- 循环状态对历史的压缩方式不同于注意力
- 精确token召回(混合模型的注意力窗口)需要更多曝光来学习该模式
- 门控DeltaNet更新比静态注意力矩阵有更多可学习动态
这个现象如何随模型规模变化仍未解决。把标准缩放形式拟合到所有实测点会得到退化答案——它意味着1B模型需要的数据比它的参数还少,与每一条原始曲线矛盾。三个模型规模不足以区分各项。
实用规则:用本组合时至少按30 token/参数做预算。1.11B模型需要约330亿token(1579 tok/s下375天)。按20 token/参数你会停在约220亿token,留下约2–3%的质量损失。
6 GB卡适合做什么
| 目标 | 所需规模 | 训练时间 |
|---|---|---|
| 输出格式正确的工具调用 | 约1–2.7亿 | 数天 |
| 维持连贯对话 | 约2.7亿 | 约24天 |
| 写出值得信任的代码 | 远超此规模 | 遥不可及 |
前两个是这里真正做过的项目。第三个不是——大好几倍、用数万亿token训练过的代码模型依然做不好。
使用的预训练配置(1.11B运行)
python train_edge_conversational.py \
--arch hvn \
--d-model 2048 \
--num-layers 24 \
--n-heads 64 \
--seq-len 4096 \
--batch-size 1 \
--grad-accum 8 \
--precision bf16 \
--optimizer muon \
--muon-lr 0.02 \
--lr 3e-4 \
--tie-embeddings \
--offload \
--block-coordinate \
--bcd-steps-per-block 100 \
--chunked-loss 512 \
--pretrain-mix 0.0,0.0,0.0,1.0 \
--min-tokens-per-param 0
关键参数说明:
- --arch hvn — 门控DeltaNet块(默认)
- --tie-embeddings — 嵌入层与解码器共享词表(省1.03亿参数)
- --offload — 非活跃块的CPU权重卸载(需要 --grad-checkpoint)
- --block-coordinate — BAdam:一次训练一个块,清空其优化器状态
- --chunked-loss 512 — 每次计算512个位置的损失,反向时重算(省约1.5 GB)
- --precision bf16 — 混合精度(快2–3倍,省一半显存,需要RTX 30/40系列)
- --optimizer muon — 相同损失少1.6倍步数(但每步慢约12%)
真正浪费过时间的坑
- torch.compile 在这个架构上慢2.4倍(4次独立测量)。用 --fast,它会主动跳过它。
- 裸 --no-fast 会悄悄掉回fp32——74.6秒/步而不是8.66秒。要手动重新加上精度参数。
- 默认数据混合把预训练限制在1.3亿token。OpenAssistant只有1570万token却占35%份额,把整个预算绑死了——只够训一个约700万参数的模型。1B模型用默认配置得到的数据比需要少270倍,而且损失曲线在反复重读同样token时看起来完全正常。传 --pretrain-mix 0.0,0.0,0.0,1.0 改用FineWeb-Edu。
- 第50步之前什么都不打印。短任务看起来像挂了,其实没事。盯着 training_log.csv,它每行都刷新。
- tokenizer.vocab_size 不包含添加的token。用 len(tokenizer) 来确定嵌入表大小——否则哨兵token会索引越界(裸CUDA设备端断言)。
失败的尝试(死路,刻意记录)
11个被测试并否定的想法(实测,不是猜测)
- 原始HVN架构——同等数据下比门控DeltaNet差13%(已撤回)
- torch.compile——慢2.4倍(4次独立测量)
- 混合专家——不如直接扩大稠密模型
- 张量列分解——参数减半,差22%
- GHOST(权重外推)——不如什么都不做
- 循环/权重共享模型——3层×4次不如12层,噪声底线差11倍
- 分块并行扫描——慢4倍(串行三角求解)
- 序列长度课程——慢2.9%(针对20个SM的占用率)
- 逐token纠缠率——83M规模无收益
- 熵门控早退——不工作(已撤回)
- 不带BCD的Adafactor——溢出到主机内存,慢10倍
5项撤回(本项目撤销的结论)
- 原始HVN“新架构”宣称——比已发表的DeltaNet差13%
- #117 缓存提供未训练权重——整个关于聚合损失的史诗,那损失仍在下降
- #122 5项召回门——在运气主导的测量上扩大规模
- #132 关于训练损失的结论——多轮次机制测量的是记忆,不是质量
- #138 CUDA图“6.8倍加速”——拿一次重放与同状态下一次eager步骤比较;两者做同样的事,在max|dlogits|=0时通过,而上下文悄悄停止增长
4个实测比什么都不做更慢的优化
- torch.compile — 慢2.4倍(20.72 vs 8.66秒/步)
- 分块并行扫描(方案A)— d=64时慢2–4倍
- 序列长度课程 — 83M规模慢2.9%
- 移除注意力层 — 慢6.4%(训练时注意力是便宜的部分)
可证伪测试抓到的8个静默bug
- #117 — 缓存提供未训练权重;只有测试在看聚合损失(一直在下降)
- #122 — 5项召回门靠运气通过,不是信号
- #132 — 在多轮次机制中按训练损失评分(测的是记忆)
- #138 — CUDA图比较同状态下的1步对1步;两者相同,0差异通过
- #155 — tokenizer.vocab_size 不包含添加的token;嵌入表少3行,CUDA越界断言
- #157 — 分词后的 .bin 缓存用字符串"code"而不是语料库做键;悄悄重用了旧缓存
- #112 — 预训练审计把模型deepcopy到GPU,在较大规模时OOM了6 GB卡
- #159 — 余弦LR调度在每次恢复时叠加衰减(锯齿LR)
局限(扩展前必读)
- 架构是对已发表工作的重新实现——门控DeltaNet、GLA/Mamba-2门控、Mamba/RWKV-7卷积和输出门。代码是原创的;设计不是。
- 常量内存推理是那个整个模型家族的性质,不是本工作的。
- 这里没有48M以上模型训练到收敛。每一个缩放声明都是在小于该规模的尺寸上测得的,不应外推到更大规模。
- 仓库中引用的十万token推理内存数据是算出来的,不是实测的,因此本页不引用它。
- 对比是单种子:这消除了组间运气差异,但消除不了两组共享的运气。同一个模型训练三次,只改变起始随机数,结果移动了0.089。本项目报告的一些差异小于这个值,如果不跨多个种子重复,就无法与运气分开。
资源
- 代码:https://gitlab.com/komalbarun/kramba-ai (Apache-2.0)
- 文档:https://gitlab.com/komalbarun/kramba-ai/-/tree/main/docs
- Issue / 讨论:https://gitlab.com/komalbarun/kramba-ai/-/issues
更新
2026-09-26:之前会OOM的组合(共享词嵌入 + CPU卸载 + BCD)现在结合分块交叉熵(--chunked-loss 512)后可以工作了。1.11B参数、fp32、5步测试运行在6 GB的79% = 4.85 GB,没有OOM。固定的共享词表(206 MB bf16)加上分块损失(节省1.5 GB)使得四项技术组合(BCD + 卸载 + 共享 + 分块)完全可组合。这是缺失的那块拼图——博客中的1.11B运行用的非共享词嵌入(3.48/4.51 GB);共享 + 卸载组合此前在没有分块损失时是OOM(>6 GB)的。
许可证
Apache 2.0——可自由使用、修改和出售,需注明出处并授予专利许可。许可证覆盖代码,不覆盖训练好的权重。检查点文件是独立作品,保留所有权利。你训练出的权重是你的。你构建的产品是你的。