ИИ для инженера: основы Нейросети: как обучают модель
0%

Нейросети: как обучают модель

Нейросети: как обучают модель

Фрагмент лекции: «Все что нужно знать про ИИ айтишнику», 19:32 — отсюда взято содержание этой главы.

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

Сдвиг, о котором эта глава, звучит так:

Мы больше не пишем правило. Мы пишем форму правила с пустыми местами — и алгоритм подбирает числа в этих местах так, чтобы на известных примерах ответы сходились.

Числа в пустых местах называются параметрами, или весами. Процесс их подбора называется обучением. Всё остальное в главе — детали этого предложения.

Почему нейросети выделяют в отдельный класс

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

  1. Объём практических результатов последних полутора десятилетий. Теоретическая база под нейросетями существовала десятилетиями и большую часть времени лежала без применения: не было ни данных нужного объёма, ни железа, ни рынка, который оплачивал бы эксперименты. Когда все три условия сошлись, из области, которая была одной из веток, вышла основная масса заметных результатов.
  2. Вокруг больших языковых моделей выросла отдельная индустрия со своими ролями, инструментами, поставщиками и экономикой. Инженер, который встраивает LLM в продукт, не занимается ни выбором ядра для SVM, ни настройкой бустинга — это соседняя профессия.

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

Нейрон и синапс — это разные вещи

В устном рассказе эти два слова часто используются как синонимы. Это удобно на слух и мешает понять устройство, поэтому разведём их аккуратно.

Биологическая аналогия Что это в модели Обучается?
Нейрон (клетка) Узел: считает взвешенную сумму входов и пропускает её через нелинейность Сам по себе — нет, это фиксированная операция
Синапс (связь между клетками) Ребро между узлами, у которого есть числовой вес Да, вес — это и есть то, что подбирается
Порог возбуждения Смещение (bias) узла Да, это тоже параметр

Формула одного узла в инлайн-коде: z = w1*x1 + w2*x2 + ... + wn*xn + b, затем a = f(z), где f — нелинейность (исторически сигмоида, сейчас чаще ReLU и её родственники).

Три следствия, которые сразу снимают половину недоразумений:

Обучается не структура, а числа на рёбрах. Сколько узлов, сколько слоёв, какая нелинейность — это архитектура, её выбирает человек до начала обучения. Обучение меняет только веса и смещения.

Нелинейность обязательна. Без неё стопка слоёв схлопывается в одно линейное преобразование: композиция линейных функций линейна. Именно нелинейность делает многослойность осмысленной.

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

Как именно считаются производные по каждому весу — метод обратного распространения ошибки — в этом треке не разбирается: это отдельная тема с отдельной математикой. Она подробно изложена в статье Перцептрон и обратное распространение; классическая работа — Rumelhart, Hinton, Williams, 1986.

Что такое «веса» физически

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

Никакого текста, никаких «записанных паттернов» в читаемом виде внутри нет. Есть тензоры: слой_12.attention.query.weight — матрица такого-то размера, и дальше просто числа. Формат файла описывает, где какой тензор лежит, какой у него размер и тип элемента:

  • safetensors — распространённый формат хранения весов; проектировался так, чтобы загрузка не требовала исполнения кода из файла (в отличие от Python-пикла) и допускала чтение отдельных тензоров без разбора всего файла. Спецификация.
  • GGUF — формат, ориентированный на локальный инференс на CPU и потребительских GPU: хранит веса вместе с метаданными и токенизатором, поддерживает разные схемы квантизации. Описание формата.

Арифметика размера

Это не оценка, а прямой счёт: размер файла весов ≈ число параметров × число байт на параметр.

Точность Бит на параметр Байт на параметр
FP32 (одинарная) 32 4
FP16 / BF16 (половинная) 16 2
INT8 (квантизация) 8 1
INT4 (квантизация) 4 0,5

Подставим:

Число параметров В 16 битах В 4 битах
1 млрд 2 · 10⁹ байт ≈ 1,9 ГиБ 0,5 · 10⁹ байт ≈ 0,47 ГиБ
7 млрд 14 · 10⁹ байт ≈ 13,0 ГиБ 3,5 · 10⁹ байт ≈ 3,3 ГиБ
70 млрд 140 · 10⁹ байт ≈ 130 ГиБ 35 · 10⁹ байт ≈ 32,6 ГиБ
671 млрд 1342 · 10⁹ байт ≈ 1,22 ТиБ 335,5 · 10⁹ байт ≈ 312 ГиБ

