Главная » Хабрахабр » Лекция о Толоке. Как тысячи людей помогают нам делать Яндекс

Лекция о Толоке. Как тысячи людей помогают нам делать Яндекс

Ежедневно десятки тысяч людей выполняют задания в Толоке: оценивают релевантность сайтов, классифицируют изображения, отмечают объекты на фотографиях. Решая эти и многие другие задачи, они помогают нам улучшать существующие и создавать новые алгоритмы, а также поддерживать актуальность данных.

С другой, она служит важнейшей частью всех ключевых сервисов Яндекса и десятков сервисов поменьше. С одной стороны, Толока появилась сравнительно недавно — в 2014 году. Кроме того, Артём рассказал про логику раздачи заданий пользователям, работу с геоданными на карте и управление качеством. Артём Григорьев ortemij объяснил, как эта краудсорсинговая платформа устроена, какие технологии и архитектурные решения применяются при её разработке.

— Пару слов обо мне. Я более семи лет работаю в петербургском офисе Яндекса. Когда я только пришел сюда, я занимался различными инструментами для оценки качества поиска. Мы разрабатывали разные метрики, сравнивали себя с конкурентами и разными версиями других поисковых систем. Сейчас я руковожу службой с длинным названием, как на слайде.
Вкратце, мы работаем по трем основным направлениям. Во-первых, занимаемся разработкой нашей краудсорсинговой платформы Яндекс.Толока. Во-вторых, разрабатываем разнообразные сервисы для асессоров. Это специальные люди в Яндексе типа модераторов. Специалисты поддержки, для них делаем разные сервисы, контент-менеджеры и т. д.

Эта инфраструктура связывает то, что мы делаем и чем занимаются люди, например, при разработке поиска. И наконец, мы разрабатываем инфраструктуру, которая позволяет всем командам Яндекса работать с перечисленными людьми и результатами их работы.

Вначале хочется пояснить понятие краудсорсинга. Доклад будет про нашу краудсорсинговую платформу. Не путайте с разработкой на аутсорсе или сбором денег через краудфандинг, это немножко другое.

Например, вы хотите обучить какой-то классификатор, и у вас есть набор данных, который нужно разметить и получить какие-то знания. Давайте представим, что вы занимаетесь machine learning. Этот набор данных можно представить в виде некоторого количества задач, написать инструкцию, по которой задания можно выполнить, и отправить некоторым людям, которые это сделают.

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

Поясню термин Толока́. Второй вопрос. По-русски ударение ставят на второй слог, но мы отдаем дань тому, что этот сервис начали делать изначально в нашем белорусском офисе, многие ребята приехали поотвечать на ваши вопросы в кулуарах. Многие могут сказать, что я неправильно это слово произношу.

Значение этого слова — в том, что мы перенесемся в древние времена. Когда мы начинали делать этот сервис и придумывали название, то вспомнили, что есть такое интересное понятие. Каждый по отдельности, может, эту работу сделать не смог бы, но они вместе собрались, пошли собирать урожай или строить какой-то сарай для общественных нужд. Люди в деревне собираются вместе и что-то делают на благо общества. Мы подумали, что это схоже с современным термином краудсорсинга, поэтому такое название для нашего сервиса и выбрали. Толока — это некоторый обычай собираться вместе и помогать друг другу.

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

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

Она очень популярна. Нашей платформой пользуются, без малого, все сервисы Яндекса.

Могу привести несколько примеров заданий, которые реально сейчас в платформе размещаются.

Или задания для обучения беспилотных автомобилей. Например, задания на простую классификацию фотографий. Может, видели видео, где машина Яндекса едет по Хамовникам в Москве, разметку для того, чтобы обучать компьютерное зрение в этом автомобиле, собирали в том числе в Толоке.

д. Задания для распознавания цен на фотографиях и т. В принципе, базово понятно, гуманитарную вводную часть на этом заканчиваю.

Если кому-то интересно поподробнее про краудсорсинг узнать, я осенью выступал на конференции SmartData, в Youtube можно найти видео с таким названием, там более подробно рассказывается о том, что такое краудсорсинг и про особенности интересные в нем.

Мы здесь собрались, чтобы узнать внутренние, технические особенности работы нашей платформы, поэтому перейдем к этой части.

