Ddavidich commited on
Commit
f2dce28
·
verified ·
1 Parent(s): 2dc2b5b

Сплошная сверка чисел карточки: заголовочное число было подписано неверно

Browse files

26 895 998 464 — это счёт ТОЛЬКО языковой части. Полное минус зрение минус
оверлей ALTAY даёт его до байта. Карточка называла этим числом размер
мультимодальной модели, то есть исключала ровно ту часть, которая делает её
мультимодальной. Первоисточника у числа не было нигде в репозитории.

всего в чекпойнте: 27 274 981 624
языковая часть: 26 895 998 464
зрительная башня: 371 380 976
оверлей ALTAY: 7 602 184
тензоров: 2 509 (стояло 2 503)

Объяснение под числом не сходилось само с собой: 11.96 млрд байт + 23.9 млрд
+ 3.17 млрд FP8 = 27.07 млрд, а не 26.896; и все U8 считались четырёхбитными,
хотя эмбеддинги восьмибитные, а зрение смешанное.

Считается теперь по заголовкам shard-файлов, ширина восстанавливается из формы
таблицы масштабов; неразрешённых тензоров не остаётся ни одного
(count_parameters.py, PARAMETER_COUNT_RECEIPT.json).

Плюс квитанция на ёмкость арены: 1 017 164 токена, 492 блока при 490 нужных.
Число карточка называла и раньше, но записано оно не было.

README.md CHANGED
@@ -89,8 +89,10 @@ model-index:
89
 
90
  # LOMONOSOV ZENIT 27B «ALTAY» — INDEV
91
 
92
- A 26,895,998,464-parameter multimodal model with a **1,010,000-token context**,
93
- built to run on one consumer graphics card.
 
 
94
 
95
  **This is a work in progress, and the card says so wherever it matters.** Every
96
  number below was measured on our own hardware and has a receipt. Nothing is
@@ -113,13 +115,37 @@ Languages: English, Russian, Ukrainian. Multimodal input is inherited from the
113
  base model.
114
 
115
  **The "model size" badge above says about 17B. Ignore it.** Hugging Face counts
116
- the elements in each tensor as they are stored, and the NVFP4 text weights are
117
- four-bit, packed two to a byte in `U8` tensors. There are 11.96 billion such
118
- bytes, holding 23.9 billion parameters; add the 3.17 billion FP8 attention
119
- weights, where one byte is one parameter, and you get the real figure of
120
- 26,895,998,464. The badge is halving the packed half. Nothing is missing from
121
- the files: 2,503 tensors, every shard's header verified against its file length
122
- after a round trip through the Hub.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  Built on **Qwen3.6-27B** by Alibaba, then requantised and given the ALTAY overlay
125
  and the extended window. Development followed the practices of **ChatGPT 5.6 Sol
@@ -743,8 +769,10 @@ are the same either way.
743
 
744
  # LOMONOSOV ZENIT 27B «ALTAY» — INDEV
745
 
746
- Мультимодальная модель на 26 895 998 464 параметра с контекстом
747
- **1 010 000 токенов**, рассчитанная на одну потребительскую видеокарту.
 
 
748
 
749
  **Это работа в процессе, и карточка говорит об этом там, где это важно.** Каждое
750
  число ниже измерено на нашем железе и имеет квитанцию. Ничего не оценено на глаз,
@@ -763,13 +791,37 @@ are the same either way.
763
  базовой модели.
764
 
765
  **Бейдж «model size» наверху показывает около 17B. Не верьте ему.** Hugging Face
766
- считает элементы тензоров так, как они лежат в файле, а текстовые веса в NVFP4
767
- четырёхбитные и упакованы по два значения в байт, тип `U8`. Таких байтов 11.96
768
- миллиарда, а параметров в них 23.9 миллиарда; прибавьте 3.17 миллиарда весов
769
- внимания в FP8, где байт равен параметру, и получите настоящее число
770
- 26 895 998 464. Бейдж делит упакованную половину надвое. В файлах ничего не
771
- пропало: 2 503 тензора, у каждого шарда заголовок сверен с длиной файла после
772
- скачивания обратно с Hub.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
 
