Расчет true cost of iteration: setup tax vs latency penalty в ML | AiManual
AiManual Logo Ai / Manual.
02 Янв 2026 Гайд

Setup Tax vs Latency Penalty: как посчитать настоящую цену ML-итерации

Методика расчета реальной стоимости ML-итераций. Как выбрать между быстрым запуском и дешевой инфраструктурой. Практические формулы и примеры.

Вы платите за каждую мысль

Представьте: вы запускаете эксперимент. Ждете 15 минут, пока поднимется инстанс, зальются данные, установятся зависимости. Потом еще 3 часа на обучение. И только тогда понимаете, что забыли включить gradient clipping. Вся эта история стоит денег. Реальных. Но большинство считает только стоимость GPU-часов.

Это как считать цену автомобиля, учитывая только бензин. Игнорируя страховку, амортизацию, парковку.

90% ML-инженеров ошибаются в расчетах стоимости экспериментов. Они видят только цену GPU-часов, но не замечают setup tax и latency penalty.

Две грани одной проблемы

В ML-исследованиях есть два вида "налогов", которые съедают ваши ресурсы:

  • Setup Tax - плата за подготовку к эксперименту. Время на запуск инстанса, загрузку данных, установку зависимостей, проверку окружения.
  • Latency Penalty - штраф за медленное выполнение. Когда вы используете дешевые, но медленные инстансы (spot, более слабые GPU) и теряете время исследователя, который ждет результатов.

Идея простая: setup tax фиксирован для каждой итерации. Latency penalty пропорционален времени выполнения. Но их баланс определяет, какую инфраструктуру выбрать.

Формула, которую вы не найдете в документации AWS

Давайте посчитаем реальную стоимость одной итерации:

def true_iteration_cost(
    setup_time_minutes: float,
    researcher_hourly_rate: float,
    instance_hourly_cost: float,
    experiment_duration_minutes: float,
    iterations_per_experiment: int = 1
) -> float:
    """
    Рассчитывает истинную стоимость ML-итерации.
    
    Args:
        setup_time_minutes: время на подготовку (минуты)
        researcher_hourly_rate: стоимость часа работы исследователя ($)
        instance_hourly_cost: стоимость часа инстанса ($)
        experiment_duration_minutes: длительность эксперимента (минуты)
        iterations_per_experiment: сколько итераций за одну настройку
    """
    
    # Setup Tax
    setup_cost = (setup_time_minutes / 60) * researcher_hourly_rate
    
    # Instance Cost
    instance_cost = (experiment_duration_minutes / 60) * instance_hourly_cost
    
    # Latency Penalty
    latency_penalty = (experiment_duration_minutes / 60) * (researcher_hourly_rate * 0.3)
    # 0.3 - предполагаем, что исследователь теряет 30% продуктивности во время ожидания
    
    total_cost = setup_cost + instance_cost + latency_penalty
    cost_per_iteration = total_cost / iterations_per_experiment
    
    return {
        'total_cost': total_cost,
        'cost_per_iteration': cost_per_iteration,
        'setup_tax': setup_cost,
        'instance_cost': instance_cost,
        'latency_penalty': latency_penalty
    }
💡
Обратите внимание на коэффициент 0.3 для latency penalty. Это эмпирическое значение: исследователь не сидит и не смотрит на прогресс-бар. Он переключается на другие задачи, но теряет контекст, делает ошибки, требует больше времени на возвращение к эксперименту.

Реальный пример: spot против on-demand

Давайте сравним два сценария для обучения модели размером 7B параметров:

Параметр AWS g5.2xlarge (spot) AWS g5.2xlarge (on-demand)
Стоимость часа $0.48 $1.21
Время обучения 8 часов (часть времени на ожидание spot) 6 часов (стабильная работа)
Setup time 45 мин (чаще перезапуски) 20 мин
Ставка исследователя $80/час $80/час

Теперь посчитаем:

# Spot instance
spot_cost = true_iteration_cost(
    setup_time_minutes=45,
    researcher_hourly_rate=80,
    instance_hourly_cost=0.48,
    experiment_duration_minutes=8*60,  # 8 часов
    iterations_per_experiment=1
)