Можно зайти в браузере на сайт toloka.yandex.ru, выполнять задания там. Толока доступна в нескольких формах. Они используются, в частности, для различных заданий, где требуется мобильность, люди ходят по местности и выполняют задания «в полях», ну и где нужны специальные фичи телефона, например, диктофон или камера. Также у нас есть мобильные приложения под iOS и под Android.

Наш бэкенд написан в основном на Java.

И в итоге Толока представлена в виде набора микросервисов, каждый из которых разворачивается в виде Docker-контейнере в нашей облачной системе. Мы используем активно SpringBoot и всевозможные его составляющие. Мы деплоимся в наше внутреннее облако.

Самая основная бизнес-логика сделана на PostgreSQL, потому что мы работаем с деньгами, нам очень важно обеспечивать транзакции между заказчиками и пользователями. В качестве хранилищ мы используем несколько разных. Вся логика, где критична эта особенность, у нас находится в PostgreSQL.

Во многих микросервисах, которые построены вокруг Толоки, используется это хранилище. Для тех мест, где это не нужно, мы используем MongoDB. Это наша внутренняя реализация MapReduce, похожа на BigTable, в Яндексе ее ласково называют Ыть.

Сама предметная область краудсорсинга и сам сервис довольно сложный, поэтому у нас довольно много задач на реализацию разнообразной бизнес-логики. В разработке Толоки есть несколько основных направлений, в которых мы решаем свои задачи. Мы активно используем разнообразные технологии, используем всевозможные оптимизации, поэтому такой класс задач у нас тоже довольно большой.

Наконец, предметная область довольно интересно и динамично развивается, и в мире довольно много исследований на эту тему, поэтому мы внутри тоже такими исследованиями занимаемся.

Чтобы приоткрыть завесу на то, что внутри происходит, я по одной задаче из каждой области приведу, и попытаюсь рассказать что-то интересное, что происходит, на эту тему.

Начнем с бизнес-логики.

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

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

У нас получается большая очередь, в которую, с одной стороны, заказчики заливают пулы и новые задачи, с другой стороны, приходят исполнители и спрашивают, какие задания мне доступны, выбирают себе интересные, и потом туда отправляют результаты. Заказчики обычно формируют некоторые пулы заданий, множества заданий, доступные определенному количеству исполнителей, и пачками туда эти задания загружают.

Как работает получение списка доступных заданий, главная страница Яндекс.Толоки?

Про это дальше еще расскажу, но не все задания доступны каждому, поэтому мы должны отфильтровать те, которые показать данному пользователю. Во-первых, мы ищем пулы, которые подходят исполнителям по фильтрам.

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

Мы должны найти те пулы, в которых у пользователя есть достаточно заданий, чтобы показать страницу. Важный момент, что задания мы отдаем не по одному, а страницами, на которых показывается сразу несколько заданий, это сделано для оптимизации скорости. Отфильтровав это все, мы группируем и показываем страницу на фронтенде, которую видит человек. И важно, что мы одни и те же задания пользователю второй раз не показываем, он делает все по одному разу.

Есть вычислимые поля, допустим, ОС, с которой заходит пользователь, можем по этому критерию пользователей отфильтровать. Исполнитель в нашей системе представлен несколькими характеристиками. д. Или его IP-адрес, регион, вычисленный по этому IP и т.

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

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

С другой стороны, у нас есть некоторые пулы, где заданы фильтры.

Когда пользователь заходит на страницу, мы матчим это представление фильтра на некотором псевдоязыке с JSON представлением пользователя, которое у нас есть на данный момент. Допустим, для выполнения некоторых заданий нам нужны пользователи, которые по номеру телефона указали, что они находятся в России, Украине, Казахстане или Беларуси, и еще некоторое ограничение по навыкам. Представление пользователя может меняться, и заказчик тоже может менять фильтры, настроенные на пуле.

Сейчас мы это делать перестали, потому что представление фильтра на JSquery, плагин такой для PostgreSQL. Раньше мы компилировали представление фильтров на JSquery, такой плагин для пользователя. Сейчас мы это делать перестали, потому что фильтровать на самом бэкенде в памяти оказалось сильно быстрее.

И мы опять возвращаемся к очередям. Мы отфильтровали пулы, и надо проверить, что в них достаточно заданий, которые пользователь еще не делал, чтобы показать ему полную страницу.

