Переобучение: когда модель зубрит

Иногда модель показывает идеальный результат на тренировке и проваливается на реальных данных — это и есть переобучение.
Переобучение — ситуация, когда модель зазубрила обучающие примеры и плохо работает на новых данных.

Хук: пятёрка на репетиции и двойка на экзамене

Представь друга, который готовится к экзамену по истории. Вместо того чтобы понять, почему началась война, он просто заучил наизусть три варианта билетов, которые ему дали для тренировки. На пробном тесте — где вопросы ровно те же — он отвечает идеально, без единой запинки. Все восхищаются: «Вот это голова!»

Знакомо, да? У каждого был такой одноклассник. И, честно говоря, почти каждый хоть раз сам так делал — накануне контрольной зубрил готовые решения, надеясь, что попадётся ровно то же. Иногда везёт. Но как только задачу чуть-чуть меняют формулировкой, вся «подготовка» рассыпается, потому что за заученными буквами не было понимания.

А потом приходит настоящий экзамен. Вопросы похожи, но сформулированы чуть иначе. И тут оказывается, что друг не понял ничего: он знал только конкретные буквы конкретных билетов, а не саму суть. Пробник — пятёрка, экзамен — двойка.

Ровно то же самое случается с моделями. Модель может выдавать почти 100% правильных ответов на тех примерах, которые ей показывали при обучении, и при этом позорно ошибаться на новых. Это называется переобучение. К концу урока ты будешь понимать, почему так происходит, как это поймать и почему хорошая модель — это та, которая понимает, а не та, которая зубрит.

Кстати, мы уже разбирали, как модель учится по признакам и меткам в уроке про признаки, метки и предсказания. Сегодня посмотрим, что происходит, когда это обучение идёт не туда.

Зубрёжка против понимания

Вернёмся к нашему сквозному примеру: модель учится отличать кошку от собаки. В прошлых уроках она смотрела на признаки — форму ушей, длину морды, форму зрачка. Хорошая модель находит общее правило: «у кошек уши обычно треугольные и торчком, зрачок вытянутый, морда короткая».

А теперь представь, что вместо общего правила модель поступила как тот друг с билетами. Ей показали 200 фоток. И она просто запомнила каждую фотку целиком: «вот эта картинка с таким-то расположением пикселей — кошка, а вот эта — собака». Формально на всех 200 фотках она не ошибается ни разу. Но дай ей 201-ю, новую фотку — и она растеряется, потому что такой картинки в её «шпаргалке» нет.

Подумай, чем это отличается от настоящего понимания. Когда ты сам смотришь на фотку незнакомого котёнка, ты ведь не сверяешь её с базой всех котов, которых видел в жизни. Ты просто замечаешь: ушки торчком, усы, вытянутый зрачок — и мгновенно понимаешь «кошка». Это и есть обобщение: правило, которое работает на том, чего ты раньше не встречал. Именно этого мы хотим от модели, а зубрёжка даёт ровно противоположное — намертво запомненные частности и ноль понимания.

Зубрёжка — это запоминание конкретных примеров. Понимание — это умение находить общее правило, которое работает и на тех примерах, которых ты раньше не видел.

Почему модель вообще начинает зубрить

Тут нет злого умысла. Модель просто делает то, за что её хвалят. Если единственная цель — «не ошибаться на этих 200 фотках», то самый простой способ её достичь — запомнить ответы наизусть. Зачем искать сложное правило про форму ушей, если можно просто вызубрить?

Особенно легко это происходит, когда:

  • Примеров мало. Запомнить 50 фоток наизусть — легко. Запомнить миллион — невозможно, придётся искать закономерность.
  • Модель слишком «мощная». Если у модели огромная память (много весов — помнишь, это числа, которые настраиваются при обучении), ей хватает места, чтобы тупо запомнить всё.
  • Учим слишком долго. Если гонять одни и те же примеры снова и снова, модель в какой-то момент перестаёт искать правило и начинает заучивать частности.

Главная идея: спрятать часть данных

Как понять, понял друг тему или просто зазубрил билеты? Очень просто: задай ему вопрос, которого не было в билетах. Если он ответит — он понял. Если поплывёт — зубрил.

