Внедрение новой модели машинного обучения в продуктивную среду — один из наиболее важных этапов жизненного цикла ML. Даже если модель хорошо работает на валидационных и тестовых наборах данных, прямая замена существующей продуктовой модели может быть рискованной. Офлайн-оценка редко учитывает всю сложность реальных условий — распределение данных может меняться, поведение пользователей может изменяться, а системные ограничения в продуктовой среде могут отличаться от условий контролируемых экспериментов.
В результате модель, которая во время разработки казалась превосходной, может ухудшить производительность или негативно повлиять на взаимодействие с пользователем после внедрения. Чтобы снизить эти риски, команды ML используют стратегии контролируемого развёртывания, которые позволяют им оценивать новые модели в реальных производственных условиях, минимизируя потенциальные сбои.
В этой статье мы рассмотрим четыре широко используемые стратегии — A/B-тестирование, канареечное тестирование, интерлейвированное тестирование и теневое тестирование — которые помогают организациям безопасно внедрять и проверять новые модели машинного обучения в производственных средах.
A/B-тестирование
A/B-тестирование — одна из наиболее широко используемых стратегий безопасного внедрения новой модели машинного обучения в продуктивную среду. В этом подходе входящий трафик распределяется между двумя версиями системы: существующей устаревшей моделью (контроль) и моделью-кандидатом (вариация). Распределение обычно неравномерное, чтобы ограничить риск — например, 90% запросов могут по-прежнему обрабатываться устаревшей моделью, а только 10% направляются модели-кандидату.
Подвергая обе модели реальному трафику, команды могут сравнивать такие показатели производительности, как коэффициент кликабельности, конверсии, вовлечённость или доход. Этот контролируемый эксперимент позволяет организациям оценить, действительно ли модель-кандидат улучшает результаты, прежде чем постепенно увеличивать долю трафика или полностью заменять устаревшую модель.
Канареечное тестирование
Канареечное тестирование — это стратегия контролируемого развёртывания, при которой новая модель сначала внедряется для небольшого подмножества пользователей, а затем постепенно распространяется на всю пользовательскую базу. Название происходит от старой практики шахтёров, которые брали с собой канареек в угольные шахты для обнаружения токсичных газов — птицы реагировали первыми, предупреждая шахтёров об опасности.
В развёртывании машинного обучения модель-кандидат изначально доступна для ограниченной группы пользователей, в то время как большинство продолжают обслуживаться устаревшей моделью. В отличие от A/B-тестирования, которое случайным образом распределяет трафик между всеми пользователями, канареечное тестирование нацелено на определённое подмножество и постепенно увеличивает охват, если показатели производительности указывают на успех.
Интерлейвированное тестирование
Интерлейвированное тестирование оценивает несколько моделей путём смешивания их выходных данных в одном ответе, показываемом пользователям. Вместо того чтобы направлять весь запрос либо к устаревшей, либо к модели-кандидату, система объединяет прогнозы обеих моделей в режиме реального времени.
Система затем регистрирует сигналы вовлечённости — такие как коэффициент кликабельности, время просмотра или негативная обратная связь — для каждой рекомендации. Поскольку обе модели оцениваются в рамках одного взаимодействия с пользователем, интерлейвированное тестирование позволяет командам сравнивать производительность более напрямую и эффективно, минимизируя искажения, вызванные различиями в группах пользователей или распределении трафика.
Теневое тестирование
Теневое тестирование, также известное как теневое развёртывание или тёмный запуск, позволяет командам оценивать новую модель машинного обучения в реальной производственной среде без влияния на взаимодействие с пользователем. В этом подходе модель-кандидат работает параллельно с устаревшей моделью и получает те же живые запросы, что и продуктивная система. Однако пользователям возвращаются только прогнозы устаревшей модели, а выходные данные модели-кандидата просто записываются для анализа.
Эта настройка помогает командам оценить, как новая модель ведёт себя при реальном трафике и условиях инфраструктуры, которые часто трудно воспроизвести в офлайн-экспериментах. Теневое тестирование обеспечивает способ сравнения модели-кандидата с устаревшей моделью с низким риском, хотя оно не может зафиксировать истинные показатели вовлечённости пользователей — такие как клики, время просмотра или конверсии — поскольку его прогнозы никогда не показываются пользователям.
Моделирование стратегий развёртывания ML
Настройка
Перед моделированием любой стратегии нам нужны две вещи: способ представления входящих запросов и замена каждой модели.
Каждая модель — это просто функция, которая принимает запрос и возвращает оценку — число, которое приблизительно представляет, насколько хороша рекомендация этой модели. Оценка устаревшей модели ограничена 0,35, а модели-кандидата — 0,55, что делает модель-кандидат намеренно лучше, чтобы мы могли проверить, что каждая стратегия действительно обнаруживает улучшение.
`make_requests()` генерирует 200 запросов, распределённых между 40 пользователями, что даёт нам достаточно трафика, чтобы увидеть значимые различия между стратегиями, сохраняя при этом лёгкость симуляции.
«`python
import random
import hashlib
random.seed(42)
def legacy_model(request):
return {«model»: «legacy», «score»: random.random() * 0.35}
def candidate_model(request):
return {«model»: «candidate», «score»: random.random() * 0.55}
def make_requests(n=200):
users = [f»user_{i}» for i in range(40)]
return [{«id»: f»req_{i}», «user»: random.choice(users)} for i in range(n)]
requests = make_requests()
«`
A/B-тестирование
`ab_route()` — это ядро этой стратегии — для каждого входящего запроса она генерирует случайное число и направляет запрос к модели-кандидату только если это число падает ниже 0,10, в противном случае запрос направляется к устаревшей модели. Это даёт кандидату примерно 10% трафика.
Мы собираем оценки прогнозов от каждой модели отдельно и вычисляем среднее значение в конце. В реальной системе эти оценки будут заменены фактическими показателями вовлечённости, такими как коэффициент кликабельности или время просмотра — здесь оценка просто заменяет «насколько хороша была эта рекомендация».
«`python
print(«── 1. A/B Testing ──────────────────────────────────────────»)
CANDIDATE_TRAFFIC = 0.10 # 10 % of requests go to candidate
def ab_route(request):
return candidatemodel if random.random() < CANDIDATETRAFFIC else legacy_model
results = {«legacy»: [], «candidate»: []}
for req in requests:
model = ab_route(req)
pred = model(req)
results[pred[«model»]].append(pred[«score»])
for name, scores in results.items():
print(f»{name:12s} | requests: {len(scores):3d} | avg score: {sum(scores)/len(scores):.3f}»)
«`
Канареечное тестирование
Ключевая функция здесь — `getcanaryusers()`, которая использует MD5-хеш для детерминированного назначения пользователей в канареечную группу. Важное слово — детерминированный — сортировка пользователей по их хешу означает, что одни и те же пользователи всегда оказываются в канареечной группе при перезапуске, что отражает то, как работают реальные канареечные развёртывания, где конкретный пользователь последовательно видит одну и ту же модель.
Мы затем моделируем три фазы, просто расширяя долю канареечных пользователей — 5%, 20% и 50%. Для каждого запроса маршрутизация определяется тем, принадлежит ли пользователь к канареечной группе, а не случайным подбрасыванием монеты, как при A/B-тестировании. Это фундаментальное различие между двумя стратегиями: A/B-тестирование разделяет по запросам, канареечное тестирование разделяет по пользователям.
«`python
print(«\n── 2. Canary Testing ───────────────────────────────────────»)
def getcanaryusers(all_users, fraction):
«»»Deterministic user assignment via hash — stable across restarts.»»»
n = max(1, int(len(all_users) * fraction))
ranked = sorted(all_users, key=lambda u: hashlib.md5(u.encode()).hexdigest())
return set(ranked[:n])
all_users = list(set(r[«user»] for r in requests))
for phase, fraction in [(«Phase 1 (5%)», 0.05), («Phase 2 (20%)», 0.20), («Phase 3 (50%)», 0.50)]:
canaryusers = getcanaryusers(allusers, fraction)
scores = {«legacy»: [], «candidate»: []}
for req in requests:
model = candidatemodel if req[«user»] in canaryusers else legacy_model
pred = model(req)
scores[pred[«model»]].append(pred[«score»])
print(f»{phase} | canary users: {len(canary_users):2d} «
f»| legacy avg: {sum(scores[‘legacy’])/max(1,len(scores[‘legacy’])):.3f} «
f»| candidate avg: {sum(scores[‘candidate’])/max(1,len(scores[‘candidate’])):.3f}»)
«`
Интерлейвированное тестирование
Обе модели работают над каждым запросом, и `interleave()` объединяет их выходные данные путём чередования элементов — один из устаревших, один из кандидатов, один из устаревших и так далее. Каждый элемент помечен своей исходной моделью, поэтому, когда пользователь нажимает что-то, мы точно знаем, какую модель приписать.
«`python
print(«\n── 3. Interleaved Testing ──────────────────────────────────»)
def interleave(preda, predb):
«»»Alternate items: A, B, A, B … tagged with their source model.»»»
itemsa = [(«legacy», preda[«score»] + random.uniform(-0.05, 0.05)) for _ in range(3)]
itemsb = [(«candidate», predb[«score»] + random.uniform(-0.05, 0.05)) for _ in range(3)]
merged = []
for a, b in zip(itemsa, itemsb):
merged += [a, b]
return merged
clicks = {«legacy»: 0, «candidate»: 0}
shown = {«legacy»: 0, «candidate»: 0}
for req in requests:
predl = legacymodel(req)
predc = candidatemodel(req)
for source, score in interleave(predl, predc):
shown[source] += 1
clicks[source] += int(random.random() < score) # click ~ score
for name in [«legacy», «candidate»]:
print(f»{name:12s} | impressions: {shown[name]:4d} «
f»| clicks: {clicks[name]:3d} «
f»| CTR: {clicks[name]/shown[name]:.3f}»)
«`
Теневое тестирование
Обе модели работают над каждым запросом, но цикл делает чёткое различие — `livepred` — это то, что видит пользователь, `shadowpred` идёт прямо в журнал и ничего больше. Вывод кандидата никогда не возвращается, никогда не показывается, никогда не используется. Список журналов — это вся суть теневого тестирования. В реальной системе это будет записано в базу данных или хранилище данных, и инженеры позже запросят его, чтобы сравнить распределения задержек, шаблоны вывода или распределения оценок по сравнению с устаревшей моделью — и всё это без единого пользователя.
«`python
print(«\n── 4. Shadow Testing ───────────────────────────────────────»)
log = [] # candidate’s shadow log
for req in requests:
# What the user sees
livepred = legacymodel(req)
# Shadow run — never shown to user
shadowpred = candidatemodel(req)
log.append({
«request_id»: req[«id»],
«legacyscore»: livepred[«score»],
«candidatescore»: shadowpred[«score»], # logged, not served
})
avglegacy = sum(r[«legacyscore»] for r in log) / len(log)
avgcandidate = sum(r[«candidatescore»] for r in log) / len(log)
print(f» Legacy avg score (served): {avg_legacy:.3f}»)
print(f» Candidate avg score (logged): {avg_candidate:.3f}»)
print(f» Note: candidate score has no click validation — shadow only.»)
1. Какие стратегии контролируемого развёртывания моделей машинного обучения описаны в статье и в чём их суть?
Ответ: в статье описаны четыре стратегии контролируемого развёртывания моделей машинного обучения:
* A/B-тестирование — распределение трафика между существующей и новой моделями для сравнения их производительности.
* Канареечное тестирование — внедрение новой модели для небольшого подмножества пользователей с последующим постепенным распространением на всю пользовательскую базу.
* Интерлейвированное тестирование — смешивание выходных данных нескольких моделей в одном ответе для сравнения их производительности.
* Теневое тестирование — оценка новой модели в реальной производственной среде без влияния на взаимодействие с пользователем.
2. В чём преимущество канареечного тестирования перед A/B-тестированием?
Ответ: канареечное тестирование позволяет сначала внедрить новую модель для ограниченного числа пользователей, а затем постепенно расширять её охват, если показатели производительности указывают на успех. Это позволяет минимизировать риски, связанные с внедрением новой модели, и обеспечивает более плавный переход к её использованию всей пользовательской базой. В отличие от A/B-тестирования, которое случайным образом распределяет трафик между всеми пользователями, канареечное тестирование нацелено на определённое подмножество и постепенно увеличивает охват.
3. Какие показатели производительности можно сравнивать при A/B-тестировании?
Ответ: при A/B-тестировании можно сравнивать такие показатели производительности, как коэффициент кликабельности, конверсии, вовлечённость или доход. Это позволяет организациям оценить, действительно ли модель-кандидат улучшает результаты, прежде чем постепенно увеличивать долю трафика или полностью заменять устаревшую модель.
4. В чём особенность интерлейвированного тестирования?
Ответ: интерлейвированное тестирование оценивает несколько моделей путём смешивания их выходных данных в одном ответе, показываемом пользователям. Это позволяет командам сравнивать производительность моделей более напрямую и эффективно, минимизируя искажения, вызванные различиями в группах пользователей или распределении трафика. Система регистрирует сигналы вовлечённости для каждой рекомендации, что позволяет более точно оценить производительность моделей.
5. Что такое теневое тестирование и в чём его отличие от других стратегий?
Ответ: теневое тестирование позволяет командам оценивать новую модель машинного обучения в реальной производственной среде без влияния на взаимодействие с пользователем. В этом подходе модель-кандидат работает параллельно с устаревшей моделью и получает те же живые запросы, что и продуктивная система. Однако пользователям возвращаются только прогнозы устаревшей модели, а выходные данные модели-кандидата просто записываются для анализа. Это позволяет оценить поведение новой модели в реальных условиях, но не влияет на взаимодействие с пользователем. Теневое тестирование отличается от других стратегий тем, что оно не может зафиксировать истинные показатели вовлечённости пользователей, поскольку его прогнозы никогда не показываются пользователям.