# On-demand instance
ondemand_cost = true_iteration_cost(
    setup_time_minutes=20,
    researcher_hourly_rate=80,
    instance_hourly_cost=1.21,
    experiment_duration_minutes=6*60,  # 6 часов
    iterations_per_experiment=1
)

print(f"Spot total: ${spot_cost['total_cost']:.2f}")
print(f"On-demand total: ${ondemand_cost['total_cost']:.2f}")

Результат вас удивит:

  • Spot instance: $396.40
  • On-demand: $298.60

Дешевый spot оказывается дороже на 33%. Потому что setup tax и latency penalty съедают всю экономию.

Когда spot все-таки выигрывает

Есть ситуации, где spot инстансы - лучший выбор:

  1. Длительные эксперименты (24+ часов) - setup tax становится незначительным
  2. Пакетные запуски - когда вы запускаете 50 экспериментов одним скриптом
  3. Автоматизированный пайплайн - где человеческий фактор исключен

Формула для определения границы:

def find_break_even_point(
    spot_setup_time: float,
    spot_hourly: float,
    ondemand_setup_time: float,
    ondemand_hourly: float,
    researcher_rate: float
) -> float:
    """
    Находит длительность эксперимента, при которой spot становится выгоднее.
    Возвращает длительность в часах.
    """
    # Упрощенная формула, игнорирующая latency penalty для spot
    # (предполагаем, что spot инстансы не прерываются)
    
    # Разница в setup tax
    setup_diff = (spot_setup_time - ondemand_setup_time) / 60 * researcher_rate
    
    # Разница в почасовой стоимости
    hourly_diff = ondemand_hourly - spot_hourly
    
    # Точка безубыточности
    if hourly_diff <= 0:
        return float('inf')  # spot никогда не выгоднее
    
    break_even_hours = setup_diff / hourly_diff
    return break_even_hours

Практический план: оптимизируйте ваш workflow

1 Измерьте ваши текущие метрики

Создайте простой скрипт для логирования:

import time
import json
from datetime import datetime

class ExperimentLogger:
    def __init__(self, experiment_name):
        self.start_time = time.time()
        self.phases = {}
        self.current_phase = None
        self.experiment_name = experiment_name
    
    def start_phase(self, phase_name):
        if self.current_phase:
            self.phases[self.current_phase]['end'] = time.time()
        self.current_phase = phase_name
        self.phases[phase_name] = {'start': time.time()}
    
    def end_experiment(self):
        if self.current_phase:
            self.phases[self.current_phase]['end'] = time.time()
        
        total_duration = time.time() - self.start_time
        
        report = {
            'experiment': self.experiment_name,
            'total_duration_hours': total_duration / 3600,
            'phases': {},
            'timestamp': datetime.now().isoformat()
        }
        
        for phase, times in self.phases.items():
            duration = (times['end'] - times['start']) / 60  # в минутах
            report['phases'][phase] = duration
        
        # Сохраняем в файл
        with open(f'experiment_log_{experiment_name}.json', 'w') as f:
            json.dump(report, f, indent=2)
        
        return report

Используйте его для каждого эксперимента:

logger = ExperimentLogger('bert_finetuning')
logger.start_phase('instance_setup')
# код запуска инстанса
logger.start_phase('data_loading')
# код загрузки данных
logger.start_phase('training')
# основной тренинг
logger.end_experiment()

2 Снижайте setup tax

Конкретные техники:

  • Используйте pre-built Docker images с установленными зависимостями
  • Кэшируйте датасеты в S3/EBS snapshots
  • Автоматизируйте проверку окружения перед запуском
  • Создайте шаблоны конфигураций для часто используемых setup'ов

Если работаете с локальными LLM, посмотрите статью про масштабирование локальных LLM - там есть полезные паттерны для ускорения запуска.

3 Управляйте latency penalty