Отсюда сразу читаются практические вещи, которые обычно узнают опытным путём:

  • Почему «модель на 7 миллиардов» помещается в ноутбук, а «на 70» — нет. 13 ГиБ ещё можно уложить в оперативную память, 130 ГиБ — нет ни в какую потребительскую конфигурацию.
  • Зачем нужна квантизация. Переход с 16 бит на 4 уменьшает файл вчетверо. Платой становится потеря точности представления каждого числа — при аккуратных схемах она мала, но не нулевая; см. QLoRA, где такой подход разобран подробно.
  • Веса — это только нижняя граница потребности в памяти. При инференсе к ним добавляются активации и кэш ключей-значений, растущий с длиной контекста; при обучении — ещё градиенты и состояние оптимизатора, что в типичной конфигурации умножает требования в несколько раз. Про железо — глава 08.

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

Режимы обучения

Обучение различают по тому, откуда берётся сигнал о правильности.

Обучение с учителем. Даны пары «вход — правильный ответ». Модель минимизирует расхождение между своим выходом и эталоном. Требует разметки, и разметка — обычно самая дорогая часть проекта.

Обучение без учителя. Эталонов нет. Задача — найти структуру: разбить объекты на группы, сжать признаки, выделить аномалии. Проверить результат объективно сложно, потому что не с чем сравнивать.

Самообучение (self-supervised). Формально это обучение с учителем, но разметку не делает никто — она извлекается из самих данных механически. Для языковой модели это выглядит так: берём предложение, отрезаем его на произвольном месте, просим предсказать следующий токен, сравниваем с тем, который в тексте действительно стоял. Правильный ответ уже был в тексте — его просто закрыли рукой.

Это и есть ответ на вопрос «кто размечал терабайты текста для обучения LLM». Никто. Разметка бесплатна и порождается из корпуса автоматически, поэтому предобучение и удалось масштабировать до объёмов, недостижимых для ручной разметки. Родственный приём для картинок и для двунаправленных текстовых моделей — предсказание намеренно замаскированных фрагментов (BERT).

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

На практике режимы не конкурируют, а идут стадиями. Типичный конвейер современной языковой модели:

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

Учителем не обязательно быть человеком

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

Дистилляция знаний. Большая модель («учитель») размечает данные, маленькая («ученик») учится воспроизводить не только её ответы, но и распределение уверенности. Получается модель в разы меньше, теряющая часть качества, но пригодная для запуска там, где большая не помещается. Исходная работа — Hinton, Vinyals, Dean, 2015.

RLHF — обучение с подкреплением на обратной связи человека. Люди сравнивают пары ответов и говорят, какой лучше. На этих сравнениях обучается модель вознаграждения, и уже она — не человек — оценивает миллионы ответов при оптимизации. Человек размечает тысячи примеров, а работает разметка на миллионах. Каноническая работа — InstructGPT.

RLAIF — то же самое, но оценивает модель. Вместо человеческих сравнений используются оценки другой модели, ограниченной набором явно записанных принципов; см. Constitutional AI. Дешевле и масштабируемее, но качество упирается в качество оценщика.

Синтетические данные. Учитель порождает не метки, а сами примеры: задачи с решениями, диалоги, варианты формулировок. Особенно полезно там, где реальных данных мало или их нельзя использовать по юридическим причинам.

Автоматический учитель без модели вообще. Часто самый надёжный вариант: код проверяется тестами и компилятором, ответ по базе — SQL-запросом, арифметика — калькулятором. Здесь сигнал о правильности объективен и не наследует ничьих ошибок.

Риск, о котором нужно помнить всегда: обучение на выходах другой модели наследует её ошибки. Систематическое искажение учителя переезжает к ученику как норма, а не как шум, и внутри данных ученика его больше нечем обнаружить — эталона-то нет. При многократном повторении цикла «модель породила данные → на них обучили следующую» распределение вырождается: редкие явления исчезают первыми (Shumailov et al., 2023). Практический вывод: синтетика хороша как дополнение к реальным данным и опасна как их замена, а любой конвейер с моделью-учителем нуждается в независимой проверке на данных, которых учитель не касался.

Ранжирование — это задача, а не режим обучения

В лекции звучит формулировка, которую стоит поправить: «если мы проверяем, что результат верный — это система ранжирования; если отдаём воле случая — это цепи Маркова». Здесь смешаны две независимые оси, и от их разделения становится сильно понятнее.

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

Оси ортогональны: почти любая задача сочетается почти с любым режимом.