Это та же мысль, что в рекомендациях в соцсетях. Лента, которая просто показывает тебе ровно те ролики, что ты уже лайкал, скучна — она ничего не «поняла» про твой вкус, она запомнила историю. А хорошая лента угадывает новое, чего ты ещё не видел, но что тебе зайдёт. Проверяют её именно на новом: понравится ли тебе то, чего раньше в твоей истории не было. Старое угадать легко, ценно угадывать новое — и с моделями всё так же.

В машинном обучении делают точно так же. Все данные заранее делят на две стопки:

СтопкаЧто этоАналогия
Обучающие данныеПримеры, на которых модель учится и настраивает весаБилеты, которые дали готовиться
Тестовые данныеПримеры, которые модель не видела при обучении; на них её проверяютРеальный экзамен с новыми вопросами

Главное правило: модель никогда не должна видеть тестовые данные во время обучения. Иначе это как подсмотреть ответы перед экзаменом — проверка станет бессмысленной.

И вот тут переобучение становится видимым. Сравниваем две цифры:

  • Точность на обучающих данных — насколько хорошо модель отвечает на знакомые примеры.
  • Точность на тестовых данных — насколько хорошо она отвечает на новые.

Если обе цифры высокие — отлично, модель действительно поняла. Если на обучающих данных почти 100%, а на тестовых — еле-еле 60%, это явный признак: модель зубрит, а не понимает.

Разбор на примерах

Пример 1: сравниваем две стопки

Давай посчитаем точность на двух стопках для нашей задачи «кошка или собака». Код просто считает долю верных ответов.

// Ответы модели и правильные метки на ОБУЧАЮЩИХ данных
const trainPredicted = ["кошка", "собака", "кошка", "собака", "кошка"];
const trainActual    = ["кошка", "собака", "кошка", "собака", "кошка"];

// Те же ответы на ТЕСТОВЫХ данных (модель их раньше не видела)
const testPredicted = ["кошка", "собака", "собака", "кошка", "собака"];
const testActual    = ["кошка", "собака", "кошка", "собака", "кошка"];

function accuracy(predicted, actual) {
  let correct = 0;
  for (let i = 0; i < predicted.length; i++) {
    if (predicted[i] === actual[i]) correct++;
  }
  return Math.round((correct / actual.length) * 100);
}

console.log("Точность на обучающих:", accuracy(trainPredicted, trainActual) + "%");
console.log("Точность на тестовых: ", accuracy(testPredicted, testActual) + "%");

Вывод:

Точность на обучающих: 100%
Точность на тестовых:  40%

Видишь разрыв? 100% против 40%. На знакомых примерах модель безупречна, на новых — хуже, чем подбрасывание монетки. Это классическая картина переобучения: модель запомнила обучающую стопку наизусть, но общего правила «кошка/собака» так и не выучила.

Разберём код по шагам, чтобы было понятно, что именно мы измеряем. Функция accuracy идёт по двум спискам параллельно: predicted — что ответила модель, actual — какой ответ правильный. На каждом шаге она сравнивает пару ответов и, если они совпали, прибавляет единичку к счётчику correct. В конце делит число верных на общее число примеров и переводит в проценты. Ничего волшебного — это просто доля угаданных ответов, выраженная в процентах. Сначала мы прогоняем её по обучающей стопке, потом по тестовой, и сравниваем две цифры. Весь смысл урока умещается в этот разрыв между ними.

Пример 2: тот же эффект на текстах

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

А переобученная модель ведёт себя иначе. Допустим, в её обучающих текстах фраза «Кошка пьёт молоко» встретилась ровно один раз, в каком-то конкретном рассказе. Зазубрившая модель намертво связала именно эту фразу именно с тем рассказом — и на чуть другой фразе теряется.

// Грубая имитация: "зазубренная" модель помнит только точные фразы
const memorized = {
  "Кошка пьёт": "молоко",
  "Собака грызёт": "кость"
};

function predictNext(phrase) {
  // Модель ищет ТОЧНОЕ совпадение, а общего правила у неё нет
  if (memorized[phrase]) return memorized[phrase];
  return "(не знаю)";
}

