Ends in 4 weeks
53 participants
11 submissions

Данные для участников

Даты

Датасет покрывает три месяца реального трафика Авито с временным hold-out под eval:

ПериодГраницыНазначение
Весь кликстрим2026-01-21 00:00:002026-04-22 00:00:00Исходный пул событий
Trainсобытия timestamp < 2026-04-15 00:00:00Обучение модели
Gap2026-04-15 00:00:002026-04-15 12:00:00 (12 часов)Нейтральная зона между train и eval — события из gap не используются ни в train, ни в eval
Eval (hold-out)события timestamp ≥ 2026-04-15 12:00:00Контактный таргет + история eval-пользователей

Пороговая дата train — 2026-04-15 00:00:00, eval начинается с 2026-04-15 12:00:00.

Timestamp во всех файлах хранится как число миллисекунд с начала UNIX-эпохи (Int64) — получено из колонки event_date преобразованием event_date.dt.epoch('ms') от naive московского datetime. Таймзона — московская: значение timestamp совпадает с MSK-датавременем, переведённым в мс как если бы оно было UTC (реальный UTC того же события на 3 часа меньше).

Основные материалы соревнования

Все данные лежат в публичном бакете Yandex Object Storage s3://datafest2026/. Каждый файл скачивается обычным HTTPS GET — без подписи и без регистрации.

Базовый префикс: https://storage.yandexcloud.net/datafest2026/datafest_2026_v2_v4/

ФайлРазмерСсылка
item_features.parquet1.85 GBitem_features.parquet
contact_eids.csv37 Bcontact_eids.csv
eval_users.csv1.2 MBeval_users.csv
eval_user_events.zip705 MBeval_user_events.zip
train_000-019.zip7.75 GBtrain_000-019.zip
train_020-039.zip7.74 GBtrain_020-039.zip
train_040-059.zip7.75 GBtrain_040-059.zip
train_060-079.zip7.73 GBtrain_060-079.zip
train_080-099.zip7.74 GBtrain_080-099.zip
prepare_local_eval.py9 KBprepare_local_eval.py
popular.py2.2 KBpopular.py
submission_popular.csv249 MBsubmission_popular.csv
Итого~41 GB12 объектов

Список user_id, для которых нужно строить предсказания, — это eval_users.csv (94 408 пользователей). Правильные ответы (Public / Private таргеты) участникам не выдаются.

Для быстрого скачивания целиком можно использовать wget / curl, например:

BASE=https://storage.yandexcloud.net/datafest2026/datafest_2026_v2_v4
curl -O $BASE/item_features.parquet
curl -O $BASE/contact_eids.csv
curl -O $BASE/eval_users.csv
curl -O $BASE/eval_user_events.zip
curl -O $BASE/prepare_local_eval.py
curl -O $BASE/popular.py
curl -O $BASE/submission_popular.csv     # готовый сабмит popular (можно сразу залить как sanity-check)
for i in 000-019 020-039 040-059 060-079 080-099; do
    curl -O $BASE/train_${i}.zip
done

Структура директории после распаковки всех архивов:

datafest_2026_v2_v4/
├── train_data/
│   ├── part_000.parquet
│   ├── part_001.parquet
│   ├── ...
│   └── part_099.parquet       # 100 партиций по user_id % 100
├── eval_user_events.pq        # история eval-пользователей ДО 2026-04-15
├── item_features.parquet      # фичи объявлений
├── contact_eids.csv           # анонимизированные eid контактных событий
├── eval_users.csv             # 94 408 user_id, для которых нужно сделать предсказание
├── prepare_local_eval.py      # скрипт сборки локального валидационного eval
├── popular.py                 # эталонный простейший бейзлайн
└── sample_submission.csv      # пример корректного сабмита (качается отдельно)

Принцип партиционирования train: пользователь целиком живёт в одной партиции — part_id = user_id % 100. Благодаря этому любую выборку «по пользователям» можно считать пар-партиционно, не читая весь датасет. Внутри партиции строки уже отсортированы по (user_id, timestamp), так что события пользователя идут последовательно.

Данные операций (train_data/)

Это основной источник сигнала — обезличенный кликстрим train-периода (~5 млрд событий).

КолонкаТипОписание
timestampInt64Момент события в миллисекундах от UNIX-эпохи (naive MSK через .dt.epoch("ms") — то есть мс соответствует MSK-датавремени, представленному как если бы оно было UTC).
eidUInt32Тип события: показ, клик, добавление в избранное, контакт и др. Анонимизирован.
user_idUInt32Анонимизированный идентификатор пользователя (0-based plain int).
item_idUInt32Анонимизированный идентификатор объявления.

Одна строка — одно действие. Если пользователь посмотрел объявление, сохранил его, а потом написал продавцу, это три строки с разными eid, но одинаковыми user_id и item_id.

Подмножество eid, которые считаются контактом (звонок, сообщение, показ телефона и т. д.), перечислено в contact_eids.csv — именно такие события формируют eval-таргет.

В train_data/part_*.parquet лежат события всех пользователей, кроме 94 408 eval-пользователей. Их история разбита по 100 партициям детерминированно: part_id = user_id % 100, внутри партиции строки отсортированы по (user_id, timestamp). События одного пользователя никогда не делятся между партициями.