Класс задачи Что на выходе С учителем Self-supervised С подкреплением
Классификация метка из конечного набора да, стандартно предобучение + дообучение редко
Регрессия число да, стандартно предобучение представлений редко
Ранжирование порядок элементов да, по размеченной релевантности предобучение на логах да, по кликам и удержанию
Кластеризация разбиение на группы нет (меток нет) да нет
Генерация текста последовательность токенов дообучение на парах да, предобучение да, RLHF

Ранжирование (learning to rank) — полноценный класс задач со своими функциями потерь: поточечной (предсказываем оценку каждому элементу), попарной (учим, что A должен стоять выше B), списочной (оптимизируем метрику качества всего списка). Оно лежит в основе поиска и рекомендаций и разобрано в статье рекомендательные системы.

Отдельно: «отдать воле случая» — это не режим обучения и не класс задач, а способ выбора из уже посчитанного распределения. Цепи Маркова из главы 02 вообще не обучаются градиентным спуском — их переходные вероятности просто подсчитываются по частотам в корпусе. Стохастичность там и в нейросетевой генерации — это про выбор ответа, а не про то, как модель училась.

Чего модель не видит: признаки и человек в контуре

Ценная мысль лекции, которую стоит переформулировать строго. Возьмём медиасервис, который ранжирует каталог по статистике просмотров, чтобы решить, что вывести на главную. Алгоритм честно оптимизирует ровно то, что ему дали: число просмотров и производные от него признаки. Он выдаёт список.

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

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

Роль человека в контуре здесь понятна точно: он добавляет недостающий признак или недостающую метку, а не «поправляет модель вручную». Ручная правка выдачи не переживает следующего пересчёта; добавленный признак переживает.

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

Две ловушки, в которые эта схема сваливается на практике:

  • Петля обратной связи. Если модель учится на кликах по тому, что она сама и показала, она подтверждает собственные решения: то, что не попало в выдачу, не могло быть кликнуто. Лечится логированием показов, коррекцией на позицию и долей случайного трафика. Обзор таких эффектов — Hidden Technical Debt in Machine Learning Systems.
  • Подмена цели. Метрика, которую оптимизируют, почти никогда не равна тому, что нужно бизнесу. Модель добросовестно выкрутит именно метрику — со всеми побочными эффектами.

Переобучение: почему «хорошо на обучающих данных» ничего не значит

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

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

Обучение своими руками: 40 строк без библиотек

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

import numpy as np

rng = np.random.default_rng(42)

# Данные: два признака и метка 0/1.
# Разделяющее правило нам НЕ известно — мы его не пишем, а восстанавливаем.
n = 400
X = rng.normal(size=(n, 2))
y = (X[:, 0] + 0.5 * X[:, 1] > 0).astype(float)   # скрытая закономерность

# Параметры модели: два веса и смещение. Это и есть всё, что «обучается».
w = np.zeros(2)
b = 0.0
lr = 0.5        # скорость обучения: размер шага против градиента
epochs = 300

def sigmoid(z):
    """Нелинейность: превращает взвешенную сумму в число от 0 до 1."""
    return 1.0 / (1.0 + np.exp(-z))

for epoch in range(epochs):
    # 1. Прямой проход: взвешенная сумма входов, затем нелинейность.
    p = sigmoid(X @ w + b)

    # 2. Потеря: насколько предсказание разошлось с меткой (логистическая).
    loss = -np.mean(y * np.log(p + 1e-12) + (1 - y) * np.log(1 - p + 1e-12))

    # 3. Градиент: в какую сторону сдвинуть каждый вес, чтобы потеря упала.
    err = p - y                    # вектор длины n
    grad_w = X.T @ err / n         # вектор длины 2
    grad_b = err.mean()

    # 4. Шаг оптимизатора: обновляем числа. Вот это и есть «обучение».
    w -= lr * grad_w
    b -= lr * grad_b

    if epoch % 100 == 0:
        print(f"эпоха {epoch:3d}  потеря {loss:.4f}  w={w.round(3)}  b={b:.3f}")

# Смысл имеет не величина весов, а их отношение: оно задаёт наклон
# разделяющей границы, восстановленной по одним лишь примерам.
print("итог:", w.round(3), round(b, 3))

Что здесь стоит увидеть:

  • Правило нигде не записано. В коде нет ни одного if про то, как отличить класс 0 от класса 1. Есть только форма — взвешенная сумма — и процедура подгонки чисел.
  • Обучение — это цикл обновления массива. Тот же самый цикл крутится при обучении модели на сотни миллиардов параметров. Меняются размерности, оптимизатор, распараллеливание, но не суть.
  • Веса не сходятся к «правильным» числам — они задают направление. Скрытое правило было x1 + 0.5*x2 > 0, а модель выдаёт что-то вроде [5.4, 3.0]: отношение примерно совпадает, масштаб — нет. На линейно разделимых данных веса продолжают расти сколько угодно долго, а граница почти не двигается; именно этот рост и ограничивает регуляризация.

