20 жніўня я зафіксаваў у файле 6 задач і пачаў задаваць claude-opus-5 адны і тыя ж пытанні праз Claude Code CLI. Кожны адказ правярае грэйдар. Мне патрэбна была кропка адліку для пастоў “мадэль на гэтым тыдні зрабілі дурнейшай”, але спачатку файл паказаў розніцу ўнутры аднаго заходу. Адна з задач: 60 месяцаў росту MRR па складаных працэнтах, дзе тэмп мяняецца пасля 6-га месяца. Код лічыць 58937.5857. За 64 выклікі з 20 жніўня па 12 верасня мадэль вярнула 21 розны лік. На астатніх 5 задачах яна давала адзін адказ на задачу. 1 выклік упаў на ліміце запытаў.
Я заўсёды тлумачыў гэта сабе тэмпературай. У CLI яе не задаць, таму я ні разу не ставіў яе ў 0 і лічыў, што пры 0 адказ перастане мяняцца. Яшчэ я чытаў “Defeating Nondeterminism in LLM Inference” Хораса Хэ з Thinking Machines, верасень 2025. Там сказана, што нават пры тэмпературы 0 сервер аддае розны тэкст, бо яго вылічальныя ядры (kernels) не інварыянтныя адносна памеру батча. А батч залежыць ад чужога трафіку. У гэта я таксама паверыў і таксама не правяраў. Таму на гэты раз я ўзяў мадэль, у якой нічога не схавана. У ёй я стаў шукаць месца, дзе розніца пачынаецца.
Выбарка выключаная, float32
Qwen2.5 0.5B Instruct, transformers 5.16.1 і torch 2.14.0 на 4 ядрах працэсара AMD EPYC, прагнае дэкадаванне, гэта значыць ніякай выбаркі. Qwen кладзе ў свой generation config штраф за паўтор 1.1 і я пакінуў яго ўключаным ва ўсіх прагонах. 8 промптаў з доўгімі адказамі, ад хэш-табліц да хатняга хлеба.
У float32 не зрушылася нічога. 5 паўтораў запар далі тыя ж лагіты біт у біт. 1, 2 і 4 патокі таксама далі тыя ж біты, чаго я не чакаў. Батч з 2, 4 ці 8 мяняў лагіты ва ўсіх 8 промптах, але толькі ў пятым знаку пасля коскі. Токены супалі на ўсіх 200 кроках, а ў другой серыі на ўсіх 400.
Прагнае дэкадаванне бярэ токен з найбольшай ацэнкай, значыць для развілкі зазор да другога токена мусіць быць меншы за шум. Самы малы зазор у 8 адказах даўжынёй да 400 токенаў быў 0.000122, на промпце пра суму. У батчы з 8 той жа крок даў 0.000109. Батч ні разу не зрушыў зазор больш чым на 0.00004.
bfloat16
Я загрузіў тыя ж вагі ў bfloat16. Паўтор на батчы 1 зноў даў тыя ж токены, гэта значыць сама з сабой мадэль па-ранейшаму дэтэрмінаваная.
У bfloat16 8 бітаў дакладнасці і лагіт каля 17 захоўваецца з крокам 0.125. 2 токены могуць атрымаць дакладна аднолькавую ацэнку. У float32 такога не было ні разу за 2782 крокі, а ў bfloat16 2 верхнія токены зраўняліся 38 разоў за 2709 крокаў. Промпт пра хлеб пачаўся з нічыёй: у Certainly і Sure па 17.625, а ў float32 у іх было 17.693 і 17.598. Без батча прагон пачаўся з “Sure! Here’s a simple guide”. У батчы з 8 ён пачаўся з “Certainly! Making bread at home”.
У простым прамым праходзе батч зрушваў лагіты першага кроку не больш чым на 0.000047 у float32 і на 0.29 у bfloat16. Праз generate у батчы з 8 іншым шляхам пайшлі 4 промпты з 8. float32 супраць bfloat16, абодва на батчы 1, разышліся ў 7 промптах з 8. Промпт пра суму просіць скласці лікі ад 1 да 250, якія дзеляцца на 3 ці 7, сума роўная 13482. Абодва прагоны замест сумы выдалі колькасць. Правільная колькасць, 107, атрымалася толькі ў bfloat16, а float32 памыліўся на кратных 21 і напісаў 106.
Тут я захрас. 2 з 4 развілак у батчы здарыліся там, дзе зазор паміж 2 верхнімі токенамі быў 1.375, далёка ад любой нічыёй. Я не разумеў, як шум у 0.29 можа такое перавярнуць. Адказ ляжаў у маім жа скрыпце. Зазоры я браў з сырых лагітаў, а дэкодар выбірае пасля штрафу за паўтор. У float32 токен, першы па сырых лагітах, не супадаў з выбраным у 396 кроках з 2782. Я прагнаў серыю нанова, цяпер з зазорам пасля штрафу. Токены выйшлі тыя ж ва ўсіх 32 прагонах. На гэтых 2 кроках зазор пасля штрафу быў 0.09 без батча, а ў батчы 0.034 і 0.023. Астатнія 2 развілкі былі дакладнымі нічыімі.
Назад да opus
Потым я 20 разоў папрасіў claude-opus-5 растлумачыць калізіі ў хэш-табліцы прыкладна ў 150 словах. CLI 2.1.283, thinking выключаны, без інструментаў, пусты канфіг MCP, просты сістэмны промпт і тэчка без аніводнага CLAUDE.md вышэй па дрэве. Я атрымаў 20 розных тэкстаў, усе пачынаюцца з “A hash table” і кажуць пра метад ланцужкоў і адкрытую адрасацыю. У 132 парах з 190 агульны пачатак абодвух тэкстаў не даўжэйшы за 11 слоў.
Пасля лакальных прагонаў я чытаю 21 адказ на арыфметыку як групы, а не як шум. Тыя выклікі ішлі праз CLI ад 2.1.237 да 2.1.269 з уключаным thinking. Грэйдар дапускае адносную памылку да 1e-6, гэта каля 6 цэнтаў. У допуск трапілі 46 адказаў з 64. 31 з іх супадае з правільным значэннем да цэнта: 16 акруглілі да 58937.59, 15 адкінулі хвост да 58937.58. З 18 па-за допускам 6 ляжаць каля 58928.2, а 7 прамахваюцца менш чым на 20 цэнтаў.
Разабраць мадэль у сэрвісе так, як я разабраў маленькую, я не магу. CLI бярэ тую выбарку, якую наладзіў сэрвіс, таму выбарка і батчынг трапляюць у адзін лік разам з усім астатнім, што адбываецца на серверы. Лакальныя прагоны паказваюць толькі тое, чаго хапае, каб тэкст змяніўся пры выключанай выбарцы: нічыя ў bfloat16 ці суседзі па батчы. Пра арыфметыку ў мяне здагадка: мадэль лічыць яе ў доўгім схаваным разважанні, ад 691 да 6632 выходных токенаў на выклік. Калі адзін крок рана пойдзе інакш, ён пацягнуў бы за сабой усё астатняе. Адказы кладуцца групамі і гэта са здагадкай сыходзіцца, але я яе не правяраў.
Для маіх уласных інструментаў вывад сціплы. Грэйдар і так параўноўвае лікі з допускам. Допуск я пакідаю. Крытык, які ацэньвае мае чарнавікі, шуміць гэтак жа. На адным чарнавіку ён 30 разоў з 30 вынес адзін і той жа адмоўны вердыкт. Пры гэтым ён назваў 160 розных заўваг і 114 з іх прагучалі толькі аднойчы.
Чаго я не правяраў
- GPU. Усе лакальныя прагоны ішлі на CPU, а вылічальныя ядры, пра якія піша Thinking Machines, напісаныя для GPU. Юань з суаўтарамі (arXiv 2506.09501) мералі, як уплываюць bf16 супраць fp32 і памер батча, на GPU з мадэлямі на 7B. Мой вынік з bfloat16 гэта той жа эфект дакладнасці на значна меншай мадэлі.
- Мадэлі праз API на вялікай колькасці выклікаў. Атыл з суаўтарамі (arXiv 2408.04667) мералі гэты раскід на 5 мадэлях.
- Тэмпература 0 у мадэлі ў сэрвісе. У CLI такой налады няма.
- Чаму батч у прамым праходзе захоўвае нічыю ў промпце пра хлеб на 17.625, а батч з 8 праз
generateразбівае яе на 0.125. Самаму доўгаму промпту падзінг не патрэбны і паводзіць ён сябе гэтак жа. Пры прамым праходзе батчам ён супадае біт у біт, а празgenerateразыходзіцца ў пятым знаку. - Якое вылічэнне дае 58928.2. Гэты лік вярнуўся 6 разоў, так што гэта, хутчэй за ўсё, устойлівы няправільны шлях, але крок я не знайшоў.