Фаза 02 · урок 13

ML-пайплайны

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

Текущий релиз AlexBred.com: первые 100 уроков русскоязычной программы.

Курс
AI Engineering from Scratch
Фаза
Основы машинного обучения
Чтение
12 мин.
Проверено
Содержание урока
  1. Цели обучения
  2. Проблема
  3. Концепция
  4. Что такое пайплайн
  5. Утечка данных: тихий убийца
  6. sklearn Pipeline
  7. ColumnTransformer: разные пайплайны для разных столбцов
  8. Отслеживание экспериментов
  9. Версионирование моделей
  10. Версионирование данных с DVC
  11. Воспроизводимые эксперименты
  12. От ноутбука к продакшен-пайплайну
  13. Распространённые ошибки в пайплайнах
  14. Соберите это
  15. Шаг 1: пользовательский преобразователь
  16. Шаг 2: пайплайн с нуля
  17. Шаг 3: кросс-валидация с пайплайном
  18. Шаг 4: полный продакшен-пайплайн со sklearn
  19. Внедрите это
  20. Упражнения
  21. Ключевые термины
  22. Дополнительное чтение

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

Тип: Сборка Язык: Python Предварительные требования: Фаза 2, урок 12 (Настройка гиперпараметров) Время: ~120 минут

Цели обучения

  • Собрать с нуля ML-пайплайн, который объединяет заполнение пропусков, масштабирование, кодирование и обучение модели в один воспроизводимый объект
  • Выявлять сценарии утечки данных и объяснять, как пайплайны предотвращают их, подгоняя преобразователи только на обучающих данных
  • Построить ColumnTransformer, применяющий разную предобработку к числовым и категориальным признакам
  • Реализовать сериализацию пайплайна и показать, что один и тот же обученный пайплайн даёт идентичные результаты при обучении и в продакшене

Проблема

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

Через месяц кто-то переобучает модель и получает другие результаты. Медиана была вычислена по полному набору данных, включая тестовые данные (утечка данных). Параметры масштабирования не сохранили, поэтому при инференсе используется другая статистика. Код проектирования признаков был скопирован между обучением и обслуживанием, и копии разошлись. В категориальном столбце в продакшене появилось новое значение, которого кодировщик никогда не видел.

Это не гипотетические случаи. Это самые распространённые причины, по которым ML-системы отказывают в продакшене. Пайплайны решают все эти проблемы, упаковывая каждый шаг преобразования в единый упорядоченный воспроизводимый объект.

Концепция

Что такое пайплайн

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

Диаграмма к уроку «ML-пайплайны»

Пайплайн гарантирует:

  • Преобразования подгоняются только на обучающих данных (нет утечки)
  • При инференсе применяются те же преобразования
  • Весь объект можно сериализовать и развернуть как один артефакт
  • Кросс-валидация применяет пайплайн к каждому фолду, предотвращая трудноуловимые утечки

Утечка данных: тихий убийца

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

С утечкой (неверно):

X = df.drop("target", axis=1)
y = df["target"]

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

X_train, X_test = X_scaled[:800], X_scaled[800:]
y_train, y_test = y[:800], y[800:]

Масштабировщик увидел тестовые данные. Среднее и стандартное отклонение включают тестовые образцы. Это завышает оценку точности.

Правильно:

X_train, X_test = X[:800], X[800:]

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

С пайплайном вам не нужно об этом думать. Пайплайн обрабатывает это автоматически.

sklearn Pipeline

Pipeline из sklearn объединяет преобразователи и оцениватель. Он предоставляет .fit(), .predict() и .score(), которые применяют все шаги по порядку.

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("model", LogisticRegression()),
])

pipe.fit(X_train, y_train)
predictions = pipe.predict(X_test)

Когда вы вызываете pipe.fit(X_train, y_train):

  1. Масштабировщик вызывает fit_transform для X_train
  2. Модель вызывает fit для масштабированного X_train

Когда вы вызываете pipe.predict(X_test):

  1. Масштабировщик вызывает transform (а не fit_transform) для X_test
  2. Модель вызывает predict для масштабированного X_test

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

ColumnTransformer: разные пайплайны для разных столбцов

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

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer

numeric_pipe = Pipeline([
    ("impute", SimpleImputer(strategy="median")),
    ("scale", StandardScaler()),
])

categorical_pipe = Pipeline([
    ("impute", SimpleImputer(strategy="most_frequent")),
    ("encode", OneHotEncoder(handle_unknown="ignore")),
])

preprocessor = ColumnTransformer([
    ("num", numeric_pipe, ["age", "income", "score"]),
    ("cat", categorical_pipe, ["city", "gender", "plan"]),
])

