Как я уместил «Войну и мир» в 10 ГБ видеопамяти, или почему нейросеть убивает героев и выдумывает Пьера Бездаровского

Как я уместил «Войну и мир» в 10 ГБ видеопамяти, или почему нейросеть убивает героев и выдумывает Пьера Бездаровского

Я — обычный школьник, только начинающий изучать машинное обучение. Недавно на уроках литературы задали прочитать первые два тома «Войны и мира». Проблема в том, что в интернете почти нет качественных кратких содержаний по главам — везде либо «вода», либо пропущены важные детали. Раз уж я учу ML, я решил: пусть Толстого читает нейросеть, а я — её отчёты. Задача казалась простой: скормить текст LLM и получить саммари. Но тут вмешалась реальность — у меня всего 10 ГБ видеопамяти на RTX 3080. В этой статье — как я пытался впихнуть невпихуемое с помощью 4-битного квантования, почему ИИ пытался убить Николая Ростова и как токенизация породила нового героя русской классики — Пьера Бездаровского.

Архитектура

После тестов наиболее стабильной оказалась модель IlyaGusev/saiga_llama3_8b с 4-битным квантованием. Из-за аппаратных ограничений я подавал текст по главам, обрезая по 7500 символов. Это значение было подобрано эмпирически.

Модель Qwen2.5-7B-Instruct при генерации выдавала не только краткое содержание, но и длинные фрагменты кода на Python. Чтобы избежать этого, я добавил в системный промт обработку краевых случаев. Также парсер обрезал текст не по символам, а по словам — чтобы незавершённое слово не сломало токенизацию.

Я пробовал использовать скользящее окно: модель должна была суммаризировать уже сгенерированные саммари. Но это привело к эффекту «сломанного телефона» и увеличению времени обработки. От идеи пришлось отказаться.

Для реализации использовались библиотеки transformers и bitsandbytes. Оценку результатов проводил с помощью Gemini 3 Flash.

Часть 1: Обычный промт

Я начал с базового промта. Уже на ранних этапах модель начала путаться в именах и родственных связях.

Главы 12–14: Критическая ошибка. Текст утверждает, что Пьер — сын графа Ростова и живёт у него. На самом деле Пьер — сын графа Безухова. Илья Ростов назван «графом Безуховым». Это полностью ломает логику наследования.

Князь Василий Курагин назван «отцом Пьера». Это фактическая катастрофа: весь смысл интриги Василия — в том, что он дальний родственник, пытающийся отобрать деньги у Пьера.

Часть 2: База знаний

Чтобы уменьшить путаницу, я добавил в системный промт базу знаний с ключевыми персонажами и их связями. Однако модель начала выдавать новые ошибки — уже в сюжетных деталях.

Глава 21: Критическая ошибка. Написано, что Николай Ростов «умирает от ран». Это ложь: Николай — один из главных героев, он доживает до эпилога.

Глава 5: Критическая ошибка. Написано, что княжна Марья «соглашается на предложение Анатоля». В оригинале она категорически отказывает ему.

Часть 3: Расследование смерти Николая Ростова

У меня появилась гипотеза. Толстой часто описывает, как герой едва не умирает, но в последний момент спасается. Для модели это — 2000–3000 токенов, посвящённых смерти, и всего несколько — о выживании. Поэтому вероятность сгенерировать «умер» оказывается выше.

С княжной Марьей и Анатолем ситуация похожа. Нейросеть обучалась на фанфиках и женских романах. Для неё комбинация токенов «Анатоль» + «Наташа» + «письмо» почти всегда означает свадьбу. Толстой же ломает эти шаблоны.

Чтобы проверить гипотезу, я заглянул под капот и проанализировал распределение вероятностей токенов «жизни» и «смерти» в начале генерации.

Я использовал три промта и подавал на вход отрывок из главы, где Ростов ранен, но жив. Оценка Gemini 3.1 Pro:

Основываясь только на этом отрывке, герой (Ростов) не умер.

Я заметил, что сумма вероятностей не равна 100%. Причина оказалась в том, что модель не отвечает одним словом, а начинает с «герой...». Чтобы это обойти, я добавил догенерацию и смотрел, что будет дальше.

