Это статья о двух месяцах исследовательской работы над одним вопросом: может ли языковая модель 3B накапливать компетентность не за счёт роста весов. Здесь есть числа, код и баги. Все результаты воспроизводимы: сиды, хеши и протоколы заморожены до прогонов.
Доставка верифицированного решения похожей задачи в контекст 3B-модели даёт +30 п.п. к решаемости (53% → 83%), повторено на 3 сидах
Дообучение на верифицированных решениях (через маленькие адаптеры LoRA, один цикл) даёт эффект +0.145: прирост на новых задачах минус изменение на старых (95-процентная вилка значений: от +0.0575 до +0.250). Эффект есть, но это одна точка, а не кривая
Сейчас идёт эксперимент: 4 цикла переучивания × 2 траектории × 200 задач. Первый замер формы кривой (рост / плато / затухание)
Попутно: почему нейросеть тормозит, когда видеопамять кончается, и как мы поймали ошибку «не-число» в метрике удивлённости на 100% попыток
Постановка
Мейнстрим растит модели масштабом. Мы меряем противоположное: можно ли растить компетентность маленькой модели без роста весов, через верифицированную память и локальное обучение, с жёстким потолком ресурсов (бытовая GPU 12 GB).
Ключевое различие, вокруг которого построены все эксперименты:
доставка знаний: решение похожей задачи кладётся в контекст, модель его потребляет;
интернализация: модель учится на решениях так, чтобы решать без доставки;
рост через повторение: несколько циклов обучения на одном корпусе (в литературе это называется кривой обучения).
Это три разных явления, и они требуют трёх разных измерений.
Методология, которая спасала нас чаще, чем идеи
Каждый эксперимент фиксируется до запуска: критерии, сиды, пулы задач, файл замораживается хешем. После идёт независимый пересчёт из сырых логов кодом, который не писал автор эксперимента.
Это не бюрократия. Два примера, где это окупилось конкретно:
Баг «слепого к веткам резюме». Ключ возобновления прогона не включал имя экспериментальной группы: рестарт прогона молча пропускал контрольные группы как «уже посчитанные». Мы потеряли бы контрольные данные целого эксперимента и узнали бы об этом через сутки GPU. Поймал CPU-тест полного цикла на крошечной модели за 13 секунд.
«Не-число» в метрике удивлённости на 100% попыток. На игрушечной модели в тестах энтропия считалась идеально. На живой 3B, «не-число» на каждой попытке: модель изредка выдаёт бесконечные значения, и формула энтропии на них даёт «ноль умножить на минус бесконечность». Классика, но мы её поймали не глазами, а анализатором, который считает нечисловые значения громко.
Теперь эти проверки постоянные: монитор, который ничего не изменяет, перечитывает живой лог каждые 15 минут.
Методологическая дисциплина (то, что останется при любом исходе)
Верификация-центричность. Надёжность ответа определяется свойствами верификатора, а не генеративной политикой. Первый полигон: синтетическое программирование: компиляция → тесты → интеграция → регресс. «Мы не знаем» тоже допустимый ответ.
Предрегистрация и заморозка. Критерии приёма, сиды, пулы задач фиксируются с хешами ДО прогона. Интерпретация по замороженной таблице, а не задним числом. Реестр того, где прошлые выводы оказались НЕВЕРНЫМИ, ведётся и не переписывается задним числом.
Разделение трёх явлений. «Сработала память» ≠ «система научилась» ≠ «изменились веса». Каждый замер спроектирован так, чтобы различать хотя бы два из трёх.
Самоулучшение без права испортить. Обучение и память не имеют права портить подтверждённую компетентность: транзакция «подготовить → проверить → принять или откатить», у каждого состояния контрольная сумма и история происхождения.
Два бага, которые окупили всю дисциплину
Ключ возобновления, слепой к веткам. Ключ рестарта прогона не включал имя экспериментальной группы, рестарт молча пропускал контрольные группы как «посчитанные». Контрольные данные целого эксперимента были бы потеряны; поймал CPU-тест полного цикла на крошечной модели (13 секунд) до расходования GPU-часов.
«Не-число» в метрике удивлённости на 100% попыток. На игрушечной модели в тестах энтропия считалась идеально. На живой 3B модель изредка выдаёт бесконечные значения, и формула энтропии на них даёт «ноль умножить на минус бесконечность». Поймал не глазами, а анализатором, который считает нечисловые значения громко. Теперь монитор, который ничего не изменяет, перечитывает живой лог каждые 15 минут.
Фрагмент скорера наклона (метод Тейла-Сена: берём все пары точек кривой, считаем наклон линии между каждой парой и берём средний. Если один цикл прогона выдал мусор, он портит не всю оценку, а только свою пару):
def theil_sen(points): pts = sorted(points) slopes = [(y2 - y1) / (x2 - x1) for i, (x1, y1) in enumerate(pts) for (x2, y2) in pts[i+1:]] slopes.sort() m = len(slopes) return slopes[m // 2] if m % 2 else (slopes[m//2 - 1] + slopes[m//2]) / 2
И транзакционная запись в память: подготовить → проверить → принять или откатить (отказ записи не портит состояние):
def submit_episode(self, episode, verifier): vr = verifier(episode) if not vr.ok: # невалидное не пишется self._journal(rec | {“decision”: “reject-unverified”}) return rec entry = self.op.prepare_write(working, episode) if not self.op.gate(working, episode, signal): # «фильтр удивлённости» return rec # отложено: записано в журнал self.snapshot = self._commit(entry) # новая версия состояния
Что тут происходит: попытка записи сначала проходит проверку. Не прошла, отказ записывается в журнал, и состояние не трогается. Прошла проверку, но сработал «фильтр удивлённости» (мы ещё не решили, по какому сигналу его включать, поэтому сейчас он выключен), запись откладывается. Всё остальное, новая версия состояния с контрольной суммой, к которой всегда можно откатиться.
Замер 1: доставка знаний работает, и дистанция решает
Архитектура замера: 200 свежих задач одного семейства, никогда не виденных моделью. Для каждой, верифицированное решение соседней задачи: близкой (много общих действий) и дальней (почти без общих). Модель решает задачу в 4 попытках, успех, прохождение полного набора тестов в песочнице.
Результаты (40 задач, сравнение пар «было/стало», с поправкой на то, что сравнений несколько):
Группа | Решаемость | Разница |
|---|---|---|
базовая | 50% | нет |
+ решение близкой задачи | 80% | +30 п.п. (p=0.004) |
+ решение дальней задачи | 45% | разница статистически незначима |
+ готовый ответ этой же задачи | 100% | потолок |
Затем воспроизведение на 3 настройках генератора задач: в среднем +18.3 п.п. (p=0.0003). Форма подтверждена на промежуточной дистанции (45% → 67.5% → 80%).
Вывод: перенос знания через контекст работает и критически зависит от дистанции. Это канал доставки, он дешевле и предсказуемее обучения и предсказуемее обучения.
Замер 2: обучение в весах, один цикл
Дообучение через маленькие адаптеры LoRA (40 шагов) на 80 верифицированных решениях, затем экзамен без доставки:
экзамен: 52.75% → 57.25% (+4.5 п.п., парный статистический тест p=0.011)
регрессия на старых задачах: −10 п.п. (улучшение, не деградация)
итоговый показатель: прирост на новых задачах минус изменение на старых = +0.145 (95-процентная вилка значений: от +0.0575 до +0.250)
Это эффект одного цикла. Он ничего не говорит о росте: положительная точка не отличает рост от плато, и мы прямо пишем: одного цикла мало, чтобы заявлять самообучение.
Замер 3 (идёт): форма кривой роста
Дизайн: 4 цикла переучивания на фиксированном корпусе × 2 траектории × 3 группы (копит веса / стартует с нуля каждый цикл / вообще не учится). Экзамен 200 задач, фиксированный, идентичный между циклами. Первичная метрика: наклон экзаменационной траектории (метод Тейла-Сена, описан выше), с доверительным интервалом, посчитанным на случайных подвыборках траекторий.
Промежуточно (данные ещё неполные, без выводов): траектория 0: 53% → 59% → 55.5% → 54% → 57.5%; вторая траектория повторяет форму. Похоже на «скачок первого цикла → плато», но финальный вердикт по полному набору данных, а не на глаз.
Инфраструктура, которая осталась
независимая проверка целостности прогона: 17 типов проблем (дубликаты, правильность сидов, смешение групп, чекпоинты, «не-числа» в данных), тестами, которые специально ломают данные и смотрят, заметит ли проверка, доказано, что ловит все 15 классов порчи
пересчёт всех исторических результатов из сырых данных: 51/51 проверок сошлись
транзакционный слой памяти: запись → проверка → принять или откатить, у каждого состояния контрольная сумма и история происхождения (7 контрактов как исполняемые тесты)
Что не работает / что не знаем
Обучаемый интерфейс без доставки: +10 п.п., разница статистически незначима, эффект не подтверждён
Доставка на другие семейства задач: не измерена (мощности не хватает, нужен дизайн)
Растут ли знания в весах при повторении: решает текущий прогон
Сколько циклов нужно для «настоящего роста» не определено, ждёт данных
Репозиторий и код
Протоколы, сырые данные и анализаторы открыты: экспериментальные скрипты, проверка целостности, независимый пересчёт всех результатов, тесты (286 штук). Задаю вопросы в комментариях, особенно интересен опыт людей, которые меряли непрерывное обучение (continual learning) на малых моделях.
Вопросы к сообществу (то, ради чего постим)
Непрерывное обучение на малых моделях. Кто мерял форму кривой при многократном дообучении на фиксированном наборе? Наш ранний паттерн, «скачок первого цикла → плато», это у вас тоже было, или мы видим артефакт протокола?
Подсказка против дообучения. Есть ли у кого-то строгий протокол разделения «сработала память» / «система научилась» / «изменились веса»? Мы делаем это через три группы сравнения (одна копит знания от цикла к циклу, одна стартует с нуля каждый цикл, одна не учится) — видим ли вы дыры?
Проверка как единственный источник правды. Мы проверяем только программные задачи (песочница + тесты + контроль подменой решений). Как вы решаете задачу проверки для областей, где правильный ответ автоматически не проверить?
Отрицательные результаты. Как вы их публикуете и структурируете, чтобы их вообще читали? Наш реестр неверных выводов — половина ценности проекта, но честный ноль в статье всегда выглядит слабее, чем цифра.
Любая критика методологии самый ценный комментарий, который мы можем получить.