У нее есть некоторый набор свойств. Каждый пул заданий — это очередь заданий для различного количества пользователей. Во-первых, эти очереди динамически создаются, удаляются, и также заказчики могут доливать в пулы новые задания, поэтому эти очереди могут дополняться новыми элементами.

В этой очереди для каждого задания получается много разных потребителей. Во-вторых, каждое задание может быть сделано многими исполнителями. Все эти ограничения вкупе с тем, что таких очередей может быть десятки тысяч, выкладываются в то, чтобы отрисовать главную страницу, которую спрашивают у нас примерно 150 раз в секунду, нам нужно сделать около 40 тыс. И для исполнителей задания повторяться не должны, мы не должны два раза платить человеку за то, что он делает. Это тяжелое место, и мы его соптимизировали следующим образом — мы перенесли все эти проверки в базу данных. проверок того, что в данной очереди, в данном пуле заданий есть доступные для пользователя.

Получается, вместо того, чтобы джоинить огромные таблицы, мы делаем простую выборку и повторяем эту проверку на бэкенде базы каждый раз. Использовали такую штуку в PostgreSQL, называется lateral join, он позволяет для каждого элемента из левой таблицы выполнить запрос в цикле по одному разу. проверок мы делаем на порядки меньше, они все унесены в базу, из бэкенда мы туда в цикле не ходим. Запросы, которые находятся внутри, довольно легкие, от этого получается, что вместо 40 тыс.

Представим, что он на кнопку «Приступить» нажимает. Отфильтровали, поняли, какие пулы доступны пользователю. В этот момент мы должны зарезервировать ему некоторое задание, страницу, которую он будет видеть, и за выполнение которой потом получит деньги.

После этого ищем уже готовую страницу. Снова проверяем, что этот пул, в котором данное задание находится, все еще доступен пользователю. Если такой страницы нет, мы ее подготавливаем заново, мы берем из нескольких источников задания, смешиваем друг с другом и отображаем на экране для пользователя. Если где-то есть подготовленная, которую пользователь еще не делал, мы ему ее отдаем.

Мы берем деньги со счета заказчика и резервируем их на выполнение этого задания. В этот же момент мы производим финансовую операцию.

Если он от задания откажется или по каким-то причинам оно будет отклонено, деньги вернутся на счет заказчика. Если пользователь в конечном итоге задания выполнит, все будет хорошо, эти деньги переведутся ему на счет.

д. Это все происходит в транзакции, именно по причине того, что мы работаем здесь с деньгами, и нам очень важно ничего лишнего не потратить, не выдать одно и то же два раза разным людям, если это не надо, и т.

И это накладывает такую сложность на и так не очень простую бизнес-логику.

Получается, эти люди конкурируют примерно за одни и те же задачи. Представьте, что многие пользователи видят одни и те же задания, и пытаются одновременно к ним приступить. Здесь полезна такая штука, которая позволяет заблокированные строки пропускать при селекте. Важно, чтобы они друг друга не перелочили в базе.

Перейдем к части про технологии.

Хочется рассказать, как мы пришли к тому, что начали использовать реплики, и какой был к этому тернистый путь. Я много говорил, что мы сильно используем PostgreSQL. И на бэкенде используется некий пул соединений, который позволяет эти соединения переиспользовать. Практически любое приложение, которое использует БД, обычно начинает с того, что все запросы идут на мастер, реплики используются для отказоустойчивости.

Сервисом пользуются, нагрузка на него начинает расти.

Потом начинаете смотреть, что на мастере начинает кончаться CPU, начинаете оптимизировать ваши запросы, смотреть, какие там долгие транзакции, почему соединения выделяются лишний раз. Вы увеличиваете размер этого пула соединений. Так же поступали и мы.

В какой-то момент добавили pgbouncer, который позволяет иметь заготовленный пул соединений и не тратить процессор на то, чтобы выделять новые.

По-всякому крутили клиентские настройки, перешли на быстро работающий пул соединений hikari-cp.

В конечном итоге получалось так, что все эти наши оптимизации не приводили к нужному эффекту, и нагрузка, и те фичи, которые мы постоянно добавляли, превосходили нас, поэтому в какой-то прекрасный момент мы пришли к выводу, что пришла пора читать с реплик.

