Сплит 50/50 в логах даёт 50.8/49.2 на 2M пользователей. О чём сигнализирует этот SRM и как починить назначение ниже?
Эксперимент должен назначать пользователей 50/50, но логи показов дают 50.8% control / 49.2% treatment на 2 000 000 пользователей.
Ограничения: назначение детерминировано по user_id; ~40% трафика не проходят eligibility; каждый показанный пользователь логируется ровно в одной группе.
ELIGIBLE = load_eligible_users() # ~60% всех пользователей
def assign(user_id: str) -> str:
h = int(hashlib.md5(user_id.encode()).hexdigest(), 16)
if h % 1000 < 500:
return "control"
if user_id in ELIGIBLE: # eligibility проверяется только здесь
return "treatment"
return "control" # неподходящие -> уходят в control
Объясните, что SRM означает для вывода, затем почините назначение, чтобы сплит стал сбалансированным.
SRM значит, что сплит отклоняется от заданного соотношения сильнее случайного — χ² по счётчикам групп даёт крошечный p-value — назначение или логирование сломано и вывод недействителен. Баг: eligibility проверяется только в ветке treatment, поэтому неподходящие падают в control; отсеките их до бакетирования.
- ✗Списывать значимый SRM на безвредный шум округления
- ✗Пытаться перевзвесить или залатать группы вместо починки назначения и перезапуска
- ✗Применять фильтр eligibility или логирования только к одной группе
- →Какую χ²-статистику и порог вы бы взяли, чтобы отметить SRM?
- →Почему нельзя спасти смещённые данные перевзвешиванием постфактум?
Гейт eligibility применяется асимметрично — проверяются только пользователи, направляемые в treatment, — поэтому каждый неподходящий пользователь из верхней половины хеш-пространства перенаправляется в control. Control поэтому набирает примерно 50% плюс долю неподходящих из верхней половины, что и даёт излишек. Рандомизируйте только внутри eligible-популяции, решая членство до бакетирования:
def assign(user_id: str) -> str | None:
if user_id not in ELIGIBLE:
return None # вообще не в эксперименте
h = int(hashlib.md5((user_id + SALT).encode()).hexdigest(), 16)
return "control" if h % 1000 < 500 else "treatment"
Теперь обе группы берутся из одной отфильтрованной популяции, сплит возвращается к 50/50, и χ²-проверка SRM проходит. Отбросьте данные, собранные до фикса, и перезапустите.