26 / 27 · Не только классификация
Обучение ранжированию
Когда ответ — порядок, а не метка: запросы, документы и многоуровневая релевантность; метрики ранжирования — точность на k, MAP, MRR и NDCG; поточечные, попарные и списочные функции потерь на примере RankNet и ListNet; LambdaRank и LambdaMART, чьи градиенты взвешены изменением NDCG; клики как метки, смещённые позицией.
Поиск аномалий · Логистическая регрессия · Метрики классификации · Градиентный бустинг
На этой странице 14
Заметка о поиске аномалий закончилась оценкой (score) для каждой точки и аналитиком, который читает список сверху. Там имел значение только порядок оценок: детектор полезен, если настоящие аномалии идут первыми, а до тысячной строки никто не доходит. Это уже была задача ранжирования, решённая без разметки. В этой заметке мы решаем её с учителем. Поисковая система, каталог товаров, лента новостей и поисковая модель (retriever) вопросно-ответной системы — все они возвращают упорядоченный список, и люди судят о списке по его началу. Выход — порядок, а не метка. Заметка определяет, как измерить качество порядка, объясняет, почему такие меры нельзя оптимизировать напрямую, и строит три семейства функций потерь, которые их заменяют, заканчивая LambdaMART — ранжировщиком на градиентном бустинге, с которого до сих пор начинается большинство промышленных систем.
Обозначения. Запросу соответствуют документов-кандидатов. Документ описывается вектором признаков пары (запрос, документ) и имеет метку многоуровневой релевантности (graded relevance) , от нерелевантного до идеального; бинарная метка — частный случай. Модель ставит каждому документу оценку , и ранжирование сортирует документы по убыванию оценки. — метка документа на позиции этого ранжирования, а — порог (cutoff): метрика «на » смотрит только на первые позиций. — логистическая функция.
Запросы, документы и релевантность#
Обучающие данные для ранжирования сгруппированы. У каждого запроса свой список кандидатов — обычно несколько сотен документов, которые дешёвый первый этап отобрал из миллионов, — и у каждого кандидата есть метка, поставленная асессором, который прочитал запрос и документ. Порядки сравниваются только внутри запроса: должен ли документ о ягуаре-животном идти раньше документа об автомобиле Jaguar, зависит от того, что спросили. Отсюда следует, что оценки модели имеют смысл лишь с точностью до сдвига, своего для каждого запроса. Прибавление константы ко всем оценкам запроса ничего не меняет.
Признаки описывают пару и бывают трёх видов:
- Признаки запроса зависят только от запроса: его длина, частота в логах, называет ли он товар. Они не могут изменить порядок внутри одного запроса, но могут изменить то, как следует читать другие признаки.
- Признаки документа зависят только от документа: его длина, свежесть, важность по ссылкам, например PageRank — стационарное распределение случайного пользователя, переходящего по ссылкам (random surfer).
- Признаки пары запрос–документ измеряют соответствие: оценка BM25 текста по словам запроса, косинус между их эмбеддингами, доля слов запроса в заголовке.
Из группировки следуют два практических правила. Валидационная выборка отделяется по запросам, а не по строкам: если документы одного запроса попадут и в обучение, и в валидацию, модель выучит особенности запроса и покажет качество, которого не достигнет на новых запросах, — это утечка из заметки о данных и утечках и групповые разбиения из заметки о валидации. Кроме того, ранжировщик обычно работает как второй этап каскада: быстрая поисковая модель сужает миллионы документов до сотен, а обученный ранжировщик их упорядочивает. Заметка о семантическом поиске строит именно такую пару для текста: биэнкодер (bi-encoder), который находит кандидатов, и кросс-энкодер (cross-encoder), который их переранжирует.
Измерение качества порядка#
Метрики классификации считают ошибки на отдельных объектах. Метрика ранжирования должна взвешивать ошибки по позиции: нерелевантный документ на первом месте стоит дороже, чем на сороковом, потому что читатель видит первое место, а не сороковое. Все метрики ниже считаются для каждого запроса и затем усредняются по запросам, так что каждый запрос весит одинаково независимо от длины своего списка.
Сквозной пример заметки — один запрос с шестью документами. В порядке, в котором их ранжирует модель, их метки равны
Бинарная релевантность: точность, MAP и MRR#
Для бинарных метрик будем называть документ релевантным, если : здесь это позиции 2, 3 и 5.
Точность на (precision at k) — доля релевантных документов среди первых :
В примере , и . Её легко читать, и она не учитывает порядок внутри первых : релевантный документ на первом и на -м месте учитывается одинаково.
Средняя точность (average precision) поощряет релевантные документы за то, что они идут рано. Она усредняет точность на позиции каждого релевантного документа по всем релевантным документам запроса:
При релевантных документах на позициях 2, 3 и 5 . Релевантный документ, который так и не был найден, даёт точность 0, но всё равно учитывается в , поэтому AP штрафует за пропуски (Manning, Raghavan and Schütze, 2008, §8.4). Библиотеки расходятся в усечённых вариантах, AP@: одни делят на , другие на ; стоит проверять, какой вариант использован в опубликованном числе. Среднее AP по запросам — MAP (mean average precision).
Обратный ранг (reciprocal rank) смотрит только на первый релевантный документ: , где — его позиция, и 0, если такого нет. Здесь . Его среднее по запросам, MRR (средний обратный ранг), подходит для задач с одним правильным ответом, например для вопроса с одним фрагментом, который на него отвечает; так заметка о семантическом поиске измеряет качество поисковой модели.
Разобранный пример для двух запросов. Второй запрос с бинарными метками в порядке ранжирования имеет и . После усреднения с первым запросом и .
Многоуровневая релевантность: DCG и NDCG#
Бинарные метрики отбрасывают разницу между идеальным ответом и сносным. Дисконтированный совокупный выигрыш (discounted cumulative gain, DCG) её сохраняет. Каждый документ вносит выигрыш (gain), который растёт с его меткой, делённый на дисконт (discount), который растёт с его позицией:
Идея совокупного выигрыша с логарифмическим дисконтом принадлежит Järvelin and Kekäläinen (2002), §2.2; они использовали в качестве выигрыша саму метку и не дисконтировали позиции до выбранной. Экспоненциальный выигрыш , при котором один идеальный документ ценнее нескольких удовлетворительных, с логарифмическим дисконтом начиная с первой позиции — это форма, которую использовали Burges et al. (2005), §5, и которую реализует большинство библиотек (Manning, Raghavan and Schütze, 2008, §8.4). Основание логарифма сокращается в NDCG ниже; основание 2 — соглашение. У DCG нет фиксированной шкалы: запрос с большим числом релевантных документов достигает больших значений, чем запрос с единственным хорошим ответом. NDCG делит на DCG идеального порядка — документов, отсортированных по метке, — так что 1 означает идеальное ранжирование:
Разобранный пример. Шесть документов дают такие вклады:
| позиция | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| метка | 0 | 3 | 1 | 0 | 2 | 0 |
| выигрыш | 0 | 7 | 1 | 0 | 3 | 0 |
| дисконт | 1 | 0.631 | 0.5 | 0.431 | 0.387 | 0.356 |
| вклад | 0 | 4.417 | 0.5 | 0 | 1.161 | 0 |
поэтому . Идеальный порядок даёт , и . При документ с меткой 2 выпадает из числителя, но не из идеального порядка, и . Больше всего теряется на позиции 1: документ с меткой 3 стоит на одно место ниже, чем нужно, и его подъём умножил бы его вклад на .
Соглашения, которые меняют среднее. AP не определена для запроса с , а NDCG — для запроса с , то есть для запроса без единого релевантного документа. Раздел «Код» исключает такие запросы из среднего (68 из 1000 тестовых запросов); другие реализации ставят им 0 или 1. Каждый выбор описывает свою совокупность запросов, поэтому соглашение нужно указывать рядом с числом. Важны ещё два выбора: идеальный DCG обрезается на том же , что и DCG, а документы с равными оценками упорядочиваются по какому-то правилу, здесь — по порядку сортировки.
Почему метрики нельзя оптимизировать напрямую#
Каждая метрика выше зависит от оценок только через порядок, который они задают. Малое изменение одной оценки либо оставляет порядок, а с ним и метрику, неизменным, либо меняет местами двух соседей, и метрика меняется скачком. Как функция оценок, а значит, и параметров модели, NDCG кусочно-постоянна: её градиент почти всюду равен нулю и не определён в точках скачков (Burges, 2010, §3). Градиентный спуск не получает от неё сигнала.
NDCG — ступенчатая функция оценок, функция потерь RankNet — гладкая
С этой проблемой заметка о линейных классификаторах столкнулась на функции потерь 0–1, и ответ тот же: обучать гладкую суррогатную функцию потерь, минимизация которой, как правило, улучшает метрику, а саму метрику использовать для валидации и ранней остановки. Суррогаты различаются тем, на что они смотрят: на один документ за раз, на пары или на весь список.
Поточечный подход: регрессия на метки#
Простейший, поточечный (pointwise) подход забывает, что задача — ранжирование. Каждая пара (запрос, документ) рассматривается как пример с целевой переменной , на этих примерах обучается регрессия с квадратичной ошибкой или порядковый (ordinal) классификатор, и документы сортируются по предсказанию. Подходит любая модель курса, включая градиентный бустинг, и этот метод — сильное базовое решение.
Его функция потерь, однако, измеряет не то. Возьмём два документа одного запроса с метками 1 и 2. Предсказания близки к меткам, среднеквадратичная ошибка , но ставят документы в неправильном порядке. Предсказания далеки от меток, среднеквадратичная ошибка , но порядок правильный. Регрессия предпочитает первые. В общем случае поточечная функция потерь тратит усилия на то, что не влияет на порядок: на точное значение каждого нерелевантного документа внизу длинного списка и на общий уровень меток запроса, который мог бы предсказать признак запроса, но который сдвигает все оценки запроса вместе. В разделе «Код» бустинговый регрессор отдаёт половину прироста от разбиений (split gain) такому признаку запроса.
Попарный подход: RankNet#
Ранжирование — это утверждение о парах: документ должен идти раньше документа , когда . RankNet (Burges et al., 2005) моделирует вероятность этого предпочтения через разность двух оценок,
и максимизирует правдоподобие предпочтений, наблюдаемых в метках. Для каждой пары одного запроса с функция потерь — отрицательный логарифм правдоподобия
Это логистическая функция потерь классификатора, на вход которому подаётся пара, а выход говорит, какой документ выигрывает, — кросс-энтропия из заметки о методе максимального правдоподобия, применённая к предпочтениям. С линейной оценкой это буквально логистическая регрессия на векторах разностей , без свободного члена, поскольку сдвиг всех оценок сокращается. Burges et al. использовали в качестве нейронную сеть, а вместо жёстких 0 или 1 — целевые вероятности , что допускает равные метки () и предпочтения, измеренные несколькими экспертами (§3, формулы 1–3). Замена логистической функции потерь кусочно-линейной (hinge loss) даёт RankSVM (Joachims, 2002).
Разобранный пример. Неправильно упорядоченная пара с , стоит . Та же пара, упорядоченная правильно с отступом (margin) 2, , , стоит ; при равных оценках стоимость равна . Функция потерь продолжает раздвигать правильно упорядоченные пары, слабо, пока отступ не станет большим.
Функция потерь — гладкая верхняя оценка числа неправильно упорядоченных пар с точностью до множителя: . Поэтому её минимизация повышает счётчики правильно упорядоченных пар в духе AUC, а не NDCG.
Градиент и силы, которые он задаёт. Производная по оценкам равна
Обозначим через величину этого толчка: она близка к 1 для сильно перепутанной пары и близка к 0 для пары, упорядоченной с большим отступом. Шаг градиентного спуска поднимает на и опускает на ту же величину. Суммирование по всем парам, содержащим документ, даёт одно число на документ — его суммарную силу
положительную, если документ должен подняться. В разобранном примере у перепутанной пары с оценками 0.5 и 1.0 , а у правильно упорядоченной пары с оценками 2 и 0 . Для первой пары , поэтому шаг спуска поднимает на , как и должна положительная сила. Поскольку для полной функции потерь выполнено , силы — это градиент по оценкам, взятый со знаком минус. Burges (2010, §2.1) заметил, что обновлению параметров нужны только эти суммы по документам: для нейронной сети антиградиент равен , а обновление — — один обратный проход на документ вместо одного на пару, что сократило время обучения на запрос с почти квадратичного по числу документов до почти линейного. Burges записывает градиент с противоположным знаком; принятое здесь соглашение — положительное значение означает «вверх» — делает рисунки нагляднее.
Списочный подход: распределение на порядках#
Попарная функция потерь видит два документа за раз. Списочная (listwise) функция потерь сравнивает весь предсказанный список со всем идеальным. Поскольку порядки дискретны, приём, как и для классов, состоит в том, чтобы превратить оценки в распределение вероятностей. Модель Плакетта–Льюса (Plackett–Luce) строит порядок по одной позиции: первый документ выбирается с вероятностью, пропорциональной , второй — так же среди оставшихся, и так далее, поэтому вероятность порядка равна
Порядок, сортирующий по оценке, — самый вероятный, а перемещение документа с большей оценкой ниже документа с меньшей делает порядок менее вероятным (Cao et al., 2007, §4.1). Суммировать по всем порядкам непрактично, поэтому ListNet оставляет только первый множитель — вероятность оказаться первым (top-one probability) для каждого документа,
то есть softmax по списку (§4.2, теорема 6). Метки задают целевое распределение так же, , а функция потерь — кросс-энтропия (§5).
Разобранный пример. У трёх документов с оценками вероятности оказаться первым равны , затем и ; вероятность полного порядка равна . Если их метки равны , целевое распределение равно , а кросс-энтропия — : функция потерь хочет поменять местами второй и третий документы.
Softmax по кандидатам с кросс-энтропией появляется снова всякий раз, когда из списка нужно выбрать один элемент: заметка о многоклассовой классификации применяет его к классам, а контрастная функция потерь, на которой обучаются биэнкодеры семантического поиска, — к фрагментам, с одним релевантным фрагментом на запрос и остальными фрагментами батча в роли списка. Списочная функция потерь видит весь список, но всё ещё обращается со всеми позициями одинаково: ничто в ней не говорит, что первое место важнее пятого.
LambdaRank: градиенты, взвешенные по NDCG#
RankNet минимизирует число неправильно упорядоченных пар, и на сквозном примере это приводит к неверным приоритетам. Сильнее всего он толкает документ с меткой 2 на пятом месте, который проигрывает три пары менее релевантным документам над ним. Для NDCG важнее документ с меткой 3 на втором месте: один его обмен с нерелевантным документом наверху поднял бы NDCG больше, чем что-либо, что может произойти на позиции 5.
Наблюдение, лежащее в основе LambdaRank (Burges, Ragno and Le, 2006), состоит в том, что обучению нужны только градиенты по оценкам, , а не функция потерь, градиентами которой они являются. Поэтому запишем градиенты напрямую: сохраним силу RankNet для каждой пары и умножим её на то, насколько пара важна для метрики,
где — изменение NDCG, если документы и поменяются позициями в текущем ранжировании, а все остальные останутся на местах (Burges, 2010, §4.1, формула 6). Силы документов суммируются, как и раньше. Из десяти функций сил, которые попробовали Burges, Ragno и Le, лучше всего на их валидационных данных сработала именно эта — градиент RankNet, масштабированный изменением NDCG при обмене (§6.2).
Изменение при обмене выражается в замкнутом виде. Обмен документов и на позициях и меняет только их два слагаемых в DCG. С выигрышами и дисконтами два слагаемых превращаются в , поэтому
Разобранный пример. В сквозном списке обмен документа с меткой 3 на позиции 2 с нерелевантным на позиции 1 меняет NDCG на . Его обмен с документом с меткой 2 на позиции 5 меняет NDCG на , а обмен документов с метками 1 и 2 на позициях 3 и 5 — всего на . При оценках суммарные силы такие:
Силы λ на документах запроса: RankNet и LambdaRank
Знаки сил совпадают везде, кроме документа с меткой 1 на третьем месте, который LambdaRank едва заметно толкает вниз, а приоритеты меняются. Самый сильный толчок вверх у RankNet, , получает документ на пятом месте; у LambdaRank, против , — документ на втором месте. Оба сильнее всего толкают вниз нерелевантный документ наверху.
Есть ли функция потерь? При фиксированном порядке документов — это градиенты, взятые с обратным знаком, функции , то есть функции потерь RankNet с весами; но веса меняются всякий раз, когда меняется порядок, поэтому лямбды не являются градиентами одной фиксированной гладкой функции оценок (Burges, 2010, §4.3 и §7). Можно записать кусочные целевые функции, веса которых зависят от текущего ранжирования, но лямбды не являются обычной производной ни этих функций, ни самого NDCG, кусочно-постоянного. Эмпирически обучение с ними, по-видимому, достигает локального максимума NDCG: Burges (2010, §4.2) описывает, как веса обученной модели возмущали в сотнях случайных направлений и NDCG уменьшался во всех. Замена изменением другой метрики, MAP или MRR, обучает модель уже под эту метрику (§4.1).
LambdaMART: лямбды как цели бустинга#
Градиентному бустингу нужен только градиент функции потерь по текущим оценкам: каждое новое дерево обучается на антиградиент в каждой обучающей точке, а значения в его листьях задаются линейным поиском или шагом Ньютона. Лямбды — ровно такие градиенты, поэтому они подставляются напрямую. LambdaMART (Wu et al., 2010; Burges, 2010, §7) — это MART, градиентный бустинг Фридмана над регрессионными деревьями, в котором целевые значения заменены на :
- Для каждого запроса отсортировать документы по текущим оценкам и вычислить для каждого документа и его кривизну , где , а сумма берётся по всем парам, содержащим .
- Обучить регрессионное дерево на всех документов всех запросов методом наименьших квадратов.
- Задать значение листа шагом Ньютона, , и добавить дерево с шагом обучения: .
Силы одного запроса в сумме дают ноль, поскольку каждая пара добавляет одному документу и другому. Разбиение по признаку, постоянному внутри каждого запроса, например по признаку запроса, отправляет в каждую сторону запросы целиком; поэтому силы с каждой стороны по-прежнему суммируются в ноль, и разбиение по целым запросам не уменьшает квадратичную ошибку. Признак запроса ещё может помочь глубже в дереве, когда в узле остаётся лишь часть документов каждого запроса, но общий уровень запроса, который поточечному бустингу приходится моделировать, не даёт LambdaMART ничего, что можно было бы подогнать. Ансамбль ранжировщиков LambdaMART выиграл трек 1 конкурса Yahoo! Learning to Rank Challenge 2010 года (§1), и именно этот метод реализуют целевые функции ранжирования распространённых библиотек бустинга, например lambdarank в LightGBM и rank:ndcg в XGBoost.
На синтетических данных из раздела «Код», 1000 тестовых запросов по 30 документов, линейная модель получает одинаковый NDCG@5 с поточечной и с попарной функцией потерь (0.647 и 0.646), поточечно обученные бустинговые деревья достигают 0.766, а LambdaMART с тем же числом и глубиной деревьев — 0.814. Поточечный бустинг тратит 51% прироста от разбиений на признак запроса, LambdaMART — ничего. Разрыв зависит от случайной генерации данных: на пяти генерациях он составлял от 0.007 до 0.048, всегда в пользу LambdaMART. На данных без эффекта уровня запроса два бустинга сближаются гораздо сильнее, и это общая картина: преимущество LambdaMART реально, но часто невелико, а хорошо настроенный поточечный бустинг — трудное базовое решение.
Клики и смещение из-за позиции#
Разметка асессоров дорога, а поисковая система видит миллионы кликов в день. Клики — тоже метки, но смещённые. Пользователь может кликнуть только на то, что он просмотрел, а верх списка люди просматривают гораздо чаще, чем низ. В исследовании с отслеживанием взгляда (eye-tracking) Joachims et al. (2005) для одних пользователей меняли местами два первых результата поисковой системы, а для других переворачивали весь список: первая позиция всё равно собирала больше кликов, чем оправдывала её релевантность, — это смещение доверия (trust bias) к порядку поисковой системы (§5.2.1). Модель, обученная на сырых кликах, учится ставить первым то, что показывали первым.
Стандартная позиционная модель (position-based model) разделяет два шага. Она предполагает, что клик требует просмотра, что просмотр зависит только от позиции и что после просмотра клик зависит только от документа. При этих предположениях формула умножения вероятностей (chain rule) даёт
где второй множитель отвечает за релевантность, а вероятность просмотра (propensity) зависит только от позиции (Joachims, Swaminathan and Schnabel, 2017, §5.1). Деление каждого клика на вероятность просмотра той позиции, где он произошёл, — взвешивание по обратной вероятности просмотра (inverse propensity scoring, IPS) — даёт оценку того, какими были бы клики, если бы каждый документ был просмотрен (формула 5). Она несмещённая в рамках модели, если у каждой используемой позиции и вероятности просмотра верны; она устраняет смещение из-за показа, заложенное в модель, но не описанное выше смещение доверия, при котором сам клик после просмотра зависит от позиции. Это выборка по значимости (importance sampling), в которой роль распределения-предложения (proposal) играет залогированное ранжирование. Сами вероятности просмотра оцениваются по небольшим рандомизированным экспериментам, например по обмену результата между двумя позициями для части пользователей (§5.3).
Разобранный пример (симуляция). Документ, на который кликнули бы 30% просмотревших его пользователей, показан 100 000 раз на позиции 1, где его просматривают всегда, и 100 000 раз на позиции 3, где его просматривают в 40% случаев. Смоделированные доли кликов (click-through rate, CTR) равны 0.299 и 0.119: по сырым кликам документ на позиции 3 в два с половиной раза хуже. После деления на вероятности просмотра 1.0 и 0.4 оценки равны 0.299 и 0.297 — это тот же документ.
Второе средство меняет то, что извлекается из кликов, а не то, как они взвешиваются. Клик на результат на позиции 3, когда позиции 1 и 2 были пропущены, говорит, что пользователь предпочёл его этим двум, — это попарное предпочтение, гораздо менее чувствительное к позиции, чем сам клик (Joachims et al., 2005, §5.3). Такие предпочтения напрямую подаются в попарную функцию потерь. Какими бы ни были метки, окончательный вердикт новому ранжировщику выносит онлайн-эксперимент: A/B-тест на поведении реальных пользователей.
Что изменилось#
| Классификация и регрессия | Обучение ранжированию | |
|---|---|---|
| Выход | метка или число для каждого объекта | порядок кандидатов каждого запроса |
| Данные | независимые примеры | списки, сгруппированные по запросам; разбиение по запросам |
| Метрика | по объектам: доля правильных ответов, log loss, AUC | по спискам, с весом по позиции: P@, MAP, MRR, NDCG |
| Сигнал для обучения | градиент функции потерь | у метрики его нет; поточечные, попарные или списочные суррогаты |
| Самый известный метод | логистическая регрессия, градиентный бустинг | LambdaMART: бустинг на попарных градиентах, взвешенных по NDCG |
| Метки | даны | асессоры или клики с поправкой на смещение из-за позиции |
Ограничения#
- Метрика — это выбор при моделировании. Выигрыш , логарифмический дисконт и порог кодируют предположения о том, как люди читают список. Разные метрики предпочитают разные ранжировщики, а офлайн-метрика — лишь суррогат того, что делают пользователи.
- Лямбды — эвристика. Они не являются градиентом фиксированной функции потерь, и их успех на NDCG эмпиричен. Каждый шаг обучения требует времени, квадратичного по длине списка запроса, поскольку перебирается каждая пара.
- Документы оцениваются независимо. Сортировка по независимым оценкам не может выразить, что два почти дубликата не должны оба оказаться наверху или что список должен покрывать несколько значений неоднозначного запроса. Для разнообразия нужна модель всего списка.
- Меток мало, или они смещены. Асессоры расходятся во мнениях и стоят дорого; клики требуют модели поведения пользователей, и ошибка в вероятностях просмотра становится смещением ранжировщика.
- Петли обратной связи. Ранжировщик, обученный на кликах по собственным прошлым ранжированиям, учится на том, что сам решил показать; документы, которые он никогда не показывал, не получают ни кликов, ни шанса.
Что нужно запомнить#
- Данные для ранжирования сгруппированы по запросам; важен только порядок внутри запроса, и валидация должна разбивать данные по запросам.
- P@ считает релевантные документы среди первых ; AP усредняет точность на каждой релевантной позиции; RR равен позиция первого релевантного документа; DCG суммирует выигрыши , дисконтированные делением на , а NDCG делит на идеальный DCG.
- Метрики ранжирования кусочно-постоянны по оценкам: градиент почти всюду равен нулю. Обучать нужно суррогат, а валидировать — по метрике.
- Поточечный подход: регрессия на метки. Попарный (RankNet): логистическая функция потерь от для каждой пары с . Списочный (ListNet): кросс-энтропия между softmax от меток и softmax от оценок.
- LambdaRank умножает градиент RankNet для каждой пары, , на ; LambdaMART обучает бустинговые деревья на этих лямбдах со значениями листьев по шагу Ньютона.
- Клик — это релевантность, умноженная на просмотр; взвешивание по обратной вероятности просмотра — это выборка по значимости с залогированным ранжированием в роли распределения-предложения.
Код#
Сквозной пример, силы с рисунка, сравнение четырёх ранжировщиков на синтетических запросах и клики при смещении из-за позиции:
import numpy as np
from sklearn.linear_model import Ridge, LogisticRegression
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.tree import DecisionTreeRegressor
def dcg(y, k): # y: метки в порядке ранжирования (последняя ось); выигрыш 2^y - 1, дисконт log2(1 + позиция)
y = np.asarray(y, float)[..., :k]
return ((2 ** y - 1) / np.log2(np.arange(2, y.shape[-1] + 2))).sum(-1)
def ndcg(y, s, k): # y, s: (запросы, документы); запрос без релевантных документов даёт nan
ranked = np.take_along_axis(y, np.argsort(-s, axis=-1), -1)
with np.errstate(invalid='ignore'):
return dcg(ranked, k) / dcg(-np.sort(-y, axis=-1), k)
# сквозной пример: метки в том порядке, в котором модель ранжирует шесть документов
lab = np.array([0, 3, 1, 0, 2, 0]); rel = lab > 0
ap = (np.cumsum(rel) / np.arange(1, 7))[rel].mean()
print(rel[:3].mean().round(3), rel[:5].mean(), ap.round(4), 1 / (np.argmax(rel) + 1)) # 0.667 0.6 0.5889 0.5
print(dcg(lab, 6).round(3), dcg(np.sort(lab)[::-1], 6).round(3), (dcg(lab, 6) / dcg(np.sort(lab)[::-1], 6)).round(3)) # 6.077 9.393 0.647
def lambdas(y, s): # силы LambdaRank, веса Ньютона и силы RankNet для батча запросов
g = 2.0 ** y - 1
pos = np.argsort(np.argsort(-s, axis=-1), axis=-1) + 1
disc = 1 / np.log2(1 + pos)
ideal = np.maximum(dcg(-np.sort(-y, axis=-1), y.shape[-1]), 1e-12)[..., None, None]
better = y[..., :, None] > y[..., None, :] # пары (i, j), в которых i релевантнее j
rho = 1 / (1 + np.exp(s[..., :, None] - s[..., None, :])) # сила RankNet для пары: sigmoid(s_j - s_i)
dn = np.abs((g[..., :, None] - g[..., None, :]) * (disc[..., :, None] - disc[..., None, :])) / ideal
lam, hes, rn = np.where(better, rho * dn, 0), np.where(better, rho * (1 - rho) * dn, 0), np.where(better, rho, 0)
return lam.sum(-1) - lam.sum(-2), hes.sum(-1) + hes.sum(-2), rn.sum(-1) - rn.sum(-2)
lam, _, ranknet = lambdas(lab[None].astype(float), np.array([[2.5, 2.0, 1.5, 1.0, 0.5, 0.0]]))
print(ranknet.round(3), lam.round(3))
# [[-2.234 1.571 0.182 -1.269 2.429 -0.679]] [[-0.383 0.286 -0.005 -0.052 0.184 -0.031]]
# синтетические запросы: по 30 документов, 6 признаков; признак 5 — признак запроса, общий для всех его документов
rng = np.random.default_rng(0)
Q, N, d = 1300, 30, 6
X = rng.normal(size=(Q, N, d))
X[..., 5] = rng.normal(size=(Q, 1))
z = X[..., 0] + 0.7 * X[..., 1] + X[..., 2] * X[..., 3] + 1.5 * X[..., 5] + rng.normal(0, 0.7, (Q, N))
Y = np.digitize(z, [1, 2, 3, 4]).astype(float) # многоуровневая релевантность 0..4
tr, te = slice(0, 300), slice(300, Q) # разбиение по запросам: 300 для обучения, 1000 для теста
Xtr, Ytr, Xte = X[tr].reshape(-1, d), Y[tr].ravel(), X[te].reshape(-1, d)
score = lambda s: np.nanmean(ndcg(Y[te], s.reshape(1000, N), 5)).round(3)
print('random', score(rng.normal(size=(1000, N)))) # random 0.19
print('linear, pointwise', score(Ridge(1.0).fit(Xtr, Ytr).predict(Xte))) # linear, pointwise 0.647
i = np.nonzero(Y[tr][:, :, None] > Y[tr][:, None, :]) # линейный RankNet: логистическая регрессия на разностях
diff = X[tr][i[0], i[1]] - X[tr][i[0], i[2]]
pairwise = LogisticRegression(fit_intercept=False).fit(np.vstack([diff, -diff]), np.r_[np.ones(len(diff)), np.zeros(len(diff))])
print('linear, RankNet', score(Xte @ pairwise.coef_.ravel())) # linear, RankNet 0.646
gbm = GradientBoostingRegressor(n_estimators=100, max_depth=3, random_state=0).fit(Xtr, Ytr)
print('trees, pointwise', score(gbm.predict(Xte)), gbm.feature_importances_.round(2))
# trees, pointwise 0.766 [0.25 0.13 0.06 0.06 0. 0.51]
F, Ft, imp = np.zeros((300, N)), np.zeros(len(Xte)), np.zeros(d) # LambdaMART: деревья обучаются на лямбдах, листья делают шаг Ньютона
for m in range(100):
lam, hes, _ = lambdas(Y[tr], F)
tree = DecisionTreeRegressor(max_depth=3, random_state=0).fit(Xtr, lam.ravel())
leaf = tree.apply(Xtr)
imp += tree.tree_.compute_feature_importances(normalize=False)
gamma = np.bincount(leaf, lam.ravel(), leaf.max() + 1) / np.maximum(np.bincount(leaf, hes.ravel(), leaf.max() + 1), 1e-12)
F += 0.1 * gamma[leaf].reshape(300, N)
Ft += 0.1 * gamma[tree.apply(Xte)]
print('trees, LambdaMART', score(Ft), (imp / imp.sum()).round(2))
# trees, LambdaMART 0.814 [0.49 0.41 0.05 0.04 0. 0. ]
# клики при смещении из-за позиции: релевантность 0.3, вероятность просмотра 1.0 на позиции 1 и 0.4 на позиции 3
for p in (1.0, 0.4):
clicks = rng.random(100_000) < p * 0.3
print(p, clicks.mean().round(3), (clicks / p).mean().round(3)) # 1.0 0.299 0.299 | 0.4 0.119 0.297
Дальше#
Обучение ранжированию предполагало, что у каждой пары (запрос, документ) есть признаки и что релевантность — свойство пары, которое может оценить асессор. Рекомендация — это ранжирование, в котором запрос — человек: лента пользователя, следующий фильм, товары для корзины. Часто ни у одной из сторон нет описания, достойного этого слова, есть лишь запись о том, кто что выбрал. Следующая заметка, последняя в курсе, учится только по этой записи: матрица «пользователь — объект», соседи и низкоранговые факторы, скрытые в ней, и то, как оценивать рекомендательную систему, не обманывая себя.
Литература#
- C. Burges, T. Shaked, E. Renshaw, A. Lazier, M. Deeds, N. Hamilton, G. Hullender. Learning to Rank using Gradient Descent. ICML, 2005. RankNet: вероятностная попарная функция потерь (§3, формулы 1–3); NDCG с выигрышем (§5, формула 16).
- C. J. C. Burges, R. Ragno, Q. V. Le. Learning to Rank with Nonsmooth Cost Functions. NeurIPS, 2006. LambdaRank; выбор функции сил (§6.2).
- C. J. C. Burges. From RankNet to LambdaRank to LambdaMART: An Overview. Microsoft Research Technical Report MSR-TR-2010-82, 2010. RankNet и его факторизация (§2, §2.1), метрики информационного поиска (§3), LambdaRank (§4.1–4.3, формула 6), LambdaMART и его шаг Ньютона (§7).
- Q. Wu, C. J. C. Burges, K. M. Svore, J. Gao. Adapting Boosting for Information Retrieval Measures. Information Retrieval, 13(3), 2010. LambdaMART.
- Z. Cao, T. Qin, T.-Y. Liu, M.-F. Tsai, H. Li. Learning to Rank: From Pairwise Approach to Listwise Approach. ICML, 2007; расширенная версия — MSR-TR-2007-40. ListNet: вероятность перестановки (§4.1), вероятность оказаться первым (§4.2), функция потерь (§5).
- T. Joachims. Optimizing Search Engines using Clickthrough Data. KDD, 2002. RankSVM.
- K. Järvelin, J. Kekäläinen. Cumulated Gain-Based Evaluation of IR Techniques. ACM Transactions on Information Systems, 20(4), 2002. Дисконтированный совокупный выигрыш (§2.2) и его нормированная версия (§2.3).
- C. D. Manning, P. Raghavan, H. Schütze. Introduction to Information Retrieval. Cambridge University Press, 2008. §8.4 (оценка ранжированных результатов: точность на , MAP, NDCG).
- T. Joachims, L. Granka, B. Pan, H. Hembrooke, G. Gay. Accurately Interpreting Clickthrough Data as Implicit Feedback. SIGIR, 2005. Смещение доверия (§5.2.1), клики как относительные предпочтения (§5.3).
- T. Joachims, A. Swaminathan, T. Schnabel. Unbiased Learning-to-Rank with Biased Feedback. WSDM, 2017. Модель вероятностей просмотра, зависящих от позиции, и оценка IPS (§5.1, формула 5), оценка вероятностей просмотра с помощью обменов (§5.3).
- E. Sokolov et al. Машинное обучение (HSE), конспекты лекций, лекция 24: обучение ранжированию, 2019.