Sampled Softmax for Large-Scale Retrieval.
В рекомендашках на стадии генерации кандидатов часто используются двухбашенные модели, в которых пользователи и айтемы отдельно кодируются в векторы нейросетевыми «башнями», и затем с помощью скалярного произведения определяется, насколько пользователю релевантен тот или иной айтем. Мы умеем быстро находить для пользователя примерный топ айтемов с максимальным скалярным произведением (например, с помощью HNSW), даже для очень больших каталогов с миллионами айтемов.
У таких моделей должна быть способность глобального сравнения — возможность для пользователя сравнивать между собой «скоры» всевозможных айтемов из каталога. Ранжирующие лоссы, на которые учатся верхние стадии, для обучения нам не подходят — при таком обучении у модели нет цели научиться ранжировать для пользователя все айтемы, ей важно научиться ранжировать только то, что обычно попадает к нему в выдачу. Если YouTube обычно показывает испанцам видео на испанском, а итальянцам на итальянском, то модели не нужно уметь сравнивать видео на испанском с видео на итальянском, и она может «наложить» (англ. folding) векторы айтемов из этих двух групп друг на друга в векторном пространстве.
А вот что нам подходит — это softmax-лосс. Можно сформулировать задачу рекомендации в виде экстремальной классификации, в которой каждому айтему соответствует свой класс (экстремальной — потому что много классов). Если пользователь лайкнул трек, то что это был за трек? Считаем для каждого трека некоторый скор (читай «логит»), затем превращаем скоры в вероятностное распределение, применив softmax. Учим модель максимизировать правдоподобие правильного класса — минимизируем кросс-энтропийную функцию потерь. При таком обучении в саму модель заложено, что мы должны уметь делать глобальное ранжирование между всеми айтемами в каталоге.
У такого лосса есть одна «большая» проблема — чем больше каталог, тем сложнее посчитать сам softmax. В знаменателе есть сумма экспонент от логитов по всему каталогу. Нам в целом не нужно считать сам лосс, важен только градиент, но в градиенте эта сумма тоже присутствует. Чтобы это побороть, можно использовать сэмплирование негативов — вместо суммы скоров по всему каталогу считать её по небольшому сэмплированному подмножеству. Популярные стратегии сэмплирования — равномерное (выбираем айтемы из каталога абсолютно случайно) и униграммное (пропорционально популярности айтема). Частный случай униграммного — in-batch сэмплирование: при обучении нейросетей мы набираем «батчи» из положительных пар (пользователь, айтем), и можно для каждого пользователя из батча использовать в качестве негативов чужие позитивы из этого же батча.
Почему же всё-таки недостаточно простого равномерного сэмплирования? Здесь поможет следующая интуиция: «полный» softmax-лосс неявно занимается майнингом негативов — у айтемов из каталога, которым модель даёт высокий скор, большой вклад в градиент. Это отличительное свойство softmax-а — например, у BPR-лосса, если модель сильно ошибается в каком-то айтеме, его вклад в градиент всё равно сильно ограничен. Поэтому, если мы хотим аппроксимировать softmax-лосс, нам важно сэмплировать как можно больше «сложных, информативных» негативов, в которых модель ошибается и у которых был бы большой вклад в градиент. А равномерное сэмплирование, особенно при больших каталогах, будет давать простые негативы. Добиться хорошего качества с равномерными негативами всё равно можно, но понадобится гораздо большее количество негативов, чем при использовании альтернативных, более «сложных» источников негативов.
А вот при униграммном сэмплировании мы получаем популярные айтемы, которые чуть ли не по определению имеют высокий средний скор у пользователей и поэтому представляют из себя более «сложные» негативы. Но и здесь не без проблем :) Что же идёт не так — я расскажу в следующем посте.
P.S.: это я вас так готовлю к посту про статью, которую у нас приняли на RecSys :) А ещё это небольшое переосмысление прошлого поста на ту же тему. Что-то похожее я рассказываю в рамках лекции про нейросетевой кандген в ШАД.
Post #236
3.31K
- 👍 24
- 🔥 9
- ❤ 6
- 🙏 1