Глава 15 из 17 30 мин
Больше модели
У Ростка-2 125 миллионов параметров в тридцати узких блоках. Где эти параметры сидят, что нужно модели такого размера, без чего маленькая обходилась, как мы проверили всё это на маленьких копиях и как прошли девять миллиардов токенов обучения.
В этой главе
- посчитать, куда уходят параметры трансформера, и объяснить, почему Росток-2 глубокий и узкий
- вычислить KV-кэш модели и увидеть, как его ужимает групповое внимание
- объяснить маску документов, мягкий потолок для логитов и расписание с контрольной точкой перед спуском — и прочитать настоящие кривые обучения по источникам
Росток-1 — это 8 блоков шириной 384 числа, 6 голов внимания, словарь в 8 192 токена и окно в 512. Росток-2 — 30 блоков по 576, 9 голов запросов, которые делят между собой 3 головы ключей и значений, словарь в 32 768 токенов и окно в 2 048: 125,1 миллиона параметров вместо 17,3 миллиона. Росток-1 прочитал 330 миллионов токенов за 10 070 шагов — три с половиной часа на Mac. Росток-2 прочитал 9,0 миллиарда за 17 166 шагов по 524 288 токенов.
Ничто из глав 7, 8 и 10 не устарело: то же внимание, тот же блок из внимания и SwiGLU, те же Muon и AdamW, та же функция потерь. Когда модель растёт, меняется другое, менее заметное: её форма, то, чем занята память, пока она пишет, несколько ремней безопасности, без которых маленькая модель обходилась, и то, как всё это выбрать до долгого обучения. Но сначала — результат.
Как он рос
В одиннадцать моментов обучения Ростку-2 давали одни и те же пять затравок: начало сказки, начало объяснения, функцию на Python, команду shell и вопрос. Выберите затравку и поднимайтесь по лесенке — или нажмите «Вырастить».
На что посмотреть.
- Шаг 200: 105 миллионов токенов, треть всего, что прочитал Росток-1. У сказки уже есть интонация детской истории. У функции отступы стоят на своих местах, но она ничего не вычисляет:
weight = 0,height = 1. Команда превращается в конвейер изxargsиawk. - К шагу 2 000 сказка держится: кролик, бревно, желание заглянуть внутрь. К шагу 4 000 после команды
tarидёт то, что напечатал бы терминал:ls -lи список файлов с правами и владельцами. - Функция даётся труднее всего. На шаге 8 000 у неё есть правильная строка документации — «Return the n-th fibonacci number.», — а возвращает она 2, 3, 4 и 5. На шаге 16 000 появляется рекурсия из учебника,
fibonacci(n-1) + fibonacci(n-2), но с таким базовым случаем, что вычисление никогда не закончится. А на самом последнем шаге Росток-2 выдумывает «FIBONAREA». Одна запись — один бросок кубиков; измерение — это кривая рядом. - На вопрос он не отвечает ни на одном шаге. С шага 6 000 Росток-2 продолжает его так, как продолжается ветка на форуме: «Here is a list of all files that are hidden from view. How do I get the list of hidden files…?» На шаге 16 000 он даже предлагает команду
find— и, как и положено на форуме, следом сообщение об ошибке, которое кто-то от неё получил. Запомните это: в конце главы мы к этому вернёмся.
А теперь заглянем внутрь.
Куда уходят параметры
Разберём Росток-2 так же, как глава 8 разбирала Росток-1. Рядом:
| Росток-1 | Росток-2 | |
|---|---|---|
| блоков | 8 | 30 |
| ширина потока $d$ | 384 | 576 |
| головы запросов по 64 | 6 | 9 |
| головы ключей и значений | 6 | 3 |
| ширина SwiGLU $d_{ff}$ | 1 024 | 1 536 |
| словарь | 8 192 | 32 768 |
| окно | 512 | 2 048 |
| основание RoPE | 10 000 | 100 000 |
| логиты | как есть | под мягким потолком 30 |
| параметров | 17 309 056 | 125 081 664 |
Посчитаем один блок. У внимания четыре матрицы, но квадратных среди них только две: запросы $W_Q$ и выходная проекция $W_O$ — $576 \times 576$, а ключам и значениям хватает 3 голов по 64, так что $W_K$ и $W_V$ — $576 \times 192$. В сети SwiGLU три матрицы $576 \times 1\,536$. Остаются две нормировки и усиления QK-norm:
$$2 \cdot 576^2 + 2 \cdot 576 \cdot 192 + 3 \cdot 576 \cdot 1\,536 + 2 \cdot 576 + 2 \cdot 64 = 3\,540\,224.$$Тридцать таких блоков, таблица эмбеддингов и последняя нормировка:
$$32\,768 \cdot 576 + 30 \cdot 3\,540\,224 + 576 = 125\,081\,664.$$Росток-2 больше Ростка-1 в 7,2 раза, а его блоки — в 7,5 раза: рост ушёл в те части, которые думают. Одни только сети SwiGLU держат 79,6 миллиона чисел — 64% модели. Таблица эмбеддингов выросла вчетверо по числу строк и в полтора раза по ширине, до 18,9 миллиона чисел, но её доля упала с 18% до 15%. И это по-прежнему одна таблица на входе и на выходе (глава 8): без этого трюка модели пришлось бы нести ещё 18,9 миллиона.
Две строки конфигурации не стоят ни одного параметра. Окно — 2 048 токенов, вчетверо больше, чем у Ростка-1, а основание RoPE — 100 000 вместо 10 000. Большее основание замедляет медленные повороты RoPE (глава 8) — самый медленный почти вдесятеро, — чтобы далёкие друг от друга позиции длинного окна по-прежнему различались: обычная поправка, когда окно растёт.
Глубокий и узкий
Почему 30 блоков по 576, а не, скажем, 16 по 768 — там почти столько же параметров? Переключайте ширину в нижней части виджета.
Широкая модель тратит больше бюджета на словарь: при ширине 1 152 таблица эмбеддингов съедает 31% модели, и блоков остаётся всего 6. Глубокая модель даёт каждому токену больше шагов обработки, один за другим, а почему шаги важны, мы видели в главе 8: индукционной голове нужен слой раньше неё, который подготовит то, что она прочитает. У глубины есть и цена: тридцать слоёв работают строго по очереди, а видеокарте удобнее несколько широких, и KV-кэш из следующего раздела растёт с каждым слоем.
Вот тот же подсчёт в виде функции, которую можно запустить, — с третьей моделью: Ростком-2 без группового внимания.
Будь у Ростка-2 столько же голов ключей и значений, сколько голов запросов, он весил бы 138,4 миллиона: на 13,3 миллиона больше, и все они — в $W_K$ и $W_V$. Это меньшая из двух экономий. Бо́льшая видна, только когда модель пишет.
Ключей меньше, чем запросов
В главе 11 появился KV-кэш: пока модель пишет, она хранит ключ и значение каждого прошлого токена в каждом слое, и новый токен обходится одним проходом. Платить за это приходится памятью, и её легко посчитать. На каждый токен модель хранит
$$2 \times \text{слоёв} \times \text{голов ключей и значений} \times \text{размер головы}$$чисел: ключ и значение в каждом слое для каждой головы, у которой свои ключи и значения. У Ростка-1 это $2 \times 8 \times 6 \times 64 = 6\,144$ числа. Дайте Ростку-2 столько же голов ключей и значений, сколько голов запросов, — и получится $2 \times 30 \times 9 \times 64 = 34\,560$ чисел на токен: 69 КБ в 16-битных числах и 141,6 МБ на полное окно в 2 048 токенов, почти вдвое больше 78 МБ его 4-битных весов.
Групповое внимание (grouped-query attention, GQA) делит ключи и значения на несколько голов. 9 голов запросов Ростка-2 разбиты на 3 группы по три, и каждая группа читает один общий набор ключей и значений. Каждая голова по-прежнему задаёт свой вопрос — у неё своя часть $W_Q$; общими на три головы группы становятся только ярлыки, с которыми вопрос сравнивают, и содержимое, которое смешивают (глава 7). Кэш сжимается втрое, до 11 520 чисел на токен.
В браузере движок Ростка-2 хранит ключи и значения 32-битными числами, так что полное окно в 2 048 токенов занимает 94,4 МБ — больше, чем сами веса в 4 битах. С 9 головами ключей и значений было бы 283 МБ. Под кэши движок отводит не больше 256 МБ: с 3 головами туда помещаются два полных разговора, с 9 — ни одного. К тому же веса загружаются один раз, а кэш нужен каждому разговору свой, поэтому серверы, которые говорят с тысячами людей сразу, считают кэш ещё придирчивее, чем телефон.
В model.py вся идея — несколько строк класса Attention:
По сравнению с Ростком-1 общая матрица qkv исчезла: у k и v теперь свои, более узкие слои — 192 выхода вместо 576. Посмотрите на порядок строк: кэш забирает ключи и значения до того, как repeat_interleave скопирует каждый для трёх его голов запросов, поэтому хранятся только три головы. После копирования всё остальное — обычное внимание из главы 7, а на видеокарте FlexAttention обходится даже без копий (enable_gqa=True).
У Ростка-2 9 голов запросов и 3 головы ключей и значений. Что изменится, если голов ключей и значений станет 9?
Кэш хранит ключи и значения, но не запросы: запрос нужен один раз — тому токену, который его задаёт. Окно в любом случае остаётся в 2 048 токенов. Растёт память на токен — с 11 520 до 34 560 чисел — и матрицы $W_K$ и $W_V$ в каждом блоке.
Что нужно большой модели
Кое-что, что сходит с рук маленькой модели, перестаёт быть безобидным, когда обучение идёт миллиарды токенов, а кое-какая защита почти ничего не стоит. Росток-2 добавляет две такие защиты и сохраняет одну от Ростка-1.
Одна строка, несколько документов
Обучение читает строки по 1 024 токена (в конце — по 2 048). Документы такого размера не бывают: средняя задача TinyGSM — 216 токенов, сказка TinyStories — 196, страница FineWeb-Edu — 1 070. Поэтому prepare.py записывает каждый источник одним длинным потоком токенов, где каждый документ открывается меткой <|endoftext|>, а train.py вырезает из потока строки там, где придётся. Обычно строка начинается посреди одного документа и захватывает начала следующих.
С одной причинной маской (глава 7) каждый токен может смотреть на всё, что было раньше в строке, — в том числе на документы, которые к нему не имеют никакого отношения. Вот одна такая строка: конец сказки, команда shell и начало веб-страницы.
Включите «Причинная маска» и нажмите на «·ls»: из 9 токенов, на которые он может смотреть, 6 — конец сказки. Не обращать внимания на такой шум модель научиться может, но это стоит усилий, а короткие документы делают дело хуже: в строке из 1 024 токенов задач TinyGSM около 80% пар, которые разрешает причинная маска, смотрят в чужую задачу.
Лечится это так: пронумеровать документы и пропускать внимание только внутри одного. train.py нумерует их одной строкой, (x == eot).cumsum(1), — бегущим счётом меток <|endoftext|>, так что каждая метка открывает новый документ; model.py превращает номера в маску (код — в конце раздела). На маленьких тестовых моделях прогон без маски закончился на 3,367 ната, с маской — на 3,342: лучше на 0,024, примерно как хорошая скорость обучения лучше вдвое завышенной. И стоит маска ничего: с torch.compile на Mac она не изменила скорость, а на видеокарте FlexAttention пропускает куски матрицы, закрытые целиком.
Потолок для логитов
На самом верху модели 32 768 логитов идут в softmax (глава 2). Ничто не ограничивает их величину, а у обучения есть причина их раздувать: больший логит у лёгкого токена — это более уверенная модель и меньшая ошибка. Убежавший логит делает модель самоуверенной, и тогда одна уверенная ошибка обходится огромной потерей. Росток-2 ставит над каждым логитом мягкий потолок:
$$\ell' = 30 \tanh\!\left(\frac{\ell}{30}\right).$$Около нуля тангенс почти прямая: 5 превращается в 4,95, 10 — в 9,65, так что обычные логиты проходят почти нетронутыми. После 30 кривая загибается: 60 становится 28,9, 100 — 29,9, и ни один логит никогда не выходит из полосы от −30 до 30. Отсюда два следствия. Наклон, $1 - \tanh^2(\ell/30)$, равен 0,42 при 30 и 0,07 при 60: чем дальше убежал логит, тем слабее обучение может его толкать, и смысла раздувать логиты больше нет. А цена любого отдельного токена ограничена: даже самая уверенная ошибка не может стоить больше $2 \cdot 30 + \ln 32\,768 \approx 70{,}4$ ната.
На маленьких тестовых моделях потолок немного помог: 3,351 без него и 3,342 с ним. На первом замере модель без потолка даже чуть опережала, а окупился потолок к концу. Движок в вашем браузере применяет ту же функцию к каждому логиту, так что говорит там Росток-2 ровно так, как его учили.
QK-norm остаётся
Второе место, где числа могут убежать, — само внимание: $q \cdot k$ растёт вместе с длиной векторов. Росток-1 уже нормировал каждый запрос и ключ перед сравнением (QK-norm, глава 7), и Росток-2 это сохраняет. Вместе QK-norm и потолок охраняют оба softmax модели: тот, что внутри внимания, и тот, что на выходе. Вот маска и потолок в model.py:
Маска — ровно матрица из виджета: причинный треугольник (tril) И «тот же номер документа». Потолок — одна строка после выходного слоя, перед функцией потерь.
Большая модель получает ремни безопасности до долгого обучения: внимание не выходит за пределы своего документа, и ни одна оценка — ни внутри внимания, ни на выходе — не может убежать.
Репетиции на маленьких копиях
Откуда известно, что маска даёт 0,024, а 0,02 — правильная скорость для Muon, ещё до долгого обучения? Не у большой модели же спрашивать. Строят её маленькую копию — прокси — и меняют по одной вещи за раз.
У прокси Ростка-2 8 блоков по 256, 4 головы запросов и 2 головы ключей и значений: 14,3 миллиона параметров, почти 60% из них — в таблице эмбеддингов, у которой по-прежнему 32 768 строк. Каждая прокси прочитала 100 миллионов токенов из уменьшенной копии корпуса в 1,4 миллиарда токенов — около часа на Mac; всё остальное — рецепт Ростка-2 в уменьшенном виде. Вот все кривые.
Muon со скоростью 0,02 заканчивает на 3,342 — лучше, чем с 0,01 (3,353) и 0,04 (3,368). Для таблицы эмбеддингов, которую учит AdamW, 0,006 лучше и 0,003 (3,361), и 0,012 (3,347). Маска документов даёт 0,024, потолок — 0,008. Теперь переключитесь на «Сама ошибка»: кривые почти лежат друг на друге. Каждое решение здесь — это сотые и тысячные доли ната на кривой, которая падает с 4,8 до 3,3. В начале разрывы больше (на первом замере Muon со скоростью 0,04 отстаёт на 0,18) и потом в основном сокращаются; считается конец. Резкий спад после 80 миллионов токенов — собственный спуск прокси.
Две оговорки. Каждый вариант обучен один раз, так что мы не знаем, насколько сдвинуло бы результат другое случайное зерно: тысячным стоит доверять меньше, чем сотым. И прокси в девять раз меньше Ростка-2 и читает в девяносто раз меньше, так что лучшие для неё значения не обязаны быть лучшими для большой модели. Росток-2 просто их взял.
Расписание
В главе 10 расписание состояло из трёх частей: разогрев, плато, спуск. Росток-2 сохраняет форму и меняет пропорции:
- Разогрев — 250 шагов, 131 миллион токенов: скорость обучения растёт от почти нуля до пика.
- Плато — пиковая скорость до шага 13 732.
- Спуск — последние 20%: 3 434 шага и 1,8 миллиарда токенов, за которые скорость линейно падает до нуля. Меняется и смесь (глава 14): больше кода, вопросов с ответами и математики, меньше веба и 3% разговоров в формате чата.
Строки по дороге удлиняются: 256 токенов первые 2% шагов, 512 до отметки 6%, 1 024 бо́льшую часть обучения и 2 048 только в последние 10%. Батч всё время — 524 288 токенов, так что в начале это 2 048 строк по 256, а в конце — 256 строк по 2 048. Длинные строки приберегают на конец, потому что цена внимания растёт с квадратом длины (глава 7) и потому что длинное прошлое полезнее всего модели, которая уже хорошо пишет. Вот две функции из train.py, которые всё это задают:
Для 17 166 шагов они дают строки по 256 токенов до шага 343, по 512 — до шага 1 029, по 1 024 — до шага 15 449 и по 2 048 — с шага 15 450; спуск начинается на шаге 13 732. Версия context_at у Ростка-1 удлиняла окно только в начале, а у Ростка-2 ещё и приберегает самые длинные строки на самый конец.
Контрольная точка перед спуском
На шаге 13 732, прямо перед тем как скорость начнёт падать, train.py сохраняет полную контрольную точку, ckpt_pre_cooldown.pt. В этом и состоит практический подарок расписания «разогрев, плато, спуск» (глава 10): в этот момент модель находится посреди обучения, а не в его конце. Если позже появятся новые данные, обучение можно продолжить с этой точки на полной скорости, как будто спуска не было, и снова остудить модель уже в новом конце. Готовая модель — не тупик.
Что лежит в полной контрольной точке
Итоговой точке достаточно весов. Точка перед спуском хранит ещё состояние обоих оптимизаторов (импульс Muon, скользящие средние AdamW), место загрузчика в каждом источнике (какой кусок в миллион токенов, в каком перемешанном порядке, как далеко внутри) и счётчики шагов и токенов. С --resume всё это возвращается, и продолжение читает ровно те токены, которые прочитало бы следующими; заново выставляются только скорости обучения, так что у продолжения могут быть свои.
Обучение
Сколько это работы? Обучение стоит примерно шесть операций на параметр на токен, $6ND$ (глава 9): две на прямой проход и четыре на обратный. Для Ростка-1 это $6 \times 17{,}3 \cdot 10^6 \times 330 \cdot 10^6 \approx 3{,}4 \cdot 10^{16}$, для Ростка-2 — $6 \times 125{,}1 \cdot 10^6 \times 9{,}0 \cdot 10^9 \approx 6{,}75 \cdot 10^{18}$, примерно в 200 раз больше. Поэтому Росток-2 не мог вырасти на ноутбуке, как Росток-1: он учился на одной видеокарте для дата-центров класса H100, около 9,5 часа.
Каждые 500 шагов train.py мерил ошибку на отложенном тексте из каждого из 31 источника и из разговоров. Вот всё обучение целиком.
Что говорят кривые:
- Каждая группа улучшается до самого конца. Веб-текст — с 3,75 на шаге 500 до 2,94, код — с 2,26 до 1,47, математика — с 1,98 до 1,35. Кривые выполаживаются, но продолжают падать: 9 миллиардов токенов, 72 на параметр, не исчерпали того, чему может научиться модель такого размера.
- Разговоры ждут спуска. Сначала их ошибка падает вместе с обычным английским, а потом стоит около 2,6. На шаге 13 732 смесь спуска приносит данные чата; к шагу 14 000 ошибка уже 1,59, а в конце — 1,43. В этих 268 шагах было около 4,2 миллиона токенов разговоров: слова модели были не в новинку, новым был формат.
- Ошибка на обучении падает скачком в начале спуска: примерно с 2,51 до 2,23 за сотню шагов, когда скорость ещё на 98% от пика. Это не модель оседает на дно: сменилась смесь, а код, математику и чаты предсказывать легче, чем веб-страницы. Чёрная линия — один неизменный экзамен — почти не двигается: 2,581 на шаге 13 500, 2,578 на шаге 14 000 и 2,505 в конце спуска.
- Включите «val_mix из журнала». Это число печатает
train.py, и оно падает с 2,58 на шаге 13 500 до 2,30 на шаге 14 000.val_mixвзвешивает источники по текущей смеси, так что на шаге 13 732 поменялся сам экзамен: лёгкие предметы получили больший вес, а в зачёт пошли и разговоры. - Длинные строки немного помогают. Когда на шаге 15 450 строки вырастают до 2 048 токенов, ошибка на обучении снова делает шаг вниз — с 2,21 до 2,12, если усреднить по тысяче шагов с каждой стороны: с длинным прошлым есть на что опереться, как в главе 10 было и у Ростка-1.
Теперь переключитесь на «По источникам». В конце задачи TinyGSM стоят 0,64 ната на токен, а веб-страницы DCLM — 3,37. Математику Росток-2 выучил не впятеро лучше английского: задача TinyGSM — короткая текстовая задача с решением на Python в одном неизменном стиле, как ещё два миллиона таких же, а веб-страница может быть о чём угодно. Ошибка измеряет не только то, насколько хорошо текст выучен, но и то, насколько он вообще предсказуем, так что сравнивать ошибки можно только на одном и том же тексте. Даже 1,34 на TinyStories с 1,554 Ростка-1 (глава 10) сравнивать нельзя: модели режут текст на разные токены, а ошибка на токен зависит от того, что такое токен.
Между шагами 13 500 и 14 000 val_mix, который пишет в журнал train.py, упал с 2,58 до 2,30. В чём главная причина?
На одном неизменном экзамене ошибка за эти шаги сдвинулась только с 2,581 до 2,578. Остальное падение — смена весов: код и математика, у которых ошибка низкая, в смеси спуска весят больше, веб-страницы — меньше, а разговоры, которые модель схватывает быстро, вообще начинают учитываться. До 2 048 токенов строки вырастают только на шаге 15 450.
Ошибка, упавшая за ночь, — это вопрос, а не ответ: сначала проверьте, не поменялись ли текст или экзамен.
Базовая модель
После 17 166 шагов у нас есть базовая модель — модель, которая только и умеет, что продолжать текст. Много ли она знает? Базовую модель просят выбирать, а не писать: к каждому вопросу теста прилагается несколько вариантов окончания, и eval.py выбирает то, которое модель считает самым вероятным после вопроса. acc сравнивает простые суммы логарифмов вероятностей, acc_norm делит каждую на длину окончания в символах, чтобы длинные окончания не проигрывали только из-за длины. Использованы все задания каждого теста.
| Тест | Наугад | acc | acc_norm |
|---|---|---|---|
| SciQ вопрос по естествознанию после поясняющего абзаца | 0,25 | 0,799 | 0,746 |
| ARC-Easy школьные вопросы по естествознанию | 0,25 | 0,506 | 0,459 |
| ARC-Challenge вопросы, на которых ошибаются простые методы | 0,25 | 0,219 | 0,250 |
| HellaSwag самое правдоподобное продолжение бытовой сцены | 0,25 | 0,305 | 0,341 |
| PIQA какой из двух способов сделать что-то руками сработает | 0,5 | 0,645 | 0,638 |
| WinoGrande к кому или чему относится местоимение | 0,5 | 0,520 | 0,520 |
| OpenBookQA применить научный факт к новой ситуации | 0,25 | 0,180 | 0,304 |
| BoolQ да или нет по абзацу текста | 0,5 | 0,579 | 0,619 |
Читать таблицу надо рядом со столбцом «наугад». SciQ, ARC-Easy, PIQA и HellaSwag явно выше него: с абзацем перед глазами Росток-2 выбирает правильный ответ по естествознанию четыре раза из пяти, и какое-то бытовое чутьё на то, что за чем следует, у него есть. ARC-Challenge и WinoGrande — на уровне случайного: вопросы, специально собранные так, чтобы не помогали словесные ассоциации, и местоимения, для которых нужно настоящее понимание, ему не по силам. BoolQ выглядит выше случайного, но это не так: 2 033 из 3 270 ответов в нём — «да», так что, всегда отвечая «да», можно набрать 0,622.
Писать труднее, чем выбирать. Текстовые задачи GSM8K, записанные как начало функции на Python в стиле TinyGSM и проверенные запуском программы, которую он напишет, Росток-2 решает в 21,5% случаев — это стиль TinyGSM из его математической смеси. В MBPP и HumanEval, где нужна целая функция, проходящая тесты, у него 1,0% и 2,4%. При жадном декодировании он сваливается в петли: пишет один и тот же import двадцать раз или копирует функцию, которую только что написал. Сам проверочный стенд проверили на эталонных решениях: прошли 40 из 40.
А вопрос из начала главы? На «How do I list all files in a folder, including hidden ones?» готовая модель пишет: «Here is a list of all files that are hidden from view. How do I get the list of hidden files (with 0 size, 0 characters) when the folder is listed in the list in the admin console?» Она не отвечает, а продолжает ветку форума: в интернете за таким вопросом обычно следуют новые вопросы.
Базовая модель продолжает текст. Она очень многое знает о том, как выглядят тексты, и отвечает, только когда ответ — самое вероятное продолжение.
Росток сейчас
Вот Росток-2: готовая базовая модель с верхней ступеньки лесенки. Она отвечает с нашего сервера — там тот же движок, что считает остальные модели курса у вас в браузере, и тот же файл: 4 бита на вес, 78 МБ вместо 250 МБ, которые веса занимают в 16 битах, ценой 0,026 ната ошибки. Скачивать ничего не нужно, и ничего не сохраняется. Попробуйте пять затравок из начала главы: с новыми кубиками они каждый раз выходят по-другому. Росток-2 пишет сказки, объяснения, Python и сеансы shell — и по-прежнему превращает любой вопрос в ветку форума: он продолжает текст, а не отвечает. В следующей главе он научится разговаривать, думать перед ответом и запускать Python, когда арифметика становится трудной.