РЕЛИЗ · ПРЕТРЕЙН + ДООБУЧЕНИЕ ЗАВЕРШЕНЫ

Q-164M и Q-U-164M: текст, диалог и точные вызовы инструментов

Новая модель в архитектуре Q, обучена с нуля — крупнее текстовой ветки Q-Omni, только английский язык, с упором на диалог и обобщённый механизм точных вычислений: модель никогда сама не считает арифметику, поэтому не ошибается там, где обычно ошибаются маленькие модели. Претрейн (Q-164M) и дообучение для диалога (Q-U-164M) — оба полностью завершены.

0Mпараметров
1.58-биттернарные веса
0Bтокенов претрейна
6/8ходов диалога Q-U-164M держит верно
Q-164M · база

Q-164M

Претрейн с нуля, 200 000 шагов, 13.1B токенов. Пишет текст и решает задачи с точными вызовами инструментов.

Q-U-164M · диалог

Q-U-164M

Тот же чекпоинт, дообученный chat-SFT + RL специально для диалога. Работает прямо в браузере.

Код (finetune + инференс) — на GitHub. Честное сравнение с другими маленькими моделями — на QBench.

Почему отдельная модель, а не Q-Omni

Ставка Q-Omni была на единый словарь для текста, изображений и звука с замороженным (необучаемым) «отпечатком» вместо обучаемой embedding-таблицы. Эта ставка сработала для текста и точной арифметики, но не для генерации изображений и речи — честные результаты в заметке про Q-Omni. Q-164M полностью отказывается от мультимодальности и ставит более узкий вопрос: как далеко можно зайти с тем же рецептом (замороженный отпечаток + тернарные веса), если все параметры потратить на текст, диалог и надёжный вызов инструментов?

Что такое «точные вызовы инструментов» здесь

Q-Omni уже показал один пример этой идеи: модель пишет <CALC>347*86<EQ>, и результат 29842 вписывает не нейросеть, а маленькая детерминированная программа. Единственная задача сети — решить, когда открыть вычисление и что в него положить; саму арифметику ей верно считать не нужно. Именно поэтому модель на 45–95M параметров достигает 98–99% точного совпадения на арифметике, датах, конвертации единиц и сортировке — заметно ниже порога, который обычно называют необходимым для неявного вызова инструментов.

Архитектура

Десять декодерных блоков между двумя замороженными таблицами с обучаемой поправкой — всё внутри остаётся тернарным. Цветные точки на схеме — это один проход токена насквозь; смотри, как он бежит по кругу.

token id frozen fingerprint + Δ_in codes[id] + delta_in[id], 512-dim Δ_in learned entire vocabulary w_in projection Q-layer ×10 RMSNorm grouped-query attention 20 query heads · 4 kv heads · head dim 64 RoPE · per-head QK-norm · sigmoid gate Q / K / V / O projections: TernaryLinear + RMSNorm SiLU feed-forward 1280 → 3520 → 1280, two matrices both matrices: TernaryLinear + final RMSNorm readout h · w_out · (codes+Δ_out)ᵀ / √512 + bias dot product against a second table Δ_out learned separate from Δ_in ordinary token sampled and emitted as-is <CALC> span? deterministic tool → exact answer the model chooses which the tool is never wrong

Архитектура целиком: у токенов нет обучаемой embedding-таблицы — только случайный фиксированный «отпечаток» плюс маленькая обучаемая поправка, отдельная для входа (Δ_in) и выхода (Δ_out).

Q-164M распространяет этот же приём на растущий набор инструментов помимо арифметики (сейчас это ещё расчёт длительности между датами и фильтрация списков) и — самая непроверенная часть — впервые в проекте сочетает это с настоящим многоходовым диалогом. У Q-Omni с диалоговой дообучкой смешанный опыт: loss на учительском форсинге стабильно падает, но свободная генерация не всегда следует ему. Это открытая проблема, а не готовое решение, которое досталось модели по наследству.

Что отличается архитектурно

  • Вообще нет словаря для изображений и звука — каждый из ~32.8K id в словаре это либо обычный текст, либо управляющий токен диалога/инструмента.
  • Обучаемая поправка поверх замороженного отпечатка покрывает весь словарь с самого первого шага (в Q-Omni её добавили только для мультимодальных id, позже — Q-Omni-Text-95M это тот самый эксперимент, где её донастроили и для текста тоже, и это заметно помогло). В Q-164M она заложена изначально.
  • Крупнее: около 131M тернарных параметров в теле сети плюс ~33.6M в таблице поправки (полная, не только под спецтокены) — против 45–62M у Q-Omni.