774
  Построена на **Qwen3.6-27B** от Alibaba, затем переквантована и получила
775
  наложение ALTAY и расширенное окно. Разработка шла с оглядкой на практики
 
89
 
90
  # LOMONOSOV ZENIT 27B «ALTAY» — INDEV
91
 
92
+ A **26,895,998,464**-parameter language model with a **1,010,000-token context**,
93
+ built to run on one consumer graphics card. With the vision tower and the ALTAY
94
+ overlay the checkpoint holds **27,274,981,624** parameters in total
95
+ (`receipts/PARAMETER_COUNT_RECEIPT.json`).
96
 
97
  **This is a work in progress, and the card says so wherever it matters.** Every
98
  number below was measured on our own hardware and has a receipt. Nothing is
 
115
  base model.
116
 
117
  **The "model size" badge above says about 17B. Ignore it.** Hugging Face counts
118
+ the elements in each tensor as they are stored, and most weights here are packed
119
+ several to an element. Counted properly original width recovered per tensor
120
+ from the shape of its scale table, which resolves every packed tensor with none
121
+ left over:
122
+
123
+ | | parameters |
124
+ |---|---:|
125
+ | four-bit packed | 24,054,511,616 |
126
+ | eight-bit packed | 1,504,074,752 |
127
+ | unpacked: FP8 attention, BF16 norms and biases | 1,716,395,256 |
128
+ | **total** | **27,274,981,624** |
129
+ | of that, the language model | 26,895,998,464 |
130
+ | vision tower | 371,380,976 |
131
+ | ALTAY overlay | 7,602,184 |
132
+
133
+ Embeddings are not tied (`tie_word_embeddings: false`), so `embed_tokens` and
134
+ `lm_head` are 1,271,398,400 each and counted separately; there is no double
135
+ count. Nothing is missing from the files: **2,509** tensors, matching
136
+ `model.safetensors.index.json` exactly, every shard's header verified against its
137
+ file length after a round trip through the Hub.
138
+
139
+ > **Corrected 2026-07-26.** This paragraph used to derive the figure as "11.96
140
+ > billion bytes holding 23.9 billion parameters, add 3.17 billion FP8" — which
141
+ > sums to 27.07 billion, not to the number it claimed to produce, and it counted
142
+ > every `U8` tensor as four-bit when the embeddings are eight-bit and the vision
143
+ > tower is mixed. It also said 2,503 tensors. And the headline called
144
+ > 26,895,998,464 the size of a *multimodal* model, when that figure is precisely
145
+ > the count with the multimodal part left out. The number itself had no primary
146
+ > source anywhere in the repository — it is measured now
147
+ > (`receipts/PARAMETER_COUNT_RECEIPT.json`, reproduced by `count_parameters.py`),
148
+ > and it turns out to be exactly the language model.
149
 
150
  Built on **Qwen3.6-27B** by Alibaba, then requantised and given the ALTAY overlay
151
  and the extended window. Development followed the practices of **ChatGPT 5.6 Sol
 
769
 
770
  # LOMONOSOV ZENIT 27B «ALTAY» — INDEV
771
 
772
+ Языковая модель на **26 895 998 464** параметра с контекстом
773
+ **1 010 000 токенов**, рассчитанная на одну потребительскую видеокарту. Вместе
774
+ со зрительной башней и оверлеем ALTAY в чекпойнте **27 274 981 624** параметра
775
+ (`receipts/PARAMETER_COUNT_RECEIPT.json`).
776
 
777
  **Это работа в процессе, и карточка говорит об этом там, где это важно.** Каждое
778
  число ниже измерено на нашем железе и имеет квитанцию. Ничего не оценено на глаз,
 
791
  базовой модели.
792
 
793
  **Бейдж «model size» наверху показывает около 17B. Не верьте ему.** Hugging Face
