The post has been translated automatically. Original language: Russian
Artificial intelligence is becoming a strategic technology. The ability to train and run modern models affects the economy, information security, medicine, education, and technological independence of states.
But one's own work in the field of AI does not necessarily begin with teaching another huge language model on hundreds of billions of tokens. Today, there are already a large number of strong open models. An equally important task is to learn how to make them more compact, cheaper and more accessible for local launch.
That's why I took up extreme compression of neural networks.
My first major result is a ternary version of Whisper Small, a speech recognition model from OpenAI.
I have converted all 192 large linear Whisper Small matrices into weights of three possible states:
[{-1, 0, +1}.]
This is 198,180,864 weights, or 81.98% of all model parameters.
The resulting model:
- It takes about 140 MiB instead of 465 MiB.;
- it runs on a regular processor via a modified whisper.cpp;
- shows normalized WER below the original stock Whisper Small on LibriSpeech;
- It has open weights, a learning library, exporter, CPU-patch, and measurement logs.
But this is not a story about the "magic 1.58 bits lossless".
In the process, data, training, export, punctuation, multilingualism, and a few of my own beautiful explanations broke down. That's why I decided to tell you not only the totals, but also the whole way.
The result is a single screen
Architecture
The original model:
OpenAI Whisper Small
241,734,912 parameters
12 encoder blocks
12 decoder blocks
Transferred to ternary:
192 large Linear matrices
198 180 864 weights
81.98% of model parameters
Remained in higher precision:
- convolutional audio frontend;
- tied token embedding and output head;
- LayerNorm;
- biases;
- positional embeddings;
- other small tensors.
Recognition quality
| LibriSpeech, greedy decoding, batch 16 | Ternary Whisper | Stock Whisper Small |
| test-clean | 2,7145% WER | 3,3391% |
| test-other | 7,3817% WER | 7,5002% |
| long-form, 9 entries | 3,632% WER | 3,891% |
Test-clean and test-other did not participate in gradient training. However, several checkpoints were subsequently compared on these sets, so strictly scientifically I consider them as strong evaluation suites, but not as permanently sealed final audit.
An important caveat
This is a comparison with the original stock Whisper Small.
I also trained the matched FP16 control:
- the same data;
- the same training code;
- the same schedule;
- the same duration;
- quantization is disabled.
Matched FP16-the control is still about 0.37 absolute points better than the ternary branch. Both models continued to improve by the end of the experiment, so the final quantization gap has not yet been established.
Therefore, the correct conclusion is as follows:
The additionally trained ternary model has become better than the original stock Whisper Small, but it has not yet been proven that ternarization itself improved the quality.
What does "1.58 bits" mean here?
The expression "1.58-bit" comes from the information capacity of three states:
[log_2(3)approx1{,}585.]
But real computer storage works differently.
In my training format, each weight uses a two-bit code.:
00 → −1
01 → 0
10 → +1
11 → reserved
An additional FP16-scale is stored for each group of 128 weights.:
[w_i=s_gz_i,qquadz_iin{-1,0,+1}.]
The physical cost of large matrices in the internal format:
[2+�rac{16}{128}=2{,}125 ext{ bit/weight}.]
The current deploy file for whisper.cpp uses the existing Q2_0 format with group 64. The initial group of 128 is divided into two halves, which are assigned the same learned scale. The values of the weights are restored accurately, but the physical value becomes:
[2+�rac{16}{64}=2{,}25 ext{ bit/weight}.]
Therefore, there are four different numbers:
| Indicator | Bit depth |
| Information capacity of three states | 1,585 bpw |
| Training/reference layout, g128 | 2,125 bpw |
| Deploy Q2_0, g64 | 2,25 bpw |
| The average of the entire hybrid file | about 4.86 bit/parameter |
The last figure is higher because about 18% of the model parameters — primarily the tied embedding/output head — remain FP16.
So it's not a fully 1.58-bit model. This is a hybrid Whisper Small, in which 82% of the parameters have strict ternary values and a two-bit physical representation.
Why did I do this?
The worldwide development of frontier models requires huge computing resources. Competing alone with organizations that train models on thousands of accelerators is not the most rational first goal.
But after learning, another fundamental problem arises.:
- the size of the scales;
- the cost of video memory;
- memory bandwidth;
- Energy consumption;
- the possibility of local launch;
- dependence on expensive server hardware.
This is where the binary and ternary neural networks direction appears.
As of July 2026, this area is no longer limited to Microsoft alone. There is the official Microsoft BitNet b1.58 2B4T, the Falcon3-1.58 family from TII, Bonsai from PrismML, BitCPM-CANN from OpenBMB, and several academic projects. But there are still relatively few organizations that publicly show the full cycle—training or conversion, packaged representation, specialized runtime, and normal quality assessment.
There are competitors in speech recognition too. In 2025, a paper on one-bit Conformer ASR was published, and in July 2026, Microsoft released VibeVoice-ASR-BitNet, a multilingual ASR system with heterogeneous low-bit quantization and CPU-runtime. This means that I cannot call my model the first ternary ASR model in the world.
But at the time of publication, I have not found another open-source job that simultaneously:
- Takes a regular stock Whisper Small;
- converts all 192 Transformer matrices to ternary;
- publishes the QAT library;
- publishes weights;
- publishes the exporter;
- adds a work path to whisper.cpp;
- it shows WER, matched FP16 control and negative results.
This is where the value of the project lies.
How the model was translated into ternary
Step 1. Simple rounding does not work
The naive version looks like this:
[z_i=operatorname{round}(w_i/s),qquadz_iin{-1,0,+1}.]
The problem is that all weights are considered equally important in this approach.
But the real impact of the error is determined by which activations the weight is multiplied by.
For the linear layer:
[y=Wx.]
After replacing the scales:
[hat y=Qx.]
Exit error:
[delta y=(W-Q)x.]
Therefore, the average error of the layer depends not only on the difference in weights, but also on the statistics of input activations.:
[mathbb E|delta y|^2
operatorname{Tr}left[(W-Q)C_x(W-Q)^T ight].]
For each group of 128 weights, I solve an activation-weighted problem:
[min_{s,;z_iin{-1,0,+1}}sum_i h_i(w_i-sz_i)^2,]
where (h_i) is an estimate of the importance of the corresponding input direction.
Initialization of ternary codes is not the usual rounding, but an accurate projection taking into account the actual activations of the model.
Step 2. Hard-forward QATAR
After the initial projection, the model undergoes quantization-aware training.
Only rigid weights are used in forward:
[{-s, 0, +s}.]
But the usual rounding is undifferentiable. Therefore, backward uses a continuous surrogate variable and a straight-through-like gradient estimation.
Meaning:
forward:
only hard ternary codes
backward:
the gradient passes through a latent proxy
The loss function includes:
- cross-entropy by transcripts;
- KL-divergence to BF16 teacher;
- matching intermediate features;
- additional recovery components.
A very important feature: latent weight is not a master copy
In classical QAT, it is often assumed:
FP master weight
→ fake quantization
→ forward
and after training, the final quantized checkpoint is re-built from master weights.
In my implementation, this is not the case.
The deployment representation of the model is a trainable pair:
hard codes
+
group scales
The Latent FP32 tensor is only needed as a surrogate for movement between discrete states.
During the training latent weights:
- we have moved away from the operating values by about 3.8 medians;
- about 60% were saturated;
- it has ceased to be a meaningful master copy.
When I tried to re-project latent weights during export, I got:
WER = 1000,56%
The correct export does not use latent tensor, but rather learned hard codes and scales.
This is the method-specific output:
In WAL-TAT, the master representation is a pair (codes, scales), and latent weights are just an optimization tool.
What broke down along the way
1. The model has learned to repeat phrases
After a few hours of training, the model started to loop.
It turned out that some of the pseudo-labels created by the teacher model already contained long repetitions - up to 108 repeating five—grams inside one label.
There were few such entries, but the error was extremely concentrated. A few bad tags could produce more erroneous tokens than hundreds of clean examples.
The model just learned what she was shown.
After that, they are automatically checked before each launch.:
repeating n-grams
label length
p99 and maximum token count
punctuation
case
service prefix
audio duration
several complete manual examples
Five hours of training were lost due to a problem that could be detected in a minute.
2. Wall for exactly 30 seconds
Whisper accepts a 30-second window.
If the recording is shorter, the rest is filled with silence. But if the audio is exactly 30,000 seconds long, padding is completely missing.
After fine-tuning, the model almost did not see such entrances and sharply degraded precisely at this boundary.
Control experiment:
30,000 seconds → error
29.990 seconds → running
The difference is one small frame of silence.
Decision:
- add a significant proportion of windows in training corpus for exactly 30 seconds;
- Never cut a utterance in the middle;
- close the window at the edge of the replica.
3. The export of ternary scales yielded a thousand percent of WER
It was a mistake with latent weights, described above.
It turned out to be especially valuable because it showed:
The inference format should be defined during the training.
You can't train something like ternary first and then decide how to package it.
Forward, checkpoint, and runtime must implement the same math.
4. I have assembled a chimera model.
During the first export, 192 ternary matrices were taken from my checkpoint, and the remaining 287 tensors were taken from the original stock model.
But these tensors have also changed over 80,000 learning steps.
The result is a combination:
new ternary matrices
+
old FP16 strapping
The first words were recognized correctly, then the text broke up.
It's especially annoying that I've found a similar error before in another script, but I didn't turn the fix into an automatic invariant.
After that, the exporter is required to check the lineage of all tensors.
5. The tied embedding/output head did not survive the current T3 projection
Whisper uses a single matrix both as a token embedding and as an output projection.
It contains about 18% of all parameters and occupies approximately 86 of the 140 MiB of the finished file.
When trying to transfer it using the current one-shot T3 method:
WER: 2.71% → 1152%
inserts: 563 054
This does not prove that a tied head is fundamentally impossible to translate into two bits.
This proves a more narrow fact.:
The current projector without specialized QAT completely destroys the tied embedding/head.
Therefore, it remains FP16 in the current version.
The next reasonable way:
Q8
→ Q6
→ learned Q4
→ mixed Q2/Q4
→ separate QAT for the tied head
Where did I bother myself
Several times, my first explanations were not just wrong — they made the picture more beautiful.
"The problem is with the prefix"
I explained the discrepancy by saying that one evaluator does not transfer language tokens.
Then I opened the code and saw that the tokens were being transferred.
The real reason is the sensitivity of batched matrix multiplication to the order of calculations:
batch 16 → 3,4298% WER
batch 8 → 3,4224%
batch 1 → 3,4168%
Therefore, the batch size is now fixed.
I call the range of about 0.013 percentage points not statistical noise in the strict sense, but the implementation sensitivity of the current harness. Differences of less than 0.02 percentage points are not interpreted without a separate paired check.
"The effect is symmetrical"
I measured one side of the effect, got a nice figure, and assumed that the other side was behaving the same way.
She turned out to be almost three times worse.
"The training did not reach 36 thousand steps"
I wrote it down in the results, and then I found line 36000 in the log.
"1.48× acceleration"
I compared the option with CUDA Graphs against the option without CUDA Graphs.
These were different conditions, so the result was deleted.
Incorrect memory measurement
The old Python dictionary held references to 756 MiB of already unused weights. I declared 991 MiB memory, although the real result was about 228 MiB.
After these incidents, I started recording in advance.:
- expected result;
- success criteria;
- partial success;
- failure;
- the alarm threshold.
This makes it difficult to change the explanation after the figure is already known.
What happened in terms of quality
The ternary model is better than the stock Whisper Small on selected normalized WER benchmarks.
But at the same time:
- matched FP16-Control remains better than T3;
- Punctuation has almost disappeared;
- The capital letters are almost gone;
- The Russian language has seriously degraded;
- out-of-domain English is significantly worse than LibriSpeech.
Punctuation
For 300 utterances:
Semicolon replicas:
T3 → 0.0–0.7%
stock → 63.3%
capitalized lines:
T3 → 2%
stock → 98%
Normalized WER usually removes punctuation and case. Therefore, the improvement of normalized TEXT does not mean that the user's text has improved in all respects.
Correct wording:
The model improved the normalized accuracy of word recognition, but significantly worsened the spelling of the text.
The reason is known: training labels have been reduced to lowercase and stripped of punctuation marks.
Multilingualism
Russian has deteriorated by about 12.6 times.
But the matched FP16 control, trained on the same English data, degraded similarly. Therefore, the main reason is catastrophic forgetting during monolingual fine-tuning, and not directly ternary quantization.
CPU-runtime
To run without a GPU, I used whisper.cpp .
The Q2_0 type already existed in ggml, but in the fixed version:
- whisper quantize path rejected it;
- x86 did not have a dedicated SIMD vec-dot;
- generic fallback was too slow.
I added:
- achievable path Q2_0 for Whisper;
- AVX2 Q2_0 × Q8_0 kernel;
- exclusion of the tied embedding/head from the extreme quantization;
- exact promotion encoder weights Q2_0 → Q8_0 when loading.
The last point is important.
The encoder is stored on disk as Q2_0. But encoder Whisper performs dense multiplication over multiple audio frames at once is a compute-bound. Therefore, during loading, its ternary weights are precisely deployed in Q8_0.:
Disk:
encoder Q2_0
decoder Q2_0
Runtime:
encoder exact Q8-expanded ternary
decoder packed Q2_0
tied head and FP16/F32 service tensors
Mathematically, the encoder values remain the same ternary weights. But physically, the encoder works like a Q8 in RAM.
Speed
An honest comparison of CPU vs CPU
One Xeon, 16 threads, same audio and one runtime:
| FP16 | Ternary hybrid | |
| File | 465 MiB | 140 MiB |
| Encoder / 30 seconds | 578 ms | 473 ms |
| Decoder / token | 6,8 ms | 6,0 ms |
This is the main apples-to-apples comparison.
The ternary decoder benefits from a lower volume of weights and a specialized kernel. The encoder after the exact Q8 expansion also turned out to be faster than the stock FP16 in this CPU-runtime.
CPU vs H200
In a different configuration:
| H200, Hugging Face/PyTorch FP16 | Xeon, custom whisper.cpp | |
| Encoder / window | 7,1 ms | 550 ms |
| Decoder / token | 8,3 ms | 6,0 ms |
This should be formulated carefully.
This is not a pure CPU and GPU comparison. They are being compared at the same time:
- two hardware;
- two runtimes;
- different levels of fusion;
- custom C/AVX2;
- PyTorch graph;
- kernel-launch overhead.
Correct output:
In this particular pair of implementations, the latency of one autoregressive decoder step turned out to be lower on the CPU. But the H200 is about 77 times faster at processing the encoder, so it remains faster end-to-end for most records.
Profiling showed about 439 GPU kernel launches on decoder pass. With batch 1, Whisper Small is too small to load the H200 efficiently: a significant portion of the time is spent running operations rather than doing math.
What's not working yet
1. Punctuation
We need a mixed training curriculum:
normalized ASR labels
+
original punctuated labels
+
filtered teacher labels
You need to evaluate at the same time:
- normalized WER;
- raw/orthographic WER;
- punctuation F1;
- capitalization accuracy.
2. Multilingualism
We need multilingual replay corpus and KL for the original stock Whisper in other languages.
3. Tied head
The next main compression target is learned Q4 or mixed Q2/Q4 for embedding/output matrix. It is he who is able to reduce the file the most.
4. The final quantization gap
It is necessary to bring matched FP16 and T3 to a plateau, preferably with several seeds.
5. Independent assessment
We need a new one-shot audit:
- accented English;
- noisy speech;
- long-form;
- silence/no-speech;
- multilingual;
- timestamps.
How rare is this direction
There are already hundreds of artifacts with the BitNet tag on Hugging Face, but a significant part are mirrors, small experiments, simple post—training conversions, or models without a full-fledged low-bit runtime and independent evaluation.
I would consider serious open areas to be:
- Microsoft BitNet b1.58 2B4T and BitNet Embeddings;
- Microsoft VibeVoice-ASR-BitNet;
- TII Falcon3/Falcon-E 1.58;
- PrismML Bonsai;
- OpenBMB BitCPM-CANN;
- separate academic projects like One-bit ASR, Echo-1.58 and BitTTS.
This is no longer an empty field. But it is incomparably smaller than the usual ecosystem of FP16, FP8 and INT4.
There are especially few public works where the researcher takes an existing model, translates it into ternary, publishes the entire training pipeline and brings the result to the present packed runtime.
Why is this important to me?
I live and work in Kazakhstan.
Kazakhstan does not have to start participating in the global AI race by trying to train the world's largest model alone. In technological development, it is often not the one who repeats everything first who wins, but the one who has deeply mastered one critical area.
I want to do extremely low-bit models.:
- ternary and binary weights;
- by converting ready-made models;
- quantization-aware training;
- recovery after extreme compression;
- mixed-precision allocation;
- packed CPU/GPU kernels;
- local and edge-inference.
Today it's Whisper.
The next model may be a language model with 1-8 billion parameters. Then there is a multimodal model, an embedding model, or a specialized agent.
My goal is not just to issue individual checkpoints.
I want to create a reproducible technology.:
ready-made open model
→ architecture-aware conversion
→ hard low-bit training
→ independent quality audit
→ packed artifact
→ real runtime
If such a stack is created and opened from Kazakhstan, it will be a more significant result than another presentation on the "development of artificial intelligence."
It will be working code, a model, measurements, and proof that complex research engineering can be done here.
Open result
Published:
- model weights;
- WAL-TAT/QAT-library;
- data preparation pipeline;
- training scripts;
- exporter;
- patch for whisper.cpp;
- correctness tests;
- measurement logs;
- negative experiments;
- matched FP16-control.
The public repository describes 192 ternary matrices, a model file of about 140 MiB, WER results, and a working CPU path.
The Exporter takes exactly the learned codes and scales, transfers them to Q2_0 without re-evaluating them, and verifies the accuracy of the reverse unpacking.
Patch adds an achievable Q2_0 path, AVX2 kernel, and hybrid precision policy for encoder and tied head.
Result
This project has not proven that ternary is always better than FP16.
He proved something else.:
- The usual pre-trained Whisper Small can be completely translated over all large Transformer matrices into strict ternary.
- After QAT, the model can maintain strong ASR quality.
- Three-digit weights can really be packaged and run directly on the CPU.
- Most of the errors are not in the quantization formula alone, but in the data, evaluation, export, and runtime.
- The improvement of one indicator may conceal the degradation of another.
- Extreme compression requires the same rigor of measurement as the training itself.
For me, this is only the first model.
The main goal is to turn this single result into a universal, architecture-aware tool for converting open models into ternary and binary representation.
And to make Kazakhstan appear publicly among the countries that not only use other people's AI models, but also create their own methods of their transformation and launch.
Model, code, and reproduction
The project has been fully published and is available for self-review.:
- Finished model and weights on Hugging Face:armanibadboy/whisper-small-ternary
- Source code, WAL-TAT, training, exporter and patch for whisper.cpp:AubakirovArman/whisper-ternary
Published in the repository:
- quantization-aware training library;
- pipeline of data preparation and verification;
- training configurations;
- evaluation scripts WER;
- exporter in GGML Q2_0 format;
- AVX2-patch for whisper.cpp;
- tests of correctness of packaging and calculations;
- matched FP16-control;
- diagnostic reports and negative results.
The model can be downloaded and run locally.:
hf download armanibadboy/whisper-small-ternary \
ggml-small-wal-ternary-q2_0.bin \
--local-dir models/
After applying the published patch to whisper.cpp:
./build/bin/whisper-cli \
-m models/ggml-small-wal-ternary-q2_0.bin \
-f audio.wav \
-ng
Links
Code, weights, training, exporter, CPU-runtime and results published openly. The project can not only be read, but also independently checked and reproduced.
Искусственный интеллект становится стратегической технологией. Возможность обучать и запускать современные модели влияет на экономику, информационную безопасность, медицину, образование и технологическую независимость государств.
Но собственная работа в области ИИ не обязательно начинается с обучения очередной огромной языковой модели на сотнях миллиардов токенов. Сегодня уже существует большое количество сильных открытых моделей. Не менее важная задача — научиться делать их компактнее, дешевле и доступнее для локального запуска.
Именно поэтому я занялся экстремальным сжатием нейронных сетей.
Мой первый серьёзный результат — тернарная версия Whisper Small, модели распознавания речи от OpenAI.
Я перевёл все 192 крупные линейные матрицы Whisper Small в веса трёх возможных состояний:
[{-1, 0, +1}.]
Это 198 180 864 веса, или 81,98% всех параметров модели.
Получившаяся модель:
- занимает около 140 МиБ вместо 465 МиБ;
- запускается на обычном процессоре через модифицированный whisper.cpp;
- показывает normalized WER ниже исходного stock Whisper Small на LibriSpeech;
- имеет открытые веса, библиотеку обучения, exporter, CPU-patch и журналы измерений.
Но это не история о «магических 1,58 бита без потерь».
В процессе сломались данные, обучение, экспорт, пунктуация, мультиязычность и несколько моих собственных красивых объяснений. Именно поэтому я решил рассказать не только итоговые цифры, но и весь путь.
Результат одним экраном
Архитектура
Исходная модель:
OpenAI Whisper Small
241 734 912 параметров
12 encoder-блоков
12 decoder-блоков
В ternary переведены:
192 крупные Linear-матрицы
198 180 864 веса
81,98% параметров модели
Остались в более высокой точности:
- convolutional audio frontend;
- tied token embedding и output head;
- LayerNorm;
- biases;
- positional embeddings;
- другие небольшие tensors.
Качество распознавания
| LibriSpeech, greedy decoding, batch 16 | Ternary Whisper | Stock Whisper Small |
| test-clean | 2,7145% WER | 3,3391% |
| test-other | 7,3817% WER | 7,5002% |
| long-form, 9 записей | 3,632% WER | 3,891% |
Test-clean и test-other не участвовали в gradient training. Однако несколько checkpoints впоследствии сравнивались на этих наборах, поэтому строго научно я рассматриваю их как сильные evaluation suites, но не как навсегда sealed финальный аудит.
Важная оговорка
Это сравнение с исходным stock Whisper Small.
Я также обучил matched FP16-контроль:
- те же данные;
- тот же training code;
- то же расписание;
- та же длительность;
- квантование отключено.
Matched FP16-контроль пока примерно на 0,37 абсолютного пункта WER лучше тернарной ветки. Обе модели продолжали улучшаться к завершению эксперимента, поэтому окончательный quantization gap пока не установлен.
Следовательно, корректный вывод звучит так:
Дополнительно обученная тернарная модель стала лучше исходного stock Whisper Small, но пока не доказано, что тернаризация сама по себе улучшила качество.
Что здесь означает «1,58 бита»
Выражение «1,58-bit» происходит из информационной ёмкости трёх состояний:
[log_2(3)approx1{,}585.]
Но реальное компьютерное хранение устроено иначе.
В моём training-формате каждый вес использует двухбитный код:
00 → −1
01 → 0
10 → +1
11 → reserved
Для каждой группы из 128 весов дополнительно хранится один FP16-scale:
[w_i=s_gz_i,qquadz_iin{-1,0,+1}.]
Физическая стоимость крупных матриц во внутреннем формате:
[2+rac{16}{128}=2{,}125 ext{ bit/weight}.]
Текущий deploy-файл для whisper.cpp использует существующий формат Q2_0 с группой 64. Исходная группа 128 делится на две половины, которым назначается один и тот же выученный scale. Значения весов при этом восстанавливаются точно, но физическая стоимость становится:
[2+rac{16}{64}=2{,}25 ext{ bit/weight}.]
Поэтому есть четыре разные цифры:
| Показатель | Разрядность |
| Информационная ёмкость трёх состояний | 1,585 bpw |
| Training/reference layout, g128 | 2,125 bpw |
| Deploy Q2_0, g64 | 2,25 bpw |
| Среднее по всему гибридному файлу | около 4,86 bit/parameter |
Последняя цифра выше, потому что около 18% параметров модели — прежде всего tied embedding/output head — остаются FP16.
Таким образом, это не полностью 1,58-битная модель. Это гибридная Whisper Small, в которой 82% параметров имеют строгие тернарные значения и двухбитное физическое представление.
Почему я занялся именно этим
Мировая разработка frontier-моделей требует огромных вычислительных ресурсов. Конкурировать в одиночку с организациями, которые обучают модели на тысячах ускорителей, — не самая рациональная первая цель.
Но после обучения возникает другая фундаментальная проблема:
- размер весов;
- стоимость видеопамяти;
- пропускная способность памяти;
- энергопотребление;
- возможность локального запуска;
- зависимость от дорогого серверного оборудования.
Здесь и появляется направление binary и ternary neural networks.
На июль 2026 года эта область уже не ограничивается одной Microsoft. Существуют официальная Microsoft BitNet b1.58 2B4T, семейство Falcon3-1.58 от TII, Bonsai от PrismML, BitCPM-CANN от OpenBMB и несколько академических проектов. Но организаций, которые публично показывают полный цикл — обучение или конверсию, packed representation, специализированный runtime и нормальную оценку качества, — всё ещё относительно мало.
В распознавании речи тоже есть конкуренты. В 2025 году была опубликована работа о one-bit Conformer ASR, а в июле 2026 года Microsoft выпустила VibeVoice-ASR-BitNet — мультиязычную ASR-систему с heterogeneous low-bit quantization и CPU-runtime. Это означает, что я не могу называть свою модель первой тернарной ASR-моделью в мире.
Но на момент публикации я не нашёл другой открытой работы, которая одновременно:
- берёт обычный stock Whisper Small;
- переводит все 192 Transformer-матрицы в ternary;
- публикует QAT-библиотеку;
- публикует веса;
- публикует exporter;
- добавляет рабочий путь в whisper.cpp;
- показывает WER, matched FP16-контроль и отрицательные результаты.
Именно в этом заключается ценность проекта.
Как модель переводилась в ternary
Шаг 1. Простое округление не работает
Наивный вариант выглядит так:
[z_i=operatorname{round}(w_i/s),qquadz_iin{-1,0,+1}.]
Проблема в том, что все веса при таком подходе считаются одинаково важными.
Но реальное влияние ошибки определяется тем, на какие активации умножается вес.
Для линейного слоя:
[y=Wx.]
После замены весов:
[hat y=Qx.]
Ошибка выхода:
[delta y=(W-Q)x.]
Поэтому средняя ошибка слоя зависит не только от разницы весов, но и от статистики входных активаций:
[mathbb E|delta y|^2
operatorname{Tr}left[(W-Q)C_x(W-Q)^T ight].]
Для каждой группы из 128 весов я решаю activation-weighted задачу:
[min_{s,;z_iin{-1,0,+1}}sum_i h_i(w_i-sz_i)^2,]
где (h_i) — оценка важности соответствующего входного направления.
Инициализация тернарных кодов является не обычным округлением, а точной проекцией с учётом реальных активаций модели.
Шаг 2. Hard-forward QAT
После начальной проекции модель проходит quantization-aware training.
В forward используются только жёсткие веса:
[{-s, 0, +s}.]
Но обычное округление недифференцируемо. Поэтому backward использует непрерывную surrogate-переменную и straight-through-подобную оценку градиента.
Смысл:
forward:
только hard ternary codes
backward:
gradient проходит через latent proxy
Функция потерь включает:
- cross-entropy по транскриптам;
- KL-divergence к BF16 teacher;
- matching промежуточных признаков;
- дополнительные recovery-компоненты.
Очень важная особенность: latent weight не является master-copy
В классическом QAT часто предполагается:
FP master weight
→ fake quantization
→ forward
и после обучения финальный quantized checkpoint повторно строится из master weights.
В моей реализации это не так.
Deployment-представление модели — это обучаемая пара:
hard codes
+
group scales
Latent FP32 tensor нужен только как surrogate для движения между дискретными состояниями.
За время обучения latent weights:
- ушли от рабочих значений примерно на 3,8 медианы;
- около 60% оказались в насыщении;
- перестали быть осмысленной master-copy.
Когда я попытался заново спроецировать latent weights при экспорте, получил:
WER = 1000,56%
Правильный экспорт использует не latent tensor, а именно выученные hard codes и scales.
Это method-specific вывод:
В WAL-TAT master-представлением является пара (codes, scales), а latent weights — только инструмент оптимизации.
Что сломалось по пути
1. Модель научилась повторять фразы
Через несколько часов обучения модель начала зацикливаться.
Оказалось, что часть pseudo-labels, созданных teacher-моделью, уже содержала длинные повторы — до 108 повторяющихся пятиграмм внутри одной метки.
Таких записей было немного, но ошибка была чрезвычайно концентрированной. Несколько плохих меток могли дать больше ошибочных token, чем сотни чистых примеров.
Модель просто выучила то, что ей показали.
После этого перед каждым запуском автоматически проверяются:
повторяющиеся n-граммы
длина меток
p99 и maximum token count
пунктуация
регистр
служебный префикс
длительность аудио
несколько полных примеров вручную
Пять часов обучения были потеряны из-за проблемы, которую можно было обнаружить за минуту.
2. Стена на ровно 30 секундах
Whisper принимает окно длительностью 30 секунд.
Если запись короче, оставшаяся часть заполняется тишиной. Но если аудио имеет длину ровно 30,000 секунды, padding полностью отсутствует.
После fine-tuning модель почти не видела такие входы и резко деградировала именно на этой границе.
Контрольный эксперимент:
30,000 секунды → ошибка
29,990 секунды → работает
Разница — один небольшой кадр тишины.
Решение:
- добавить в training corpus значительную долю окон ровно 30 секунд;
- никогда не разрезать utterance посередине;
- закрывать окно на границе реплики.
3. Экспорт тернарных весов дал тысячу процентов WER
Это была ошибка с latent weights, описанная выше.
Она оказалась особенно ценной, потому что показала:
Формат inference должен быть определён ещё во время обучения.
Нельзя сначала обучить нечто похожее на ternary, а потом решить, как его упаковывать.
Forward, checkpoint и runtime должны реализовывать одну и ту же математику.
4. Я собрал модель-химеру
При первом экспорте 192 тернарные матрицы были взяты из моего checkpoint, а остальные 287 tensors — из исходной stock-модели.
Но эти tensors также изменились за 80 тысяч шагов обучения.
Получилась комбинация:
новые ternary matrices
+
старая FP16-обвязка
Первые слова распознавались правильно, затем текст распадался.
Особенно неприятно, что подобную ошибку я уже находил раньше в другом скрипте — но не превратил исправление в автоматический invariant.
После этого exporter обязан проверять lineage всех tensors.
5. Tied embedding/output head не пережил текущую T3-проекцию
Whisper использует одну матрицу и как token embedding, и как output projection.
Она содержит около 18% всех параметров и занимает примерно 86 из 140 МиБ готового файла.
При попытке перевести её текущим one-shot T3-методом:
WER: 2,71% → 1152%
вставки: 563 054
Это не доказывает, что tied head принципиально невозможно перевести в два бита.
Это доказывает более узкий факт:
Текущий projector без специализированного QAT полностью разрушает tied embedding/head.
Поэтому в текущей версии он остаётся FP16.
Следующий разумный путь:
Q8
→ Q6
→ learned Q4
→ mixed Q2/Q4
→ отдельный QAT для tied head
Где я сам себе мешал
Несколько раз мои первые объяснения были не просто ошибочными — они делали картину красивее.
«Проблема в префиксе»
Я объяснил расхождение тем, что один evaluator не передаёт языковые tokens.
Потом открыл код и увидел, что tokens передавались.
Настоящая причина — чувствительность batched matrix multiplication к порядку вычислений:
batch 16 → 3,4298% WER
batch 8 → 3,4224%
batch 1 → 3,4168%
Поэтому batch size теперь фиксируется.
Я называю диапазон около 0,013 процентного пункта не статистическим шумом в строгом смысле, а implementation sensitivity текущего harness. Различия меньше 0,02 п.п. не интерпретируются без отдельной paired-проверки.
«Эффект симметричен»
Я измерил одну сторону эффекта, получил красивую цифру и предположил, что вторая сторона ведёт себя так же.
Она оказалась почти втрое хуже.
«Обучение не дошло до 36 тысяч шагов»
Я записал это в результаты, а потом обнаружил в журнале строку 36000.
«1,48× ускорение»
Я сравнил вариант с CUDA Graphs против варианта без CUDA Graphs.
Это были разные условия, поэтому результат был удалён.
Неправильный замер памяти
Старый Python-словарь удерживал ссылки на 756 МиБ уже неиспользуемых weights. Я объявил память 991 МиБ, хотя настоящий результат составлял около 228 МиБ.
После этих случаев я начал заранее записывать:
- ожидаемый результат;
- критерий успеха;
- частичный успех;
- провал;
- порог тревоги.
Это мешает менять объяснение после того, как цифра уже известна.
Что получилось по качеству
Тернарная модель лучше stock Whisper Small на выбранных normalized WER-бенчмарках.
Но одновременно:
- matched FP16-контроль остаётся лучше T3;
- пунктуация почти исчезла;
- заглавные буквы почти исчезли;
- русский язык серьёзно деградировал;
- out-of-domain English значительно хуже LibriSpeech.
Пунктуация
На 300 utterances:
реплики с запятой:
T3 → 0,0–0,7%
stock → 63,3%
реплики с заглавной:
T3 → 2%
stock → 98%
Normalized WER обычно удаляет пунктуацию и регистр. Поэтому улучшение normalized WER не означает, что пользовательский текст стал лучше по всем параметрам.
Корректная формулировка:
Модель улучшила нормализованную точность распознавания слов, но существенно ухудшила орфографическое оформление текста.
Причина известна: training labels были приведены к нижнему регистру и очищены от знаков препинания.
Мультиязычность
Русский ухудшился примерно в 12,6 раза.
Но matched FP16-контроль, обученный на тех же английских данных, деградировал аналогично. Поэтому основной причиной является catastrophic forgetting при monolingual fine-tuning, а не непосредственно ternary quantization.
CPU-runtime
Для запуска без GPU я использовал whisper.cpp.
В ggml уже существовал тип Q2_0, но в зафиксированной версии:
- whisper quantize path его отвергал;
- x86 не имел специализированного SIMD vec-dot;
- generic fallback был слишком медленным.
Я добавил:
- достижимый путь Q2_0 для Whisper;
- AVX2 Q2_0 × Q8_0 kernel;
- исключение tied embedding/head из экстремального quantization;
- exact promotion encoder weights Q2_0 → Q8_0 при загрузке.
Последний пункт важен.
На диске encoder хранится как Q2_0. Но encoder Whisper выполняет плотные умножения сразу по множеству аудиофреймов и является compute-bound. Поэтому во время загрузки его тернарные weights точно разворачиваются в Q8_0:
Disk:
encoder Q2_0
decoder Q2_0
Runtime:
encoder exact Q8-expanded ternary
decoder packed Q2_0
tied head и служебные tensors FP16/F32
Математически значения encoder остаются теми же ternary weights. Но физически в оперативной памяти encoder работает как Q8.
Скорость
Честное сравнение CPU против CPU
Один Xeon, 16 threads, одинаковое аудио и один runtime:
| FP16 | Ternary hybrid | |
| Файл | 465 МиБ | 140 МиБ |
| Encoder / 30 секунд | 578 ms | 473 ms |
| Decoder / token | 6,8 ms | 6,0 ms |
Это главное apples-to-apples сравнение.
Тернарный decoder выигрывает за счёт меньшего объёма weights и специализированного kernel. Encoder после exact Q8 expansion также оказался быстрее stock FP16 в этом CPU-runtime.
CPU против H200
В другой конфигурации:
| H200, Hugging Face/PyTorch FP16 | Xeon, custom whisper.cpp | |
| Encoder / window | 7,1 ms | 550 ms |
| Decoder / token | 8,3 ms | 6,0 ms |
Формулировать это нужно осторожно.
Это не чистое сравнение CPU и GPU. Одновременно сравниваются:
- два hardware;
- два runtime;
- разные уровни fusion;
- custom C/AVX2;
- PyTorch graph;
- kernel-launch overhead.
Корректный вывод:
В данной конкретной паре реализаций latency одного autoregressive decoder step оказалась ниже на CPU. Но H200 примерно в 77 раз быстрее обрабатывает encoder, поэтому для большинства записей он остаётся быстрее end-to-end.
Профилирование показало около 439 GPU kernel launches на decoder pass. При batch 1 Whisper Small слишком мала, чтобы эффективно загрузить H200: значительная доля времени тратится на запуск операций, а не на математику.
Что пока не работает
1. Пунктуация
Нужен смешанный training curriculum:
нормализованные ASR labels
+
исходные punctuated labels
+
отфильтрованные teacher labels
Оценивать нужно одновременно:
- normalized WER;
- raw/orthographic WER;
- punctuation F1;
- capitalization accuracy.
2. Мультиязычность
Нужен multilingual replay corpus и KL к исходному stock Whisper на других языках.
3. Tied head
Следующий главный compression target — learned Q4 или mixed Q2/Q4 для embedding/output matrix. Именно он способен уменьшить файл сильнее всего.
4. Окончательный quantization gap
Нужно довести matched FP16 и T3 до плато, желательно с несколькими seeds.
5. Независимая оценка
Нужен новый one-shot audit:
- accented English;
- noisy speech;
- long-form;
- silence/no-speech;
- multilingual;
- timestamps.
Насколько это редкое направление
На Hugging Face уже существуют сотни артефактов с тегом BitNet, но значительная часть — это mirrors, небольшие эксперименты, простые post-training conversions или модели без полноценного low-bit runtime и независимой оценки.
К серьёзным открытым направлениям я бы отнёс:
- Microsoft BitNet b1.58 2B4T и BitNet Embeddings;
- Microsoft VibeVoice-ASR-BitNet;
- TII Falcon3/Falcon-E 1.58;
- PrismML Bonsai;
- OpenBMB BitCPM-CANN;
- отдельные академические проекты вроде One-bit ASR, Echo-1.58 и BitTTS.
Это уже не пустое поле. Но оно несравнимо меньше обычной экосистемы FP16, FP8 и INT4.
Особенно мало публичных работ, где исследователь берёт уже существующую модель, переводит её в ternary, публикует весь training pipeline и доводит результат до настоящего packed runtime.
Почему для меня это важно
Я живу и работаю в Казахстане.
Казахстан не обязан начинать участие в глобальной гонке ИИ с попытки в одиночку обучить крупнейшую модель мира. В технологическом развитии часто побеждает не тот, кто первым повторил всё, а тот, кто глубоко освоил одно критическое направление.
Я хочу заниматься экстремально низкобитными моделями:
- ternary и binary weights;
- преобразованием готовых моделей;
- quantization-aware training;
- recovery после экстремального сжатия;
- mixed-precision allocation;
- packed CPU/GPU kernels;
- локальным и edge-inference.
Сегодня это Whisper.
Следующей моделью может стать языковая модель на 1–8 миллиардов параметров. Затем — мультимодальная модель, embedding-модель или специализированный агент.
Моя цель — не просто выпускать отдельные checkpoints.
Я хочу создать воспроизводимую технологию:
готовая open model
→ architecture-aware conversion
→ hard low-bit training
→ independent quality audit
→ packed artifact
→ реальный runtime
Если такой стек будет создан и открыт из Казахстана, это станет более значимым результатом, чем ещё одна презентация о «развитии искусственного интеллекта».
Это будет работающий код, модель, измерения и доказательство того, что сложная исследовательская инженерия может делаться здесь.
Открытый результат
Опубликованы:
- веса модели;
- WAL-TAT/QAT-библиотека;
- pipeline подготовки данных;
- training scripts;
- exporter;
- patch для whisper.cpp;
- correctness tests;
- журналы измерений;
- отрицательные эксперименты;
- matched FP16-control.
Публичный репозиторий описывает 192 тернарные матрицы, модельный файл около 140 МиБ, WER-результаты и рабочий CPU-путь.
Exporter берёт именно выученные codes и scales, переносит их в Q2_0 без повторной переоценки и проверяет точность обратной распаковки.
Patch добавляет достижимый Q2_0 path, AVX2 kernel и hybrid precision policy для encoder и tied head.
Итог
Этот проект не доказал, что ternary всегда лучше FP16.
Он доказал другое:
- Обычную предобученную Whisper Small можно полностью перевести по всем крупным Transformer-матрицам в строгий ternary.
- После QAT модель может сохранить сильное качество ASR.
- Трёхзначные weights можно действительно упаковать и запускать напрямую на CPU.
- Большая часть ошибок находится не в одной формуле quantization, а в данных, evaluation, экспорте и runtime.
- Улучшение одного показателя может скрывать деградацию другого.
- Экстремальное сжатие требует такой же строгости измерений, как само обучение.
Для меня это только первая модель.
Главная цель — превратить этот единичный результат в универсальный, architecture-aware инструмент преобразования открытых моделей в ternary и binary representation.
И сделать так, чтобы среди стран, которые не только используют чужие AI-модели, но и создают собственные методы их преобразования и запуска, публично появился Казахстан.
Модель, код и воспроизведение
Проект опубликован полностью и доступен для самостоятельной проверки:
В репозитории опубликованы:
- библиотека quantization-aware training;
- pipeline подготовки и проверки данных;
- training-конфигурации;
- скрипты оценки WER;
- exporter в формат GGML Q2_0;
- AVX2-патч для whisper.cpp;
- тесты корректности упаковки и вычислений;
- matched FP16-контроль;
- диагностические отчёты и отрицательные результаты.
Модель можно скачать и запустить локально:
hf download armanibadboy/whisper-small-ternary \
ggml-small-wal-ternary-q2_0.bin \
--local-dir models/
После применения опубликованного патча к whisper.cpp:
./build/bin/whisper-cli \
-m models/ggml-small-wal-ternary-q2_0.bin \
-f audio.wav \
-ng
Ссылки
Код, веса, обучение, exporter, CPU-runtime и результаты опубликованы открыто. Проект можно не только прочитать, но и самостоятельно проверить и воспроизвести.