Итак, про плюсы и минусы подхода:
Начнём с минусов.
➖ Вся кандидато-генерация, в том числе способ измерения её качества, начинают существенно зависеть от текущего метода ранжирования. Это увеличивает сложность, нужно это учитывать при сравнении. И когда ранкер меняется, ранние стадии нужно переобучать.
➖ Чаще всего системы изначально строятся без следования этому принципу. И перевести систему в состояние следования из другого состояния может быть очень сложно. В частности, если у системы довольно плохое ранжирование (но благодаря разным хакам результат рекомендаций приемлемый), то следование этому принципу не сделает систему лучше, а наоборот, может в моменте сильно ухудшить рекомендации.
➖ Принцип предполагает, что ранкер должен хорошо работать на всей базе. В противном случае, если есть плохие документы, которые ранкер ошибочно порекомендовал бы, то кандидато-генерация, пытаясь угодить ранкеру, рано или поздно тоже их найдёт. Это несколько усложняет обучение ранкера по сравнению со случаем, когда он работает только на множестве уже достаточно неплохих кандидатов.
➖ Кандидато-генерация не пытается улучшить end-to-end метрики сервиса. Можно её улучшить согласно этому принципу, но получить красный эксперимент. (Впрочем, это будет как раз означать, что в ранжировании есть проблема, например неправильный target.) Это усложняет работу: улучшаешь-улучшаешь, а выкатить потом не можешь.
➖ Ограниченная поддержка бизнес-правил. Этот принцип говорит, что все такие правила (кроме жестких) надо применять на финальной стадии, а ранние будут сами приспосабливаться к ним. И это не только про костыли, но и про полезные аспекты рекомендаций вроде exploration, diversity, etc. (Придётся выдавать разнообразных кандидатов, потому что ранжирование их выбирает.)
А теперь к плюсам.
➕ Принцип основан на декомпозиции. У ранних стадий появляется более понятная и измеримая цель, и это сильно упрощает систему. Вся сложность с выбором таргетов и лоссов для рекомендаций концентрируется в ранжировании (где от этого всё равно не уйти), здесь же решается чисто утилитарная задача эффективного нахождения топа. Ранние стадии — просто инструмент для ускорения ранжирования.
➕ В этом принципе нет фундаментальных ограничений. Если представить себе идеальную систему рекомендаций, то ничего не мешает ей быть устроенной именно так. (Чего нельзя сказать про остальные подходы — не обязаны идеальные рекомендации угадывать то, с чем пользователь и сам потом взаимодействовал!) И с улучшением ранжирования такие упрощенные метрики кандидато-генерации становятся всё ближе к end-to-end метрикам. Так же, как в известном в определенном кругу итеративном подходе "улучшаем метрики — улучшаем продукт по этим метрикам".
➕ Разные стадии ранжирования согласованы друг с другом, они не пытаются оптимизировать разные вещи. В системах же, где это не так, если взять и, скажем, увеличить общее число кандидатов вдвое, то качество всей системы может не улучшиться, а, наоборот, деградировать. Например, если ранние стадии оптимизировали некую релевантность, то дополнительные кандидаты будут менее релевантными, и общая релевантность снизится (хотя кликабельность возрастёт).
➕ Как следствие пункта про декомпозицию: ранние стадии намного проще измерять (а значит, и оптимизировать). В упрощённом случае, когда финальное ранжирование определяется только какой-то моделью (без других правил), можно запустить её на двух методах кандидато-генерации и сравнить средние предсказания. А обучение в этом упрощенном случае сводится, по сути, к дистилляции модели ранжирования. (Хотя тут есть нюансы. Например, хорошо бы логировать некоторых кандидатов, которые не попали в топ ранжирования.)
➕ Более того, для обучения и измерения ранних стадий нам теперь не нужны пользователи, и поэтому необязательно выкатывать новый метод на них. Можно, например, использовать scraping, т.е. обстреливать сервис ранжирования новыми кандидатами.
Главная задача ранних стадий состоит в том, чтобы найти наилучшие документы с точки зрения финального ранжирования.
Начнём с минусов.
А теперь к плюсам.
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
Wazowski Recommends
Как известно, в рекомендательных системах есть несколько стадий построения рекомендаций: сначала происходит генерация кандидатов, а затем — одна или несколько стадий ранжирования. В статьях уделяют не очень много внимания ранним стадиям. Но на практике они…
❤12🔥5
Из забавного:
Google Discover порекомендовал мне прочитать мою же статью, опубликованную в Towards Data Science 😁
Google Discover порекомендовал мне прочитать мою же статью, опубликованную в Towards Data Science 😁
❤15🔥11😁10🍓4
А теперь обсудим, как именно на практике можно измерять качество кандидато-генерации (или ранних стадий ранжирования), согласно тому самому принципу.
Сначала разберём упрощенный, но довольно важный случай: когда ранжирование производится просто по скорам одной финальной модели. Как я уже упоминал в предыдущем посте, мы просто можем сравнить средние скоры этой модели на двух наборах кандидатов. Если один метод находит кандидатов, которым финальная модель выдаёт бОльшие предсказания, чем у другого метода, то первый метод лучше.
Брать ли средние предсказания по всей выдаче, или только по топовым позициям, или с каким-то затуханием по позициям (получается что-то вроде IDCG — знаменателя в NDCG) — кажется, не очень принципиально. Можно выбрать любое по вкусу.
Есть технический нюанс. Если измерять такую метрику в офлайне, то надо уметь запускать ранжирование (или весь рекомендательный стек) на кастомных кандидатах. Это можно сделать либо через симуляцию (offline replay — т.е. пытаться ретроспективно воспроизвести всю информацию про все сущности) на исторических запросах, либо через scraping — "обстрелять" сервис рекомендаций новыми запросами, чтобы он при этом использовал интересующие методы кандидато-генерации. В обоих случаях получаются результаты (предсказания финальной модели) для разных методов генерации для одних и тех же запросов. Это хорошо для чувствительности метрики.
Если же измерять эту метрику в онлайне, на продакшен-сервисе, то можно всё посчитать просто по залогированным предсказаниям модели. Это сильно проще, но не так гибко, и сравнение будет на разных запросах. Чувствительность метрики снижается (вдруг одному из методов просто достались более сложные запросы).
А теперь перейдём к общему случаю: финальное ранжирование — это не только предсказания какой-то модели, но и много другой логики, переранжирования, бизнес-правил, рандомизации и т.д. Если задуматься, как вообще сравнить разные наборы кандидатов в такой нестрогой формулировке (что такой хорошо и что такое плохо) — совсем не очевидно.
Но когда-то я придумал способ для этого, который получился очень простым и полезным. И до сих пор нигде не видел его упоминания.
Способ такой. Добавляем в список источников кандидатов специальный источник, который выдаёт случайных кандидатов (скажем, равномерно). Назначаем этому источнику небольшую фиксированную квоту (скажем, 50 кандидатов). И смотрим, какая доля порекомендованных документов в итоге из этого источника. Если наша кандидато-генерация достаточно хорошая, то случайные кандидаты крайне редко будут побеждать у неё, т.е. попадать в топ. Если же плохая — то часто.
Конечно, тут мы предполагаем, что добавление случайных кандидатов не сильно ухудшает систему: большинство из них не порекомендуется, а те, которые порекомендуются, не сильно ухудшат жизнь пользователей, да ещё и добавят exploration как пользователям, так и модели ранжирования (она дообучится на этих примерах). Если это не так, то сначала стоит "починить ранжирование". 😉
Самое прикольное в этом методе — что он может служить не только метрикой кандидато-генерации, но и мониторингом здоровья всей системы, в том числе и финального ранжирования. Он проверяет, насколько кандидато-генерация согласована с ранжированием (оптимизирована под ранжирование). Если само ранжирование по каким-то причинам деградирует, то и кандидаты становятся не такими уж хорошими для него. Мы это видели на практике, когда одна из компонент поломалась, доля рандомных кандидатов в ответе увеличилась.
Кстати, случайность этого специального источника можно настраивать. Если использовать не равномерную, а пропорциональную популярности документа, то это будет более сильный "adversarial" игрок (что тоже может увеличить чувствительность). Зато при равномерном сэмплировании можно дать аналитическую оценку того, в какой доле запросов наша кандидато-генерация была идеальной (т.е. результат бы не поменялся, даже если бы мы добавили в кандидаты всю базу).
Сначала разберём упрощенный, но довольно важный случай: когда ранжирование производится просто по скорам одной финальной модели. Как я уже упоминал в предыдущем посте, мы просто можем сравнить средние скоры этой модели на двух наборах кандидатов. Если один метод находит кандидатов, которым финальная модель выдаёт бОльшие предсказания, чем у другого метода, то первый метод лучше.
Брать ли средние предсказания по всей выдаче, или только по топовым позициям, или с каким-то затуханием по позициям (получается что-то вроде IDCG — знаменателя в NDCG) — кажется, не очень принципиально. Можно выбрать любое по вкусу.
Есть технический нюанс. Если измерять такую метрику в офлайне, то надо уметь запускать ранжирование (или весь рекомендательный стек) на кастомных кандидатах. Это можно сделать либо через симуляцию (offline replay — т.е. пытаться ретроспективно воспроизвести всю информацию про все сущности) на исторических запросах, либо через scraping — "обстрелять" сервис рекомендаций новыми запросами, чтобы он при этом использовал интересующие методы кандидато-генерации. В обоих случаях получаются результаты (предсказания финальной модели) для разных методов генерации для одних и тех же запросов. Это хорошо для чувствительности метрики.
Если же измерять эту метрику в онлайне, на продакшен-сервисе, то можно всё посчитать просто по залогированным предсказаниям модели. Это сильно проще, но не так гибко, и сравнение будет на разных запросах. Чувствительность метрики снижается (вдруг одному из методов просто достались более сложные запросы).
А теперь перейдём к общему случаю: финальное ранжирование — это не только предсказания какой-то модели, но и много другой логики, переранжирования, бизнес-правил, рандомизации и т.д. Если задуматься, как вообще сравнить разные наборы кандидатов в такой нестрогой формулировке (что такой хорошо и что такое плохо) — совсем не очевидно.
Но когда-то я придумал способ для этого, который получился очень простым и полезным. И до сих пор нигде не видел его упоминания.
Способ такой. Добавляем в список источников кандидатов специальный источник, который выдаёт случайных кандидатов (скажем, равномерно). Назначаем этому источнику небольшую фиксированную квоту (скажем, 50 кандидатов). И смотрим, какая доля порекомендованных документов в итоге из этого источника. Если наша кандидато-генерация достаточно хорошая, то случайные кандидаты крайне редко будут побеждать у неё, т.е. попадать в топ. Если же плохая — то часто.
Конечно, тут мы предполагаем, что добавление случайных кандидатов не сильно ухудшает систему: большинство из них не порекомендуется, а те, которые порекомендуются, не сильно ухудшат жизнь пользователей, да ещё и добавят exploration как пользователям, так и модели ранжирования (она дообучится на этих примерах). Если это не так, то сначала стоит "починить ранжирование". 😉
Самое прикольное в этом методе — что он может служить не только метрикой кандидато-генерации, но и мониторингом здоровья всей системы, в том числе и финального ранжирования. Он проверяет, насколько кандидато-генерация согласована с ранжированием (оптимизирована под ранжирование). Если само ранжирование по каким-то причинам деградирует, то и кандидаты становятся не такими уж хорошими для него. Мы это видели на практике, когда одна из компонент поломалась, доля рандомных кандидатов в ответе увеличилась.
Кстати, случайность этого специального источника можно настраивать. Если использовать не равномерную, а пропорциональную популярности документа, то это будет более сильный "adversarial" игрок (что тоже может увеличить чувствительность). Зато при равномерном сэмплировании можно дать аналитическую оценку того, в какой доле запросов наша кандидато-генерация была идеальной (т.е. результат бы не поменялся, даже если бы мы добавили в кандидаты всю базу).
Telegram
Wazowski Recommends
Как известно, в рекомендательных системах есть несколько стадий построения рекомендаций: сначала происходит генерация кандидатов, а затем — одна или несколько стадий ранжирования. В статьях уделяют не очень много внимания ранним стадиям. Но на практике они…
👍9🔥4🤔2❤1
Когда этим летом запускался Threads, большая часть ленты состояла из ВП — взаимного пиара.
Так вот, не могу не порекомендовать 😁
Если вам нравится этот канал, то вам обязательно понравится и канал Кирилла Хрыльченко: https://xn--r1a.website/inforetriever
Один из типов постов там (не единственный!), который лично для меня очень полезен: Кирилл раз в неделю выкладывает дайджест свежих статей с arxiv на тему рекомендаций, ранжирования и прочего information retrieval. Он это уже делает больше года, но только сейчас это стало публичным.
И про negative sampling, например, Кирилл тоже рассказывает в одном из недавних постов, можете сравнить. (И на меня тоже ссылается, куда ж без взаимности 😉)
Так вот, не могу не порекомендовать 😁
Если вам нравится этот канал, то вам обязательно понравится и канал Кирилла Хрыльченко: https://xn--r1a.website/inforetriever
Один из типов постов там (не единственный!), который лично для меня очень полезен: Кирилл раз в неделю выкладывает дайджест свежих статей с arxiv на тему рекомендаций, ранжирования и прочего information retrieval. Он это уже делает больше года, но только сейчас это стало публичным.
И про negative sampling, например, Кирилл тоже рассказывает в одном из недавних постов, можете сравнить. (И на меня тоже ссылается, куда ж без взаимности 😉)
Telegram
Information Retriever
Author: @kkhrylchenko
❤24👍3
Персонализация и popularity bias
Распространённая проблема в рекомендательных системах — недостаток персонализации, когда показываются в основном популярные и не очень релевантные пользователю документы.
В сообществе есть известная проблема popularity bias. Но что это в точности такое? Bias — это системное смещение. А где здесь смещение? И есть ли оно вообще?
Если общими словами, то под popularity bias понимается ситуация "the rich get richer", когда популярные документы рекомендуются системой непропорционально чаще непопулярных. Причины у этого могут быть разные, и в литературе освещаются разные аспекты этого явления. Важно разделять эти причины, потому что это сильно помогает дебажить систему.
В работе над рекомендациями очень полезно выделять два важных шага:
1) Обучение модели предсказания отклика пользователя на рекомендованный объект. В простом случае это просто вероятность клика, в более общем — E(engagement | item, user, context).
2) Собственно, построение рекомендаций с помощью этой модели. Простое ранжирование по предсказаниям — не самый оптимальный, хотя и хороший бейзлайн.
Во многих случаях, говоря о popularity bias, подразумевают неоптимальность шага 2. То есть, даже если более популярный объект вызовет у пользователя с большей вероятностью позитивный отклик, может быть лучше порекомендовать ему менее популярный объект. Причин тут тоже может быть несколько — как пользователецентричные (долгосрочно клик на популярный объект менее ценен для этого пользователя, чем клик на непопулярный), так и с точки зрения всей экосистемы (этому пользователю станет чуть хуже, но зато мы выровняем распределение потребления по всей базе объектов). Это, в целом, разумные мысли, но надо честно себе признаться: мы жертвуем engagement-ом в момент конкретного запроса ради светлого будущего.
Самый простой способ имплементировать эту идею (и, по-моему, другие способы не очень-то далеко ушли от этого) — пенализировать за популярность объекта. Это очень тесно связано с PMI, который мы обсуждали в посте про двух-башенные сети.
В других же случаях popularity bias относят к первому пункту: дисбаланс объектов мешает нам хорошо обучить модель E(engagement | item, user, context). В частности, она может плохо учитывать пользовательские фичи и, по сути, просто выучить E(engagement | item), тесно связанную с популярностью (кстати, в этом посте я тоже иногда под популярностью имею в виду не P(item), а E(engagement | item)). Вот это уже очень ощутимая проблема. Хотя я не очень понимаю, почему её называют баисом.
Тут советы зависят от конкретной модели. Вот несколько:
- Убедитесь, что у модели есть информативные персональные фичи.
- Введите отдельный член внутри модели, отвечающий за популярность, чтобы оставшаяся часть модели могла сфокусироваться на специфичности.
- Если модель выучивает эмбеддинги объектов, проверьте, хорошо ли они выучились. Например, посмотрев на самые похожие объекты на данный.
- Если используется negative sampling, то учитывайте в нём популярность. Только не забудьте при применении обратно умножить на неё, чтобы получить E(engagement | ...), как обсуждали в том же посте.
- Ну и просто проверьте, что модель нормально выучилась. Да, это не так-то просто. Это часть довольно сложной, но критически важной темы ML Debugging.
Кстати про "непропорционально чаще". Никто ведь не обещал, что при простом ранжировании вероятность быть порекомендованным будет пропорциональна популярности или CTR документа. Это совсем не так. Может быть, поэтому это и называют bias-ом?
На моей же практике было очень много случаев, когда команды
а) не задумываются, что именно они называют popularity bias-ом и в чём его причины,
б) имеют проблемы с недостатком персонализации просто из-за плохо обученной модели E(engagement | ...).
Очень важно понимать — это мир так устроен, что у популярных, но менее релевантных объектов действительно в среднем лучше отклики, или просто мы модель плохо обучили.
Намного чаще popularity bias — это просто популярный миф, скрывающий баги системы.
Не стоит недооценивать важность хорошей engagement-модели.
Распространённая проблема в рекомендательных системах — недостаток персонализации, когда показываются в основном популярные и не очень релевантные пользователю документы.
В сообществе есть известная проблема popularity bias. Но что это в точности такое? Bias — это системное смещение. А где здесь смещение? И есть ли оно вообще?
Если общими словами, то под popularity bias понимается ситуация "the rich get richer", когда популярные документы рекомендуются системой непропорционально чаще непопулярных. Причины у этого могут быть разные, и в литературе освещаются разные аспекты этого явления. Важно разделять эти причины, потому что это сильно помогает дебажить систему.
В работе над рекомендациями очень полезно выделять два важных шага:
1) Обучение модели предсказания отклика пользователя на рекомендованный объект. В простом случае это просто вероятность клика, в более общем — E(engagement | item, user, context).
2) Собственно, построение рекомендаций с помощью этой модели. Простое ранжирование по предсказаниям — не самый оптимальный, хотя и хороший бейзлайн.
Во многих случаях, говоря о popularity bias, подразумевают неоптимальность шага 2. То есть, даже если более популярный объект вызовет у пользователя с большей вероятностью позитивный отклик, может быть лучше порекомендовать ему менее популярный объект. Причин тут тоже может быть несколько — как пользователецентричные (долгосрочно клик на популярный объект менее ценен для этого пользователя, чем клик на непопулярный), так и с точки зрения всей экосистемы (этому пользователю станет чуть хуже, но зато мы выровняем распределение потребления по всей базе объектов). Это, в целом, разумные мысли, но надо честно себе признаться: мы жертвуем engagement-ом в момент конкретного запроса ради светлого будущего.
Самый простой способ имплементировать эту идею (и, по-моему, другие способы не очень-то далеко ушли от этого) — пенализировать за популярность объекта. Это очень тесно связано с PMI, который мы обсуждали в посте про двух-башенные сети.
В других же случаях popularity bias относят к первому пункту: дисбаланс объектов мешает нам хорошо обучить модель E(engagement | item, user, context). В частности, она может плохо учитывать пользовательские фичи и, по сути, просто выучить E(engagement | item), тесно связанную с популярностью (кстати, в этом посте я тоже иногда под популярностью имею в виду не P(item), а E(engagement | item)). Вот это уже очень ощутимая проблема. Хотя я не очень понимаю, почему её называют баисом.
Тут советы зависят от конкретной модели. Вот несколько:
- Убедитесь, что у модели есть информативные персональные фичи.
- Введите отдельный член внутри модели, отвечающий за популярность, чтобы оставшаяся часть модели могла сфокусироваться на специфичности.
- Если модель выучивает эмбеддинги объектов, проверьте, хорошо ли они выучились. Например, посмотрев на самые похожие объекты на данный.
- Если используется negative sampling, то учитывайте в нём популярность. Только не забудьте при применении обратно умножить на неё, чтобы получить E(engagement | ...), как обсуждали в том же посте.
- Ну и просто проверьте, что модель нормально выучилась. Да, это не так-то просто. Это часть довольно сложной, но критически важной темы ML Debugging.
Кстати про "непропорционально чаще". Никто ведь не обещал, что при простом ранжировании вероятность быть порекомендованным будет пропорциональна популярности или CTR документа. Это совсем не так. Может быть, поэтому это и называют bias-ом?
На моей же практике было очень много случаев, когда команды
а) не задумываются, что именно они называют popularity bias-ом и в чём его причины,
б) имеют проблемы с недостатком персонализации просто из-за плохо обученной модели E(engagement | ...).
Очень важно понимать — это мир так устроен, что у популярных, но менее релевантных объектов действительно в среднем лучше отклики, или просто мы модель плохо обучили.
Намного чаще popularity bias — это просто популярный миф, скрывающий баги системы.
Не стоит недооценивать важность хорошей engagement-модели.
👍25🔥6❤1
Хотя и не все соглашаются с таким подходом, но я считаю, что в рекомендациях надо исходить из того, что главная цель любой рекомендательной системы — оптимизация суммарного value. Это value может измеряться разными метриками. Главные четыре типа, которые я видел: DAU, time spent, транзакции (GMV) и подпиcки. Кроме того, иногда это value не только обычных потребляющих пользователей, но и других сторон — провайдеров контента.
Как я писал в предыдущем посте, основная часть рекомендательной системы — это engagement-модель E(engagement | item, user, context), которая предсказывает это самое value (или какое-то его осмысленное упрощение) для одного порекомендованного объекта. И можно строить рекомендации, просто сортируя по предсказаниям этой модели, не обращая внимания ни на что другое. Назовём этот бейзлайн циничным ранжированием.
Циничное ранжирование не является оптимальным для заявленной цели оптимизации суммарного value. Вы часто можете услышать про разные "beyond accuracy" аспекты рекомендаций вроде exploration, diversity, novelty, serendipity. Давайте переведём эти понятия с языка ощущений и "продуктового видения" на язык оптимизации суммарного value.
В этом посте начнём с exploration. Все слышали о дилемме exploration vs. exploitation. Это о том, что зачастую выгодно пожертвовать value (наградой) в текущем моменте ради того, чтобы узнать что-то новое и в будущем действовать более оптимально.
Довольно важно разделять user exploration и system exploration, потому что работать с ними надо по-разному. В первом случае мы жертвуем value пользователя в моменте ради него же самого, чтобы узнать о нём больше. Проверить, насколько хорошо нам это удаётся, можно с помощью обычных A/B-тестов. Только иногда нужно увеличивать их длительность, чтобы уловить более долгосрочные эффекты.
В system exploration же мы хотим узнать больше о всей системе. Одним важным частным случаем является item exploration — узнать больше про недоисследованные объекты (особенно недавно появившиеся в системе). Также можно исследовать разные области в пространстве признаков любой из использующихся моделей (model exploration). Про это уже был пост от Саши.
В отличие от user exploration, в system exploration всё сильно сложнее с замерами. Мы приносим пользователя в жертву ради остальных, а остальные могут оказаться в другой выборке A/B-теста.
При этом какие-то простые и полезные метрики item exploration всё же можно использовать: доля объектов, которые получают не меньше X показов/кликов за первые Y часов, — как метрика на дашборде, и доли кликов и показов на такие "недоисследованные" (новые с малым числом показов) объекты — как метрики в A/B. Эти метрики позволяют сравнить уровень exploration, но не отвечают на вопрос, какой уровень был бы оптимальным.
У YouTube есть попытки более принципиального подхода, но нельзя назвать эту область решённой.
Как я писал в предыдущем посте, основная часть рекомендательной системы — это engagement-модель E(engagement | item, user, context), которая предсказывает это самое value (или какое-то его осмысленное упрощение) для одного порекомендованного объекта. И можно строить рекомендации, просто сортируя по предсказаниям этой модели, не обращая внимания ни на что другое. Назовём этот бейзлайн циничным ранжированием.
Циничное ранжирование не является оптимальным для заявленной цели оптимизации суммарного value. Вы часто можете услышать про разные "beyond accuracy" аспекты рекомендаций вроде exploration, diversity, novelty, serendipity. Давайте переведём эти понятия с языка ощущений и "продуктового видения" на язык оптимизации суммарного value.
В этом посте начнём с exploration. Все слышали о дилемме exploration vs. exploitation. Это о том, что зачастую выгодно пожертвовать value (наградой) в текущем моменте ради того, чтобы узнать что-то новое и в будущем действовать более оптимально.
Довольно важно разделять user exploration и system exploration, потому что работать с ними надо по-разному. В первом случае мы жертвуем value пользователя в моменте ради него же самого, чтобы узнать о нём больше. Проверить, насколько хорошо нам это удаётся, можно с помощью обычных A/B-тестов. Только иногда нужно увеличивать их длительность, чтобы уловить более долгосрочные эффекты.
В system exploration же мы хотим узнать больше о всей системе. Одним важным частным случаем является item exploration — узнать больше про недоисследованные объекты (особенно недавно появившиеся в системе). Также можно исследовать разные области в пространстве признаков любой из использующихся моделей (model exploration). Про это уже был пост от Саши.
В отличие от user exploration, в system exploration всё сильно сложнее с замерами. Мы приносим пользователя в жертву ради остальных, а остальные могут оказаться в другой выборке A/B-теста.
При этом какие-то простые и полезные метрики item exploration всё же можно использовать: доля объектов, которые получают не меньше X показов/кликов за первые Y часов, — как метрика на дашборде, и доли кликов и показов на такие "недоисследованные" (новые с малым числом показов) объекты — как метрики в A/B. Эти метрики позволяют сравнить уровень exploration, но не отвечают на вопрос, какой уровень был бы оптимальным.
У YouTube есть попытки более принципиального подхода, но нельзя назвать эту область решённой.
Telegram
Wazowski Recommends
Персонализация и popularity bias
Распространённая проблема в рекомендательных системах — недостаток персонализации, когда показываются в основном популярные и не очень релевантные пользователю документы.
В сообществе есть известная проблема popularity bias.…
Распространённая проблема в рекомендательных системах — недостаток персонализации, когда показываются в основном популярные и не очень релевантные пользователю документы.
В сообществе есть известная проблема popularity bias.…
👍16🔥3❤2👎1
Telegram
Wazowski Recommends
Хотя и не все соглашаются с таким подходом, но я считаю, что в рекомендациях надо исходить из того, что главная цель любой рекомендательной системы — оптимизация суммарного value. Это value может измеряться разными метриками. Главные четыре типа, которые…
Diversity
В некоторых продуктах сразу понятно, что без разнообразия рекомендации будут ужасными (понравится ли вам слушать в рекомендациях музыки только лишь одного исполнителя?), в некоторых — не так очевидно. Говоря о разнообразии, имеют в виду несколько разных вещей.
Есть intra-list diversity — разнообразие в рамках одной страницы (одного запроса, request-level). Циничное ранжирование не разнообразно, потому что каждый объект оценивается независимо и все они имеют один и тот же контекст. На самом же деле пользователи видят порекомендованные объекты в контексте друг друга. Например, если два объекта очень похожи друг на друга, то, скорее всего, пользователь кликнет лишь на один из них.
Поэтому стоит динамически обогащать этот контекст объектами из начала списка во время listwise-ранжирования — как я описывал. Хотя и это еще не идеально, т.к. на каждом шаге жадно оптимизируется value одного объекта, а не всего запроса.
Есть разнообразие в рамках истории пользователя — user-level. В этом случае оно очень сильно пересекается с user exploration. Но не полностью — можно себе попытаться представить ситуации с достаточным diversity, но недостаточным user exploration, и наоборот, а также и другие комбинации с novelty и serendipity. Суть у этих эффектов одна — краткосрочность оптимизации. Если мы научимся оптимизировать долгосрочное value пользователя, то мы сразу решим все эти вопросы user-level diversity, user exploration, novelty, serendipity etc.
Кстати, в сервисах, где потребляется один объект за раз (TikTok или радио), граница между этими двумя видами разнообразия размыта. Технически, на один запрос всё равно обычно выдаётся список объектов (скажем, пять), который показывается пользователю последовательно. Меняя этот параметр — размер списка — мы переходим между request-level (intra-list) и user-level diversity.
Есть также и system-level diversity. Как несложно догадаться, оно нацелено на экосистемную оптимизацию — т.е. суммарного value всех пользователей. Мы его затрагивали, говоря о popularity bias.
—
А всё ли разнообразие покрывается этими аспектами? А если для максимизации value (даже долгосрочного) не нужно разнообразие, или его нужно мало? А интуитивно кажется, что разнообразия не хватает?
Это достаточно частая ситуация — когда любое повышение разнообразия только ухудшает метрики.
Как я и говорил, я приверженец подхода:
Если интуиция противоречит наблюдениям по метрикам, то либо надо подправить метрики, либо — интуицию.
Метрики:
Возможно, мы неправильно выбрали определение value. Например, оптимизируем time spent, а с интуицией (счастьем пользователей?) больше согласуется оптимизация DAU/retention. Или измеряем их на недостаточном периоде времени. Или (что тоже очень частый случай!) просто пока не нашли хороший способ, как оптимизировать value согласованно с интуицией.
Интуиция:
А может, и не надо повышать разнообразие и выводить пользователей из пузыря? Может, им на самом деле это не нужно, или, если и нужно, то, скажем, покупать они всё равно от этого больше не станут?
Философский вопрос. Можете высказать своё мнение в комментариях :)
—
Если обобщить этот и предыдущий посты, то эволюция рекомендательных таргетов может быть такая:
1. E(value | item, user, context) — циничное ранжирование, оптимизация value каждого конкретного айтема.
2. E(value | request) — listwise-ранжирование, оптимизация суммарного value каждого запроса.
3. E(value | user) — оптимизация долгосрочного value каждого пользователя.
4. E(value) — оптимизация суммарного value всей системы.
При этом на каждом уровне можно выбирать своё определение value. (И именно так каждый следующий уровень может использовать предыдущий.)
В некоторых продуктах сразу понятно, что без разнообразия рекомендации будут ужасными (понравится ли вам слушать в рекомендациях музыки только лишь одного исполнителя?), в некоторых — не так очевидно. Говоря о разнообразии, имеют в виду несколько разных вещей.
Есть intra-list diversity — разнообразие в рамках одной страницы (одного запроса, request-level). Циничное ранжирование не разнообразно, потому что каждый объект оценивается независимо и все они имеют один и тот же контекст. На самом же деле пользователи видят порекомендованные объекты в контексте друг друга. Например, если два объекта очень похожи друг на друга, то, скорее всего, пользователь кликнет лишь на один из них.
Поэтому стоит динамически обогащать этот контекст объектами из начала списка во время listwise-ранжирования — как я описывал. Хотя и это еще не идеально, т.к. на каждом шаге жадно оптимизируется value одного объекта, а не всего запроса.
Есть разнообразие в рамках истории пользователя — user-level. В этом случае оно очень сильно пересекается с user exploration. Но не полностью — можно себе попытаться представить ситуации с достаточным diversity, но недостаточным user exploration, и наоборот, а также и другие комбинации с novelty и serendipity. Суть у этих эффектов одна — краткосрочность оптимизации. Если мы научимся оптимизировать долгосрочное value пользователя, то мы сразу решим все эти вопросы user-level diversity, user exploration, novelty, serendipity etc.
Кстати, в сервисах, где потребляется один объект за раз (TikTok или радио), граница между этими двумя видами разнообразия размыта. Технически, на один запрос всё равно обычно выдаётся список объектов (скажем, пять), который показывается пользователю последовательно. Меняя этот параметр — размер списка — мы переходим между request-level (intra-list) и user-level diversity.
Есть также и system-level diversity. Как несложно догадаться, оно нацелено на экосистемную оптимизацию — т.е. суммарного value всех пользователей. Мы его затрагивали, говоря о popularity bias.
—
А всё ли разнообразие покрывается этими аспектами? А если для максимизации value (даже долгосрочного) не нужно разнообразие, или его нужно мало? А интуитивно кажется, что разнообразия не хватает?
Это достаточно частая ситуация — когда любое повышение разнообразия только ухудшает метрики.
Как я и говорил, я приверженец подхода:
главная цель любой рекомендательной системы — оптимизация суммарного value
Если интуиция противоречит наблюдениям по метрикам, то либо надо подправить метрики, либо — интуицию.
Метрики:
Возможно, мы неправильно выбрали определение value. Например, оптимизируем time spent, а с интуицией (счастьем пользователей?) больше согласуется оптимизация DAU/retention. Или измеряем их на недостаточном периоде времени. Или (что тоже очень частый случай!) просто пока не нашли хороший способ, как оптимизировать value согласованно с интуицией.
Интуиция:
А может, и не надо повышать разнообразие и выводить пользователей из пузыря? Может, им на самом деле это не нужно, или, если и нужно, то, скажем, покупать они всё равно от этого больше не станут?
Философский вопрос. Можете высказать своё мнение в комментариях :)
—
Если обобщить этот и предыдущий посты, то эволюция рекомендательных таргетов может быть такая:
1. E(value | item, user, context) — циничное ранжирование, оптимизация value каждого конкретного айтема.
2. E(value | request) — listwise-ранжирование, оптимизация суммарного value каждого запроса.
3. E(value | user) — оптимизация долгосрочного value каждого пользователя.
4. E(value) — оптимизация суммарного value всей системы.
При этом на каждом уровне можно выбирать своё определение value. (И именно так каждый следующий уровень может использовать предыдущий.)
👍12🔥9👎1
Из личных новостей: сегодня я начал работать в 𝕏 (как обычно приписывают — formerly known as Twitter). Теперь буду там улучшать качество рекомендаций.
Почему именно X? За последние полгода мне не раз приходилось отвечать на этот вопрос и для себя, и для других.
В мире не так много крупномасштабных рекомендательных систем. И практически все они принадлежат большим корпорациям. Я несколько устал от корпораций (особенно после Microsoft), мне очень хотелось в компанию поменьше. И вот как раз таким исключением и является X. И там уже даже работает пара моих бывших коллег: Сева @yalinter и недавно вышедший туда Саша @knowledge_accumulator. Поэтому я приблизительно знал, на что иду. И надеюсь, что со временем еще больше наших бывших коллег присоединятся.
Рекомендации — это core функциональность для X, и я вижу в них большой потенциал.
К Маску можно по-разному относиться. Наверняка с ним будет непросто работать. Но результаты его управления мне, в целом, нравятся.
А Саша недавно писал более подробно про свои причины, и с ними я тоже согласен.
Надеюсь, у нас получится.
Почему именно X? За последние полгода мне не раз приходилось отвечать на этот вопрос и для себя, и для других.
В мире не так много крупномасштабных рекомендательных систем. И практически все они принадлежат большим корпорациям. Я несколько устал от корпораций (особенно после Microsoft), мне очень хотелось в компанию поменьше. И вот как раз таким исключением и является X. И там уже даже работает пара моих бывших коллег: Сева @yalinter и недавно вышедший туда Саша @knowledge_accumulator. Поэтому я приблизительно знал, на что иду. И надеюсь, что со временем еще больше наших бывших коллег присоединятся.
Рекомендации — это core функциональность для X, и я вижу в них большой потенциал.
К Маску можно по-разному относиться. Наверняка с ним будет непросто работать. Но результаты его управления мне, в целом, нравятся.
А Саша недавно писал более подробно про свои причины, и с ними я тоже согласен.
Надеюсь, у нас получится.
🔥82👍21❤11❤🔥5 4🎉3😁1💩1
Больше года назад я начал второй сезон в этом канале. Но в последние полгода случилось много всего: переезд, новая работа (а работать здесь и правда надо и хочется побольше, это вам не Майкрософт 😁). Времени регулярно писать не было, поэтому пришлось снова уйти на каникулы.
Сейчас жизнь хоть как-то начала стабилизироваться, поэтому постараюсь выйти на третий сезон и снова начать писать регулярно. И, скорее всего, от формата «фундаментальных» постов я немножко отойду.
По традиции, тишину прерываюдружеским пиаром рекомендациями. Мы с авторами нескольких каналов о рекомендательных системах (и не только) собрали папку, на которую можно подписаться.
Подключайтесь!
Сейчас жизнь хоть как-то начала стабилизироваться, поэтому постараюсь выйти на третий сезон и снова начать писать регулярно. И, скорее всего, от формата «фундаментальных» постов я немножко отойду.
По традиции, тишину прерываю
Подключайтесь!
❤24🔥5 1
Не так давно я узнал, что в нашей индустрии появился новый тренд. Причем там, где, казалось бы, и так всё неплохо работает и улучшить не так-то просто.
Как мы уже не раз обсуждали, для генерации кандидатов лучше всего работают двух-башенные сети и ANN-индексы для быстрого поиска, например HNSW.
Так вот, сначала Meta, а потом LinkedIn (и по слухам — ТикТок тоже) показали, что в современном мире это можно делать лучше.
Двух-башенные сети на первой стадии всё ещё остаются. Но вот складывать в ANN-индекс не нужно. А нужно… Просто использовать GPU!
При небольшой размерности эмбеддингов, да ещё и в квантизованном виде, на одной карточке A100 можно хранить порядка 100 миллионов документов (а этого хватит, конечно же, всем... ну почти) и успевать с ними со всеми посчитать скалярное произведение за несколько десятков миллисекунд. А для хорошего throughput запросные эмбеддинги стоит собирать в батчи (матрицы), чтобы всё это можно было сделать одним матричным перемножением.
Какие у этого преимущества?
1) Полнота поиска выше. Как бы мы ни любили ANN, их полнота на практике выше 95%, но всё-таки не 100%. А тут мы считаем произведение со всеми объектами в базе.
2) Если обычно мы отбираем одну или несколько тысяч кандидатов из ANN, то здесь можно выдавать сразу 100'000. ANN с таким количеством работают уже не очень хорошо. Только вот что делать дальше с этими 100000? Мета предлагает на следующей стадии ранжировать их моделью потяжелее, mixture-of-logits, MoL (всё ещё двух-башенная, но в конце не произведение, а более сложная сеть), тоже на GPU. И уже результат этого выдавать в тяжелое ранжирование, как и раньше.
3) А ещё такой подход позволяет намного быстрее и чаще обновлять эмбеддинги документов. Их же просто нужно обновить в памяти GPU. В ANN-индексе же это сложнее, поэтому обычно так часто не обновляют.
Выглядит перспективно.
Как мы уже не раз обсуждали, для генерации кандидатов лучше всего работают двух-башенные сети и ANN-индексы для быстрого поиска, например HNSW.
Так вот, сначала Meta, а потом LinkedIn (и по слухам — ТикТок тоже) показали, что в современном мире это можно делать лучше.
Двух-башенные сети на первой стадии всё ещё остаются. Но вот складывать в ANN-индекс не нужно. А нужно… Просто использовать GPU!
При небольшой размерности эмбеддингов, да ещё и в квантизованном виде, на одной карточке A100 можно хранить порядка 100 миллионов документов (а этого хватит, конечно же, всем... ну почти) и успевать с ними со всеми посчитать скалярное произведение за несколько десятков миллисекунд. А для хорошего throughput запросные эмбеддинги стоит собирать в батчи (матрицы), чтобы всё это можно было сделать одним матричным перемножением.
Какие у этого преимущества?
1) Полнота поиска выше. Как бы мы ни любили ANN, их полнота на практике выше 95%, но всё-таки не 100%. А тут мы считаем произведение со всеми объектами в базе.
2) Если обычно мы отбираем одну или несколько тысяч кандидатов из ANN, то здесь можно выдавать сразу 100'000. ANN с таким количеством работают уже не очень хорошо. Только вот что делать дальше с этими 100000? Мета предлагает на следующей стадии ранжировать их моделью потяжелее, mixture-of-logits, MoL (всё ещё двух-башенная, но в конце не произведение, а более сложная сеть), тоже на GPU. И уже результат этого выдавать в тяжелое ранжирование, как и раньше.
3) А ещё такой подход позволяет намного быстрее и чаще обновлять эмбеддинги документов. Их же просто нужно обновить в памяти GPU. В ANN-индексе же это сложнее, поэтому обычно так часто не обновляют.
Выглядит перспективно.
arXiv.org
Revisiting Neural Retrieval on Accelerators
Retrieval finds a small number of relevant candidates from a large corpus for information retrieval and recommendation applications. A key component of retrieval is to model (user, item)...
👍49🔥13❤4🗿1
В двух предыдущих компаниях, в которых я работал, очень любили градиентный бустинг. И очень сильно в нём специализировались (возможно, даже слишком сильно).
Но, на удивление, ни там, ни там не было настоящего работающего механизма feature selection.
Уточню, что я называю «настоящим». Все градиентные бустинги предоставляют feature importance — насколько они каждый признак использовали и насколько это помогло оптимизации лосса. Также бывают SHAP values. Но все грамотные ML-инженеры знают, что всё это совсем не настоящая полезность фичей. Их нужно использовать так: если importance нулевая (или очень маленькая), то фича бесполезная, ее можно убрать. Но не в обратную сторону.
В Яндексе был (есть) настоящий feature evaluation — убрать фичи и посмотреть, как меняется качество, да еще и стат-тест запустить. И даже была (есть) более дешевая приближенная модификация.
Но вот именно полноценного feature selection не было. Точнее, при мне даже была попытка его сделать, но вроде как большого успеха (распространения) она не достигла. Может быть, спрос на этот инструмент был недостаточным, а может, сделать его эффективным очень сложно. Задача же нетривиальная — есть N (тысячи) фичей, нужно среди 2^N наборов выбрать оптимальный (с точки зрения качества и какого-то понятия стоимости — например, количества фичей). А протестировать каждый набор может занимать часы.
Год назад я над этим размышлял-размышлял... И подумал, что можно это попробовать сделать сильно более эффективно с помощью Random Forest. Предлагаю вам оценить. (Я уже с бустингами перестал работать и вряд ли буду тестировать.) Сразу оговорюсь, что я никогда ничего про feature selection для random forest не читал и не слышал. Вполне вероятно, что уже давным-давно придумали либо то же самое (тогда почему не используют? не работает?), либо что-то ещё получше. Расскажите в комментах, если знаете.
Итак, идея:
- Предположим, что для отбора фичей можно временно заменить бустинг на random forest. (Это слишком сильное предположение?)
- Обучим random forest на всех фичах с раз в сто большим количеством деревьев. (Его же обучать дешевле, чем бустинг, он очень легко параллелится.)
- Затем запускаем любой стандартный алгоритм отбора фичей. И когда тестируем очередной набор, то не обучаем модель заново, а просто выбираем те деревья, которые не используют выкинутых фичей.
- Обычно нас интересуют наборы, в которых большая часть фичей не выкинуты, поэтому таких деревьев должно быть не слишком мало. И можно оценить, какое качество будет у модели с ровно M такими деревьями.
- ...
- Profit.
Что думаете?
Но, на удивление, ни там, ни там не было настоящего работающего механизма feature selection.
Уточню, что я называю «настоящим». Все градиентные бустинги предоставляют feature importance — насколько они каждый признак использовали и насколько это помогло оптимизации лосса. Также бывают SHAP values. Но все грамотные ML-инженеры знают, что всё это совсем не настоящая полезность фичей. Их нужно использовать так: если importance нулевая (или очень маленькая), то фича бесполезная, ее можно убрать. Но не в обратную сторону.
В Яндексе был (есть) настоящий feature evaluation — убрать фичи и посмотреть, как меняется качество, да еще и стат-тест запустить. И даже была (есть) более дешевая приближенная модификация.
Но вот именно полноценного feature selection не было. Точнее, при мне даже была попытка его сделать, но вроде как большого успеха (распространения) она не достигла. Может быть, спрос на этот инструмент был недостаточным, а может, сделать его эффективным очень сложно. Задача же нетривиальная — есть N (тысячи) фичей, нужно среди 2^N наборов выбрать оптимальный (с точки зрения качества и какого-то понятия стоимости — например, количества фичей). А протестировать каждый набор может занимать часы.
Год назад я над этим размышлял-размышлял... И подумал, что можно это попробовать сделать сильно более эффективно с помощью Random Forest. Предлагаю вам оценить. (Я уже с бустингами перестал работать и вряд ли буду тестировать.) Сразу оговорюсь, что я никогда ничего про feature selection для random forest не читал и не слышал. Вполне вероятно, что уже давным-давно придумали либо то же самое (тогда почему не используют? не работает?), либо что-то ещё получше. Расскажите в комментах, если знаете.
Итак, идея:
- Предположим, что для отбора фичей можно временно заменить бустинг на random forest. (Это слишком сильное предположение?)
- Обучим random forest на всех фичах с раз в сто большим количеством деревьев. (Его же обучать дешевле, чем бустинг, он очень легко параллелится.)
- Затем запускаем любой стандартный алгоритм отбора фичей. И когда тестируем очередной набор, то не обучаем модель заново, а просто выбираем те деревья, которые не используют выкинутых фичей.
- Обычно нас интересуют наборы, в которых большая часть фичей не выкинуты, поэтому таких деревьев должно быть не слишком мало. И можно оценить, какое качество будет у модели с ровно M такими деревьями.
- ...
- Profit.
Что думаете?
👍17🤔11❤3
В моей жизни не так много было периодов, когда мне нравилась моя работа.
Было много периодов, когда мне текущее место работы нравилось намного больше, чем всё остальное вокруг, и никуда переходить не хотелось. Чаще всего — за счёт очень интересного проекта и/или хорошей команды. Но при этом обычно было и что-то, что очень сильно разочаровывало и не давало быть полноценно счастливым.
Однако были и исключения.
И вот с моим последним переходом мне как раз очень повезло — работать в 𝕏 мне очень нравится. Команда маленькая, нет никаких согласований и прочей бюрократии, нет булшита, люди вокруг умные (а некоторые — настолько, что с ними даже поговорить интересно 😁). Главное — можно вот прямо взять и поменять (желательно — в лучшую сторону) рекомендации в одной из самых больших соцсетей в мире. И не то что «можно», а создаётся активное здоровое давление, так как это core часть продукта.
Всё это очень повышает осмысленность работы и способствует большой продуктивности. За первые несколько месяцев я, как ML-инженер, сделал больше, чем за годы до этого (по собственным ощущениям).
Так вот, если вдруг вы хотите так же, — наша команда нанимает. На все уровни. Можно релоцироваться в Лондон или Калифорнию (рекомендую к нам в Лондон😉 ). Ну и другие вакансии тоже можете посмотреть: https://careers.x.com/en (надеюсь, у вас не составит труда обойти блокировку при необходимости).
Было много периодов, когда мне текущее место работы нравилось намного больше, чем всё остальное вокруг, и никуда переходить не хотелось. Чаще всего — за счёт очень интересного проекта и/или хорошей команды. Но при этом обычно было и что-то, что очень сильно разочаровывало и не давало быть полноценно счастливым.
Однако были и исключения.
И вот с моим последним переходом мне как раз очень повезло — работать в 𝕏 мне очень нравится. Команда маленькая, нет никаких согласований и прочей бюрократии, нет булшита, люди вокруг умные (а некоторые — настолько, что с ними даже поговорить интересно 😁). Главное — можно вот прямо взять и поменять (желательно — в лучшую сторону) рекомендации в одной из самых больших соцсетей в мире. И не то что «можно», а создаётся активное здоровое давление, так как это core часть продукта.
Всё это очень повышает осмысленность работы и способствует большой продуктивности. За первые несколько месяцев я, как ML-инженер, сделал больше, чем за годы до этого (по собственным ощущениям).
Так вот, если вдруг вы хотите так же, — наша команда нанимает. На все уровни. Можно релоцироваться в Лондон или Калифорнию (рекомендую к нам в Лондон
Please open Telegram to view this post
VIEW IN TELEGRAM
x.ai
Careers | xAI
We are a team of AI technologists and business leaders on a mission to build AI systems that can help humanity understand the world better.
🔥57 29❤12
В рекомендательных системах, как и в некоторых других областях ML, практически во всех данных присутствует важное поле — таймстемп. Почти всё, с чем мы работаем, — это события, и нам важно, когда они произошли. И на этом можно было бы не заострять внимание. Но есть нюансы.
Почему важны таймстемпы? Во-первых, конечно, самый правильный способ измерять качество моделей в офлайне — разделяя trainset и testset по времени (причём глобально, а не для каждого пользователя отдельно, как любят делать в научных статьях). Потому что в реальной жизни всё будет работать именно так: мы обучаем модель, потом выкатываем её, и потом уже она работает на пользователях. А вообще-то, чаще всего у нас модели постоянно инкрементально дообучаются, поэтому и тестировать хорошо бы в таком же инкрементальном/онлайн режиме, а не с отложенным тестсетом.
Во-вторых, иногда нам нужно в офлайне вычислить какие-то фичи. Конечно, лучше всего этого просто не делать, а всегда использовать фичи, залогированные в онлайне. И вообще, золотое правило — лучше всегда обучаться ровно на тех данных, которые используются при применении модели. Чем меньше от этого правила отходишь, тем меньше неожиданных проблем приходится разгребать.
Но всё-таки иногда приходится фичи вычислять заново («вычисление» может быть и очень простым — просто подджойнить таблицу), например, когда это новая фича и нам надо её сначала протестировать перед тем, как выкатывать на логирование. В этом случае надо строго следить за тем, чтобы при вычислении мы использовали данные, максимально похожие на те, которые были бы в онлайне. В том числе — не заглядывать в будущее. Иногда для надежности делают некоторое отставание в несколько минут или часов.
Этот механизм — приджойнить или вычислить фичи ретроспективно, с заданным отставанием по времени — достаточно общий, и его имеет смысл реализовать один раз в обобщенном виде.
Есть ещё и специальный вид вычисления — обучение и применение подмодели. (Например, фичи от трансформеров мы хотим использовать в верхней ранжирующей модели, а трансформеры постоянно дообучаются.) Вся логика тут сохраняется, при применении нужно брать последние известные данные (историю пользователю) и последнюю обученную подмодель (трансформеры). Это, кстати, показывает, что это примерно одно и то же — вычисление ретроспективных фичей и тестирование моделей в онлайн-режиме. Для тестирования сначала надо просто посчитать фичу — предсказание модели.
Отдельно скажу про избитый вопрос — клиентское или серверное время. Всегда используйте серверное. Клиентское время может быть в другом часовом поясе, а главное — просто битым (привет прослушиваниям в Яндекс Музыке из 2099 года). Его можно использовать как сопутствующую информацию (в том числе и для фичей), но не для основного механизма тестирования моделей.
Наконец, мало где можно встретить такое параноидальное внимание к деталям, но всё-таки уточню, что у пользовательских событий (взаимодействий с объектами) есть не один таймстемп и не два, а много:
T_req_start — пришёл пользовательский запрос на рекомендации;
T_req_end — мы построили рекомендации;
T_impression — пользователь увидел (начал смотреть/слушать) порекомендованный объект;
T_click, T_like, ... — пользователь совершил какое-то действие с объектом;
T_ingest — наша система узнала про всё это (что в свою очередь, конечно, зависит от конкретной части системы, которая узнала).
С точки зрения правильного тестирования модели, самые главные из этих таймстемпов — T_req_start и T_ingest. Но, к сожалению, не часто их разделяют, а вместо этого используют один таймстемп и иногда добавляют отставание. Так проще, а разница между ними обычно незначительная. Но думаю, что чем сложнее и мощнее становятся наши модели, тем больше проблем будет у неправильного учёта таймстемпов. С транформерами, например, это уже стало выстреливать намного чаще, чем раньше.
Почему важны таймстемпы? Во-первых, конечно, самый правильный способ измерять качество моделей в офлайне — разделяя trainset и testset по времени (причём глобально, а не для каждого пользователя отдельно, как любят делать в научных статьях). Потому что в реальной жизни всё будет работать именно так: мы обучаем модель, потом выкатываем её, и потом уже она работает на пользователях. А вообще-то, чаще всего у нас модели постоянно инкрементально дообучаются, поэтому и тестировать хорошо бы в таком же инкрементальном/онлайн режиме, а не с отложенным тестсетом.
Во-вторых, иногда нам нужно в офлайне вычислить какие-то фичи. Конечно, лучше всего этого просто не делать, а всегда использовать фичи, залогированные в онлайне. И вообще, золотое правило — лучше всегда обучаться ровно на тех данных, которые используются при применении модели. Чем меньше от этого правила отходишь, тем меньше неожиданных проблем приходится разгребать.
Но всё-таки иногда приходится фичи вычислять заново («вычисление» может быть и очень простым — просто подджойнить таблицу), например, когда это новая фича и нам надо её сначала протестировать перед тем, как выкатывать на логирование. В этом случае надо строго следить за тем, чтобы при вычислении мы использовали данные, максимально похожие на те, которые были бы в онлайне. В том числе — не заглядывать в будущее. Иногда для надежности делают некоторое отставание в несколько минут или часов.
Этот механизм — приджойнить или вычислить фичи ретроспективно, с заданным отставанием по времени — достаточно общий, и его имеет смысл реализовать один раз в обобщенном виде.
Есть ещё и специальный вид вычисления — обучение и применение подмодели. (Например, фичи от трансформеров мы хотим использовать в верхней ранжирующей модели, а трансформеры постоянно дообучаются.) Вся логика тут сохраняется, при применении нужно брать последние известные данные (историю пользователю) и последнюю обученную подмодель (трансформеры). Это, кстати, показывает, что это примерно одно и то же — вычисление ретроспективных фичей и тестирование моделей в онлайн-режиме. Для тестирования сначала надо просто посчитать фичу — предсказание модели.
Отдельно скажу про избитый вопрос — клиентское или серверное время. Всегда используйте серверное. Клиентское время может быть в другом часовом поясе, а главное — просто битым (привет прослушиваниям в Яндекс Музыке из 2099 года). Его можно использовать как сопутствующую информацию (в том числе и для фичей), но не для основного механизма тестирования моделей.
Наконец, мало где можно встретить такое параноидальное внимание к деталям, но всё-таки уточню, что у пользовательских событий (взаимодействий с объектами) есть не один таймстемп и не два, а много:
T_req_start — пришёл пользовательский запрос на рекомендации;
T_req_end — мы построили рекомендации;
T_impression — пользователь увидел (начал смотреть/слушать) порекомендованный объект;
T_click, T_like, ... — пользователь совершил какое-то действие с объектом;
T_ingest — наша система узнала про всё это (что в свою очередь, конечно, зависит от конкретной части системы, которая узнала).
С точки зрения правильного тестирования модели, самые главные из этих таймстемпов — T_req_start и T_ingest. Но, к сожалению, не часто их разделяют, а вместо этого используют один таймстемп и иногда добавляют отставание. Так проще, а разница между ними обычно незначительная. Но думаю, что чем сложнее и мощнее становятся наши модели, тем больше проблем будет у неправильного учёта таймстемпов. С транформерами, например, это уже стало выстреливать намного чаще, чем раньше.
🔥34❤10🤝5👍2👌1
Представьте себе систему рекомендаций, в которой очень важен реалтайм, т.е. быстрое обновление. Например, рекомендации новостей или постов. В ней очень важно быстро обновлять внутренние параметры, особенно те, которые соответствуют самим новостям. Это могут быть как обычные счетчики, выражающие CTR документов, так и обучаемые эмбеддинги для каждой новости, в которых в том числе и этот CTR будет зашит. Будем считать, что у нас есть модель, которая обучается в реальном времени и обновляет эти эмбеддинги.
А теперь давайте подумаем, в какой момент времени относительно времени показа (T_impression) и времени разных взаимодействий (T_click, etc.), о которых говорили в прошлом посте, наша модель должна получить новый сэмпл для обучения (т.е. каким должен быть T_ingest в обучатор модели). Конечно, нам хочется, чтобы это происходило как можно раньше, чтобы мы могли быстро выявлять очень популярные новости. Но сделать это прямо в момент показа мы не можем, так как еще не знаем, произойдёт ли клик.
Самый простой вариант — подождать X времени (скажем, несколько минут) после показа и соответствующим образом решить, положительный ли этот сэмпл или нет. Тогда модель будет предсказывать, с какой вероятностью пользователь кликнет/полайкает/… документ за время X. Если X будет слишком большим, реакция системы будет медленнее. Если X будет слишком маленьким, то мы упустим часть положительных взаимодействий из-за того, что они не успели произойти за X. Эта часть может оказаться совсем не нулевой, больше, чем кажется на первый взгляд, особенно для более сложных таргетов (например, подписался ли пользователь на источник новости). У каждого таргета есть своё вероятностное распределение времени, через которое он случается после показа. И у этих распределений тяжелые хвосты.
Есть ли способы эффективнее?
Например, можно было бы завести несколько разных событий с разными X: кликнул ли за 30 секунд, за 2 минуты, за 5 минут и т.д. Но тогда мы увеличим объём данных для обучения в несколько раз, хотя информации для обучения мы будем выдавать не сильно больше, чем раньше. Это очень большая нагрузка на инфраструктуру и на обучение.
Можно было бы сразу же в момент показа использовать негативный сэмпл, а потом в случае, если какое-то действие произойдёт, сделать новый сэмпл, «уточняющий» предыдущий. (А если еще одно действие произойдёт — то еще одно уточнение, и так далее.) Так как уточнений будет на порядок меньше, чем показов, то нагрузка на инфраструктуру возрастёт не слишком сильно. Понятно, как обрабатывать такие уточнения для счетчиков. А вот как это делать для обучающихся эмбеддингов? Т.е. как «отменить» действие предыдущего негативного сэмпла? Возможно, можно просто сделать обратный шаг — шаг по градиенту (т.е. шаг градиентного подъёма вместо градиентного спуска). Но параметры модели на этот момент уже не те, которые были при прямом шаге, поэтому это не будет совсем точной его отменой. Не разнесёт ли модель от этого? Кроме того, как только новость появляется, она сразу получает много негативных сэмплов, а позитивные придут только с задержкой, из-за чего получается сильное смещение в негативную сторону.
Можно также комбинировать этот способ с изначальным простым — сначала чуть-чуть подождать (маленькое X), использовать первый сэмпл и уже только после этого начинать делать уточнения. Это снизит степень проблем, связанных с уточнениями. Но решит ли полностью? Да и сложновато получается.
Наверно, стоит отметить, что если подумать не про сторону объектов (новостей), а про сторону пользователей, то эта проблема уже кажется не такой специфической — обновлять рекомендации для пользователя в реальном времени хотят все. И если используется обучаемый для каждого пользователя эмбеддинг, то проблема та же самая. Но, например, если использовать трансформер по пользовательской истории, то в него можно запихнуть всё как есть, а он уже сам разберётся, достаточно ли времени прошло от каждого показа, чтобы считать его полноценно отрицательным событием.
Что думаете?
А теперь давайте подумаем, в какой момент времени относительно времени показа (T_impression) и времени разных взаимодействий (T_click, etc.), о которых говорили в прошлом посте, наша модель должна получить новый сэмпл для обучения (т.е. каким должен быть T_ingest в обучатор модели). Конечно, нам хочется, чтобы это происходило как можно раньше, чтобы мы могли быстро выявлять очень популярные новости. Но сделать это прямо в момент показа мы не можем, так как еще не знаем, произойдёт ли клик.
Самый простой вариант — подождать X времени (скажем, несколько минут) после показа и соответствующим образом решить, положительный ли этот сэмпл или нет. Тогда модель будет предсказывать, с какой вероятностью пользователь кликнет/полайкает/… документ за время X. Если X будет слишком большим, реакция системы будет медленнее. Если X будет слишком маленьким, то мы упустим часть положительных взаимодействий из-за того, что они не успели произойти за X. Эта часть может оказаться совсем не нулевой, больше, чем кажется на первый взгляд, особенно для более сложных таргетов (например, подписался ли пользователь на источник новости). У каждого таргета есть своё вероятностное распределение времени, через которое он случается после показа. И у этих распределений тяжелые хвосты.
Есть ли способы эффективнее?
Например, можно было бы завести несколько разных событий с разными X: кликнул ли за 30 секунд, за 2 минуты, за 5 минут и т.д. Но тогда мы увеличим объём данных для обучения в несколько раз, хотя информации для обучения мы будем выдавать не сильно больше, чем раньше. Это очень большая нагрузка на инфраструктуру и на обучение.
Можно было бы сразу же в момент показа использовать негативный сэмпл, а потом в случае, если какое-то действие произойдёт, сделать новый сэмпл, «уточняющий» предыдущий. (А если еще одно действие произойдёт — то еще одно уточнение, и так далее.) Так как уточнений будет на порядок меньше, чем показов, то нагрузка на инфраструктуру возрастёт не слишком сильно. Понятно, как обрабатывать такие уточнения для счетчиков. А вот как это делать для обучающихся эмбеддингов? Т.е. как «отменить» действие предыдущего негативного сэмпла? Возможно, можно просто сделать обратный шаг — шаг по градиенту (т.е. шаг градиентного подъёма вместо градиентного спуска). Но параметры модели на этот момент уже не те, которые были при прямом шаге, поэтому это не будет совсем точной его отменой. Не разнесёт ли модель от этого? Кроме того, как только новость появляется, она сразу получает много негативных сэмплов, а позитивные придут только с задержкой, из-за чего получается сильное смещение в негативную сторону.
Можно также комбинировать этот способ с изначальным простым — сначала чуть-чуть подождать (маленькое X), использовать первый сэмпл и уже только после этого начинать делать уточнения. Это снизит степень проблем, связанных с уточнениями. Но решит ли полностью? Да и сложновато получается.
Наверно, стоит отметить, что если подумать не про сторону объектов (новостей), а про сторону пользователей, то эта проблема уже кажется не такой специфической — обновлять рекомендации для пользователя в реальном времени хотят все. И если используется обучаемый для каждого пользователя эмбеддинг, то проблема та же самая. Но, например, если использовать трансформер по пользовательской истории, то в него можно запихнуть всё как есть, а он уже сам разберётся, достаточно ли времени прошло от каждого показа, чтобы считать его полноценно отрицательным событием.
Что думаете?
❤12👍5🔥1
Неделю назад я выступал в Яндексе с презентаций про свой карьерный путь. Это такой немного странный жанр, вроде автобиографии, с примесью каких-то своих выводов и мыслей. Я даже сначала думал отказаться, вроде такое не должно быть интересно большому число людей. Но оказалось, что нет, народ пришёл послушать, и даже немало вопросов задали.
Когда я закончил первую версию слайдов и решил прогнаться по ним, у меня на это ушло примерно 5 часов. А доклад был рассчитан на час-полтора. Поэтому пришлось всё сильно сокращать. В том числе выкинуть и какие-то занятные истории и байки. В итоге смог уложиться.
А в процессе подумал, что про многое из этого можно было бы написать и сюда в канал. Чуть ли не каждый слайд. Да и один знакомый высказал такую же мысль. Вот только я снова не уверен, что это будет многим интересно, особенно людям, не связанным с Яндексом. Ведь основная часть этого карьерного пути (больше 14 лет!) прошла именно там.
В общем, выскажите своё мнение:
❤️ — да, с интересом будете читать
🤨 — нет, не надо такого
P.S.Если наберётся 100 сердечек Порога нет, всё равно потом сам решу :)
P.P.S.⭐️ тоже засчитываются как положительные голоса :D
Когда я закончил первую версию слайдов и решил прогнаться по ним, у меня на это ушло примерно 5 часов. А доклад был рассчитан на час-полтора. Поэтому пришлось всё сильно сокращать. В том числе выкинуть и какие-то занятные истории и байки. В итоге смог уложиться.
А в процессе подумал, что про многое из этого можно было бы написать и сюда в канал. Чуть ли не каждый слайд. Да и один знакомый высказал такую же мысль. Вот только я снова не уверен, что это будет многим интересно, особенно людям, не связанным с Яндексом. Ведь основная часть этого карьерного пути (больше 14 лет!) прошла именно там.
В общем, выскажите своё мнение:
❤️ — да, с интересом будете читать
🤨 — нет, не надо такого
P.S.
P.P.S.
Please open Telegram to view this post
VIEW IN TELEGRAM
53❤336🤨6👍4🤡3🔥1
Не знаю, все ли поняли, за что проголосовали. Это будет не один и даже не один десяток постов. Но постараюсь их чередовать с обычными постами. И истории будут в около-хронологическом порядке, поэтому ждать байки про Майкрософт придётся ещё долго 🙂
Работать я начал ещё до первого курса. После завершительных школьных экзаменов папа взял меня работать к себе в отдел в филиале РЖД. Моим первым рабочим языком (Паскаль не в счёт) стал VB.NET. К счастью, потом стали переписывать всё на C#. Занимался я тренажёром машиниста, но это не помешало в последствии друзьям подшучивать надо мной: «Миш, у меня сайт РЖД не работает, почини!» Задачи у меня были не очень сложные, поэтому вся энергия выливалась в перфекционизм: пытался заставить остальных писать код хорошо — по канонам ООП и без транслита в названиях.
В 2007 году я закончил второй курс на мехмате и пошёл на кафедру алгебры, в научное руководство к Лене Буниной. Тем летом её пригласили в Яндекс открывать Школу Анализа Данных, куда она меня и позвала учиться. Я как раз очень хотел научиться алгоритмам, поэтому с радостью пошёл. Причём так как Лена меня уже хорошо знала, то взяли меня без собеседований, по блату. Вместо собеседования, она просто покормила меня в столовой Яндекса (еще на Самокатной).
Учиться в ШАДе мне очень понравилось, было много всего интересного. Например, курс алгоритмов, который вёл Макс Бабенко. Мы были первым набором ШАДа, поэтому на нас как раз была отладка в продакшене. К примеру, лекции Ширяева только человека 3-4 с курса могли понять. И из-за этого после первого семестра нам устроили специальный туториал, где объясняли понятнее. А на том же курсе алгоритмов ещё пока не было той самой жесткой системы код-ревью.
Важно понимать, что это были те времена, когда самой крутой IT-компанией в мире, куда хотели попасть все вокруг, была компания Google. И я, конечно, тоже хотел. Да и Макс Бабенко тоже говорил, что если вдруг он решит всё-таки пойти работать в индустрию (для чего ему пришлось бы частично пожертвовать научной работой), то тоже скорее пойдёт в Google.
Во втором семестре у нас с ребятами из ШАДа завелась традиция — каждую вторую пятницу после занятий ходить вместе ужинать в какой-нибудь бар неподалёку. И Лена тоже с нами ходила. На одном из таких вечеров у нас завёлся примерно такой разговор:
— Ой, да ну этот Яндекс, вот вырастим, закончим ШАД и пойдём лучше в Google, — соглашались мы с товарищем, обсуждая вопрос, идти ли в Яндекс на стажировку (как делали многие однокурсники по ШАДу).
— Да? — спросила Лена, — А вот если, например, вы бы пошли не просто в Яндекс, а пошли бы туда работать с Максом?
— С Максом?.. — Задумались мы. А Макса мы считали не просто отличным лектором по алгоритмам, но и профессионалом, у которого хотелось поучиться на практике, — Ну, с Максом, пожалуй, можно даже в Яндекс.
И через пару месяцев мы все, включая Макса, оказались в Яндексе.
Без собеседований, по блату.
#lifestories
Работать я начал ещё до первого курса. После завершительных школьных экзаменов папа взял меня работать к себе в отдел в филиале РЖД. Моим первым рабочим языком (Паскаль не в счёт) стал VB.NET. К счастью, потом стали переписывать всё на C#. Занимался я тренажёром машиниста, но это не помешало в последствии друзьям подшучивать надо мной: «Миш, у меня сайт РЖД не работает, почини!» Задачи у меня были не очень сложные, поэтому вся энергия выливалась в перфекционизм: пытался заставить остальных писать код хорошо — по канонам ООП и без транслита в названиях.
В 2007 году я закончил второй курс на мехмате и пошёл на кафедру алгебры, в научное руководство к Лене Буниной. Тем летом её пригласили в Яндекс открывать Школу Анализа Данных, куда она меня и позвала учиться. Я как раз очень хотел научиться алгоритмам, поэтому с радостью пошёл. Причём так как Лена меня уже хорошо знала, то взяли меня без собеседований, по блату. Вместо собеседования, она просто покормила меня в столовой Яндекса (еще на Самокатной).
Учиться в ШАДе мне очень понравилось, было много всего интересного. Например, курс алгоритмов, который вёл Макс Бабенко. Мы были первым набором ШАДа, поэтому на нас как раз была отладка в продакшене. К примеру, лекции Ширяева только человека 3-4 с курса могли понять. И из-за этого после первого семестра нам устроили специальный туториал, где объясняли понятнее. А на том же курсе алгоритмов ещё пока не было той самой жесткой системы код-ревью.
Важно понимать, что это были те времена, когда самой крутой IT-компанией в мире, куда хотели попасть все вокруг, была компания Google. И я, конечно, тоже хотел. Да и Макс Бабенко тоже говорил, что если вдруг он решит всё-таки пойти работать в индустрию (для чего ему пришлось бы частично пожертвовать научной работой), то тоже скорее пойдёт в Google.
Во втором семестре у нас с ребятами из ШАДа завелась традиция — каждую вторую пятницу после занятий ходить вместе ужинать в какой-нибудь бар неподалёку. И Лена тоже с нами ходила. На одном из таких вечеров у нас завёлся примерно такой разговор:
— Ой, да ну этот Яндекс, вот вырастим, закончим ШАД и пойдём лучше в Google, — соглашались мы с товарищем, обсуждая вопрос, идти ли в Яндекс на стажировку (как делали многие однокурсники по ШАДу).
— Да? — спросила Лена, — А вот если, например, вы бы пошли не просто в Яндекс, а пошли бы туда работать с Максом?
— С Максом?.. — Задумались мы. А Макса мы считали не просто отличным лектором по алгоритмам, но и профессионалом, у которого хотелось поучиться на практике, — Ну, с Максом, пожалуй, можно даже в Яндекс.
И через пару месяцев мы все, включая Макса, оказались в Яндексе.
Без собеседований, по блату.
#lifestories
👍60🔥42😁20❤12❤🔥2
Летом прослушал выпуск подкаста с Максимом Страховым, а затем и подписался на его канал. Он рассказывает про то, как определяются уровни инженеров в FAANG-like компаниях.
Junior
Активно учится и растёт. Выполняет задачи под присмотром. Нужно рассказать, как делать. Пользы приносит меньше, чем на него тратят сил. По сути — инвестиция.
Middle
Независимо выполняет задачи. Может сам придумать, как делать, но нужно рассказать, что делать.
Senior
Достигает цели. Сам может придумать, что делать для достижения.
Staff
Разрабатывает стратегию и ставит цели.
Senior Staff
Запускает проекты с импактом крупного масштаба.
Principal
Трансформирует индустрию.
Конечно, это переупрощенное определение. Есть море нюансов. Очень многое зависит от компании, от конкретного отдела в компании и даже просто от везения (если говорить о повышениях и офферах). Что такое «импакт крупного масштаба»? Где четкая граница между задачами и целями? Почему в этой шкале только тип выполняемой работы, но нет её качества?
Тем не менее считаю это крайне полезным ориентиром. На нём можно основываться, дополнять и уточнять.
Если бы у меня был такой референс несколько лет назад, мне было бы чуть-чуть проще — и на калибровках, и направлять своих подчиненных, а может быть, и для собственного роста.
Junior
Активно учится и растёт. Выполняет задачи под присмотром. Нужно рассказать, как делать. Пользы приносит меньше, чем на него тратят сил. По сути — инвестиция.
Middle
Независимо выполняет задачи. Может сам придумать, как делать, но нужно рассказать, что делать.
Senior
Достигает цели. Сам может придумать, что делать для достижения.
Staff
Разрабатывает стратегию и ставит цели.
Senior Staff
Запускает проекты с импактом крупного масштаба.
Principal
Трансформирует индустрию.
Конечно, это переупрощенное определение. Есть море нюансов. Очень многое зависит от компании, от конкретного отдела в компании и даже просто от везения (если говорить о повышениях и офферах). Что такое «импакт крупного масштаба»? Где четкая граница между задачами и целями? Почему в этой шкале только тип выполняемой работы, но нет её качества?
Тем не менее считаю это крайне полезным ориентиром. На нём можно основываться, дополнять и уточнять.
Если бы у меня был такой референс несколько лет назад, мне было бы чуть-чуть проще — и на калибровках, и направлять своих подчиненных, а может быть, и для собственного роста.
Telegram
Podlodka Podcast – анонсы и новости подкаста про IT in Podlodka – IT Podcast
Podlodka #384 – Карьера в FAANG
Существует популярное мнение – делай свою работу быстрее и лучше других, и продвижение по карьере не заставит себя ждать. На ранних этапах карьеры это еще справедливо, но чем дальше – тем больше правила игры меняются. Особенно…
Существует популярное мнение – делай свою работу быстрее и лучше других, и продвижение по карьере не заставит себя ждать. На ранних этапах карьеры это еще справедливо, но чем дальше – тем больше правила игры меняются. Особенно…
👍19❤2
Попав в Яндекс, мы получили проект от Ильи Сегаловича. Илья умел очень классно делиться идеями и объяснять суть. Он нам рассказал, что на самом деле Гугл в своё время выиграл у всех предыдущих поисковиков за счёт хорошо сделанных сниппетов. А теперь для нас самое главное — сделать так, чтобы поисковые результаты не были сплошь одинаковыми. Надо бороться с полу-дублями.
Только сделать это у нас не удалось. Зато мне удалось получить свою первую психологическую травму на работе.
В Яндексе тогда не было почти никакой документации. Даже как собирать проект — было тайным знанием, передающимся из уст в уста.
Когда нужно было разобраться в каком-то куске поискового кода, Макс сказал:
— Ну давай посмотрим, кто автор этого кода... Ага, некий Антон с ником pg@. Просто сходи и спроси у него, что здесь происходит.
Я сходил и спросил. Антон с ником pg@ ответил мне, чтобы я просто прочитал код.
Прочитать и понять код у меня не получилось. А так как работали мы на четверть ставки, то в следующий раз мы с Максом встретились примерно через неделю. Узнав, что прогресса особо нет, Макс сказал:
— Нет, ну так дело не пойдёт. Пойдём вместе сходим и спросим.
Сходили и спросили. На что Антон с ником pg@ просто накричал на нас обоих: какого чёрта какие-то стажёры его отвлекают и не могут даже за неделю самостоятельно прочитать код?!
С тех пор ни я, ни Макс уже больше никогда не хотели работать в Яндекс.Поиске.
#lifestories
Только сделать это у нас не удалось. Зато мне удалось получить свою первую психологическую травму на работе.
В Яндексе тогда не было почти никакой документации. Даже как собирать проект — было тайным знанием, передающимся из уст в уста.
Когда нужно было разобраться в каком-то куске поискового кода, Макс сказал:
— Ну давай посмотрим, кто автор этого кода... Ага, некий Антон с ником pg@. Просто сходи и спроси у него, что здесь происходит.
Я сходил и спросил. Антон с ником pg@ ответил мне, чтобы я просто прочитал код.
Прочитать и понять код у меня не получилось. А так как работали мы на четверть ставки, то в следующий раз мы с Максом встретились примерно через неделю. Узнав, что прогресса особо нет, Макс сказал:
— Нет, ну так дело не пойдёт. Пойдём вместе сходим и спросим.
Сходили и спросили. На что Антон с ником pg@ просто накричал на нас обоих: какого чёрта какие-то стажёры его отвлекают и не могут даже за неделю самостоятельно прочитать код?!
С тех пор ни я, ни Макс уже больше никогда не хотели работать в Яндекс.Поиске.
#lifestories
😁127😢27🤯8💩5👍4🥴3🤡1
После неудачи с первым проектом мы решили заняться чем-то ещё. И снова у Ильи Сегаловича возникла прекрасная идея: давайте сделаем специальный поиск по стихотворениям! Обычный поиск здесь не очень хорошо справляется, потому что стихи нужно искать в меньшей степени по смыслу, а в большей — по совпадению последовательности слов. И так зародился проект Стихолюб.
С этим проектом у нас пошло сильно лучше, потому что его можно было писать отдельно от существующей кодовой базы. А так как мы были группой исследовательских проектов, свой исследовательский прототип мы решили писать на том языке, который и я, и Макс знали лучше всего. На C#.
За полгода получилось сделать отличный поиск стихов, в котором были и расстояние Левенштейна, и суффиксные массивы, и алгоритм Ахо-Корасик, и фильтр Блума. И это даже неплохо работало.
И вот в конце апреля 2009 приходит к нам снова Сегалович и говорит:
— Слушайте, тут такое дело... 6 июня юбилей Пушкина. Очень хочется к этой дате что-то прикольное запустить. Нельзя ли теперь быстро ваш прототип превратить в продакшен?
Мы слегка удивились.
Я работал на четверть ставки.
Впереди меня ждала сессия.
C++ я знал очень плохо.
Учитывая это, быстро всё переписывать на православные технологии мы всё-таки отказались.
И тогда Сегалович решил договориться со всеми, чтобы проект выкатили в продакшен прямо в таком виде. На C#. На виндовых серверах.
От одного из виндовых админов мы получили новое прозвище. «Эти стихоблуды опять чего-то странное делают», — написал он на общую рассылку.
А руководитель качества поиска Денис усмехнулся:
— Вы что, с ума сошли? На C#? Как вы вообще собираетесь 5K RPS держать на своём дот-нете?
Когда провели нагрузочное тестирование, наш поиск выдержал и 10K, а дальше закончились патроны.🤷♂️
Но на всякий случай решили написать отдельное правило на верхнем поиске, которое бы фильтровало трафик к нам. (Именно там и был фильтр Блума.) Правда, потом это правило просто забыли включить, и мы получили весь поисковый трафик.
Но перед самым запуском нас встретила ещё пара неожиданных проблем.
Во-первых, по соображениям безопасности, нельзя было где-либо показывать наружу, что поиск работает на винде. Поэтому Максу пришлось быстро патчить какую-то виндовую библиотеку, чтобы IIS притворялся nginx-ом в каких-то заголовках.
А во-вторых, surprise-surprise, оказывается, для такого колдунщика нужно не просто научиться находить самое подходящее стихотворение в базе, но и сделать классификатор, который бы говорил, показывать колдунщик или мы нашли мусор.
Я как раз прослушал новый курс в ШАДе под названием Machine Learning. Мы собрали ответы нашего поиска и разметили их на мусор. Получили целых 300 примеров! И дальше я несколько дней пытался обучить на этом SVM с разными фичами. И feature selection написал. Но почему-то всё равно получилось плохо.
В итоге наш продакт-менеджер Рост просто сам придумал decision tree, с которым мы и запустились.
Это был единственный раз, когда я работал несколько ночей подряд.
А ещё, за пару дней до запуска к нам пришли Антон с ником pg@ и его руководитель и сказали:
— Ребят, вы, конечно, молодцы. Но мы не хотим потом поддерживать ещё один поиск, да ещё на C#.
И после запуска мы специально наняли в команду ещё одного разработчика Алексея — преподавателя C++ в ШАДе, — вместе с которым я всё-таки переписал это, заодно и подучив язык хоть немного.
А переписанный Стихолюб проработал ещё несколько лет. Пока его не отключили за ненадобностью.
#lifestories
С этим проектом у нас пошло сильно лучше, потому что его можно было писать отдельно от существующей кодовой базы. А так как мы были группой исследовательских проектов, свой исследовательский прототип мы решили писать на том языке, который и я, и Макс знали лучше всего. На C#.
За полгода получилось сделать отличный поиск стихов, в котором были и расстояние Левенштейна, и суффиксные массивы, и алгоритм Ахо-Корасик, и фильтр Блума. И это даже неплохо работало.
И вот в конце апреля 2009 приходит к нам снова Сегалович и говорит:
— Слушайте, тут такое дело... 6 июня юбилей Пушкина. Очень хочется к этой дате что-то прикольное запустить. Нельзя ли теперь быстро ваш прототип превратить в продакшен?
Мы слегка удивились.
Я работал на четверть ставки.
Впереди меня ждала сессия.
C++ я знал очень плохо.
Учитывая это, быстро всё переписывать на православные технологии мы всё-таки отказались.
И тогда Сегалович решил договориться со всеми, чтобы проект выкатили в продакшен прямо в таком виде. На C#. На виндовых серверах.
От одного из виндовых админов мы получили новое прозвище. «Эти стихоблуды опять чего-то странное делают», — написал он на общую рассылку.
А руководитель качества поиска Денис усмехнулся:
— Вы что, с ума сошли? На C#? Как вы вообще собираетесь 5K RPS держать на своём дот-нете?
Когда провели нагрузочное тестирование, наш поиск выдержал и 10K, а дальше закончились патроны.
Но на всякий случай решили написать отдельное правило на верхнем поиске, которое бы фильтровало трафик к нам. (Именно там и был фильтр Блума.) Правда, потом это правило просто забыли включить, и мы получили весь поисковый трафик.
Но перед самым запуском нас встретила ещё пара неожиданных проблем.
Во-первых, по соображениям безопасности, нельзя было где-либо показывать наружу, что поиск работает на винде. Поэтому Максу пришлось быстро патчить какую-то виндовую библиотеку, чтобы IIS притворялся nginx-ом в каких-то заголовках.
А во-вторых, surprise-surprise, оказывается, для такого колдунщика нужно не просто научиться находить самое подходящее стихотворение в базе, но и сделать классификатор, который бы говорил, показывать колдунщик или мы нашли мусор.
Я как раз прослушал новый курс в ШАДе под названием Machine Learning. Мы собрали ответы нашего поиска и разметили их на мусор. Получили целых 300 примеров! И дальше я несколько дней пытался обучить на этом SVM с разными фичами. И feature selection написал. Но почему-то всё равно получилось плохо.
В итоге наш продакт-менеджер Рост просто сам придумал decision tree, с которым мы и запустились.
Это был единственный раз, когда я работал несколько ночей подряд.
А ещё, за пару дней до запуска к нам пришли Антон с ником pg@ и его руководитель и сказали:
— Ребят, вы, конечно, молодцы. Но мы не хотим потом поддерживать ещё один поиск, да ещё на C#.
И после запуска мы специально наняли в команду ещё одного разработчика Алексея — преподавателя C++ в ШАДе, — вместе с которым я всё-таки переписал это, заодно и подучив язык хоть немного.
А переписанный Стихолюб проработал ещё несколько лет. Пока его не отключили за ненадобностью.
#lifestories
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥63👍17❤🔥13❤9👏2🤡1💔1
Невероятно.
Разработчик Алексей рассказал в комментах, что Стихолюб до сих пор жив!
Разработчик Алексей рассказал в комментах, что Стихолюб до сих пор жив!
🔥24🦄5😁1😱1