Тоже понятная простая задача, давайте добавим еще несколько датасорсов, будем на них направлять те транзакции, которые только данные читают, read only, и попробуем найти способ определять эти read only транзакции.

Но есть некоторые особенности, например, при использовании автоматически сгенерированных запросов Spring JPA он у нас не работал. В Java есть метод с длинным названием, который должен позволять это определять в момент выполнения транзакций. Тогда мы нашли эти места и обернули во вспомогательный метод, мы форсируем чтение с реплик в этих местах, давайте так поступать, все будет хорошо.

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

Пользователь в UI, заказчик, создал новый пул заданий, его страничка отрисовывается, а его на самом деле нет, потому что создавался он на мастере, а чтение пошло с реплик. Или другой момент. Такие места мы отловили различными автоматическими тестами, и первое, что нам пришло в голову, давайте захачим и начнем форсированно читать все с мастера.

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

Она не будет закоммичена до тех пор, пока не пройдет на мастере и на реплике. Есть еще способ в PostgreSQL, есть флажок remote_apply, он говорит о том, что нам нужно эту транзакцию закоммитить еще на реплики.

С асинхронными репликами этот флаг не действует, а держать кучу синхронных реплик накладно, сильно замедляется запись, мы это делать не хотели. По идее можно было его использовать, но особенность в том, что она работает только для синхронных реплик.

Здесь тоже представлен кусочек кода. А хотели примерно такого. Кажется удобный и простой метод. Мы хотели сделать некоторый вспомогательный метод, назвали его replicationBarrier, суть его в том, что в тех местах, где нам обязательно надо дождаться, что данные доехали до реплик, мы вставляем эту строчку кода, и приложение не выйдет из этого метода до тех пор, пока данные, созданные в транзакциях до этого места в этом потоке, не доедут до реплик. Давайте подумаем, как его можно реализовать.

И мы можем прочитать, в какой позиции мы в логе находимся, и подождать, когда эта позиция будет достигнута на реплике. В 11-м PostgreSQL — сейчас в продакшен внедряют 10-й — будет специальная команда WAIT FOR LSN, где LSN — lock sequence numbers. Пока 11-го PostgreSQL нет, этим пользоваться не можем.

В момент, когда мы транзакции провели, давайте замерять, на какой позиции в логе мы находимся, и потом через replay_location в этой таблице на каждой реплике проверять, где мы есть. Еще вариант — вспомогательная вьюха в PostgreSQL, pg_stat_replication, в ней есть всякая диагностическая информация на тему того, как у нас работают реплики. Реализовали, на тестовых окружениях все было хорошо, на продакшене под более высокой нагрузкой это не заработало, были моменты, когда реплики почему-то были рассинхронизированы. И если мы находимся в позиции, которая нам нужна, или позже, то мы будем выходить из этого барьера.

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

Мы увеличиваем этот счетчик в момент, когда заходим в барьер, и ждем, пока все реплики вернут значение не меньше того, который мы только что заинкрементили. Завели специальную таблицу, в ней у каждого бэкенда есть строчка, где хранится счетчик. Помимо того, что он получает значение счетчика, он еще следит за живостью реплик. И для того, чтобы получать значение с реплик, у нас есть отдельный процесс. Реплики могут по тем или иным причинам отваливаться, на короткие моменты времени плохо работать, и он еще мониторингом доступности занимается.

Если несколько потоков одновременно пришли ожидать этот счетчик, запрашивать его только один раз, и если у нас происходит одновременно из разных потоков увеличение этого счетчика, тоже делать это увеличение только однажды. Понятно, тут еще можно сделать оптимизации.

Примерно 1,5 тыс. В итоге наша база сейчас по объему занимает где-то 2 ТБ, у нас есть мастер, есть три реплики, на каждом из серверов по 32 ядра. Это практически только запросы на создание или изменение каких-то данных. запросов в секунду выполняется на мастере. запросов на каждой из реплик, это запросы на чтение. И 10 тыс.

В replication_counter таблицу мы обращаемся где-то 200 раз в секунду за тем, чтобы получить значение счетчика, а увеличиваем его около 80 раз.

Ну и медиана ожидания барьера примерно равна времени ping до реплики — очень быстро.

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

