Розробка AI-системи корзинного аналізу Market Basket Analysis
Роздрібна мережа з 500 магазинів втрачала 12% виручки через відсутність персоналізованих рекомендацій. Товари в кошику не перетиналися, промо-акції не використовували синергію, а відділ аналітики витрачав тижні на Excel-звіти. Ми побудували AI-систему корзинного аналізу, яка замінила ручну працю та збільшила крос-продажі на 18% за перший квартал. Ключова ідея — гібрид класичних association rules та нейромереж, що дає інтерпретованість основних правил і глибину long-tail.
ML-рішення в рітейлі стають стандартом. Сегментація кошиків за типом покупця — ще один шар персоналізації, який ми реалізуємо через кластеризацію транзакцій.
Які проблеми вирішує Market Basket Analysis?
Розрідженість даних. У типовому датасеті 1 млн транзакцій і 10 тис. SKU більшість пар товарів зустрічаються рідко. Класичні алгоритми (Apriori, FP-Growth) дають багато шуму. Нейромережева модель вловлює слабкі сигнали, використовуючи ембеддінги та механізм self-attention. Додатково застосовуємо техніки аугментації даних і зваженого семплювання для long-tail категорій.
Сезонні тренди. Правила, виведені на даних січня, не працюють у липні. Окремі моделі для кожного сезону підвищують точність на 30%. Враховуємо свята, розпродажі та погодні фактори. Для кожного сезону будуємо свої правила та ембеддінги, що дозволяє рекомендувати сезонні товарні зв'язки.
Масштабування. Обробка 1 млн транзакцій FP-Growth на single CPU займає 3-8 хвилин. Для щоденного перерахунку в реальному часі використовуємо інкрементальні оновлення та GPU для нейромережі. Гібридна архітектура дозволяє перераховувати правила раз на день, а нейромережу донавчати раз на тиждень — це баланс між швидкістю та якістю.
Брудні дані — ще одна проблема. Пропуски, дублікати та застарілі ціни безпосередньо впливають на якість правил. Ми автоматизуємо ETL-процеси з перевіркою узгодженості.
Чому гібридний підхід кращий за класичні правила?
Згідно з звітом McKinsey, рітейлери з AI-рекомендаціями збільшують виручку на 10–30%.
Класичний FP-Growth + нейромережа: rules для високопідтримуваних пар (lift > 1.2, confidence > 0.3), нейромережа для long-tail. Це дає +15-20% Recall@10 проти чистих association rules.
import pandas as pd
import numpy as np
from mlxtend.frequent_patterns import fpgrowth, association_rules
from mlxtend.preprocessing import TransactionEncoder
import torch
import torch.nn as nn
class BasketAnalyzer:
"""Класичний FP-Growth + нейромережеве розширення"""
def __init__(self, min_support: float = 0.01, min_confidence: float = 0.3):
self.min_support = min_support
self.min_confidence = min_confidence
self.rules = None
self.te = TransactionEncoder()
def fit(self, transactions: list[list[str]]) -> pd.DataFrame:
"""
transactions: [['молоко', 'хлеб', 'масло'], ['молоко', 'яйца'], ...]
"""
te_array = self.te.fit_transform(transactions)
df = pd.DataFrame(te_array, columns=self.te.columns_)
# FP-Growth быстрее Apriori на 10-100x для больших датасетов
frequent_itemsets = fpgrowth(df, min_support=self.min_support, use_colnames=True)
self.rules = association_rules(
frequent_itemsets,
metric='confidence',
min_threshold=self.min_confidence
)
# Добавляем lift и conviction для фильтрации
self.rules = self.rules[self.rules['lift'] > 1.2]
self.rules = self.rules.sort_values('lift', ascending=False)
return self.rules
def get_recommendations(self, basket: list[str],
top_k: int = 5) -> list[dict]:
"""Рекомендации для текущей корзины"""
if self.rules is None:
return []
basket_set = frozenset(basket)
matching_rules = self.rules[
self.rules['antecedents'].apply(lambda x: x.issubset(basket_set))
]
# Убираем товары, уже в корзине
recommendations = []
seen = set()
for _, rule in matching_rules.iterrows():
for item in rule['consequents']:
if item not in basket_set and item not in seen:
recommendations.append({
'item': item,
'confidence': rule['confidence'],
'lift': rule['lift'],
'support': rule['support']
})
seen.add(item)
if len(recommendations) >= top_k:
break
if len(recommendations) >= top_k:
break
return recommendations
def get_category_affinity(self, transactions: pd.DataFrame) -> pd.DataFrame:
"""Матриця афінності між категоріями товарів"""
# Транзакции с категориями вместо конкретных товаров
cat_transactions = transactions.groupby('order_id')['category'].apply(list).tolist()
te_cat = TransactionEncoder()
cat_array = te_cat.fit_transform(cat_transactions)
cat_df = pd.DataFrame(cat_array, columns=te_cat.columns_)
# Совместная встречаемость категорий
co_occurrence = cat_df.T.dot(cat_df)
np.fill_diagonal(co_occurrence.values, 0)
# Нормализуем по поддержке (PMI-подобная мера)
totals = cat_df.sum()
n = len(cat_df)
affinity = co_occurrence / n / (totals.values[:, None] * totals.values[None, :] / n**2 + 1e-9)
return affinity
class NeuralBasketPredictor(nn.Module):
"""
Нейросетевая модель для предсказания следующего товара в корзину.
Входной вектор: bag-of-items бинарный вектор текущей корзины.
"""
def __init__(self, n_items: int, embedding_dim: int = 64):
super().__init__()
self.item_embedding = nn.Embedding(n_items, embedding_dim, padding_idx=0)
self.attention = nn.MultiheadAttention(embedding_dim, num_heads=4, batch_first=True)
self.fc = nn.Sequential(
nn.Linear(embedding_dim, 256),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(256, n_items)
)
def forward(self, basket_item_ids: torch.Tensor) -> torch.Tensor:
"""
basket_item_ids: (batch, seq_len) — индексы товаров в корзине
Returns: (batch, n_items) — logits для каждого товара
"""
emb = self.item_embedding(basket_item_ids) # (batch, seq, dim)
attended, _ = self.attention(emb, emb, emb) # Self-attention по корзине
pooled = attended.mean(dim=1) # (batch, dim)
return self.fc(pooled)
Як враховувати часові патерни та сезонність?
class TemporalBasketAnalyzer:
"""Аналіз часових патернів покупок"""
def find_sequential_patterns(self, orders: pd.DataFrame,
days_window: int = 7) -> pd.DataFrame:
"""Виявлення послідовних покупок у вікні N днів"""
orders_sorted = orders.sort_values(['user_id', 'order_date'])
sequential = []
for user_id, user_orders in orders_sorted.groupby('user_id'):
order_list = user_orders.to_dict('records')
for i, order_i in enumerate(order_list):
for order_j in order_list[i+1:]:
days_diff = (order_j['order_date'] - order_i['order_date']).days
if days_diff > days_window:
break
if days_diff > 0:
sequential.append({
'item_a': order_i['sku'],
'item_b': order_j['sku'],
'days_between': days_diff
})
df = pd.DataFrame(sequential)
if df.empty:
return df
# Топ последовательных пар
return (df.groupby(['item_a', 'item_b'])
.agg(count=('days_between', 'count'),
avg_days=('days_between', 'mean'))
.reset_index()
.sort_values('count', ascending=False))
def get_seasonal_baskets(self, transactions: pd.DataFrame) -> dict:
"""Сезонні кошики: що зазвичай купують разом у конкретний період"""
transactions['month'] = transactions['order_date'].dt.month
transactions['season'] = transactions['month'].map({
12: 'winter', 1: 'winter', 2: 'winter',
3: 'spring', 4: 'spring', 5: 'spring',
6: 'summer', 7: 'summer', 8: 'summer',
9: 'autumn', 10: 'autumn', 11: 'autumn'
})
seasonal_rules = {}
for season, group in transactions.groupby('season'):
season_transactions = group.groupby('order_id')['sku'].apply(list).tolist()
if len(season_transactions) < 100:
continue
te = TransactionEncoder()
arr = te.fit_transform(season_transactions)
df_season = pd.DataFrame(arr, columns=te.columns_)
freq = fpgrowth(df_season, min_support=0.02, use_colnames=True)
if not freq.empty:
rules = association_rules(freq, metric='lift', min_threshold=1.5)
seasonal_rules[season] = rules.sort_values('lift', ascending=False).head(20)
return seasonal_rules
Часові патерни дозволяють рекомендувати товари не тільки на основі одного кошика, але й послідовностей покупок у часі. Наприклад, після покупки принтера через тиждень часто купують картриджі. Сезонні кошики будуються окремо для кожного сезону, що збільшує релевантність рекомендацій. Для кожного користувача система враховує його історію — це дає персоналізовані часові зв'язки.
Процес впровадження: від аудиту до деплою
- Аналітика — аудит даних (джерела, якість, частота оновлення). Перевіряємо пропуски, дублікати, розподіл транзакцій.
- Проектування — вибір архітектури (гібрид / тільки rules / тільки NN), прототип на вибірці 10k транзакцій.
- Реалізація — навчання моделей, написання REST API на FastAPI, інтеграція з CRM/ERP через webhook.
- Тестування — A/B-тест на 10% аудиторії, замір CTR та ATC, аналіз lift за категоріями.
- Деплой — контейнеризація Docker, розгортання у вашому Kubernetes або хмарі, налаштування моніторингу (Grafana, Prometheus, alerting).
Що входить в роботу (deliverables)
- Навчання 2-3 моделей (FP-Growth, Neural, Temporal) з підбором гіперпараметрів.
- REST API з документацією (OpenAPI 3.0) — ендпоінти: рекомендації за кошиком, сезонні правила, часові послідовності.
- Дашборд метрик (Grafana) — recall@k, precision@k, CTR, latency p99.
- Навчання ваших інженерів (2 воркшопи по 4 години) — архітектура, інтерпретація результатів, донавчання.
- Гарантійна підтримка 12 місяців з реакцією до 4 годин.
Порівняння підходів: метрики та швидкості
| Підхід | Recall@10 | Час навчання (1M транзакцій) | Покриття long-tail | Інтерпретованість |
|---|---|---|---|---|
| Тільки правила | 0.42 | 8 хв | Низьке | Висока |
| Тільки нейромережа | 0.55 | 2 год (GPU) | Середнє | Низька |
| Гібрид (наш) | 0.61 | 2 год 8 хв | Високе | Середня |
Гібридний підхід дає на 45% більший recall, ніж чисті правила, при цьому зберігає інтерпретованість для топ-пар. Використовуємо його для роздрібних мереж з 50+ магазинів — досвід понад 10 проектів.
Детальніше про метрики
precision@k — частка релевантних рекомендацій серед перших k, recall@k — повнота. Для вашого бізнесу важливіший recall@10, якщо ціль — знайти якомога більше відповідних товарів. latency p99 — час відповіді API; гарантуємо < 200ms.| Сценарій | Зростання крос-продажів | Зниження відтоку | Час окупності |
|---|---|---|---|
| Продуктовий рітейл | +22% | -15% | 4-6 міс. |
| Fashion | +18% | -10% | 6-8 міс. |
| DIY/товари для дому | +25% | -12% | 5-7 міс. |
Отримайте попередню оцінку ваших даних — ми проаналізуємо структуру і запропонуємо оптимальну архітектуру за 2 дні.
Зв'яжіться з нами, щоб обговорити вашу задачу. Замовте пілотний проект — отримайте перші рекомендації за 2 тижні. Отримайте консультацію щодо оцінки ваших даних.