Как работает qpack

Чтобы модель можно было запустить где угодно — не только в PyTorch — чекпоинт упаковывается в один файл формата QPACK1: тернарные веса без потерь (5 «трит» в байт + масштаб на строку), отпечатки токенов по биту на измерение, и обучаемые поправки в fp16. Файл выровнен по 64 байта, так что рантайм может сделать mmap и сразу указывать на любой тензор без копирования.

checkpoint last.pt · PyTorch state_dict export_qpack.py ternary → 5 trits/byte · codes → 1 bit/dim Δ_in, Δ_out → fp16 model.qpack one file, ~100 MB, 64-byte aligned Python / numpy qpack_run.py · no PyTorch needed browser · JS qpack.js + model.js · no server, no GPU same logits checked: Δ ≈ 0.0002, top-1 always matches

Оба пути численно сверены друг с другом и с оригинальной моделью на живом чекпоинте — расхождение логитов ~0.0002, топ-1 токен совпадает всегда. Второй путь (чистый JS, без сервера и GPU) означает, что модель в принципе может работать прямо в браузере — и чат-демо теперь и правда гоняет именно этот, финальный чекпоинт Q-164M, а не более ранние модели серии Q-Omni, как раньше.

Претрейн: финальные результаты

Все 200 000 шагов пройдены: ≈92.4 часа чистого времени на одной Tesla V100, 13.1 млрд токенов по плану, без единого NaN за весь прогон. Финальные метрики: val perplexity 16.98 (было 24.9 на шаге 28 000), perplexity на полностью отложенном куске FineWeb (модель его не видела) — 25.61 (было 35.6). На фиксированном наборе из 8 задач с инструментами — 7 из 8: путаница деления с умножением, заметная на ранних чекпоинтах, за время обучения исчезла сама («What is 91 divided by 7?» теперь честно вызывает 91/7, а не 91*7). А вот «sort» так и не выучился: на финальном чекпоинте модель вызвала уже третий по счёту неправильный инструмент вместо него (draxe на шаге 34 500, cmp сейчас) — то есть она не сходится к одной устойчивой ошибке, а каждый раз путается по-новому.

Q-U-164M: дообучение для диалога

Q-U-164M — тот же чекпоинт Q-164M, дообученный в два этапа специально для чата: сначала chat-SFT, затем RL по верифицируемой награде от собственного детерминированного исполнителя инструментов модели (её же <CALC>…<EQ>…<ECALC>-механизм). Толчком стала жалоба, что чат на сайте генерирует несвязный текст. Проверка вручную подтвердила реальный баг: модель уверенно решала одиночные задачи, но уже на втором ходу разговора бросала вызов инструмента и начинала повторяться.

Первая же попытка (SFT + RL) держалась только на первом ходу — почти все обучающие диалоги были одноходовыми. Вторая версия добавила синтетический корпус из 2–4-ходовых диалогов и действительно продлила устойчивость до 5-го хода, но семиходовой стресс-тест показал тот же отказ на ходах 6–7 — ровно там, где заканчивалось покрытие обучающих данных. Третья версия расширила синтетику до 8 ходов и добавила реальные интернет-диалоги (UltraChat, SmolTalk) для живости речи — но это дало обратный эффект: модель переняла манеру реальных данных рассуждать вслух и в длинных диалогах вовсе переставала вызывать инструмент. Четвёртая версия урезала долю реальных данных вдвое и ограничила RL 200 шагами (в третьей версии RL расходилась после этого шага) — но само SFT стало нестабильным изнутри: пик качества в середине обучения (6 из 8 ходов верно), спад к концу (2 из 8), а без отслеживания лучшего чекпоинта по этой метрике лучшая точка оказалась потеряна.

