Что это такое

Соревнование — это задача с закрытой проверкой. Вы даёте классу данные и условие, участник присылает тетрадь .ipynb файлом, сервер исполняет её с нуля и считает результат вашей метрикой. Ответы, по которым считается результат, участник не видит никогда, а в лидерборде видит своё место.

Соревнование живёт не в комнате: у него свой адрес вида /k/<имя>, своя очередь на весь инстанс и свои участники. Занятие для него не нужно — достаточно ссылки. Заводят и ведут соревнование в панели, в разделе «Соревнования».

Заведите соревнование

  1. В панели откройте «Соревнования» и нажмите «НОВОЕ СОРЕВНОВАНИЕ». Дайте название и адрес — адрес попадёт в ссылку /k/<имя>, и по ней класс откроет задачу.
  2. Напишите краткую строку (её видно в списке) и описание в Markdown: условие, что лежит в данных и что сдавать.
  3. Положите открытые файлы данных. Участник скачивает их со страницы задачи и видит внутри контейнера как каталог data/, только на чтение. Потолок — 200 МБ на соревнование.
  4. Положите скрытые ответы отдельной кнопкой. На диске файл всегда называется solution.csv и лежит вне рабочих папок занятий, поэтому из комнаты его не прочитать. Ключ строки обязан называться id: по нему делятся строки и сверяется присланный файл. Другого имени колонки пока не задать, и соревнование с ним не откроется — проверка бейзлайна это поймает.
  5. Вставьте код метрики или возьмите заготовку (MAPE, RMSE, MAE, ROC AUC, F1, accuracy, QWK) — заготовка соберётся по колонкам вашего файла ответов. Укажите, в какую сторону лучше: меньше или больше.
  6. Загрузите сэмпл-тетрадь — базовое решение, с которого начинает каждый участник, — и нажмите «Проверить целиком».
  7. Поставьте дедлайн и нажмите «ОТКРЫТЬ СОРЕВНОВАНИЕ».

Пока какой-нибудь из шагов не сделан, кнопка «ОТКРЫТЬ СОРЕВНОВАНИЕ» не нажимается и называет недостающее. Отдельный гейт — сэмпл-тетрадь: пока она не прошла весь путь до числа, соревнование не открыть вовсе. Задача, которая не решается даже у автора, — это сто человек, безуспешно ищущих ошибку у себя.

Код метрики

Метрика — одна функция. Сервер зовёт её с двумя таблицами pandas: вашим файлом ответов и присланным submission.csv, уже сведёнными по колонке id. Вернуть надо число.

import numpy as np
import pandas as pd


class ParticipantVisibleError(Exception):
    pass


def score(solution: pd.DataFrame, submission: pd.DataFrame) -> float:
    if 'orders' not in submission.columns:
        raise ParticipantVisibleError('В ответе нет колонки orders')
    merged = solution.merge(submission, on='id', how='left', suffixes=('_true', '_pred'))
    if merged['orders_pred'].isna().any():
        raise ParticipantVisibleError('Не для всех строк test.csv есть прогноз')
    true = merged['orders_true'].to_numpy(dtype=float)
    pred = merged['orders_pred'].to_numpy(dtype=float)
    return float(np.mean(np.abs((true - pred) / np.maximum(true, 1e-9))))

Текст ParticipantVisibleError участник читает дословно — это и есть способ объяснить ему, чем плох его файл. Любая другая ошибка видна только вам: посылка помечается «УПАЛА МЕТРИКА», участник видит фразу «проверяющий код упал», а трассировку — вы. Поправив код, нажмите «Исправить метрику и пересчитать всех»: тетради при этом не перезапускаются, считается только метрика.

«Проверить на бейзлайне» прогоняет метрику по сохранённому ответу сэмпл-тетради, не запуская её заново, — так правку кода проверяют за секунды.

Публичная и приватная части

Метрика считается дважды: на публичной части строк ответа и на приватной. Публичный лидерборд виден всегда — по нему участники понимают, движутся ли они. Итоговый закрыт до дедлайна: под скрытую часть нельзя подогнаться, обновляя страницу.