full_pipeline = Pipeline([
    ("preprocess", preprocessor),
    ("model", GradientBoostingClassifier()),
])

Параметр handle_unknown="ignore" в OneHotEncoder критически важен для продакшена. Когда появляется новая категория (например, город, которого модель никогда не видела), он создаёт нулевой вектор вместо аварийного завершения.

Отслеживание экспериментов

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

MLflow — наиболее распространённое решение с открытым исходным кодом:

import mlflow

with mlflow.start_run():
    mlflow.log_param("max_depth", 5)
    mlflow.log_param("n_estimators", 100)
    mlflow.log_param("learning_rate", 0.1)

    pipe.fit(X_train, y_train)
    accuracy = pipe.score(X_test, y_test)

    mlflow.log_metric("accuracy", accuracy)
    mlflow.sklearn.log_model(pipe, "model")

Каждый запуск записывается вместе с параметрами, метриками, артефактами и полной моделью. Вы можете сравнивать запуски, воспроизводить любой эксперимент и развёртывать любую версию модели.

Weights & Biases (wandb) предоставляет ту же функциональность с размещённой панелью мониторинга:

import wandb

wandb.init(project="my-pipeline")
wandb.config.update({"max_depth": 5, "n_estimators": 100})

pipe.fit(X_train, y_train)
accuracy = pipe.score(X_test, y_test)

wandb.log({"accuracy": accuracy})

Версионирование моделей

После отслеживания экспериментов нужно управлять версиями моделей. Какая модель находится в продакшене? Какая — в стейджинге? Какая была на прошлой неделе?

Реестр моделей MLflow предоставляет:

  • Отслеживание версий: каждая сохранённая модель получает номер версии
  • Переходы между стадиями: «Staging», «Production», «Archived»
  • Процесс утверждения: модели необходимо явно продвигать в продакшен
  • Откат: мгновенное переключение на предыдущую версию

Версионирование данных с DVC

Код версионируется с помощью git. Данные тоже должны версионироваться, но git не умеет работать с большими файлами. Это решает DVC (Data Version Control).

dvc init
dvc add data/training.csv
git add data/training.csv.dvc data/.gitignore
git commit -m "Track training data"
dvc push

DVC хранит собственно данные в удалённом хранилище (S3, GCS, Azure), а в git оставляет небольшой файл .dvc, в котором записан хеш. Когда вы переключаетесь на коммит git, dvc checkout восстанавливает точные данные, которые использовались.

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

Воспроизводимые эксперименты

Воспроизводимому эксперименту нужны четыре вещи:

  1. Фиксированные случайные seed: задайте seed для numpy, random и фреймворка (torch, sklearn)
  2. Закреплённые зависимости: requirements.txt или poetry.lock с точными версиями
  3. Версионированные данные: DVC или аналогичный инструмент
  4. Файлы конфигурации: все гиперпараметры находятся в конфигурации, а не жёстко заданы в коде
import numpy as np
import random

def set_seed(seed=42):
    random.seed(seed)
    np.random.seed(seed)
    try:
        import torch
        torch.manual_seed(seed)
        torch.cuda.manual_seed_all(seed)
        torch.backends.cudnn.deterministic = True
    except ImportError:
        pass

От ноутбука к продакшен-пайплайну

Диаграмма к уроку «ML-пайплайны»

Типичная последовательность:

  1. Исследование в ноутбуке: быстрые эксперименты, визуализации, идеи признаков
  2. Выделение функций: перенос предобработки, проектирования признаков и оценки в модули
  3. Сборка пайплайна: объединение преобразований в Pipeline sklearn или пользовательский класс
  4. Управление конфигурацией: перенос всех гиперпараметров в YAML/JSON-конфигурацию
  5. Отслеживание экспериментов: добавление журналирования MLflow или wandb
  6. Валидация данных: проверка схемы, распределений и шаблонов пропущенных значений перед обучением
  7. Тесты: модульные тесты преобразователей, интеграционные тесты полного пайплайна
  8. Развёртывание: сериализация пайплайна, обёртка в API (FastAPI, Flask), контейнеризация

Распространённые ошибки в пайплайнах

Ошибка Чем она плоха Исправление
Подгонка по полным данным до разделения Утечка данных Используйте Pipeline с cross_val_score
Проектирование признаков вне пайплайна Разные преобразования при обучении и обслуживании Поместите все преобразования в Pipeline
Нет обработки неизвестных категорий Сбой в продакшене при новых значениях OneHotEncoder(handle_unknown="ignore")
Жёстко заданные имена столбцов Ломается при изменении схемы Используйте списки имён столбцов из конфигурации
Нет валидации данных Молча неверные предсказания на плохих данных Добавьте проверки схемы перед предсказанием
Расхождение обучения и обслуживания Модель видит в продакшене другие признаки Один объект Pipeline для обоих случаев