Настоящую причину нашли только в пятой версии, контролируемым A/B-тестом: один и тот же чекпоинт решал 7 из 8 ходов, если вопросы шли «голым» текстом — точно в том стиле, каким синтетический генератор собирал диалоги, — но только 2 из 8, если вопросы формулировались как в живой переписке («Спасибо, а теперь…»). Синтетический генератор никогда не вставлял разговорные связки между вопросами, поэтому модель никогда не видела в одном примере одновременно «поддержать реплику собеседника» и «всё равно вызвать инструмент» — а реальные пользователи пишут именно так. Исправление оказалось точечным: связки внедрили прямо в синтетические диалоги, реальные интернет-данные убрали вовсе (они больше не нужны), заново прогнали SFT и RL и добавили отслеживание лучшего чекпоинта по многоходовой метрике на каждом шаге eval — на случай повторной поздней деградации.

Результат: 6 из 6 на фиксированных одиночных задачах, 6 из 8 на восьмиходовом стресс-тесте с естественными формулировками, и ни одного срыва в повторяющуюся бессмыслицу ни на одной из примерно 50 контрольных точек за весь прогон обучения — раньше это было обычным исходом. RL прошла стабильнее всех прежних попыток: KL не превышал 0.004 за все 200 шагов. Остаются честные слабости: модель иногда путает похожие операции (например, «разделить» с «умножить» на конкретных числах) и может выбрать не тот инструмент для открытого, не про вычисления вопроса — оба случая показаны как есть, без прикрас, в чат-демо. Веса — отдельный репозиторий q-project/Q-U-164M, код — релиз qu164m-v5.

Как это выглядит рядом с другими маленькими моделями

Мы собрали отдельный бенчмарк QBench — специально чтобы честно сравнивать маленькие модели: без подсказок инструментам, только то, что модель реально генерирует сама. Живая таблица — на лидерборде.

МодельПараметрыОдиночныеМногоходовыеARC-EasyTruthfulQA MC1
GPT-2124M0%0%42%26%
SmolLM2-360M-Instruct360M50%50%50%18%
Qwen2.5-0.5B-Instruct500M45%50%62%24%
Q-U-164M164M35%25%43%26%

Честно: модель заметно уступает Qwen2.5-0.5B (втрое крупнее) почти везде — кроме TruthfulQA, там чуть впереди. Не сенсация, но обучена на одной V100, а не на кластере, и без всякой подсказки инструментам — цифры выше не учитывают, что в реальном развёртывании арифметику Q-U-164M пересчитывает детерминированный исполнитель, а не сама сеть.

RELEASE · PRETRAIN + FINE-TUNE COMPLETE

Q-164M & Q-U-164M: text, dialogue, and exact tool calls

A from-scratch successor in the Q architecture family, trained from scratch — bigger than Q-Omni's text branch, English-only, dialogue-first, and built around a generalized version of exact-arithmetic circuits: real tool calls a small model can pull off reliably because it never has to compute the answer itself. Both pretraining (Q-164M) and dialogue fine-tuning (Q-U-164M) are done.

0Mparameters
1.58-bitternary weights
0Bpretrain tokens
6/8multi-turns Q-U-164M holds correctly
Q-164M · base

Q-164M

Pretrained from scratch, 200,000 steps, 13.1B tokens. Writes text and solves tasks with exact tool calls.

Q-U-164M · dialogue

Q-U-164M

The same checkpoint, fine-tuned chat-SFT + RL specifically for dialogue. Runs right in your browser.

Code (fine-tuning + inference) is on GitHub. An honest comparison against other small models is on QBench.

Why a separate model from Q-Omni

Q-Omni's bet was one shared vocabulary across text, images, and audio, with a frozen (non-trainable) fingerprint standing in for a learned embedding table. That bet paid off for text and for exact arithmetic, but not for image or speech generation — see the Q-Omni write-up for the honest results. Q-164M drops the multimodal ambition entirely and asks a narrower question instead: how far can the same frozen-fingerprint, ternary-weight recipe go if every parameter is spent on text, dialogue, and reliable tool use?

What "exact tool calls" means here

Q-Omni already demonstrated one instance of this idea: a model that writes <CALC>347*86<EQ> and a small deterministic program — not the neural network — fills in 29842 before the model continues. The network's only job is deciding when to open a calculation and what to put inside it; it never has to get the arithmetic itself right. That split is why a 45–95M-parameter model can hit 98–99% exact-match on arithmetic, dates, unit conversion, and sorting, well below the parameter counts research on implicit tool-calling generally reports as necessary.

The architecture

Ten decoder blocks between two frozen tables, each with its own learned correction — everything inside stays ternary. The dots trace one token's forward pass; watch it loop.

