How karpathy/nanochat Works
nanochat并非为了与大型闭源模型或企业级训练框架竞争,而是定位于降低LLM科研和实验的门槛。相较于早期仅覆盖预训练的nanoGPT,它提供了完整的“预训练-微调-部署”闭环。其核心竞争优势在于通过“单旋钮”复杂度调控(只需设定模型深度),自动推导所有超参数,极大地简化了训练流程,并以极低的成本(约72美元)复现GPT-2级别的模型能力,是专为预算有限、追求高性价比和代码可控性的个人开发者与研究者设计的“强大基线”实验平台。
Overview
nanochat并非为了与大型闭源模型或企业级训练框架竞争,而是定位于降低LLM科研和实验的门槛。相较于早期仅覆盖预训练的nanoGPT,它提供了完整的“预训练-微调-部署”闭环。其核心竞争优势在于通过“单旋钮”复杂度调控(只需设定模型深度),自动推导所有超参数,极大地简化了训练流程,并以极低的成本(约72美元)复现GPT-2级别的模型能力,是专为预算有限、追求高性价比和代码可控性的个人开发者与研究者设计的“强大基线”实验平台。
一个极简、可复现的端到端语言模型训练工具,旨在让研究者和开发者能以低于100美元的成本,在单机上完成从数据处理、预训练、微调到部署聊天的完整流程。
How It Works: End-to-End Flows
端到端基础模型预训练('GPT-2 Speedrun')
该流程覆盖了从零开始训练一个具备GPT-2级别能力的基础语言模型的全过程,是项目的核心价值展示。用户通过执行一个脚本,系统会自动完成数据下载、模型规格设定、分布式训练、质量监控和检查点保存。此流程的设计核心在于用极致的自动化和效率,将原本需要数万美元和复杂工程的预训练任务,压缩到单机几小时和百美元以内的成本。它首先从网络拉取并准备好大规模文本数据,然后依据用户设定的“深度”旋钮,智能地推导出最优的模型架构和训练参数。在多GPU上,训练过程利用数据并行和梯度累积高效进行,同时可选用FP8混合精度进一步提速。系统会周期性地评估模型的BPB和CORE分数,让用户直观看到模型能力的成长,并在训练结束后产出一个可用于下游微调或直接聊天的基础模型检查点。
- 启动训练脚本,系统自动下载并准备数据
- 系统根据用户指定的“深度”,自动推导模型架构和训练参数
- 数据加载器通过高效打包策略,为GPU提供无填充的训练批次
- 在分布式环境中执行训练循环,支持FP8加速
- 周期性评估模型质量(BPB/CORE)并保存可恢复的检查点
- 训练完成,产出最终的基础模型检查点
通过Web或命令行与模型进行交互式聊天
此流程让用户能直观地体验和评估训练好的模型。用户可以通过一个简洁的Web界面或本地命令行,与模型进行实时、流式的对话。当用户在Web界面发起请求时,后端的并发工作池会分派一个空闲的GPU来处理。推理引擎接收到对话历史后,通过高效的“一次预填充、多次增量解码”的KV缓存机制,快速生成回复,并通过SSE技术将token逐字推送到前端,创造出流畅的“打字机”效果。该流程还内置了一个轻量级的“计算器”工具,当模型判断需要进行精确计算时,能生成特殊指令来调用它,并将计算结果无缝地插入到回复中。整个交互体验的核心在于低延迟和资源的高效利用,使得在单台机器上也能获得流畅的本地ChatGPT体验。
- 启动Web服务,系统在每个GPU上加载模型副本并创建工作池
- 用户在Web UI或CLI输入问题,系统校验并格式化请求
- 推理引擎使用KV缓存机制进行高效的流式生成
- (可选)模型生成特殊指令,触发“计算器”工具执行计算并注入结果
- Web服务通过SSE将生成的token流式、安全地传输到前端
- 用户看到模型逐字生成的完整回复
通过强化学习对模型进行特定任务优化
这是一个进阶流程,旨在将一个已经过微调(SFT)的模型,针对某个特定可量化评估的任务(如数学题解答)进行能力强化。其核心思想是“用结果说话”,不再仅仅模仿标准答案,而是直接根据任务的成功与否(奖励信号)来调整模型。系统会针对一个任务问题,让模型生成多个不同的答案,然后使用任务自带的评估函数给每个答案打分。得分高的答案所对应的生成路径会得到正向激励,而得分低的则会受到抑制。为了保证学习的有效性,系统通过掩码机制,确保梯度只施加在模型自己生成的回复上,而不会影响到对用户问题的理解。通过这种方式,模型可以在特定领域的能力上实现突破,获得超越SFT的效果。
- 加载一个SFT阶段的模型作为起点
- 针对任务(如GSM8K)的输入,批量生成多个候选答案
- 使用可插拔的任务奖励函数,为每个答案计算得分
- 计算优势值,并构建策略梯度损失
- 通过掩码机制,确保只在模型生成的回复部分计算并反向传播损失
- 更新模型权重,并周期性评估pass@k等指标,保存最优模型
Key Features
数据与分词管线
提供一套从原始网络文本到模型可训练批次的全自动化数据处理管线。其核心设计在于通过高效的文档打包策略最大化计算资源利用率,并通过与词表无关的度量标准,确保不同模型间性能的可比性。该模块解决了数据准备阶段的效率、浪费和评估一致性三大核心问题。
- BOS对齐的最佳适配文档打包 — 【用户价值】在预训练中,避免因填充(Padding)无效token导致GPU算力浪费,并保证每个文档都有清晰的上下文起始边界,提升训练质量。 【设计策略】采用一种“牺牲数据、保全算力”的打包策略,优先保证序列被完全利用。 【业务逻辑】 Step 1: 为一个批次中的每行序列(容量为T+1)分配一个文档缓冲区。 Step 2: 从缓冲区中寻找能放入当前行剩余空间的最大文档,并将其置入。 Step 3: 重复Step 2,直到没有任何文档能完整放入剩余空间。 Step 4: 为填满最后的空隙,选择缓冲区中最短的文档,并将其裁剪至恰好能填满剩余部分。所有放入序列的文档都强制以BOS(序列开始)符开头。 【权衡】 好处:实现了100%的GPU计算利用率,无任何填充浪费,且保证了每个文档上下文的独立和完整性。 代价:在序列长度为2048时,约有35%的token数据因裁剪而被丢弃。此策略适用于数据量极其庞大(如网页语料)而算力成本高昂的场景。
- 词表无关的模型质量度量(BPB) — 【用户价值】公平地比较使用不同分词器(Tokenizer)训练出的模型的性能。 传统的“每个token的困惑度”指标会因分词方式不同而产生偏差。 【设计策略】将评估标准从“token”转移到更底层的“字节”,衡量模型对原始文本的压缩能力。 【业务逻辑】 Step 1: 在训练分词器时,生成一个“token ID到字节长度”的映射表。即计算每个token解码为字符串后,其UTF-8编码所占的字节数(特殊token计为0字节)。 Step 2: 在模型评估时,计算每个token的损失(nats),并使用上述映射表查找其对应的字节长度。 Step 3: 总损失(BPB)= (所有token的nats损失之和 / 所有token的字节长度之和) / ln(2)。 Step 4: 最终得到的“每字节比特数”(Bits Per Byte)指标,与词表大小和分词策略无关,可以直接对比不同模型的压缩效率。
- 标准化的聊天与工具调用分词模式 — 【用户价值】为多轮对话、指令微调(SFT)和强化学习(RL)提供一套统一、清晰的对话结构表示,能够明确区分用户输入、模型回复和工具调用,从而实现精确的监督学习。 【设计策略】定义一套固定的特殊token,用于包裹对话中的不同角色和内容,并通过掩码(mask)机制控制损失计算范围。 【业务逻辑】 - **特殊Token定义**: 包含`<|bos|>` (序列开始), `<|user_start|>`/`<|user_end|>` (用户), `<|assistant_start|>`/`<|assistant_end|>` (助手), 以及用于工具调用的 `<|python_start|>`/`<|python_end|>` 和 `<|output_start|>`/`<|output_end|>`。 - **SFT数据渲染**: 将对话历史转换为token序列。用户输入和工具输出部分,其对应的监督标签掩码为0(不计算损失);只有模型应学习模仿的助手回复部分,其掩码为1(计算损失)。 - **RL数据渲染**: 移除最后一轮助手的回复,仅保留`<|assistant_start|>`,以提示模型开始生成,用于后续的策略优化。
- 自动化的大规模预训练数据集管理 — 【用户价值】为用户屏蔽了处理百亿级token语料库的复杂性,提供开箱即用的数据下载、缓存、切分和分布式读取能力。 【设计策略】通过脚本自动化处理FineWeb-Edu数据集的下载和分布式训练的DDP分片逻辑。 【业务逻辑】 Step 1: 自动从HuggingFace下载zstd压缩的Parquet格式数据分片。 Step 2: 下载过程支持多进程、5次指数退避重试和原子化重命名,保证下载的稳定性和文件完整性。 Step 3: 将1823个分片中的最后一个作为验证集,其余作为训练集。 Step 4: 在分布式训练(DDP)中,每个GPU进程(rank)自动只读取属于自己分片部分的数据(例如,rank 0读取第0, N, 2N...个数据块,rank 1读取第1, N+1, 2N+1...个数据块,N为GPU总数),实现数据并行。
基础模型预训练引擎
这是模型训练的核心控制平面,其产品设计的精髓在于将极其复杂的LLM超参数调优过程,抽象为一个唯一的“复杂度旋钮”——模型深度。用户只需调节这一个参数,系统即可自动推导并配置出计算最优的模型架构、训练批次和迭代次数,实现了极简操作与高效训练的统一。此模块还集成了FP8混合精度训练、断点续训和质量监控等关键工程能力。
- “单旋钮”计算最优模型规格自动推导 — 【用户价值】让不具备深厚LLM工程经验的用户也能轻松训练出计算最优的模型。用户无需关心宽度、头数等复杂参数,只需决定模型“大小”(深度)即可。 【设计策略】将模型深度(depth)作为唯一输入,基于经验公式和硬件对齐规则,反向推导出其他所有架构参数。 【业务逻辑】 Step 1: 用户指定模型深度(`--depth`),例如24。 Step 2: 系统根据预设的宽高比(aspect ratio)计算出一个基础维度(`base_dim = depth * aspect_ratio`)。 Step 3: 为了满足GPU硬件(特别是Attention计算)的对齐要求,将基础维度向上取整到注意力头维度(`head_dim`)的整数倍,得到最终的模型嵌入维度(`model_dim`)。 Step 4: 根据最终模型维度和头维度,计算出注意力头的数量(`num_heads = model_dim / head_dim`)。 Step 5: 将计算出的层数、头数、嵌入维度等参数组装成模型配置文件,用于构建GPT模型。
- 训练预算感知的迭代与批次自动配置 — 【用户价值】用户可以从更业务化的目标(如“训练一个消耗X算力的模型”)出发,而无需手动计算迭代步数和批次大小。系统会自动完成这些繁琐的转换。 【设计策略】提供多种训练目标设定方式,并基于scaling law自动预测最优批次大小,最后通过梯度累积来匹配任意硬件配置。 【业务逻辑】 Step 1: **确定训练时长**:用户可指定总迭代步数,或指定总算力预算(FLOPs),或指定数据-参数比。系统会据此计算出需要训练的总token数。 Step 2: **预测最优批次**: 如果用户未指定总批次大小(`total_batch_size`),系统会基于一个参考模型(B_REF=2^19)和scaling law公式(`B_opt ∝ D^0.383`)来预测一个理论上的最优批次大小,并取整到最接近的2的幂次方。 Step 3: **计算梯度累积**: 系统会计算出单台设备单步前向传播处理的token数,再乘以设备总数,得到一个物理批次能处理的token数。用总批次大小除以物理批次大小,即可得到梯度累积的步数(`grad_accum_steps`)。系统会强制要求能整除,否则报错,提示用户调整配置。
- FP8混合精度训练与安全的BF16评估 — 【用户价值】在兼容的硬件(如H100)上利用FP8格式加速训练,同时保证模型评估指标(如BPB、CORE分数)的稳定性和可比性,避免因精度变化导致评估结果失真。 【设计策略】采用“训练时FP8,评估时动态切换回BF16”的策略。 【业务逻辑】 Step 1: **FP8转换**:在训练开始时,如果用户启用`--fp8`且在CUDA环境,系统会使用`torchao`库将模型中输入输出维度均能被16整除的线性层(`nn.Linear`)转换为`Float8Linear`变体,以利用硬件加速。 Step 2: **评估前切换**:当需要进行模型评估(计算BPB或CORE分数)时,一个上下文管理器会介入。它会遍历模型,将所有`Float8Linear`层临时替换为标准的`nn.Linear`层,这些新层与原层共享权重数据(无需数据拷贝)。 Step 3: **BF16评估**:在上述临时替换的BF16模型状态下,完成评估计算。 Step 4: **评估后恢复**:评估结束后,上下文管理器会自动将模型恢复到之前的`Float8Linear`状态,以便继续进行FP8训练。
- 支持断点续训的检查点管理 — 【用户价值】支持长时间的预训练任务可以从失败中恢复,不仅恢复模型权重,还能恢复优化器状态和数据加载位置,最大程度减少算力浪费。 【设计策略】将模型、元数据和优化器状态分离存储,其中优化器状态按分布式训练的每个进程(rank)分别保存。 【业务逻辑】 Step 1: **保存**: 在指定的时间步,由主进程(rank 0)负责保存模型权重(`model_{step}.pt`)和元数据(`meta_{step}.json`),元数据中包含了数据加载器当前的文件和行号位置。 Step 2: **分片保存优化器**: 每个GPU进程(rank 0, 1, 2...)各自保存自己所持有的那部分优化器状态(`optim_{step}_rank{rank}.pt`)。 Step 3: **加载**: 恢复训练时,系统加载模型权重和元数据,并根据元数据中的信息让数据加载器跳转到上次中断的位置。同时,每个进程加载回自己对应的优化器状态分片。 Step 4: **向后兼容**: 加载逻辑中包含补丁,例如如果旧的检查点缺少某个参数(如`x0_lambdas`),系统会自动为其创建一个零张量,以兼容模型结构的演进。
聊天模型后训练(SFT与RL)
此模块负责将预训练好的基础模型调教为具备特定能力的聊天模型。它通过模仿学习(SFT)和强化学习(RL)两个阶段,让模型学会遵循指令和对话,并针对特定任务(如数学解题)的成功率进行直接优化。其核心设计在于通过“奖励”信号驱动模型学习,并通过精细的掩码机制,确保模型只学习它应该学习的部分。
- 基于任务奖励的强化学习(RL)优化 — 【用户价值】让模型不再仅仅是模仿语料,而是直接为“做好任务”这一目标进行优化,从而在特定能力(如代码生成、数学推理)上取得超越SFT的效果。 【设计策略】采用一种简化的在线策略梯度方法,通过采样-评估-更新的循环来优化模型。 【业务逻辑】 Step 1: **采样(Rollout)**: 加载一个SFT阶段的模型,针对一个任务的输入(如一道数学题),生成多个不同的答案样本。 Step 2: **评估(Reward)**: 调用任务自带的奖励函数(`task.reward()`),为每个生成的答案计算一个标量分数(如,答案正确得1分,错误得0分)。 Step 3: **计算优势**: 将每个样本的奖励减去当前批次所有样本的平均奖励,得到优势值(Advantage)。这表示某个答案比平均水平好多少。 Step 4: **策略梯度更新**: 计算模型生成这些样本时每个token的对数概率。用优势值加权这些概率,形成策略梯度损失。即,高奖励样本的生成路径会被“鼓励”(增加概率),低奖励样本的路径会被“抑制”。 Step 5: **反向传播**: 基于上述损失更新模型权重。
- 基于掩码的精细化学习目标控制 — 【用户价值】确保模型在训练时只学习“助手应该说的内容”,而不会去学习模仿用户输入、历史对话或者工具调用返回的固定内容,保证了监督信号的纯净性。 【设计策略】利用推理引擎在生成时同步产生的掩码(mask)信息,在计算损失时忽略掉所有非学习目标的token。 【业务逻辑】 Step 1: 推理引擎在生成token序列的同时,会并行生成一个等长的掩码序列。对于用户输入(prompt)、工具强行注入的输出等,掩码值为0;对于模型自己生成的部分,掩码值为1。 Step 2: 在RL训练计算损失前,将目标(target)序列中所有掩码值为0位置的token,替换为PyTorch中的`ignore_index`(默认为-1)。 Step 3: PyTorch的损失函数(如交叉熵)在计算时会自动忽略掉所有`ignore_index`位置,因此梯度将只来自于模型需要学习的、掩码为1的部分。
- 可插拔的任务与奖励组合系统 — 【用户价值】为研究者提供了极大的灵活性,可以轻松添加新的评测任务、自定义奖励函数、或者将多个任务混合/排序,以设计复杂的训练流程(课程学习),而无需修改核心训练代码。 【设计策略】通过定义清晰的任务API(接口)和提供任务组合工具(TaskMixture, TaskSequence)来实现解耦。 【业务逻辑】 - **任务接口**: 每个任务(如GSM8K数学题、SpellingBee拼写检查)都遵循一个统一的接口,必须实现`evaluate()`(评估)和`reward()`(奖励)等方法。 - **训练调用**: RL训练循环不关心具体任务是什么,它只是调用`task[idx]`获取数据,然后调用`task.reward()`获取分数。 - **任务组合**: `TaskMixture`允许将多个任务打包成一个。如果一个任务在列表中出现多次,它的采样权重就会变高(过采样)。`TaskSequence`则支持按顺序执行一系列任务,这对于需要先学习简单技能再挑战复杂技能的“课程学习”场景非常有用。
推理引擎与用户交互界面
此模块是用户与训练好的模型进行直接交互的入口,包含了一个高效的流式推理引擎、一个即开即用的Web聊天界面和一个命令行工具。其产品设计重点在于提供低延迟、高响应性的对话体验。通过“预填充+KV缓存复制”技术实现高效生成,通过“工具注入”机制赋予模型简单的计算能力,并通过多GPU工作池支持在单机上提供并发服务。
- 基于预填充与KV缓存的高效流式生成 — 【用户价值】在进行多轮对话时,显著降低后续生成的延迟。用户输入长篇大论后,模型的首次响应速度更快,后续逐字输出也更流畅。 【设计策略】将prompt处理和token生成分为两个阶段,对计算量大的prompt处理只做一次,并复用其计算结果(KV缓存)。 【业务逻辑】 Step 1: **预填充(Prefill)**: 当收到用户输入(包含历史对话)时,推理引擎将整个文本作为一个批次,一次性通过模型,计算出所有token的键(Key)和值(Value)并存入KV缓存。 Step 2: **复制(Replicate)**: 如果需要生成多个候选回复(`num_samples > 1`),引擎会将这个预填充好的KV缓存复制多份,每个候选回复独享一份。 Step 3: **增量解码(Decode)**: 之后,每生成一个新token,只需将这一个新token输入模型,并结合已有的KV缓存,即可快速计算出下一个token的概率分布。这个过程循环进行,直到生成结束符或达到最大长度。
- 多GPU工作池支持并发聊天 — 【用户价值】允许在单台多卡机器上部署聊天服务,同时处理多个用户的请求,提高硬件利用率和服务的吞吐能力。 【设计策略】在Web服务启动时,为每个GPU创建一个独立的模型副本和工作进程,并通过异步队列来管理和调度这些工作进程。 【业务逻辑】 Step 1: **初始化**: FastAPI应用启动时,会检测可用的GPU数量。对于每个GPU,它会加载一个完整的模型副本,并将其封装成一个`Worker`对象。 Step 2: **入队**: 所有创建好的`Worker`对象都被放入一个异步队列`available_workers`中,等待被调用。 Step 3: **获取与处理**: 当一个HTTP请求到达时,处理函数会尝试从队列中获取一个`Worker`(`await queue.get()`)。如果队列为空(所有GPU都在忙),请求会在此处异步等待。 Step 4: **释放**: 请求处理完毕(无论成功或失败),处理函数都会在`finally`语句块中确保将`Worker`对象归还到队列中(`await queue.put(worker)`),以便其他请求可以使用。
- 保障UTF-8安全的流式API(SSE) — 【用户价值】为前端(Web UI)提供稳定、逐字显示的实时打字机效果,避免因中文字符等占多个字节的字符被截断而导致的乱码问题。 【设计策略】在服务器端进行token缓冲和解码,只有当解码出的文本片段是完整的UTF-8字符时,才通过SSE(服务器发送事件)发送给客户端。 【业务逻辑】 Step 1: 推理引擎逐个生成token。 Step 2: 服务器端维护一个token缓冲区,不断将新生成的token加入。 Step 3: 每次加入新token后,尝试对整个缓冲区进行解码(`tokenizer.decode`)。 Step 4: 检查解码后的文本是否以Unicode的“替换字符”()结尾。这个特殊字符通常意味着一个多字节字符被截断了。 Step 5: **只有当**解码文本不以“替换字符”结尾时,才将新解码出的部分文本作为SSE事件发送给前端。否则,继续等待下一个token,直到可以形成一个完整的UTF-8字符。
- 基础请求合法性与滥用防护 — 【用户价值】保护服务器资源不被恶意的、格式错误的或超长的请求耗尽,为合法用户提供更稳定的服务。 【设计策略】在API入口处设置一系列明确的、硬编码的规则,对请求参数进行校验,对不合规的请求直接返回400错误。 【业务逻辑】 - **消息数量**: 限制单次请求中的对话轮次在1到500之间。 - **消息长度**: 限制每条消息的字符数不超过8000。 - **总对话长度**: 限制整个对话的总字符数不超过32000。 - **角色**: 强制要求角色必须是'user'或'assistant'。 - **生成参数**: 限制`temperature`在0.0到2.0之间,`top_k`在0到200之间,`max_tokens`在1到4096之间。任何违反规则的请求都将被拒绝。
Core Technical Capabilities
“单旋钮”计算最优超参数自动推导
Problem: 如何让不熟悉LLM工程的研究者也能轻松发起一次计算最优的训练,而无需手动配置和平衡数十个复杂的超参数(如模型宽度、头数、批次大小等)?
Solution: 该系统将复杂的超参数配置抽象为唯一的“深度”(depth)旋钮。\nStep 1: 用户仅需提供模型深度,系统便会基于内置的、经过经验验证的缩放法则(Scaling Laws)和硬件对齐规则,自动计算出最优的模型宽度(`n_embd`)和注意力头数(`n_head`)。\nStep 2: 系统进一步根据目标模型的参数量,再次运用缩放法则,预测出能使训练效率最高的“理论最优总批次大小”(`total_batch_size`)。\nStep 3: 最后,根据实际的GPU数量和显存限制,系统通过梯度累积(Gradient Accumulation)技术,将理论上的大批量转化为硬件可执行的、多次前向传播和一次权重更新的等效操作。\n**核心价值**:这种设计将复杂的、多变量的调优问题,降维成单一变量的决策,极大地降低了LLM训练的认知门槛和操作复杂度。
Technologies: Scaling Laws, Hyperparameter Automation
Boundaries & Risks: 该方案下的缩放法则和经验比率是针对本项目特定的GPT架构和FineWeb-Edu数据集凭经验调优得出的,不保证对其他模型结构(如MoE)或数据分布同样适用。自动预测的批次大小在某些极端硬件配置下可能仍需手动微调。
通过预填充与KV缓存复制实现高效流式推理
Problem: 在进行聊天等多轮对话时,如何避免每次生成新内容时都重复处理越来越长的对话历史,从而导致响应延迟随对话增长而线性增加的问题?
Solution: 该方案采用了“一次预填充,多次增量解码”的策略。\nStep 1: **预填充(Prefill)**:当收到包含长对话历史的请求时,推理引擎将整个历史文本作为单一大批次,只进行一次模型前向传播,计算出所有历史token对应的键(Key)和值(Value),并将其存储在KV缓存中。\nStep 2: **缓存复制(Replication)**:如果需要一次性生成多个候选回复(`num_samples > 1`),该预填充好的KV缓存会被高效地复制多份,供每个候选回复的生成过程独立使用。\nStep 3: **增量解码(Incremental Decode)**:之后,模型进入逐token生成模式。每生成一个新token,只需将这**一个**新token作为输入,并结合KV缓存中已有的全部历史信息,即可快速计算出下一个token。\n**核心价值**:将昂贵的对历史上下文的处理开销均摊到一次操作中,使得后续每个token的生成成本变得极低且固定,从而实现了低延迟的流式响应。
Technologies: KV Cache, Inference Optimization
Boundaries & Risks: KV缓存需要占用大量GPU显存,其大小与批次大小、序列长度成正比,这会限制在显存有限的设备上能够处理的最大上下文长度和并发数。该实现假设批次内的所有序列长度保持同步,对于需要处理变长序列或提前终止部分序列的高级批处理场景,可能需要更复杂的设计。
通过字节映射实现与词表无关的模型质量评估(BPB)
Problem: 当比较两个使用不同分词器(Tokenizer)的语言模型时,传统的“每个token的平均损失”指标是不可靠的。如何建立一个公平、统一的基准来衡量哪个模型对原始文本的压缩能力更强?
Solution: 该方案的核心思想是将评估的单位从“token”还原到更本质的“字节”。\nStep 1: 在训练分词器阶段,系统会预先计算并存储一个映射表,记录每个token ID解码成字符串后,其对应的UTF-8编码字节长度。\nStep 2: 在模型评估阶段,对于模型预测的每个token,除了计算其标准交叉熵损失(单位为nats)外,还会从映射表中查出该token的字节长度。\nStep 3: 最终的BPB指标通过将总的nats损失除以总的字节长度,再进行单位转换(除以ln(2))得到。\n**核心价值**:BPB衡量的是模型压缩每字节信息所需的平均比特数,这是一个物理意义明确且不受分词策略影响的指标。它使得在不同词表大小、不同分词算法的模型之间进行“苹果对苹果”的性能比较成为可能。
Technologies: Bits Per Byte (BPB), Model Evaluation Metrics
Boundaries & Risks: BPB主要衡量模型的压缩效率,这与模型的语言建模能力高度相关,但不完全等同于其在下游任务(如问答、摘要)中的表现。特殊token(如BOS, EOS)的字节长度被记为0,意味着它们不参与BPB的计算。
通过BOS对齐与最佳适配打包实现零填充训练
Problem: 在训练语言模型时,如何解决因将不同长度的文本填充到相同长度而导致的大量GPU算力浪费在无效“填充符”上的问题?
Solution: 该方案通过一种“最佳适配”算法,将多个可变长度的文档紧密地打包进一个固定长度的序列中,以实现100%的算力利用率。\nStep 1: 维护一个待处理的文档池。\nStep 2: 对于一个固定长度的空序列,系统会从池中寻找能够放入剩余空间的最大文档,并将其置入。此过程会确保每个放入的文档都以一个明确的BOS(序列开始)符开头。\nStep 3: 不断重复此过程,直到剩余空间无法容纳池中任何一个完整的文档。\nStep 4: 此时,为了填满最后的空隙,系统会选择池中最短的一个文档,并从头部将其裁剪一部分,精确地填满序列。\n**核心价值**:这种策略完全消除了padding token,保证GPU的每一次计算都作用于真实的文本数据上。同时,BOS对齐确保了模型在处理每个文档时都有清晰的上下文边界,有利于学习。\n**权衡**:为了达到100%的算力利用率,该方案牺牲了部分数据,在特定配置下约有35%的token数据因被裁剪而丢弃。这适用于拥有海量数据(如网页语料)的预训练场景,但在数据稀缺的场景下可能得不偿失。
Technologies: Data Loading Optimization, Best-fit Algorithm
Boundaries & Risks: 高数据裁剪率使其不适用于数据量有限的微调任务。算法的打包效率依赖于文档长度的分布,极端情况下(如所有文档都略大于序列长度的一半)可能会导致更高的裁剪率。