Подготовка данных
Правильная подготовка данных - ключевой этап в построении рекомендательной системы. В этой главе мы подробно рассмотрим все аспекты работы с данными: от загрузки до создания обучающих батчей.
В этой статье мы рассмотрим подготовку данных для рекомендательных систем с использованием библиотеки sota-recommender.
pip install sota-recommenderЗагрузка датасетов
Библиотека предоставляет встроенные загрузчики для популярных датасетов и утилиту для создания синтетических данных.
MovieLens
MovieLens - самый популярный датасет для рекомендательных систем. Содержит рейтинги фильмов от пользователей.
from recommender.data import load_movielens
# Доступные размеры: '100k', '1m', '10m', '20m', '25m'
df = load_movielens(size='100k')
print(df.head())
print(f"Shape: {df.shape}")
print(f"Columns: {df.columns.tolist()}")
Выход:
user_id item_id rating timestamp
0 1 193 5.0 978300760
1 1 661 3.0 978302109
...
Shape: (100000, 4)
Columns: ['user_id', 'item_id', 'rating', 'timestamp']
Характеристики датасетов:
| Размер | Пользователи | Items | Рейтинги | Размер файла |
|---|---|---|---|---|
| 100k | 943 | 1,682 | 100,000 | ~5 MB |
| 1m | 6,040 | 3,706 | 1,000,209 | ~24 MB |
| 10m | 69,878 | 10,677 | 10,000,054 | ~265 MB |
| 20m | 138,493 | 27,278 | 20,000,263 | ~533 MB |
| 25m | 162,541 | 59,047 | 25,000,095 | ~666 MB |
Amazon Reviews
Датасет отзывов товаров на Amazon. Поддерживаются различные категории.
from recommender.data import load_amazon
# Доступные категории: 'Books', 'Electronics', 'Movies', etc.
df = load_amazon(
category='Books',
max_reviews=100000 # Ограничение для быстрой загрузки
)
print(df.head())
Формат:
user_id item_id rating timestamp
0 A123... B001... 5.0 1234567890
...
Book-Crossing
Датасет рейтингов книг.
from recommender.data import load_book_crossing
df = load_book_crossing()
print(f"Загружено {len(df)} рейтингов")
Синтетические данные
Для тестирования и экспериментов можно создать синтетический датасет:
from recommender.data import create_synthetic_dataset
df = create_synthetic_dataset(
n_users=1000, # Количество пользователей
n_items=500, # Количество items
n_interactions=10000,# Количество взаимодействий
implicit=True, # Implicit (True) или explicit (False)
seed=42 # Для воспроизводимости
)
print(df.head())
Загрузка собственных данных
Если у вас есть свои данные, приведите их к формату DataFrame с нужными колонками:
import pandas as pd
# Пример: загрузка из CSV
df = pd.read_csv('my_data.csv')
# Необходимые колонки:
# - user_id: ID пользователя (int или str)
# - item_id: ID item (int или str)
# - rating: рейтинг или 1 для implicit (float)
# - timestamp: временная метка (опционально, int или datetime)
# Переименование колонок при необходимости
df = df.rename(columns={
'userId': 'user_id',
'movieId': 'item_id',
'score': 'rating',
'datetime': 'timestamp'
})
print(df.head())
Создание InteractionDataset
InteractionDataset - основной класс для работы с данными взаимодействий в библиотеке.
Базовое создание
from recommender import InteractionDataset
# Создание датасета
dataset = InteractionDataset(
df,
implicit=True, # Тип данных: implicit или explicit
min_user_interactions=0, # Минимум взаимодействий на пользователя
min_item_interactions=0 # Минимум взаимодействий на item
)
# Информация о датасете
print(f"Пользователей: {dataset.n_users}")
print(f"Items: {dataset.n_items}")
print(f"Взаимодействий: {len(dataset.data)}")
print(f"Плотность: {dataset.density:.4f}")
Implicit vs Explicit
Implicit feedback - бинарные взаимодействия (клик, просмотр, покупка):
- Значения: 0 или 1
- Используется для: EASE, SLIM, ALS, NCF, LightGCN, SASRec
# Implicit feedback
dataset = InteractionDataset(df, implicit=True)
Explicit feedback - явные рейтинги (1-5 звёзд):
- Значения: непрерывные (обычно 1-5)
- Используется для: SVD, SVD++
# Explicit feedback
dataset = InteractionDataset(df, implicit=False)
Фильтрация данных
Часто нужно отфильтровать редких пользователей и items:
dataset = InteractionDataset(
df,
implicit=True,
min_user_interactions=5, # Минимум 5 взаимодействий на пользователя
min_item_interactions=3 # Минимум 3 взаимодействия на item
)
# Фильтрация происходит итеративно до сходимости
Работа с данными
# Получить все данные
data = dataset.data # DataFrame
# Конвертация в разреженную матрицу
matrix = dataset.to_csr_matrix() # scipy.sparse.csr_matrix
print(f"Matrix shape: {matrix.shape}") # (n_users, n_items)
# Получить items для пользователя
user_items = dataset.get_user_items(user_id=1)
print(f"User 1 items: {user_items}")
# Получить пользователей для item
item_users = dataset.get_item_users(item_id=100)
print(f"Item 100 users: {item_users}")
Препроцессинг
Библиотека предоставляет различные функции препроцессинга.
Фильтрация по количеству взаимодействий
from recommender.data import filter_by_interaction_count
# Фильтрация редких пользователей и items
df_filtered = filter_by_interaction_count(
df,
min_user_interactions=5,
min_item_interactions=5,
max_iterations=10 # Максимум итераций
)
print(f"До: {len(df)} взаимодействий")
print(f"После: {len(df_filtered)} взаимодействий")
Бинаризация (преобразование в implicit)
from recommender.data import binarize_implicit_feedback
# Преобразование explicit ratings в implicit feedback
# Например, рейтинг >= 4.0 считается положительным
df_implicit = binarize_implicit_feedback(
df,
threshold=4.0, # Порог для положительного feedback
rating_col='rating'
)
# Теперь rating = 1 для всех положительных взаимодействий
print(df_implicit['rating'].unique()) # [1]
Нормализация рейтингов
from recommender.data import normalize_ratings
# Нормализация рейтингов (для explicit feedback)
df_norm = normalize_ratings(
df,
method='minmax', # 'minmax', 'standard', или 'none'
rating_col='rating'
)
# MinMax: приводит к [0, 1]
# Standard: z-score нормализация (mean=0, std=1)
Обработка временных меток
from recommender.data import add_time_features
# Добавление временных признаков
df_with_time = add_time_features(df, timestamp_col='timestamp')
# Добавляются колонки:
# - hour: час дня (0-23)
# - day_of_week: день недели (0-6)
# - month: месяц (1-12)
# - year: год
Разделение данных
Правильное разделение данных на train/val/test критично для корректной оценки модели.
Random Split
Случайное разделение - самая простая стратегия:
# Разделение на train и test
train_data, test_data = dataset.split(
test_size=0.2,
strategy='random',
seed=42
)
print(f"Train: {len(train_data)} взаимодействий")
print(f"Test: {len(test_data)} взаимодействий")
# Разделение на train, validation и test
train_data, val_data, test_data = dataset.split(
test_size=0.2,
val_size=0.1, # 10% для validation
strategy='random',
seed=42
)
Temporal Split
Разделение по времени - более реалистичная оценка (требуется timestamp):
train_data, test_data = dataset.split(
test_size=0.2,
strategy='temporal',
seed=42
)
# Train содержит ранние взаимодействия
# Test содержит поздние взаимодействия
Когда использовать:
- Когда важна временная динамика
- Для sequential моделей (SASRec)
- Для более реалистичной оценки
Leave-One-Out Split
Последнее взаимодействие каждого пользователя идёт в test:
train_data, test_data = dataset.split(
strategy='leave_one_out',
seed=42
)
# Для каждого пользователя:
# - Train: все взаимодействия кроме последнего
# - Test: последнее взаимодействие
Когда использовать:
- Стандартная практика в sequential рекомендациях
- Когда важно предсказать следующее действие
- Для SASRec и подобных моделей
Stratified Split
Сохранение распределения по пользователям:
from recommender.data import stratified_split
train, test = stratified_split(
df,
test_size=0.2,
user_col='user_id'
)
# Гарантирует, что каждый пользователь есть в train и test
Negative Sampling
Для implicit feedback часто нужны негативные примеры (items, с которыми пользователь НЕ взаимодействовал).
Стратегии семплирования
Библиотека предоставляет 5 стратегий negative sampling:
1. Uniform Sampling
Равномерное случайное семплирование из всех items:
from recommender.data import UniformSampler, create_negative_samples
# Создание sampler
sampler = UniformSampler(
n_items=dataset.n_items,
seed=42
)
# Генерация негативных примеров
train_with_negatives = create_negative_samples(
interactions_df=train_data.data,
sampler=sampler,
n_negatives_per_positive=4 # 4 негативных на 1 позитивный
)
print(f"Исходно: {len(train_data.data)} примеров")
print(f"С негативами: {len(train_with_negatives)} примеров")
2. Popularity-based Sampling
Семплирование пропорционально популярности items:
from recommender.data import PopularitySampler
# Подсчёт популярности
item_popularity = train_data.data['item_id'].value_counts().to_dict()
# Создание sampler
sampler = PopularitySampler(
n_items=dataset.n_items,
item_popularity=item_popularity,
seed=42
)
train_with_negatives = create_negative_samples(
train_data.data,
sampler,
n_negatives_per_positive=4
)
Преимущество: Более сложные негативные примеры (популярные items сложнее отличить).
3. In-batch Negative Sampling
Использование позитивов других пользователей в батче как негативов:
from recommender.data import InBatchSampler
sampler = InBatchSampler()
# Используется внутри DataLoader во время обучения
# Эффективно для deep learning моделей
4. DNS (Dynamic Negative Sampling)
Семплирование сложных негативов на основе текущей модели:
from recommender.data import DNSSampler
sampler = DNSSampler(
model=model, # Текущая модель
n_candidates=100 # Количество кандидатов для выбора
)
# Выбирает негативы с высоким score (сложные для модели)
5. Hard Negative Mining
Выбор самых сложных негативов:
from recommender.data import HardNegativeSampler
sampler = HardNegativeSampler(
model=model,
difficulty='hard' # 'easy', 'medium', 'hard'
)
Использование в обучении
# Создание обучающих данных с негативами
train_with_negatives = create_negative_samples(
train_data.data,
sampler=UniformSampler(n_items=dataset.n_items),
n_negatives_per_positive=4
)
# Добавляется колонка 'label': 1 для позитивов, 0 для негативов
print(train_with_negatives['label'].value_counts())
# 1 10000 (позитивы)
# 0 40000 (негативы)
# Использование для обучения
model = NCFRecommender()
model.fit(train_with_negatives)
Создание последовательностей
Для sequential моделей (SASRec) нужно создавать последовательности действий пользователей.
Основное использование
from recommender.data import create_sequences
# Создание последовательностей
sequences, targets = create_sequences(
df,
max_seq_length=50, # Максимальная длина последовательности
user_col='user_id',
item_col='item_id',
time_col='timestamp' # Важно: сортировка по времени
)
print(f"Создано {len(sequences)} последовательностей")
print(f"Пример последовательности: {sequences[0]}")
print(f"Соответствующий target: {targets[0]}")
Формат:
sequences[0] # [item1, item2, item3, ..., item_n]
targets[0] # item_{n+1} (следующий item для предсказания)
Padding
Короткие последовательности дополняются padding'ом:
# Последовательности короче max_seq_length дополняются нулями слева
sequences = [
[0, 0, 0, 5, 12, 34], # короткая последовательность
[1, 3, 7, 11, 23, 45] # полная последовательность
]
Sliding Window
Создание множественных подпоследовательностей:
from recommender.data import create_sequences_sliding_window
sequences, targets = create_sequences_sliding_window(
df,
max_seq_length=10,
stride=5 # Сдвиг окна
)
# Из последовательности [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
# Создаёт:
# seq1: [1,2,3,4,5,6,7,8,9,10] -> target: 11
# seq2: [6,7,8,9,10,11,12,13,14,15] -> target: 16
# и т.д.
Примеры полных pipeline'ов
Pipeline 1: Implicit Feedback
from recommender import InteractionDataset, load_movielens
from recommender.data import filter_by_interaction_count, binarize_implicit_feedback
# 1. Загрузка
df = load_movielens(size='1m')
# 2. Фильтрация
df = filter_by_interaction_count(df, min_user_interactions=5, min_item_interactions=5)
# 3. Бинаризация
df = binarize_implicit_feedback(df, threshold=4.0)
# 4. Создание датасета
dataset = InteractionDataset(df, implicit=True)
# 5. Разделение
train, test = dataset.split(test_size=0.2, strategy='random', seed=42)
print(f"✅ Готов к обучению: {len(train)} train, {len(test)} test")
Pipeline 2: Explicit Feedback
# 1. Загрузка
df = load_movielens(size='100k')
# 2. Фильтрация
df = filter_by_interaction_count(df, min_user_interactions=10)
# 3. Нормализация (опционально)
from recommender.data import normalize_ratings
df = normalize_ratings(df, method='minmax')
# 4. Создание датасета
dataset = InteractionDataset(df, implicit=False)
# 5. Разделение
train, test = dataset.split(test_size=0.2, strategy='random', seed=42)
print(f"✅ Готов к обучению explicit модели")
Pipeline 3: Sequential Data
from recommender.data import create_sequences
# 1. Загрузка (важно: нужен timestamp!)
df = load_movielens(size='1m')
# 2. Фильтрация
df = filter_by_interaction_count(df, min_user_interactions=10)
# 3. Бинаризация
df = binarize_implicit_feedback(df, threshold=4.0)
# 4. Создание последовательностей
sequences, targets = create_sequences(
df,
max_seq_length=50,
user_col='user_id',
item_col='item_id',
time_col='timestamp'
)
# 5. Temporal split
dataset = InteractionDataset(df, implicit=True)
train, test = dataset.split(test_size=0.2, strategy='temporal')
print(f"✅ Готов к обучению SASRec")
Best Practices
1. Фильтрация данных
# ✅ Хорошо: фильтруйте редких пользователей/items
dataset = InteractionDataset(
df,
min_user_interactions=5,
min_item_interactions=5
)
# ❌ Плохо: оставляете пользователей с 1-2 взаимодействиями
# Это приводит к переобучению и плохой генерализации
2. Выбор стратегии split
# ✅ Хорошо: temporal split для realistic оценки
train, test = dataset.split(strategy='temporal')
# ⚠️ Осторожно: random split может быть слишком оптимистичным
# Используйте random только для быстрого прототипирования
3. Negative sampling
# ✅ Хорошо: используйте 4-10 негативов на позитив
sampler = UniformSampler(n_items=dataset.n_items)
train_data = create_negative_samples(train, sampler, n_negatives_per_positive=4)
# ❌ Плохо: слишком мало или слишком много негативов
# <4: модель не учится различать
# >20: замедляет обучение без улучшения качества
4. Сохранение воспроизводимости
# ✅ Всегда указывайте seed
dataset.split(test_size=0.2, seed=42)
sampler = UniformSampler(n_items=n, seed=42)
Что дальше?
Теперь, когда вы знаете как подготовить данные, можно переходить к моделям:
- Простые модели (EASE, SLIM) - начните отсюда
- Matrix Factorization (SVD, SVD++, ALS) - классические методы
- Нейронные модели (NCF, LightGCN, SASRec) - SOTA решения