Доля публичной части задаётся числом в редакторе (по умолчанию 30 %). Строки делятся один раз и навсегда: зерно деления записывается при заведении соревнования, поэтому у всех участников и у всех посылок публичная часть одна и та же. Если в файле ответов есть колонка Usage со значениями Public и Private, деление берётся из неё, а доля не спрашивается.

В зачёт идёт та посылка, которую участник выбрал сам кнопкой «Выбрать в зачёт». Не выбрал — лучшая по публичной части. Правило зачёта можно сменить на «лучшая по публичной» или «последняя» в настройках соревнования.

Как исполняется посылка

Участник нажимает «ВЫБРАТЬ ФАЙЛ» и отправляет тетрадь. Загрузки прямо из тетради занятия нет: только файл.

  1. Посылка встаёт в очередь инстанса.
  2. В одноразовом контейнере без сети тетрадь исполняется целиком, ячейка за ячейкой, с нуля: сохранённых переменных нет, каталог data/ примонтирован только на чтение. Тетрадь обязана записать submission.csv.
  3. Файл забирается из контейнера, контейнер сносится.
  4. Во втором одноразовом контейнере — уже без данных участника и без его кода — запускается ваша метрика. Скрытые ответы бывают только здесь.

Пределы соревнования (время на тетрадь, память, ядра) стоят в редакторе. По времени контейнер убивают снаружи, по памяти его ограничивает docker. Участник видит движение этапов вживую и, если тетрадь упала, свою трассировку целиком.

ИсходЧто видит участник
ГОТОВОПубличный результат и своё место.
УПАЛА ТЕТРАДЬ«Упала на ячейке 5 из 14» и свою трассировку дословно.
ЛИМИТ ВРЕМЕНИ«Не уложилась в 10 минут, ячейка 6 из 50».
НЕ ХВАТИЛО ПАМЯТИ«Заняла больше 4 ГБ, ячейка 2».
ОТВЕТ НЕ ПРИНЯТ«Дошла до конца, но submission.csv не оставила» — или текст вашего ParticipantVisibleError.
УПАЛА МЕТРИКА«Проверяющий код упал» без подробностей; трассировку видите вы, и посылка ждёт пересчёта.

Очередь и пульт

Очередь одна на весь инстанс: она лежит в базе и переживает перезапуск сервера. По умолчанию исполняется одна посылка за раз — на той же машине рядом идёт занятие. Очередь честная по людям: вторая посылка одного человека встаёт за первыми посылками остальных, поэтому один участник не займёт исполнитель на всю пару.

Пульт соревнования (вкладка «Посылки») показывает, что исполняется сейчас, кто ждёт, пять чисел сводки и ленту посылок. В меню строки: открыть исполненную тетрадь, посмотреть весь вывод, исполнить заново, пересчитать метрику, не засчитывать. Полоса исполнителя в списке соревнований позволяет приостановить очередь, а у идущего прогона есть кнопка «Убить».

Чтобы никто не занимал очередь целиком, поставьте дневную норму посылок. Посылка, упавшая до первой ячейки, норму не тратит.

Участники и ключ входа

Участник соревнования — не житель комнаты: его личность принадлежит всему инстансу. Вступая, человек называет имя и получает ключ входа вида K7Q-M2X-9FD и ссылку к нему. Тем же ключом он возвращается к своим посылкам с другого устройства, из другого браузера или после очистки данных.

Ключ лежит в карточке «ВАШ КЛЮЧ ВХОДА» на странице соревнований — её видно каждый раз, а не однажды. В базе самого ключа нет: хранятся его отпечаток и копия, зашифрованная секретом инстанса, поэтому украденная база без секрета не открывает ни одного входа.

Если участник ключ потерял, выдайте новый на вкладке «Участники». Старый перестаёт действовать в ту же секунду — вместе со всеми вкладками, которые были на нём открыты.

Дедлайн и разбор

Когда дедлайн проходит, приём посылок закрывается сам, а итоговый лидерборд открывается — если в настройках выбрано «открыть на дедлайне». Второй вариант — «Открою вручную — на разборе»: тогда итоги показывает кнопка, и вы решаете, в какую минуту пары их увидят.

«Завершить сейчас» закрывает приём раньше срока. На разборе пригодится «Лидерборд на проектор» — отдельное окно с одной таблицей, без имён файлов и служебных отметок.