Почему автоматизация ML-пайплайна до сих пор похожа на цирк с конями?
Ты запускаешь fine-tuning. Ждешь 8 часов. Проверяешь логи. Находишь ошибку в предобработке данных. Исправляешь. Запускаешь снова. Еще 8 часов. Потом квантование. Потом тесты. Потом выгрузка. За неделю ты тратишь 60 часов на рутину и 2 часа на реальную работу.
Знакомо? Я тоже через это проходил.
Пока корпорации строят сложные MLOps-платформы за миллионы, есть простой способ автоматизировать весь цикл: от загрузки датасета до публикации GGUF-модели. Без единой строчки кода вручную. Без мониторинга логов в полночь. Без нервного тика при виде "CUDA out of memory".
Сегодня соберем систему, где агенты делают за тебя всю грязную работу.
Важно: эта система не для разовых экспериментов. Если ты тренируешь модель раз в месяц, ручные методы проще. Но если у тебя пайплайн из 5+ шагов, который повторяется регулярно — читай дальше.
Архитектура: что склеивает агентов в работающую систему
Забудь про "возьми этот скрипт и запусти". Наша цель — полностью автономный пайплайн:
- Агент-оркестратор (Claude Code) получает задачу: "Дообучи Gemma 2B на медицинских данных"
- Он разбивает задачу на подзадачи: подготовка данных → fine-tuning → оценка → квантование → публикация
- Каждую подзадачу выполняет специализированный агент через HF-skills
- Trackio собирает метрики и логи, отправляет уведомления
- В конце система выдает GGUF-файл и ссылку на Hugging Face Hub
Звучит как фантастика? Это уже работает. И дешевле, чем нанимать джуна, который будет сидеть над ноутбуком сутками.
1 Подготовка: собираем инструменты
Тебе понадобится:
- Аккаунт на Hugging Face с доступом к Inference Endpoints (или свой сервер с GPU)
- Claude Code API ключ (или локальная LLM для кодинга — например, из этого обзора)
- Python 3.10+ с установленными библиотеками
- Базовое понимание, как работает fine-tuning (если нет — сначала прочитай этот гайд по медицинской настройке)
# Устанавливаем базовые зависимости
pip install huggingface-hub transformers datasets peft trl accelerate
pip install anthropic # для Claude Code API
pip install gguf # для работы с квантованными моделями
2 Настраиваем HF-skills: даём агентам руки
HF-skills — это набор инструментов, которые позволяют агентам работать с экосистемой Hugging Face. Вместо того чтобы писать сложные скрипты, ты описываешь задачу на естественном языке.
Вот как выглядит типичный сценарий:
from hf_skills import FineTuner, Quantizer, Evaluator
from claude_code import ClaudeCoder
# Инициализируем агента-оркестратора
agent = ClaudeCoder(api_key="your_key_here")
# Описываем задачу
task = """
Fine-tune модель google/gemma-2b-it на датасете medical_qa.
Использовать LoRA с rank=16.
Обучить 3 эпохи с learning_rate=2e-4.
После обучения сделать квантование в GGUF формат с q4_k_m.
Загрузить результат на Hugging Face Hub.
"""
# Агент сам разбивает задачу и вызывает нужные skills
result = agent.execute_with_skills(task)
В теории всё просто. На практике агенты тупят на мелочах. Вот самые частые косяки:
Ошибка 1: Агент не понимает контекст задачи. Если сказать "сделай квантование", он может выбрать неоптимальные параметры. Всегда явно указывай: "использовать imatrix для лучшего качества" (про imatrix подробнее в отдельном гайде).
Ошибка 2: Агент забывает про ограничения памяти. 40B-модель на карте 24GB? Привет, OOM. Всегда указывай доступные ресурсы явно: "у меня 48GB VRAM" (кстати, про модифицированные 48GB RTX 4090 есть интересная статья).
3 Пишем оркестратор: мозг системы
Claude Code — не волшебная таблетка. Ему нужна четкая инструкция. Вот шаблон, который реально работает:
import json
from datetime import datetime
class MLPipelineOrchestrator:
def __init__(self, hf_token, claude_api_key):
self.hf_token = hf_token
self.agent = ClaudeCoder(api_key=claude_api_key)
def run_pipeline(self, config):
"""
config = {
'base_model': 'google/gemma-2b-it',
'dataset': 'medical_qa',
'task_type': 'instruction_following',
'lora_rank': 16,
'epochs': 3,
'quantization': 'q4_k_m',
'use_imatrix': True, # Важно для качественного квантования
'output_hf_repo': 'your-username/medical-gemma-2b-gguf'
}
"""
steps = [
self._prepare_data,
self._fine_tune,
self._evaluate,
self._quantize,
self._upload_to_hub
]
results = {}
for step in steps:
step_name = step.__name__.replace('_', ' ').title()
print(f"[ {datetime.now().strftime('%H:%M:%S')} ] Starting: {step_name}")
try:
result = step(config)
results[step.__name__] = result
print(f"[ {datetime.now().strftime('%H:%M:%S')} ] Completed: {step_name}")
except Exception as e:
print(f"[ {datetime.now().strftime('%H:%M:%S')} ] Failed: {step_name} - {str(e)}")
# Здесь можно добавить логику повтора или уведомление
raise
return results
def _fine_tune(self, config):
# Агент генерирует код для fine-tuning на лету
prompt = f"""
Write a complete fine-tuning script for:
Model: {config['base_model']}
Dataset: {config['dataset']}
Use LoRA with rank={config['lora_rank']}
Training epochs: {config['epochs']}
Task type: {config['task_type']}
Requirements:
1. Use PEFT library
2. Save checkpoints every epoch
3. Log metrics to WandB
4. Handle OOM errors gracefully
"""
code = self.agent.generate_code(prompt)
# Сохраняем и выполняем сгенерированный код
with open('finetune_script.py', 'w') as f:
f.write(code)
# Запускаем через subprocess с мониторингом
# ...
Ключевой момент: каждый шаг должен быть идемпотентным. Если квантование упало на 90%, ты должен иметь возможность перезапустить только его, а не весь пайплайн с начала.
4 Квантование в GGUF: где агенты спотыкаются чаще всего
Квантование — самый капризный этап. Особенно если нужна imatrix. Вот как заставить агента сделать это правильно:
def _quantize(self, config):
"""
Генерация скрипта для квантования с imatrix
"""
prompt = f"""
Write a script to convert and quantize a Hugging Face model to GGUF format.
Input model: ./fine-tuned-model (directory with Hugging Face format)
Output: ./quantized-model.gguf
Requirements:
1. Use llama.cpp for conversion
2. Quantization type: {config['quantization']}
3. Generate imatrix first using calibration data
4. Calibration data: 100 samples from {config['dataset']}
5. Apply quantization with the generated imatrix
6. Verify the output GGUF file is valid
Important: Handle large models that don't fit in memory by using
memory-efficient conversion methods.
"""
code = self.agent.generate_code(prompt)
# Проверяем, что агент не наделал глупостей
required_imports = ['llama_cpp', 'json', 'numpy']
for imp in required_imports:
if imp not in code:
print(f"Warning: missing {imp} import, adding manually")
code = f"import {imp}\n" + code
# Если нужно работать с большими моделями
if '40b' in config['base_model'].lower() or '70b' in config['base_model'].lower():
if '--split-max-tensors' not in code:
print("Adding tensor splitting for large model")
code = code.replace('llama.cpp', 'llama.cpp --split-max-tensors')
return self._execute_quantization(code, config)
Почему так сложно? Потому что агенты не понимают разницу между "теоретически возможным" и "практически работающим". Они могут сгенерировать код, который компилируется, но падает на реальных данных. Всегда добавляй проверки.
5 Мониторинг и логирование: Trackio вместо бессонных ночей
Запустил пайплайн и ушел пить кофе? Вернёшься к разбитому корыту. Или к успешно завершённой задаче. Разница — в мониторинге.
Trackio (или любой подобный инструмент) должен отслеживать:
- Использование GPU памяти (рост = утечка)
- Loss кривую (если не падает — что-то не так)
- Прогресс квантования (проценты, а не "работаю")
- Ошибки (сразу в телеграм, а не в лог-файле)
import trackio
tracker = trackio.Tracker(
project_name="medical-finetuning",
api_key="your_trackio_key",
alerts_enabled=True,
alert_channels=["telegram", "email"]
)
def monitored_step(step_func, step_name, config):
"""Обёртка для отслеживания шагов"""
with tracker.span(step_name):
# Логируем начало
tracker.log(f"Starting {step_name} with config: {json.dumps(config, indent=2)}")
try:
result = step_func(config)
tracker.log(f"{step_name} completed successfully")
tracker.metric(f"{step_name}_success", 1)
return result
except Exception as e:
tracker.error(f"{step_name} failed: {str(e)}")
tracker.metric(f"{step_name}_failure", 1)
# Автоматически создаём тикет в issue tracker
tracker.create_issue(
title=f"Pipeline failed at {step_name}",
description=str(e),
priority="high"
)
raise
Теперь если fine-tuning упадет на 3-й эпохе из-за NaN в градиентах, ты получишь сообщение в телеграм с полным контекстом. Не через 8 часов, а сразу.
6 Публикация на Hugging Face Hub: финальный аккорд
Агенты любят загружать модели с кривыми метаданными. Потом месяц ищешь в Hub свою модель, потому что она называется "model-gguf" без тегов.
def _upload_to_hub(self, config):
"""Умная загрузка с правильными метаданными"""
prompt = f"""
Write a script to upload a GGUF model to Hugging Face Hub.
Model file: ./quantized-model.gguf
Target repo: {config['output_hf_repo']}
HF token: [HF_TOKEN]
Requirements:
1. Create README.md with:
- Model description
- Training details
- Quantization info
- Usage example
- License (Apache 2.0)
2. Add tags: ['gguf', 'quantized', 'medical', 'gemma']
3. Upload model file
4. Set model card metadata properly
5. Make repo private initially, then public after verification
"""
code = self.agent.generate_code(prompt)
# Заменяем placeholder на реальный токен
code = code.replace('[HF_TOKEN]', f'"{self.hf_token}"')
# Добавляем проверку размера файла
if 'os.path.getsize' not in code:
code = code.replace(
'# Upload model file',
'# Check file size\nimport os\nfile_size = os.path.getsize(\"./quantized-model.gguf\")\nprint(f\"Uploading {file_size / 1024**3:.2f} GB\")\n\n# Upload model file'
)
return self._execute_upload(code)
Где система ломается (и как чинить)
| Проблема | Причина | Решение |
|---|---|---|
| Агент генерирует код, который не работает | Недостаточный контекст в промпте | Добавляй примеры кода в промпт. Покажи, как ты делал бы это вручную. |
| Fine-tuning падает на OOM | Агент не учитывает доступную память | Явно указывай: "available VRAM: 24GB, use gradient checkpointing, batch size max 2" |
| Квантование занимает вечность | Агент не использует imatrix или выбирает медленный метод | Специфицируй: "use q4_k_m with imatrix, prioritize speed over smallest size" |
| Модель на Hub без метаданных | Агент загружает только файл | Давай шаблон README.md и требуй заполнить все поля |
Сколько это стоит на самом деле?
Давай посчитаем на примере Gemma 2B:
- Claude Code API: $0.02 за 1K токенов генерации кода. На весь пайплайн ~50K токенов = $1
- Hugging Face Inference Endpoints: A10G (24GB) = $1.05/час. Fine-tuning 3 эпохи = 4 часа = $4.20
- Квантование на CPU (c6i.2xlarge): $0.34/час, 2 часа = $0.68
- Итого: ~$6 за одну итерацию
Твое время на ручное выполнение: минимум 2 часа активной работы (плюс 8 часов ожидания). При ставке $50/час = $100.
Экономия 94%. И ты не тратишь нервы.
Важный нюанс: первые 2-3 итерации ты потратишь на отладку системы. Не жди, что с первого раза всё заработает идеально. Но после настройки она будет воспроизводимо работать десятки раз.
Что дальше? Куда двигаться после базовой автоматизации
Когда система работает стабильно:
- Добавь A/B тестирование моделей — пусть агент тренирует 3 варианта с разными гиперпараметрами и выбирает лучший по метрикам
- Интегрируй RLHF — если нужно выравнивание по человеческим предпочтениям (про RLHF есть отличный пример в этой статье про автоматизацию обучения)
- Настрой каскадное квантование — создавай сразу несколько версий (q4_k_m, q5_k_m, q8_0) для разных use case
- Добавь автоматическое тестирование — после загрузки на Hub запускай инференс-тесты, проверяй, что модель действительно работает
Самая большая ошибка — пытаться автоматизировать всё и сразу. Начни с одного шага (например, fine-tuning), добейся стабильной работы, потом добавь квантование, потом мониторинг.
Через месяц у тебя будет система, которая делает за тебя 80% рутинной ML-работы. Ты останешься с интересными задачами: проектирование архитектур, анализ результатов, постановка экспериментов.
А не с дебагом CUDA ошибок в 3 часа ночи.