console.log("Кошка пьёт ->", predictNext("Кошка пьёт"));   // знакомая фраза
console.log("Котёнок пьёт ->", predictNext("Котёнок пьёт")); // почти то же самое

Вывод:

Кошка пьёт -> молоко
Котёнок пьёт -> (не знаю)

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

Разберём этот код. Объект memorized — это и есть «шпаргалка» модели: пары «точная фраза → следующее слово». Функция predictNext ищет в этой шпаргалке буквальное совпадение строки. Есть совпадение — выдаёт заученное слово, нет — честно говорит «не знаю». Для фразы «Котёнок пьёт» совпадения нет, хотя по смыслу ответ тот же, что и для «Кошка пьёт». В этом вся суть зубрёжки: модель оперирует строками, а не смыслом. Если бы она по-настоящему поняла, что котёнок — это маленькая кошка, она бы дала тот же ответ. Но понимания нет — есть только таблица заученных строк.

Сравни это с автодополнением в твоём телефоне. Хорошее автодополнение подсказывает разумное продолжение даже для фраз, которые ты никогда раньше не печатал, — потому что оно выучило общие закономерности языка. Плохое (зазубренное) умело бы продолжать только те фразы, что уже встречало слово в слово, и беспомощно молчало бы на всём новом. Тебе бы такая клавиатура быстро надоела.

Пример 3: как выглядит «в самый раз»

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

СостояниеНа обучающихНа тестовыхЧто это значит
Недообучение55%53%Модель не выучила даже простое правило — слишком слаба или мало училась
В самый раз92%89%Поняла суть: хорошо и на знакомых, и на новых
Переобучение100%61%Зазубрила обучающие, провалилась на новых

Обрати внимание на среднюю строку. Идеал — это не 100% на обучающих. Идеал — когда обе цифры высокие и близки друг к другу. Большой разрыв между ними — главный сигнал тревоги.

Частые ошибки и подводные камни

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

1. «Чем выше точность на обучении, тем лучше»

Самая частая ловушка. 100% на обучающих данных — это не повод радоваться, а повод насторожиться. Скорее всего, модель просто зазубрила. Хорошая модель честно ошибается на паре сложных обучающих примеров, зато уверенно работает на новых.

2. Случайно показать модели тестовые данные

Если хоть один тестовый пример попал в обучение, проверка испорчена. Это как если бы друг увидел экзаменационные вопросы заранее: его пятёрка уже ничего не доказывает. Стопки обучающих и тестовых данных нельзя смешивать ни в коем случае.

3. Думать, что переобучение — это «слишком умная» модель

Звучит логично, но это не так. Переобученная модель не умная — она глупая особым образом. Она отлично помнит частности и совершенно не умеет обобщать. Это не сила, а слабость, замаскированная под идеальные оценки на тренировке.

4. Слишком долго учить модель

Кажется, что чем дольше учишь, тем лучше. Но в какой-то момент модель перестаёт находить новые закономерности и начинает заучивать шум в данных — случайные мелочи конкретных примеров. Точность на тестовых данных при этом не растёт, а падает. Иногда лучше остановиться раньше.

5. Слишком мало данных

Если у тебя всего 30 фоток кошек и собак, почти любая модель их просто запомнит. Чем больше и разнообразнее данные, тем труднее зубрить и тем сильнее модель вынуждена искать настоящее правило. Поэтому большие модели и кормят гигантскими наборами данных.

6. Путать разрыв с обычными ошибками

Любая модель иногда ошибается — это нормально, идеальных не бывает. Важно не само наличие ошибок, а именно разрыв между обучающими и тестовыми данными. Модель с 88% на обучении и 86% на тесте ошибается, но честно: она одинаково хороша и там, и там. А вот 100% и 61% — это тревога, даже несмотря на красивую первую цифру. Смотри не на одну точность, а на обе сразу и на разницу между ними.

Мини-практика

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