Соберите это

Код в code/pipeline.py собирает полный ML-пайплайн с нуля:

Шаг 1: пользовательский преобразователь

class CustomTransformer:
    def __init__(self):
        self.means = None
        self.stds = None

    def fit(self, X):
        self.means = np.mean(X, axis=0)
        self.stds = np.std(X, axis=0)
        self.stds[self.stds == 0] = 1.0
        return self

    def transform(self, X):
        return (X - self.means) / self.stds

    def fit_transform(self, X):
        return self.fit(X).transform(X)

Шаг 2: пайплайн с нуля

class PipelineFromScratch:
    def __init__(self, steps):
        self.steps = steps

    def fit(self, X, y=None):
        X_current = X.copy()
        for name, step in self.steps[:-1]:
            X_current = step.fit_transform(X_current)
        name, model = self.steps[-1]
        model.fit(X_current, y)
        return self

    def predict(self, X):
        X_current = X.copy()
        for name, step in self.steps[:-1]:
            X_current = step.transform(X_current)
        name, model = self.steps[-1]
        return model.predict(X_current)

Шаг 3: кросс-валидация с пайплайном

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

Шаг 4: полный продакшен-пайплайн со sklearn

Полный пайплайн с ColumnTransformer, несколькими путями предобработки и моделью, обученный с корректной кросс-валидацией и журналированием экспериментов.

Внедрите это

Этот урок создаёт:

  • outputs/prompt-ml-pipeline.md – навык для сборки и отладки ML-пайплайнов
  • code/pipeline.py – полный пайплайн от реализации с нуля до sklearn

Упражнения

  1. Соберите пайплайн, который работает с набором данных из 3 числовых и 2 категориальных столбцов. С помощью ColumnTransformer примените к числовым столбцам заполнение медианой + масштабирование, а к категориальным — заполнение наиболее частым значением + one-hot-кодирование. Обучите его с 5-фолдовой кросс-валидацией.

  2. Намеренно внесите утечку данных: подгоните масштабировщик на всём наборе данных до разделения. Сравните оценку кросс-валидации (с утечкой) с оценкой кросс-валидации пайплайна (чистой). Насколько велика разница?

  3. Сериализуйте свой пайплайн с помощью joblib.dump. Загрузите его в отдельном скрипте и выполните предсказания. Убедитесь, что предсказания идентичны.

  4. Добавьте в пайплайн пользовательский преобразователь, который создаёт полиномиальные признаки (степени 2) для двух наиболее важных числовых столбцов. Где он должен находиться в пайплайне?

  5. Настройте отслеживание MLflow для пайплайна. Запустите 5 экспериментов с разными гиперпараметрами. С помощью интерфейса MLflow (mlflow ui) сравните запуски и выберите лучшую модель.

Ключевые термины

Термин Как обычно говорят Что это действительно означает
Пайплайн «Цепочка преобразований + модель» Упорядоченная последовательность подогнанных преобразователей и модели, применяемая как единое целое для предотвращения утечки
Утечка данных «Тестовая информация попала в обучение» Использование информации вне обучающего набора для построения модели, завышающее оценки качества
ColumnTransformer «Разная предобработка для каждого столбца» Применяет разные пайплайны к разным подмножествам столбцов и объединяет результаты
Отслеживание экспериментов «Журналирование запусков» Запись параметров, метрик, артефактов и версий кода для каждого обучающего запуска
MLflow «Отслеживание и развёртывание моделей» Платформа с открытым исходным кодом для отслеживания экспериментов, реестра моделей и развёртывания
DVC «Git для данных» Система контроля версий больших файлов данных: хранит хеши в git, а данные — в удалённом хранилище
Реестр моделей «Каталог версий моделей» Система, которая отслеживает версии моделей с метками стадий (стейджинг, продакшен, архив)
Расхождение обучения и обслуживания «В ноутбуке это работало» Различия в обработке данных при обучении и инференсе, вызывающие молчаливые ошибки
Воспроизводимость «Один код, один результат» Возможность получать идентичные результаты из тех же кода, данных и конфигурации

Дополнительное чтение


Источник: ML Pipelines 02.12 — Настройка гиперпараметров · Фаза 2 — Основы машинного обучения · Полный каталог · 02.14 — Наивный Байес