Как настроить CI/CD для AI-проектов
Пошаговый гайд по настройке CI/CD для ML-проектов: инструменты, пайплайны, тестирование моделей и автоматический деплой.
Вы обучили модель, метрики выглядят отлично, ноутбук показывает красивые графики. Осталось «просто задеплоить». И тут начинается хаос: ручные копирования весов, забытые версии данных, «а у меня локально работало». Знакомо? Именно для этого AI-проектам нужен CI/CD — но не такой, как в классической веб-разработке. Здесь кроме кода нужно версионировать данные, автоматически валидировать модели и безопасно выкатывать их в продакшен.
В этом гайде — как выстроить CI/CD-пайплайн для ML-проекта с нуля: какие инструменты использовать, как тестировать модели автоматически и какие стратегии деплоя спасут вас от сломанного прода.
Чем CI/CD для ML отличается от классического
В традиционной разработке CI/CD — это сборка кода, прогон тестов и автоматический деплой. В ML-проектах добавляются три новых измерения:
| Аспект | Классический CI/CD | CI/CD для ML |
|---|---|---|
| Что версионируется | Исходный код | Код + данные + модели + конфигурации экспериментов |
| Что тестируется | Юнит-тесты, интеграционные тесты | + валидация данных, метрики модели, проверка на дрейф |
| Что деплоится | Приложение (контейнер, бинарник) | Модель + сервис инференса + мониторинг |
| Триггер пайплайна | Push в репозиторий | Push в репозиторий + новые данные + расписание ретрейна |
| Время прогона | Минуты | Минуты — часы (тренировка модели) |
CI/CD для ML — это не просто «прогнать тесты и задеплоить». Это автоматизация всего жизненного цикла: от валидации данных до мониторинга модели в продакшене.
Архитектура CI/CD-пайплайна для ML
Прежде чем выбирать инструменты, разберёмся, из чего состоит типичный ML-пайплайн.
graph TD
A[Push кода / Новые данные] --> B[CI: Валидация данных]
B --> C[CI: Тренировка модели]
C --> D[CI: Оценка метрик]
D --> E{Метрики лучше baseline?}
E -->|Да| F[CD: Регистрация модели]
E -->|Нет| G[Отчёт об ошибке]
F --> H[CD: Стейджинг-деплой]
H --> I[CD: Интеграционные тесты]
I --> J{Тесты прошли?}
J -->|Да| K[CD: Продакшен-деплой]
J -->|Нет| G
K --> L[Мониторинг]
L --> M{Дрейф данных / деградация?}
M -->|Да| A
Пайплайн состоит из трёх основных фаз:
Continuous Integration (CI) — валидация данных, проверка кода, тренировка модели, оценка метрик. Запускается на каждый pull request.
Continuous Delivery (CD) — регистрация модели в реестре, деплой на стейджинг, прогон интеграционных тестов, деплой в продакшен. Запускается при мерже в основную ветку.
Continuous Training (CT) — автоматический ретрейн при обнаружении дрейфа данных или деградации метрик. Запускается по расписанию или по триггеру от системы мониторинга.
Инструменты: что использовать
Оркестрация пайплайна
Для запуска самого CI/CD-пайплайна есть несколько вариантов:
| Инструмент | Тип | Плюсы | Минусы |
|---|---|---|---|
| GitHub Actions | Облачный CI | Нативная интеграция с GitHub, бесплатные минуты, простой YAML-синтаксис | Ограничение на GPU-раннеры (нужны self-hosted) |
| GitLab CI | Облачный / self-hosted | Встроенный реестр контейнеров, гибкие раннеры | Сложнее конфигурация для ML |
| Jenkins | Self-hosted | Максимальная гибкость, сотни плагинов | Требует обслуживания, устаревший UI |
| Kubeflow Pipelines | ML-нативный | Создан специально для ML, работает на Kubernetes | Высокий порог входа, оверхед для небольших проектов |
Версионирование данных и моделей
В ML нельзя полагаться только на Git для кода. Данные и модели — это бинарные файлы на гигабайты.
DVC (Data Version Control) — open-source инструмент, который работает поверх Git. Хранит метаданные (.dvc-файлы) в Git, а сами данные — в S3, GCS, Azure Blob или локальном хранилище. Команды напоминают Git:
# Инициализация DVC в проекте
dvc init
# Добавление данных под версионный контроль
dvc add data/training_set.parquet
# Настройка удалённого хранилища
dvc remote add -d storage s3://my-bucket/dvc-store
# Пуш данных
dvc push
MLflow — платформа для управления ML-экспериментами. Tracking-сервер логирует параметры, метрики и артефакты каждого запуска. Model Registry позволяет регистрировать модели с версиями и стадиями (Staging → Production):
import mlflow
with mlflow.start_run():
mlflow.log_param("learning_rate", 0.001)
mlflow.log_param("epochs", 50)
# ... тренировка модели ...
mlflow.log_metric("accuracy", 0.94)
mlflow.log_metric("f1_score", 0.91)
# Регистрация модели
mlflow.sklearn.log_model(model, "model",
registered_model_name="fraud-detector")
CML (Continuous Machine Learning) — CLI-инструмент от Iterative (создатели DVC), который генерирует визуальные отчёты об экспериментах прямо в pull request. Показывает таблицы метрик, графики, diff данных — всё в комментарии к PR.
Мониторинг в продакшене
После деплоя модели работа не заканчивается. Нужно отслеживать:
- Data drift — изменение распределения входных данных
- Concept drift — изменение связи между признаками и целевой переменной
- Деградация метрик — падение качества предсказаний
Инструменты: Evidently AI (open-source), Whylogs, NannyML, Arize AI (облачный).
Практика: настраиваем пайплайн на GitHub Actions
Разберём конкретный пример — CI/CD-пайплайн для ML-проекта на Python с GitHub Actions, DVC и CML.
Структура проекта
ml-project/
├── .github/workflows/
│ ├── ci.yml # CI-пайплайн (на каждый PR)
│ └── deploy.yml # CD-пайплайн (на мерж в main)
├── data/
│ └── training_set.parquet.dvc
├── src/
│ ├── train.py
│ ├── evaluate.py
│ └── serve.py
├── tests/
│ ├── test_data.py # Тесты данных
│ └── test_model.py # Тесты модели
├── dvc.yaml # DVC-пайплайн
├── params.yaml # Гиперпараметры
└── requirements.txt
CI-пайплайн: валидация на каждый PR
# .github/workflows/ci.yml
name: ML CI Pipeline
on:
pull_request:
branches: [main]
jobs:
test-and-train:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Pull data from DVC
run: dvc pull
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Run data validation tests
run: pytest tests/test_data.py -v
- name: Train model
run: dvc repro
- name: Run model evaluation tests
run: pytest tests/test_model.py -v
- name: Generate CML report
env:
REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cml comment update report.md
Тесты данных и модели
Тестирование — ключевое отличие зрелого ML-пайплайна от наивного. Вот что стоит проверять:
# tests/test_data.py
import pandas as pd
import great_expectations as gx
def test_training_data_schema():
"""Проверяем схему данных: типы, обязательные колонки."""
df = pd.read_parquet("data/training_set.parquet")
assert "target" in df.columns
assert "feature_1" in df.columns
assert df["target"].dtype in ["int64", "float64"]
assert len(df) > 1000, "Слишком мало данных для тренировки"
def test_no_data_leakage():
"""Проверяем отсутствие утечки данных."""
train = pd.read_parquet("data/train.parquet")
test = pd.read_parquet("data/test.parquet")
overlap = set(train.index) & set(test.index)
assert len(overlap) == 0, f"Утечка данных: {len(overlap)} строк"
def test_feature_distributions():
"""Проверяем, что распределения не сдвинулись."""
df = pd.read_parquet("data/training_set.parquet")
assert df["feature_1"].mean() > 0
assert df["feature_1"].std() < 100
assert df["feature_1"].isna().sum() / len(df) < 0.05
# tests/test_model.py
import mlflow
import json
def test_model_metrics_above_baseline():
"""Модель должна быть лучше baseline."""
with open("metrics.json") as f:
metrics = json.load(f)
assert metrics["accuracy"] > 0.85, "Accuracy ниже порога"
assert metrics["f1_score"] > 0.80, "F1 ниже порога"
def test_model_inference_time():
"""Инференс должен укладываться в SLA."""
import time
model = mlflow.sklearn.load_model("model")
sample = [[1.0, 2.0, 3.0, 4.0]]
start = time.time()
for _ in range(100):
model.predict(sample)
avg_time = (time.time() - start) / 100
assert avg_time < 0.1, f"Инференс слишком медленный: {avg_time:.3f}s"
assert accuracy == 0.9431 сломается при любом изменении данных. Тестируйте пороги: «accuracy выше 0.85» или «не хуже предыдущей версии более чем на 2%». Хрупкие тесты — враг автоматизации.CD-пайплайн: деплой модели
# .github/workflows/deploy.yml
name: ML CD Pipeline
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t ml-service:${{ github.sha }} .
docker tag ml-service:${{ github.sha }} \
registry.example.com/ml-service:latest
- name: Push to registry
run: docker push registry.example.com/ml-service:latest
- name: Deploy to staging
run: kubectl apply -f k8s/staging/
- name: Run smoke tests
run: |
sleep 30
curl -f http://staging.example.com/health
python tests/test_integration.py
- name: Deploy to production (canary)
run: kubectl apply -f k8s/production-canary/
Стратегии деплоя моделей
Выкатывать ML-модель на 100% трафика сразу — рискованно. Вот три безопасных подхода:
graph LR
subgraph "Blue-Green"
A1[v1 — активная] --> A2[v2 — подготовлена]
A2 --> A3[Переключение трафика]
end
subgraph "Canary"
B1[v1 — 95% трафика] --> B2[v2 — 5% трафика]
B2 --> B3[Постепенное увеличение]
end
subgraph "Shadow"
C1[v1 — обслуживает] --> C2[v2 — работает параллельно]
C2 --> C3[Сравнение результатов]
end
Blue-Green — два одинаковых окружения. Новая модель деплоится в неактивное, после проверки трафик переключается. Откат — мгновенный: переключение обратно.
Canary — новая модель получает малую долю трафика (1–5%). Если метрики в норме, доля постепенно растёт. Идеальный вариант для продакшен-критичных систем.
Shadow (теневой) — новая модель обрабатывает реальные запросы параллельно со старой, но её ответы не показываются пользователям. Результаты сравниваются оффлайн. Самый безопасный, но самый ресурсоёмкий подход.
Shadow — финансовые модели, медицинские AI-системы, где ошибка критична и нужна полная проверка перед выкатом.
Blue-Green — batch-предсказания, модели, обрабатывающие запросы без реального пользователя.
Типичные ошибки и как их избежать
1. Не версионировать данные. «Я просто обновлю CSV на сервере» — и через неделю невозможно воспроизвести эксперимент. Используйте DVC или аналог с первого дня.
2. Тестировать только код, но не модель. Юнит-тесты функций — это хорошо, но без проверки метрик модели и валидации данных CI бесполезен для ML-проекта.
3. Хранить секреты в коде. API-ключи, пароли от баз данных, креденшлы облачных провайдеров — только в GitHub Secrets / переменных окружения CI-системы. Никогда в репозитории.
4. Игнорировать размер артефактов. ML-модели могут весить гигабайты. Не коммитьте их в Git. Используйте model registry (MLflow, DVC) или облачное хранилище.
5. Не мониторить модель после деплоя. Модель, которая отлично работала на тестовых данных, может деградировать в продакшене из-за дрейфа данных. Настройте алерты на ключевые метрики.
Лучший CI/CD-пайплайн — тот, которому команда доверяет. Если каждый мерж в main требует ручной проверки «на всякий случай», значит пайплайн недостаточно тестирует.
Заключение
CI/CD для AI-проектов — это не роскошь, а необходимость, как только вы выходите за рамки Jupyter-ноутбуков. Вот минимальный план действий:
- Начните с версионирования — Git для кода, DVC для данных и моделей
- Добавьте автоматические тесты — валидация данных, проверка метрик, тесты инференса
- Настройте CI — GitHub Actions + CML для автоматических отчётов в PR
- Автоматизируйте деплой — Docker-образ с моделью, canary-деплой
- Замкните петлю — мониторинг в продакшене, автоматический ретрейн при деградации
Не нужно строить всё сразу. Даже базовый пайплайн с DVC + GitHub Actions + pytest уже радикально улучшит воспроизводимость и надёжность вашего ML-проекта.