Куча людей хотят как-то считить, получить это недобросовестным образом. Можете представить, что есть система, где платят деньги за выполнение каких-то заданий. Есть какие-то более продвинутые, которые пишут различные скрипты, которые пытаются автоматически эти деньги с нас намайнить. Есть люди, которые пытаются случайно все прокликать, чтобы заработать побольше.

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

Самый известный способ ловить роботов — это, конечно, капча. Чтобы контролировать качество, существует несколько методов. Иногда, когда мы подозреваем исполнителя в том, что он может быть роботом, показываем ему капчу, и если он несколько раз не справляется с ней, мы его в системе баним. Мы ей тоже пользуемся.

Если задания выполняются настолько быстро, что исполнитель вообще не думает, как это выполнить качественно, или это бот, в который забыли вставить таймаут между сабмитом заданий, то мы тоже можем считать данного исполнителя подозрительным. Также можно использовать различные эвристики по скорости выполнения заданий. Или если ответы идут одинаковые, тоже довольно простой паттерн — сидеть и одну и ту же кнопку все время нажимать, так тоже обычно поступают читеры.

Например, ханипоты, это, на которые мы заранее знаем ответы, но визуально они никак не отличаются от настоящих. Есть более сложные способы. Они все примерно одинаково выглядят, но на серверной стороне мы уже можем проверить, сравнить ответ пользователя и ответ, который мы считаем правильным. И когда мы страницу заданий показываем исполнителю, в ней в некоторых местах есть ханипоты. Либо он некачественно делает задания, либо это вариант бота. И если исполнитель часто ошибается, это тоже подозрительно.

Это нам позволяет нам сравнивать их ответы между собой. Еще в краудсорсинге одни и те же задания часто специально выдают нескольким исполнителям. И тех, кто отвечает часто не так, как остальные, мы тоже подозреваем в какой-то недобросовестности выполнения заданий.

Мы постоянно внедряем новые способы контроля качества, которые будут доступны заказчикам. Эти способы в платформе Толоки доступны из коробки.

Обычно, чтобы потом эти данные использовать — например, для обучения классификатора, — нам нужно по каждому входному элементу иметь итоговый вердикт. Я упомянул, что одни и те же задания часто делают несколько исполнителей, и возникает интересная задача — задача агрегации ответов. И самой простой моделью агрегации является модель majority vote — надеюсь, так у нас выборы работают. Нам неинтересно иметь пять разных мнений. Берем тот результат, за который проголосовало большее количество исполнителей.

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

На картинке представлено, что к нам пришли исполнители-анонимы, мы про них ничего не знаем, а есть те, кто работает на нашем задании давно. Перекрытия тоже не всегда можно использовать полностью. Или вообще у нас, может, есть мегаэксперт, одного ответа которого будет достаточно, чтобы итоговое решение принять. У нас про них накоплена какая-то статистика.

За счет динамического перекрытия можно экономить до 40% бюджета без снижения в качестве. И за счет того, что мы при раздаче заданий не всегда отдаем это, например, пяти исполнителям, а используем динамическое перекрытие, мы можем выигрывать в экономии денег.

Может, что-то из этого будет полезно вам в ваших проектах. Я продемонстрировал три разных направления разработки в Толоке. Напомню, она открыта для любых заказчиков, у нее есть API, можно заливать туда свои задания и собирать данные для себя. Может, кто-то из вас занимается ML или Data Science и захочет попробовать Толоку для своих задач. Спасибо, на этом все. У нас много интересных задач — может, кто-то захочет с ними помочь.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Перевод] Курс MIT «Безопасность компьютерных систем». Лекция 18: «Частный просмотр интернета», часть 1

Массачусетский Технологический институт. Курс лекций #6.858. «Безопасность компьютерных систем». Николай Зельдович, Джеймс Микенс. 2014 год Computer Systems Security — это курс о разработке и внедрении защищенных компьютерных систем. Лекции охватывают модели угроз, атаки, которые ставят под угрозу безопасность, и методы ...

[Из песочницы] Как я понял, что ем много сладкого, или классификация товаров по чекам в приложении

Задача В этой статье мы хотим рассказать, как мы создали решение для классификации названий продуктов из чеков в приложении для учёта расходов по чекам и помощника по покупкам. Мы хотели дать пользователям возможность просматривать статистику по покупкам, собранную автоматически на ...