794
+ считает элементы тензоров так, как они лежат в файле, а почти все веса здесь
795
+ упакованы по нескольку в элемент. Если считать правильно — восстанавливая
796
+ исходную ширину каждого тензора по форме его таблицы масштабов, при этом
797
+ неразрешённых тензоров не остаётся ни одного:
798
+
799
+ | | параметров |
800
+ |---|---:|
801
+ | упаковано по четыре бита | 24 054 511 616 |
802
+ | упаковано по восемь бит | 1 504 074 752 |
803
+ | без упаковки: внимание FP8, нормировки и смещения BF16 | 1 716 395 256 |
804
+ | **всего** | **27 274 981 624** |
805
+ | из них языковая часть | 26 895 998 464 |
806
+ | зрительная башня | 371 380 976 |
807
+ | оверлей ALTAY | 7 602 184 |
808
+
809
+ Эмбеддинги не связаны (`tie_word_embeddings: false`), поэтому `embed_tokens` и
810
+ `lm_head` весят по 1 271 398 400 и считаются порознь — двойного счёта нет. В
811
+ файлах ничего не пропало: **2 509** тензоров, ровно столько же в
812
+ `model.safetensors.index.json`, у каждого шарда заголовок сверен с длиной файла
813
+ после скачивания обратно с Hub.
814
+
815
+ > **Исправлено 2026-07-26.** Здесь число выводилось так: «11.96 миллиарда байт,
816
+ > в них 23.9 миллиарда параметров, прибавьте 3.17 миллиарда FP8» — а это в сумме
817
+ > 27.07 миллиарда, а вовсе не то число, которое абзац брался получить. Считались
818
+ > при этом все `U8` как четырёхбитные, хотя эмбеддинги восьмибитные, а зрение
819
+ > смешанное. И тензоров стояло 2 503. Заголовок же называл 26 895 998 464
820
+ > размером **мультимодальной** модели, тогда как это ровно тот счёт, из которого
821
+ > мультимодальная часть исключена. Первоисточника у числа не было нигде в
822
+ > репозитории — теперь оно замерено
823
+ > (`receipts/PARAMETER_COUNT_RECEIPT.json`, воспроизводится `count_parameters.py`),
824
+ > и оказалось в точности размером языковой части.
825
 
826
  Построена на **Qwen3.6-27B** от Alibaba, затем переквантована и получила
827
  наложение ALTAY и расширенное окно. Разработка шла с оглядкой на практики