token id frozen fingerprint + Δ_in codes[id] + delta_in[id], 512-dim Δ_in learned entire vocabulary w_in projection Q-layer ×10 RMSNorm grouped-query attention 20 query heads · 4 kv heads · head dim 64 RoPE · per-head QK-norm · sigmoid gate Q / K / V / O projections: TernaryLinear + RMSNorm SiLU feed-forward 1280 → 3520 → 1280, two matrices both matrices: TernaryLinear + final RMSNorm readout h · w_out · (codes+Δ_out)ᵀ / √512 + bias dot product against a second table Δ_out learned separate from Δ_in ordinary token sampled and emitted as-is <CALC> span? deterministic tool → exact answer the model chooses which the tool is never wrong

The whole architecture: tokens have no trainable embedding table at all — just a random fixed "fingerprint" plus a small learned correction, a separate one for the input lookup (Δ_in) and the output readout (Δ_out).

Q-164M generalizes the same trick to a small, growing registry of tools beyond arithmetic (starting with elapsed-time/duration calculations and list filtering), and — the harder, unproven part — combines it with genuine multi-turn dialogue for the first time in this project. Chat fine-tuning in the Q-Omni line has a mixed track record: teacher-forced loss reliably improves, free-running answers don't always follow. That's an open problem this model has to clear, not a solved one it inherits.

What's different, architecturally

  • No image or audio vocabulary at all — every id in a ~32.8K-token vocabulary is either ordinary text or a dialogue/tool control token.
  • A learned per-token correction on top of the frozen fingerprint, covering the entire vocabulary from step one (Q-Omni added this only to multimodal ids, later; it was retrofitted to text on top of that and measurably helped — Q-Omni-Text-95M is that experiment). Q-164M starts with it baked in.
  • Bigger: roughly 131M ternary backbone parameters plus a ~33.6M correction table (covering the full vocabulary, not just special ids), versus Q-Omni's 45–62M backbone.

How qpack works

To run the model anywhere — not just inside PyTorch — a checkpoint gets packed into a single QPACK1 file: ternary weights stored losslessly (5 trits per byte, plus a per-row scale), token fingerprints packed one bit per dimension, and the learned corrections as fp16. The file is 64-byte aligned, so a runtime can mmap it and point straight at any tensor with no copying.

checkpoint last.pt · PyTorch state_dict export_qpack.py ternary → 5 trits/byte · codes → 1 bit/dim Δ_in, Δ_out → fp16 model.qpack one file, ~100 MB, 64-byte aligned Python / numpy qpack_run.py · no PyTorch needed browser · JS qpack.js + model.js · no server, no GPU same logits checked: Δ ≈ 0.0002, top-1 always matches

Both paths are numerically checked against each other and against the original model on a live checkpoint — logits differ by ~0.0002, top-1 token always matches. The JS path (no server, no GPU) means the model can in principle run entirely in a browser tab — and the Chat page's demo now actually runs this final Q-164M checkpoint, not an earlier Q-Omni model like it used to.

Pretrain: final results

All 200,000 steps finished: ≈92.4 hours of wall-clock training on a single Tesla V100, 13.1B tokens as planned, no NaNs across the entire run. Final metrics: val perplexity 16.98 (was 24.9 at step 28,000), held-out FineWeb perplexity (a slice the model never saw) — 25.61 (was 35.6). On a fixed set of 8 tool-use tasks: 7 of 8 — the division/multiplication mix-up visible on early checkpoints quietly fixed itself along the way (“What is 91 divided by 7?” now correctly calls 91/7, not 91*7). sort, though, never got learned — at the final checkpoint the model reached for its third different wrong tool instead of it (draxe at step 34,500, cmp now), which reads less like a stable wrong answer and more like it never actually landed on the concept at all.

Q-U-164M: fine-tuning for dialogue

Q-U-164M is the same Q-164M checkpoint, fine-tuned in two stages specifically for chat: chat-SFT first, then RL on a verifiable reward supplied by the model's own deterministic tool executor (the same <CALC>…<EQ>…<ECALC> mechanism). The work started from a user complaint that the site's chat demo produced disconnected text. Hands-on testing confirmed a real bug: the model solved single-turn tasks confidently, but dropped tool-calling and started repeating itself by the second turn of a conversation.