История eval-пользователей (eval_user_events.pq)

Важная особенность: eval_user_events.pq и train_data/part_*.parquet не пересекаются по user_id. История 94 408 eval-пользователей вынесена в отдельный файл и в основных train-партициях её нет. Поэтому для любого инференса или скоринга на eval-юзерах нужно явно читать именно этот файл, а не искать их события в train_data/.

Файл устроен так же, как train-партиции (те же 4 колонки: timestamp, eid, user_id, item_id), но покрывает только:

  • пользователей из eval-выборки (94 408 человек);
  • события из train-периодаtimestamp < 2026-04-15 00:00:00;
  • уже отсортировано по (user_id, timestamp).

Зачем он публикуется отдельно:

  • Быстрый инференс без 40 ГБ. Чтобы получить кандидатов для eval-юзера, достаточно прочитать этот один файл (~700 МБ упакованный) и item_features.parquet — не нужно скачивать все 5 zip-архивов train-партиций.
  • Явный источник списка eval-юзеров. Уникальные user_id этого файла — это ровно те 94 408 пользователей, для которых нужно предсказать кандидатов.
  • История до cutoff. Файл содержит только события до threshold_date, так что использовать его в качестве признаков безопасно — утечки из eval-периода в нём быть не может.

Этих данных достаточно, чтобы пройти весь инференс-пайплайн и сгенерировать валидный сабмит.

Фичи айтема (item_features.parquet)

Справочник всех объявлений, встречающихся в датасете. Одна строка — один item_id.

КолонкаТипОписание
item_idUInt32Анонимизированный id объявления — ключ, на котором джойнятся кликстрим и таргет.
vertical_idUInt32Анонимизированная вертикаль каталога (8 уникальных значений 0–7). Конкретные имена вертикалей не раскрываются.
category_ext_yIntТокенизированная категория (низкий уровень таксономии).
region_id_yIntТокенизированный регион (субъект РФ).
loc_id_yIntТокенизированный город / локация внутри региона.
sid_0_yIntСемантический id объявления, кодбук № 0.
sid_1_yIntСемантический id, кодбук № 1.
sid_2_yIntСемантический id, кодбук № 2.
sid_3_yIntСемантический id, кодбук № 3.

sid_0..sid_3 — это выход Residual Quantization-кодировщика над BERT-эмбеддингом текста объявления (title + description). Четыре последовательных кодбука дают «грубое → тонкое» представление контента. Семантически близкие объявления имеют близкие префиксы sid, поэтому эти колонки особенно полезны для кандидат-генерации в холодном старте.

Контактные события (contact_eids.csv)

Маленький CSV с одной колонкой mapped_eid — анонимизированные значения eid, которые считаются контактом с продавцом.

Пример типов событий, попадающих в этот список (до анонимизации): показ номера телефона, сообщение в чат, кнопка «Написать», «Позвонить» из карточки, из поиска, из рекомендаций и т. п. Всего в исходном пайплайне в CONTACT_EIDS входит 11 таких идентификаторов.

Используется в двух местах:

  • при сборке eval-таргета организаторами — чтобы оставить только события-контакты;
  • участниками — как признак «пользователь сделал сильное действие» при построении фич.

Список eval-пользователей (eval_users.csv)

CSV с одной колонкой user_id — это ровно те 94 408 пользователей, для которых нужно предсказать кандидатов. Список выровнен с уникальными user_id из eval_user_events.pq.

Способ формирования (см. также раздел «Как собирается eval» на главной странице): из всех пользователей (с хотя бы одним валидным контактом в eval-периоде) сэмплируются 10 непересекающихся бакетов до 10 000 человек в каждом, итого 94 408 уникальных пользователей:

  • 5 вертикальных бакетов — пользователи, у которых ≥90% уникальных train-объявлений лежит в одной вертикали; до 10 000 пользователей в каждом бакете. Используются 6 из 8 анонимизированных mapped_vertical_id, сгруппированные в 5 бакетов: v0={0}, v2={2}, v3={3}, v4={4}, v57={5, 7} (id 5 и 7 объединены в один бакет, так как поведение пользователей в них схоже). mapped_vertical_id 1 и 6 — маленькие нишевые вертикали — в eval не входят;
  • 5 holdout-бакетов (h0..h4) — по 10 000 пользователей в каждом, случайно сэмплируются из «свободного» пула (т. е. из тех, кто не попал ни в один вертикальный бакет).

Локальный eval (prepare_local_eval.py)

Файл prepare_local_eval.pyготовый скрипт для построения локальной валидации. Он полностью повторяет логику серверного eval-пайплайна, но делает синтетический split внутри train-периода:

python prepare_local_eval.py \
    --train data/train.parquet \
    --item-features data/item_features.parquet \
    --contact-eids data/contact_eids.csv \
    --out data/local_eval.csv

