Как строить выборку для обучения модели с релевантными парами наблюдений
Что проверяют
Понимаете ли вы, как собирать обучающие пары для ранжирования, поиска похожих объектов или сиамских моделей: откуда брать позитивы, как выбирать негативы и как не устроить утечку при разбиении. Следом обычно спрашивают про hard negatives и про то, почему случайные негативы дают завышенные метрики.
Как отвечать
- позитивы: пары, связь в которых подтверждена сигналом. Покупка после запроса, два описания одного товара, вопрос и принятый ответ. Сразу оговорите шум: клик по первой позиции часто говорит о позиции, а не о релевантности;
- негативы: случайные дёшевы, но слишком лёгкие, модель быстро перестаёт на них учиться. Hard negatives берут из выдачи текущей модели или BM25: похожие, но нерелевантные объекты. Среди них встречаются неразмеченные позитивы, поэтому их фильтруют;
- in-batch negatives: в контрастивном обучении позитивы соседних примеров батча служат негативами, это даёт много пар без явной генерации;
- объём и баланс: число возможных пар растёт квадратично, поэтому пары семплируют с фиксированным числом негативов на позитив и следят, чтобы популярные объекты не забили выборку;
- разбиение: делить по группам, а не по парам. Запрос или пользователь целиком уходит в train или в test, в scikit-learn для этого есть
GroupKFoldиGroupShuffleSplit. Если данные во времени, делите по времени; - оценка: метрики ранжирования внутри группы, NDCG@k, MRR, Recall@k, а не accuracy по парам.
В коде это цепочка из трёх шагов: таблица взаимодействий, groupby по запросу с семплингом негативов на группу, разбиение по идентификатору группы.
Где спотыкаются
Делят пары случайно. Один и тот же объект оказывается и в train, и в test, и метрика отражает запоминание, а не обобщение.
Берут только случайные негативы. На валидации всё отлично, а в реальной выдаче модель не различает похожие объекты.
Считают любой клик позитивом, не учитывая смещение по позиции и случайные клики.