Git + CI/CD
Git (branching, merge, rebase) + CI/CD (GitHub Actions) для ML: автотесты кода и данных, DVC для версионирования датасетов, автоматический деплой модели при merge в main.
Git + CI/CD — от «работает на моём ноутбуке» до автоматического деплоя
Ты обучил модель, получил отличные метрики — и что дальше? Скинуть коллеге model_final_v3_FINAL.pkl в Slack? ML-проект — это не только модель. Это код предобработки, конфиги, данные, эксперименты, Docker-образы. И всё это нужно версионировать, тестировать и деплоить. Без системы — хаос: «кто сломал прод?», «какая версия модели сейчас крутится?», «где код, который обучил ту самую модель?».
Git решает первую проблему — версионирование кода и совместная работа. CI/CD решает вторую — автоматическое тестирование и деплой. Вместе они дают гарантию: каждый push проверен, каждый merge — безопасен, каждый деплой — автоматический. Для ML-инженера это не «приятно иметь», а базовая гигиена.

Большая картина: путь кода от ноутбука до продакшна
Представь полный цикл работы ML-инженера за один день:
1. Код — пишешь новый feature engineering pipeline в ветке feature/add-time-features.
2. Commit — фиксируешь изменения: git commit -m "feat: add time-based features". Pre-commit хуки автоматически прогоняют линтер и форматтер.
3. Push — отправляешь ветку на GitHub: git push origin feature/add-time-features.
4. CI (Continuous Integration) — GitHub Actions автоматически запускает: lint → unit-тесты → тесты данных → обучение модели → проверку метрик. Если что-то упало — PR не пройдёт.
5. Code Review — коллега смотрит PR, оставляет комментарии, аппрувит.
6. Merge — код вливается в main.
7. CD (Continuous Delivery) — автоматически собирается Docker-образ, пушится в registry, деплоится на staging. Smoke-тесты проходят → production.
Руками — ноль шагов после git push. Всё остальное делает автоматика.
Загрузка интерактивного виджета...
Git: ключевые концепции
Git — распределённая система контроля версий. Каждый клон репозитория — полная копия с историей. Даже без интернета ты можешь коммитить, создавать ветки, смотреть историю. Вот четыре операции, которые ты используешь каждый день:
Commit — снимок состояния проекта. Каждый коммит имеет уникальный хеш (SHA), автора, дату и сообщение. Хороший коммит — атомарный: одно логическое изменение. Не «fix everything» на 500 строк.
Branch — параллельная линия разработки. Создаёшь ветку из main, работаешь в изоляции, не мешаешь остальным. Ветка — это просто указатель на коммит, создаётся мгновенно.
Merge — объединение двух веток. git merge feature/x берёт все коммиты из ветки и вливает в текущую. Если обе ветки меняли один файл — возникает merge conflict, который нужно разрешить вручную.
Rebase — альтернатива merge. Вместо слияния «переносит» твои коммиты поверх другой ветки, создавая линейную историю. git rebase main = «сделай вид, что я начал работу от последнего коммита main». Чище история, но опаснее: переписывает коммиты. Золотое правило: никогда не делай rebase ветки, которую уже запушил и расшарил с коллегами.
# Типичный workflow
git checkout -b feature/add-catboost # создали ветку
# ... пишем код ...
git add src/models/catboost_model.py
git commit -m "feat: add CatBoost model with time features"
# Подтягиваем свежий main
git fetch origin
git rebase origin/main # линейная история
git push origin feature/add-catboost # пушим, создаём PRConventional Commits
git commit -m "fix" используй структурированные сообщения: feat: add feature importance plot, fix: resolve OOM in batch prediction, ci: add model quality gate, docs: update API schema. Через полгода откроешь git log и поймёшь, что происходило. Подробнее: conventionalcommits.org.Branching-стратегии: как организовать работу команды
Когда в команде 3+ человека, нужны правила: кто куда коммитит, откуда деплоим, как ревьюим. Три основные стратегии:
GitHub Flow — самая простая. Одна ветка main (всегда рабочая). Для каждой задачи — short-lived ветка (живёт 1-5 дней). Создал PR → прошёл CI → code review → merge → автодеплой. Подходит большинству ML-команд.
Trunk-Based Development — ещё проще. Все коммитят в main (или ветки живут максимум 1 день). Требует сильную CI-дисциплину и feature flags (прятать незаконченный код за флагами). Используют команды с непрерывным деплоем: Google, Meta.
Git Flow — сложная. Ветки: main (production), develop (интеграция), feature/*, release/*, hotfix/*. Каждый релиз — отдельная ветка с тестированием. Подходит для продуктов с чётким релизным циклом (раз в 2 недели, раз в месяц). Для ML-команд обычно избыточна.
Что выбрать?
CI для ML: что тестировать помимо кода
В обычном софте CI запускает unit-тесты и линтер. В ML этого недостаточно — у тебя три источника ошибок: код, данные и модель. Каждый нужно тестировать отдельно.
Уровень 1: Код. Стандартные проверки — работают быстро, запускаются на каждый коммит: • Linting (ruff) — стиль кода, неиспользуемые импорты, потенциальные баги • Type checking (mypy) — ловит ошибки типов до запуска • Unit-тесты (pytest) — функции предобработки, feature engineering, утилиты • Pre-commit hooks — ruff + mypy + проверка на большие файлы локально, до push
Уровень 2: Данные. Проверяешь данные перед обучением — ловишь проблемы на входе: • Нет пропусков в критичных колонках • Типы данных соответствуют схеме (age — число, не строка) • Распределение не сдвинулось относительно baseline (PSI < 0.25) • Нет дупликатов, нет утечки из будущего (target leakage) Инструменты: Great Expectations, Pandera, dbt tests.
Уровень 3: Модель. После обучения — проверяешь, что модель не деградировала: • Метрики не хуже baseline (AUC ≥ 0.90) • Нет деградации по сегментам (новые пользователи, premium) • Inference-время ≤ 100ms • Модель не предсказывает один класс для всех • Размер модели ≤ лимита (актуально для мобильных/edge)
# Пример: тесты данных + модели в CI
import pytest
from sklearn.metrics import roc_auc_score
def test_no_nulls_in_critical_columns(df):
"""Критичные фичи не должны содержать пропуски"""
critical = ["user_id", "amount", "timestamp"]
for col in critical:
assert df[col].notna().all(), f"Пропуски в {col}"
def test_feature_distribution_stable(train_df, fresh_df):
"""PSI < 0.25 — распределение не сдвинулось критично"""
for col in train_df.select_dtypes("number").columns:
psi = calc_psi(train_df[col], fresh_df[col])
assert psi < 0.25, f"{col}: PSI={psi:.3f} — data drift!"
def test_model_quality(model, test_data):
"""AUC не упал ниже порога"""
preds = model.predict_proba(test_data.X)[:, 1]
auc = roc_auc_score(test_data.y, preds)
assert auc >= 0.90, f"AUC {auc:.3f} < 0.90"CD для ML: от registry до production
Continuous Delivery в ML — это не просто «собрать Docker и задеплоить». У тебя два артефакта: код (API-сервис, предобработка) и модель (обученные веса). Они могут меняться независимо. CD должен уметь обновлять оба.
Типичный pipeline деплоя ML-модели: 1. Model Registry — обученная модель регистрируется с версией и метриками (MLflow, W&B). Подробнее — в ноде MLOps Basics. 2. Staging — модель деплоится в staging-окружение. Прогоняются интеграционные тесты: API отвечает, латентность в норме, предсказания адекватны. 3. Shadow mode — новая модель работает параллельно с production, но её предсказания не показываются пользователям. Сравниваешь метрики. 4. Canary deploy — 5-10% трафика переключается на новую модель. Если метрики не падают за 1-2 дня — продолжаешь. 5. Full rollout — 100% трафика на новую модель. Старая версия остаётся в registry для быстрого отката.
Rollback за 30 секунд
GitHub Actions: CI/CD для ML-проекта
GitHub Actions — бесплатный CI/CD для публичных репозиториев (2000 минут/месяц для приватных). Описываешь pipeline в YAML, GitHub запускает его на каждый push или PR. Вот полный пример для ML-проекта:
# .github/workflows/ml-ci.yml
name: ML Pipeline CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install uv && uv sync
# Уровень 1: код
- name: Lint
run: uv run ruff check .
- name: Type check
run: uv run mypy src/
- name: Unit tests
run: uv run pytest tests/unit/ -v
# Уровень 2: данные (на PR — только smoke)
- name: Data validation
run: uv run pytest tests/data/ -v
# Уровень 3: модель (только для main)
- name: Model quality gate
if: github.ref == 'refs/heads/main'
run: |
uv run python scripts/train.py --config configs/ci.yaml
uv run pytest tests/model/ -v# .github/workflows/deploy.yml — CD при merge в main
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
needs: lint-and-test
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t ml-service:{{ github.sha }} .
- name: Push to registry
run: |
echo {{ secrets.REGISTRY_TOKEN }} |
docker login ghcr.io -u {{ github.actor }} --password-stdin
docker push ghcr.io/{{ github.repository }}/ml-service:{{ github.sha }}
- name: Deploy to staging
run: |
kubectl set image deployment/ml-service
app=ghcr.io/{{ github.repository }}/ml-service:{{ github.sha }}Тяжёлые задачи (полная тренировка, GPU) — не на каждый PR. Выноси в отдельный workflow: on: schedule (раз в неделю) или workflow_dispatch (ручной запуск). GitHub предоставляет GPU-runners, но платно. Альтернатива — self-hosted runner на своём сервере с GPU.
DVC: версионирование данных и моделей
Git отлично версионирует код, но не подходит для больших файлов: датасет на 5 GB или модель на 2 GB в Git — это боль (медленный clone, раздутый .git). DVC (Data Version Control) решает эту проблему: данные и модели хранятся в S3/GCS/локально, а в Git лежат только маленькие .dvc-файлы — указатели на конкретные версии.
Как это работает:
1. dvc init — инициализируешь DVC в Git-репозитории
2. dvc add data/train.csv — DVC сохраняет файл в кеш, а в Git добавляет data/train.csv.dvc (хеш + метаданные)
3. dvc push — загружает данные в remote storage (S3, GCS, SSH)
4. git commit + git push — в Git уходит только .dvc-файл
5. Коллега делает git pull + dvc pull — получает те же данные
# Настройка DVC
dvc init
dvc remote add -d storage s3://my-bucket/dvc-store
# Версионируем датасет
dvc add data/train.csv # → data/train.csv.dvc
git add data/train.csv.dvc .gitignore
git commit -m "data: add training dataset v1"
# Обновили данные → новая версия
dvc add data/train.csv # хеш изменился
git add data/train.csv.dvc
git commit -m "data: update training dataset v2"
dvc push # → S3
# Коллега получает данные
git pull && dvc pull # скачает именно версию v2
# Откат к предыдущей версии данных
git checkout HEAD~1 -- data/train.csv.dvc
dvc checkout # → данные v1 вернулисьDVC pipelines — следующий уровень. Описываешь ML-пайплайн в dvc.yaml: какие стадии (preprocess → train → evaluate), какие входы/выходы. dvc repro запускает только те стадии, входы которых изменились. Это как Makefile для ML.
# dvc.yaml — воспроизводимый ML pipeline
stages:
preprocess:
cmd: python src/preprocess.py
deps:
- data/raw/
- src/preprocess.py
outs:
- data/processed/train.parquet
train:
cmd: python src/train.py
deps:
- data/processed/train.parquet
- src/train.py
- configs/model.yaml
outs:
- models/model.pkl
metrics:
- metrics/scores.json:
cache: false
evaluate:
cmd: python src/evaluate.py
deps:
- models/model.pkl
- data/processed/test.parquet
plots:
- plots/roc.jsondvc repro — запускает pipeline. Если изменился только configs/model.yaml, пересоберёт train и evaluate, но пропустит preprocess. dvc metrics show покажет метрики, dvc plots show — графики. Вся история экспериментов — в Git.
🎯 На собеседовании
Junior
model_v2_final_FINAL.py.
• Что такое CI/CD? CI — автоматические тесты при каждом push (lint, unit tests). CD — автоматический деплой при merge в main (Docker build → registry → staging → production).
• Merge vs rebase? Merge — сливает ветки, сохраняет историю. Rebase — переносит коммиты, линейная история. Rebase чище, но не делай на расшаренных ветках.
• Что такое DVC? Git для данных и моделей. В Git — маленький .dvc-файл (указатель), сами данные — в S3/GCS.Middle
dvc repro пересобирает только изменившиеся стадии. Метрики и данные версионируются вместе с кодом.Senior
Собираем всё вместе
Git + CI/CD для ML — это цепочка: код в ветке → pre-commit hooks → push → CI (lint, тесты кода, данных, модели) → code review → merge → CD (Docker → registry → staging → canary → production). DVC добавляет версионирование данных и воспроизводимые пайплайны. Model Registry — версионирование моделей и мгновенный rollback.
Если запомнить одну вещь: ML-проект — это не только модель, а полный pipeline от данных до продакшна. Git + CI/CD + DVC — минимальный набор, чтобы этот pipeline был воспроизводимым, тестируемым и автоматическим.
Дальше на роадмапе: Docker — как упаковать модель в контейнер, MLOps Basics — полный цикл ML-системы от experiment tracking до мониторинга.
Материалы
Официальный туториал по GitHub Actions — от первого workflow до деплоя.
Быстрый старт с DVC: версионирование данных, pipelines, эксперименты.
Автоматические проверки перед каждым коммитом: линтеры, форматтеры, типы.
Спецификация структурированных commit-сообщений: feat, fix, ci, docs.
ThoughtWorks — CI/CD адаптированный для ML: тестирование данных, моделей, pipeline.
Полный курс GitHub Actions от основ до продвинутых workflow.