The first pass (SFT + RL) only held up for turn one — almost all training dialogues were single-turn. The second version added a synthetic corpus of 2–4-turn conversations and did extend stability to turn 5, but a 7-turn stress test showed the same failure reappearing at turns 6–7 — exactly where the training data's own coverage ran out. The third version extended the synthetic data to 8 turns and added real internet dialogue (UltraChat, SmolTalk) for phrasing variety — which backfired: the model picked up the real data's habit of reasoning out loud in prose and, in long conversations, stopped calling the tool at all. The fourth version halved the real-data weight and capped RL at 200 steps (RL had diverged past that point in v3) — but SFT itself turned out unstable on its own: quality peaked mid-training (6 of 8 turns correct) then declined by the end (2 of 8), and with no tracking of the best checkpoint on this metric, that peak was simply lost.

The actual root cause only surfaced in the fifth version, via a controlled A/B test: the very same checkpoint solved 7 of 8 turns when questions were phrased bare — exactly the style the synthetic generator used — but only 2 of 8 when phrased the way people actually write (“Thanks, now what's...”). The synthetic generator never inserted conversational connectives between questions, so the model had never seen “acknowledge what was just said, and still call the tool” in a single training example — and that is exactly how real users write. The fix was narrow: splice connectives directly into the synthetic dialogues, drop the real internet data entirely (no longer needed), rerun SFT and RL, and add tracking for the best checkpoint on this same multi-turn metric at every eval, in case the late-training regression showed up again.

Result: 6 of 6 on the fixed single-turn tasks, 6 of 8 on an 8-turn stress test using natural phrasing, and zero collapses into repetitive nonsense across roughly 50 checkpoints over the entire training run — that used to be the normal outcome. RL was also the most stable run yet: KL never exceeded 0.004 across all 200 steps. Honest remaining weaknesses: the model occasionally confuses adjacent operations (e.g. division with multiplication on specific numbers) and can pick the wrong tool for an open-ended, non-computational question — both shown as-is, unpolished, in the chat demo. Weights are their own repo, q-project/Q-U-164M; code is release qu164m-v5.

How it stacks up against other small models

We built a separate benchmark, QBench, specifically to compare small models honestly: no tool-execution help for any model, only what it actually generates on its own. Live table on the leaderboard.

ModelParamsSingle-turnMulti-turnARC-EasyTruthfulQA MC1
GPT-2124M0%0%42%26%
SmolLM2-360M-Instruct360M50%50%50%18%
Qwen2.5-0.5B-Instruct500M45%50%62%24%
Q-U-164M164M35%25%43%26%

Honestly: the model trails Qwen2.5-0.5B (three times the size) on nearly everything, except TruthfulQA where it edges ahead. Not a triumph, but trained on one V100, not a cluster, and without any tool-execution help — deployed normally, a deterministic executor corrects the arithmetic Q-U-164M decides to do, so the network itself never has to get it right.

正式发布 · 预训练 + 微调均已完成

Q-164M 与 Q-U-164M:文本、对话与精确工具调用

Q 架构家族的最新成员,从零开始训练完成 —— 比 Q-Omni 的文本分支更大,仅支持英文,以对话为核心,并围绕一种通用化的精确计算机制构建:真正的工具调用,一个小模型也能可靠完成,因为它从来不需要自己去算出答案。预训练(Q-164M)和面向对话的微调(Q-U-164M)现在都已完成。

0M参数量
1.58 比特三元权重
0B预训练词元数
6/8Q-U-164M 正确保持的对话轮次
Q-164M · 基础模型

Q-164M

从零预训练,200,000 步,131 亿词元。能写文本,也能用精确工具调用解决问题。

Q-U-164M · 对话

Q-U-164M

同一个检查点,专门为对话做了 chat-SFT + RL 微调。直接在浏览器中运行。

代码(微调 + 推理)发布在 GitHub。与其他小模型的如实对比见 QBench。

为什么不直接用 Q-Omni,而要做一个独立的模型

Q-Omni 的赌注是:让文本、图像和音频共用一套词表,用一个冻结的(不可训练的)「指纹」取代可训练的嵌入表。这个赌注在文本和精确算术上是成功的,但在图像和语音生成上并不成功 —— 详见 Q-Omni 的文章 中如实呈现的结果。Q-164M 完全放弃了多模态的野心,转而提出一个更聚焦的问题:如果把所有参数都用在文本、对话和可靠的工具调用上,同样的「冻结指纹 + 三元权重」配方能走多远?

这里的「精确工具调用」是什么意思

Q-Omni 已经展示过一个例子:模型写出 <CALC>347*86<EQ>,然后在模型继续生成之前,由一个小型确定性程序 —— 而不是神经网络本身 —— 填入结果 29842。网络唯一的任务是决定何时发起一次计算,以及在里面写什么;它完全不需要自己把算术算对。正因如此,一个 45–95M 参数的模型就能在算术、日期、单位换算和排序上做到 98–99% 的精确匹配,远低于研究中通常认为隐式工具调用所需要的参数规模。

架构

十个解码器块夹在两张冻结表之间,各自带一份可学习修正 —— 内部全程保持三元。图中的光点描摹的是单个词元的完整前向传播;看它循环流动。

token id frozen fingerprint + Δ_in codes[id] + delta_in[id], 512-dim Δ_in learned entire vocabulary w_in projection Q-layer ×10 RMSNorm grouped-query attention 20 query heads · 4 kv heads · head dim 64 RoPE · per-head QK-norm · sigmoid gate Q / K / V / O projections: TernaryLinear + RMSNorm SiLU feed-forward 1280 → 3520 → 1280, two matrices both matrices: TernaryLinear + final RMSNorm readout h · w_out · (codes+Δ_out)ᵀ / √512 + bias dot product against a second table Δ_out learned separate from Δ_in ordinary token sampled and emitted as-is <CALC> span? deterministic tool → exact answer the model chooses which the tool is never wrong

完整架构:词元根本没有可训练的嵌入表 —— 只有一个随机固定的「指纹」,加上一个小的可学习修正,输入查找(Δ_in)和输出读出(Δ_out)各自独立一份。

Q-164M 把同样的技巧推广到算术之外一个不断增长的工具集合(目前新增了日期间隔计算和列表筛选),并且 —— 这是最没有把握的部分 —— 第一次在这个项目里把它和真正的多轮对话结合在一起。Q-Omni 系列在对话微调上的表现参差不齐:teacher-forcing 下的 loss 稳定下降,但自由生成的回答并不总能跟上。这是这个模型必须自己解决的开放问题,而不是继承来的现成方案。

架构上有什么不同

  • 完全没有图像或音频词表 —— 约 3.28 万个 id 里,每一个要么是普通文本,要么是对话/工具的控制词元。
  • 在冻结指纹之上的可学习修正,从第一步起就覆盖整个词表(Q-Omni 是后来才只为多模态 id 加上这个机制,之后又追加应用到文本上,效果明显 —— Q-Omni-Text-95M就是这个实验)。Q-164M 从一开始就内置了这一点。
  • 规模更大:主干三元参数约 1.31 亿,外加约 3360 万的修正表(覆盖整个词表,而不只是特殊 id),相比之下 Q-Omni 的主干是 4500 万–6200 万。

qpack 是如何工作的

为了让模型能在任何地方运行 —— 而不只是在 PyTorch 里 —— 检查点会被打包成一个 QPACK1 单文件:三元权重无损存储(每字节 5 个「trit」,外加每行一个缩放系数),词元指纹按每维 1 比特打包,可学习修正以 fp16 存储。文件按 64 字节对齐,运行时可以直接 mmap 并指向任意张量而无需拷贝。

checkpoint last.pt · PyTorch state_dict export_qpack.py ternary → 5 trits/byte · codes → 1 bit/dim Δ_in, Δ_out → fp16 model.qpack one file, ~100 MB, 64-byte aligned Python / numpy qpack_run.py · no PyTorch needed browser · JS qpack.js + model.js · no server, no GPU same logits checked: Δ ≈ 0.0002, top-1 always matches

两条路径都在一个真实检查点上,相互之间以及与原始模型之间做过数值校验 —— logits 相差约 0.0002,top-1 词元始终一致。JS 这条路径(不需要服务器和 GPU)意味着模型原则上完全可以在浏览器标签页里运行 —— 现在Chat 页面上的演示运行的正是这个最终的 Q-164M 检查点,而不再是早期的 Q-Omni 模型。

预训练:最终结果

全部 200,000 步已跑完:在单张 Tesla V100 上累计约 92.4 小时,按计划处理了 131 亿词元,全程没有出现一次 NaN。最终指标:val 困惑度 16.98(第 28,000 步时为 24.9),在模型从未见过的 FineWeb 留出片段上的困惑度为 25.61(此前为 35.6)。在一组固定的 8 个工具调用任务中:答对 7 个 —— 早期检查点上明显的除法与乘法混淆问题,在训练过程中不知不觉自己消失了(现在「What is 91 divided by 7?」会正确调用 91/7,而不是 91*7)。但「sort」始终没有学会:在最终检查点上,模型又调用了第三个不同的错误工具(第 34,500 步是 draxe,现在是 cmp)—— 这看起来不像是收敛到某个稳定的错误答案,更像是它压根没理解这个概念。

Q-U-164M:面向对话的微调

Q-U-164M 就是同一个 Q-164M 检查点,专门为聊天分两个阶段微调而来:先是 chat-SFT,然后是基于模型自身确定性工具执行器(同样的 <CALC>…<EQ>…<ECALC> 机制)给出的可验证奖励进行的 RL。这项工作起因于一位用户反映网站聊天演示会生成不连贯的文本。动手测试证实确有其事:模型能自信地解决单轮任务,但在对话的第二轮就会放弃调用工具,并开始重复自己说过的话。

第一版(SFT + RL)只能撑住第一轮——几乎所有训练对话都是单轮的。第二版加入了一个 2 到 4 轮的合成对话语料库,确实把稳定性延长到了第 5 轮,但一次 7 轮压力测试显示同样的失败又在第 6-7 轮重新出现——正好是训练数据覆盖范围的尽头。第三版把合成数据扩展到 8 轮,并加入真实的互联网对话(UltraChat、SmolTalk)来增加表达的多样性——但这带来了反效果:模型学会了真实数据里"大声推理"的说话方式,在长对话里干脆完全不再调用工具。第四版把真实数据的权重减半,并把 RL 限制在 200 步以内(因为 v3 的 RL 在这一步之后就发散了)——但 SFT 本身却变得不稳定:训练到一半时质量达到峰值(8 轮里答对 6 轮),到训练结束时又下降(只对 2 轮),而由于当时没有针对这个指标跟踪最佳检查点,那个峰值就这样白白丢失了。

真正的根本原因直到第五版才通过一次对照 A/B 测试找到:同一个检查点,如果问题用"裸"文本表达——正是合成生成器组织对话的那种风格——能答对 8 轮里的 7 轮;但如果问题按人们真实说话的方式表达(比如"谢谢,那……"),就只能答对 2 轮。合成生成器从未在问题之间插入过对话连接词,所以模型从未在同一个训练样本里见过"先回应对方刚说的话,然后依然去调用工具"——而这恰恰是真实用户说话的方式。修复很精准:把连接词直接拼接进合成对话里,彻底去掉真实互联网数据(已经不再需要),重新跑一遍 SFT 和 RL,并且在每次评估时都跟踪这同一个多轮指标上的最佳检查点,以防训练后期再次出现这种衰退。

结果:在固定的单轮任务上 6 之 6 全对,在使用自然表达的 8 轮压力测试中答对 6 轮,整个训练过程约 50 个检查点里没有一次崩溃成重复的胡言乱语——这在以前是常态。RL 也是历次尝试中最稳定的一次:全部 200 步中 KL 从未超过 0.004。如实说明仍存在的弱点:模型有时会混淆相近的运算(比如把特定数字的除法当成乘法),也可能为一个开放式、与计算无关的问题选错工具——这两种情况都原样展示在聊天演示里,没有美化。权重是独立的仓库 q-project/Q-U-164M;代码发布版本是 qu164m-v5。

和其他小模型比起来如何

我们专门做了一个基准测试 QBench,用来如实比较小模型:不给任何模型工具执行的帮助,只看它自己实际生成的内容。实时表格见排行榜。

模型参数量单轮多轮ARC-EasyTruthfulQA MC1
GPT-2124M0%0%42%26%
SmolLM2-360M-Instruct360M50%50%50%18%
Qwen2.5-0.5B-Instruct500M45%50%62%24%
Q-U-164M164M35%25%43%26%

如实说:模型在几乎所有指标上都明显落后于三倍大小的 Qwen2.5-0.5B,只有 TruthfulQA 略微领先。算不上什么惊人成就,但它只在一张 V100 上训练,而不是在集群上;而且这些数字没有给任何工具执行提供帮助——在正常部署中,确定性执行器会修正 Q-U-164M 决定做的计算,网络本身从不需要自己算对。