Оказалось, что модель стремится ответить «герой Николай...» — захардкоженный шаблон промта въелся в её поведение. Я обновил список токенов «жизни».

Результат оказался странным: вероятности «жизни» и «смерти» выровнялись до 50/50. Я узнал, что это называется максимальной энтропией. Промт настолько сильным образом сместил распределение, что уравновесил контекстное окно из 6500 символов.

Как получился такой идеальный баланс — я до конца не понимаю. Буду рад мнению экспертов.

В итоге я добавил в системный промт хардкод и обработку краевых случаев, а также подобрал оптимальные настройки генерации.

Часть 4: Рождение Пьера Бездаровского

Затем я столкнулся с ещё одной странной ошибкой:

Глава 21: (Галлюцинация). Пьер назван «Пьером Бездаровским». Это Пьер Безухов. Суть главы (попытка самоубийства Наташи и разговор с Андреем) передана верно.

Моя теория проста. В этой главе Пьер называет себя «бездарным». При генерации фамилии модель взяла корень [Без] из «Безухов», затем [дар] из «бездарный», а в конце добавила типичное окончание [овский].

Наглядно:

Безухов → [Без], [ухов]

Бездарный → [Без], [дар], [ный]

Такие ошибки редки, поэтому проще исправлять их на постпродакшене, чем усложнять промт-инжиниринг.

Финальная оценка от Gemini 3 Flash

  • Средняя точность фактов: ~93%
  • Главная проблема: путаница в фамилиях (Андрей Безухов, Пьер Бездаровский)
  • Критические сбои: 2 случая (например, попытка женить Андрея не на той)

Том 1, Часть 1 (Петербург и Москва)

  • Глава 1–4: 95%. Точно описан салон Шерер. Нюанс: Андрей Болконский назван «виконтом» — ошибка, это другой гость.
  • Глава 5: 50% (грубая ошибка). Пьер не рассказывает о желании стать масоном — это произойдёт позже. Здесь он спорит о Наполеоне.
  • Глава 7: 90%. Пьер — сын Кирилла Владимировича Безухова, а не Ростова (в тексте возможна двусмысленность).
  • Глава 12: 70% (ошибка). У графа Безухова не было дочерей — это его племянницы (три княжны).
  • Глава 18–19: 60% (техническая ошибка). Глава 19 обрывается на полуслове («княжна Мария и кня...»). Суть ясна, но текст неполон.

Том 1, Часть 2 (Война 1805 года)

  • Глава 16–21: 95%. Шенграбенское сражение, подвиг батареи Тушина, ранение Ростова.

Том 1, Часть 3 (Сватовство и Аустерлиц)

  • Глава 15–19: 98%. Сражение, подвиг Андрея со знаменем, его ранение и «высокое небо». Наполеон у тела Андрея.

Том 2, Часть 1 (Возвращение и дуэль)

  • Глава 12–16: 95%. Танцы у Иогеля. Николай Ростов проигрывает 43 тысячи Долохову. Раскаяние Николая.

Том 2, Часть 2 (Масонство)

  • Глава 18–21: 95%. Тильзитский мир. Николай Ростов видит Наполеона и Александра вместе, его внутренний кризис.

Том 2, Часть 3 (Сперанский и помолвка)

  • Глава 1: 30% (грубейшая ошибка). Главный герой назван «князь Андрей Безухов». Это Андрей Болконский. Фамилия перепутана, хотя сюжет про дуб описан верно.

Том 2, Часть 5 (Наташа и Анатоль)

  • Глава 21: 60% (галлюцинация). Пьер назван «Пьером Бездаровским». Это Пьер Безухов. Суть главы передана верно.

Заключение

В итоге 230 000 слов из двух томов превратились в 18 000 слов плотной выжимки. Для меня это был невероятно интересный и первый действительно рабочий проект. Я не стал описывать все провалы и ложные гипотезы — надеюсь, вам было интересно.

А какие странные слияния токенов встречали вы?

Читать оригинал