Стоимость обучения
Руководство описывает honestml 2.0.0. Изменения и переход с 1.x описаны в changelog. Инструменты benchmarks запускаются из checkout репозитория и не входят в wheel или sdist.
Время обучения и качество измеряются отдельно от детерминированного honesty-бенчмарка.
benchmarks/training_cost.py создаёт локальные отчёты производительности; benchmarks/run.py
и его baseline.json сохраняют собственный контракт без таймингов.
Быстрый поиск
SearchConfig включает ограниченный выбор модели и FS-рецепта. Числа в конфигурации задают вычислительный ресурс; они не являются подтверждёнными порогами качества или ускорения относительно прода.
from honestml import AutoML, SearchConfig, FeatureSelectionConfig, CVConfig
model = AutoML(
models=("catboost", "lightgbm"),
cv=CVConfig(scheme="timeseries_period", period="month", n_splits=3, n_es=1000),
feature_selection=FeatureSelectionConfig(
compare=("importance", "random_probe"),
refine=True,
),
search=SearchConfig(
max_rows=4096,
max_features=64,
model_iterations=64,
max_probe_fits=64,
reserve_fraction=0.3,
probe_fraction=0.2,
threads=1,
),
budget=600,
cache=".cache/churn-training",
)
# time_values содержит даты наблюдений; CV и purge задаются под горизонт таргета.
model.fit(X_train, y_train, time=time_values)
print(model.run_report_["search"])
print(model.run_report_["cost"]["fit_counts"])
Для заранее выбранного алгоритма задают, например, models=("catboost",): пробы семейств пропускаются. Одна FS-процедура может состоять из ранжирования, порогового отбора и последовательного уточнения. compare перечисляет альтернативные рецепты, а strategy без compare фиксирует один рецепт.
Семейства сравниваются на ограниченных строках исходных folds с сохранением широкой матрицы. Первая стадия оставляет максимум двух финалистов; они проверяются с большим лимитом строк и итераций на репрезентативно выбранных исходных folds. Все модели в пробах обучаются на fit-строках; ES служит validation для поддерживающих его моделей. Выборка сохраняет пропорции классов с минимум одной строкой каждого присутствующего класса. В отчёте отдельно видны малый ES, недостаток событий, смена лидера и незавершённое подтверждение.
model_margin задаёт абсолютный допустимый проигрыш DEV-метрики при выборе по прогнозируемой стоимости завершения. По умолчанию он равен нулю. Более дешёвая модель принимается только после парной односторонней проверки non-inferiority. Это правило поиска на DEV; независимая приёмка итогового качества выполняется отдельно. Поле estimated_cv_refit_s описывает только CV/refit, а completion_cost_forecast добавляет условную стоимость поддержанных FS/HPO-процедур. CV оценивается по наблюдавшимся остановкам и потолку обучения; refit — отдельно по текущему известному бюджету фабрики без ES. Будущие параметры HPO неизвестны, поэтому прогноз остаётся условным. Если бюджет refit известен, но положительное число наблюдавшихся CV-итераций недоступно, оценка CV/refit равна None с причиной missing_cv_iterations: такая стоимость не служит основанием для замены лидера по качеству. DEV/full refit использует бюджет выбранной фабрики, включая явные параметры и результат HPO.
Общий train-only фильтр сокращает ширину FS-проб; рецепты подтверждаются выбранным семейством на отдельных контрольных строках. Пробы не используют target-encoded колонки; исходные категории доступны. Если классы не помещаются в FS-лимит строк, сохраняется готовый широкий контроль с явной причиной.
После выбора полный CV и HPO получают одно семейство. Выбранный FS-рецепт исполняется на ограниченном ресурсе внутри train-частей исходных folds: ранжирование, сокращение ширины и уточнение. Null/random-probe этапы используют до трёх повторов, прокси — лимит model_iterations; полный итоговый train использует весь предусмотренный объём данных. Широкий DEV-контроль сохраняется как опорный результат и переиспользуется при остановке FS. Подтверждение общего subset на том же DEV имеет статус post_search_dev.
Итоговый выбор между широким и сокращённым вариантом следует действующему статистическому правилу: внутри полосы эквивалентности допустим более компактный набор с несколько меньшим score. Эта проверка не устраняет адаптивность поиска. По умолчанию HPO выключен; при включении его IID inner folds выделяют ES внутри inner-train, сохраняя отдельный inner-test.
max_rows ограничивает суммарные размеры fit/ES/test частей первой стадии и строки каждого FS-прокси fit; max_folds — число folds первой стадии. confirmation_rows, confirmation_iterations, confirmation_folds задают второй ресурс, не меньший первого. Их значения по умолчанию — 65536, 256 и 2; первая стадия использует 4096 строк, 64 итерации и один fold. Лимит строк каждого этапа суммирует fit/ES/test по его folds для одной семьи, включая повторные вхождения строк между folds.
min_es_rows=10 и min_class_count=5 — диагностические минимумы, а не гарантии статистической мощности. max_features ограничивает ширину FS-проб; при уточнении сохраняются действующие минимумы признаков и важностной массы. max_probe_fits=64 ограничивает фактические обучения обеих модельных стадий и FS-проб вместе. Отдельный max_fs_fits=64 ограничивает исполнение выбранного рецепта. Лимиты считаются перед backend fit; незавершённый subset не становится итоговым.
Нативный fit прерывается только на границе следующей операции. Временной резерв сохраняет возможность завершения, но не гарантирует жёсткий deadline; overshoot_s раскрывает фактическое превышение. При budget=None общий временной бюджет отсутствует.
threads ограничивает поддерживающие этот контракт бустинги и ExtraTrees-прокси. Потоки BLAS, Polars и построения SHAP-background требуют отдельного ресурсного контракта. Память проверяется перед fit, включая широкий контроль и refit; уже выполняющийся нативный fit может превысить лимит до следующей проверки.
Завершённая подготовка FS сохраняется перед HPO, trials — по мере завершения, полный selection — после завершения поиска. Повтор с тем же вычислительным контекстом восстанавливает эти стадии; refit учитывается отдельно. Частичный поиск и временный отказ не закрепляют candidate/selection cache. Изменение данных, split, seed, конфигурации или значимых версий означает новый scope. При фиксированном числе trials HPO воспроизводит историю sampler, включая завершённые ошибки; для wall-clock режима идентичная последовательность не обещается.
Для регулярного переобучения фиксируют search.selected_model и итоговый блок feature_selection отчёта. Если FS принят, его strategy задаёт процедуру: устанавливают compare=None, сохраняют её параметры и исходный SearchConfig, чтобы сохранить вычислительные ограничения. Единственный models пропускает оба этапа выбора семейства; один рецепт пропускает сравнение FS-альтернатив. search=None выполняет процедуру с её полными FS-настройками.
Если итоговый блок feature_selection равен None, FS отключают. Поле search.selected_recipe описывает предварительный выбор и само по себе не задаёт итоговый рецепт: контроль может вернуть широкий вариант. Фиксированный набор признаков задают проекцией входной таблицы на сохранённые имена и отключением FS.
Оценки DEV после поиска отмечены как post_search_dev. Качество проверяют на отдельном итоговом периоде, который не участвовал в выборе модели, FS и порогов. Приёмка качества и численные цели времени остаются открытыми. Содержимое preset="fast" сохраняет отдельный существующий контракт; ограниченный поиск включается явно через search.
Прогноз FS/HPO до выбора семейства
После подтверждения максимум двух семейств поиск измеряет стоимость одной фиксированной FS-процедуры и HPO каждого финалиста. Измерения входят в общий max_probe_fits и временной бюджет проб. Основные FS/HPO запускаются после выбора семейства. Результаты профиля не используются как выбранный subset или история trials.
FS профилируется на двух лимитах train-строк первого исходного DEV fold с сохранением его test-части, seed и ограничений рецепта. Измеряется процедура, включая ранжирование, доступное уточнение и локальную статистику. Отчёт разделяет наблюдаемую траекторию и верхнюю границу числа fit; потолок не считается ожидаемым количеством обучений. Перенос времени на остальные folds предполагает сходную траекторию и пропорциональную стоимость строк.
HPO использует центральную конфигурацию пространства параметров на двух различных ресурсах. Прогноз предполагает выполнение всех запрошенных trials на этой конфигурации и полной ширине признаков. Фактические параметры, early stopping и выбранный FS-subset могут изменить стоимость. Подготовка матрицы, target encoding, sampler и checkpoint в эту компоненту не входят. timeout_s записывается как лимит, а не как оценка времени.
В run_report_["search"]["completion_cost_forecast"] для каждого семейства доступны:
fsиhpo: измерения, условная оценка секунд, seed, хэши контекста, ограничения и ошибка переноса между двумя ресурсами.cv_refit_s,additional_cv_s,additional_cv_count: широкий контроль, предусмотренные повторные CV и refit. При HPO сkeep_baseline=Trueучитываются оба последующих CV.estimated_s: сумма включённых процедур;status="conditional"обозначает зависимость от перечисленных допущений.
Это оценка оставшейся включённой работы. Уже выполненные пробы, общая статистика, ансамбль, ввод-вывод и перечисленные расходы подготовки исключены; сумма не является прогнозом полного wall time.
Несколько альтернатив FS в compare, неподдержанный estimator, одинаковые фактические ресурсы или исчерпанный бюджет дают estimated_s=None с причиной. При неизвестной компоненте поиск сохраняет лидера подтверждения по качеству; время не служит основанием для его замены. Один явно заданный алгоритм пропускает пробы и прогноз вместе с ними, сохраняя быстрый путь переобучения.
Ошибка профиля — диагностический замер, а не гарантия точности. На малых данных постоянные расходы и прогрев могут преобладать; точность прогноза проверяют на согласованном DEV-сценарии отдельно от внешней приёмки качества.
Статистика и диагностические прогоны
Bootstrap встроенных accuracy, MAE и RMSE использует подготовленные построчные вклады на поддерживаемых входах. Для log loss этот путь проверен на scikit-learn 1.7.2; другие версии используют общий scorer. ROC-AUC и PR-AUC переиспользуют подготовленное ранжирование для поддержанных бинарных входов без весов на scikit-learn 1.7.2. Неподдержанные входы, пользовательские и изменённые scorers, включая аддитивные, используют общий путь. Число bootstrap-повторов, RNG, веса, классы и критерии выбора сохраняются.
Внутри построения ансамбля ограниченный кеш встроенного scorer переиспользует повторные оценки при фиксированных target и весах. Пользовательские и изменённые scorers обходят этот кеш. Статистический критерий и правило принятия ансамбля сохраняются.
benchmarks/bootstrap_scoring.py сравнивает статистику на фиксированных предсказаниях без обучения моделей. benchmarks/search_diagnostics.py выполняет локальные DEV-абляции ресурса и сопоставимые копии notebook-сценариев; исходные ноутбуки сохраняются. Native/BLAS threads, HPO до пяти trials на семейство, seeds, порядок и provenance записываются отдельно. Просмотренные holdout не используются для настройки политики.
Что означает отчёт AutoML
total_wall_s фиксирует завершённое обучение и сборку отчёта до его отправки во внешний tracker. Tracker и run_report_ получают один итоговый снимок; длительность внешней публикации измеряют вокруг вызова fit, если она входит в границы сравнения.
run_report_["cost"] содержит независимо измеренное total_wall_s, сумму
attributed_wall_s и overhead_s. Последние два значения объясняют полное время.
exclusive_timings исключает вложенную работу: HPO внутри selection не прибавляется к
полной selection второй раз. stages сохраняет каждый повторный вызов с parent_id,
временем и исходом, в том числе несколько refit. Поле timings содержит накопленные
включающие длительности и не предназначено для суммирования полного времени.
В work записываются модель, стадия, recipe для FS-процедуры, trial/fold, число строк и столбцов, длительность,
лимит деревьев и выполненные итерации, если backend их сообщает. fit_counts
различает завершённые и неуспешные попытки. Записи работы уточняют стоимость стадий;
их длительности не прибавляются ещё раз к total_wall_s. Историческое время из кеша
помечается source="cache" и не входит в измеренное время текущего прогона.
RSS снимается на границах операций при доступном optional-пакете psutil.
sampled_peak_rss_mb — максимум этих наблюдений, а не непрерывно измеренный пик.
Без probe память обозначается null, а memory_measurement="unavailable".
Отчёт включает CPU count, ОС, Python, версии библиотек и доступный общий объём RAM.
Исходные строки, значения признаков и предсказания в отчёт стоимости не входят.
Сопоставимые сценарии
| Сценарий | Работа | Назначение |
|---|---|---|
manual_reference |
Принятый ручной рецепт с отключённым HPO | База времени и качества |
fixed_recipe |
Те же данные, признаки, split, модель, параметры, ES и число fit/refit | Накладные расходы библиотеки |
full_search |
Полный поиск с заранее заданным бюджетом | Стоимость выбора с нуля |
fast_search |
Ограниченный поиск с тем же независимым оценочным набором | Компромисс времени и качества |
Каждый сценарий измеряется отдельно в cold и warm. Подготовка состояния кеша
выполняется явно перед каждым повтором и не входит в timed-область. Для холодного
прогона используют отдельный пустой каталог кеша; для тёплого — заранее подготовленный
кеш того же рецепта. Обёртка ничего не удаляет и не прогревает автоматически.
Состояние кеша файловой системы/ОС и процесса следует фиксировать в протоколе среды:
cold здесь характеризует объявленный прикладной кеш, а не принудительный сброс
кеша ОС. Модели для каждого повтора создаются заново.
В ручном ориентире 03_train.ipynb, ветка TUNE=False, финальный CatBoost получает
iterations=1500, learning_rate=0.03, depth=6, early_stopping_rounds=80.
Валидация отделяется по двум последним уникальным значениям DATE; соответствие
этих значений календарным месяцам проверяется на локальных данных. При включённом
FS рецепт содержит random-baseline на подвыборке до 262000 строк с тремя seeds и
двумя голосами, cumulative importance 0.99, затем backward elimination с 250
итерациями и относительным допуском 0.011. Отключение HPO не исключает стоимость FS.
Для измерения накладных расходов фиксируют итоговый набор признаков и сравнивают одинаковую работу. Для поиска с нуля стоимость FS включают в обе timed-области либо явно задают разные рецепты и сравнивают их как поисковые стратегии. Валидация, участвующая в early stopping или отборе признаков, не служит независимым итоговым тестом. Итоговое качество всех сценариев оценивается на одном неизменном OOT/holdout.
Локальный замер
Из корня проекта функции доступны через benchmarks.training_cost:
from pathlib import Path
import json
from benchmarks.training_cost import TrainingManifest, WorkRecipe, measure_training
manifest = TrainingManifest(
dataset_id="sha256-of-local-data",
evaluation_id="sha256-of-untouched-evaluation-partition",
scenario="fixed_recipe",
seed=42,
cache_mode="cold",
work=WorkRecipe(
split_id="sha256-of-train-and-es-partitions",
feature_set_id="sha256-of-ordered-feature-schema",
estimator="catboost",
parameters={"learning_rate": 0.03, "depth": 6},
tree_budget=1500,
early_stopping_rounds=80,
fit_count=1,
refit_count=0,
fs_recipe_id=None,
),
training_boundary="prepared matrices through fitted model; FS excluded",
)
# callables are supplied by the local experiment; no data is sent externally
report = measure_training(
train_local_recipe,
manifest=manifest,
repeats=3,
prepare=prepare_local_cache_state,
save=save_local_model,
predict=predict_untouched_evaluation,
evaluate=evaluate_untouched_predictions,
)
Path("training-cost-fixed-cold.json").write_text(
json.dumps(report, indent=2, allow_nan=False), encoding="utf-8"
)
train_local_recipe() возвращает новую обученную модель. prepare_local_cache_state(i)
устанавливает состояние перед повтором i. save_local_model(model) и
predict_untouched_evaluation(model) выполняют отдельные измеряемые операции;
evaluate_untouched_predictions(model, predictions) возвращает только словарь численных
метрик и вызывается после закрытия timed-области. Необязательные save/predict можно
опустить, но состав операций должен совпадать в сравнении. Подготовка данных внутри
train входит в обучение; подготовка снаружи отражается в training_boundary.
Отчёт сохраняет каждый повтор, медиану общего времени, медианы training/save/inference,
агрегаты стоимости AutoML при наличии и медианы качества. Оценка качества и подготовка
кеша явно исключены из wall time. Отдельные предсказания не сохраняются. В примере
fit_count и refit_count являются декларацией рецепта; фактические события cost.work
нужны для проверки выполненной работы.
Сравнение и критерии
python benchmarks/training_cost.py manual-cold.json fixed-cold.json --metric average_precision --output comparison.json
Для сравнения full и fast используется --search-comparison. Для метрик потерь
передают --lower-is-better. average_precision и pr_auc различаются:
binary_quality(y, scores) считает AP через average precision, а pr_auc через
трапецеидальную площадь precision-recall. Эти значения не подменяют друг друга.
Сравнение требует одинаковых идентификаторов данных и итоговой оценки, seed, среды,
состояния кеша и границ операций. Для fixed-рецепта дополнительно должны совпадать
split, признаки, параметры, ES, лимит деревьев, FS-рецепт и число fit/refit. При
расхождении выдаётся not_comparable; отношение времён остаётся диагностикой.
Совпадение деклараций само по себе не доказывает равную выполненную работу.
Без согласованных --quality-tolerance и --max-time-ratio результат имеет статус
pending_thresholds. Допуск качества задаётся в абсолютных единицах указанной метрики,
отношение времени — candidate/reference. После сверки фактических операций можно
передать --work-verified; до такой сверки успешный fixed-сценарий получает
pending_work_review. Флаг фиксирует проверку владельца эксперимента, а не заменяет её.
Корпоративный коэффициент ускорения и допустимая потеря качества подтверждаются локальным запуском на принятом наборе данных. Synthetic/unit проверки доказывают арифметику и границы измерителя; они не подтверждают эти коэффициенты.
Парная проверка качества на внешнем наборе
QualityContract, PredictionBatch, freeze_quality_contract и evaluate_paired_quality из benchmarks.training_cost проверяют одну фиксированную пару предсказаний отдельно от времени обучения. До доступа к итоговым меткам согласуют метрику, положительный класс и порядок классов, абсолютный допуск, alpha, seed и число bootstrap-повторов; фиксируют обе модели, предсказания, веса, блоки и оценочную популяцию. Требуется n_bootstrap * alpha >= 50.
В этой API metric="pr_auc" означает библиотечную average precision, в отличие от трапециевидного поля binary_quality()["pr_auc"] выше. Направление берётся из выбранного библиотечного scorer. Для binary поддержаны roc_auc/pr_auc/log_loss/brier и одномерная P(positive); для multiclass — log_loss/brier и матрица вероятностей в явно заданном отсортированном порядке классов; для regression — rmse/mae, classes=(), positive=None.
Хранитель внешнего набора готовит NPZ ровно с массивами y_true и row_ids, без pickle, и заранее передаёт его SHA-256. Уникальные строковые row IDs должны иметь одну стабильную область идентификаторов с training/tuning inventories. Пример использует локальный согласованный quality-plan.json, отдельный used-row-ids.json с четырьмя inventories и две NPZ предсказаний с полями values, row_ids:
import json
from pathlib import Path
import numpy as np
from benchmarks.training_cost import (
PredictionBatch, QualityContract, evaluate_paired_quality, freeze_quality_contract,
)
plan = json.loads(Path("quality-plan.json").read_text(encoding="utf-8"))
used = json.loads(Path("used-row-ids.json").read_text(encoding="utf-8"))
contract = QualityContract(
task="binary", metric="pr_auc", classes=(0, 1), positive=1,
margin=plan["absolute_margin"], alpha=plan["alpha"],
seed=plan["bootstrap_seed"], n_bootstrap=plan["n_bootstrap"],
baseline_artifact_sha256=plan["baseline_artifact_sha256"],
candidate_artifact_sha256=plan["candidate_artifact_sha256"],
external_partition_sha256=plan["labels_npz_sha256"],
row_id_namespace=plan["row_id_namespace"],
)
def load_predictions(path: Path, artifact_sha: str) -> PredictionBatch:
with np.load(path, allow_pickle=False) as data:
return PredictionBatch(
artifact_sha256=artifact_sha,
row_ids=tuple(data["row_ids"].tolist()),
values=data["values"].copy(),
classes=contract.classes, positive=contract.positive,
)
baseline = load_predictions(Path("baseline-predictions.npz"), contract.baseline_artifact_sha256)
candidate = load_predictions(Path("candidate-predictions.npz"), contract.candidate_artifact_sha256)
receipt = Path("quality-receipt.json")
receipt_sha = freeze_quality_contract(
receipt, contract, baseline=baseline, candidate=candidate,
baseline_training_ids=used["baseline_training"],
baseline_tuning_ids=used["baseline_tuning"],
candidate_training_ids=used["candidate_training"],
candidate_tuning_ids=used["candidate_tuning"],
provenance={"review_plan": "quality-plan.json", "lineage": "used-row-ids.json"},
)
# доступ к файлу меток предоставляется после фиксации receipt
result = evaluate_paired_quality(
receipt, frozen_sha256=receipt_sha, baseline=baseline, candidate=candidate,
labels_path=Path("external-labels.npz"),
)
Path("quality-result.json").write_text(
json.dumps(result, indent=2, allow_nan=False), encoding="utf-8",
)
Freeze создаёт новый receipt эксклюзивно и не читает метки. Он связывает SHA артефактов, массивы предсказаний, порядок строк, веса/блоки и версии/scorer SHA. Evaluate проверяет их неизменность, затем сверяет SHA NPZ до загрузки тех же bytes с allow_pickle=False. Перестановка строк не исправляется автоматически; несовпадение строк/классов, комплексные или нечисловые предсказания и невалидные вероятности отвергаются.
При наличии весов передают одинаковые sample_weight в обеих PredictionBatch; блоки зависимости — одинаковые целочисленные block_index. Они фиксируются до меток. После проверки полных fingerprints строки с нулевым весом исключаются из score и парного bootstrap. Остаются минимум две строки положительного веса и, при блоках, минимум два непустых блока; каждый заданный класс должен иметь положительную массу. Отчёт раскрывает effective_rows, excluded_zero_weight_rows и effective_blocks.
Training/tuning inventories включают все строки, использованные в FS, ES, калибровке и выборе модели; известное пересечение с внешними row IDs блокирует freeze. None означает неизвестную историю, пустой список — декларацию отсутствия строк в данной роли. Проверка полноты истории, отсутствия прежнего просмотра меток, независимости групп/периодов и происхождения предсказаний от заявленных артефактов остаётся внешней обязанностью.
statistical_verdict="passed" означает успешную парную NI в согласованном абсолютном допуске; failed — что она не установлена. Это не совместная приёмка качества и скорости: independence_review.status="pending" и quality_acceptance="pending_review" сохраняются независимо от NI. Receipt и названия файлов сами по себе не доказывают независимость данных или production-готовность.
Измеренные результаты и границы их интерпретации приведены в сводке контрольных прогонов.