function diagnose(trainAcc, testAcc) {
  // ЗАДАНИЕ: верни строку-диагноз по правилам:
  // 1) если обе точности низкие (меньше 70) -> "Недообучение"
  // 2) если разрыв (trainAcc - testAcc) больше 20 -> "Переобучение"
  // 3) иначе -> "В самый раз"

  // ... твой код здесь ...
}

// Проверь на этих случаях:
console.log(diagnose(55, 53));   // ожидаем: Недообучение
console.log(diagnose(100, 61));  // ожидаем: Переобучение
console.log(diagnose(92, 89));   // ожидаем: В самый раз

Подсказка: сначала проверь условие про недообучение, потом про разрыв, и только в конце возвращай «В самый раз». Если справишься — попробуй добавить четвёртый случай: что вывести, если точность на тестовых данных выше, чем на обучающих? (Спойлер: обычно это значит, что данные разделили как-то странно — повод перепроверить.)

Итоги

Соберём главное в одну табличку, чтобы осталось в голове:

ПонятиеПростыми словами
ПереобучениеМодель зазубрила примеры вместо того, чтобы понять правило
Обучающие данныеБилеты, по которым модель готовилась
Тестовые данныеНовый экзамен, которого модель не видела
Главный сигналБольшой разрыв: высокая точность на обучении, низкая на тесте
ИдеалОбе точности высокие и близкие друг к другу

Запомни простую мысль: цель обучения — не получить идеальные оценки на тренировке, а научиться работать с тем, чего раньше не видел. Именно поэтому настоящие модели — от распознавания кошек до ChatGPT — учат на огромных данных и обязательно проверяют на отложенной стопке, которую модель не видела.

И вот ещё что важно понять про честность ИИ, раз уж мы об этом. Когда ChatGPT уверенно выдаёт что-то выдуманное (мы потом разберём это как «галлюцинацию»), часть проблемы — родом отсюда же: модель видела похожие куски текста и достраивает «правдоподобное продолжение», а не проверенный факт. Понимание того, что модель учится на примерах и может ошибаться на новом, — это прививка от завышенных ожиданий. ИИ не всезнающий оракул, а система, которая ищет закономерности в данных и иногда промахивается. Это нормально — просто держи это в голове, когда пользуешься такими инструментами.

В следующих уроках мы перейдём от того, как модель учится, к тому, из чего она устроена. Наша задача «кошка или собака» снова вернётся — но уже как нейросеть: множество простых элементов-нейронов, связанных весами. Ты увидишь, что то самое «общее правило», которое модель должна найти вместо зубрёжки, прячется именно в этих весах.

Проверьте себя
1. Что такое переобучение?
AМодель учится слишком быстро и поэтому ошибается
BМодель зазубрила обучающие примеры и плохо работает на новых данных
CМодель обучили на слишком большом количестве данных
DМодель отлично работает и на знакомых, и на новых примерах
2. Зачем данные делят на обучающие и тестовые?
AЧтобы обучение шло в два раза быстрее
BЧтобы проверить модель на примерах, которых она не видела, и поймать зубрёжку
CЧтобы модели было легче запомнить все ответы
DЧтобы тестовые данные тоже участвовали в настройке весов
3. Модель даёт 100% на обучающих данных и 60% на тестовых. Что это значит?
AЭто идеальная модель, можно использовать
BЭто недообучение — модель ничего не выучила
CЭто переобучение — большой разрыв между знакомыми и новыми примерами
DЭто ошибка в коде, такого не бывает
4. Почему случайно показать модели тестовые данные во время обучения — плохо?
AЭто замедлит обучение
BПроверка станет бессмысленной — это как подсмотреть ответы перед экзаменом
CМодель станет слишком умной
DНичего страшного, так даже лучше
5. Какое состояние модели считается идеальным?
A100% на обучающих данных любой ценой
BНизкая точность и на обучающих, и на тестовых
CОбе точности высокие и близкие друг к другу
DВысокая точность на обучающих и низкая на тестовых
6. Почему большие модели кормят гигантскими наборами данных?
AЧтобы обучение длилось дольше
BПотому что чем больше данных, тем труднее зубрить и тем сильнее модель вынуждена искать настоящее правило
CЧтобы модель могла запомнить каждый пример наизусть
DЭто никак не влияет на переобучение