По умолчанию синтетическая граница — 2026-04-08 00:00:00 (на неделю раньше реальной), gap — 12 часов, K — 160. Скрипт применяет все те же шаги, что и реальный eval:

  1. Контактные события из периода [synth_threshold + 12h, …].
  2. Item_id с ≥2 уникальными пользователями в synth_train.
  3. Anti-join: (user_id, item_id) не должна встречаться в synth_train.
  4. Бакетинг 5 вертикальных (до 10 000 пользователей в каждом) + 5 holdout × 10 000 пользователей по официальной схеме: v0={0}, v2={2}, v3={3}, v4={4}, v57={5, 7} (значения — mapped_vertical_id из item_features.parquet).
  5. Первые 160 уникальных контактных таргетов на пользователя по времени.

На выходе получается два файла:

  • local_eval.csv(user_id, item_id), скоррится через bin/datafest_2026_v2/calc_metric.py;
  • local_eval_users.csv(user_id, bucket), можно считать Recall@160 по бакетам отдельно.

CLI принимает только пути к данным и --synth-threshold. Остальные параметры (12h gap, K=160, ≥2 users per item, 0.9 vertical threshold, бакетинг v0/v2/v3/v4/v57 × до 10 000 пользователей + 5 holdout × 10 000) зашиты в скрипт как константы — они зафиксированы официальной спецификацией eval.

Эталонный бейзлайн (popular.py)

Файл popular.pyсамый простой работающий бейзлайн: для каждого eval-пользователя выдаёт один и тот же топ-160 объявлений, чаще всего встречающихся в pre-threshold истории eval-пользователей. Из расчёта исключаются вертикали 1 и 6 (не входят в eval-бакеты), оставшиеся 6 вертикалей {0, 2, 3, 4, 5, 7} определяют пул объявлений.

python popular.py \
    --eval-user-events data/eval_user_events.pq \
    --item-features data/item_features.parquet \
    --eval-users data/eval_users.csv \
    --out submission_popular.csv

CLI:

  • --eval-user-events — путь к eval_user_events.pq (для реального сабмита) или к временному synth_train.parquet (для локальной проверки совместно с prepare_local_eval.py).
  • --item-features — путь к item_features.parquet (для фильтрации по vertical_id).
  • --eval-users — путь к eval_users.csv (одна колонка user_id).
  • --out — куда писать сабмит (по умолчанию submission_popular.csv).
  • --k — сколько объявлений на пользователя (по умолчанию 160; равен лимиту Recall@160).

На выходе — CSV (user_id, item_id) ровно K * len(eval_users) строк, где у всех пользователей одинаковый список топ-K. На реальном eval даёт Recall@160 ≈ 0.0010 — это нижняя планка качества: любая разумная модель должна обогнать эту цифру.

Готовый результат запуска popular.py уже выложен в бакет: submission_popular.csv (249 MB, 94 408 пользователей × 160 объявлений). Можно скачать и сразу залить в систему — будет валидный sanity-check сабмит.

Формат сабмита (sample_submission.csv)

sample_submission.csv — пример валидного файла-сабмита. Формат — две колонки user_id,item_id, не более 160 строк на одного user_id, пары (user_id, item_id) уникальны.

user_id,item_id
42,81234
42,8912
42,14
77,553
...

В нём перечислены все user_id, для которых нужно предсказать кандидатов, — это полный список eval-пользователей (94 408 человек). Файл можно использовать как «скелет»: взять колонку user_id, приклеить к ней свои предсказания и отправить.

Правильные ответы (Public/Private таргеты) участникам не выдаются — без них нельзя случайно или умышленно подогнать submission под публичный таргет. Метрика Public/Private считается только на сервере после отправки.

Глоссарий

ТерминЗначение
user_idАнонимизированный идентификатор пользователя Авито.
item_idАнонимизированный идентификатор объявления.
eid (event id)Тип события пользователя на площадке: показ, клик, добавление в избранное, контакт и т. д.
контактСобытие, означающее, что пользователь обратился к продавцу (сообщение, звонок, показ телефона…). Именно эти события — таргет соревнования. Список eid-ов контактов см. в contact_eids.csv.
vertical_idАнонимизированный идентификатор вертикали каталога Авито (8 значений 0–7, конкретные названия не раскрываются). Eval сэмплируется с балансом по вертикалям.
category_extКатегория объявления — более мелкий уровень таксономии внутри вертикали.
region_id / loc_idГеографические идентификаторы: регион и локация (город / район). Все значения токенизированы.
sid_0 … sid_3Semantic IDs — выход Residual-Quantization BERT-кодировщика над текстом объявления; 4 последовательных кодбука. Близкие по смыслу объявления имеют общие префиксы.
threshold_dateГраница train: 2026-04-15 00:00:00 MSK. Eval начинается с 2026-04-15 12:00:00 (12-часовой gap).
Recall@160Доля объявлений из таргета, попавших в топ-160 сабмита, усреднённая по пользователям.
Public / PrivateДетерминированное разбиение eval-пользователей по хэшу user_id × bucket в пропорции 20% / 80% внутри каждого бакета . Public виден всё соревнование, Private раскрывается в конце.

Our website uses cookies, including web analytics services. By using the website, you consent to the processing of personal data using cookies. You can find out more about the processing of personal data in the Privacy policy