count_parameters.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Пересчёт параметров по заголовкам shard-файлов. Веса не читаются.
3
+
4
+ Зачем. Заголовочное число карточки — 26 895 998 464 — не имело первоисточника:
5
+ его нет ни в конфиге, ни в родословных, ни в одной квитанции. Правило выпуска
6
+ запрещает в карточке цифры без замера, поэтому число надо либо подтвердить, либо
7
+ исправить.
8
+
9
+ Почему «в лоб» не считается. Веса упакованы, и упакованы по-разному:
10
+
11
+ * `weight_packed` встречается и в `U8`, и в `I32` — то есть число элементов
12
+ тензора само по себе о числе параметров не говорит;
13
+ * ширина тоже разная: NVFP4 кладёт по два значения в байт, эмбеддинги
14
+ квантованы в 8 бит, зрение — `SELECTIVE_W8_W4_A16`, то есть вперемешку.
15
+
16
+ Первая попытка удвоила все `U8` подряд и дала 27 553 793 896 — мимо на 658
17
+ миллионов. Вторая читала число элементов вместо байт и промахнулась в другую
18
+ сторону.
19
+
20
+ Как считается здесь. Исходная ширина восстанавливается из таблицы масштабов:
21
+ при групповом квантовании `weight_scale` имеет форму [out, in/group_size], а
22
+ `weight_packed` занимает `in * bits / 8` байт на строку. Перебирая размер группы
23
+ из конфига (16, 32, 64, 128), берём тот, при котором ширина выходит ровно 4 или
24
+ 8 бит. Неразрешённых тензоров при этом не остаётся ни одного — это и есть
25
+ проверка, что подбор не выдумывает.
26
+
27
+ Служебные тензоры (масштабы, нули, формы) в счёт не идут; нормировки, смещения
28
+ и параметры Gated DeltaNet (`A_log`, `dt_bias`, `altay_alpha`) идут.
29
+ """
30
+ from __future__ import annotations
31
+
32
+ import argparse
33
+ import collections
34
+ import glob
35
+ import json
36
+ import math
37
+ import os
38
+ import struct
39
+
40
+ BYTES = {"U8": 1, "I8": 1, "I32": 4, "I64": 8,
41
+ "F8_E4M3": 1, "BF16": 2, "F16": 2, "F32": 4}
42
+
43
+ # Не параметры: таблицы масштабов, нулей и сохранённые формы.
44
+ SKIP = ("weight_scale", "weight_shape", "weight_zero_point",
45
+ "weight_global_scale", "input_scale", "input_global_scale",
46
+ "weight_g_idx")
47
+
48
+
49
+ def bucket(name: str) -> str:
50
+ if name.startswith("model.visual"):
51
+ return "vision_tower"
52
+ if "altay" in name.lower():
53
+ return "altay_overlay"
54
+ if "embed_tokens" in name:
55
+ return "embed_tokens"
56
+ if name.startswith("lm_head") or ".lm_head" in name:
57
+ return "lm_head"
58
+ return "language_model"
59
+
60
+
61
+ def header(path: str) -> dict:
62
+ with open(path, "rb") as fh:
63
+ n = struct.unpack("<Q", fh.read(8))[0]
64
+ return json.loads(fh.read(n))
65
+
66
+
67
+ def count(directory: str) -> dict:
68
+ per = collections.Counter()
69
+ by_file = collections.Counter()
70
+ by_width = collections.Counter()
71
+ unresolved: list = []
72
+
73
+ for path in sorted(glob.glob(os.path.join(directory, "*.safetensors"))):
74
+ hdr = header(path)
75
+ shapes = {k: (v.get("shape") or [], v.get("dtype"))
76
+ for k, v in hdr.items() if k != "__metadata__"}
77
+ for name, (shape, dtype) in shapes.items():
78
+ if name.endswith(SKIP):
79
+ continue
80
+ n = 0
81
+ if name.endswith(".weight_packed"):
82
+ scale = shapes.get(name[: -len(".weight_packed")] + ".weight_scale")
83
+ if not scale or len(shape) < 2 or len(scale[0]) < 2:
84
+ unresolved.append(name)
85
+ continue
86
+ out_features = shape[0]
87
+ row_bytes = shape[1] * BYTES[dtype]
88
+ groups = scale[0][1]
89
+ for group_size in (16, 32, 64, 128):
90
+ in_features = groups * group_size
91
+ bits = row_bytes * 8 / in_features
92
+ if abs(bits - 4) < 1e-9 or abs(bits - 8) < 1e-9:
93
+ n = out_features * in_features
94
+ by_width[int(round(bits))] += n
95
+ break
96
+ else:
97
+ unresolved.append(name)
98
+ continue
99
+ elif dtype in ("F8_E4M3", "BF16", "F32", "F16"):
100
+ n = math.prod(shape) if shape else 1
101
+ per[bucket(name)] += n
102
+ by_file[os.path.basename(path)] += n
103
+
104
+ packed_total = sum(by_width.values())
105
+ total = sum(per.values())
106
+ language = per["language_model"] + per["embed_tokens"] + per["lm_head"]
107
+ return {
108
+ "schema": "lomonosov_zenit_parameter_count_v1",
109
+ "method": "заголовки safetensors; ширина восстановлена из формы weight_scale",
110
+ "unresolved_tensors": unresolved,
111
+ "by_purpose": dict(per),
112
+ "by_file": dict(by_file),
113
+ "quantised_by_width_bits": {str(k): v for k, v in sorted(by_width.items())},
114
+ "packed_total": packed_total,
115
+ "unpacked_total": total - packed_total,
116
+ "language_model_total": language,
117
+ "vision_tower": per["vision_tower"],
118
+ "altay_overlay": per["altay_overlay"],
119
+ "total_all_parts": total,
120
+ }
121
+
122
+
123
+ def main() -> int:
124
+ ap = argparse.ArgumentParser()
125
+ ap.add_argument("--model", required=True)
126
+ ap.add_argument("--out")
127
+ args = ap.parse_args()
128
+
129
+ result = count(args.model)
130
+ cfg = json.load(open(os.path.join(args.model, "config.json"), encoding="utf-8"))
131
+ result["tie_word_embeddings"] = cfg.get("tie_word_embeddings")
132
+ result["note_on_tying"] = (
133
+ "эмбеддинги НЕ связаны, поэтому embed_tokens и lm_head считаются отдельно "
134
+ "и двойного счёта нет"
135
+ )
136
+ result["card_headline_figure"] = 26_895_998_464
137
+ result["card_figure_equals"] = (
138
+ "language_model_total: заголовочное число карточки — счёт ТОЛЬКО языковой "
139
+ "части, без зрения и без оверлея ALTAY"
140
+ )
141
+ result["card_figure_matches_language_model"] = (
142
+ result["language_model_total"] == 26_895_998_464
143
+ )
144
+
145
+ if args.out:
146
+ with open(args.out, "w", encoding="utf-8") as fh:
147
+ json.dump(result, fh, ensure_ascii=False, indent=1)
148
+ print(json.dumps(result, ensure_ascii=False, indent=1))
149
+ return 0
150
+
151
+
152
+ if __name__ == "__main__":
153
+ raise SystemExit(main())
receipts/KV_ARENA_CAPACITY_RECEIPT.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "lomonosov_zenit_kv_arena_capacity_v1",
3
+ "what_this_shows": "ёмкость арены KV на релизной сборке, как её объявляет сам движок",
4
+ "source": "строка vLLM kv_cache_utils.py: 'GPU KV cache size'",
5
+ "arena_bytes": 13000000000,
6
+ "kv_cache_tokens": 1017164,
7
+ "window_tokens": 1010001,
8
+ "spare_tokens": 7163,
9
+ "block_tokens": 2064,
10
+ "blocks_total": 492,
11
+ "blocks_for_window": 490,
12
+ "spare_blocks": 2,
13
+ "why_spare_matters": "арена впритык (ноль свободных блоков) once дала шестикратный простой движка; сквозной прогон на двух свободных блоках простоя не показал — million_arena.json",
14
+ "observed_in": [
15
+ "million_arena2.log 03:18:19 UTC+3",
16
+ "повтор 04:41:30 UTC+3"
17
+ ],
18
+ "host": "арендованная RTX 5090, vast.ai"
19
+ }
receipts/PARAMETER_COUNT_RECEIPT.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "lomonosov_zenit_parameter_count_v1",
3
+ "method": "заголовки safetensors; ширина восстановлена из формы weight_scale",
4
+ "unresolved_tensors": [],
5
+ "by_purpose": {
6
+ "altay_overlay": 7602184,
7
+ "embed_tokens": 1271398400,
8
+ "language_model": 24353201664,
9
+ "lm_head": 1271398400,
10
+ "vision_tower": 371380976
11
+ },
12
+ "by_file": {
13
+ "model-altay-overlay.safetensors": 7602184,
14
+ "model-embed-int8.safetensors": 1271398400,
15
+ "model-text-1-of-5.safetensors": 2493626176,
16
+ "model-text-2-of-5.safetensors": 6670320992,
17
+ "model-text-3-of-5.safetensors": 6734207840,
18
+ "model-text-4-of-5.safetensors": 6665569632,
19
+ "model-text-5-of-5.safetensors": 3060875424,
20
+ "model-vision.safetensors": 371380976
21
+ },
22
+ "quantised_by_width_bits": {
23
+ "4": 24054511616,
24
+ "8": 1504074752
25
+ },
26
+ "packed_total": 25558586368,
27
+ "unpacked_total": 1716395256,
28
+ "language_model_total": 26895998464,
29
+ "vision_tower": 371380976,
30
+ "altay_overlay": 7602184,
31
+ "total_all_parts": 27274981624,
32
+ "tie_word_embeddings": false,
33
+ "note_on_tying": "эмбеддинги НЕ связаны, поэтому embed_tokens и lm_head считаются отдельно и двойного счёта нет",
34
+ "card_headline_figure": 26895998464,
35
+ "card_figure_equals": "language_model_total: заголовочное число карточки — счёт ТОЛЬКО языковой части, без зрения и без оверлея ALTAY",
36
+ "card_figure_matches_language_model": true
37
+ }