← Назад к вопросам

Что такое параллельное проведение экспериментов?

2.0 Middle🔥 111 комментариев
#A/B тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Параллельное проведение экспериментов

Параллельное проведение экспериментов — это практика одновременного запуска нескольких независимых A/B тестов на одной и той же группе пользователей в одно и то же время. Это позволяет компаниям ускорить процесс экспериментирования и быстрее принимать решения о внедрении изменений. Однако у этого подхода есть существенные математические и практические сложности.

Пример параллельных экспериментов

Пользователь попадает в приложение
         ↓
   ┌─────┴──────────────────┐
   │                        │
   ↓ Тест 1                ↓ Тест 2
   (Кнопка)                (Цвет шрифта)
   ├─ Группа A             ├─ Группа C
   └─ Группа B             └─ Группа D
   
Итог: 4 комбинации
- Пользователь видит и новую кнопку И новый цвет
- Видит и старую кнопку И новый цвет
- Видит и новую кнопку И старый цвет
- Видит и старую кнопку И старый цвет

Как работает параллельное экспериментирование

Подход 1: Независимые тесты (Orthogonal Testing)

Разделяем пользователей на полностью независимые группы:

# Структура групп
users_total = 100000

# Тест 1: Кнопка (50% vs 50%)
test_1_ratio = 0.5
test_1_control = users_total * test_1_ratio          # 50000
test_1_variant = users_total * test_1_ratio          # 50000

# Тест 2: Цвет (50% vs 50%)
test_2_ratio = 0.5
test_2_control = users_total * test_2_ratio          # 50000
test_2_variant = users_total * test_2_ratio          # 50000

# ПРОБЛЕМА: Каждый тест получает ТОЛЬКО 50% пользователей!
# Чувствительность снижается в √2 раз

# Матрица 2x2 (Факторный дизайн)
matrix = [
    [25000, 25000],  # Старая кнопка × старый цвет, старая кнопка × новый цвет
    [25000, 25000]   # Новая кнопка × старый цвет, новая кнопка × новый цвет
]

print(f"Тест 1 контроль: {25000 + 25000} (комбинация обеих)")
print(f"Тест 1 вариант: {25000 + 25000} (комбинация обеих)")

Подход 2: Вложенные тесты (Nested Testing)

Тесты делаются на разных уровнях иерархии:

100% трафика
      ↓
   [Тест 1] 50% vs 50%
   ├─ Контроль (50%)
   │  ├─ [Тест 2a] 50% vs 50%
   │  └─ Групп для Теста 2: 25000 + 25000
   │
   └─ Вариант (50%)
      ├─ [Тест 2b] 50% vs 50%
      └─ Групп для Теста 2: 25000 + 25000

Проблемы параллельного экспериментирования

Проблема 1: Снижение статистической мощности

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

import numpy as np
from statsmodels.stats.power import tt_ind_solve_power

# Один тест (100% трафика)
n_full = 10000
effect_detectable_full = tt_ind_solve_power(
    effect_size=None,
    alpha=0.05,
    power=0.80,
    nobs1=n_full
)

# Два параллельных теста (50% трафика каждому)
n_half = 5000
effect_detectable_half = tt_ind_solve_power(
    effect_size=None,
    alpha=0.05,
    power=0.80,
    nobs1=n_half
)

print(f"MDE (100% трафика): {effect_detectable_full:.4f}")
print(f"MDE (50% трафика): {effect_detectable_half:.4f}")
print(f"Увеличение MDE: {effect_detectable_half / effect_detectable_full:.2f}x")
print(f"Требуется в {(effect_detectable_half / effect_detectable_full)**2:.2f}x больше времени")

Математика: Когда вы делите трафик поровну между двумя тестами:

  • Размер выборки каждого теста: n/2
  • MDE увеличивается на √2 ≈ 1.41x
  • Требуемое время теста увеличивается на 2x

Проблема 2: Интерференция между тестами (Interference)

Если тесты взаимодействуют, результаты искажаются:

Примеры интерференции:
1. Визуальная интерференция
   - Новая кнопка + новый цвет → выглядит странно
   - Результат не репрезентативен для отдельных изменений

2. Поведенческая интерференция
   - Пользователь отвлекается на ДВА изменения сразу
   - Его поведение отличается от нормального

3. Технологическая интерференция
   - Два скрипта отслеживания конфликтуют
   - Данные загрязняются ошибками

Проблема 3: Множественные сравнения (Multiple Testing Problem)

Чем больше тестов, тем выше вероятность ложноположительного результата.

# Парадокс: каждый тест на уровне значимости 5%
# Но вероятность получить хотя бы один ложноположительный результат растёт

alpha_per_test = 0.05
num_tests = 5

# Вероятность того, что ВСЕ результаты верны (нет ложных)
prob_all_correct = (1 - alpha_per_test) ** num_tests
prob_false_positive = 1 - prob_all_correct

print(f"Вероятность ложноположительного результата: {prob_false_positive:.1%}")
print(f"При {num_tests} тестах: почти {prob_false_positive*100:.0f}% шанс ошибки")

