Кэш прогнозов модели: что включить в ключ кроме id клиента

Кэш по id клиента возвращает устаревший прогноз после обновления признаков или модели. Составим ключ из идентичности вычисления и проверим, какие изменения должны давать новый результат.

Один клиент — несколько разных вычислений

Модель оценивает вероятность покупки, а API кэширует ответ по id клиента на час. Через десять минут клиент оформил заказ, история изменилась, но сервис продолжает возвращать старую оценку. Затем обновилась модель, и новая версия тоже получает прежнее значение. Проблема в том, что id человека не описывает выполненное вычисление.

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

Хранить весь вход прямо в ключе неудобно, поэтому его часто сериализуют в однозначный байтовый вид и хешируют. Хеш не исправляет пропущенную зависимость: если забыть версию модели, разные модели всё равно разделят один ключ. Сначала определите договор о вычислении, затем способ компактного представления.

Стабильная сериализация и новый ключ при изменении

В примере вход содержит целочисленные признаки, версию pipeline и дату решения. JSON записывается с отсортированными ключами, без лишних пробелов и с запретом NaN. Поэтому два словаря с разным порядком полей дадут одинаковую строку, а изменение значения или версии — другой ключ.

Для дробных чисел и дат договоритесь о формате заранее: 1 и 1.0, разные часовые пояса и эквивалентные округления могут давать разные байты. Иногда проще хешировать уже подготовленный входной тензор вместе с формой и dtype. Тогда кэш расположен после preprocessing, и стоимость получения признаков он не убирает.

import hashlib
import json
def key(version, features, context):
    payload = {"pipeline": version, "features": features, "context": context}
    raw = json.dumps(payload, sort_keys=True, separators=(",", ":"),
                     ensure_ascii=False, allow_nan=False).encode("utf-8")
    return hashlib.sha256(raw).hexdigest()
context = {"date": "2026-10-01", "horizon_days": 30}
first = key("v1", {"orders": 2, "days_since": 10}, context)
same = key("v1", {"days_since": 10, "orders": 2}, context)
new_data = key("v1", {"orders": 3, "days_since": 10}, context)
new_model = key("v2", {"orders": 2, "days_since": 10}, context)
print("same input:", first == same)
print("new data:", first == new_data, "new model:", first == new_model)
assert first == same and first != new_data and first != new_model

Перестановка полей сохраняет ключ, новые признаки и новая версия его меняют. В значении кэша полезно хранить версию, время расчёта и срок применимости. Это позволяет объяснить происхождение ответа и не выдавать технический hit за гарантию актуальности.

TTL отвечает на отдельный вопрос: как долго результат разрешено использовать. Он ограничивает устаревание только при корректно заданном контексте. Например, days_since меняется со временем даже без новых событий, поэтому бессрочный ключ по последнему заказу неверен. Включите дату расчёта признака либо версию снимка, которая меняется по расписанию.

Проверьте границы клиента и организации. При мультитенантном сервисе одинаковые внешние id могут относиться к разным заказчикам. Если признаки или модель зависят от организации, её область должна присутствовать в ключе либо в физическом пространстве кэша. Доступ к кэшу при этом остаётся обычным доступом к данным; SHA-256 сам по себе не является шифрованием.

Перед внедрением измерьте hit rate и время на подготовку ключа. Если почти каждый запрос имеет уникальный временной контекст, кэш прогнозов может не окупиться. Возможно, полезнее кэшировать медленно меняющиеся признаки. Выбор слоя кэширования следует из измеренного повторения вычислений и допустимой свежести, а не из общего желания ускорить API.

Для следующего шага пригодится сохранение preprocessing вместе с моделью.

В платном курсе «ML Engineer / Data Scientist: табличные модели в продакшн» вы соберёте сохраняемый inference pipeline и REST API. Явные входы и версия модели в таком пайплайне дают основу для корректного повторного использования результатов. Откройте программу курса.

Продолжить на практике

  • ML Engineer / Data Scientist: табличные модели в продакшн ↗

    Практический курс для тех, кто хочет получить навыки ML Engineer / Data Scientist на реальных табличных задачах. В курсе Вы не просто обучите CatBoost/LightGBM, а соберёте полноценный production-style пайплайн: подготовка данных, feature engineering (генерация признаков), корректная валидация, защита от leakage (утечек), тюнинг через Optuna, интерпретация через SHAP, batch-инференс и REST API для предсказаний.