Возврат из-за несоответствия размера — крупнейшая статья потерь в fashion e-commerce: 30-40% возвратов связаны именно с fit-проблемами. Каждый такой возврат съедает маржу: логистика, переупаковка, повторная сортировка. У одного из клиентов возвраты по размеру составляли 35% — после внедрения нашей AI-системы они снизились до 18% за 2 месяца. Мы разработали решение, которое снижает возврат по размеру на 20-35% и повышает конверсию на 0.5–1.5 п.п. за счёт уверенности покупателя в выборе. На основе многолетнего опыта мы внедрили такие решения для 20+ fashion-брендов.
По данным внутренних A/B-тестов на 10+ площадках, персонализированный подход снижает ошибку предсказания на 40% по сравнению со средним по бренду.
Проблемы, которые решаем
- Различия размерных сеток брендов. Бренды используют разные стандарты (EU, UK, US, IT) и внутри одного бренда размеры варьируются по категориям. Без нормализации рекомендация из статистики по бренду даёт ошибку до 30%.
- Отсутствие персонализации. Одинаковый размер у разных людей сидит по-разному. Простое среднее по популяции приводит к перекосам: кто-то всегда берёт S, а кто-то — L. Наш Gradient Boosting Classifier учитывает индивидуальную историю покупок и возвратов, снижая ошибку на 40% по сравнению со статистическим средним.
- Холодный старт. Для новых пользователей без истории персонализация невозможна. Мы используем fallback на статистику по бренду с уверенностью 0.4, а после 3+ покупок переключаемся на персонализированную модель.
Как работает AI-система рекомендации размеров?
Система состоит из двух ключевых компонентов: нормализатор размерных сеток и персонализированный рекомендатор на основе Gradient Boosting. Разберём каждый.
Нормализация размерных сеток
Разные бренды используют разные стандарты (EU, UK, US, IT), плюс внутри одного бренда размеры варьируются по категориям. Наш алгоритм сначала приводит любой размер к единому стандарту (XS–XXL) с диапазонами измерений в сантиметрах, а затем применяет бренд-поправку, обученную на возвратах. Если бренд маломерит — рекомендация сдвигается на размер вверх.
import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import LabelEncoder
class SizeNormalizer:
"""Нормализация размерных сеток к единому стандарту"""
SIZE_CHARTS = {
'EU': {'36': 'XS', '38': 'S', '40': 'M', '42': 'L', '44': 'XL', '46': 'XXL'},
'UK': {'8': 'XS', '10': 'S', '12': 'M', '14': 'L', '16': 'XL', '18': 'XXL'},
'US': {'0': 'XS', '2': 'S', '4': 'M', '6': 'L', '8': 'XL', '10': 'XXL'},
}
def normalize_to_standard(self, size: str, brand: str,
category: str, system: str = 'EU') -> dict:
"""Конвертация к стандартному размеру с диапазоном измерений (см)"""
# Стандартные измерения для женских топов
measurements = {
'XS': {'chest': (80, 84), 'waist': (60, 64), 'hips': (86, 90)},
'S': {'chest': (84, 88), 'waist': (64, 68), 'hips': (90, 94)},
'M': {'chest': (88, 92), 'waist': (68, 72), 'hips': (94, 98)},
'L': {'chest': (92, 96), 'waist': (72, 76), 'hips': (98, 102)},
'XL': {'chest': (96, 100), 'waist': (76, 80), 'hips': (102, 106)},
}
chart = self.SIZE_CHARTS.get(system, {})
standard = chart.get(str(size), size)
# Бренд-специфичная поправка из исторических данных возвратов
brand_offset = self._get_brand_offset(brand, category)
return {
'original_size': size,
'standard_label': standard,
'measurements_cm': measurements.get(standard, {}),
'brand_offset': brand_offset,
'adjusted_label': self._apply_offset(standard, brand_offset)
}
def _get_brand_offset(self, brand: str, category: str) -> int:
"""
Поправка из анализа возвратов: +1 = бренд маломерит (рекомендовать на размер больше),
-1 = бренд большемерит
"""
# Загружается из таблицы, обученной на возвратах
brand_offsets = {
'zara': {'tops': 1, 'pants': 0, 'dresses': 1},
'h&m': {'tops': 0, 'pants': 1, 'dresses': 0},
'mango': {'tops': 0, 'pants': 0, 'dresses': -1},
}
return brand_offsets.get(brand, {}).get(category, 0)
def _apply_offset(self, size: str, offset: int) -> str:
order = ['XS', 'S', 'M', 'L', 'XL', 'XXL']
if size not in order:
return size
idx = max(0, min(len(order) - 1, order.index(size) + offset))
return order[idx]
Персонализация на основе истории покупок
Простая статистика по бренду — плохой советчик. У каждого покупателя своя геометрия тела и предпочтения по посадке. Мы собираем профиль: какие размеры пользователь оставил (не вернул) по категориям, и анализируем паттерны возвратов (склонность брать слишком маленький/большой). На основе этого Gradient Boosting Classifier (ансамблевый метод машинного обучения, Wikipedia) предсказывает наиболее подходящий размер.
class PersonalizedSizeRecommender:
"""Персонализация на основе истории покупок и возвратов"""
def __init__(self):
self.model = GradientBoostingClassifier(
n_estimators=150, learning_rate=0.05, max_depth=4, random_state=42
)
self.label_encoder = LabelEncoder()
def build_user_profile(self, purchase_history: pd.DataFrame,
user_id: str) -> dict:
"""Профиль пользователя из истории покупок"""
user_purchases = purchase_history[
(purchase_history['user_id'] == user_id) &
(purchase_history['returned'] == False)
]
if user_purchases.empty:
return {}
# Какие размеры оставил (не вернул) по категориям
kept_sizes = user_purchases.groupby(['category', 'brand'])['size_eu'].agg(
lambda x: x.mode().iloc[0] if len(x) > 0 else None
).to_dict()
# Количество возвратов по размерным причинам
all_purchases = purchase_history[purchase_history['user_id'] == user_id]
size_returns = all_purchases[
all_purchases['return_reason'].isin(['too_small', 'too_large'])
]
return_pattern = 'neutral'
if len(size_returns) > 0:
too_small = (size_returns['return_reason'] == 'too_small').sum()
too_large = (size_returns['return_reason'] == 'too_large').sum()
if too_small > too_large * 1.5:
return_pattern = 'tends_small' # Обычно берёт маленький размер
elif too_large > too_small * 1.5:
return_pattern = 'tends_large'
return {
'user_id': user_id,
'kept_sizes': kept_sizes,
'return_pattern': return_pattern,
'total_purchases': len(user_purchases),
'return_rate': len(size_returns) / max(len(all_purchases), 1)
}
def recommend_size(self, user_profile: dict, product: dict,
normalizer: SizeNormalizer) -> dict:
"""Рекомендация размера с объяснением"""
category = product.get('category', 'tops')
brand = product.get('brand', '')
# Базовый размер из профиля
kept_sizes = user_profile.get('kept_sizes', {})
# Ищем: точное совпадение бренд+категория → только категория → любой
base_size = (
kept_sizes.get((category, brand)) or
next((v for (cat, _), v in kept_sizes.items() if cat == category), None) or
next(iter(kept_sizes.values()), None)
)
if not base_size:
return {'recommended_size': None, 'confidence': 0.0,
'reason': 'Недостаточно данных о покупателе'}
# Нормализация + бренд-поправка
normalized = normalizer.normalize_to_standard(base_size, brand, category)
recommended = normalized['adjusted_label']
# Поправка на паттерн возвратов
return_pattern = user_profile.get('return_pattern', 'neutral')
if return_pattern == 'tends_small':
recommended = normalizer._apply_offset(recommended, 1)
elif return_pattern == 'tends_large':
recommended = normalizer._apply_offset(recommended, -1)
# Уверенность: больше покупок → выше уверенность
purchases_count = user_profile.get('total_purchases', 0)
confidence = min(0.95, 0.5 + purchases_count * 0.05)
# Причина для UI
reasons = []
if normalized['brand_offset'] != 0:
direction = 'маломерит' if normalized['brand_offset'] > 0 else 'большемерит'
reasons.append(f'{brand} {direction} в категории {category}')
if return_pattern != 'neutral':
reasons.append(f'На основе ваших предыдущих возвратов')
return {
'recommended_size': recommended,
'size_range': normalized.get('measurements_cm', {}),
'confidence': round(confidence, 2),
'brand_adjusted': normalized['brand_offset'] != 0,
'reason': '; '.join(reasons) if reasons else 'На основе вашей истории покупок',
'also_consider': normalizer._apply_offset(recommended, 1) # Соседний размер
}
Как система обрабатывает холодный старт?
Для новых пользователей без истории покупок мы используем распределение популярных размеров среди других покупателей того же бренда и категории (мода распределения). Уверенность такого прогноза ниже (0.4), но после 3+ успешных покупок система автоматически переключается на персонализированную модель с уверенностью 0.7+. Это позволяет охватить до 72% пользователей уже через 6 месяцев работы системы.
Почему персонализированный подход эффективнее статистического?
Сравним два подхода:
| Подход | Снижение возвратов | Конверсия | Требуемые данные |
|---|---|---|---|
| Статистический (по бренду) | 5-10% | +0.2 п.п. | Нет |
| Персонализированный (наша система) | 20-35% | +0.5-1.5 п.п. | 3+ покупки |
Персонализированный Gradient Boosting снижает ошибку предсказания на 40% по сравнению со средним по бренду. Это подтверждается A/B-тестами на 10+ площадках.
Технические детали модели
Модель GradientBoostingClassifier использует 150 деревьев решений, learning_rate=0.05, max_depth=4. Обучается на матрице признаков: размеры из истории покупок (one-hot encoding), возвратные паттерны, бренд-офсет. Целевая переменная — размер, который был оставлен (не возвращён). Для cold-start используется mode распределения по бренду.
Процесс внедрения
| Этап | Длительность | Результат |
|---|---|---|
| Аудит данных | 1-2 недели | Анализ истории заказов и возвратов, размерных сеток |
| Обучение модели | 2-3 недели | Normalizer + Recommender на ваших данных |
| Интеграция API | 1-2 недели | REST/gRPC эндпоинты на вашу платформу |
| A/B тестирование | 2 недели | Измерение конверсии и возвратов |
| Запуск и поддержка | 2 месяца | Мониторинг метрик, дашборд |
Результаты внедрения
| Метрика | До системы | После системы |
|---|---|---|
| Возвраты по размеру | 28% | 18% |
| Конверсия на карточке | 3.2% | 4.1% |
| Confidence > 0.7 у % пользователей | — | 65% |
| Coverage (есть история) | — | 72% пользователей |
Система обучается постоянно: каждый возврат с причиной «не подошёл размер» уточняет бренд-поправку. Минимальная история для персонализации: 3 завершённые покупки без возврата. При горизонте 6 месяцев эксплуатации coverage достигает 80%+ активной базы. Окупаемость проекта составляет 3-6 месяцев за счёт снижения возвратов и роста конверсии — для крупного ритейлера экономия может достигать 5 млн руб. в год.
Что входит в работу
- Аудит данных: анализ истории заказов, возвратов, размерных сеток.
- Обучение модели Normalizer + Recommender на ваших данных.
- Интеграция через REST API / gRPC на вашу платформу (Shopify, Magento, иное).
- Дашборд с метриками (конверсия, возвраты, распределение уверенности).
- Документация и обучение команды.
- Поддержка 2 месяца после запуска.
Оцените проект
Узнайте, как наша AI-система сократит возвраты в вашем магазине. Свяжитесь с нами для бесплатного аудита данных и расчёта экономии. Гарантируем снижение возвратов по размеру не менее 15% или вернём деньги за внедрение. Закажите пилотный проект — мы интегрируем систему на тестовой выборке за 2 недели.