# Решение: Bonferroni correction
alpna_corrected = alpha_per_test / num_tests
print(f"\nCorrected alpha: {alpha_corrected:.5f}")
print(f"Это делает каждый тест менее чувствительным")

Способы безопасного параллельного тестирования

Способ 1: Факторный дизайн (Factorial Design)

Достаточный размер выборки, чтобы сбалансировать все комбинации:

# Пример: 2 x 2 факторный дизайн
# Фактор 1: Кнопка (2 варианта)
# Фактор 2: Цвет (2 варианта)

users_per_group = 10000  # Большой размер!
total_users = users_per_group * 4  # 40000

groups = {
    'old_button_old_color': users_per_group,
    'old_button_new_color': users_per_group,
    'new_button_old_color': users_per_group,
    'new_button_new_color': users_per_group,
}

print(f"Требуемый трафик: {total_users} пользователей")
print(f"Каждый фактор на 20000 пользователей (достаточно для точного анализа)")
print(f"Можно проверить эффект взаимодействия (interaction effect)")

Способ 2: Orthogonal testing (Ортогональное тестирование)

Убедиться, что тесты полностью независимы:

import hashlib

def assign_experiment_group(user_id, experiment_salt):
    """Assign user to group based on user_id and experiment salt"""
    hash_input = f"{user_id}_{experiment_salt}"
    hash_value = int(hashlib.md5(hash_input.encode()).hexdigest(), 16)
    return hash_value % 2  # 0 или 1 (контроль или вариант)

# Для каждого теста используем разный salt
user_id = 12345

test_1_group = assign_experiment_group(user_id, "button_test")
test_2_group = assign_experiment_group(user_id, "color_test")

print(f"Тест 1 (кнопка): группа {test_1_group}")
print(f"Тест 2 (цвет): группа {test_2_group}")
print("Группы полностью независимы ✓")

Способ 3: Sequential testing с контролем типа I ошибки

# Bonferroni correction для множественных сравнений
alpha_global = 0.05
num_hypotheses = 5  # Пять независимых гипотез

alpha_per_test = alpha_global / num_hypotheses

print(f"Уровень значимости для каждого теста: {alpha_per_test:.4f}")
print(f"Используйте p < {alpha_per_test:.4f} как критерий значимости")
print(f"Это контролирует общую вероятность ошибки на уровне {alpha_global}")

Практический пример: Безопасное параллельное тестирование

# Сценарий: Компания хочет тестировать 3 изменения одновременно

from datetime import datetime, timedelta
from scipy.stats import chi2_contingency

class ExperimentPlatform:
    def __init__(self, daily_traffic=50000):
        self.daily_traffic = daily_traffic
        self.experiments = {}
    
    def plan_experiments(self, experiments_list):
        """
        Спланировать параллельные эксперименты
        experiments_list: список dict с 'name', 'mde_pct'
        """
        # Требуемый размер выборки для факторного дизайна
        # Каждой группе нужно 10000 пользователей
        groups_total = 2 ** len(experiments_list)  # 2^n комбинаций
        users_per_group = 10000
        users_required = groups_total * users_per_group
        
        duration_days = users_required / self.daily_traffic
        
        print(f"Число экспериментов: {len(experiments_list)}")
        print(f"Комбинаций: {groups_total} (факторный дизайн 2^{len(experiments_list)})")
        print(f"Требуется пользователей: {users_required:,}")
        print(f"Требуемая длительность: {duration_days:.1f} дней")
        print(f"\nРекомендация: ПАРАЛЛЕЛЬНОЕ тестирование БЕЗОПАСНО на таком объёме")
        
        return {
            'groups_total': groups_total,
            'users_per_group': users_per_group,
            'duration_days': duration_days
        }

# Использование
platform = ExperimentPlatform(daily_traffic=50000)
plan = platform.plan_experiments([
    {'name': 'button_color', 'mde_pct': 10},
    {'name': 'font_size', 'mde_pct': 10},
    {'name': 'cta_text', 'mde_pct': 10}
])

Таблица: Когда параллельное тестирование уместно

ПараметрУместноНе уместно
Трафик> 50k/день< 10k/день
Число тестов2-3> 5
Гипотезы независимы?ДаНет (пересекаются)
Время критично?ДаНет (можно ждать)
MDE требуемый5-10%< 2%

Best Practices для параллельных экспериментов

1. Планирование:

  • Убедитесь, что тесты полностью независимы
  • Используйте факторный дизайн с достаточным трафиком
  • Рассчитайте требуемую продолжительность заранее

2. Контроль ошибок:

  • Применяйте Bonferroni correction для α
  • Или используйте família-wise error rate (FWER)

3. Логирование:

print("Запущено 3 параллельных теста:")
print("1. Кнопка (30 дней)")
print("2. Цвет (30 дней)")
print("3. Текст (30 дней)")
print("\nОжидаемый MDE по каждому: 5-7%")
print("Уровень значимости (Bonferroni): 0.05/3 = 0.0167")

4. Анализ:

  • Смотрите на эффекты взаимодействия (interaction effects)
  • Проверяйте гипотезы независимо
  • Строите доверительные интервалы, не только p-values
Что такое параллельное проведение экспериментов? | PrepBro