Сложность одной эпохи. При n примерах и d признаках прямой проход X @ w — это O(n*d) операций, вычисление градиента X.T @ err — тоже O(n*d). Итого O(n*d) по времени на эпоху и O(n*d) по памяти на хранение выборки плюс O(d) на сами параметры. Полное обучение — O(epochs*n*d).

Отсюда же понятно, зачем нужен стохастический градиентный спуск: когда n — это миллионы примеров, а d — тысячи признаков, считать градиент по всей выборке на каждом шаге слишком дорого. Вместо этого берут случайный мини-батч размера m, и эпоха стоит те же O(n*d), но шагов внутри неё делается n/m, и обновления начинаются сразу, а не после полного прохода. Оптимизаторы разобраны в статье обучение и оптимизация.

Две поправки к устному изложению

«Нейросети работают так же, как цепи Маркова»

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

Цепь Маркова из главы 02 по определению обладает марковским свойством: вероятность следующего состояния зависит только от текущего состояния, а не от того, как в него пришли. Отсюда её потолок: она не способна учесть согласование с началом фразы, потому что начало фразы для неё уже не существует.

Языковая модель устроена иначе: она задаёт распределение следующего токена при условии всего префикса — всего, что было от начала контекста. Все токены доступны ей одновременно, и механизм внимания на каждом шаге решает, какие из них важны. Это не количественное улучшение цепи Маркова, а другое свойство: снятие ограничения на длину зависимости. Из него растёт всё остальное — от связного текста на страницу до способности следовать инструкции, данной пять реплик назад.

Ограничение при этом есть, просто другое: не «одно состояние», а «сколько влезло в контекстное окно». Подробности — глава 06 и глава 07.

«Общую модель вы не натренируете, данных слишком много»

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

Корректная версия того же наблюдения:

Обучение фундаментальной модели с нуля требует вычислительных ресурсов и корпусов, недоступных вне крупных лабораторий. Поэтому прикладная работа почти всегда начинается с готовой модели: её берут как есть, дообучают на своих данных или встраивают в систему с поиском по своим документам.

Практическая лестница выглядит так, и подниматься по ней нужно снизу:

Что делаем Когда оправдано Порядок затрат
Промптинг готовой модели почти всегда — первая попытка часы
Поиск по своим документам (RAG) модель не знает ваших данных дни
Дообучение готовой модели нужен устойчивый стиль, формат, узкий домен недели
Обучение с нуля предметная область, для которой нет базовых моделей вне досягаемости большинства команд

Прикладная часть этой лестницы разобрана в треке по ИИ-инженерии, в частности в статьях про RAG и дообучение. Сколько данных и вычислений нужно для модели заданного размера — отдельная изученная зависимость, см. Kaplan et al., 2020 и Hoffmann et al., 2022.

Мини-итог

  • Обучение — это не написание правила, а подбор чисел: человек задаёт форму (архитектуру), алгоритм подгоняет параметры так, чтобы на известных примерах ошибка была минимальной.
  • Узел (нейрон) считает взвешенную сумму и пропускает её через нелинейность; вес живёт на связи. Обучаются веса и смещения, а не структура сети; прямой проход при фиксированных весах детерминирован.
  • Веса физически — массив чисел с плавающей точкой в файле (safetensors, GGUF). Размер считается арифметически: 7 млрд параметров это 14 ГБ в 16 битах и 3,5 ГБ в 4 битах.
  • Режимы обучения различаются источником сигнала о правильности: учитель, никто, сами данные (self-supervised), среда. Предобучение языковых моделей — self-supervised, поэтому его и удалось масштабировать.
  • Учителем бывает не человек: дистилляция, RLHF и RLAIF, синтетические данные, автоматические проверки. Обучение на выходах другой модели наследует её систематические ошибки.
  • Ранжирование — класс задач, обучение с учителем — режим обучения; это ортогональные оси, а не синонимы. Модель не знает того, чего нет в её признаках, и человек в контуре ценен тем, что добавляет недостающий признак, а не правит выдачу руками.
  • Обучить фундаментальную модель с нуля можно — но не в обычной команде; прикладная работа начинается с готовой модели.

Что дальше

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

Представления: почему модель «не знает», как выглядит мир

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

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

Доска запросов