Что делает нейросеть #

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

Представьте, что у вас есть таблица: 3000 точек с координатами (x, y) и подписью «класс 0», «класс 1» или «класс 2». Нейросеть смотрит на пары (x, y) и учится угадывать класс. Сначала она угадывает случайно — треть точек правильно (3 класса). Потом, постепенно меняя веса, начинает угадывать всё лучше. К концу обучения — больше 90%.

Ключевое понятие: ошибка (лосс)
На каждом шаге обучения считается «лосс» — насколько неправильно сеть ответила. Цель обучения — снизить лосс. Чем ниже лосс — тем точнее сеть. Кузня показывает лосс на экране в виде кривой: смотрите, она должна идти вниз.

Задача: Spirals #

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

Данные: 3000 точек, 2 признака (x, y), 3 класса. На входе сеть получает координаты точки — на выходе даёт три числа: «вероятность класса 0», «вероятность класса 1», «вероятность класса 2». Правильный ответ — тот класс, у которого вероятность максимальная.

Конфиг человеческим языком #

Вот конфиг пресета spirals — разберём каждый блок:

jsonc
{
  "project": "spiral-classifier",  // имя — для ваших записей
  "task": "supervised",            // тип задачи: supervised или rl

  "model": {
    "input_dim": 2,     // 2 числа на вход: x и y координата
    "output_dim": 3,    // 3 числа на выход: вероятность каждого класса
    "layers": [
      { "type": "linear", "units": 64 },  // скрытый слой
      { "type": "relu" },                 // нелинейность
      { "type": "linear", "units": 64 },  // ещё один
      { "type": "relu" }
    ]
  },

  "data": {
    "source": "builtin:spirals",
    "n_samples": 3000,  // сколько точек генерировать
    "val_split": 0.2,   // 20% — валидационная выборка
    "batch_size": 64    // обрабатывать по 64 точки за раз
  },

  "train": {
    "epochs": 40,       // сколько раз прочитать все данные
    "loss": "cross_entropy",
    "optimizer": {
      "name": "adamw",
      "lr": 0.003       // скорость обучения
    }
  }
}

Эпоха — что это #

Эпоха = один полный проход по всем обучающим данным.

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

В Кузне: при 3000 точках и batch_size=64, за одну эпоху сеть делает примерно 3000 × 0.8 / 64 = 37 шагов обновления весов. За 40 эпох — около 1 500 шагов.

Сколько эпох нужно?
Нет универсального ответа. Смотрите на кривую лосса: если она перестала снижаться — больше эпох не помогут. Кузня сама остановит обучение через early stopping (по умолчанию через 10 эпох без улучшения).

Learning rate — скорость обучения #

Learning rate (lr) — это насколько сильно сеть меняет веса за один шаг. Представьте, что вы ищете самую низкую точку в тёмной долине наощупь, маленькими шагами.

Слишком большой LR (0.1+)

Шаги слишком большие — перепрыгиваете через минимум. Лосс скачет или вовсе растёт. Обучение не сходится.

Хороший LR (0.001–0.003)

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

Слишком маленький LR (0.00001)

Лосс снижается, но очень медленно. За 40 эпох сеть почти не выучит ничего — нужно в 10 раз больше эпох.

Для большинства задач 0.001–0.003 с оптимизатором AdamW — хорошая отправная точка. Именно такой используется в пресете spirals.

Планировщик (scheduler)
Кузня поддерживает постепенное снижение LR в процессе обучения. Пресет spirals использует cosine: LR начинает с 0.003 и плавно падает к нулю к концу. Это позволяет быстро выучить грубую структуру, а к концу «дошлифовать» точно.

Batch size — размер батча #

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

Маленький батч (8–16): обновлений много, но шумные. Большой батч (256+): обновлений меньше, они точные, но нужно больше памяти. Батч 64 — хорошее значение для большинства задач на CPU.

Валидационная выборка — зачем #

val_split: 0.2 означает: откладываем 20% данных в сторону и не даём сети их видеть при обучении. После каждой эпохи проверяем точность на этих «спрятанных» данных.

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

Хороший признак: train loss и val loss снижаются примерно одинаково.
Плохой признак: train падает, а val остаётся на месте или растёт. Это переобучение.

Кривая потерь — как читать #

На экране Кузни — два графика: оранжевый — train loss (потери на обучающих данных), зелёный/голубой — val loss (потери на валидации).

Эпохи Лосс 0 10 20 30 40 train loss val loss 0.24 0.28 Хорошее обучение

Что хорошо выглядит:

  • Обе кривые снижаются со временем.
  • Val loss немного выше train loss — это нормально.
  • К концу кривые выравниваются (сеть достигла предела).

