Машинное обучение: pandas, NumPy, матстатистика и PyTorch

Курс охватывает полный цикл решения задач машинного обучения: от подготовки данных в pandas/NumPy и базовой математической статистики до обучения и отладки моделей в PyTorch. Акцент на практических навыках анализа данных, построении признаков, оценке качества и реализации моделей.

1. Python для данных: NumPy и pandas как основа ML-пайплайна

Python для данных: NumPy и pandas как основа ML-пайплайна

Зачем в машинном обучении нужны NumPy и pandas

Большая часть работы в машинном обучении происходит до обучения модели: сбор данных, очистка, объединение источников, построение признаков, контроль утечек, подготовка матрицы признаков и целевого вектора. В Python эти задачи чаще всего решаются связкой:

  • NumPy — быстрые многомерные массивы и векторные вычисления.
  • pandas — табличные данные, индексация, группировки, объединения, удобная работа с пропусками и типами.
  • Дальше по курсу мы будем добавлять матстатистику (оценки, проверки гипотез, доверительные интервалы) и PyTorch (обучение моделей). Поэтому цель этой статьи — научиться уверенно превращать сырой датасет в числовые тензоры, пригодные для обучения.

    Полезные первоисточники:

  • Документация NumPy
  • Руководство пользователя pandas
  • Документация по тензорам PyTorch
  • !Схема показывает, как данные проходят путь от источников через pandas/NumPy к тензорам PyTorch и обучению модели

    Базовые структуры данных

    NumPy: ndarray

    numpy.ndarray — это однородный массив (все элементы одного типа), оптимизированный для быстрых операций.

    Ключевые свойства:

  • shape — форма массива (например, (n_samples, n_features)).
  • dtype — тип элементов (например, float32, int64).
  • Векторизация — операции выполняются над целыми массивами без явных Python-циклов.
  • Пример:

    pandas: Series и DataFrame

    pandas.Series — одномерный массив с индексом.

    pandas.DataFrame — таблица: столбцы (часто разных типов) + индекс строк.

    Типичное правило:

  • pandas — когда данные табличные, есть имена колонок, смешанные типы, нужно groupby, merge, работа с датами.
  • NumPy — когда данные уже приведены к числам и нужно быстро считать/передавать в модель.
  • Сравнение:

    | Что сравниваем | NumPy ndarray | pandas DataFrame | |---|---|---| | Типы данных | Обычно один dtype на весь массив | Типы по столбцам | | Индексация | По позициям | По позициям и меткам | | Работа с пропусками | Есть np.nan, но меньше инструментов | Много встроенных средств | | Объединения таблиц | Не основная задача | merge, join, concat | | Типичная роль в ML | Матрица признаков | Подготовка и исследование данных |

    Векторизация и broadcasting в NumPy

    Почему циклы в Python — часто плохая идея

    Циклы по миллионам строк в чистом Python медленные. NumPy переносит вычисления в оптимизированный C-код.

    Пример: стандартизация признака (вычитание среднего и деление на стандартное отклонение).

    Формулы:

    Здесь:

  • — значение признака в объекте с номером
  • — число объектов
  • — сумма по всем объектам
  • — среднее значение признака
  • Здесь:

  • — исходное значение
  • — среднее
  • — стандартное отклонение
  • — стандартизированное значение
  • Реализация в NumPy:

    Broadcasting

    Broadcasting — это правило NumPy, позволяющее выполнять операции над массивами разных форм, если они совместимы.

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

    Индексация и срезы: где чаще всего ошибаются

    NumPy: позиции и маски

    Два базовых подхода:

  • Индексация по позициям: X[0, 1]
  • Булевы маски: X[X[:, 0] > 10]
  • pandas: loc и iloc

    В pandas важно различать:

  • iloc — индексация по позициям (как в NumPy)
  • loc — индексация по меткам индекса и названиям колонок
  • Практическое правило для ML-пайплайна:

  • Для явных выборок строк и колонок используйте loc/iloc, а не цепочки вида df[...][...].
  • Пропуски и типы данных: основа надежной подготовки данных

    Пропуски: NaN и NA

    В реальных данных пропуски встречаются всегда. В pandas вы будете видеть:

  • NaN (часто в числах)
  • None (часто в объектах)
  • pd.NA (универсальный пропуск в новых nullable-типах)
  • Базовые операции:

    Стратегии обработки пропусков зависят от смысла данных:

  • Удаление строк/колонок (если пропусков мало или признак нерелевантен)
  • Заполнение константой (например, 0, если это валидное значение)
  • Заполнение статистикой (среднее/медиана)
  • Отдельный признак "было пропущено" (часто полезно)
  • Типы: почему object — частый источник проблем

    В pandas колонка типа object может содержать строки, числа и что угодно вперемешку. Для ML это плохо: модели ожидают числа.

    Полезные практики:

  • Явно приводить числовые колонки: pd.to_numeric(..., errors="coerce")
  • Явно приводить даты: pd.to_datetime(...)
  • Следить за категориальными признаками: astype("category")
  • Пример:

    Объединение таблиц и агрегации: частая часть feature engineering

    merge: собрать датасет из нескольких источников

    merge — аналог SQL JOIN.

    Важно для ML:

  • how="left" помогает не потерять пользователей без покупок (появятся пропуски в amount).
  • Проверяйте, не возникает ли размножение строк из-за неверного ключа соединения.
  • groupby: агрегировать историю в признаки

    Частый сценарий: из транзакций сделать признаки на пользователя.

    Затем такие агрегаты объединяют обратно с таблицей пользователей через merge.

    Из DataFrame к матрице признаков

    Кодирование категорий

    Многие модели работают только с числами. Простой базовый вариант для категорий — one-hot encoding.

    Важно:

  • В реальных пайплайнах нужно обеспечивать одинаковый набор колонок на train и test.
  • Позже в курсе мы будем обсуждать более строгие подходы к кодированию и разделению данных.
  • Получение X и y

    Типичный формат для обучения:

  • X — матрица признаков размера (n_samples, n_features)
  • y — целевая переменная размера (n_samples,)
  • Мини-пайплайн: от CSV к тензорам PyTorch

    Ниже пример сквозной подготовки: чтение, чистка, кодирование, базовая стандартизация, конвертация в тензоры.

    Что важно в этом примере:

  • Мы контролируем dtype, потому что в ML это влияет и на корректность, и на скорость.
  • Пропуски обрабатываем до конвертации в NumPy/тензоры.
  • Категории превращаем в числовые колонки.
  • Производительность и надежность: практические правила

  • Старайтесь писать преобразования векторно (через pandas/NumPy), а не for по строкам.
  • Явно проверяйте размеры X и y после каждого крупного шага.
  • Всегда смотрите на df.dtypes, особенно если видите object.
  • Для выбора строк и колонок используйте loc/iloc.
  • После merge проверяйте число строк и уникальность ключей соединения.
  • Итоги

  • pandas отвечает за удобную и безопасную подготовку табличных данных: пропуски, типы, объединения, группировки.
  • NumPy — за быстрые численные представления и операции над матрицами признаков.
  • Хороший ML-пайплайн почти всегда строится как переход: сырье → DataFrame → чистые числовые признаки → NumPy → тензоры PyTorch.
  • В следующей части курса логично перейти от подготовки данных к матстатистическим базовым понятиям, которые помогают принимать решения на данных: распределения, оценки, дисперсия, доверительные интервалы и проверка гипотез.

    2. Математическая статистика для ML: распределения, оценки, проверка гипотез

    Математическая статистика для ML: распределения, оценки, проверка гипотез

    Зачем матстатистика в машинном обучении

    В предыдущей статье мы научились превращать сырой датасет в числовые признаки с помощью pandas и NumPy и готовить тензоры для обучения. Но дальше неизбежно возникает вопрос: насколько можно доверять выводам из данных.

    Математическая статистика нужна в ML, чтобы:

  • понимать, как данные могли получиться из некоторого случайного процесса
  • оценивать неизвестные характеристики этого процесса (например, средний чек)
  • сравнивать варианты (A/B-тест, две модели, два источника данных)
  • отличать шум от реального эффекта
  • строить доверительные интервалы, а не опираться на одну точечную оценку
  • Полезные источники:

  • e-Handbook of Statistical Methods (NIST)
  • Документация SciPy: scipy.stats
  • Случайная величина и распределение

    Случайная величина

    Случайная величина — это число, которое получается в результате случайного эксперимента.

    Примеры в ML и продуктовой аналитике:

  • число покупок пользователя за неделю
  • время до следующего события
  • ошибка модели на случайном объекте
  • бинарный таргет (купил или нет)
  • Распределение

    Распределение описывает, какие значения случайная величина принимает и как часто.

    Два наиболее частых типа:

  • дискретное распределение — значения из набора (например, )
  • непрерывное распределение — значения из интервала (например, любое число на отрезке)
  • Параметры распределения: среднее и дисперсия

    Самые используемые характеристики:

  • математическое ожидание (в быту — среднее)
  • дисперсия (разброс) и стандартное отклонение
  • Если обозначить случайную величину как , то запись означает:

  • — случайная величина
  • — операция взятия среднего “в среднем по бесконечному числу повторов”
  • — истинное среднее значение процесса
  • Дисперсия записывается так:

    Где:

  • — случайная величина
  • — её истинное среднее
  • — квадрат отклонения от среднего (квадрат нужен, чтобы отклонения не взаимно уничтожались и сильные выбросы сильнее влияли)
  • — усреднение
  • — истинная дисперсия
  • !Иллюстрация того, что среднее может быть одинаковым, но разброс разным

    Выборка и почему статистики “плавают”

    Генеральная совокупность и выборка

  • Генеральная совокупность — весь процесс/мир, который мы хотим описать (например, все будущие пользователи).
  • Выборка — доступные нам наблюдения (например, пользователи за прошлую неделю).
  • В ML мы почти всегда работаем с выборкой, поэтому любые числа (средний чек, доля конверсии, средняя ошибка) — это оценки, а не истина.

    Выборочное среднее

    Пусть у нас есть выборка .

    Выборочное среднее:

    Где:

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

    Интуитивно важные идеи:

  • закон больших чисел: при росте выборочное среднее стремится к истинному среднему
  • центральная предельная теорема: при достаточно большом распределение часто близко к нормальному, даже если исходные данные не нормальные (при стандартных условиях)
  • Практический вывод для ML: если вы оценили метрику модели на малом числе объектов, она может сильно “плавать”, и сравнение моделей становится ненадежным.

    Оценки параметров: точечные и интервальные

    Оценка и оцениватель

  • оцениватель — правило/формула, которое по данным считает число
  • оценка — конкретное число, полученное на конкретной выборке
  • Например, формула выборочного среднего — оцениватель, а число на ваших данных — оценка.

    Несмещенность и дисперсия оценивателя

    Качества оценивателя, которые часто обсуждают:

  • несмещенность: в среднем оценка попадает в истинное значение
  • дисперсия оценивателя: насколько оценка “шумная” от выборки к выборке
  • Это важно в ML-мышлении, потому что напоминает компромисс смещение-дисперсия в моделях: вы хотите, чтобы вывод был и “в среднем правильным”, и “стабильным”.

    Доверительный интервал

    Доверительный интервал — диапазон значений параметра, совместимый с данными.

    Интуиция: вместо “средняя конверсия 3.2%” полезнее “средняя конверсия примерно 3.2%, и мы ожидаем, что реальное значение лежит в диапазоне …”.

    Технически часто выбирают уровень доверия (например, ). Тогда:

  • — уровень значимости (например, )
  • — уровень доверия (например, )
  • Важное уточнение: 95% доверительный интервал не означает “вероятность 95%, что параметр внутри интервала” для уже посчитанного интервала. Это означает, что процедура построения интервала при многократном повторении даст интервал, содержащий истинный параметр, примерно в 95% случаев.

    Bootstrap как универсальный способ получить интервал

    Если не хочется опираться на формулы и допущения, часто используют bootstrap:

  • многократно выбираем из имеющейся выборки новые выборки с возвращением
  • считаем интересующую статистику (например, среднее или метрику модели)
  • берём квантили полученного распределения как границы интервала
  • Пример (интервал для среднего через bootstrap):

    Проверка гипотез: как формализовать “есть ли эффект?”

    Нулевая и альтернативная гипотезы

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

  • нулевая гипотеза : эффекта нет, различий нет (например, конверсии равны)
  • альтернативная гипотеза : эффект есть (например, конверсии различаются)
  • Пример A/B:

  • : средняя конверсия в A равна средней конверсии в B
  • : средние конверсии различаются
  • Ошибки первого и второго рода

    Решение по гипотезе может быть ошибочным.

  • ошибка первого рода: отклонить , хотя она верна (ложноположительный эффект)
  • ошибка второго рода: не отклонить , хотя верна (пропустить реальный эффект)
  • Уровень значимости обычно контролирует вероятность ошибки первого рода.

    p-value

    p-value — это мера того, насколько наблюдаемые (или более “экстремальные”) данные были бы редкими, если верна.

    Если , часто говорят “отклоняем ” на уровне значимости .

    Типичная ошибка интерпретации: — это не “вероятность, что верна”.

    !Визуальная интуиция p-value как площади в хвосте распределения при H0

    Частые тесты, которые реально встречаются рядом с ML

    Ниже не “полный список”, а практический минимум, чтобы ориентироваться.

    t-тест: сравнение средних

    Когда используют:

  • хотим сравнить среднее значение метрики/показателя в двух группах
  • пример: средний чек в A и B
  • Предпосылки (упрощенно):

  • наблюдения независимы
  • распределения не слишком “патологические” (или выборка достаточно большая)
  • В Python часто используют реализацию из SciPy.

    Тест для долей (конверсий)

    Когда таргет бинарный (конверсия), сравнивают доли.

    На практике часто используют:

  • приближения через нормальное распределение
  • или бутстрэп/пермутационные тесты
  • Хи-квадрат тест независимости: категориальные признаки

    Когда используют:

  • есть таблица сопряженности (например, “кликнул/не кликнул” и “группа A/B”)
  • проверяем связь между двумя категориальными переменными
  • Пермутационный тест: меньше предположений

    Идея:

  • если верна (разницы нет), то метки групп можно “перемешивать” между наблюдениями
  • сравниваем наблюдаемую разницу с распределением разниц при случайных перемешиваниях
  • Часто полезно для метрик моделей, которые сложно анализировать формулами.

    Размер эффекта: почему одной значимости недостаточно

    Статистическая значимость не равна практической полезности.

  • На огромной выборке микроскопическое отличие даст маленький .
  • На маленькой выборке полезный эффект может быть “незначимым” из-за большого шума.
  • Поэтому вместе с тестом обычно смотрят:

  • разницу в абсолютных единицах (например, +0.2% конверсии)
  • относительную разницу (например, +5% относительно базы)
  • доверительный интервал для эффекта
  • Множественные проверки: ловушка “нашли что-то значимое”

    Если вы проверяете десятки гипотез (много фичей, много сегментов, много метрик), то даже при отсутствии эффектов какие-то тесты случайно окажутся значимыми.

    Базовые способы снизить риск:

  • заранее фиксировать список гипотез и метрик
  • корректировать уровень значимости (например, метод Бонферрони)
  • разделять анализ на исследовательский и подтверждающий
  • Как это связано с пайплайном данных и PyTorch

    Статистика встраивается в ML-процесс на нескольких этапах:

  • подготовка данных: оценка долей пропусков, устойчивые статистики, сравнение распределений train и test
  • обучение и валидация: доверительные интервалы для метрик (bootstrap по объектам), проверка стабильности качества
  • A/B и выкатывание: тестирование изменений модели или фичей в онлайне, контроль ошибок первого рода
  • Практическое правило: если вы построили признаки в pandas/NumPy и получили метрику модели, следующий вопрос должен быть не “сколько получилось”, а “насколько это число надежно”.

    Мини-пример: bootstrap-интервал для метрики

    Допустим, есть ошибки модели на объектах (например, абсолютная ошибка), и мы хотим интервал для среднего значения ошибки.

    Такой подход переносится и на более сложные метрики (AUC, F1), хотя там нужно аккуратно выбирать схему ресэмплинга.

    Итоги

  • распределение описывает, как устроена случайность в данных
  • выборочные статистики (среднее, дисперсия, доля) — это оценки, и они имеют разброс
  • доверительные интервалы дают диапазон правдоподобных значений и делают выводы надежнее
  • проверка гипотез формализует сравнение групп через , , и уровень значимости
  • в ML статистика нужна не “для галочки”, а чтобы корректно сравнивать модели, измерять эффект изменений и не переобучаться на шум
  • 3. Классическое ML: признаки, регуляризация, метрики и валидация

    Классическое ML: признаки, регуляризация, метрики и валидация

    Как эта тема связана с предыдущими статьями

    В прошлых частях курса мы закрыли два фундамента:

  • pandas и NumPy: как превратить сырые таблицы в чистую матрицу признаков и целевой вектор .
  • матстатистика: почему оценки на выборке “плавают”, что такое доверительные интервалы и проверка гипотез.
  • Теперь соберём это в классический ML-пайплайн: построение признаков, контроль переобучения через регуляризацию, выбор метрик и корректная валидация.

    !Сквозная схема классического ML-процесса

    Что такое признаки и почему “качество данных” часто важнее модели

    Признак — это численное описание объекта, которое модель использует для предсказания.

  • Если у вас таблица пользователей, то объект — пользователь, а признаки — возраст, город, число покупок за месяц.
  • Если у вас таблица транзакций, то объект может быть транзакцией, а признаки — сумма, категория, час дня.
  • В классическом ML часто работает правило: простая модель + хорошие признаки сильнее, чем сложная модель + плохие признаки.

    Типы признаков и базовые преобразования

    | Тип исходного столбца | Пример | Типичная подготовка | Риск ошибки | |---|---|---|---| | Числовой | income | заполнение пропусков, масштабирование | выбросы и разные масштабы | | Категориальный | city | one-hot или target encoding | утечка таргета при неправильном кодировании | | Дата/время | timestamp | извлечение hour, dayofweek, лаги | утечка “из будущего” | | Текст | review | мешок слов, TF-IDF, эмбеддинги | слишком высокая размерность |

    Масштабирование: когда оно нужно

    Многие модели чувствительны к масштабу признаков:

  • линейные модели с регуляризацией
  • методы на расстояниях, например kNN
  • методы оптимизации градиентом
  • Частый выбор для чисел — стандартизация:

    Где:

  • — исходное значение признака
  • — среднее значение признака на обучающей выборке
  • — стандартное отклонение признака на обучающей выборке
  • — стандартизированное значение
  • Ключевое практическое правило: и считаются только на train, а затем применяются к val/test, иначе получится утечка.

    Категории: one-hot и проблема согласованности колонок

    При one-hot кодировании категориальный столбец превращается в набор бинарных колонок.

    Риски:

  • на train и test могут появиться разные наборы категорий
  • итоговая матрица признаков должна иметь одинаковую структуру
  • В индустрии это часто решают через пайплайны преобразований.

    Утечка данных: главная причина “слишком хороших” результатов

    Утечка (data leakage) — это ситуация, когда в признаках или процедуре обучения оказывается информация, которая недоступна в момент реального предсказания.

    Типичные источники утечки:

  • признаки “из будущего” в задачах по времени (например, агрегат по покупкам за период, который включает дату после предсказания)
  • предобработка на всех данных до разбиения (например, стандартизация или заполнение пропусков, посчитанные на всей таблице)
  • использование таргета в кодировании категорий без строгой схемы train-only
  • Практический тест на утечку:

  • спросите себя: могу ли я вычислить этот признак в момент применения модели?
  • Регуляризация: как бороться с переобучением

    Переобучение в терминах ошибок

    У модели есть два вида качества:

  • качество на train
  • качество на новых данных
  • Если качество на train отличное, а на новых данных резко хуже, это часто означает переобучение.

    !Интуиция компромисса между недообучением и переобучением

    Регуляризация добавляет к обучению “штраф” за слишком большие веса и тем самым снижает склонность модели подгонять шум.

    L2-регуляризация (Ridge): штраф за большие веса

    Для линейной регрессии часто оптимизируют сумму ошибки и штрафа:

    Где:

  • — число объектов в обучении
  • — истинный ответ для объекта
  • — предсказание модели для объекта
  • — число признаков
  • — вес (коэффициент) при признаке
  • — сила регуляризации: чем больше, тем сильнее штраф
  • Интуиция: модель может использовать все признаки, но ей “дорого” делать веса большими.

    L1-регуляризация (Lasso): разреженные решения

    L1-регуляризация использует штраф по абсолютным значениям весов:

    Где:

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

    Что регуляризация не решает

    Регуляризация не заменяет:

  • корректную валидацию
  • борьбу с утечками
  • осмысленный feature engineering
  • Если есть утечка, регуляризация лишь сделает “нечестный результат” чуть менее экстремальным.

    Метрики: что именно считать “качеством”

    Метрика зависит от типа задачи и бизнес-цели.

    Регрессия

    Частые метрики:

  • MAE (средняя абсолютная ошибка):
  • MSE (средняя квадратичная ошибка):
  • Обозначения:

  • — число объектов
  • — истинное значение
  • — предсказание
  • Практические различия:

  • MAE устойчивее к выбросам
  • MSE сильнее штрафует большие ошибки
  • Классификация

    #### Матрица ошибок и базовые величины

    Для бинарной классификации полезно мыслить через:

  • TP: предсказали 1 и правда 1
  • FP: предсказали 1, но правда 0
  • TN: предсказали 0 и правда 0
  • FN: предсказали 0, но правда 1
  • !Матрица ошибок и смысл типов ошибок

    #### Accuracy

    Accuracy может быть опасной при дисбалансе классов: если 99% объектов — класс 0, то модель “всегда 0” даст 99% accuracy и будет бесполезной.

    #### Precision, Recall, F1

    Обозначения:

  • — как выше
  • Когда что важнее:

  • важнее не тревожить пользователей лишний раз: оптимизируют precision
  • важнее не пропускать события: оптимизируют recall
  • нужен баланс: используют F1
  • #### ROC-AUC и PR-AUC

    Если модель выдаёт вероятности, то можно оценивать качество ранжирования:

  • ROC-AUC хорошо показывает общую способность отделять классы
  • PR-AUC часто информативнее при сильном дисбалансе, когда положительный класс редкий
  • Полезные источники по метрикам:

  • Документация scikit-learn по метрикам
  • Валидация: как честно оценить модель и выбрать гиперпараметры

    Разбиение на train, validation, test

    Типовая схема:

  • train: обучаем модель и считаем параметры преобразований
  • validation: выбираем гиперпараметры, сравниваем подходы
  • test: финальная оценка один раз, как имитация “продакшена”
  • Если вы много раз “смотрите” на test и подбираете решения, test перестаёт быть test.

    K-fold кросс-валидация

    K-fold CV уменьшает зависимость оценки от конкретного разбиения:

  • данные делятся на частей (фолдов)
  • модель обучается раз, каждый раз один фолд становится валидацией
  • итоговая оценка усредняется
  • !Как работает k-fold кросс-валидация

    Полезный источник:

  • Документация scikit-learn по кросс-валидации
  • Стратификация

    В классификации часто важно, чтобы доля классов была похожа в train и val. Тогда используют stratified split.

    Временные данные: нельзя перемешивать прошлое с будущим

    Если данные зависят от времени, то случайный shuffle часто создаёт утечку.

    Правило:

  • обучаемся на прошлом
  • валидируемся на будущем
  • Вместо обычного K-fold применяют схемы вроде TimeSeriesSplit.

    Пайплайн преобразований: защита от утечек

    Ключевая идея: все преобразования должны “учиться” только на train внутри каждой валидационной итерации.

    На практике это удобно делать пайплайном.

    Ниже пример для табличных данных: числовые признаки стандартизируем, категориальные кодируем one-hot, модель — логистическая регрессия с регуляризацией.

    Полезные источники:

  • Документация scikit-learn по Pipeline
  • Документация scikit-learn по ColumnTransformer
  • Как матстатистика “встроена” в валидацию

    Валидационная метрика — это статистическая оценка, которая зависит от конкретной выборки.

    Практичные выводы из матстатистики:

  • одну точку качества полезно дополнять разбросом по фолдам (например, среднее и стандартное отклонение)
  • для финальной модели можно оценивать доверительный интервал метрики через bootstrap по объектам
  • маленькая разница метрик без интервала может быть просто шумом
  • Итоги

  • Признаки — центральная часть классического ML, а утечки данных способны полностью “сломать” оценку.
  • Регуляризация (L1/L2) помогает бороться с переобучением и делает модели устойчивее.
  • Метрика должна соответствовать задаче: accuracy не спасает при дисбалансе, а вероятностные метрики оценивают ранжирование.
  • Корректная валидация и пайплайны преобразований нужны, чтобы оценка качества была честной и воспроизводимой.
  • Дальше в курсе эту логику удобно перенести в PyTorch: там мы будем явно управлять тензорами, функциями потерь, оптимизацией и обучением моделей, но принципы признаков, метрик, регуляризации и валидации останутся теми же.

    4. PyTorch: тензоры, автодифференцирование и обучение нейросетей

    PyTorch: тензоры, автодифференцирование и обучение нейросетей

    Как эта тема продолжает курс

    В прошлых статьях мы построили фундамент ML-пайплайна:

  • pandas и NumPy: как из сырых таблиц получить чистые числовые признаки и таргет .
  • матстатистика: почему оценки метрик на выборке “плавают”, как мыслить доверительными интервалами и честно сравнивать варианты.
  • классическое ML: признаки, регуляризация, метрики и корректная валидация.
  • PyTorch — это следующий шаг: вместо готовых моделей мы начинаем явно управлять тем, как считается предсказание, как считается ошибка и как обновляются параметры. При этом принципы остаются прежними:

  • данные готовим в pandas/NumPy, затем превращаем в тензоры
  • качество считаем метриками и валидируем честно
  • избегаем утечек и следим за переобучением
  • !Общая картина: как данные превращаются в тензоры и проходят через обучение

    Полезные первоисточники:

  • Документация PyTorch
  • Раздел про autograd
  • Раздел про nn.Module
  • Torchvision (датасеты и примеры)
  • Тензоры: главный тип данных в PyTorch

    Что такое тензор

    Тензор в PyTorch — это многомерный массив, очень похожий на numpy.ndarray, но с важными отличиями:

  • может жить на CPU или GPU
  • может хранить информацию для автодифференцирования (градиентов)
  • является базовой единицей для построения вычислительных графов
  • Практическая аналогия:

  • в NumPy вы обычно “просто считаете числа”
  • в PyTorch вы считаете числа так, чтобы потом можно было автоматически посчитать производные и обновить параметры модели
  • Форма, типы и устройство

    У тензора почти всегда интересуют три вещи:

  • форма shape (например, (n_samples, n_features))
  • тип данных dtype (например, torch.float32)
  • устройство device (cpu или cuda)
  • Пример создания и проверки свойств:

    От NumPy к PyTorch и обратно

    В первой статье курса мы получали X и y как NumPy-массивы. Переход в PyTorch обычно выглядит так:

    Важные правила:

  • torch.from_numpy обычно не копирует данные: NumPy-массив и тензор могут ссылаться на одну память. Это ускоряет работу, но требует осторожности при изменениях.
  • для нейросетей входы почти всегда float32, а:
  • - таргет для регрессии чаще float32 - таргет для классификации (класс-индекс) чаще int64

    GPU: когда и как переносить тензоры

    Если есть CUDA-совместимая видеокарта и установлен PyTorch с поддержкой CUDA, вы можете ускорить обучение.

    Ключевое практическое правило:

  • модель и все тензоры, участвующие в вычислениях, должны быть на одном device
  • Автодифференцирование: как PyTorch считает градиенты

    Зачем вообще нужны градиенты

    Обучение большинства нейросетей — это минимизация функции потерь.

    Пусть:

  • — предсказания модели
  • — истинные ответы
  • — функция потерь (ошибка)
  • — параметры модели (веса)
  • Тогда обучение пытается подобрать параметры так, чтобы ошибка была маленькой:

    Здесь:

  • — входные признаки
  • — настраиваемые параметры
  • — предсказание, зависящее от и
  • — скалярная функция, которую мы минимизируем
  • Чтобы “понимать, куда двигать веса”, обычно используют градиентный спуск: параметры сдвигают в сторону, уменьшающую потерю.

    requires_grad и вычислительный граф

    PyTorch умеет строить вычислительный граф операций и автоматически находить производные.

    Что здесь происходит:

  • w помечен как параметр, по которому нужен градиент
  • loss.backward() запускает обратное распространение
  • w.grad хранит производную потерь по w
  • Почему надо обнулять градиенты

    По умолчанию PyTorch накапливает градиенты в .grad.

    Поэтому типичный шаблон обучения выглядит так:

  • optimizer.zero_grad()
  • посчитать loss
  • loss.backward()
  • optimizer.step()
  • Минимальный цикл обучения: “ручной” вариант

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

    Пояснения к ключевым элементам:

  • nn.Linear(a, b) — линейный слой, переводит вектор размера a в размер b
  • nn.ReLU() — нелинейность, без неё сеть из линейных слоёв схлопнется в одну линейную модель
  • logits — “сырые” выходы до вероятностей
  • nn.CrossEntropyLoss() ожидает:
  • - вход: logits размера (batch_size, n_classes) - таргет: индексы классов размера (batch_size,) типа int64
  • Adam — популярный адаптивный оптимизатор
  • nn.Module: как устроены модели в PyTorch

    Зачем нужен nn.Module

    nn.Module — это базовый класс для моделей. Он:

  • хранит параметры
  • умеет переключаться между режимами train() и eval()
  • позволяет удобно сохранять/загружать веса
  • Пример собственной модели

    Что важно:

  • всё, что вы хотите обучать, должно быть зарегистрировано как часть nn.Module (например, через self.layer = nn.Linear(...))
  • метод forward описывает, как считаются выходы
  • Dataset и DataLoader: обучение батчами

    Зачем нужны батчи

    Вместо того чтобы считать градиенты на всей выборке сразу, обычно используют мини-батчи:

  • быстрее и экономнее по памяти
  • добавляют полезный “шум” в градиенты, что иногда улучшает обобщение
  • TensorDataset и DataLoader

    shuffle=True обычно включают для train, но часто выключают для val/test.

    Полный шаблон: train и validation без утечек

    Ниже — пример “как делать правильно”: отдельная функция обучения и отдельная функция оценки.

    Почему это согласуется с логикой курса:

  • валидация отделена от обучения, как в статье про классическое ML
  • при оценке мы используем model.eval() и @torch.no_grad()
  • качество (например, accuracy) — это оценка на выборке, которая может иметь разброс, как в статье про матстатистику
  • train() и eval(): почему это важно

    Некоторые слои ведут себя по-разному в обучении и в инференсе, например:

  • nn.Dropout
  • nn.BatchNorm*
  • Поэтому правило:

  • на обучении: model.train()
  • на оценке и инференсе: model.eval()
  • И отдельно:

  • torch.no_grad() отключает построение графа и экономит память/время при валидации
  • Функции потерь: как выбрать под задачу

    Табличный минимум:

    | Тип задачи | Выход модели | Типичная функция потерь | Что подать как таргет | |---|---|---|---| | Регрессия | одно число на объект | nn.MSELoss() или nn.L1Loss() | вещественное значение (float32) | | Бинарная классификация (2 класса) | 2 logits или 1 logit | nn.CrossEntropyLoss() или nn.BCEWithLogitsLoss() | для CE: индекс класса (int64), для BCE: 0/1 (float32) | | Мультикласс | n_classes logits | nn.CrossEntropyLoss() | индекс класса (int64) |

    Практическое правило, чтобы избежать ошибок:

  • если используете nn.CrossEntropyLoss(), не применяйте softmax вручную: функция сама корректно работает с logits
  • Регуляризация и переобучение в PyTorch

    Из статьи про классическое ML вы уже знаете идею регуляризации. В PyTorch есть несколько типовых инструментов:

  • L2-регуляризация через weight_decay в оптимизаторе
  • Dropout как регуляризующий слой
  • ранняя остановка: остановить обучение, если качество на val перестало улучшаться
  • Пример L2 через weight_decay:

    Смысл: большие веса становятся “дороже”, и модель меньше подгоняет шум.

    Воспроизводимость: сиды и детерминизм

    Чтобы эксперименты было проще сравнивать:

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

    Сохранение и загрузка модели

    Чаще всего сохраняют веса (state dict):

    Это удобно, потому что архитектура задаётся кодом, а веса — файлом.

    Частые ошибки новичков и быстрые проверки

  • Несовпадение dtype таргета и функции потерь
  • Модель на cuda, а батч на cpu (или наоборот)
  • Забыли optimizer.zero_grad() и градиенты накапливаются
  • Считают метрики в режиме train() и получают “плавающее” качество из-за Dropout/BatchNorm
  • Случайно делают предобработку “на всех данных” до разбиения и получают утечку
  • Практичные проверки:

  • после формирования батча печатать Xb.shape, Xb.dtype, Xb.device
  • проверять, что loss — скаляр
  • проверять, что logits.shape и yb.shape соответствуют ожидаемому формату функции потерь
  • Итоги

  • Тензоры — базовый контейнер данных в PyTorch, важны shape, dtype, device.
  • Autograd автоматически считает градиенты, если параметры помечены requires_grad=True.
  • Цикл обучения почти всегда: zero_grad → forward → loss → backward → step.
  • Dataset/DataLoader дают обучение батчами и масштабируемость.
  • Разделяйте train() и eval(), а валидацию делайте с torch.no_grad().
  • Идеи метрик, валидации и регуляризации из классического ML напрямую переносятся на нейросети.
  • 5. Практика и продвинутые темы: DataLoader, GPU, дебаг, воспроизводимость

    Практика и продвинутые темы: DataLoader, GPU, дебаг, воспроизводимость

    Как эта тема завершает базовый контур курса

    В предыдущих статьях вы уже умеете:

  • готовить данные в pandas/NumPy и превращать их в тензоры
  • мыслить статистически про качество и разброс метрик
  • строить честную валидацию и понимать переобучение
  • обучать модели в PyTorch через цикл zero_grad → forward → loss → backward → step
  • Эта статья про то, что обычно отделяет учебный код от рабочего:

  • эффективная подача данных через Dataset и DataLoader
  • ускорение и типичные ловушки GPU
  • практические техники дебага обучения
  • воспроизводимость экспериментов
  • !Общая карта процесса обучения и типичных мест, где возникают ошибки

    Полезные источники:

  • Документация PyTorch: DataLoader
  • Документация PyTorch: CUDA semantics
  • Документация PyTorch: Automatic Mixed Precision
  • Документация PyTorch: Reproducibility
  • DataLoader на практике: как подавать данные быстро и корректно

    Dataset: что именно должен вернуть один элемент

    Dataset отвечает за то, как по индексу получить один объект обучения.

    Самый частый интерфейс:

  • __len__ возвращает число объектов
  • __getitem__(idx) возвращает один элемент, обычно (X, y)
  • Пример простого map-style датасета для табличных данных, которые уже подготовлены в NumPy:

    Практические правила:

  • возвращайте тензоры согласованных dtype и форм
  • если таргет для nn.CrossEntropyLoss(), то y должен быть типа torch.int64 и формы (batch,)
  • если в __getitem__ есть сложная логика, сначала отладьте её с num_workers=0
  • collate_fn: когда элементы не складываются в прямоугольный батч

    По умолчанию DataLoader пытается “стекнуть” элементы в батч. Это ломается, если объекты разной длины.

    Типичный случай: последовательности разной длины. Тогда пишут collate_fn, который:

  • паддит последовательности до одной длины
  • собирает маски
  • возвращает батч в удобном формате
  • Мини-пример: паддинг списка 1D-тензоров до одинаковой длины.

    Производительность DataLoader: основные ручки

    Главные параметры, которые реально влияют на скорость:

  • batch_size уменьшает относительные накладные расходы, но увеличивает память
  • num_workers включает параллельную загрузку и препроцессинг на CPU
  • pin_memory=True ускоряет передачу батчей CPU → GPU
  • persistent_workers=True не пересоздаёт воркеры каждый эпох
  • prefetch_factor управляет тем, сколько батчей заранее готовят воркеры
  • Пример типичной настройки для обучения на GPU:

    Практические замечания:

  • если препроцессинг лёгкий, слишком большой num_workers может даже замедлить из-за overhead
  • если вы обучаетесь на CPU, pin_memory обычно не нужен
  • при отладке почти всегда начинайте с num_workers=0, чтобы видеть нормальный traceback
  • Важная тема: случайность shuffle и семена в DataLoader

    Если вы хотите воспроизводимость, важно контролировать не только torch.manual_seed, но и:

  • генератор для DataLoader (влияет на shuffle)
  • инициализацию воркеров (иначе у каждого воркера могут быть “одинаковые” случайные последовательности для аугментаций)
  • Мини-шаблон:

    GPU: ускоряемся и не ломаем обучение

    Базовое правило: модель и данные на одном device

    Частая ошибка: модель на cuda, а батч на cpu.

    Типичный шаблон:

    non_blocking=True имеет смысл в основном вместе с pin_memory=True у DataLoader.

    Как понимать, что вы упёрлись не в GPU, а в загрузку данных

    Если GPU простаивает, часто причина не в модели, а в том, что батчи не успевают готовиться.

    Признаки:

  • загрузка GPU маленькая, но CPU загружен
  • ускорение почти не растёт при увеличении размера модели
  • время итерации сильно зависит от num_workers
  • Быстрый подход:

  • временно поставьте num_workers=0 и измерьте время
  • затем увеличивайте num_workers, пока время не перестанет улучшаться
  • Для честных таймингов на GPU учитывайте асинхронность:

    Mixed precision: быстрее и экономнее по памяти

    На современных GPU обучение часто ускоряют через mixed precision: часть вычислений идёт в float16 или bfloat16.

    В PyTorch стандартный путь: autocast и GradScaler.

    Практические правила:

  • сравнивайте качество с full precision, особенно на нестабильных задачах
  • если появились NaN, сначала отключите AMP и проверьте, воспроизводится ли проблема
  • Gradient accumulation: когда не хватает памяти на большой batch

    Если “нужен большой batch”, но GPU не помещает, применяют накопление градиентов.

    Эффективный размер батча:

    Где:

  • это эффективный размер батча, который “видит” оптимизатор
  • это реальный batch_size, который помещается в память
  • это число шагов накопления, после которых делается optimizer.step()
  • Шаблон:

    Ключевая идея: делим loss на accum_steps, чтобы общий масштаб градиента был сопоставим с обучением на большом батче.

    Дебаг обучения: быстрые техники, которые экономят часы

    Санити-чек: “переобучи один батч”

    Если модель и пайплайн корректны, она почти всегда способна переобучить один маленький батч.

    Алгоритм:

  • возьмите 1 батч данных
  • отключите всё лишнее
  • обучайте 200-500 шагов
  • убедитесь, что loss падает почти до нуля (для задач, где это ожидаемо)
  • Если не падает, ищите ошибки в:

  • таргете и функции потерь
  • dtype таргета
  • нормализации признаков
  • размере выходного слоя
  • Самые частые ошибки: shape, dtype, device

    Практика: перед loss печатайте инварианты одного батча.

    Напоминание для nn.CrossEntropyLoss():

  • logits форма (batch, n_classes), тип float32 или float16
  • y форма (batch,), тип int64, значения от 0 до n_classes-1
  • NaN и взрывы градиентов: как ловить и чинить

    Минимальный набор инструментов:

  • проверять loss на NaN и Inf
  • включать поиск проблемной операции через anomaly detection
  • клиппинг градиентов
  • Если NaN появляется только на GPU, проверьте:

  • AMP включён или выключен
  • нет ли деления на ноль в препроцессинге
  • нет ли экстремальных значений в признаках
  • Дебаг DataLoader: как локализовать “падает внутри воркера”

    Если ошибка выглядит как “Caught RuntimeError in DataLoader worker process”, действуйте так:

  • поставьте num_workers=0, чтобы получить нормальный traceback
  • прогоните несколько __getitem__ вручную и проверьте типы
  • временно уберите случайные аугментации и проверьте, не они ли ломают данные
  • добавьте проверки и понятные ошибки внутри __getitem__
  • Воспроизводимость: что реально можно гарантировать

    Что значит “воспроизводимо” в ML

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

  • метрики получались одинаковыми или почти одинаковыми
  • сохранялась возможность сравнивать эксперименты честно
  • Но важно понимать: на GPU полная детерминированность не всегда бесплатна по скорости и не всегда возможна для всех операций. Официальные детали и ограничения собраны в документации PyTorch по воспроизводимости.

    Минимальный набор: зафиксировать все основные источники случайности

    Дальше добавьте контроль DataLoader (пример был выше): generator и worker_init_fn.

    Детерминизм в PyTorch: когда он нужен и чем платим

    Если вам критично получить одинаковые результаты, используйте:

    Практический смысл:

  • benchmark=False запрещает cuDNN подбирать “самый быстрый” алгоритм, который может быть недетерминированным
  • deterministic=True заставляет выбирать детерминированные реализации, если они есть
  • use_deterministic_algorithms(True) просит PyTorch выбрасывать ошибку, если детерминированного пути нет
  • Что сохранять, чтобы эксперимент можно было повторить

    Минимальный чеклист:

  • код модели и препроцессинга (желательно с commit hash)
  • гиперпараметры обучения
  • state_dict модели
  • state_dict оптимизатора
  • все seed и флаги детерминизма
  • версию PyTorch и CUDA
  • Шаблон сохранения:

    Практический чеклист перед тем, как верить метрике

  • данные разбиты без утечек, все преобразования fit делались только на train
  • Dataset возвращает правильные dtype и формы
  • DataLoader настроен так, чтобы GPU не простаивал
  • включён санити-чек “переобучи один батч”
  • есть baseline без AMP, если используете mixed precision
  • зафиксированы seed и параметры воспроизводимости
  • !Короткий маршрут дебага типичных проблем обучения

    Итоги

  • Dataset определяет, как получить один объект, а DataLoader как собрать батчи быстро и воспроизводимо.
  • Для GPU важны не только .to(device), но и скорость подачи данных, pinned memory и корректные тайминги.
  • Mixed precision и gradient accumulation помогают ускорять и укладываться в память, но требуют проверок.
  • Дебаг нейросетей почти всегда начинается с проверок shape/dtype/device и теста “переобучи один батч”.
  • Воспроизводимость это набор дисциплин: сиды, контроль DataLoader, настройки детерминизма, и полноценные чекпоинты.