Стратегии:

  • Параллельные эксперименты - запускайте несколько конфигураций одновременно
  • Ранняя остановка - убивайте плохие эксперименты через 20% времени
  • Используйте более быстрые инстансы для коротких итеративных экспериментов
  • Внедрите continuous training - обновляйте модели инкрементально

Парадокс: иногда дешевле арендовать A100 на час, чем ждать 8 часов на T4. Потому что время исследователя стоит дороже.

Ошибки, которые все совершают

Ошибка 1: Игнорирование стоимости контекстных переключений

Исследователь запустил эксперимент на 8 часов. Ушел домой. Утром обнаружил ошибку в лоссе. Потерял 8 часов GPU времени + 1 час на анализ. Правильный подход: запускать короткие smoke tests (1-2% данных) перед полноценным запуском.

Ошибка 2: Оптимизация не того этапа

Команда месяц оптимизировала скорость инференса на 5%, но setup time занимал 30 минут. Снизили setup time до 5 минут - получили 10x улучшение эффективности.

Ошибка 3: Неучет вероятности прерывания spot инстансов

В некоторых регионах вероятность прерывания spot достигает 20%. Каждое прерывание - это повторный setup tax. Формула для учета:

def expected_spot_cost(
    base_cost: float,
    interruption_probability: float,
    setup_cost: float
) -> float:
    """
    Ожидаемая стоимость с учетом вероятности прерывания.
    """
    expected_interruptions = interruption_probability
    expected_additional_setup = expected_interruptions * setup_cost
    return base_cost + expected_additional_setup

Когда локальные GPU выгоднее облачных

Интересный кейс: покупка собственного железа. В статье про локальные LLM vs API мы уже обсуждали этот вопрос. С точки зрения setup tax:

  • Локальная машина: setup time ≈ 0 (все уже установлено)
  • Облако: setup time = 5-30 минут

Если вы делаете 10 экспериментов в день, локальная машина экономит 50-300 минут ежедневно. За месяц это 25-150 часов времени исследователя.

Интеграция с MLOps пайплайнами

Современные инструменты могут автоматически рассчитывать эти метрики. Пример конфигурации для Metaflow:

# metaflow_config.yaml
cost_tracking:
  enabled: true
  researcher_hourly_rate: 80
  
  setup_phases:
    - name: environment_setup
      expected_duration_minutes: 5
    - name: data_loading
      expected_duration_minutes: 10
    
  instance_types:
    g4dn.xlarge:
      hourly_cost: 0.526
      typical_training_speed: 1.0  # baseline
    p3.2xlarge:
      hourly_cost: 3.06
      typical_training_speed: 3.2  # в 3.2 раза быстрее
  
  optimization_goal: minimize_total_cost  # или minimize_wall_time

Частые вопросы

Как оценить стоимость часа исследователя?

Формула: (годовая зарплата + налоги + офис + оборудование) / (рабочих часов в год). Для США типичный диапазон: $60-150/час. Для России: $20-50/час.

Что делать, если эксперименты часто падают?

Каждый fallback - это setup tax. Инвестируйте в стабильность:

  1. Добавьте retry логику с exponential backoff
  2. Используйте checkpointing каждые 30 минут
  3. Внедрите health checks для инстансов

Как считать для распределенного обучения?

Умножьте setup tax на количество нод. Latency penalty считается по самой медленной ноде. Плюс добавьте стоимость межнодовой коммуникации.

Что будет дальше?

Тренд - автоматический выбор инфраструктуры на основе predicted iteration cost. Представьте систему, которая:

  • Анализирует ваш код перед запуском
  • Предсказывает длительность эксперимента
  • Выбирает оптимальный тип инстанса (spot/on-demand, GPU тип)
  • Запускает smoke test автоматически
  • Рекомендует batch size и learning rate для минимизации total cost

Уже сейчас такие системы появляются в крупных компаниях. Через год-два они станут стандартом.

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

Начните считать сегодня. Добавьте одну строчку в ваш пайплайн:

print(f"True cost of this iteration: ${calculate_true_cost():.2f}")

И посмотрите, какие эксперименты на самом деле приносят прибыль, а какие просто сжигают деньги.