Что плохо выглядит:

  • Лосс вообще не снижается — слишком маленький LR или неправильная архитектура.
  • Лосс скачет хаотично — слишком большой LR.
  • Val loss начинает расти, пока train падает — переобучение.
  • Лосс стал NaN — взрыв градиентов (снизьте LR или включите grad_clip).

Переобучение — что делать #

Переобучение (overfitting) — сеть выучила тренировочные данные «наизусть», но потеряла способность обобщать. На графике это выглядит так: train loss падает, а val loss в какой-то момент останавливается или растёт.

Эпохи Лосс 0 10 20 30 40 здесь остановиться train loss val loss ↑ Переобучение

Что делать при переобучении

Варианты от простого к сложному:

  1. Включить early stopping (уже включён по умолчанию). "early_stop": { "enabled": true, "patience": 10 } — остановит обучение через 10 эпох без улучшения val loss.
  2. Добавить Dropout — слой, который случайно «выключает» часть нейронов при обучении:
    { "type": "linear", "units": 64 },
    { "type": "relu" },
    { "type": "dropout", "p": 0.2 },  // выключает 20% нейронов
  3. Добавить weight decay — L2-регуляризация в оптимизаторе: "weight_decay": 0.01
  4. Уменьшить сеть — меньше нейронов в слоях, меньше слоёв.
  5. Добавить данных — больший датасет снижает переобучение.

Карта решений — как читать #

Карта решений показывает, как нейросеть делит плоскость по классам. Кузня строит её для задач с 2D-входом (например, spirals).

  • Цвет области — предсказанный класс для каждой точки плоскости.
  • Насыщенность цвета — уверенность сети. Яркий = уверен, бледный = сомневается.
  • Граница между цветами — линия решения. Идеально: она должна совпасть со спиралями.
  • Точки на карте — настоящие данные. Совпадают ли цвет точки с цветом фона — правильный ответ?

Ползунок эпох позволяет смотреть на любую промежуточную карту. В эпоху 1 — каша. К эпохе 40 — чёткие спиральные области.

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

Полоса градиентов #

Полоса grad/weight показывает «здоровье» обучения по слоям. Каждый столбик — один слой, высота — отношение градиента к весу.

  • Все столбики низкие и равномерные — хорошо. Обучение идёт ровно.
  • Столбики первых слоёв почти нулевые — затухание градиентов (vanishing). Сеть глубокая, первые слои не учатся. Попробуйте LayerNorm или другую активацию.
  • Один столбик резко выбивается вверх — взрыв градиентов (exploding). Уменьшите LR или проверьте grad_clip.

Обучение на своём CSV #

Когда spirals освоена, следующий шаг — загрузить свои данные. Кузня читает CSV автоматически: распознаёт заголовок и разделитель.

Формат файла

Требования к CSV:

  • Последняя колонка — метка (класс или целевое значение для регрессии).
  • Остальные колонки — признаки (числа).
  • Заголовок опционален — Кузня определит автоматически.

Пример файла iris.csv:

csv
sepal_len,sepal_wid,petal_len,petal_wid,species
5.1,3.5,1.4,0.2,0
4.9,3.0,1.4,0.2,0
6.3,3.3,6.0,2.5,2
...

Конфиг для CSV

jsonc
{
  "project": "iris-classifier",
  "task": "supervised",
  "model": {
    "input_dim": 4,    // число признаков (колонок минус метка)
    "output_dim": 3,   // число классов
    "layers": [
      { "type": "linear", "units": 32 },
      { "type": "relu" },
      { "type": "linear", "units": 32 },
      { "type": "relu" }
    ]
  },
  "data": {
    "source": "data/iris.csv",  // путь к вашему файлу
    "val_split": 0.2,
    "batch_size": 32,
    "normalize": true           // всегда включайте для числовых признаков
  },
  "train": {
    "epochs": 60,
    "loss": "cross_entropy",
    "optimizer": { "name": "adamw", "lr": 0.001 },
    "early_stop": { "enabled": true, "patience": 15 }
  }
}
model.input_dim
При загрузке CSV Кузня автоматически исправит input_dim под реальное число признаков в файле — так что можно не считать вручную. Просто укажите примерное значение, в логе появится предупреждение с правильным числом.

Регрессия (числовой выход)

Если метка — не класс, а число (например, цена квартиры), поменяйте:

"model": {
  "output_dim": 1    // один выход
},
"train": {
  "loss": "mse"      // MSE вместо cross_entropy
}

Проверить перед запуском

bash
kuz check my.json

Команда проверяет конфиг и данные, не запуская обучение. Выведет: количество примеров в train/val, тип задачи, число параметров.