Русский
Русский
English
Статистика
Реклама

Поисковые технологии

Наша анонимность утрачена?

04.06.2021 22:07:07 | Автор: admin

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

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

1. Ip-адрес. Провайдеры годами хранят статистику на каком ip-адресе работал их пользователь. Зная ip-адрес можно легко вычислить расположение пользователя.

2. DNS-запросы. Каждый раз, когда Ваш браузер хочет получить доступ к определенной службе, например Google, обратившись к www.google.com, Ваш браузер запросит службу DNS, чтобы найти IP-адреса веб-серверов Google. Таким образом, интернет-провайдер DNS-записей сможет рассказать обо всем, что Вы делали в сети, просто просмотрев те журналы, которые, в свою очередь, могут быть предоставлены злоумышленнику.

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

4. Устройства Wi-Fi и Bluetooth вокруг Вас. Производители смартфонов заложили функции поиска Вашего местонахождения без использования GPS-функций. Достаточно сканировать Wi-Fi сети вокруг устройства. Данные о Вашем местоположении отправляются на сервера третьих компаний.

5. Использование Wi-Fi на смартфоне. Чтобы получить Ваши данные хакеры могут использовать специальные устройства, которые будут мешать работе wi-fi точек доступа и вынуждать Ваш смартфон подключиться к их устройству вместо публичной wi-fi точки доступа. В этом случае все Ваши данные становятся доступными для злоумышленника. В данном случае используются технологии MITM (Man-In-The-Middle) или человек посередине.

6. Использование Tor/VPN. За прошедшие годы было разработано и изучено множество передовых методов деанонимизации зашифрованного трафика Tor. Атака по корреляционным отпечаткам пальцев: эта атака будет отпечатывать ваш зашифрованный трафик (например, веб-сайты, которые вы посещали) только на основе анализа вашего зашифрованного трафика (без его расшифровки). Он может сделать это с колоссальным успехом в 96%. Такие отпечатки пальцев могут использоваться злоумышленником, имеющим доступ к вашей исходной сети (например, Ваш интернет-провайдер), для выяснения некоторых ваших зашифрованных действий (например, какие веб-сайты вы посещали).

7. Современные смартфоны на Android, IOS.
После выключения такого устройства оно будет продолжать передавать идентификационную информацию на близлежащие устройства даже в автономном режиме с использованием Bluetooth Low-Energy. Это дает способ найти Вас даже если Ваш телефон выключен, но имеет подключенный аккумулятор.

8. IMEI идентификатор Вашего оборудования. IMEI привязан непосредственно к телефону, который вы используете. Этот номер известен и отслеживается операторами мобильной связи, а также известен производителями. Каждый раз, когда ваш телефон подключается к мобильной сети, он регистрирует IMEI в сети. Он также используется многими приложениями (например, банковскими приложениями, злоупотребляющими разрешением телефона на Android) и операционными системами смартфонов (Android/IOS) для идентификации устройства. Сегодня предоставление вашего реального номера телефона в основном то же самое или лучше, чем предоставление вашего паспорта. Журналы антенн оператора мобильной связи также хранят некоторые данные о подключении. Они знают и регистрируют, например, что телефон с IMEI, подключен к набору мобильных антенн, и насколько мощный сигнал каждой из этих антенн. Это позволяет легко выполнять триангуляцию/геолокацию сигнала. Они также знают, какие другие телефоны (например, ваш настоящий) подключены в одно и то же время к тем же антеннам с тем же сигналом, что позволит точно знать, что этот телефон всегда был подключен в том же месте и в то же время, что и этот другой телефон, который появляется каждый раз, когда используется записывающий телефон. Эта информация может использоваться различными третьими сторонами для точного определения вашего местоположения/отслеживания.

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

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

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

12. Использование антивируса. Антивирус кроме основных своих целей по поиску и обезвреживанию вирусов анализирует вашу сетевую активность, данные браузеров. Эти данные могут быть переданы третьим лицам.

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

  • пол;

  • возраст;

  • семейное положение;

  • политические, религиозные взгляды;

  • финансовое состояние;

  • интересы;

  • привычки;

  • и многие другие.

Согласно результатам исследования EFF (Electronic Frontier Foundation), уникальность отпечатка браузера очень высока и он содержит в себе ниже описанные данные:

  • User-agent (включая не только браузер, но и версию ОС, тип устройства, языковые настройки, панели инструментов и т.п.);

  • Часовой пояс;

  • Разрешение экрана и глубину цвета;

  • Supercookies;

  • Настройки куки;

  • Системные шрифты;

  • Плагины к браузеру и их версии;

  • Журнал посещений;

  • И другие данные.

Если говорить о статистике, то только раз на 286777 случаев случается полное совпадение отпечатков браузеров двух разных пользователей.

Согласно исследованию Browser Fingerprinting via OS and Hardware Level Features, точность идентификации пользователя при помощи отпечатка браузера составляет 99,24%.

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

Статья написана в соавторстве:
1. Меньшиков Ярослав
2. Беляев Дмитрий

Подробнее..

Перевод Как работает поиск изображений в Dropbox

27.05.2021 20:23:24 | Автор: admin

Если вам нужно найти фотографию, сделанную на пикнике несколько лет назад, вряд ли вы помните имя, которое камера автоматически присвоила файлу в момент съёмки, например, 2017-07-04 12.37.54.jpg.Вы просматриваете всё подряд фотографии, их эскизы, пытаетесь определить объекты или признаки искомого и не важно, ищете ли вы потерянное фото или хотите подыскать в архивах приличный снимок для презентации нового проекта.

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

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


Результаты поиска изображений по ключевому слову "пикник"Результаты поиска изображений по ключевому слову "пикник"

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

Наш метод

Формулируем задачу поиска изображений: необходимо найти функцию релевантности, которая принимала бы на входе (текстовый) запрос q и изображение j, а на выходе выдавала бы оценку релевантности s в баллах, показывающую, насколько точно изображение соответствует запросу:

s = f(q, j).

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

Классификация изображений

Классификатор изображений считывает изображение и выдаёт балльный список категорий, описывающих его содержание. Более высокие баллы указывают на большую вероятность того, что изображение относится к данной категории.

Категории могут быть следующими:

  • конкретные объекты на изображении, например, дерево или человек;

  • общие дескрипторы сцены, например, на улице или свадьба;

  • характеристики самого изображения, например, чёрно-белое или крупный план.

За последнее десятилетие был сделан огромный шаг вперёд в области в классификации изображений с помощью свёрточных нейронных сетей достаточно упомянуть замечательную работу 2012 года. Krizhevsky и др. на турнире ImageNet Сhallenge. С тех пор, благодаря усовершенствованию архитектуры моделей, улучшению методов обучения, большим базам данных, например Open Images или ImageNet, и простым в использовании библиотекам, таким как TensorFlow и PyTorch, исследователи создали классификаторы изображений, способные распознавать тысячи категорий. Оцените, насколько качественно сегодня работает классификация изображений:

Результаты применения классификатора изображений к типичной непостановочной фотографииРезультаты применения классификатора изображений к типичной непостановочной фотографии

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

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

Векторы слов

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

Получение векторных представлений текста предмет большого количества исследований в области обработки с использованием естественного языка. Один из наиболее известных методов в этой области word2vec описан в статье Mikolov и др. в 2013 году. Каждому слову в словаре функция Word2vec присваивает собственный вектор, и, исходя из этого, слова имеющие сходные значения, будут иметь близкие друг к другу векторы. Эти векторы находятся в d-мерном векторном пространстве слов, где d обычно составляет несколько сотен.

Узнать векторное представление поискового запроса можно, просто просмотрев его вектор word2vec. Этот вектор не является частью пространства категорий векторов классификатора изображений, однако можно преобразовать его в пространство категорий, ссылаясь на названия категорий изображений следующим образом:

  1. Для слова запроса q можно получить d-мерный вектор слов qw, нормализованный к единичному вектору. Для векторов в пространстве слов будем использовать нижний индекс w, а для векторов в пространстве категорий нижний индекс c.

  2. Для каждой категории получаем нормализованный вектор слов для названия категории ciw. Затем определяем значение mi = qw - ciw косинусное сходство векторов запроса и i-й категории. Полученная оценка от -1 до 1 показывает, насколько близко слово запроса соответствует названию категории. Сводя отрицательные значения к нулю (то есть применяя функцию mi = max(0, mi)), получаем оценку в том же диапазоне, что и результаты классификатора изображений.

  3. Таким образом, можно вычислить qc = [m1 m2 ... mC], вектор в C-мерном пространстве категорий, показывающий, насколько близко запрос соответствует каждой из категорий аналогично тому, как вектор классификатора изображений для каждого изображения показывает, насколько близко изображение соответствует каждой из категорий.

Шаг 3 осуществляем обычное векторно-матричное умножение, qc = qwC, где C матрица со столбцами векторов слов категории ciw.

После сопоставления запроса с вектором пространства категорий qc к вектору пространства категорий для каждого изображения можно применить косинусный коэффициент и получить таким образом окончательный балл релевантности для изображения s = qcjc.

Мы получили функцию релевантности. С её помощью мы ранжируем изображения побалльно и формируем список результатов запроса. Применение данной функции к набору изображений можно также выразить как векторно-матричное умножение, s = qcJ, в котором каждый столбец J представляет собой выходной вектор классификатора jc для изображения, а s вектор балльных оценок релевантности для всех изображений.

Пример

Рассмотрим пример с небольшим количеством измерений. В нём векторы слов имеют три измерения, а классификатор четыре категории: яблоко, пляж, одеяло и собака.

Предположим, пользователь искал берег. Просматриваем вектор слов, получаем [0,350,62 0,70], затем умножаем на матрицу векторов слов категорий и проецируем запрос на пространство категорий.

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

Проекция вектора слов запроса на пространство категорийПроекция вектора слов запроса на пространство категорий

Описание модели

Наш классификатор изображений представляет собой сеть EfficientNet, натренированную на наборе данных OpenImages. Классификатор выдаёт балльные оценки примерно по 8500 категориям. Мы выяснили, что данная архитектура и набор данных обеспечивают хорошую точность при разумных затратах. Это довольно важно, если приходится работать с клиентской базой Dropbox.

Для тренировки и запуска модели будем использовать TensorFlow и заранее обученные векторы слов ConceptNet Numberbatch. Полученные результаты оказались удовлетворительными, и, что важно для нас, модель способна работать с несколькими языками, возвращая сходные векторы для слов на разных языках со сходными значениями. Это упрощает процесс поиска изображений на разных языках: векторы слов dog на английском языке и chien на французском сходны, поэтому мы можем осуществлять поиск на обоих языках без перевода поискового запроса с одного языка на другой.

Для поиска по нескольким словам будем анализировать запрос как логическую операцию AND, применённую к отдельным словам. Мы также ведём перечень терминов, состоящих из нескольких слов, например beach ball, которые можно рассматривать как одно слово. Если запрос содержит один из таких терминов, мы делаем альтернативный синтаксический разбор и применяем операцию OR для двух разобранных запросов, после чего запрос beach ball принимает вид (beach AND ball) OR (beach ball). Этот термин будет подходить для поиска как больших разноцветных надувных мячей для игры на пляже, так и для теннисных мячей на песке.

Архитектура в производственной среде

Каждый раз, когда пользователь выполняет поиск, получать полную и актуальную матрицу J не вполне практично. Пользователь может иметь доступ к сотням тысяч или даже миллионам изображений, а наш классификатор способен работать с тысячами измерений, поэтому такая матрица может содержать миллиарды записей и должна обновляться каждый раз, когда пользователь добавляет, удаляет или изменяет изображение. Для сотен миллионов пользователей такой подход просто нереализуем (пока).

Поэтому вместо создания экземпляра J для каждого запроса мы используем описанный выше приближённый метод, который может быть эффективно реализован на поисковом движке Dropbox Nautilus.

По сути, Nautilus состоит из прямого индекса (forward index), сопоставляющего каждый файл с определёнными метаданными (например, именем файла) и полным текстом файла, и инвертированного индекса (inverted index), сопоставляющего каждое слово со списком публикаций (posting list) для всех файлов, содержащих это слово. При текстовом поиске содержимое индекса для нескольких файлов с рецептами может выглядеть примерно так:

Содержание поискового индекса для текстового поискаСодержание поискового индекса для текстового поиска

Если пользователь ищет белое вино, мы ищем эти два слова в инвертированном индексе и обнаруживаем в doc_1 и doc_2 оба слова, поэтому мы должны включить их в результаты поиска. В doc_3 есть только одно из этих слов, поэтому мы должны либо опустить его, либо поставить последним в списке результатов.

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

Применение методов текстового поиска для поиска изображений

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

Содержание поискового индекса для поиска изображений по содержимомуСодержание поискового индекса для поиска изображений по содержимому

Предположим, пользователь ищет слово пикник:

  1. Находим вектор qw для слова пикник и умножаем его на матрицу проекции пространства категорий C и получаем qc, как описано выше. C это фиксированная матрица, одинаковая для всех пользователей, поэтому её можно хранить в памяти.

  2. Для каждой категории, имеющей ненулевую запись в qc, получаем список идентификаторов из инвертированного индекса. Объединение таких списков представляет собой набор совпадающих изображений, полученных в результате поиска, однако эти результаты ещё необходимо ранжировать.

  3. Для каждого результата поиска возьмём вектор пространства категорий jc из прямого индекса и умножим его на qc, чтобы получить балльную оценку релевантности s. Получаем результаты с балльной оценкой выше порогового значения, ранжированные по этой балльной оценке.

Оптимизация для масштабируемости

Описанный подход по-прежнему связан со значительными затратами как с точки зрения пространства для хранения данных, так и с точки зрения времени обработки запросов. Для 10000 категорий в прямом индексе для каждого изображения необходимо хранить 10000 балльных оценок классификатора, что при использовании четырёхбайтовых значений с плавающей запятой составляет 40 КБ. А поскольку оценки классификатора редко когда бывают строго нулевыми, в большинство из таких 10000 списков идентификаторов будет добавлено стандартное изображение. Если для идентификаторов изображений использовать четырёхбайтовые целые числа, добавятся ещё 40 КБ и стоимость индексирования одного изображения составит 80 КБ. Другими словами, для многих изображений их индекс будет больше по размеру, чем сам файл изображения!

Что касается времени обработки запроса, под которым понимается задержка, с которой система выдаёт пользователю результаты поиска, мы можем ожидать, что около половины балльных оценок совпадения запроса и категории mi будут положительными, поэтому нам придётся прочитать около 5000 списков идентификаторов из инвертированного индекса. Если сравнивать с текстовыми запросами, то они обычно считывают всего около 10 списков публикаций.

К счастью, существует множество близких к нулю значений, которые вполне можно отбросить, и это не повлияет качество поиска. Оценка релевантности для каждого изображения была представлена выше как s = qcjc, где qc балльная оценка соответствия между запросом и каждой из примерно 10000 категорий, а jc балльные оценки приблизительно 10000 категорий, полученные с помощью классификатора. Оба вектора в основном состоят из близких к нулю значений, которые не вносят практически никакого вклада в s.

В нашем приближении мы определим значения всех записей qc и jcкак равные нулю, кроме нескольких самых больших. Экспериментально было выяснено, что сохранение 10 записей в qc и 50 записей в jc вполне достаточно для получения не менее содержательного итогового результата. Экономия на хранении и обработке данных получилась довольно существенной:

  • В прямом индексе вместо 10000-мерных плотных векторов мы храним разрежённые векторы с 50 ненулевыми записями, то есть с 50 записями с наиболее высокими балльными оценками категорий для каждого изображения. В разрежённом представлении мы храним позицию и значение каждой ненулевой записи; 50 двухбайтовых позиций (записанных целыми числами) и 50 четырёхбайтовых значений (записанных в виде числа с плавающей точкой) требуют всего около 300 байт.

  • В инвертированном индексе каждое изображение добавляется не в 10000, а в 50 списков идентификаторов, что обходится всего в 200 байт. Таким образом, общий размер индекса для каждого изображения составляет 500 байт вместо 80КБ.

  • Во время запроса qc имеет 10 ненулевых записей, поэтому нужно просканировать только 10 списков идентификаторов примерно такой же объём работы выполняется в текстовых запросах. Набор результатов сужается, и такой набор можно быстрее оценить.

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

Доступность функций

Функция поиска в изображениях в настоящее время доступна для всех наших пользователей профессионального и бизнес-уровня. Объединяя функции поиска в любых изображениях, OCR-поиска в изображениях и документах и полнотекстового поиска в текстовых документах, мы делаем большинство файлов пользователей доступными для поиска на основе содержимого.

Поиск в видео?

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

Искусственный интеллект сегодня экономит не только время, но и силы, которые направляются на решение всё более сложных задач. И если вам интересно решать задачи в области искусственного интеллекта, вы можете обратить внимание на наш курс "Machine Learning и Deep Learning", разработанный совместно с компанией NVIDIA.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Перевод У каждого приложения должна быть палитра команд

20.05.2021 12:07:50 | Автор: admin
В старых и новых приложениях незаметно начинает появляться инструмент, упрощающий взаимодействие и ускоряющий выполнение действий. Это мощное поле поиска, которое я называю power bar; иногда оно имеет название command palette.

Power bar, похожая на поиск Spotlight в macOS, встраивается в приложение и обычно вызывается сочетанием горячих клавиш CMD+K (или CMD+SHIFT+P). После её вызова пользователь вводит в неё то действие, которое хочет выполнить. Однако в отличие от Spotlight, power bar позволяет выполнять задачи, а не просто искать файлы или переходить в другие части приложения.


Command palette приложения Superhuman.

В таких приложениях, как, например, в клиенте электронной почты Superhuman, power bar позволяет выполнять всё, что можно сделать нажатием на кнопку, не убирая пальцев с клавиатуры. Нажать сочетание горячих клавиш в письме внутри Superhuman, ввести schedule (запланировать), нажать на Enter и ввести next week (следующая неделя) гораздо быстрее, чем искать и нажимать кнопки в интерфейсе, если вы знаете, как пользоваться этой возможностью.

Правильно спроектированная power bar позволяет пользователям перемещаться по всему приложению, не прикасаясь к мыши, а для нахождения задачи она не требует точного совпадения поисковой фразы. При поиске snooze (перенести) или later (позже) вместо schedule (запланировать) должны выдаваться те же результаты, потому что люди используют для одной задачи разные слова, особенно когда ещё не освоили приложение. Самое важное, чтобы в результатах поиска power bar отображались практически все задачи, которые можно выполнить в приложении; таким образом она будет полезна всегда, а не только в отдельных случаях.


Power bar в приложении Visual Studio Code.

В последние годы power bar всё чаще начали появляться во всевозможных приложениях. Я находил их и в инструменте для создания слайд-шоу Pitch, и в календаре Cron, и в более сложных инструментах наподобие Visual Studio Code, системы отслеживания ошибок Linear, и даже в Adobe Photoshop, а также во множестве других мест. Подозреваю, что они появляются повсюду потому, что сегодня подавляющее большинство людей привыкло задавать голосовым помощникам открытые вопросы наподобие Какая погода?, а power bar предоставляют аналог такой функциональности в отдельном приложении.

Строго говоря, power bar не является новой концепцией. Как пишет Мэттью Гуай в Capiche, потребовалось чуть меньше десятка лет для того, чтобы функция, добавленная разработчиком Джоном Скиннером во вторую версию Sublime Text, стала одной из важнейших новых функций программного обеспечения этого десятилетия.


Приложение-календарь Cron использует power bar.

По сути, изначально power bar появилась как инструмент, оптимизированный под опытных пользователей, например, инженеров, не желающих убирать руки от клавиатуры, но теперь начинает появляться повсюду, потому что даёт людям возможность быстрее выполнять их задачи без изучения документации или поисков в Google Search. Ещё важнее то, что она позволяет благодаря текстовому вводу найти горячие клавиши или функции, о которых пользователь даже не знал.

Больше всего в тенденции популяризации power bar меня восхищает то, что она помогает превращать людей в опытных пользователей, знающих, как пользоваться сложными функциями приложений гораздо быстрее, чем если бы им пришлось изучать их самим. Также power bar позволяют не забредать в тупики; если вы обыскали весь интерфейс пользователя, но не нашли нужной функции, power bar поможет вам при помощи простого текстового поиска.

Так как мы создаём и проектируем приложения, которые постепенно становятся всё сложнее, одна из самых сложных задач заключается том, чтобы помочь людям найти мощные функции, о которых они могли не знать. Подобные функции скрываются в меню, за таинственными значками или в раскрывающихся панелях под кнопкой "". Если вы не знаете, где находится то действие, которое вы хотите выполнить, то достаточно просто нажать CMD+K и ввести его, а не заниматься бесконечным щёлканьем.

Хорошо спроектированная power bar поможет найти нужный инструмент, даже если слова запроса совпадают не полностью, позволяя человеку добраться до функции и научить его сочетанию горячих клавиш для выполнения задачи в будущем.

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

Но как только люди освоят этот инструмент, они обретут уверенность и, скорее всего, начнут искать power bar в каждом инструменте, считая её стандартным паттерном.

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



На правах рекламы


Наши эпичные серверы хоть и не подходят для майнинга, но могут использоваться для любых других задач. Надёжные серверы на Linux или Windows с мощными процессорами семейства AMD EPYC и очень быстрой файловой системой, используем исключительно NVMe диски от Intel. Попробуйте как можно быстрее!

Присоединяйтесь к нашему чату в Telegram.

Подробнее..

Устройство поисковых систем базовый поиск и инвертированный индекс

21.03.2021 12:21:06 | Автор: admin


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

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


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

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

Определение релевантности


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

Основной проблемой в векторной модели поиска является построение векторного пространства $V$ и преобразования документов и запросов в $V$. Вообще говоря, векторные пространства и преобразования могут быть любыми, лишь бы близкие по смыслу документы или запросы отображались в близкие вектора.

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

  • Берем любимую библиотеку построения эмбеддингов для текстов, например fastText или BERT, преобразуем документы в вектора
  • Складываем полученные вектора в любимую библиотеку для поиска K ближайших соседей (k-NN) к данному вектору, например в faiss
  • Поисковый запрос преобразовываем в вектор тем же методом, что и документы
  • Находим ближайшие вектора к запросу-вектору и извлекаем соответствующие найденным векторам документы

Поиск, основанный на k-NN, будет очень медленным, если вы попытаетесь засунуть в него весь Интернет. Поэтому мы сузим определение релевантности так, чтобы всё стало вычислительно проще.

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

NB.: Здесь и далее слова в контексте документов и запросов будут называться термами с целью избежания путаницы

Запишем релевантность в виде двух математических функций и далее будем наполнять их содержанием:

  • $score(q, d)$ релевантность документа запросу
  • $score(t, d)$ релевантность документа одному терму

На $score(q, d)$ наложим ограничение аддитивности и выразим релевантность запроса через сумму релевантностей термов:

$score(q, d)=\sum_{t \in q}score(t, d)$

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

Наиболее известные аддитивные функции релевантности TF-IDF и BM25. Они используются в большинстве поисковых систем как основные метрики релевантности.

Откуда взялись TF-IDF и BM25


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

И TF-IDF, и BM25 показывают степень релевантности документа запросу одним числом. Чем выше значение метрик, тем более релевантен документ. Сами по себе значения не имеют какой-либо значимой интерпретации. Важно только сравнение значений функций для различных документов. Один документ более релевантен данному запросу, чем другой, если значение его функции релевантности выше.

Попробуем повторить рассуждения авторов формул и воспроизвести этапы построения TF-IDF и BM25. Обозначим размер корпуса проиндексированных документов как $N$. Самое простое, что можно сделать это определить релевантность равной количеству вхождений терма (termFrequency или $tf$) в документ:

$score(t, d)=tf(t, d)$

Что делать, если у нас не один терм $t$, а запрос $q$, состоящий из нескольких термов, и мы хотим посчитать $score(q, d)$ запроса для этого документа? Вспоминаем про ограничение аддитивности и просто суммируем все отдельные $score(t, d)$ по термам из запроса:

$score(q, d)=\sum_{t \in q}score(t, d)$

В формуле выше есть проблема мы не учитываем различную важность разных термов. Если у нас будет запрос cat and dog, то наиболее релевантными окажутся документы, в которых есть 100500 вхождений терма and. Вряд ли это то, что хотел бы получить пользователь.

Исправляем проблему, взвешивая каждый терм в соответствии с его важностью:

$score(t, d)=\frac{tf(t, d)}{df(t)}$

$df(t)$ это количество документов в корпусе, содержащих терм $t$. Получается, что чем чаще терм встречается, тем менее он важен и тем меньше будет $score(t, d)$. Термы вроде and будут иметь огромный $df(t)$ и соответственно маленький $score(t, d)$.

Вроде уже лучше, но теперь есть другая проблема сам по себе $df(t)$ мало о чём говорит. Если $df(жираф) = 100$, и размер корпуса проиндексированных текстов 100 документов, то терм жираф в этом случае считается очень частым. А если размер корпуса 100 000, то уже редким.

Зависимость $df(t)$ от $N$ может быть убрана превращением $df(t)$ в относительную частоту путем деления на $N$:

$score(t, d)=\frac{tf(t, d)}{\frac{df(t)}{N}}=tf(t, d)\frac{N}{df(t)}$

Теперь представим следующее у нас 100 документов, в одном из них есть терм слон, в двух жираф. $\frac{N}{df(t)}$ в первом случае будет равно 100, а во-втором 50. Терм жираф получит в два раза меньше очков, чем терм слон только лишь потому, что документов с жирафом на один больше, чем со слоном. Исправим эту ситуацию, сгладив функцию $\frac{N}{df(t)}$.

Сглаживание можно произвести различными способами, мы сделаем это логарифмированием:

$score(t, d) = tf(t, d)\log\frac{N}{df(t)}$

Мы только что получили TF-IDF. Едем дальше к BM25.

Вряд ли документ, содержащий терм жираф 200 раз в два раза лучше, чем документ, содержащий терм жираф 100 раз. Поэтому и тут проедемся сглаживанием, только теперь сделаем это не логарифмированием, а чуть иначе. Заменим $tf(t, d)$ на $tf_s(t, d) = \frac{tf(t, d)}{tf(t, d) + k}$ С каждым увеличением числа вхождения терма $tf(t, d)$ на единицу, значение $tf_s(t, d)$ прирастает все меньше и меньше функция сглажена. А параметром $k$ мы можем контролировать кривизну этого сглаживания. Говоря по-умному, параметр $k$ контролирует степень сатурации функции.


Рис. 0: Чем выше значение $k$, тем сильнее будут учитываться последующие вхождения одного и того же терма.

У $tf_s(t, d)$ есть два замечательных побочных эффекта.

Во-первых, $score(q, d)$ будет больше у документов, содержащих все слова из запроса, чем у документов, которые содержат одно слово из запроса несколько раз. Топ документов в этом случае будет больше радовать глаз и ум пользователя, ведь все термы запроса обычно печатаются не просто так.

Во-вторых, значение функции $tf_s(t, d)$ ограничено сверху. Остальная часть $score(t, d)$ тоже ограничена сверху, поэтому и вся функций $score(t, d)$ имеет ограничение сверху (далее $UB_t$ upper bound). Более того, $UB_t$ в нашем случае очень просто посчитать.

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

Последний шаг начнем учитывать длину документов в $score(t, d)$. В длинных документах терм жираф может встретится просто по-случайности и его наличие в тексте ничего не скажет о реальной теме документа. А вот если документ состоит из одного терма и это терм жираф, то можно совершенно точно утверждать, что документ о жирафах.

Очевидный способ учесть длину документа взять количество слов в документе $dl(d)$. Дополнительно поделим $dl(d)$ на среднее количество слов во всех документах $dl_{avg}$. Сделаем мы это исходя из тех же соображений, из каких нормировали $df(t)$ выше абсолютные значения портят качество метрики.

Найдем теперь место для длины документа в нашей формуле. Когда $k$ растет, то значение $tf_s$ падает. Если мы будем умножать $k$ на $\frac{dl(d)}{dl_{avg}}$, то получится, что более длинные документы будут получать меньший $score(t, d)$. То что нужно!

Можно еще дополнительно параметризовать силу, с которой мы учитываем длину документа, для контроля поведения формулы в разных ситуациях. Заменим $\frac{dl(d)}{dl_{avg}}$ на $1 b + b\frac{dl(d)}{dl_{avg}}$ и получим:

$\frac{tf_s(t, d)}{tf_s(t, d) + k(1 - b + b\frac{dl(d)}{dl_{avg}})}$

При $b = 0$ формула вырождается в $\frac{tf_s(t, d)}{tf_s(t, d) + k}$, а при $b = 1$ формула принимает вид $\frac{tf_s(t, d)}{tf_s(t, d) + k\frac{dl(d)}{dl_{avg}}}$.

Ещё раз: $k$ сила влияния повторяющихся термов на релевантность, а $b$ сила влияния длины документа на релевантность.

Подставим $tf$ в $tf_s$:

$score(q, d)=\sum_{t \in q} \frac{tf(t, d) (k + 1)}{tf(t, d) + k(1 - b + b\frac{dl(d)}{dl_{avg}})} * \log\frac{N}{df(t)}$

У нас получилась формула BM25 с небольшим нюансом. В каноничной формуле $\log\frac{N}{df(t)}$ (этот член называется $IDF$) заменен на $\log\frac{N - df(t) + 0.5}{df(t) + 0.5}$. Замена не имеет простых эвристик под собой и основана на подгонке под теоретически более чистую форму RSJ модели. Такая форма $IDF$ дает меньший вес слишком часто встречающимся термам: артиклям, союзам и прочим сочетаниям букв, несущим малое количество информации.

Важное замечание: из формулы BM25 теперь видно, что $UB_t$ в бОльшей мере зависит от значения $IDF$, то есть от частоты терма в корпусе. Чем реже встречается терм, тем выше его максимально возможный вклад в $score(q, d)$.

Реализация инвертированного индекса


С учетом ограниченной памяти, медленных дисков и процессоров нам теперь нужно придумать структуру данных, способную выдавать top-K релевантных по BM25 документов.

Есть у нас набор документов, по которым необходимо вести поиск. Всем документам присваивается document ID или DID. Каждый документ разбивается на термы, термы при желании обрезаются или приводятся к каноничной форме. Для каждого обработанного терма составляется список DID документов, содержащих этот терм постинг-лист.

terms-and-posting-lists

Рис. 1: Постинг-листы

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

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


Рис. 2: Словарь термов (префиксное дерево)

В Tantivy для хранения термов вообще использованы finite-state transducers через crate fst. Если совсем упрощать, то можно считать, что префиксные деревья организуют словарь путем выделения общих префиксов у ключей, а трансдюсеры ещё могут и общие суффиксы выделять. Поэтому сжатие словаря происходит эффективнее, только в итоге получается уже не дерево, а ациклический орграф.

Библиотека fst в крайних случаях может сжимать даже лучше алгоритмов компрессии общего назначения при сохранении произвольного доступа. Крайние случаи случаются, когда большая часть ваших термов имеет длинные общие части. Например, когда вы складываете в инвертированный индекс URLы.

Ещё в fst есть методы сериализации и десериализации словаря, что сильно облегчает жизнь складывать руками графы и деревья на диск то ещё развлечение. И в отличии от хеш-таблиц, fst позволяет использовать подстановку (wildcard) при поиске по ключам. Говорят, некоторые таинственные люди пользуются звездочкой в поисковых запросах, но я таких не видел.

Используя словарь термов и постинг-листы можно для запроса из одного одного терма $t$ определить список документов, в котором этот терм появляется. Затем останется посчитать $score(t, d)$ для каждого документа из постинг-листа и взять top-K документов.

Для этого перенесем $score(t, d)$ из области математики в реальный мир. В Tantivy используется BM25, как один из вариантов функции релевантности:

$score(t, d)=\sum_{t \in q} \frac{tf(t, d) (k + 1)}{tf(t, d) + k(1 - b + b\frac{dl(d)}{dl_{avg}})} * \log\frac{N - df(t) + 0.5}{df(t) + 0.5}$

  • $tf(t, d)$ подсчитываем количество DID документа в постинг-листе терма $t$, либо храним отдельным числом, что ускорит весь процесс за счет использования дополнительной памяти. Tantivy использует последний вариант: упаковывает DID в блоки по 128 чисел, а затем пишет блок из 128 частот термов, используя bitpack-кодировку
  • $df(t)$ длина всего постинг-листа
  • $dl_{avg}$ рассчитываем на основе двух статистик, общего количества документов в индексе и суммарной длины всех постинг-листов. Обе статистики поддерживаются инвертированным индексом в актуальном состоянии при добавлении нового документа
  • $dl(d)$ храним для каждого документа в отдельном списке

Кроме словаря термов, постинг-листов и длин документов Tantivy (и Lucene) сохраняет в файлах:

  • Точные позиции слов в документах
  • Скип-листы для ускорения итерирования по спискам (об этом дальше)
  • Прямые индексы, позволяющие извлечь сам документ по его DID. Работают прямые индексы медленно, так как документы хранятся сжатыми тяжелыми кодеками типа brotli
  • FastFields встроенное в инвертированный индекс быстрое KV-хранилище. Его можно использовать для хранения внешних статистик документа а-ля PageRank и использовать их при расчете вашей модифицированной функции $score(q, d)$

Теперь, когда мы можем посчитать $score(q, d)$ для запроса из одного терма, найдем top-K документов. Первая идея посчитать для всех документов их оценки, отсортировать по убыванию и взять К первых. Потребуется хранить всё в RAM и при большом количестве документов у нас кончится память.

Поэтому при обходе постинг-листа от начала и до конца первые $K$ документов кладутся в кучу (далее top-K heap) безусловно. А затем каждый последующий документ сначала оценивается и кладется в кучу только если его $score(q, d)$ выше минимального $score(q, d)$ из кучи. Текущий минимум в top-K heap далее будет обозначен как $\theta$.

Операции над постинг-листами для запросов из нескольких термов


Что сделает инвертированный индекс с запросом скачать OR котики? Он заведет два итератора по постинг-листам для термов скачать и котики, начнет итерирование по обоим листам, попутно рассчитывая $score(q, d)$ и поддерживая top-K heap.

Аналогичным образом реализуется AND-запрос, однако тут итерирование позволяет пропускать значительные части постинг-листов без расчета $score(q, d)$ для них.

Более важными для поисковиков общего назначения являются OR-запросы. А всё потому, что они покрывают больше документов и потому, что ранжирование запросов метриками TF-IDF или BM25 всё равно поднимает в топ документы с бОльшим количеством совпавших слов. Это сделает top-K документов больше похожим на результат работы AND-запроса.

Наивный алгоритм реализации OR-запроса следующий:

  1. Создаем итераторы для постинг-листов каждого терма из запроса
  2. Заводим top-K heap
  3. Сортируем итераторы по DID, на которые они указывают
  4. Берем документ, на который указывает первый итератор и собираем среди оставшихся итераторов те, которые указывают на тот же документ. Так мы получим DID и термы, которые содержатся в этом документе
  5. Рассчитываем релевантность документа по термам, складываем их, получаем релевантность по всему запросу. Если документ хороший, то кладем его в top-K heap
  6. Продвигаем использованные итераторы и возвращаемся к п.3



Рис. 3: Итерации OR-алгоритма. Чуть ниже есть псевдокод алгоритма

В п.4 сбор итераторов осуществляется быстро, так как список итераторов отсортирован по DID. Пересортировку итераторов в п.3 тоже можно оптимизировать, если мы знаем какие итераторы были продвинуты в п.6.

Некоторые оптимизации инвертированного индекса


В обычной задаче поиска ищутся не вообще все релевантные документы, а только K наиболее релевантных. Это открывает путь для важных оптимизаций. Причина простая большая часть документов станет ненужной и мы избежим накладных вычислений над ней. Такая постановка задачи ещё известна как Top-K queries.

Посмотрим внимательнее на псевдокод OR-алгоритма Bortnikov, 2017:
Input:  termsArray - Array of query terms  k - Number of results to retrieveInit:  for t  termsArray do t.init()  heap.init(k)    0  Sort(termsArray) Loop:   while (termsArray[0].doc() < ) do    d  termsArray[0].doc()    i  1    while (i < numTerms  termArray[i].doc() = d) do      i  i + 1    score  Score(d, termsArray[0..i  1]))    if (score  ) then         heap.insert(d, score)    advanceTerms(termsArray[0..i  1])     Sort(termsArray)Output: return heap.toSortedArray()function advanceTerms(termsArray[0..pTerm])   for (t  termsArray[0..pTerm]) do    if (t.doc()  termsArray[pTerm].doc()) then       t.next()

Наивный алгоритм работает с асимптотикой $O(LQ\log{Q})$, где L суммарная длина используемых при обработке запроса постинг-листов, а Q количество слов в запросе. Немного обнаглев, из оценки можно выкинуть $Q\log{Q}$ подавляющее большинство пользователей приносит запросы не длиннее какого-то максимума и можно считать $Q\log{Q}$ константой.

На практике, наибольший вклад в скорость работы инвертированного индекса вносит размер корпуса (т.е длины постинг-листов) и частота запросов. Включенное автодополнение запроса или внутренние аналитические запросы в поисковую систему способны кратно умножить нагрузку на систему. Даже $O(L)$ в такой ситуации оказывается слишком грустной оценкой.

Сжатие постинг-листов


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

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

VarLen Encoding

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

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

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

SIMD

Для возможности параллельного чтения изобрели компромиссные схемы, похожие на varint, но не совсем. В таких схемах числа разбиваются на группы по N чисел и каждое число в группе кодируются одинаковым количеством бит, а вся группа предваряется дескриптором, описывающим что в группе находится и как это распаковать. Одинаковая длина запакованных чисел в группе позволяет использовать SIMD-инструкции (SSE3 в Intel) для распаковки групп, что кратно ускоряет время работы.

Delta-encoding

Varint хорошо сжимает малые числа и хуже сжимает большие числа. Так как в постинг-листе находятся возрастающие числа, то с добавлением новых документов качество сжатия будет становиться хуже. Простое изменение в постинг-листе будем хранить не сами DID, а разницу между соседними DID. Например, вместо [2, 4, 6, 9, 13] мы будем сохранять [2, 2, 2, 3, 4].

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

Скип-листы для итерирования по постинг-листам


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

Есть такая замечательная штука скип-листы. Скип-лист живет рядом со связанным списком чисел и представляет из себя разреженный индекс по содержанию этого списка. Если вы хотите в списке чисел найти Х, то скип-лист за время $O(log(L))$ пояснит вам, куда именно надо прыгнуть, чтобы оказаться в вашем связанном списке примерно в нужном месте перед Х. После прыжка вы уже обычным линейным поиском идете до Х.

Точность прыжка зависит от объема памяти, который мы можем выделить под скип-лист типичный компромисс в алгоритмах. В Tantivy перемещение вдоль постинг-листа реализовано именно скип-листами. Известна lock-free реализация скип-листа, но на момент написания статьи (март 2021) библиотека выглядит не слишком поддерживаемой.

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

Оптимизации OR-запросов


Все оптимизации обхода постинг-листов делятся на безопасные и небезопасные. В результате применения безопасных оптимизаций top-K документов остается без изменений по сравнению с наивным OR-алгоритмом. Небезопасные оптимизации могут дать большой выигрыш по скорости, но они меняют top-K и могут пропустить некоторые документы.

MaxScore

MaxScore одна из первых известных попыток ускорить выполнение OR-запросов. Оптимизация относится к безопасным, описана в Turtle, 1995.

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

Помните $UB_t$ терма, введеный в разделе про TF-IDF и BM25? Напоминаю, что это максимально возможный вклад терма в релевантность любого запроса. $UB_t$ является функцией от $IDF$ и рассчитывается на лету.

Имея на руках $UB_t$, можно отсортировать все термы из запроса по убыванию их $UB_t$ и посчитать частичные суммы $UB_t$ от первого и до последнего терма. Все термы с частичной суммой меньше текущего $\theta$ можно отнести к необязательному множеству. Документ, содержащий термы только из необязательного множества не может быть оценен выше, чем сумма $UB_t$ этих документов. Стало быть, такой документ не войдет в итоговое множество.

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


Рис. 4: Промотка итераторов в MaxScore

После каждого обновления top-K heap производится перестроение двух множеств и алгоритм завершает работу в момент, когда все термы оказываются в необязательном множестве.

Weak AND (WAND)

WAND также является безопасным методом оптимизации поиска, описанным в Broder, 2003. В чем-то он похож на MaxScore: также анализирует частичные суммы $UB_t$ и $\theta$.

  1. Все итераторы термов WAND сортирует в порядке DID, на который указывает каждый итератор
  2. Рассчитываются частичные суммы $UB_t$
  3. Выбирается pivotTerm первый терм, чья частичная сумма превосходит $\theta$
  4. Проверяются все предшествующие pivotTerm'у итераторы.

    Если они указывают на один и тот же документ, то этот документ теоретически может входить в top-K документов и поэтому для него производится полноценный рассчет $score(q, d)$.

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

    После этого, мы возвращаемся на первый шаг алгоритма


Block-max WAND (BMW)

BMW является расширением алгоритма WAND из предыдущего пункта, предложенным в Ding, 2011. Вместо использования глобальных $UB_t$ для каждого терма, мы теперь разбиваем постинг-листы на блоки и сохраняем $UB$ отдельно для каждого блока. Алгоритм повторяет WAND, но проверяет в дополнение еще и частичную сумму $UB$ блоков, на которые сейчас указывают итераторы. В случае, если эта сумма ниже $\theta$, то блоки пропускаются.

Блочные оценки $UB$ термов в большинстве случаев гораздо ниже глобальных $UB_t$. Поэтому многие блоки будут скипнуты и это позволит сэкономить время на расчете $score(q, d)$ документов.

Для понимания разрыва между продакшеном инвертированных индексов и академическими исследованиями можно занырнуть в широко известный в узких кругах тикет LUCENE-4100.

TLDR: Реализация важной Block-max WAND-оптимизации молчаливо дожидалась смены TF-IDF на BM25, заняла 7 лет и была выкачена только в Lucene 7.

Block Upper Scoring

Рядом с $UB$ для блока можно хранить другие метрики, помогающие принять решение не трогать этот блок. Либо сам $UB$ поправить нужным образом так, чтобы его значение отражало вашу задумку.

Автор статьи экспериментировал с поиском, в котором необходимы были только свежие документы-новости. BM25 была заменена на $BM25D(q, d, time) = BM25(q, d) * tp(time)$ где $tp$ функция, накладывающая пенальти на устаревающие документы и принимающая значения от 0 до 1. Поменяв формулу для $UB$, удалось добиться пропуска 95% всех блоков с несвежими новостями, что сильно ускорило конкретно этот вид поиска. Сам подход с хранением поблочных метрик, а также вычислимым и конечным пределом функции релевантности располагает к экспериментам.

Предварительная обработка поискового запроса


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

Разбор поискового запроса



Рис. 5: Этапы обработки поискового запроса

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

Будут ли токены соединены по умолчанию оператором OR или AND зависит от настроек индекса. С одной стороны соединение через AND иногда может дать более точный результат, с другой есть риск вообще ничего не найти. Можно составить несколько запросов и после их выполнения на основе размера выдачи или внешних метрик выбрать лучший вариант.

Логическое дерево ложится в основу плана операций. В Tantivy соответствующая структура называется Scorer. Реализация Scorer является центром вселенной инвертированных индексов, так как эта структура ответственна за итерирование постинг-листов и за все возможные оптимизации этого процесса.

Этап расширения поискового запроса


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

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

На этапе расширения поисковых запросов дешево проводить эксперименты. Представьте, что вы хотите выяснить, даст ли вам какой-либо профит использование морфологии при поиске. Морфология в этом контексте приведение разных словоформ к каноничной форме (лемме).

Например:
скачивать, скачать, скачал, скачали -> скачатькотики, котиков, котикам -> котик

У вас есть несколько вариантов:

  • Тяжелый: сразу попытаться научить инвертированный индекс хранить и леммы, и словоформы, а также научиться учитывать их при поиске. Нужно много программировать и проводить переиндексацию корпуса документов.
  • Компромиссный: переиндексировать весь корпус документов, приводя все словоформы к леммам на лету, а также лемматизировать приходящие запросы. Меньше программирования, но все так же требуется переиндексация.
  • Простой: разбавлять запрос всеми словоформами. В таком случае запрос пользователя скачка котиков будет преобразован во что-то типа "(скачка^2 OR скачать OR скачивать OR скачал) AND (котиков^2 OR котики OR котик OR котикам)". Выдача будет выглядеть так, как будто бы мы умеем по-настоящему работать с леммами. Содержимое инвертированного индекса менять не требуется!

Все гипотетические затраты процессора на переиндексацию благодаря простому подходу будут перенесены на этап query extension и на обработку такого расширенного запроса. Это сэкономит вам кучу времени разработки. Fail often, fail fast!

Запись и сегментирование индекса


В архитектуре Lucene инвертированный индекс нарезан на сегменты. Сегмент хранит свою часть документов и является неизменяемым. Для добавления документов мы собираем в RAM новые документы, делаем commit и в этот момент документы из RAM сохраняются в новый сегмент.

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

Сегменты могут обрабатывать запросы одновременно, поэтому сегмент является естественной единицей распараллеливания нагрузки. Сложность здесь возникает только в слиянии результатов из разных сегментов, так как нужно выполнять N-Way Merge потоков документов от каждого сегмента.

Тем не менее, много маленьких сегментов плохо. А такое иногда случается, когда запись ведется небольшими порциями. В таких ситуациях Tantivy запускает процедуру слияния сегментов, превращая много маленьких сегментиков в один большой сегмент.

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

Шардирование


Существует два способа распараллеливания нагрузки на инвертированный индекс: по документам или по термам.

В первом случае каждый из N серверов хранит только часть документов, но является сам по себе полноценным мини-индексом, во втором хранит только часть термов для всех документов. Ответы шардов во втором случае требуют дополнительной нетривиальной обработки.
По документам По термам
Нагрузка на сеть Маленькая Большая
Хранение дополнительных аттрибутов для документа Просто Сложно
Disk-seek'ов для запроса из K слов на N шардах O(K*N) O(K)

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

Поскольку в инвертированном индексе перестроение сегментов является сложной операцией, лучше сразу начать использовать схемы типа Ring или Jump Consistent Hashing для снижения объемов перешардируемых документов при открытии нового шарда.

Многофазовые поиски и ранжирование


В поисковых системах обычно выделяются две части: базовые поиски и мета-поиск. Базовый поиск ищет по одному корпусу документов. Мета-поиск делает запросы в несколько базовых поисков и хитрым способом сливает результаты каждого базового поиска в единый список.

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

Первая фаза двухфазовых (или даже многофазовых) поисков делает всё тоже самое. А вот на второй фазе происходит переранжирование top-K документов из первой фазы с использованием более тяжелых для вычисления метрик. Такое деление оправдано, поскольку отделяет быструю первую фазу на всем множестве документов от тяжелой второй фазы на ограниченном множестве документов.

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

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

Первая фаза ранжирования


Метрика соответствия документа запросу может быть очень простой, например $BM25$, или чуть более сложной, например $BM25 * f(IMP)$, где $IMP$ статическое качество документа, а $f$ произвольное отображение с областью значений $[0; 1]$.

Ограничение на $f$ в этом случае произрастает из-за использованных оптимизаций в индексе типа BMW, которые не позволяют модифицировать $score(t, d)$ в большую сторону без изменения сохраненных блочных $UB$.

BM25 высчитывается в процессе работы с постинг-листами, а вот дополнительные члены типа $IMP$ должны лежать рядом с инвертированным индексом. Это первое существенное ограничение первой фазы поиска. Через функцию $score(q, d)$ в общем случае проходит слишком много документов, чтобы была возможность на каждый из них бегать куда-то во внешние системы за дополнительными атрибутами документа.

В веб-поиске роль члена $IMP$ обычно исполняет PageRank, рассчитываемый раз в N дней на больших кластерах MapReduce. Посчитанный PageRank записывается в быстрое KV-хранилище инвертированного индекса, такое как FastFields в случае Tantivy и используется при вычислении релевантности.

Документы из других источников могут иметь другие метрики. Например, для поиска по научным статьям имеет смысл использовать импакт-фактор или индекс цитирования.

Вторая фаза ранжирования


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

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

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

Качество поиска


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

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

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

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

Далее пара метрик, с которых вам стоит начать. Они просто формулируется даже в терминах SQL-запроса, особенно если у вас что-то типа Clickhouse для хранения логов.

Success Rate

Самое первое и очевидное нашёл ли вообще пользователь у вас хоть что-нибудь в рамках сессии.
SQL-сниппет
with 1.959964 as zselect    t,    round(success_rt + z*z/(2*cnt_sess) - z*sqrt((success_rt*(1 - success_rt) + z*z/(4*cnt_sess))/cnt_sess)/(1 + z*z/cnt_sess), 5) as success_rate__lower,    round(success_rt, 5) as success_rate,    round(success_rt + z*z/(2*cnt_sess) + z*sqrt((success_rt*(1 - success_rt) + z*z/(4*cnt_sess))/cnt_sess)/(1 + z*z/cnt_sess), 5) as success_rate__upperfrom (    select        toDateTime(toDate(min_event_datetime)) as event_datetime,        $timeSeries as t,        count(*) as cnt_sess,        avg(success) as success_rt    from (        select            user_id,            session_id,            min(event_datetime) as min_event_datetime,            max(if($yourConditionForClickEvent, 1, 0)) as success        from            $table        where            $timeFilter and            $yourConditionForSearchEvent        group by            user_id,            session_id    )    group by        t,        event_datetime)order by      t


MAP@k

Хорошее введение в MAP@k, а также в несколько других learning-to-rank метрик есть на Хабре. Скорее всего первое, что вы посчитаете из серьезных метрик. Метрика характеризует насколько хороший у вас top-K документов, где K обычно берется равным количеству элементов на странице поисковой выдачи.
SQL-сниппет
select    $timeSeries as t,    avg(if(AP_10 is null, 0, AP_10)) as MAP_10from(    select        session_id,        min(event_datetime) as event_datetime    from (        select            session_id,            event_datetime        from            query_log        where            $timeFilter and            $yourConditionForSearchEvent    )    group by        session_id) searchleft join(    select        session_id,        sum(if(position_rank.1 <= 10, position_rank.2 / position_rank.1, 0))/10 as AP_10    from (        select            session_id,            groupArray(toUInt32(position + 1)) as position_array_unsorted,            arrayDistinct(position_array_unsorted) as position_array_distinct,            arraySort(position_array_distinct) as position_array,            arrayEnumerate(position_array) as rank_array,            arrayZip(position_array, rank_array) as position_rank_array,            arrayJoin(position_rank_array) as position_rank        from            query_log        where            $timeFilter and            $yourConditionForClickEvent        group by            session_id    )    group by        session_id) clickon    search.session_id = click.session_idgroup by    torder by    t


Вместо заключения: Google и их первый инвертированный индекс



Рис. 6: Вот что бывает, когда программистов заставляют рисовать схемы против их воли (воспроизведение оригинальной блок-схемы из Brin, 1998)

Студенты Сергей Брин и Ларри Пейдж создали первую версию Google и проиндексировали около 24 миллионов документов в 1998 году. Скачивание документов студенты реализовали на Python, всего они запускали по 3-4 процесса паука. Один паук объедал примерно по 50 документов в секунду. Полное заполнение базы занимало 9 дней, выкачивалось под сотню ГБ данных. Сам индекс исчислялся десятками ГБ.

В Google изобрели свою собственную архитектуру инвертированного индекса, отличную от той, что будет использована через год в первых версиях Lucene. Формат индекса Google был простым как пять рублей и, как по мне, очень красивым.

Основа индекса файлы в формате Barrel. Barrel текстовый файл, хранящий четверки did, tid, offset, info, отсортированные по did, offset. В этой нотации did id документа, tid id терма, offset позиция терма в документе.

В оригинальной системе было 64 таких Barrel файла, каждый из которых отвечал за свой диапазон термов (tid). Новый документ получал новый did и соответствующие четверки дописывались в конец Barrel файлов.

Набор таких Barrel файлов является прямым индексом, позволяющий достать список термов для заданного did бинарным поиском по did (файлы же отсортированы по did). Получить инвертированный индекс из прямого можно операцией обращения берем и сортируем все файлы по tid, did.


Рис. 7: Пересортировка Barrel-файлов

Всё! Ещё раз мы пересортировываем одновременно 64 файла и получаем инвертированный индекс из прямого, так как теперь бинарным поиском можно искать уже по tid.

За форматом Barrel-файлов совершенно явно выглядывают уши MapReduce концепции, полноценно реализованной и задокументированной в работе J.Dean, 2004 позже.

О Google в общем доступе находится много вкусных материалов. Начать можно с оригинальной работы Brin, 1998 об архитектуре поиска, дальше потыкать в материалы Университета Нью-Джерси и шлифануть всё презентацией J.Dean о внутренней кухне первых версий индекса.

Представляете, приходите вы на работу, а вам говорят, что весь следующий год каждый месяц вам нужно будет ускорять код на 20%, чтобы дебит с кредитом сошлись. Так жил Google в начале нулевых годов. Разработчики в компании уже до того доигрались, что насильно переселяли файлы индекса на внешние цилиндры HDD у них линейная скорость вращения выше была и файлы считывались быстрее.

К счастью, в 2021 году такие оптимизации уже не особо нужны. HDD практически вытеснены из оперативной работы, а индекс, начиная с середины 2010-ых, целиком размещается в RAM.

Дополнительная литература:

Подробнее..

А контент они за меня тоже выберут? Как умная система рекомендаций студентам советы по учёбе даёт

06.05.2021 10:07:53 | Автор: admin

Весной 2021 проходит шестой запуск проектно-образовательных интенсивов Университета 20.35: студенты из 40+ вузов объединились в команды вокруг собственных идей или бизнес-задач от компаний и разрабатывают технологические решения. Звучит уже круто, а на практике ещё лучше. За три месяца участники проходят путь от идеи будущего продукта к его прототипу: знакомятся с экспертами рынков НТИ, питчат первые версии решений на хакатоне, знакомятся с другими участниками из университетов со всей страны, от Архангельска до Владивостока. В итоге каждая команда получает возможность развивать идею и дальше: защищать курсовые или дипломные работы по теме проекта, получать поддержку от вуза, входить в акселераторы, искать гранты и инвесторов, участвовать в конференциях.

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

Помимо проектной работы всем участникам открывается доступ в сетевое пространство непрерывного обучения с образовательными подборками и онлайн-курсами. Сегодня подход я не знаю, этого не было в программе, у меня лапки всё больше вызывает недоумение, поэтому студенты интенсивов сразу учатся мыслить в логике мне нужно чему-то научиться, чтобы сделать шаг развития. До этой весны участники выстраивали индивидуальную образовательную траекторию, либо пользуясь материалами Университета 20.35, либо самостоятельно подыскивая нужные курсы, лекции, книги. Но кто сказал, что систему Lifelong Learningа нельзя прокачать ещё больше? Ведь осознанный выбор с каждым днём делать всё труднее, особенно с учётом возросших объёмов контента на удаленке: количество открытых вкладок становится несоразмерно добываемым знаниям.

Умная система рекомендаций: сама найдёт, выберет, предложит

Платформа Университета 20.35 может автоматически подбирать и рекомендовать образовательный контент на основе

  • тем и целей, на которых хочет сфокусироваться пользователь,

  • задач, зафиксированных в таск-трекерах Trello или MS Teams.

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

Система уже умеет вытаскивать текст из:

  • карточек канбан-досок Trello и планировщика MS Teams в них работают команды, размечая свой прогресс в колонках To do, Doing и Done;

  • мессенджеров (slack, discord, zulip) коммуникативных пространств;

  • раздела Фокусы развития на платформе Университета 20.35 во время работы над проектом каждый может столкнуться с областями, которые ему не знакомы и интересны для изучения, например: agile или UX/UI дизайн. С помощью Фокусов пользователь целенаправленно фиксирует, чему хочет научиться, добавляя к записи специальные теги из тематических областей.

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

https://drive.google.com/drive/u/0/search?q=фокус%20развития

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

Любой человек может стать пользователем образовательного пространства Университета 20.35, а значит получать рекомендации образовательного контента по поставленным целям. Чтобы разместить фокус развития, откройте страницу, зарегистрируйтесь в системе и добавьте запись.

Чтобы посмотреть полезный контент для ваших бизнес-процессов и проектов на досках Trello, зарегистрируйте проект на платформе Университета 20.35 в модуле People&Teams. Проектом будет тот комплекс задач, который требует образовательных рекомендаций.

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

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

Задавайте вопросы про персональные рекомендации в комментариях и делитесь опытом работы с нашей платформой.

Подробнее..

Перевод Однажды Microsoft забанила всю мою страну за читерство

19.03.2021 12:22:16 | Автор: admin

Об игре


Club Bing это набор игр, в которые можно было играть в 2007-2012 годах. Все игры были связаны со словами, в них нужно было играть онлайн, чтобы зарабатывать очки, которые можно было тратить в онлайн-магазине для покупки призов. Одна из игр называлась Chicktionary. Цель игры: использовать 7 слов, чтобы составить как можно больше слов.


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

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

  • 123 Main St. Apt #1, Anywhere, YZ, USA
  • 123 Main St. Apt #2, Anywhere, YZ, USA
  • 123 Main St. Apt #3, Anywhere, YZ, USA

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

  • Телескопы
  • Проигрыватель
  • Xbox
  • Наручные часы Michael Kors
  • Одежда North Face
  • Надувной каяк

На всех призах была маркировка Bing.

Microsoft выпустила эту игру для рекламы своего поискового движка Bing. Каждый раз, когда ты вводил ответ, браузер выполнял его поиск в отдельном фрейме. Вероятно, это позволяло убедить людей больше пользоваться Bing, а также увеличивало количество пользователей, якобы использующих Bing. А этот показатель позволял Microsoft требовать больше денег у рекламодателей, желавших появляться в результатах поиска Bing. Я подсчитал, что все играющие в Chicktionary скриптеры составляли 2-4% от всех поисковых запросов Bing. К тому же я провёл небольшие вычисления, сравнив доход и ежемесячное количество поисковых запросов Google и Bing, и выяснил, что Microsoft получала от игры довольно неплохую прибыль. Иллюзия того, что Bing стал популярнее, вероятно, принесла больше рекламных долларов, чем стоили призы.

Скриптинг


Существовало несколько скриптов для автоматической игры, часто в них использовался AutoHotKey. Я написал собственный скрипт на VB.Net, у него был встроенный браузер. Я назвал его Chicken в честь Chicktionary, а ещё потому, что в моей стране был забавный телевизионный скетч о курице.


Так как игра была написана на Flash, было не очень легко взаимодействовать с элементами в DOM, поэтому игровой процесс состоял из сочетания:

  • Взаимодействий DOM
  • browser.Go(<URL>)
  • Скриншотов и поиска пикселей
  • Windows API для имитации ввода и нажатий мыши

В Microsoft работают не дураки. Они знали о существовании хакеров. Они отлично справлялись с выявлением пользователей, слишком быстро решающих головоломки, и обнуляли их призы. Кроме того, они усовершенствовали распознавание дубликатов адресов доставки. И, разумеется, они использовали капчу.

Captcha


Знаете, такие кривые буквы, которые нужно вводить, чтобы доказать, что ты человек:


Можно подумать, что они бы полностью препятствовали автоматизации, но это не так. Существует больше десятка онлайн-сервисов решения captcha. Их цена составляет примерно 1 доллар за 500 решённых капч. Club Bing отображал капчу через каждые четыре игры. За каждую игру ты получал 20 билетов (если ввести все слова правильно), а Xbox стоил 55000 билетов.

(55000 билетов / Xbox) * (1 игра / 20 билетов) * (1 капча/ 4 игры) * (1 доллар / 500 капч) = 1,375 доллара за Xbox

Довольно выгодная сделка. Кроме того, для этого понадобится 55 дней, потому что в день можно зарабатывать только по 1000 билетов. Но можно завести несколько разных аккаунтов, чтобы через 55 дней получить несколько консолей Xbox. С более дешёвыми призами всё было ещё быстрее: видеоигра стоила 5000 билетов.

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

Капча с котами


Примерно в 2010 году Microsoft заменила алгоритм капчи с кривых букв на Asirra. Asirra выглядит вот так:


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

Когда Club Bing перешёл на эту капчу, всё сообщество читеров Club Bing приостановило свою деятельность. Я приступил к работе.

Первым делом я попробовал отправлять сервису решения капч кошек и собак. В 2010 году эти сервисы не обладали мощными средствами распознавания изображений. Это были просто люди из Бангладеша, отвечавшие на ваши запросы. Я попытался прикинуть, сколько они зарабатывали:

(2000 рабочих часов / год) * (1 капча/ 5 секунд) * (1 доллар / 500 капч) = 2880 долларов / год при полной занятости.

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

Я захотел узнать, смогут ли они решать Asirra. Вот изображение, которое я отправил:


Используемый мной сервис de-captcher вернул такой ответ:

cat or dog?

Строго говоря, он был верным, но меня не устраивал. Я отправил картинку ещё несколько раз:

imageimage

Чтобы получить полезный ответ, потребовалось четыре попытки:

dog

Это не предвещало ничего хорошего. Во-первых, мне нужно было 12 правильных ответов. Если предположить, что каждый раз на поиск работника в Бангладеше, способного сделать всё правильно, потребуется 4 попытки, то это составит 48 запроса. Цена Xbox только увеличилась до 66 долларов! А если они ошибутся хотя бы один раз, мне придётся удвоить эту сумму. Немыслимо! Мне нужно было решение получше.

Почему бы просто не использовать глубокое обучение?


Не забывайте, что дело происходило в 2010 году. Глубокое обучение (deep learning) тогда было развито совсем не так хорошо, как сейчас. В этой статье Стэнфордского университета говорится, что авторам удавалось использовать машинное обучение для правильного решения головоломок всего примерно в 10% случаев. Совершенно неприемлемо! (В основном они просто замечали зелёный цвет, потому что собаки лежат на лужайке с большей вероятностью, чем кошки.)

Также Microsoft использовала схему token-bucket, которая временно блокировала пользователя, если он слишком много раз подряд ошибается. Хотя token-bucket не выполнялась на тестовом сервера Asirra, её применяли в Club Bing.

Harvest: новая надежда


Исследовательский отдел Microsoft создал веб-сайт для демонстрации новой технологии Asirra.
На нём была тестовая площадка, на которой можно было проверить работу сервиса, она сообщала, если вы решали всё правильно. Снова взглянем на изображение:


Видите эту маленькую кнопку Adopt me (Приюти меня)? Она там есть, потому что Asirra была результатом партнёрства с petfinder.com. Petfinder это сервис поиска новых хозяев для домашних питомцев, у которого есть большие списки животных. При нажатии на кнопку Adopt me браузер переходит к профилю этого животного. Разумеется, в профиле указан его вид: кошка или собака.

Повторюсь, в Microsoft работают не дураки. Они знали, что кто-то попробует нажимать на Adopt me под каждым изображением и получить правильный ответ. Поэтому они делали недействительными саму задачу капчи и все ссылки на профили после первого нажатия на кнопку Adopt me. То есть можно было получить только один ответ.

Я решил написать программу, которая сделает это много раз и сопоставит каждое изображение с числом:

  • 0: неизвестно
  • 1: собака
  • 2: кошка

Я назвал её Harvest (уборка урожая), чтобы продолжить тему куриц и сельского хозяйства.

Каждая попытка занимала довольно мало времени, но я не знал, сколько животных мне нужно узнать. На веб-сайте Asirra утверждалось, что их 3,1 миллиона. Но на самом ли деле это так?

Обратный парадокс дней рождения


Многим людям известен парадокс дней рождения: несмотря на то, что в году 365 дней, достаточно собрать в одной комнате всего 22 человека, чтобы получить вероятность 50 на 50 того, что у двух из них день рождения совпадает. Если d=365, а n=22, то вероятность можно посчитать так:


Обратность заключается в том, что если мы знаем, что 22 человека в комнате дают вероятность 50 на 50 нахождения двух людей с одинаковым днём рождения, то можно перевернуть уравнение и вычислить, сколько дней в году.

Аналогично если я буду запрашивать серверы Asirra и отслеживать каждое встреченное изображение, то как долго мне надо будет ждать повтора? Я провёл примерно такой эксперимент (но на VB.Net, а не на псевдокоде python):

def trial():   images_seen = []   while True:       p = fetch_puzzle()       for image in p.images():           if image in images_seen:               break           images_seen.append(image)   return len(images_seen)

В каждой попытке я запрашивал задачи, пока не получу в попытке повтор кошки или собаки. Я проводил множество попыток и отслеживал, сколько изображений получалось до первого повтора. Затем я брал медиану всех этих попыток, что позволяло мне определить, сколько животных нужно увидеть, прежде чем вероятность повтора составит 50 на 50. Далее я вычислил в обратном порядке представленное выше уравнение, чтобы получить количество изображений. И в самом деле, мой ответ оказался достаточно близок к 3,1 миллиона.

Распределённая уборка урожая


Я записал свой скрипт на USB-флэшки и раздал их друзьям. Также я написал программу слияния для объединения баз данных. Через каждые один-два дня мои друзья возвращали мне свои флэшки, я запускал Combine (комбайн, снова сельскохозяйственная тема) и записывал базу на все флэшки, чтобы мои уборщики урожая не повторяли свою работу. Уборщики нажимали Adopt me только на неизвестных изображениях, поэтому поддержание актуальности распределённых баз данных позволяло избегать повторной работы.

Можно ли ускориться?


Спустя 2-3 недели я собрал примерно 1,5 миллиона изображений. Я приближался к этапу, когда задачу иногда можно было почти решить из базы данных. Однако в базе данных присутствовали никогда не заполняемые дыры, потому что ссылка по кнопке Adopt me была сломана. Возможно, это животное уже забрали? Я добавил в базу данных ещё один результат:

  • 0: неизвестно
  • 1: кошка
  • 2: собака
  • 3: нерабочая ссылка

Но есть и другой способ получить правильный ответ: отгадать!

Asirra давала знать, если пользователь решил задачу правильно. Я погонял код какое-то время и замерил следующие показания:

  • adopt_time: сколько времени нужно для нажатия на ссылку Adopt me, загрузку petfinder.com и получить ответ кошка/собака.
  • adopt_success_rate: вероятность того, что при нажатии на Adopt me мы получим ответ, а не сломанную ссылку.
  • guess_time: сколько времени нужно на то, чтобы отправить случайную догадку и узнать, правильно ли решена задача. (На это требовалось меньше времени, чем на загрузку petfinder.com.)

Предположив, что соотношение кошек к собакам 50 на 50 (на самом деле оно было ближе к 40 на 60, ну да ладно), я мог вывести уравнение того, сколько животных я узнаю за секунду при помощи adopt me:

adopt_learning_rate = 1 / adopt_time * adopt_success_rate

Также можно было вычислить скорость узнавания при догадках. Если количество неизвестных животных равно n, то вероятность угадать правильно равна 1 к 2n. Но если я угадаю их правильно, то узнаю все n животных:

guess_learning_rate = n / guess_time * (1 / 2**n)

Приравняв эти два значения и вычислив n, я смог выяснить, что если знаю больше, чем 7 из 12 животных, то смогу просто угадывать остальных, и это будет эффективнее, чем adopt me. Я поместил этот код в уборщик урожая, и пару недель спустя у меня и друзей была достаточная для работы база данных.

Cats Be Gone: сервер решений


Microsoft Research проделал хорошую работу, но допустил пару ошибок в протоколе. Во-первых, они не ограничили частоту запросов к сервису. Благодаря этому и стала возможной уборка урожая. Во-вторых, они неправильно реализовали proofs-of-work.

В процессе решения капчи есть три стороны:

  1. Поставщик captcha (например, Microsoft Asirra или Google reCaptcha)
  2. Сервер captcha (например, Club Bing или другой веб-сервер)
  3. Пользователь captcha (например, игроки в Club Bing)

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


Эта идея плоха по нескольким причинам:

  1. Теперь вся обработка производится на сервере. Что если сервер реализует её неправильно?
  2. Если Asirra когда-нибудь захочет изменить протокол, то каждому серверу придётся обновлять своей веб-сайт.
  3. Сервер-имитатор может стать самым эффективным уборщиком урожая.

Вот как это работало на самом деле (на этот раз для удобства чтения Asirra размещён посередине):


Теперь серверу не нужно знать подробностей работы системы. У него даже нет ответа! Но здесь Microsoft совершила ошибку:

  • В Club Bing было ограничение по частоте, поэтому нельзя было совершать слишком много ошибок подряд, но такого ограничения не было у Asirra.
  • Отсутствовала проверка токена по IP-адресу.

Поэтому я с лёгкостью смог создать веб-сайт cats-be-gone.kicks-ass.org, передававший действительные токены по HTTP. Вот так:


(Хотя IP-адреса токенов не проверялись, проверялась их метка времени. Токены были действительны только примерно один час. Сервер Cats Be Gone (Кошки, брысь) на самом деле генерировал их заранее, и всегда имел наготове 20 токенов, чтобы они были под рукой, когда потребуются.)

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

Превращаем эту систему в бизнес (доход за всё время: 0 долларов)


Поговорив с друзьями, я подумал: Стоит открыть сервер для всех и сделать из него бизнес! Люди уже привыкли к сервисам платы за капчи, поэтому я решил, что они будут вместо этого платить мне. Я решил брать по 1 доллару за 200 решений, это больше, чем стандартная ставка 1 доллар/500, но у меня не было конкурентов. Я выложил клиент на один из самых популярных форумов для подобных вещей и открыл Google Store для приёма платежей.

На форумах много болтали о том, что это какой-то способ обхода, а не солвер, поэтому когда дойдёт время до покупки приза, полученные билеты будут деактивированы. В прошлом уже были проблемы с программами обхода капчи. Люди имели полное право на подозрения. Поэтому я на какое-то время сделал сервис бесплатным. А после того, как он начал доказывать свою действенность при отсутствии альтернатив, у меня появились клиенты и 50 долларов на потенциальных продажах. Ого, да я теперь богач! Ага, точно! Даже бангладешцы выгоднее тратили своё время.

Я обещал, что не буду обрабатывать оплату, пока не будет потрачено 10% платежа, и что верну деньги неудовлетворённым покупателям. Однако всего спустя неделю я отменил все заказы, потому что защита Microsoft полностью уничтожила мою идею.

Империя Microsoft наносит ответный удар


Всё это время Microsoft пыталась использовать различные способы, чтобы победить моё читерство. Первым делом они попробовали переименовать все изображения. Это оказалось полной катастрофой и мне пришлось начинать всё сначала, потому что единственная привязка к кошке или собаке была по имени файла!

Да не, шучу, конечно. Я уже скачал все изображения. 3,1 миллиона изображений по 1 МБ каждый это всего 3,1 терабайта. Даже в те времена 3 терабайта были вполне доступны. Это на меня никак не повлияло. Я знал, что они могут попробовать что-то подобное, поэтому написал уборщик-скачиватель.

Ещё они попытались изменять изображения. Случайным образом выбирались 10-20 пикселей изображения и менялся их цвет. Этого было бы более чем достаточно для поломки любого криптографического хэша, который бы я мог использовать, например, сопоставление SHA1(image), -> cat/dog. Но я его не использовал. Я пользовался MinHash.

Хэширование изображений, v1: MinHash


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


Если Microsoft изменит пару пикселей, то это особо ни на что не повлияет. Каковы шансы, что они совпадут? А если и совпадут, то это, вероятно, будет всего 1 из 12 изображений, поэтому я могу просто попробовать угадать ответ для него. В худшем случае придётся получить ещё одну задачу и попробовать снова.

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

Microsoft побеждает Cats Be Gone


Microsoft наконец ограничил частоту запросов Asirra, поэтому один сервер Cats-Be-Gone больше не мог создавать токены для всех. И они начали сопоставлять токены с IP-адресами, поэтому токены сервера Cats-Be-Gone потеряли свою ценность. Хуже всего то, что они удалили 3,1 миллиона изображений с petfinder.com и создали совершенно новый набор.

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

Краудсорсинг


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

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

Кроме того, поскольку у меня не было изображений, всё хэширование выполнялось в коде клиента, потому что не хотел отправлять 12 изображений на сервер при каждом запросе. И я знал, что MinHash недостаточно надёжен, поэтому решил перейти на pHash.

Хэширование изображений, v2: pHash


pHash великолепен. Его принцип примерно таков:

  1. Преобразуем изображение в оттенки серого.
  2. Применяем гауссово размытие.
  3. Ужимаем до одинакового квадратного размера.
  4. Применяем к нему дискретное косинусное преобразование.
  5. Сохраняем только 64 наиболее значимые значения.
  6. Для каждого значения записываем 1, если оно больше медианы, в противном случае 0.
  7. Теперь у нас есть 64-битное число!

Для этого у pHash есть библиотека, и естественно, она написана не на VB.Net, поэтому я реализовал её самостоятельно. Сегодня можно было бы просто использовать другой язык, в котором есть библиотека, но я объясню, как работает pHash, потому что это довольно круто.

Преобразование в оттенки серого


Тут всё довольно просто, достаточно преобразовать RGB-значение каждого пикселя в яркость. Существует несколько способов сделать это, вот тот, который описан в Википедии:

Y = 0.2126 * R + 0.7152 * G + 0.0722 * B

Вот как это сделать на python:

from PIL import Imageimage = Image.open('dog.jpg')image.show()for x in range(image.size[0]):    for y in range(image.size[1]):        (r, g, b) = image.getpixel((x,y))        brightness = 0.2126 * r + 0.7152 * r + 0.0722 * b        new_pixel = tuple([int(brightness)] * 3)        image.putpixel((x,y), new_pixel)image.show()


Довольно просто.

Гауссово размытие


Заменяем каждый пиксель взвешенной суммой окружающих его пикселей. Вот пример размытия с радиусом 8.


(Я пропущу этап сжатия до квадратного размера, потому что это довольно скучно и очевидно.)

Дискретное косинусное преобразование


Дискретное косинусное преобразование (Discrete cosine transform, DCT) похоже на преобразование Фурье тем, что можно преобразовать последовательность чисел из одной формы в другую, а также обратить преобразование. inverse_dct(dct(image)) == image. Вам необязательно знать всё о частотном анализе. Достаточно знать, что можно взять матрицу чисел, например, изображение в оттенках серого, и преобразовать её в ещё одну матрицу чисел. И обратное преобразование тоже возможно.

В отличие от преобразования Фурье, DCT состоит из косинусов, а не из степеней e, поэтому все результаты вещественны, без мнимых чисел. Этот способ используется в JPEG для сжатия изображений.

Вот код, который я нашёл онлайн, выполняющий эту задачу на Python. Я немного его изменил. Вот самая важная часть:

im = rgb2gray(imread('dog.jpg'))   #read in the image to grayscaleimF = dct2(im)                     #DCTfraction = 1for y in range(len(im)):    for x in range(len(im[y])):        if x > len(im[y])//fraction or y > len(im)//fraction:            im[y][x] = 0           # blacken pixels that aren't in the top left corner            imF[y][x] = 0          # blacken pixels that aren't in the top left cornerim1 = idct2(imF)                   # inverse DCT


Мы считываем изображение и выполняем с ним DCT. Затем мы зачерняем часть исходного изображения, а также ту же часть преобразованного изображения. А затем инвертируем преобразование. Вот как это выглядит без зачернения:


Здесь нет ничего неожиданного. DCT обратим, поэтому логично, что выходные данные эквивалентны входящим. Немного странно, что изображение DCT только чёрное; вскоре мы к этому вернёмся!

Давайте посмотрим, что произойдёт, если мы отбросим три четверти изображения:


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


Оригинал потерял кучу данных, но изображение DCT по-прежнему выглядит приемлемо! В этом и заключается сила DCT: все важные биты находятся в одном углу, а всё остальное можно отбросить.

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


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

Чтобы закодировать это в число, мы используем упомянутый выше способ: взяв только 64 числа из левого верхнего угла, закодируем 1, если значение выше медианы, в противном случае закодируем 0. Результатом будет 64-битное число с половиной нулей. Существует более 1018 таких чисел, что намного больше, чем 3,1 миллиона изображений, которые нам нужно закодировать, так что вероятность коллизии мала.

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

VP-деревья


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


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

                    01101010111001                    10100010010010отличающиеся биты:  ^^  ^   ^ ^ ^^Расстояние Хэмминга равно 7

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

Chicken и pHash


Теперь, когда мы решили полностью положиться на краудсорсинг, мне показалось несправедливым зарабатывать на этом деньги. Но я продолжил хостить сервер, чтобы кто угодно мог делиться ответами на задачи. Я выдавал результаты Asirra бесплатно и собирал новые ответы, когда о них сообщали. Через каждую пару дней я повторно генерировал дерево на основе последних данных и перезапускал сервер. Максимум я получал примерно 10 запросов в секунду на моём самодельном, написанном на VB.Net HTTP-сервере. Всего у меня было около 2000 уникальных пользователей. Я вычислил примерное количество очков, полученных пользователями с помощью Chicken и среднюю стоимость очка на основании продаваемых на ebay товаров. Похоже, благодаря Chicken пользователи в сумме получили призов примерно на полмиллиона долларов.

Позже Microsoft начала передавать случайным образом пикселизированные и повёрнутые изображения. Они оказались настолько искажёнными, что приводили pHash в растерянность. Компания препятствовала читерству и другими способами. Например, в Club Bing забанили мою страну целиком. В 2012 году работа Club Bing прекратилась.

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



На правах рекламы


Надёжный и недорогой VDS от VDSina позволит разместить любой проект, на любой операционной системе Linux или Windows. Всё будет работать без сбоев и с высоким uptime!

Подробнее..

Интернационализация поиска по городским адресам. Реализуем русскоязычный Soundex на Sphinx Search

18.03.2021 08:04:31 | Автор: admin

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

А как часто лично вы оказывались в такой ситуации, в незнакомом городе в другой стране?

Появление смартфонов и приложений для навигации решило много проблем. Ура, можно посмотреть свою геолокацию, можно найти куда идти, прикинуть в каком направлении и даже проложить маршрут.

Осталась одна проблема: все улицы в приложении подписаны местными иероглифами на местном наречии, и ладно если в стране пребывания принята латиница, клавиатура на латинице есть во всех смартфонах и мир к ней привык, и то я испытывал дискомфорт, из-за диакритических знаков, принятых в чешском алфавите. А боль и страдания иностранцев, видящих кириллицу, могу только представить, посмотрите псевдокириллицу и поймёте. Если бы я оказался на их месте, я бы писал названия и адреса латиницей, пытаясь воспроизвести звучание - фонетический поиск.

В публикации опишу как реализовать фонетические алгоритмы поиска Soudex на движке Sphinx Search. Одной транслитерацией здесь не обойдётся, хотя и без неё никуда. Получившийся конфигурационный файл, доступен на GitHub Gist.

Вступление

Понадобится база адресов, например, ФИАС или база названий чего-то, в общем то, что будем искать, и Sphinx Search.

Решение удобно тем, что ничего не придётся доделывать, достраивать дорабатывать в самой базе, т.е. данные останутся как есть, - всё сделает Sphinx.

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

Но, ничто не ново под луной и решение уже найдено до нас. Распространены два подхода Soundex и Metaphone, плюс их вариации. В публикации рассмотрим только Soundex и его варианты, Metaphone затрагивать не будем.

Более того, Sphinx уже поддерживает Soundex, как говорится, из коробки. Но рано бить баклуши, почивать на лаврах и стричь купоны, для кириллицы он не работает. Т.е. по сути не подходит для задачи. Придётся допиливать.

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

А теперь подумаем, как завезти поддержку кириллицы в Soundex, реализовать и его, и улучшенную версию, и NYSIIS, и Daitch-Mokotoff.

К реализации

Будут приведены некоторые примеры работы на SphinxQL, для этого использую подключение в духе:

mysql -h 127.0.0.1 -P 9306 --default-character-set=utf8

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

Оригинальный Soundex и Транслитерация

Начнём с самого простого в реализации. Простоты добавляет тот факт, что Sphinx Search, как я уже писал выше, поддерживает из коробки реализацию для английского языка, т.е. половина дела уже в шляпе.

Кстати, делает это он не совсем канонично: не обрезает все коды до четырёх символов, а если символов меньше четырёх не дополняет всё нулями. Но я в этом ни каких проблем не вижу.

Всё что от нас требуется сделать транслитерацию на великий и могучий, остальное Sphinx сделает сам.

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

Прописываем в конфигурации Sphinx все правила транслита с помощью регулярных выражений:

regexp_filter = (А|а) => a

а для Ъ и Ь можно написать

regexp_filter = (Ь|ь) =>

Не буду приводить текст для всего алфавита, если что можно скопировать всё там же, с GitHub Gist.

И не забудьте включить soundex для латиницы:

morphology = soundex

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

Всё настроили, проиндексировали данные, создали подключение к Sphinx. Давайте попробуем найти что-нибудь. Раз уж наша цель - коммунизм интернационализм поисковой выдачи, то и искать будем улицы названные в честь деятелей интернационала, и сочувствующих. Поищем Ленина. Искать будем именно Ленина, а не Ленин, я почему-то уверен, что интуристы прямо так и будут искать Lenina, может даже ulitsa Lenina.

Чтобы понять что с ним происходит воспользуемся командой CALL KEYWORDS:

mysql> call keywords('Ленин Ленина Lenina Lennina Lenin', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | lenin     | l500       || 2    | lenina    | l500       || 3    | lenina    | l500       || 4    | lennina   | l500       || 5    | lenin     | l500       |+------+-----------+------------+

Обратите внимание, в tokenized хранится то, что было получено после применения всех регулярных выражений. А в normalized, то по какому ключу Sphinx будет осуществлять поиск, и результат обусловлен тем, что включена morphology. 'Lenina' преобразуется в ключевое слово l500, и 'Ленина' в l500, спасибо транслиту, - уровнял, теперь на каком бы языке не искали найдётся одно и то же. Всё тоже самое и для Lennina, и для Lenena, и даже Lennona. Так что, если в вашем городе есть улица Джона Леннона, то тут может накладочка выйти.

Выполним поиск, наконец:

mysql> select * from STREETS where match('Lenena'); +------+--------------------------------------+-----------+--------------+| id   | aoguid                               | shortname | offname      |+------+--------------------------------------+-----------+--------------+|  387 | 4b919f60-7f5d-4b9e-99af-a7a02d344767 | ул        | Ленина       |+------+--------------------------------------+-----------+--------------+

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

mysql> call keywords('Плехановская Plechanovskaya Plehanovskaja Plekhanovska', 'STREETS', 0);+------+----------------+------------+| qpos | tokenized      | normalized |+------+----------------+------------+| 1    | plekhanovskaja | p42512     || 2    | plechanovskaya | p42512     || 3    | plehanovskaja  | p4512      || 4    | plekhanovska   | p42512     |+------+----------------+------------+

plehanovskaja -выбивается. Sphinx ничего не вернёт. Но, можно воспользоваться CALL QSUGGEST:

mysql> CALL QSUGGEST('Plehanovskaja', 'STREETS');+----------------+----------+------+| suggest        | distance | docs |+----------------+----------+------+| plekhanovskaja | 1        | 1    || petrovskaja    | 4        | 1    |+----------------+----------+------+

Функция вернёт предложения по исправлению запроса, и расстояние Левенштейна между запросом, и возможным результатом. Т.е. можно попробовать опять взять напильник в руки.

Не забудьте включить инфикс, для работы этой функции:

min_infix_len = 2

suggest содержит запись аналогичную по смыслу колонке tokenized, т.е. то, что получилось после применения регулярных выражений. В этом случае регулярные выражения касались только транслитерации, в других реализациях Soudex регулярные выражения будут применяться в том числе и для генерации кода, поэтому QSUGGEST работать не будет.

Попробуем что-нибудь ещё найти:

mysql> select * from STREETS where match('30 let Pobedy');+------+--------------------------------------+-----------+------------------------+| id   | aoguid                               | shortname | offname                |+------+--------------------------------------+-----------+------------------------+|  677 | 87234d80-4098-40c0-adb2-fc83ef237a5f | ул        | 30 лет Победы          |+------+--------------------------------------+-----------+------------------------+mysql> select * from STREETS where match('30 лет Побуды');+------+--------------------------------------+-----------+------------------------+| id   | aoguid                               | shortname | offname                |+------+--------------------------------------+-----------+------------------------+|  677 | 87234d80-4098-40c0-adb2-fc83ef237a5f | ул        | 30 лет Победы          |+------+--------------------------------------+-----------+------------------------+

Хорошо справляется, заодно и опечатки может исправить.

Спойлер: именно этот вариант в итоге и окажется самым приемлемым. Если лень читать полотенце ниже, то сразу переходите к итогам, всё равно вывод в пользу оригинального Soundex.

Улучшенный Soundex

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

Вначале копируем транслитерацию.

Sphinx поддерживает наследование для index, но вынести транслитерацию в родительский индекс, а потом унаследовать её в дочерних, не получится. При наследовании индексов, Sphinx полностью игнорирует регулярные выражения родителя, они считаются переопределёнными если вы добавляете в дочерний класс новые регулярные выражения. Т.е. наследовать можно, пока в дочернем не объявить regexp_filter, который переопределит все regexp_filter родителя.

Можно убрать morphology = soundex из конфигурации она нам уже ничем не поможет, только под ногами путается. Придётся всё прописывать самим, опять через регулярные выражения.

Sphinx будет их применять последовательно, в том порядке, в котором они в конфигурации прописаны! Это важно. Для регулярных выражений используется движок RE2.

Сразу после блока с транслитерацией запишем сохранение первого символа, например: regexp_filter = \A(A|a) => a

Затем остальные символы заменяются на числовой код, Гласные на 0.

regexp_filter = \B(A|a) => 0regexp_filter = \B(Y|y) => 0...

Хотя, гласные можно просто удалять regexp_filter = \B(Y|y) =>

Решайте сами какой вариант нравится больше, хозяин - барин. Но я гласные выкидываю, хотя бы для того чтобы ВЛКСМ и Veelkaseem давали один результат.

mysql> call keywords('ВЛКСМ Veelkaseem', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | v738      | v738       || 2    | v738      | v738       |+------+-----------+------------+

иначе будет что-то такое:

mysql> call keywords('ВЛКСМ Veelkaseem', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | v738      | v738       || 2    | v0730308  | v0730308   |+------+-----------+------------+

Далее, согласные H и W просто выкидываются.

Идущие подряд символы, или входящие в одну и ту же группу, или символы/группы, разделенные буквами H или W, записываются как один. Это самое последнее действие.

regexp_filter = 0+ => 0regexp_filter = 1+ => 1...

Проверяем что получается:

mysql> call keywords('Ленин Ленина Lenina Lennina Lenin', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | l8        | l8         || 2    | l8        | l8         || 3    | l8        | l8         || 4    | l8        | l8         || 5    | l8        | l8         |+------+-----------+------------+mysql> select * from STREETS where match('Lenina');+------+--------------------------------------+-----------+--------------+| id   | aoguid                               | shortname | offname      |+------+--------------------------------------+-----------+--------------+|  387 | 4b919f60-7f5d-4b9e-99af-a7a02d344767 | ул        | Ленина       |+------+--------------------------------------+-----------+--------------+

Вроде всё нормально, и Ленина мы находим во всех вариациях. Но обратите внимание, поле tokenized теперь содержит не индексируемый текс, а soundex-код. QSUGGEST отказывается работать. Если кто-то знает, как включить пишите. Я пытался добавить юникод для цифр в ngram_chars. Но это не помогло.

Проверим на Плеханове:

mysql> call keywords('Плехановская Plechanovskaya Plehanovskaja Plekhanovska', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | p738234   | p738234    || 2    | p73823    | p73823     || 3    | p78234    | p78234     || 4    | p73823    | p73823     |+------+-----------+------------+

Стало вариантов много, а правильный всего один, и QSUGGEST не придёт на помощь:

mysql> CALL QSUGGEST('Plehanovskaja', 'STREETS');Empty set (0.00 sec)mysql> CALL QSUGGEST('p73823', 'STREETS');Empty set (0.00 sec)mysql> CALL QSUGGEST('p78234', 'STREETS');Empty set (0.00 sec)

Хотели, как лучше, а получилось, как всегда. Сам алгоритм реализовали, вроде правильно, но непрактично. Хотя нерабочим его тоже не назовёшь. Например, вот как он побеждает 30 лет Победы:

mysql> call keywords('30 let Podedy', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | 30        | 30         || 2    | l6        | l6         || 3    | p6        | p6         |+------+-----------+------------+mysql> select * from STREETS where match('30 let Pobedy');+------+--------------------------------------+-----------+------------------------+| id   | aoguid                               | shortname | offname                |+------+--------------------------------------+-----------+------------------------+|  677 | 87234d80-4098-40c0-adb2-fc83ef237a5f | ул        | 30 лет Победы          |+------+--------------------------------------+-----------+------------------------+

И даже так работает:

mysql> select * from STREETS where match('Вэлкасэем');+------+--------------------------------------+--------------+----------------------+| id   | aoguid                               | shortname    | offname              |+------+--------------------------------------+--------------+----------------------+|  873 | abdb0221-bfe8-4cf8-9217-0ed40b2f6f10 | проезд       | 30 лет ВЛКСМ         || 1208 | f1127b16-8a8e-4520-b1eb-6932654abdcd | ул           | 50 лет ВЛКСМ         |+------+--------------------------------------+--------------+----------------------+

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

NYSIIS

Ходят слухи что разработан он был для работы с американскими фамилиями. Американские это вообще какая-то условность. Да и фамилии тоже, подойдёт и для поиска по названиям улиц и другим именам собственным, тем более в России многие улицы названы чьей-то фамилией, хотя американцами эти люди не стали.

Далее будет использоваться модификатор (?i) для работы регулярных выражений без учёта регистра.

Начинаем с транслитерации, как всегда. Затем:

  1. Преобразовать начало слова

    regexp_filter = (?i)\b(mac) => mcc

  2. Преобразовать конец слова

    regexp_filter = (?i)(ee)\b => y

  3. После гласных: удалить H, преобразовать W в А

    regexp_filter = (?i)(a|e|i|o|u|y)h => \1

    regexp_filter = (?i)(a|e|i|o|u|y)w => \1a

  4. Преобразовываем все буквы кроме первой

    regexp_filter = (?i)\B(e|i|o|u) => a

    regexp_filter = (?i)\B(q) => g

  5. Удалить S на конце

    regexp_filter = (?i)s\b =>

  6. Преобразуем AY на конце в Y

  7. Удалить A на конце

Напоминаю что регулярные выражения приведены не все, а минимум, для примера!!!

Самое замечательное, - это то, что код не числовой, а буквенный, а значит снова заработает CALLQSUGGEST.

Проверяем:

mysql> call keywords('Ленин Ленина Lenina Lennina Lenin', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | lanan     | lanan      || 2    | lanan     | lanan      || 3    | lanan     | lanan      || 4    | lannan    | lannan     || 5    | lanan     | lanan      |+------+-----------+------------+mysql> call keywords('Плехановская Plechanovskaya Plehanovskaja Plekhanovska', 'STREETS', 0);+------+---------------+---------------+| qpos | tokenized     | normalized    |+------+---------------+---------------+| 1    | plachanavscaj | plachanavscaj || 2    | plachanavscay | plachanavscay || 3    | plaanavscaj   | plaanavscaj   || 4    | plachanavsc   | plachanavsc   |+------+---------------+---------------+

Пробуем понять что имел в виду пользователь, используем для этого CALL QSUGGEST Plehanovskaja, преобразовавшуюся в plaanavscaj:

mysql> CALL QSUGGEST('plaanavscaj', 'STREETS');+---------------+----------+------+| suggest       | distance | docs |+---------------+----------+------+| paanarscaj    | 2        | 1    || plachanavscaj | 2        | 1    || latavscaj     | 3        | 1    || sladcavscaj   | 3        | 1    || pacravscaj    | 3        | 1    |+---------------+----------+------+

И тут возникают коллизии. Да ещё и без пол-литра не разберёшься что тут такое.

Узнать правду

paanarscaj Пионерская

plachanavscaj Плехановская

latavscaj Литовская

sladcavscaj Сладковская

pacravscaj Покровская

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

Daitch-Mokotoff Soundex

И последний по списку, но не по значению, из алгоритмов Soundex.

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

Как всегда, в начале транслитерация.

Потом выполняем пошаговую трансформацию.

Каждый шаг состоит из трёх условий, т.е. три разных регулярных выражения:

  • если буквосочетание в начале слова

    regexp_filter = (?i)\b(au) => 0

  • если за гласной

    regexp_filter = (?i)(a|e|i|o|u|y)(au) => \17

  • и остальные случаи, здесь даже \B в регулярном выражении можно не писать, потому что другие варианты уже были ранее обработаны

    regexp_filter = (?i)au =>

Иногда нет никакой разницы между двумя или тремя разными ситуациями и одного-два регулярных выражения на шаг хватит:

regexp_filter = (?i)j => 1

Глянем что получается:

mysql> call keywords('Ленин Ленина Lenina Lennina Lenin', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | 866       | 866        || 2    | 866       | 866        || 3    | 866       | 866        || 4    | 8666      | 8666       || 5    | 866       | 866        |+------+-----------+------------+mysql> call keywords('Плехановская Plechanovskaya Plehanovskaja Plekhanovska', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | 7856745   | 7856745    || 2    | 7856745   | 7856745    || 3    | 786745    | 786745     || 4    | 7856745   | 7856745    |+------+-----------+------------+

Опять исключительно числовой код, и опять QSUGGEST не поможет понять пользователя. Со всякими непонятками всё ещё справляется неплохо.

mysql> select * from STREETS where match('Veelkaseem'); show meta;+------+--------------------------------------+--------------+----------------------+| id   | aoguid                               | shortname    | offname              |+------+--------------------------------------+--------------+----------------------+|  873 | abdb0221-bfe8-4cf8-9217-0ed40b2f6f10 | проезд       | 30 лет ВЛКСМ         || 1208 | f1127b16-8a8e-4520-b1eb-6932654abdcd | ул           | 50 лет ВЛКСМ         |+------+--------------------------------------+--------------+----------------------+2 rows in set (0.00 sec)+---------------+-------+| Variable_name | Value |+---------------+-------+| total         | 2     || total_found   | 2     || time          | 0.000 || keyword[0]    | 78546 || docs[0]       | 2     || hits[0]       | 2     |+---------------+-------+

Ну и всё на этом, никакого чуда не произошло, - мы уже это видели.

Итоги

Реализовать Soundex, в целом получилось, из всех вариантов предпочтительнее оригинальный Soundex и NYSIIS, по той простой причине что работаем мы напрямую с буквенным кодом и можно вызвать CALL QSUGGEST, а Sphinx предложит варианты исправления, при том в NYSIIS много и всяких-разных. Улучшенный Soundex и Daitch-Mokotoff Soundex, должны снизить количество коллизий, охотно верю, что так и происходит, но проиндексировав 1286 названий улиц своего города, я не заметил, чтобы коллизии были хоть какой-то проблемой. Хотя встречаются:

mysql> call keywords('Воровского Вербовая', 'STREETS', 0);+------+------------+------------+| qpos | tokenized  | normalized |+------+------------+------------+| 1    | vorovskogo | v612       || 2    | verbovaja  | v612       |+------+------------+------------+

Это был оригинальный Soundex, в улучшенном уже нормально:

mysql> call keywords('Воровского Вербовая', 'STREETS', 0);+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | v9234     | v9234      || 2    | v9124     | v9124      |+------+-----------+------------+

Зато алгоритмы стали менее терпимы к опечаткам, особенно если она допущена в согласной. Например, оригинальный Soundex:

mysql> select * from STREETS where match('Ордхоникидзе');+------+--------------------------------------+-----------+--------------------------+| id   | aoguid                               | shortname | offname                  |+------+--------------------------------------+-----------+--------------------------+|   12 | 0278d3ee-4e17-4347-b128-33f8f62c59e0 | ул        | Орджоникидзе             |+------+--------------------------------------+-----------+--------------------------+

А остальные реализации ничего не возвращают.

Невозможность вызова QSUGGEST, тоже кажется существенной проблемой. Хотя, может я просто не умею её готовить. Если знаете, как делитесь рецептом.

В общем, не мудрствуя лукаво, мой совет: используйте оригинальный Soundex с транслитерацией. Единственный риск - коллизии, чтобы их разрешить, просто-напросто возьмите оригинальный запрос, и подсчитайте расстояние Левенштейна между ним, и теми несколькими записями что вам предлагает Sphinx.

Если возможности проводить постобработку нет, ни для исправления, ни для разрешения коллизий, то выбирайте улучшенный Soundex или Daitch-Mokotof - хотя бы Вербовую, вместо Воровского не получите. NYSIIS подойдёт если вы хотите пользователю, на запрос с ошибкой, предложить, как можно больше самых непохожих друг на друга вариантов.

Всё написанное испытано на sphinx-3.3.1, но должно работать на всём с версии 2.1.1-beta, в которой появились регулярные выражения. В том числе на Manticore. Разработчики ManticoreSearch, хвастаются что прогресс идёт в гору семимильными шагами. Может там будет поддержка и прочих алгоритмов, хотя бы для латиницы, а может и сразу для кириллицы.

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

P.S.

Статья получилось неожиданно большой, сам не ожидал. Поэтому про Metaphone не написал. Для него будет, или не будет, отдельная статья. Хотя принцип тот же:

  1. Транслит-регулярки

  2. Ещё регулярки

  3. ????

  4. PROFIT

Подробнее..

Как преобразовать текст в алгебру

23.03.2021 02:06:18 | Автор: admin

Авторы статьи: к.ф.-м.н. С.Б. Пшеничников, к.ф.-м.н. А.С. Вальков

Алгебра и язык (письменность) являются двумя разными инструментами познания. Если их объединить, то можно рассчитывать на появление новых методов машинного понимания. Определить смысл (понять) это вычислить как часть соотносится с целым. Современные поисковые алгоритмы уже имеют задачей распознавание смысла, а тензорные процессоры Google выполняют матричные умножения (свертки), необходимые для алгебраического подхода. При этом в семантическом анализе используются в основном статистические методы. В алгебре выглядело бы странным использование статистики при поиске, например, признаков делимости чисел. Использование алгебраического аппарата полезно также для интерпретации результатов вычислений при распознавании смысла текста.

Под текстом понимается последовательность знаков произвольной природы. Например, естественные языки, нотные тексты, генетические последовательности биополимеров, коды (кодовые таблицы как отношения знаков). В нотных текстах, записанных на нотоносце из одной линейки (нотоносец-нитка), знаками являются ноты, ключи, знаки аллитерации, указания громкости и темпа. В генетических текстах знаками-словами являются триплеты. Знаковые системы вкуса и обоняния пока существуют только как естественные (как образцы, вроде зоопарка). Для осязания существует рельефно-точечный тактильный код-шрифт Брайля. Хабом знаковых систем является семиотика [1], состоящая из трех тегов: семантики, синтактики и прагматики.

Пример языкового текста:

Множество это объект, являющийся множеством объектов. Полином это множество объектов-мономов, являющихся множеством объектов-сомножителей. (1)

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

(множество)1,1 (это)2,2 (объект)3,3 (являться)4,4 (множество)5,1 (объект)6,3 ("точка")7,7 (полином)8,8 (это)9,2 (множество)10,1 (объект)11,3 (моном)12,12 (являться)13,4 (множество)14,1 (объект)15,3 (сомножитель)16,16 ("точка")17,7(2)

В (2) правильная координатизация применена. Каждое слово (знак) текста приобретает два индекса, которые и есть координаты слова. Первая координата это уникальный номер слова в тексте. Со второй координатой слова немного сложнее. Она совпадает с первой координатой, если это слово впервые встречается в тексте. Например, это первые четыре слова (2). Пятое слово множество уже было в тексте это (множество)1,1. На пятом месте (первая координата) текста это слово повторяется. Оно впервые встретилось на первом месте текста. Затем повторяется на пятом. Поэтому в (2) это слово-знак находится с индексами-координатами 5,1: (множество)5,1. Таким образом, вторая координата это номер впервые встретившегося слова в тексте. Все слова, которые впервые встретились в тексте, имеют одинаковые координаты. При этом первая координата уникальна, а вторая может повторяться. В (2) пятое и шестое слово (по первой координате) уже имеются в тексте под номерами 1 и 3. Поэтому слов (...)5,5, (...)6,6 в тексте нет. Есть индексированные слова (множество)5,1 и (объект)6,3.

Слова с одинаковыми координатами называются словарем текста. Называть их алфавитом хуже (объем понятия число обозначаемых классов или множеств объектов меньше), потому что в алфавите отсутствует контекстная зависимость знаков. Но самые интересные знаковые последовательности с контекстной зависимостью и наличием знаков-омонимов (знаки одинаковые, контекстный смысл разный). Например, естественный язык и музыка без контекстов слов и нот полная бессмысленность. Знак-слово коса имеет четыре значения. Интонирование и интерпретация музыкального фрагмента зависит от предыдущих фрагментов. Блуждающие многозначные аккорды и функциональные инверсии основа атональной музыки.

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

(множество)1,1 (это)2,2 (объект)3,3 (являться)4,4 ("точка")7,7 (полином)8,8 (моном)12,12 (сомножитель)16,16(3)

При координатизации (1) (2) основными признаками слов стали индексы, а не то, что внутри круглых скобок (...)i,j. Например, для бинарного кода Морзе латинские буквы являются знаковыми последовательностями. Словарем является последовательность двух знаков-символов (точка и тире), совпадающие с буквами A и N. Порядок знаков в словаре несущественен. Остальные 24 латинские буквы являются кодовыми текстами. Единый текст (с помощью конкатенации) строится из всех букв (как фрагментов текста):

A\rightarrow (\cdot)_{1,1}(-)_{2,2}, B\rightarrow (-)_{3,2}(\cdot)_{4,1}(\cdot)_{5,1}(\cdot)_{6,1}, C\rightarrow (-)_{7,2}(\cdot)_{8,1}(-)_{9,2}(\cdot)_{10,1}, \ldots

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

Замечательно, что такие знаки существуют. Это матричные единицы. Матричные единицы Ei,j (имеют два индекса) это квадратные матрицы, в которых единица находится на пересечении i строки и j столбца, остальные элементы матрицы равны нулю. Например, при размерности n=2:

E_{1,2} = \left\| {\begin{array}{*{20}{c}} 0&1 \\ 0&0 \end{array}} \right\|, E_{2,1} = \left\| {\begin{array}{*{20}{c}} 0&0 \\ 1&0 \end{array}} \right\|, \;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;(4) E_{1,1} = {E_{1,2}}{E_{2,1}} = \left\| {\begin{array}{*{20}{c}} 1&0 \\ 0&0 \end{array}} \right\|,\;\;{E_{2,2}} = {E_{2,1}}{E_{1,2}} = \left\| {\begin{array}{*{20}{c}} 0&0 \\ 0&1 \end{array}} \right\|,

где E1,2, E2,1 и E1,1, E2,2 простые и составные матричные единицы (по аналогии с целыми числами). Произведение матричных единиц отлично от нуля (нулевой матрицы), если внутренние индексы произведения совпадают. Например, E1,1E2,1=0, E2,1E1,2=E2,2. Матричные единицы в дальнейшем будут рассматриваться как некоммутативные обобщения целых чисел. Левые и правые делители таких чисел могут различаться, а также имеются делители нуля. Но многие понятия модулярной арифметики [2] остаются справедливыми.

Обычному тексту (2) соответствует матричный текст P (сумма матричных единиц):

\begin{gathered} P = {E_{1,1}} + {E_{2,2}} + {E_{3,3}} + {E_{4,4}} + {E_{5,1}} + {E_{6,3}} + {E_{7,7}} + {E_{8,8}} + {E_{9,2}} + \\ + {E_{10,1}} + {E_{11,3}} + {E_{12,12}} + {E_{13,4}} + {E_{14,1}} + {E_{15,3}} + {E_{16,16}} + {E_{17,7}} \\ \end{gathered} \;\;\;\;\;\;(5)

Индексы (координаты) в (2) и (5) поэлементно совпадают, но P - математический объект (квадратная матрица). Разделитель (пробел) слов в (2) превращается в операцию сложения матриц. Исходный текст (2) восстанавливается по индексам из (5) забыванием алгебраических свойств (превращением операции сложения в разделитель-пробел) и обратным использованием кодовой таблицы координата-слово.

Универсальным свойством знаковых последовательностей в матричном виде (5) (текстовых полиномов) является уникальность первого индекса. На одном номере последовательности не могут находиться два и более знака. Второй индекс может повторяться.

Матричный словарь, соответствующий (3) имеет вид:

D_R = E_{1,1} + {E_{2,2}} + {E_{3,3}} + {E_{4,4}} + {E_{7,7}} + {E_{8,8}} + {E_{12,12}} + {E_{16,16}} \;\;\;\;\;\;\;(6)

Матричный словарь DR это матричный текст P с исключенными повторами. Размерность матриц P и DR imaximax, где imax номер последнего слова (знака) в тексте. В каждой строке матриц P и DR имеется не более одной единицы, остальные элементы равны нулю. Это свойство является следствием уникальности первого индекса. В матрице DR соответствующие словам текста единицы находятся на её главной диагонали. Остальные элементы диагонали и матрицы равны нулю.

Для матричных текстов выполняются соотношения:

P{D_R} = P,\;\;{D_R}P = {D_R},\;\;{P^2} = P,\;\;D_R^2 = {D_R},

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

Делимое, делитель и частное определяются для любых фрагментов матричного текста F1, F2, ,Fk почти также, как для целых чисел. Элемент Fi (делимый) делится на элемент Fj (делитель), если существует элемент Fij (частное) такой, что Fi=FijFj. В отличие от целых чисел частное располагается слева от делителя. Частное может не являться фрагментом текста.

Фрагмент текста в предельном случае может быть матричной единицей (матричным словом). По (4) матричные единицы сами могут быть простыми и составными. Из n2 матричных единиц 2(n-1) являются простыми, остальные (n2 2n 2) составные (произведения простых).

Левый идеал матричного текста это корпус всех текстов (всевозможных первых координат), которые можно составить из слов заданного словаря DR (вторых координат).

Правый идеал матричного текста это всевозможные номера слов в DR (вторых координат), которые можно разместить на заданных номерах слов в тексте (первых координат).

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

Понятие делимости матричных текстов обобщается на делимость идеалов матричных текстов. Свойства делимости матричных фрагментов текста имеют место при делении идеалов. Понятия НОД и НОК также обобщаются на случай идеалов матричных текстов.

Сравнения целых чисел также обобщаются на случай матричных текстов. Фрагменты матричных текстов F1, F2, ,Fk сравнимы по модулю (мере) Fmфрагмента , если остатки от деления F1, F2, ,Fk на Fm кратны.

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

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

Цель преобразований матричных текстов алгебраически обоснованная фрагментация P со значительным уменьшением числа используемых фрагментов по сравнению с комбинаторной оценкой, которая называется алгебраической структуризацией текста.

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

Перечисленные структурные признаки соответствующие части (фрагменты) текста. Для полиномиального представления матричного текста некоторые такие части это соответствующие некоммутативные базисы Грёбнера-Ширшова. Коммутативный базис Грёбнера-Ширшова заданного набора многочленов - это такой многочлен, что при делении любого многочлена из этого набора на этот базис получается нулевой остаток. Если многочлены некоммутативны (составляющие их мономы не перестановочные по умножению), то аналог этого базиса называется некоммутативным.

Алгебраическая структуризация текста примера (5) выглядит следующим образом:

P = F_1 + F_2,F_1(P) = E_{1,1} + E_{2,2} + E_{3,3} + E_{4,4} + E_{5,1} + {E_{6,3}} + {E_{7,7}}F_2 = E_{8,8} + {E_{9,2}} + {E_{10,1}} + {E_{11,3}} + {E_{12,12}} + {E_{13,4}} + {E_{14,1}} + {E_{15,3}} + {E_{16,16}} + E_{17,7} F_2 = \left( E_{9,2} + E_{10,5} + E_{11,6} + E_{13,4} + E_{14,5} + E_{15,6} + E_{17,7} \right)F_1+ \\ +E_{8,8}+E_{12,12}+E_{16,16}P=F_1+ \left(E_{9,2} + E_{10,5} + E_{11,6} + E_{13,4} + E_{14,5} + E_{15,6} + E_{17,7}\right)F_1 + \\ +E_{8,8} + E_{12,12} + E_{16,16}P=\left(E + E_{9,2} + E_{10,5} + E_{11,6} + E_{13,4} + E_{14,5} + E_{15,6} + E_{17,7}\right)\times \\ \times \left( E_{1,1} + E_{2,2} + E_{3,3} + E_{4,4} + E_{5,1} + E_{6,3} + E_{7,7} + E_{8,8} + E_{12,12} + E_{16,16} \right),P=\left(E + E_{9,2} + E_{10,5} + E_{11,6} + E_{13,4} + E_{14,5} + E_{15,6} + E_{17,7}\right) \left( D_R + E_{5,1} + E_{6,3} \right), \;\;\;\;\;\;\;(7)

где E единичная матрица. Используя свойства матричных единиц, исходный матричный текст в аддитивной форме (5) преобразован в мультипликативную форму (7). Сомножитель (DR+E5,1+E6,3) является некоммутативным аналогом базиса Грёбнера-Ширшова для коммутативных многочленов. Бриллиантовая лемма Ширшова выполняется в сомножителе (DR+E5,1+E6,3) имеются зацепления (повторения) справа по второму индексу, но они разрешимы (имеют общие делители).

При преобразовании (редукции) (7) произошло преобразовании словаря текста:

D_R \rightarrow \left( E_{5,1} +E_{6,3} +E \right) D_R,\;\;\;\;\;\;\;\;\;\;\; (8)

В новом словаре (базисе идеала) появились слова E5,1и E6,3. Это те же слова E1,1знаки (множество) и E3,3 (объект), но находящиеся на пятом и шестом местах текста. Слова как знаки те же, но смысл повторяющихся слов в тексте меняется. Слова определяются контекстами. Слова близки, если их контексты содержат хотя бы одно общее слово. Контексты тем более близки, чем больше общих слов из соответствующего словаря (общих вторых индексов) они содержат.

В естественных языках множественность контекстов слова является причиной неоднозначности понимания смысла слов. Смысл по Фреге это соответствующая часть значений знака (слова). Значения слова это все его контексты (свойства). Например, пусть знак это слово объект. Все его значения в тексте: множество, элемент множества, моном и сомножитель. Это означает, что слово-знак объект обозначает четыре омонима. Смысл это часть значений, например, только моном.

Словарь DR (6) в начале структуризации являлся словарем знаков-слов. В процессе структуризации он преобразуется в контекстно-зависимые матричные конструкции n-грамм (сочетаний слов-знаков, учитывающий их взаимный порядок и расстояние в тексте). Смысловая разметка текста основывается на расширении исходного словаря текста омонимами, а сам текст уже строится по такому расширенному словарю из некоммутативного базиса.

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

Расширенный словарь (базис) вместе с контекстами повторяющихся слов называется матричным контекстным словарем текста.

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

Матричный словарь антонимов - это фрагмент контекстного словаря для слов с противоположными контекстами. Признаком противоположности в языковых текстах является наличие в контекстах отрицательных слов (частиц, местоимений и наречий).

Иерархические заголовки матричного текста это фрагменты базиса, имеющие соответствующую частотность слов синонимического словаря. Например, для (8) высший заголовок две биграммы (пары слов) множество объект объект множество.

Предисловие, введение, заключение, аннотация, реферат - это заголовки, дополненные элементами базиса меньшей частотности, и вычетами, вошедшими в базис (как в алгоритме Бухбергера). Для текста примера вычет это остаток E8,8+E12,12+E16,16 в (7) или в исходном виде (полином)8,8... (моном)12,12...(сомножитель)16,16 остаток от разложения F2 по F1. Именно этими элементами базиса (вычетами) отличаются контексты биграмм множество объект объект множество.

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

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

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

Более строгое и общее описание алгебры текста изложено в [3].

Литература

  1. Шрейдер Ю. А. Логика знаковых систем. 2010.

  2. Виленкин Н.Я. Сравнения и классы вычетов. Журнал "Квант"

  3. Алгебра текста - С.Б. Пшеничников - препринт на researchgate

Подробнее..

Продолжаем интернационализацию поиска по адресам с помощью Sphinx или Manticore. Теперь Metaphone

05.04.2021 08:09:25 | Автор: admin

Это продолжение публикации Интернационализация поиска по городским адресам. Реализуем русскоязычный Soundex на Sphinx Search, в которой я разбирал, как реализовать поддержку фонетических алгоритмов Soundex в Sphinx Search, для текста написанного кириллицей. Для текста на латинице поддержка Soundex уже есть. С Metphone аналогично, для латиницы есть, для кириллицы не очень, но попытаемся исправить этот досадный факт с помощью транслитерации, регулярных выражений и напильника.

Это прямое продолжение, в котором разберём как реализовать оригинальный Metaphone, русскийMetaphone (в том смысле что транслитерация не понадобится), Caverphone, и не сможем сделать DoubleMetaphone.

Реализация подойдёт как для использования на платформе SphinxSearch, так и ManticoreSearch.

В конце, в качестве бонуса, посмотрим как Metaphone воспримет "ракомакофон".

Докер образ

Подготовил докер образ tkachenkoivan/searchfonetic, для того чтобы можно было "пощупать" результат. В образ добавлены все индексы и из этой публикации и из предыдущей, но, внимание, названия индексов из предыдущей публикации не соответствуют тому что хранится в образе. Почему? Потому что хорошая мысля приходит опосля.

Описание алгоритмов, всё так же, брал из публикации "фонетические алгоритмы". Постараюсь как можно меньше дублировать текст, написанный в ней.

Оригинальный Metaphone

Реализуется элементарно, создаются регулярные выражения для транслитерации:

regexp_filter = (А|а) => aregexp_filter = (Б|б) => bregexp_filter = (В|в) => v

И включаем metaphone:

morphology = metaphone

Всё, как и с оригинальным Soundex. В прошлый раз, мы пришли к выводу, что лучше всего, из всех Soundex алгоритмов, использовать именно оригинальный Soundex, единственный недостаток которого коллизии, разрешается вычислением расстояния Левенштейна.

В этот раз, забегая вперёд, скажу, что снова сделал бы свой выбор в пользу оригинального Metaphone + транслит. А вот причина небанальна.

Дело в том что у Sphinx есть в такой параметр blend_chars. Смысл следующий, Sphinx индексирует по словам, а слова он находит по разделителям, например, если между буквами есть пробел, значит буквы два разных слова, перенос строки, табуляция, знаки препинания и т.д., и т.п. Но могут быть символы, которые одновременно разделяют слова, а могут быть и частью одного слова, например, &. M&Ms это сколько слов? А Рога&Копыта? Для таких случаев и существует blend_chars.

И тут можно пойти на хитрость, и добавить в blend_chars пробел:

blend_chars = U+0020

Теперь, когда кто-то будет искать улицу Мориса Тореза, то найдёт её, даже если подумает, что это одно слово. Да что там слитное написание, он её найдёт даже если решит, что Мать Тереза и Морис Торез родственники.

mysql> select * from metaphone where match('Морисатереза');+------+--------------------------------------+-----------+---------------------------+| id   | aoguid                               | shortname | offname                   |+------+--------------------------------------+-----------+---------------------------+| 1130 | e21aec85-0f63-4367-b9bb-1943b2b5a8fb | ул        | Мориса Тореза             |+------+--------------------------------------+-----------+---------------------------+

Можем увидеть, как работает индекс для Мориса Тореза, вызвав call keywords:

mysql> call keywords ('Мориса Тореза', 'metaphone');+------+---------------+------------+| qpos | tokenized     | normalized |+------+---------------+------------+| 1    | morisa toreza | MRSTRS     || 1    | morisa        | MRS        || 2    | toreza        | TRS        |+------+---------------+------------+

Обратите внимание, что два слова было воспринято как три: morisa, toreza и morisa toreza, притом при создании кода Metaphone, пробел был съеден.

Это особенность реализации Metaphone в Sphinx Search. Самостоятельно, с помощью регулярных выражений её не реализовать. Нет, конечно мы можем добавить регулярное выражение, удаляющее пробелы:

regexp_filter = [ ] => 

но тогда Мориса Тореза, и другие, будут всегда восприниматься как одно слово, а нам этого не надо.

Давайте сравним как работают другие индексы, в том числе те, которые ещё разобрать не успели, но они будут ниже.

Caverphone пробел сохраняет, поэтому при слитном написании просто не находит.

mysql> call keywords ('Мориса Тореза', 'caverphone');+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | mrsa trza | mrsa trza  || 1    | mrsa      | mrsa       || 2    | trza      | trza       |+------+-----------+------------+mysql> select * from caverphone where match('Морисатереза');Empty set (0.00 sec)

Оригинальный Soundex (из предыдущей публикации), в котором используется базовая реализация Sphinx, просто сходит с ума, и не понимает, как кодировать слово, в котором встретился пробел, morisa и toreza закодирован, а morisa toreza нет:

mysql> call keywords ('Мориса Тореза', 'simple_soundex');+------+---------------+---------------+| qpos | tokenized     | normalized    |+------+---------------+---------------+| 1    | morisa toreza | morisa toreza || 1    | morisa        | m620          || 2    | toreza        | t620          |+------+---------------+---------------+

Потому не включайте пробел в blend_chars в большинстве случаем это не просто бесполезно, но и вредно. Единственно исключение metaphone. И это позволяет решить самую сложную для исправления опечатку (с машинной точки зрения) опечатку в пробелах: как наличие лишних, так и отсутствие нужных.

А это дорогого стоит.

Double Metaphone

Для двойного Metaphone планировал использовать два индекса, вместо одного, как обычно, а затем искать поочерёдно в обоих.

К сожалению, я не понял, как реализовать двойной Metaphone с помощью регулярных выражений. Когда я начал искать по нему описание, меня как будто в гугле забанили, зато нашёл много реализаций на различных языках программирования, например, DoubleMetaphone.java. Я даже попытался понять этот алгоритм и реализовать его в виде регулярок, но дошёл до буквы C, и понял, что рассудок покидает меня.

Если Вы гуру гуглапоиска, или просто знаете способ, как сделать реализацию с помощью регулярных выражений пишите, пишите мне, пишите публикацию на хабр, придумаем как прикрутить его к Sphinx и Manticore.

Но будет нечестно, если про двойной Metaphone совсем ничего не написать. Опишу как бы я его сделал, если было бы ну очень нужно. Sphinx не понадобится. Но придётся программировать.

Для начала я бы подключил уже готовую библиотеку в которой он реализован, благо их нашлось множество для разных языков, я использую Java, поэтому подключаю Commons Codec. Библиотека не работает с кириллицей требуется транслитерация, и кодирует только первое слово. Поэтому если вам надо реализовать кодирование большого текста, разделённого пробелами то самостоятельно придётся разбивать строку на слова, и в цикле их перебирать.

В таблице БД, где хранятся данные, я бы добавил две новые колонки, в которых бы хранил закодированное значение. Это один из недостатков данной реализации требуется допиливать таблицы в БД.

Затем, перед вставкой новых записей в таблицу, я бы вызывал:

DoubleMetaphone dm = new DoubleMetaphone();String metaphone1 = dm.doubleMetaphone("Text", false);String metaphone2 = dm.doubleMetaphone("Text", true);

И сохранял metaphone1 и metaphone2 вместе с данными.

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

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

Без Sphinx всё стало очень неудобно.

Русский Metaphone

Не подойдёт для целей интернационализации.

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

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

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

mysql> call keywords ('Ленина Ленин', 'rus_metaphone');+------+--------------+--------------+| qpos | tokenized    | normalized   |+------+--------------+--------------+| 1    | линина       | линина       || 2    | линин        | линин        |+------+--------------+--------------+

Реализуем регулярные выражения. Полный конфигурационный файл, как и ранее, лежит на GitHub Gist manticore.conf.

  • Переделываем гласные:

regexp_filter = (?i)(йо|ио|йе|ие) => иregexp_filter = (?i)(о|ы|я) => аregexp_filter = (?i)(е|ё|э) => иregexp_filter = (?i)(ю) => у
  • Для всех согласных букв, за которыми следует любая согласная, кроме Л, М, Н или Р, провести оглушение:

regexp_filter = (?i)(б)(б|в|г|д|ж|з|й|к|п|с|т|ф|х|ц|ч|ш|щ) => п\2regexp_filter = (?i)(г)(б|в|г|д|ж|з|й|к|п|с|т|ф|х|ц|ч|ш|щ) => к\2regexp_filter = (?i)(в)(б|в|г|д|ж|з|й|к|п|с|т|ф|х|ц|ч|ш|щ) => ф\2regexp_filter = (?i)(д)(б|в|г|д|ж|з|й|к|п|с|т|ф|х|ц|ч|ш|щ) => т\2regexp_filter = (?i)(ж)(б|в|г|д|ж|з|й|к|п|с|т|ф|х|ц|ч|ш|щ) => ш\2regexp_filter = (?i)(з)(б|в|г|д|ж|з|й|к|п|с|т|ф|х|ц|ч|ш|щ) => с\2
  • Для согласных на конце слова, провести оглушение

regexp_filter = (?i)б\b => пregexp_filter = (?i)г\b => кregexp_filter = (?i)в\b => фregexp_filter = (?i)д\b => тregexp_filter = (?i)ж\b => шregexp_filter = (?i)з\b => з
  • Склеиваем ТС и ДС в Ц

regexp_filter = (?i)(тс|дс|ц) => ц

Caverphone

Здесь сначала транслитерация.

  • Затем, нужно перевести транслитерированное в нижний регистр:

regexp_filter = (A|a) => aregexp_filter = (B|b) => b

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

  • Удалить e на конце

regexp_filter = e\b =>
  • Происходит преобразование начала слова, но это актуально для новозеландских фамилий, этот шаг можно и пропустить:

regexp_filter = \b(cough) => cou2fregexp_filter = \b(rough) => rou2f
  • Провести замены символов

regexp_filter = (cq) => 2qregexp_filter = (ci) => si
  • Заменить все гласные в начале слова на a, в остальных случаях на 3

regexp_filter = (?i)\b(a|e|i|o|u|y) => Aregexp_filter = (?i)(a|e|i|o|u|y) => 3
  • Провести очередные замены

regexp_filter = (j) => yregexp_filter = \b(y3) => Y3
  • Удалить все цифры 2

regexp_filter = 2 => 
  • Если на конце слова осталась цифра 3, то заменить её на A

regexp_filter = 3\b => A
  • Удалить все цифры 3

regexp_filter = 3 =>

До 10 символов не сокращаю и не дополняю.

Проверим:

mysql> select * from caverphone where match ('Ленина');+------+--------------------------------------+-----------+------------------+| id   | aoguid                               | shortname | offname          |+------+--------------------------------------+-----------+------------------+|    5 | 01339f2b-6907-4cb8-919b-b71dbed23f06 | ул        | Линейная         ||  387 | 4b919f60-7f5d-4b9e-99af-a7a02d344767 | ул        | Ленина           |+------+--------------------------------------+-----------+------------------+

Кроме Ленина находит и Линейная. Согласен, некоторое сходство есть, другие алгоритмы так не смогли, ну разве что Daitch Mokotoff Soundex из предыдущей публикации выкинул что-то подобное с Лунная:

mysql> select * from daitch_mokotoff_soundex where match ('Ленина');+------+--------------------------------------+-----------+--------------+| id   | aoguid                               | shortname | offname      |+------+--------------------------------------+-----------+--------------+|  387 | 4b919f60-7f5d-4b9e-99af-a7a02d344767 | ул        | Ленина       ||  541 | 69b8220e-a42d-4fec-a346-1df56370c363 | ул        | Лунная       |+------+--------------------------------------+-----------+--------------+

Можем посмотреть как это всё кодируется:

mysql> call keywords ('Ленина Линейная Лунная', 'caverphone');+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | lnna      | lnna       || 2    | lnna      | lnna       || 3    | lna       | lna        |+------+-----------+------------+mysql> call keywords ('Ленина Линейная Лунная', 'daitch_mokotoff_soundex');+------+-----------+------------+| qpos | tokenized | normalized |+------+-----------+------------+| 1    | 866       | 866        || 2    | 8616      | 8616       || 3    | 866       | 866        |+------+-----------+------------+

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

Бонус: ищем ракомакофон. Вместо заключения

Это лишено практического смысла, но наблюдение забавное, поэтому напишу. Just for fun.

Помните ракомакофон, который слышится вместо rock the microphone?! Было интересно, сможет ли Metaphone понять ракомакофон. И ведь почти!

Во-первых, добавляем пробел в blend_chars, ведь нам надо чтобы три слова rock the microphone, воспринимались как одно:

blend_chars = U+0020

Поскольку у нас только один алгоритм умеет адекватно работать в такой ситуации - оригинальный metaphone, то его и применим.

Проверим с помощью keywords как оно воспринимается Sphinx:

mysql> call keywords ('ракомакофон', 'metaphone');+------+-------------+------------+| qpos | tokenized   | normalized |+------+-------------+------------+| 1    | rakomakofon | RKMKFN     |+------+-------------+------------+

И rock the microphone:

mysql> call keywords ('rock the microphone', 'metaphone');+------+---------------------+------------+| qpos | tokenized           | normalized |+------+---------------------+------------+| 1    | rock the microphone | RK0MKRFN   || 1    | rock                | RK         || 2    | the                 | 0          || 3    | microphone          | MKRFN      |+------+---------------------+------------+

Получилось RK0MKRFN, и RKMKFN, расстояние Левенштейна между ними всего 2(!). А если найти способ исключить the из кодирования, то получится RKMKRFN:

mysql> call keywords ('rock microphone', 'metaphone');+------+-----------------+------------+| qpos | tokenized       | normalized |+------+-----------------+------------+| 1    | rock microphone | RKMKRFN    || 1    | rock            | RK         || 2    | microphone      | MKRFN      |+------+-----------------+------------+

Между RKMKRFN и RKMKFN, расстояние Левенштейна всего 1! Мы почти у цели.

Проблема убрать the, параметр stopwords здесь не поможет, ибо из-за blend_chars = U+0020 the не будет восприниматься самостоятельно. Но даже если удастся сделать предобработку, то всё равно расстояние в 1, не позволит обнаружить похожий.

Надежда на qsuggest не оправдалась, - не даст подсказок. Почему? Можно заметить, что при вызове keywords есть два столбца tokenized и normalized, qsuggest даёт подсказку по столбцу tokenized и измеряет расстояние Левенштейна относительно него, qsuggest всё равно, что там, в normalized, расстояние равно 1.

Поэтому наблюдение забавное, но не практичное.

Подробнее..

Как преобразовать текст в алгебру примеры

10.04.2021 22:12:11 | Автор: admin

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

1 Код Морзе-Вейля-Герке как алгебра матричных единиц

В азбуке Морзе знаковые последовательности (тексты) 26 латинских букв состоят из точек и тире. Пример выбран из-за предельной краткости словаря ("точка" и "тире").

Слова здесь - точки или тире. 26 букв азбуки - тексты из таких слов. У каждого слова две координаты. Первая координата номер слова (точки или тире) в этой букве (от одного до четырех). Вторая координата номер в словаре (1 или 2). Словарь E11 ("точка") и E22 ("тире").

D_R=E_{11}+E_{22}Таблица 1. Азбука Морзе: латинские буквы как знаковые последовательности (тексты)Таблица 1. Азбука Морзе: латинские буквы как знаковые последовательности (тексты)

Каждой букве (знаковой последовательности) с номером из Таблицы 1 можно поставить в соответствие матричный полином P из матричных единиц 4x4 по формуле (8) из статьи [1].

Таблица 2: Азбука Морзе: буквы как матричные полиномыТаблица 2: Азбука Морзе: буквы как матричные полиномы

Например, букве Q (17) ставится в соответствие матричный полином:

E_{12}+E_{22}+E_{31}+E_{42}= \begin{Vmatrix} 0 & 1 & 0 & 0\\ 0 & 1 & 0 & 0\\ 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 \end{Vmatrix}.

Свойством всех 26 полиномов-букв таблицы 2 является то, что крайними правыми сомножителями являются только три матричные единицы E12, E21, E32

Если все 26 полиномов Таблицы 2 представить столбцом ||P||, а также из того, что для матриц и столбцов выполняется:

 \begin{Vmatrix} a_{11} & \ldots & a_{1n}\\ \ldots & \ldots & \ldots\\ a_{m1} & \ldots & a_{mn} \end{Vmatrix} \begin{Vmatrix} b_{1} \\ \ldots \\ b_{n} \end{Vmatrix}= \begin{Vmatrix} a_{11} \\ \ldots \\ a_{m1} \end{Vmatrix}b_1+\ldots + \begin{Vmatrix} a_{1n} \\ \ldots \\ a_{mn} \end{Vmatrix}b_n,

то азбука Морзе структурируется в три левые идеалы наборов матричных полиномов Таблицы 2 с базисами ||P||1, ||P||2, ||P||3.

 \left\|P\right\|=\left\|P\right\|_1\left\|P\right\|_1=\left\|P\right\|_2\left\|P\right\|_2=\left\|P\right\|_3\left\|P\right\|_3,

где

\left\|P\right\|_1=\begin{Vmatrix} E_{12} \\ E_{21} \\ E_{32} \end{Vmatrix}, \left\|P\right\|_2=\begin{Vmatrix} E_{12} \\ E_{21}E_{12} \\ E_{12}+E_{21}E_{12} \\ E_{12}E_{21} \\ E_{21} \\ E_{21}+E_{12}E_{21} \\ E_{32} E_{21} + E_{43}E_{32} E_{21} \\ E_{43}E_{32} E_{21} \\ E_{32} E_{21} \\ E_{32} \\ E_{32} + E_{43}E_{32} \\ E_{43}E_{32} \end{Vmatrix}, \left\|P\right\|_3=\begin{Vmatrix} E_{12}E_{21} \\ E_{12} \\ E_{21} \\ E_{21}E_{12} \\ E_{32}E_{21} \\ E_{32} \\ E_{43}E_{32} E_{21} \\ E_{43}E_{32} \end{Vmatrix}, (1.1)

||P||2(||P||2)T - симметричная матрица - число в диагональных элементах это число базисных элементов (простых и составных матричных единиц), принадлежащих букве, в других элементах число совпадающих базисных элементов в соответствующей паре знаковых последовательностей (букв) - после нормализации характеризует важность буквы в азбуке.

(||P||2)T ||P||2 - симметричная матрица - число в диагональных элементах это число букв, принадлежащих базисным элементам, в недиагональных элементах число совпадающих букв в соответствующей паре базисных элементов после нормализации характеризует важность базисного элемента (заголовка) в азбуке.

Азбука Морзе с алгебраически структурирована в три идеала (класса) с базисами (1.3). Представление азбуки через идеалы описывает все подобные коды с базисами (1.3). Представление азбуки через идеалы приведено в Таблицах 3 и 4:

Таблица 3: Прямая индексацияТаблица 3: Прямая индексацияТаблица 4: Обратная индексацияТаблица 4: Обратная индексация

Азбука Морзе: ABCDEFGHIJKLMNOPQRSTUVWXYZ

из-за свойств матричных полиномов(крайние правые сомножители - только три матричные единицыE12, E21, E32) разбивается на три класса (три идеала) тремя образующими E12, E21, E32:

E12 - заголовок тех букв, которые имеют знак тире на первом месте 4-знаковой последовательности:

_BCD__G___K_MNO_Q__T___XYZ (13 букв)

E21 - заголовок тех букв, которые имеют знак точка на втором месте 4-знаковой последовательности:

_BCD_F_HI_K__N____S_UV_XY_ (13 букв)

E32 - заголовок тех букв, которые имеют знак тире на третьем месте 4-знаковой последовательности:

__C__F___JK ___OP____U_W_Y_ (9букв)

2 Алгебра математического текста

В примере [1] языковый текст превращался в математический объект (матричный полином), с которым можно совершать алгебраические операции для анализа и синтеза текстов. В этом примере совершается обратное преобразование математические объекты (формулы) сначала рассматриваются как тексты (знаковые последовательности), которые затем превращаются опять в математические объекты, но иные, чем исходные. Такая новая форма позволяет более системно находить свойства математических объектов для сравнения и классификации.

Формулы объема конуса VK, цилиндра Vци тора VТ:

 V_K=\frac{1}{3}\pi R_1^2H_1, V_{\text{Ц}}=\pi R_2^2H_2, V_T=\pi^2\left(R_3+R_4\right)r,\ \ \ \ \ \ \ \ \ (2.1)

рассматриваются как тексты. Это означает, что входящие в тексты знаки не являются математическими объектами и для них отсутствуют алгебраические операции. Например, R12 этоR1R1, R1 это не произведение двух чисел, а просто последовательность двух знаков. Знаки в (1): R1и H1 радиус основания и высота конуса,R2 иH2 радиус основания и высота цилиндра, R3 внутренний радиус тора, R4 внешний радиус тора, r радиус образующей окружности тора, это число .

Для семиотического анализа формул как текстов важно наличие повторов знаков. Повторы определяют закономерности. В формулах (2.1) повторов знаков на самом деле больше, чем указанные повторы знака . ЗнакиR1, R2, R3, R4, H1, H2 и r это длины отрезков. Тогда один из знаков, например , является простым (эталон длины), а остальные знаки составными: R1=ar, R2=br, R3=cr, R4=dr, H1=er, H2=fr . Тогда правые части формул (2.1):

\begin{gathered} \frac{1}{3}\pi ararer \\ \pi brbrfr \\ \pi \pi \left(c+d \right)rr \end{gathered} \ \ \ \ \ \ \ \ \ \ \ \ (2.2)

Или в индексной форме:

\begin{gathered} \left(\frac{1}{3}\right)_{1,1}(\pi)_{2,2}(a)_{3,3} (r)_{4,4} (a)_{5,3} (r)_{6,4} (e)_{7,7} (r)_{8,4} \\ (\pi)_{9,2} (b)_{10,10} (r)_{11,4} (b)_{12,10} (r)_{13,4} (f)_{14,14} (r)_{15,4} \\ (\pi)_{16,2} (\pi)_{17,2} \left(c+d \right)_{18,18} (r)_{19,4}(r)_{20,4} \end{gathered} \ \ \ \ \ \ \ \ \ (2.3)

Формулы (2.2) как полином матричных единиц из трех фрагментов

 P=F_1(P)+F_2(P)+F_3(P), \ \ \ \ \ \ \ \ \ \ (2.4)

где:

\begin{gathered} F_1(P) = D_L\left(E_{1,1}+E_{2,2}+E_{3,3}+E_{4,4}+E_{5,3}+E_{6,4}+E_{7,7}+E_{8,4}\right)D_R \\ F_2(P) = D_L\left(E_{9,2}+E_{10,10}+E_{11,4}+E_{12,10}+E_{13,4}+E_{14,14}+E_{15,4}\right) D_R \\ F_3(P) = D_L\left(E_{16,2}+E_{17,2}+E_{18,18}+E_{19,4}+E_{20,4}\right) D_R \\ D_R = E_{1,1}+E_{2,2}+E_{3,3}+E_{4,4}+E_{7,7}+E_{10,10}+E_{14,14}+E_{18,18} \\ D_L = E_{1,1}+E_{2,2}+E_{3,3}+E_{4,4}+E_{5,5}+E_{6,6}+E_{7,7}+ \ldots + E_{20,20} = E \\ D_L=D_R+E_{5,5}+E_{6,6}+E_{5,5}+E_{8,8}+E_{5,5}+E_{9,9} \end{gathered}

Или в блочно-матричной форме:

В столбцах P находятся знаки из трех формул (2.1) . Если в столбце два нуля, это означает, что соответствующий знак имеется только в одной формуле. Например, знак 1/3 (или E1,1), два знака a (или E3,3+E5,3) , один знак e (или E7,7) имеются только в первой формуле для конуса (первая строка (2.5)). Только в цилиндре (вторая строка (2.5)) имеются два знака b (или E11,11+E13,11) и один f (или E15,15). Только в торе (третья строка (2.5)) имеется знак (c+d) (или E20,20). Общие знаки конуса, цилиндра и тора находятся во втором и четвертом столбцах (2.5). Тогда:

\begin{gathered} P = P_{\text{частн}_1}P_{\text{дел}_1}+P_{\text{ост}} \\ P = P_{\text{частн}_2}P_{\text{дел}_1}+P_{\text{ост}} \end{gathered}

где:

 \begin{gathered} P_{\text{частн}_1} = \left(E_{2,18}+E_{4,12}+E_{6,14}+E_{8,16}\right) +\left(E_{10,18}+E_{12,12}+E_{14,4}+E_{16,16}\right)+\\ +\left(E_{18,18}+E_{19,19}+E_{21,12}+E_{22,14}\right), \\ P_{\text{частн}_2} = (E_{2,2}+E_{4,4}+E_{6,4}+E_{8,4})+(E_{10,2}+E_{12,4}+E_{14,4}+E_{16,4})+ \\ +(E_{18,2}+E_{19,2}+E_{21,4}+E_{22,4}), \\ P_{\text{дел}_1} = E_{18,2} + E_{19,2}+E_{12,4} + E_{14,4} + E_{16,4}, \\ P_{\text{дел}_2} = E_{2,2} + E_{4,4}, \\ P_{\text{ост}} = E_{1,1}+E_{3,3} + E_{5,3}+E_{7,7}+E_{11,11} + E_{13,11}+E_{15,15}+E_{20,20}.\\ \end{gathered}

В (2.6) матричный текст раскладывается по разным базисам Pдел1 и Pдел2. Базис Pдел1учитывает взаимные положения между повторяющимися знаками относительно тора в формулах (2.1). Базис Pдел2 учитывает положения между повторяющимися знаками относительно знаков словаря DR в формулах (2.1). В общем случае учет положения знаков в формулах существенен, если знаки некоммутативны (например, знаки это матрицы, вектора, тензоры, гиперкомплексные числа). Но и в скалярном это полезно, например, канонической является формула площади круга r2, а не r2 .

Базис Гребнёра-Ширшова для (2.6):

 \begin{gathered} P_{\text{дел}_1}+P_{\text{ост}} \\ P_{\text{дел}_2}+P_{\text{ост}} \end{gathered}

Тогда:

 \begin{gathered} P= P_{\text{частн}_1} \left( P_{\text{дел}_1}+P_{\text{ост}} \right) \\ P= P_{\text{частн}_2} \left( P_{\text{дел}_2}+P_{\text{ост}} \right) \end{gathered}

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

Метод алгебраическая структуризация текстов позволяет для текстов разной природы найти соответствующие классификаторы и словари. Т. е. классифицировать тексты без априорного задания признаков классификации и наименования классов. Такая классификация называется категоризацией или апостериорной классификацией. Например, для (2.3) классификационными признаками становятся:

  • Pдел1и Pдел2 (общие и r в разных местах формул),

  • общее число слагаемых в круглых скобках Pчастн1и Pчастн2 (четыре),

  • соотношения числа и r в круглых скобках Pчастн1и Pчастн2 (1,1,2 и 3,3,2),

  • сомножители мультипликативной формы Pчастн1 и Pчастн2,

  • всевозможные фрагменты Pост(вычеты, как класс формул с остатком-фрагментом).

Наименования классов совпадает с наименование признаков и их сочетаний.

Литература

[1] Пшеничников C.Б. Алгебра текста. Researchgate Preprint, 2021

Подробнее..

Ещё один поиск Вк по фото

20.03.2021 16:14:02 | Автор: admin

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

1. Предыстория

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

В то время мне об этом сервисе говорили и ленты новостей, и друзья, я отвечал "ну да, прикольно", и только. Но спустя пару лет, в начале октябре 2018 на каком-то айтишном форуме я захотел связаться с одним пользователем по специфическому вопросу, вот только он туда уже давно не заходил. Зато там было его хорошее фото, и тут-то я вспомнил про крутой сервис! Побежал на их сайт и разочаровался в сентябре 2018, буквально за месяц, они перестали предоставлять свои услуги физ.лицам, и бесплатно, и даже за деньги, перейдя в сегмент b2b и b2g. Оно и понятно, пиар уже сработал, а этических вопросов так возникает куда меньше. Но меня, законопослушного гражданина, это огорчило. И не только меня: фан-группы ФайндФейса пестрили сообщениями о том, что люди готовы заплатить в 10 раз больше, лишь бы им помогли найти нужного человека.

Я стал искать аналоги, но они все были либо точным поиском, но по крайне ограниченной выборке вроде нескольких сотен знаменитостей из Википедии, либо обширным, но почти бесполезным по точности поиском через Гугл/Яндекс.

Пару рабочих дней я размышлял, что же сложного в создании такого сервиса, который бы и точно искал людей, и по всему Вк? Решил, что ничего, ведь у меня тогда уже были базовые познания в data science, разработке и администрировании. Поэтому в пятницу, приехав с работы домой, я взялся за дело. За вечер я накидал скрипт, который парсит профили Вк, находит фото, индексирует по ним лица и сохраняет в БД. Потом просидел ещё пару суток почти без сна, заставил это дело безостановочно работать на своём сервере. Началась новая трудовая неделя, я был очень уставший, но ещё больше довольный и полный энтузиазма! Ведь мой скрипт медленно, но безостановочно бежал по всему Вк.

2. Техническое устройство

2.1. Индексирование

Как вы считаете, что происходит после того, как вы отправляете запрос в любую крупную поисковую систему? Не важно, поиск текста в Яндексе, Google или поиск лиц в FindFace или моём сервисе. Многие, особенно не-айтишники, с трудном представляют внутренние механики технических процессов, а они бывают нетривиальны даже казалось бы в простых задачах. В случае поисковых систем магия заключается в том, что при получении запроса они не начинают обегать все страницы в интернете, ища там ваш текст, или весь Вк, сравнивая вашу фотку со всеми подряд, это бы занимало астрономические объёмы времени. Вместо этого, поисковые системы сперва индексируют нужные данные. В случае текста (и подобных тексту данных вроде ДНК) в ближайшем приближении могут использоваться хэш-таблицы или префиксные деревья. В случае фоток тоже нужны индексы, которые сильно сократят время поиска. Для этого я использовал библиотеку face_recognition, которая позволяет преобразовать фото лица, если правильно помню, в 128-мерный вектор признаков со значениями от -1 до 1 (далее буду называть его просто хэш). Для поиска человека по фото, нам нужно просто пробежаться по всем фото из коллекции, считая евклидово расстояние между векторами-хэшами из запроса и набора подобный пример, реализованный на Питоне, доступен на сайте упомянутой библиотеки. Да, такая операция поиска тоже не дешёвая, но об этом позже.

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

Конечно, не только лимиты АПИ повышать надо, но и объёмы CPU. Изначально я развернул скрипт на маленьком VPS, который создавался для простого личного сайта. В подмогу ему, я взял ещё один VPS, в несколько раз мощнее. Потом я решил, что и этого мало, взял ещё и целый выделенный сервер, который сильнее моего собственного рабочего компьютера :D Не энтерпрайз-левел, но производительность стала меня устраивать, хотя расходы и выросли до 15 тысяч руб/месяц, что для меня тогда было весьма ощутимой тратой.

2.2. Подобие архитектуры и DevOps'а

Если у вас есть опыт в бэкэнд разработке, то сразу встаёт вопрос, как я заставил несколько серверов работать в тандеме, а не независимо, дублируя одну и ту же работу? На самом деле, решение классическое: один микросервис брал на себя роль ведущего, мастера, хранил состояние всей системы, и выдавал задания объёмом в тысячу профилей Вк воркерам, которые быстрее или медленнее их индексировали, а результаты, в том числе промежуточные, возвращали мастеру; если же мастер за определённый промежуток времени не получал ответа от воркера, он давал это задание другому, с учётом промежуточного прогресса.

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

Для автоматизации настройки окружения, токенов и т.п были написаны скрипты на Питоне, которые подключались к целевой машине по SSH и ставили всё что нужно. Позже я узнал, что у меня костыльный велосипед, есть качественные решения, но всё равно было интересно посмотреть подноготные детали. Из прикольного, пришлось также разобраться, что есть разные ВМ и средства виртуализации, что некоторое ПО не работает в определённых конфигурациях, благодаря чему виртуалки на Xen и OpenVZ с казалось бы одинаковыми ресурсами могут отличаться в цене на 40%.

2.3. Поиск

Помимо ролей мастера и воркера, есть роль поискового микросервиса. Проиндексированные фото Вк и айдишники их профилей сохраняются в БД, точнее, MySQL v5.7 и алгоритм поиска я переписал с Python на SQL, что позволило сильно ускорить вычисления и выйти на больший масштаб. Но с ростом данных этого всё равно было очень мало, я думал над оптимизациями, старался переиспользовать свой опыт big data аналитики с работы, экспериментировал с разными структурами запросов и генерацией SQL-запросов Питоном, это позволило ускорить вычисления в несколько раз, что мило, но всё равно мало.

Потом я решил сделать поиск двух-этапным: преобразовывать хэши-дробные-векторы в небольшой массив байт, сохраняя каждый признак в два бита: v>0.1 и v<-0.1 (здесь), затем сравнивая число совпавших бит такого хэша у целевого лица и всех лиц в БД, а потом фильтруя записи в БД по какому-то трешхолду, отправляя на более точное и медленное сравнение только потенциальных кандидатов. Пришлось повозиться и переехать на MySQL v8, т.к в 5.7 бинарных операций нет. Но это позволило ускорить поиск ещё почти в 30 раз а это уже клёво ^_^

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

2.4. Другие механики

Когда у меня накопился большой объём данных, и мой сервис научился находить некоторую долю людей по случайным фоткам за приемлемое время, я стал мечтать дальше и задумываться о развитии функционала.

Ускорять время поиска можно не только ускорением самого поискового алгоритма, но и снижением выборки, например, ища профили только среди участников каких-то групп Вк. Ясное дело, здесь встаёт вопрос ограничения размера этой выборки, т.к делать запрос с "ISIN (десятки тысяч айдишников)" такое себе, а вот на паре сотен и даже тысяч работает в разы быстрее, чем полный проход БД.

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

Если идти ещё дальше, то можно индексировать не только Вк, но и ВотсАп, Тг перебрав все русские номера, возможно частично FB, Twi, Ig. Но это уже совсем будущее, я решил двигаться в сторону скорейшей апробации и монетизации того, что есть уже.

3. Заключение

3.2. Happy ли end?

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

Я написал в тех поддержку Вк (тогда они ещё отвечали, ахах), аккуратно представился студентом, что хочу проводить социологические исследования скандируя большие объёмы данных Вк, в т.ч фото, ФИО и описание. Что на самом деле было правдой, с учётом моего интереса к аналитике и психологии. Они ответили, что ради статистики и небольших выборок в целом не против, но точно против какой-либо идентификации. А ещё "порадовали" тем, что будут и палки в колёса АПИ вставлять таким сервисам, и участвовать в разработке/внедрению законов, регулирующих эту деятельность. А недавно, уже в наше время, вышел законопроект, запрещающий автоматизированную обработку данных с сайтов, что по сути полностью блокирует подобные сервисы с парсингом.

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

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

После завершения описанной истории, я решил опубликовать исходники, но т.к там в истории коммитов засветились токены, то перезалил в новый репозиторий. Но код действительно такой, что мне самому туда страшно заглядывать :D
https://github.com/AivanF/ID-Detective-public

3.2. Польза

Здесь, как и в других своих пет-проектах и стартапах, я набрался много опыта:

  • Разобрался с многопоточностью в Питоне.

  • Покопался в специфических вопросах оптимизации MySQL запросов.

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

  • Освоил работу из кода с SSH для настройки окружения, понял, насколько чудесен Ansible.

  • Разработал микросервисную архитектуру из клея и палок, что затем позволило легко понять концепции Kubernetes.

И всё это мне очень пригодилось в последующих работах и проектах.

3.3. Мораль

Выводы каждый сделает свои, но главное не бойтесь пробовать, учиться и искать себя! Надеюсь, вам было интересно :)

Подробнее..

Поиск изображений

03.04.2021 18:14:06 | Автор: admin

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

Вода, камни, небоВода, камни, небо

Используемый датасет

Perceptual hash

[Colab]

Детальное описание работы phash

Из изображений мы создаем хеши заданной длины. Чем меньше расстояние Хэмминга между двумя хешами, тем больше схожесть изображений.

Наивный способ поиска - линейный поиск. Сравниваем хеш целевого изображения с хешами всех изображений и возвращаем N изображений с наименьшим расстоянием Хэмминга (либо отсекаем по какому-нибудь threshold'у).

Можно ли быстрее? Можно! Используя структуру данных Vantage-point tree, можно построить дерево за O(n log n) и осуществлять поиск за O(log n).

Немного о производительности

Внимательные читатели, которые просмотрели ноутбук, могли заметить, что скорость vantage-point tree либо наравне, либо медленнее простого цикла for. В ноутбукеесть закомментированный блок кода, который позволяет сгенерировать массив длиной 100к строк. Этотмассив можно подставить вместо массива хешей и убедится, что с увеличением количества строк... ничего не меняется, vptree все так же проигрывает. В чем же дело? А дело в том, что если вы начнете искать реализацию vantage point tree в PyPI, то найдете лишь 1 пакет - vptree. Он работает довольно плохо, из-за чего прироста производительности нет. Я нашел реализацию vp-tree на javascript и написал небольшой тест производительности. for-loop перестает быть быстрее,чем vptree после 10к строк и дальше отрыв только увеличивается. Кто-то может сказать, что для получения top N элементов, весь массив сортировать необязательно. Согласен, но vp-tree быстрее, даже если мы не проводим сортировку массива вовсе. Ссылка на gist

Интересный факт - я не смог найти реализацию vp-tree, где есть функция добавления новых данных в дерево. Перестраивать дерево каждый раз, когда у вас появляется новый хеш может быть очень затратно. Если вы сможете найти/написать быструю реализацию vp-tree c возможностью добавления/удаления строк, напишите мне и я обновлю ноутбук/статью.

В нашем датасете оказалось 2 дубликата первого изображенияВ нашем датасете оказалось 2 дубликата первого изображения

Плюсы

  • Хеш имеет малый размер

  • Быстро вычисляется

  • Быстро ищется

  • Устойчив к ресайзу

Минусы

  • Не устойчив к кропу

  • Не устойчив к поворотам

  • Не усточив к {transformation_name}

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

http://personeltest.ru/aways/habr.com/ru/post/205398/
http://personeltest.ru/aways/habr.com/ru/post/211773/

Вывод: phash отлично подходит для дедупликации, поиска оригиналов изображений по их preview/thumbnail. Не устойчив к кропу.

RGB Histogram

[Colab]

RGB гистограммаRGB гистограмма

Генерируем RGB гистограммы, сравниваем гистограммы, если гистограммы похожи, то изображения схожи по цвету.

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

Линейный поиск. Сравниваем гистограммы методом cv2.HISTCMP_INTERSECT (53ms)Линейный поиск. Сравниваем гистограммы методом cv2.HISTCMP_INTERSECT (53ms)

После применение операции flatten, можно заметить, что гистограмма превращается в вектор из 4096 значений.

Попробуем применить k-nearest neighbor search, в качестве метрики выберем евклидово расстояние.

Bruteforce knn (73ms) и hnsw(0.4ms) выдают одинаковые изображенияBruteforce knn (73ms) и hnsw(0.4ms) выдают одинаковые изображения

Результат неплохой. Попробуем применить approximate nearest neighbor search. Используем библиотеку hnswlib, которая реализует структуру данных под названием Hierarchical Navigable Small World. Теперь поиск занимает не 50-70ms, а всего лишь 0.4ms.

Новые данные можно добавлять в индекс без его полной перестройки.
Больше про approximate nearest neighbor search - http://personeltest.ru/aways/habr.com/ru/company/mailru/blog/338360/

Плюсы

  • Устойчив к трансформациям, не сильно меняющим гистограмму изображения

  • Более устойчив к кропу, чем phash

Минусы

  • Большой вес (Для 16 бинов RGB гистограммы вектор имеет размер 4096)

  • Учитывает только цвета, не учитывает геометрию

SIFT/SURF/ORB

[SIFT Colab]

Подробнее про SIFT.

Можно использовать более быстрые алгоритмы, например SURF или ORB. Мы будем использовать модификацию SIFT - Root SIFT.

Суть модификации:

descs /= (descs.sum(axis=1, keepdims=True) + eps)descs = np.sqrt(descs)

Вот таким нехитрым способом точность SIFT повышается на ~5 процентов.
План действий: генерируем SIFT features, применяем Brute-ForceMatcher(cv2.BFMatcher), сортируем изображения по среднему расстоянию хороших matches.

Поиск кропов (30s)Поиск кропов (30s)

Плюсы:

  • SIFT инвариантен пропорциональному масштабированию, ориентации, изменению освещённости и частично инвариантен аффинным искажениям

Минусы:

  • Занимает много места

  • Медленно вычисляется

  • Медленно ищется (Есть методы значительно ускоряющие поиск, но я не нашел понятного примера на python)

NN features

[Colab ResNet50] [Colab CLIP]

В процессе своего обучения нейросети решающие задачу классификации изображений становятся способны сжимать изображения до достаточно маленьких векторов. Вектора похожих изображений находятся близко друг к другу. Длина вектора в ResNet50 - 2048. "Открутим" полносвязный классифицирующий слой от ResNet50, сгенерируем векторы всех наших изображений и с помощью knn найдем наиболее близкие к целевому изображению. Используем евклидово расстояние в качестве метрики.

model=ResNet50(weights='imagenet',include_top=False,input_shape=(224,224,3),pooling='max')
Изображение по которому ищемИзображение по которому ищемВодопады (ResNet50)Водопады (ResNet50)Поиск сильно пикселизированного изображения (ResNet50)Поиск сильно пикселизированного изображения (ResNet50)Поиск по кропу (ResNet50)Поиск по кропу (ResNet50)

Попробуем использовать другую нейросеть - CLIP. В ней нет классифицирующего слоя, просто используем метод encode_image и получаем вектор длинной 512.

Водопады (CLIP)Водопады (CLIP)Поиск сильно пикселизированного изображения (CLIP)Поиск сильно пикселизированного изображения (CLIP)Поиск по кропу (CLIP)Поиск по кропу (CLIP)

CLIP справляется c поиском немного хуже, возможно из-за того, что его функция препроцессинга сжимает изображение до 224 с сохранением aspect ratio, а затем берет Center Crop, т.е не все детали могут попасть в кадр.

Визуализируем полученные вектора. Для этого используем алгоритм t-SNE.

t-SNE ResNet50 (10100x10100 7.91MB)
t-SNE CLIP (10100x10100 7.04MB)

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

Плюсы:

  • Хорошо справляется с большинством трансформаций

  • Быстро ищется благодаря approximate nearest neighbor search

Минусы:

  • Для быстрого вычисления features нужен GPU

CLIP text search

[Colab CLIP]

Подробнее про CLIP:

CLIP способен генерировать вектор из текстового запроса, причем этот вектор будет близок к векторам изображений, которые описываются в запросе. В наш knn search мы можем передать не features изображения, а features текстового запроса. Магия.

text_tokenized = clip.tokenize(["a picture of a windows xp wallpaper"]).to(device)with torch.no_grad():        text_features = model.encode_text(text_tokenized)        text_features /= text_features.norm(dim=-1, keepdim=True)
"a picture of a sunset near the sea""a picture of a sunset near the sea""a picture of a fog near the mountains""a picture of a fog near the mountains""a picture of a windows xp wallpaper""a picture of a windows xp wallpaper"

Github со всеми ноутбуками

Подробнее..

Как мы запустили агрегатор удаленных вакансий и зачем в нем ML

26.05.2021 18:20:11 | Автор: admin

В один прекрасный день в конце 2020 года мы решили сделать еще один агрегатор удаленных вакансий, начав с исключительно IT-позиций. Логично спросить: зачем? Мол, на рынке их уже достаточно. Ответ на этот вопрос звучит очень просто: мы понимали, как улучшить текущие решения как минимум по пяти параметрам.

  • Количество (агрегировать больше всех в мире);

  • Реальная удаленка (а не позиции в стиле "remote until COVID-19");

  • Актуальность (часто на схожих сайтах можно найти большое количество неактуальных вакансий);

  • Хороший поиск (по нашему мнению поиск на текущих сайтах с удаленными вакансиями находится на уровне 2005 года);

  • Фильтр по гражданству.

О последнем параметре я и хочу сегодня рассказать.

Прежде, чем вы начнете читать. Сегодня мы запустились с Bergamot на ProductHunt. И, если у вас вдруг возникло желание поддержать наш продукт, будет круто. Ищите нас тут.

Проблема

Иногда компании устанавливают ограничения для граждан некоторых стран (например, компания готова нанимать только ребят с гражданством США / или конкретным типом визы ЕС). Как правило, на страницах с описанием вакансий нет отдельного поля где выводились бы подобные ограничения. И поиска/фильтра, соответственно, тоже нет. Поэтому соискателю приходится внимательно читать текст каждой вакансии, чтобы понять, есть ли вообще смысл откликаться на эту позицию.

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

Анализ

Сначала мы думали решить эту задачу простыми алгоритмическими методами. Базовая идея заключалась в следующем:

Шаг 1

Ищем определенные ключевые слова в тексте, например: only, remote in, authorized to work in и так далее.

Шаг 2

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

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

  • This role is remote and you can be based anywhere across the UK

  • Living in Europe is a must

  • This opportunity is only open to candidates within Canada at this time

  • Location: Argentina (any part of the country its great for us!)

  • и еще сотни других описаний.

Очевидно, алгоритмами задачу не решить и мы попробовали использовать силу ML-a.

Задача

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

restriction: 0 (no) / 1 (yes)

если restriction = 1, то тогда необходимо выделять еще и страну, по которой есть ограничение

Решение

Структура решения

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

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

Нахождение локаций

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

Во-первых, ограничения касались не только стран и столиц мира, а также небольших городов и штатов. Например Can work full time in Eugene, OR / Hammond, IN. А сделать список локаций всех уровней уже сложнее.

Во-вторых, написания локаций в вакансиях часто отличались от стандартного (например 100% Remote in LATAM).

Поэтому для выделения локаций мы приняли решение использовать NER. Пробовали разные готовые варианты:

В итоге выбор пал на spaCy, потому что из готовых и бесплатных вариантов spaCy EntityRecognizer показал наилучший результат.

Итого: нам удалось выделить в тексте локации.

Разделение на предложения

Для разделения на предложения, где есть локации, мы тоже использовали spaCy.

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

  • The position is remote so the only thing is they have to be in the US and be able to work Eastern or Central time.

  • This job is located out of our Chicago office, but remote, US-based applicants are still encouraged to apply.

  • This is a remote role, but we're looking for candidates based in Montreal, Canada.

Классификатор

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

Решили попробовать несколько моделей, среди которых как более простые CNN и LSTM, так и более современные transformers. Последние предсказуемо оказались лучше, обучение которых сводилось по сути к fine-tunning это нам точно подходило, ведь датасет, как я уже сказал выше, был невелик.

Среди transformers наилучший результат показала архитектура RoBERTa (roberta-base) с показателем точности 94% для нашего датасета.

Нормализация локаций

На основе классификатора и NER-a для каждой вакансии мы получили вот такие дополнительные поля:

restriction: 1 (yes); location: London

Restriction отдавал классификатор. А вот Location выдавал NER. Из-за того что в поле Location могли быть разные написания городов и стран, мы еще сделали дополнительную нормализацию через Google API. Остановились на том, чтобы сделать ограничения по странам.

То есть на выходе получалось:

restriction: 1 (yes); location: United Kingdom

Итог

В итоге мы теперь умеем это делать и кандидаты могут фильтровать неподходящие для них вакансии. Mission accomplished (вроде бы! вы можете сами потестить Bergamot и написать, что думаете).

Подробнее..

Как построить свою систему поиска похожих изображений

04.04.2021 14:14:58 | Автор: admin

Представлюсь

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

Эта публикация предназначена для Machine Learning инженеров и написана по мотивам моего выступления Поиск похожих изображений - справочник от А до Я, который был опубликован сообществом Open Data Science на Data Fest Online 2020.

Данная статья содержит справочную информацию по зарекомендованным методам, применяемым в задаче Image Retireval. Прочитав статью, вы сможете построить систему поиска похожих изображений под вашу задачу с нуля (не включая процесс разработки production решения).

О задаче

Поиск похожих изображений (по-другому, Content-Based Image Retrieval или CBIR) - это любой поиск, в котором участвуют изображения.

Проще всего о задаче расскажет картинка сверху из статьи Recent Advance in Content-based Image Retrieval: A Literature Survey (2017).

Сейчас все активнее применяется подход "Поиск по фото", в частности, в e-commerce сервисах (AliExpress, Wildberries и др.). "Поиск по ключевому слову" (с пониманием контента изображений) уже давно осел в поисковых движках Google, Яндекс и пр., но вот до маркетплейсов и прочих частных поисковых систем еще не дошел. Думаю, с момента появления нашумевшего в кругах компьютерного зрения CLIP: Connecting Text and Images ускорится глобализация и этого подхода.

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

Базовые компоненты сервиса

Шаг 1. Обучение модели. Модель может быть сделана на классике CV или на базе нейронной сети. На вход модели - изображение, на выход - D-мерный дескриптор/эмбеддинг. В случае с классикой это может быть комбинация SIFT-дескриптора + Bag of Visual Words. В случае с нейронной сетью - стандартный бэкбон по типу ResNet, EfficientNet и пр. + замысловатые пулинг слои + хитрые техники обучения, о которых мы далее поговорим. Могу сказать, что при наличии достаточного объема данных или хорошего претрена нейронные сети сильно выиграют почти всегда (мы проверяли), поэтому сосредоточимся на них.

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

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

Нейросети и Metric Learning

Нейронная сеть в задаче поиска похожих используется как feature extractor (бэкбон). Выбор бэкбона зависит от объема и сложности данных - рассмотреть можно все от ResNet18 до Visual Transformer.

Первая особенность моделей в Image Retrieval - это магия в голове нейросети. На лидерборде по Image Retrieval борются за построение лучших дескрипторов - тут есть и Combined Global Descriptors с параллельными пулингами и Batch Drop Block для более равномерного распределения активации по выходной карте признаков.

Второй главной фишкой являются функции ошибок. Их очень много. Только в Deep Image Retrieval: A Survey представлено больше десятка зарекомендованных парных лоссов. Еще столько же есть классификационных. Главная суть всех этих лоссов - обучить нейросеть трансформировать изображение в вектор линейно разделимого пространства, так чтобы далее можно было сравнивать эти вектора по косинусному или евклидову расстоянию: похожие изображения будут иметь близкие эмбеддинги, непохожие - далекие. Рассмотрим подробнее.

Функции ошибок

Contrastive Loss

Самая простая для понимания функция ошибки - Contrastive Loss. Это парный лосс, т.е. объекты сравниваются по расстоянию между друг другом.

Нейросеть штрафуется за отдаленность друг от друга эмбеддингов изображений p и q, если эти изображения на самом деле похожи. Аналогично, возникает штраф за близость эмбеддингов, изображения которых на самом деле непохожи друг на друга. При этом в последнем случае мы ставим границу m (например, 0.5), преодолев которую, мы считаем, что нейросеть справилась с задачей "разъединения" непохожих изображений.

Triplet Loss

Triplet Loss берет во внимание три объекта - якорь, позитив (похожий на якорь) и негатив (отличный от якоря). Это также парный лосс.

Здесь мы нацелены на минимизацию расстояния от якоря до позитива и максимизацию расстояния от якоря до негатива. Впервые Triplet Loss был представлен в статье FaceNet от Google по распознаванию лиц и долгое время был state-of-the-art решением.

N-tupled Loss

N-tupled Loss - развитие Triplet Loss, в котором также берется якорь и позитив, но вместо одного негатива используется несколько негативов.

Angular Additive Margin (ArcFace)

Проблема парных лоссов заключается в выборе комбинаций позитивов, негативов и якорей - если их просто брать равномерно случайными из датасета, то возникнет проблема "легких пар". Это такие простые пары изображений, для которых лосс будет 0. Оказывается, сеть достаточно быстро сходится к состоянию, в котором большинство элементов в батче будут для нее "легкими", и лосс для них окажется нулевым - сеть перестанет учиться. Чтобы избежать этой проблемы, стали придумывать изощренные техники майнинга пар - hard negative и hard positive mining. Подробнее о проблеме можно почитать в этой статье. Существует также библиотека PML, в которой реализовано множество методов майнинга, да и вообще в библиотеке представлено много полезного по задаче Metric Learning на PyTorch.

Еще одним решением проблемы являются классификационные лоссы. Рассмотрим одну популярную функцию ошибки, которая привела к state-of-the-art в распознавании лиц три года назад - ArcFace.

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

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

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

Пулинги

Вернемся к архитектуре нейросети и рассмотрим парочку pooling слоев, применяемых в задачах Image Retrieval

R-MAC

Regional Maximum Activation of Convolutions (R-MAC) - пулинг слой, принимающий выходную карту нейронной сети (до глобального пулинга или слоев классификации) и возвращающий вектор-дескриптор, посчитанный как сумма активаций в различных окнах выходной карты. Здесь активацией окна является взятие максимума по этому окну для каждого канала независимо.

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

GeM

Generalized Mean (GeM) - простой пулинг, который может улучшить качество выходного дескриптора. Суть в том, что классический average pooling можно обобщить на lambda-норму. При увеличении lambda мы заставляем сеть фокусироваться на значимых частях изображения, что в определенных задачах может быть важно.

Ранжирование

Индексы

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

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

Датасет glove, размер эмбеддинга 100, расстояние - angularДатасет glove, размер эмбеддинга 100, расстояние - angular

Указанные проблемы можно решить в ущерб качеству - хранить эмбеддинги не в исходном виде, а сжатом (квантизованном). А также изменить стратегию поиска - искать не brute-force, а стараться за минимальное число сравнений найти нужное число ближайших к данному запросу. Существует большое число эффективных фреймворков приближенного поиска ближайших. Для них создан специальный бэнчмарк, где можно посмотреть, как ведет себя каждая библиотека на различных датасетах.

Самые популярные: отечественная NMSLIB, Spotify Annoy, Facebook Faiss, Google Scann. Также, если хочется взять индексирование с REST API "из коробки", можно рассмотреть приложение Jina.

Переранжирование

Исследователи в области Information Retrieval давно поняли, что упорядоченная поисковая выдача может быть улучшена неким способом переупорядочивания элементов после получения исходной выдачи.

Одним из таких методов является Query Expansion. Идея состоит в том, чтобы использовать top-k ближайших элементов для генерации нового эмбеддинга. В самом простом случае можно взять усредненный вектор, как показано на картинке выше. Также можно взвесить эмбеддинги, например, по отдаленности в выдаче или косинусному расстоянию от запроса. Подобные улучшения описаны в едином фреймворке в статье Attention-Based Query Expansion Learning. По желанию можно применить Query Expansion рекурсивно.

k-reciprocal

k-reciprocal - множество элементов из top-k, в числе k ближайших которых присутствует сам запрос. На базе этого множества строят процесс переранжирования выдачи, один из которых описан в статье Re-ranking Person Re-identification with k-reciprocal Encoding. По определению, k-reciprocal ближе к запросу, чем k-nearest neighbors. Соответственно, можно грубо считать элементы, попавшие в множество k-reciprocal заведомо позитивными и изменять правило взвешивания, например, для Query Expansion. В данной статье разработан механизм пересчета дистанций с использований k-reciprocal множеств самих элементов в top-k. В статье много выкладок, это выходит за рамки данного поста, поэтому предлагаю читателю ознакомиться самостоятельно.

Валидация

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

Метрики

В первую очередь - метрики. Рассмотрим такие популярные метрики в задаче Image Retrieval: precision@k, recall@k, R-precision, mAP и nDCG.

precision@k

Показывает долю релевантных среди top-k ответов.

Плюсы:

  • показывает, насколько система избирательна в построении top-k

Минусы:

  • очень чувствительна к числу релевантных для данного запроса, что не позволяет объективно оценить качество поиска, где для разных запросов имеется разное число релевантных примеров

  • достичь значение 1 возможно только, если число релевантных >= k для всех запросов

R-precision

То же самое, что precision@k, где k устанавливается равным числу релевантных к данному запросу

Плюсы:

  • исчезает чувствительность к числу k в precision@k, метрика становится стабильной

Минусы:

  • приходится знать общее число релевантных к запросу (может быть проблемой, если не все релевантные размечены)

recall@k

Показывает, какая доля релевантных была найдена в top-k

Плюсы:

  • отвечает на вопрос, найдены ли релевантные в принципе среди top-k

  • стабильна и хорошо усредняется по запросам

mAP (mean Average Precision)

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

Плюсы:

  • объективная стабильная оценка качества поиска

  • является одно-численным представлением precision-recall кривой, которая сама по себе богата информацией для анализа

Минусы

  • приходится знать общее число релевантных к запросу (может быть проблемой, если не все релевантные размечены)

Подробнее про метрики в Information Retrieval, в том числе посмотреть вывод mAP, можно почитать здесь.

nDCG (Normalized Discounted Gain)

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

Усреднение

Также важно отметить варианты усреднения метрик по запросам. Рассмотрим два варианта:

macro: для каждого запроса считается метрика, усредняем по всем запросам

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

micro: число размеченных релевантных и отдельно успешно найденных релевантных суммируется по всем запросам, затем участвует в дроби соответствующей метрики

+: запросы оцениваются с учетом числа размеченных релевантных для каждого из них
-: метрика может стать сильно низкой / сильно высокой, если для какого-то запроса было очень много размеченных релевантных и система неуспешно / успешно вывела их в топ

Схемы валидации

Предлагаю рассмотреть два варианта валидации.

Валидация на множестве запросов и выбранных к ним релевантных

На вход: изображения-запросы и изображения, релевантные к ним. Имеется разметка в виде списка релевантных для данного запроса.

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

Валидация на полной базе

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

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

Пример реализованного проекта

Некоторые компании спорят с другими компаниями, чтобы вторые не использовали изобразительные элементы бренда первых. В таких случаях более слабый производитель пытается паразитировать на успехе крупного бренда, выдавая свои продукты и услуги под похожей символикой. От этого страдают и пользователи - вы можете по ошибке купить сыр не того производителя, которому вы уже доверяете, а взять подделанный товар, не прочитав внимательно этикетку. Пример из недавнего: Apple Is Attempting to Block a Pear Logo Trademark.

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

Устройство нашей системы

Для обучения, валидации и разработки поискового приложения мы разработали такую систему. Здесь Training pipeline, Benchmark, Indexer и Demo Web app - независимые репозитории, Logo Search app - поисковое приложение одного из клиентов. Объем индексируемой базы изображений: 1.5 млн товарных знаков.

Примеры работы

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

Заключение

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

Подробнее..

Как именно нужно читать предупреждение ФАС Яндексу и что оно значит для Рунета

22.03.2021 10:20:41 | Автор: admin


Я один из многих юристов, который больше года назад участвовал в подготовке обращения в ФАС с вопросом, нормально ли, что Яндекс стал лучшим поиском по сервисам Яндекса. Собственно, ответ пришёл несколько недель назад в виде предупреждения о прекращении действий (бездействия), которые содержат признаки нарушения антимонопольного законодательства.

Вопрос был в том, правильно ли то, что Яндекс даёт преференции своим сервисам в поиске. Суть вопроса сводилась к тому, можно ли считать отдельным рынком рынок поиска.

Дело вот в чём: если юридически поиск в интернете это рынок, то на нём действует антимонопольное законодательство. И тогда, цитируя сам Яндекс (точнее, пост об иске против Google): это обращение признание одного неприятного факта. У нас с вами есть все шансы попасть в страну, где только одна компания будет решать, какими сервисами будут пользоваться люди. И если мы всегда верили в конкуренцию и право выбора пользователя, то ФАС должна восстановить справедливость.

Если же поиск в интернете это не отдельный рынок, то Яндекс частная компания, которая в своём поиске может делать что хочет. Ведь это их поиск, их алгоритмы ранжирования и их ручные настройки. Частное дело частной компании.

Коротко: ФАС усмотрела признаки нарушений (пока де-юре не сами нарушения, это важно) в действиях Яндекса. И попросила, по сути, вернуть нейтральность в поиск, то есть обеспечить чуть более равные условия всем компаниям на рынке. В перспективе это может коснуться не только Яндекса, но и любого поиска в России.

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

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


Уточнение


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

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

То есть всё это моё личное мнение и мой взгляд на события.

Теперь можно начинать.

Почему ситуацию сложно комментировать?


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

Смысл сообщения: ФАС усматривает признаки нарушения вот у этих юрлиц:

выразившихся в создании на рынке оказания услуг поиска общего характера в информационно-телекоммуникационной сети Интернет с применением информационных технологий неравных (дискриминационных) условий путем предоставления для сервисов лиц, группы лиц ООО Яндекс (в том числе ООО Яндекс.Вертикали, ООО Яндекс.Маркет, ООО Яндекс.Медиасервисы, ООО Кинопоиск)


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

при которых сервисы лиц, входящих в группу лиц ООО Яндекс, в том числе используют интерактивные обогащенные ответы (колдунщики1 2, объектные ответы3 и другая применимая терминология) (далее интерактивные ответы) на страницах поисковой выдачи поисковой системы Яндекс (yandex.ru), при этом у лиц и их сервисов, не входящих с ООО Яндекс в одну группу лиц, такая возможность отсутствует и (или) не соответствует тем техническим, визуальным и иным возможностям и преимуществам интерактивных ответов поисковой системы Яндекс (yandex.ru), которые получают сервисы лиц, входящих в группу ООО Яндекс


Мы оценивали влияние этих блоков на трафик и вообще собирали данные о том, что происходит. Про статистические методы и конкретные примеры лучше напишут мои коллеги.

ФАС вынесла предупреждение, сказав важную вещь: есть признаки нарушения антимонопольного законодательства:

признаков нарушения антимонопольного законодательства, предусмотренных пунктом 8 части 1 статьи 10 Федерального закона от 26.07.2006 135-ФЗ О защите конкуренции (далее Закон о защите конкуренции)


То есть усматривается ситуация, в которой всё похоже на то, что нарушен Закон о защите конкуренции. То есть похоже, что Яндекс уже подпадает под антимонопольное законодательство не только на рынке интернет-рекламы (он давно уже понятен, и там действует почти такая же схема, как на рынке обычной рекламы), но и в поиске.

Признаки это не само нарушение. Признаки это ситуация неопределённости.

Далее ФАС предлагает:
  1. Перестать создавать такие признаки прямо сейчас: Прекращения предоставления сервисам лиц, входящим в группу лиц ООО Яндекс (в том числе ООО Яндекс.Вертикали, ООО Яндекс.Маркет, ООО Яндекс.Медиасервисы, ООО Кинопоиск) преимущественных возможностей пока непонятно, как именно это реализовывать.
  2. Очень важно: обеспечить для всех прозрачный регламент, то есть, по сути, рассказать, что за факторы ведут к попаданию выше основной выдачи что есть польза для пользователя: Опубликования условий доступа сервисов к техническим, визуальным и иным возможностям по продвижению (привлечению внимания пользователей) в поисковой системе Яндекс (yandex.ru), в том числе с помощью интерактивных ответов и (или) иных применимых функций и технологий, демонстрируемых на страницах поисковой выдачи поисковой системы Яндекс (yandex.ru), в формате, доступном для ознакомления хозяйствующих субъектов вне зависимости от вхождения в группу лиц ООО Яндекс.
  3. Обеспечить равные возможности независимо от того, входит юрлицо в группу компаний Яндекс или нет: Обеспечения для сервисов лиц, не входящих с ООО Яндекс в одну группу лиц, доступа к техническим, визуальным и иным возможностям по продвижению (привлечению внимания пользователей). И Обеспечения демонстрации сервисов лиц, не входящих в группу лиц ООО Яндекс, и сервисов лиц, входящих в группу лиц ООО Яндекс по единым правилам ранжирования, применяемым при формировании органической выдачи в поисковой системе Яндекс, на равных условиях, не допуская преимущественной демонстрации сервисов лиц, входящих в группу лиц ООО Яндекс.


Всё это нужно сделать за 1 месяц, затем проинформировать ФАС и приложить отчёт о действиях.

Если это не будет сделано и признаки нарушения сохранятся, то будет возбуждено дело:

Федеральная антимонопольная служба обращает внимание ООО Яндекс на тот факт, что в соответствии с частью 8 статьи 39.1 Закона о защите конкуренции, в случае невыполнения предупреждения в установленный срок при наличии признаков нарушения антимонопольного законодательства антимонопольный орган обязан принять решение о возбуждении дела о нарушении антимонопольного законодательства.


Таким образом, в документе написано (напомню, с моей точки зрения) следующее: Яндекс может добровольно согласиться со всем этим и сделать, как предложили в ФАС. Это очень мягкая мера, ведь факт нарушения не утверждается и пока не предполагается ответственность. Либо же посчитать, что признаки нарушения и нарушение это разные вещи, и доказать, что признаки могут возникнуть без самого нарушения. То есть обратиться в суд и доказывать свою точку зрения. Либо же проигнорировать предупреждение, и тогда ФАС заведёт дело, в ходе которого будет и вынесено решение, что именно можно, а что нельзя делать на поисковом рынке и Яндексу, и Гуглу, что такое поисковый рынок и так далее.

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

Какие ещё есть инструменты у ФАС?


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

Пока предписания нет.

То есть если признаки сохраняются больше месяца и трёх дней ФАС подразумевает, что может и начать дело, и оно кончится жёстче. На текущий момент Яндекс выбирает, какой сценарий подходит компании больше, а Гугл с интересом смотрит, потому что к нему решение тоже будет иметь прямое отношение.

Общий смысл: или нужно обсуждать, есть такой отдельный поисковый рынок и определять его, или же его нет. Если он есть на нём будет стандартное антимонопольное регулирование. Если его нет это частное дело Яндекса. Сейчас погружаемся глубже в прогноз ФАС считает, что, условно, такой рынок должен быть, и если бы он был, то Яндекс бы уже нарушал. Но поскольку рынок не определён точно, нужно либо устранить непорядок самим, либо дождаться, когда он будет описан формально и устранить потом уже силами государства.

Что будет делать Яндекс?


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

Как я понимаю, они не считают, что рынок поиска есть как сущность.

Уходя дальше в область гаданий и учитывая все вышесказанное, можно предположить, что признаки нарушения, о которых говорит ФАС, могут быть устранены временно или же формально, без строгого следования тексту предупреждения ФАС. В процессе подготовки обращения в ФАС мы наблюдали ручную регулировку. Мы не в ситуации, когда кто-то будет смотреть за Яндексом, включено там или выключено если признаки нарушения всё же есть, то нужно постоянное нормативное решение.

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

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

Почему это важно для Рунета?


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

По сути, поиск (Яндекс, Гугл, Ютуб и так далее) это окно и первый шаг пользователя в Интернет. Если отдельно взятая частная компания будет контролировать его на монопольных условиях (а поисковый рынок в России имеет признаки монопольного из-за того, что Яндекс имеет долю около 60% на нём: По оценкам самого Яндекса, его доля на рынке поиска в границах РФ в 2019 году составила 56,31%, а за период с января по май 2020 года выросла до 59,92%), то это плохо. Поэтому государство хочет обеспечить равные условия игры для всех на нём. Можно сказать, что монополизация и доминирование на рынках это естественный процесс, но и регулирование поведения доминирующих игроков это также естественный процесс и устойчивая мировая практика.

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

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

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

ФАС наказывает Яндекс или нет?


  • Регулятор определяет, есть рынок или нет.
  • Если рынок есть, он регулируется по принципу равных прав и обязанностей участников, которые закреплены в антимонопольном законодательстве.
  • Если кто-то не нейтрален, ему выдаётся алгоритм действий, как исправить нарушения.
  • Суть антимонопольного регулирования в том, что на рынках должны быть установлены правила игры, одинаковые для всех участников. Во всём мире практика развивается так, что это не просто репрессивные правила, а правила, призванные сохранить конкурентную среду для всех участников рынка.
  • В настоящий момент важное значение имеет тот факт, что ФАС впервые констатирует в своем предупреждении наличие дискриминационных условий на: рынке оказания услуг поиска общего характера.
  • В равной мере все принципы будут относиться и к Гуглу и всем поисковикам.

Есть ли похожие случаи с Гуглом?


Да, и речь не только про Гугл, но и Фейсбук и другие крупные компании. Антимонопольные меры применяются в разных случаях и носят разный характер.

Пример 1: еврокомиссия против Google: претензии по работе поисковой рекламы
Вместе с окном поиска от Google, которое устанавливали различные веб-сайты, компания требовала также устанавливать принадлежащий ей сервис контекстной рекламы AdSence и запрещала рекламировать через него своих конкурентов. Было доказано, что Google нарушал европейские нормы по конкуренции при размещении рекламы в интернете и ее выделении при поиске в сети.

Нарушение антимонопольных правил ЕС продолжалось более десяти лет. Все нарушения были зафиксированы в контрактных обязательствах, которые Google навязал владельцам веб-сайтов. Еврокомиссия изучила сотни договоров, заключенных с 2006 по 2016 годы. В некоторых договорах были отдельные пункты об эксклюзивности права на рекламу, которые прямо запрещали партнерам Google размещать на своих страницах с результатами поиска рекламу от конкурентов Google. С 2009 года Google несколько ослабил эту практику, требуя от партнеров в контрактном порядке размещения в наиболее дорогих рекламных зонах сайтов строго оговоренного минимума контекстной рекламы, предоставляемой Google. Но в то же время в контракты были включены положения, требующие от издателей получать письменное разрешение от Google, прежде чем вносить изменения в способ отображения любых конкурирующих рекламных объявлений. Это означало, что Google мог контролировать, насколько привлекательной может быть контекстная реклама конкурентов.

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

Пример 2: Еврокомиссия против Google: форсированное развитие Android
Еврокомиссия в 2015 году начала официальное антимонопольное разбирательство в отношении бизнес-практик Googlе, связанных с Android. Разбирательство началось после жалобы объединения FairSearch, в которое вошли Microsoft, Nokia, Oracle и другие компании. На тот момент, по данным Statcounter, 64% мобильных устройств в Евросоюзе работали на Android, а к 2018 году (когда было вынесено решение по кейсу), доля Android выросла до 80%. Google подозревали в нарушении правил ЕС, запрещающих антиконкурентные соглашения, и в злоупотреблении доминирующим положением. Предполагалось, что Google незаконно препятствовал разработке и доступу на рынок конкурирующих мобильных операционных систем, приложений и услуг мобильной связи в Европейской экономической зоне.

Было доказано, что Google наложил определенные ограничения на производителей устройств Android и операторов сетей, чтобы трафик с устройств Android попадал в поисковую систему Google. Google использовал Android в качестве средства, чтобы укрепить доминирующее положение своей поисковой системы. В частности, Google обязывал производителей предустанавливать приложение Google Search app и приложение с браузером Chrome, платил некоторым производителям и мобильным операторам, чтобы они предустанавливали поисковое приложение Google в эксклюзивном порядке. А также компания препятствовала деятельности производителей, которые хотели продавать смартфоны на альтернативных версиях Android, не одобренных Google. Когда Google разрабатывает новую версию Android, он публикует исходный код в Интернете. Это в принципе позволяет третьим сторонам загружать и изменять этот код для создания своих версий Android (так называемых Android Forks).

В заключении Еврокомиссии говорилось, что Google получает большую часть доходов за счет своего флагманского продукта поисковой системы Google. Компания поняла, что переход от компьютеров к мобильному Интернету, который начался в середине 2000-х годов, станет фундаментальным изменением для нее. И Google разработал стратегию, которая позволяла бы нивелировать последствия этого сдвига и гарантировать, что пользователи будут продолжать использовать Google Search также на своих мобильных устройствах. По данным Еврокомиссии, озвученным в 2018 году, на устройствах с Android, где предустановлены приложения Google, 95% поисковых запросов делается через Google Search, а на устройствах Windows Mobile, где нет таких предустановленных приложений, через Google Search проходит 25% запросов, а остальные осуществляются через предустановленный поисковик Microsoft Bing.

Как ограничили: в 2018 году Еврокомиссия оштрафовала Google на 4,34 млрд евро, а также обязала пересмотреть практики, о которых говорилось в решении. Размер штрафа был рассчитан на основе суммы дохода Google от услуг поисковой рекламы на устройствах Android в странах Европейской экономической зоны. В решении Еврокомиссии подчёркивалось, что доминирование на рынке не является незаконным в ЕС, однако доминирующие компании несут особую ответственность за то, чтобы не злоупотреблять своим сильным положением на рынке, ограничивая конкуренцию. Также в Еврокомиссии отмечали, что действия Google могут наносить вред потребителям, ограничивая инновационное развитие в мобильном пространстве. По заявлению участников объединения FairSearch, за два с половиной года с момента принятия решения Еврокомиссии, мало что изменилось. В январе 2021 года участники коалиции говорили, что рынку все еще наносится ущерб, эффективных средств правовой защиты нет, а на карту теперь поставлено доверие к Еврокомиссии. FairSearch призвала принять меры прямо сейчас, потому что нет времени ждать месяцами или годами, пока предложенный закон о доминирующих платформах решит проблему.

Пример 3: Россия: притеснение Яндекса
В России в 2015 году ФАС рассматривала жалобу на Google Inc. и Google Ireland Ltd. (ирландское подразделение корпорации) со стороны Яндекса. Яндекс был недоволен тем, что Google не разрешает предустановку приложений Яндекса на мобильных устройствах марок Fly, Explay и Prestigio. Кроме того, Яндекс требовал от американской компании отказаться от обязательной привязки операционной системы Android к поисковику Google. Яндекс ссылался на то, что понес убытки из-за действий Google, которые вынудили производителей мобильных устройство оказаться сотрудничать с российской компанией, и жаловался, что его вытесняют с рынка.

Именно тогда прозвучали на Хабре слова Григория Бакунова:
Мы обратились в регулирующий орган, ФАС, чтобы вернуть равноправные отношения на рынке мобильных устройств. Наше обращение признание одного неприятного факта. У нас с вами есть все шансы попасть в мир, где только одна компания будет решать, кто получит возможность донести сервисы до людей и чем эти люди смогут пользоваться. Мы всегда верили в конкуренцию и право выбора пользователя.


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

ФАС признала компанию Google нарушителем закона о конкуренции, наложила штраф, а также потребовала устранить нарушения (то есть как раз дала алгоритм перехода обратно к нейтральности), то есть внести исправления в соглашения с производителями мобильных устройств, выпускаемых в России. Кроме того, компания должна была уведомить всех пользователей мобильных устройств на Android о возможности деактивации предустановленных сервисов и установки альтернативных приложений, совпадающих по функциональности, о возможности смены поиска в браузере Google Chrome и установки иного поискового виджета.

Google пытался оспорить решение, но суд иск отклонил. Итоговая сумма штрафа, объявленная ФАС в 2016 году, составила 438 млн рублей (9% от оборота компании на российском рынке за 2014 год плюс инфляция). За неисполнение предписания в срок Google Inc и Google Ireland Ltd были назначены дополнительные штрафы по 500 тысяч рублей. В итоге дело перешло в область уголовного права, так как ФАС пришлось подать судебный иск о принудительном исполнении решения. По условиям заключенного мирного соглашения, Google согласился выплатить штраф в полном размере, а также разработать окно выбора поисковой системы на существующих и будущих устройствах, использующих операционную систему Android. Кроме того, Google заключил одобренное ФАС коммерческое соглашение с Яндексом, предоставив дополнительные возможности для поискового сервиса в браузере Google Chrome.

Пример 4: Гугл и Турция
По мнению турецких антимонопольщиков, Google нарушил условия честной конкуренции, несправедливо распоряжаясь доступом к рекламным площадям. Антимонопольное ведомство нашло подтверждение тому, что Google усложнил деятельность других предприятий на рынке контентных услуг, отображая текстовые рекламные объявления в верхней части общих результатов поиска в интенсивной манере. Это была более привлекательная выдача, которая при этом не была четко обозначена как реклама, но делала менее интересными для пользователей обычные результаты поиска. Иными словами, компаниям, которые не платили за рекламное размещение Google, было сложнее попасть в поле зрения потенциальных клиентов.

Турецкое антимонопольное ведомство в 2020 году выписало Google штраф в размере $25,6 млн. Кроме того, теперь компания должна будет обеспечить активную конкуренцию на рынке, приняв необходимые для этого меры в течение шести месяцев (решение было опубликовано в ноябре 2020 года). Также в течение пяти лет компания обязана отчитываться о том, как она позволяет сохранять конкуренцию.

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

Так что я рад, что на наш запрос ответили. Мы 17 лет занимались обновлением расписаний электричек, прогнозированием движения поездов, научились делать удобную продажу железнодорожных и авиабилетов, недавно поучаствовали в наконец-то внедрении электронных билетов на автобусы на госуровне (с 1 января 2021). Долгие годы именно наше расписание было в топе выдачи Яндекса, как самое полезное и точное для пользователя, и пользователи привыкали, что в Яндексе можно его найти. То есть мы развивали и Яндекс тоже своим трудом. Ситуация видится мне так: Яндекс во многом скопировал наши сервисы у себя и начал продвигать их своими средствами. Мы не против копирования и не против конкуренции мы только за равные условия для всех игроков рынка. Копируйте нас и делайте лучше, но давайте сравнивать без дискриминации.
Подробнее..

Свободное API для поиска в интернете

03.03.2021 10:13:29 | Автор: admin

Во время разработки некоторых проектов может понадобится удобное API для поиска в интернете. К сожалению, популярные поисковики, такие как Google и Яндекс, не дают свободного доступа к своей поисковой выдаче.

На замену закрытым API приходит Searx.

Searx это свободная поисковая система, объединяющая результаты других поисковых систем, но не хранящая информацию о своих пользователях.

Создать свой инстанс Searx может каждый. Полный их список есть на сайте searx.space. Для своих целей мы будем использовать Roughs Searx, так как он никак не ограничивает число запросов и позволяет получать поисковую выдачу даже с помощью автоматизированных скриптов.

Адрес API-сервиса:https://searx.roughs.ru/search

Поддерживаются как GET, так и POST запросы.

Параметры запроса

  • q (обязательно)Текст запроса, результаты которого нужно получить.

  • format (обязательно)Формат вывода результатов. Доступные форматы: json, csv, rss.

  • categories Список категорий поиска, разделенных запятыми.

  • safesearch Фильтр безопасного поиска. 0 выключен, 1 включен.

С полным списком параметров можно ознакомиться на этой странице.

Примеры запросов

Найти новость по запросу Технологии и вывести результат в формате RSS:
https://searx.roughs.ru/search?q=Технологии&format=rss&categories=news

Выполнить поиск картинок по запросу Горы с фильтром безопасного поиска и вывести результаты в формате json:
https://searx.roughs.ru/search?q=Горы&format=json&categories=images&safesearch=1

Найти решение для 2+2*2 с помощью WolframAlpha и получить ответ в формате таблицы CSV:
https://searx.roughs.ru/search?q=2+2*2&engines=wolframalpha&format=csv

Подробнее..

Перевод В 2020 году две трети поисковых запросов в Google завершалось без нажатия на ссылку

24.03.2021 12:04:07 | Автор: admin


В августе 2019 года было опубликовано исследование ныне уже несуществующего поставщика данных о посещениях Jumpshot, демонстрирующее, что 50,33% всех поисковых запросов Google завершалось без клика на веб-ресурсы в результатах поиска. Сегодня благодаря новым данным SimilarWeb удалось внести в этот анализ существенное дополнение.

С января по декабрь 2020 года 64,82% поисковых запросов в Google (суммарные данные по десктопам и мобильным устройствам) завершилось результатами поиска без кликов на сторонние веб-ресурсы. Вероятно, в этой статистике недооценены некоторые мобильные и почти все голосовые запросы, поэтому возможно, что более двух третей всех поисковых запросов Google являются тем, что называется запросами с нулевыми кликами (zero-click searches). Некоторые специалисты указывают, что нулевые клики это немного ошибочный термин, поскольку к этой группе относится и поиск, который завершается кликом внутри самого Google SERP (например, при нажатии на звуки животных здесь или при нажатии на номер телефона на поле с картой). Однако терминология, похоже, устоялась, поэтому её нужно объяснить.

Предупреждение: надо сказать, что эти данные нельзя напрямую сравнивать с теми, которые были опубликованы в 2019 году, потому что выборка данных посещений SimilarWeb отличается от данных Jumpshot. Во-первых, данные за 2020 год учитывают весь мир, а в предыдущем анализе Jumpshot приведена выборка только по США. Кроме того, SimilarWeb объединяет и мобильные, и десктопные устройства, в том числе и устройства Apple/iOS (к которым у Jumpshot не было доступа). Тем не менее, высока вероятность того, что если бы предыдущая выборка по-прежнему была доступна, она бы демонстрировала похожий тренд на увеличение поедания кликов сервисом Google.

Вот основная статистика по данным:

  • SimilarWeb проанализировал примерно 5,1 триллиона поисковых запросов Google за 2020 год
  • Эти запросы выполнялись на более чем 100 миллионах мобильных и десктопных устройств, с которых SimilarWeb собирает данные о посещениях
  • Из этих 5,1 триллионов поисковых запросов 33,59% привело к кликам на органические результаты поиска
  • 1,59% привело к кликам на платные результаты поиска
  • Оставшиеся 64,82% завершили поиск без непосредственного клика на сторонний веб-ресурс
  • Процент поисковых запросов, которые привели к клику, гораздо выше на десктопных устройствах (органический CTR 50,75%, платный CTR 2,78%)
  • На мобильных устройствах поисковых запросов с нулевыми кликами гораздо больше (77,22%)

Я создал несколько графиков этих данных, однако полный охват собранной SimilarWeb информации гораздо богаче.

Интересно рассмотреть CTR мобильных и десктопных устройств по отдельности.


Как видно из диаграммы, органический CTR на десктопах по-прежнему больше 50%. Десктопные устройства (которые в этом анализе включают в себя ноутбуки и большие планшеты) также демонстрируют чуть больший CTR платных ссылок. Подозреваю, что в США эти показатели побольше, а в странах и регионах с меньшим количеством рекламодателей и меньшими бюджетами на рекламу, они, скорее всего, ниже.


Этот график показывает, насколько сильно ситуация отличается для мобильных устройств, на которых производится гораздо больше поисковых запросов по всему миру, в США и в выборке SimilarWeb. Здесь мы видим, что усилия Google по оптимизации поиска в первую очередь для мобильных устройств принесли огромные дивиденды для трёх четвертей всех мобильных поисковых запросов клики необязательны.

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

Однако я считаю важным предоставить некоторый контекст относительно мощи монополии Google. В 2020 году поисковый гигант имел более 91% всемирной доли рынка и более 87% рынка США. Как говорится в исследовании GroupM, опубликованном на прошлой неделе в Wall Street Journal, Google также контролирует огромную долю всей рекламы в США, в том числе более 95% поисковой рекламы и более 50% дисплейной рекламы.


Источник: The Wall Street Journal

Однако Google / Alphabet планируют на этом остановиться. Шосана Водински из Gizmodo в своём анализе планов Google по замене рекламных куки проприетарной системой Google для сбора информации о поведении пользователей под названием FLoC сообщила следующее:

Этот план предоставит Google эксклюзивный доступ к огромному объёму пользовательских данных, которые компания и так по большей мере монополизировала.

Такое доминирование это пугающая, но кажущаяся неизбежной перспектива. Аналогично тому, как GDPR препятствовал любым шансам европейских технологических компаний на конкуренцию с технологическими гигантами США, этот шаг по реализации приватности, похоже, в прогнозируемом будущем гарантирует господство Google в сфере поисковой онлайн-рекламы и в вебе (той его части, которая не разделена между Facebook и Amazon).

Для наглядности я изучил данные с января 2018 года (самые ранние данные SimilarWeb, вошедшие в этот проект) по декабрь 2020 года.


График немного скачет, но мы можем сделать несколько любопытных выводов:

  • Общий объём поисковых запросов растёт, однако в падении в конце 2019 года может быть виновна пандемия (может быть, причина и в уменьшении выборки SW, сложно об этом судить)
  • Доля платных поисковых запросов очевидно растёт, и если внимательнее изучить числа, это справедливо и для мобильных, и для десктопных устройств
  • Органические клики выросли в 2020 после долгого плато и небольшого снижения. Похоже, это вызвано увеличением доли использования десктопов в 2020 году (опять из-за Covid-19, вынудившего нас больше сидеть за большими экранами), поэтому после вакцинации ситуация, возможно, поменяется.
  • В конце 2020 года зафиксирована самая высокая доля поисковых запросов с нулевыми кликами. Вероятно, эксперимент Google с ограничением featured snippets (быстрых ответов) в первом квартале (который сейчас, похоже, закончился) может продемонстрировать любопытные корреляции.

Хорошие новости: сегодня в Google как никогда много поисковых запросов и доступно больше кликов.

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



На правах рекламы


Эпичные серверы это надёжные серверы на Linux или Windows с мощными процессорами семейства AMD EPYC и очень быстрой файловой системой, используем исключительно NVMe диски от Intel. Попробуйте как можно быстрее!

Подробнее..

Recovery mode Tier 2 в SEO и интернет маркетинге

08.02.2021 22:18:49 | Автор: admin

Как я вижу, Гез ссылается на Брауна, сам будучи против вас, и это верная примета, что Браун сошлется на Геза. Грин Александр. Бегущая по волнам, 1928 г.

Поскольку как в Гугле, так и в Яндексе пока почти нет информации на русском про tier 2 начнем с определений. В переводе с английского tier это ярус, уже можно понять, что tier 2 это какой-то второй ярус. Если мы начнем гуглить tier 2, то увидим, что термин применяется в построении сетей разных уровней, а также в центрах обработки данных как показатель их надежности, но еще и в выдаче виз в Великобританию. Казалось бы, а при чем здесь SEO или маркетинг?

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

Источник скриншота: https://www.blackhatworld.com/seo/what-is-tier-1-and-tier-2-in-seo.531054/#post-7085808Источник скриншота: https://www.blackhatworld.com/seo/what-is-tier-1-and-tier-2-in-seo.531054/#post-7085808

Стоп, а если я не сеошник, тогда мне не надо дальше читать этот длиннопост? Сеошники вряд ли много нового узнают, может быть только специфический термин, однако, статья должна быть полезна и маркетологам. На самом деле кто бы вы ни были, но вы в любом случае сталкиваетесь с tier 2. В этой статье я попытаюсь пояснить почему узнать всю суть этого термина действительно важно каждому пользователю сети Интернет.

Что такое tier 2 в SEO продвижении сайтов

Начнем с того как я сам не зная этого термина начал работать с tier 2 ссылками. Сейчас, что такое крауд маркетинг знают почти все, а в 2011 году этот термин не знал никто, но методом скрытого маркетинга пользовались многие. Поскольку общение на форумах тогда еще было очень активным, а трафик из соцсетей лился реками, то я просто проводил постинг для получения целевой аудитории. При чем же здесь tier 2?

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

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

Попросту говоря на ЖЖ была ссылка на мой сайт и это tier 1, а ссылка с сайта конкурентов усилила страничку в ЖЖ как tier 2. Эффективность tier 2 по части SEO в том случае была заметна за счет того, что я видел как страничка в ЖЖ также поднялась в выдаче по низкочастотным запросам о том товаре. До того ссылка на сайт, что я продвигал была на ЖЖ год, но не давала никакого SEO эффекта для нужного мне сайта акцептора.

В дальнейшем я уже осмысленно использовал методы усиления ссылок в SEO, хотя сам термин tier 2 не знал еще очень долго. Простой пример: покупаешь ссылку в статье на каком-то маленьком сайте на бирже за 5-10 долларов, а новая страница в индекс не хочет становиться неделями, что делать? Поскольку сеошнику надо отчитываться перед клиентами с индексацией и подгонять рост позиций, то если владельцы сайта донора ничего не делают с перелинковкой, значит приходится самому простимулировать индексацию. Также делал странички на ЖЖ и других платформах для блогов с ссылкой на страницу со статьей в которой ссылка на клиентский сайт, были случаи даже покупал за пару временных ссылок за копейки, чтобы нужная мне страница на совершенно чужом сайте встала в индекс. И это тоже tier 2, только его цель загнать новую страницу в индекс.

Как Google учитывает ссылки tier 2

Начнем с того, что сколько бы официальные представители Google не говорили, что ссылки с атрибутом rel= nofollow никак не повлияют на позиции, но всегда было множество исследований доказывающих это влияние. Видимо поэтому в 2019 году Google официально признал, что ссылки nofollow влияют, если они размещены на авторитетных сайтах (Wikipedia и тд).

Лучше приведу пример старого исследования по которому видно какие ссылки скорее всего и правда не влияют на позиции. В компании Tasty Placement несколько лет назад провели эксперимент, Было создано шесть сайтов одной тематики на шесть городов одинаковых по численности и провели с сайтами различные манипуляции относительно соцсетей. Через десять месяцев проверили показатели, в таблице ниже интересные результаты.

Социальные сети

Изменение коэффициента ранжирования

1-й сайт набрал 100 подписчиков Google +

14.63 %

2-й сайт получил 300 репостов Google +

9.44 %

3-й сайт 70 репостов и 50 лайков бизнес-страницы

6.9 %

4-й сайт получил 50 ретвитов

2.88 %

5-й сайт набрал 1000 подписчиков в Twitter

1.22 %

6-й сайт вообще ни в чем не участвовал.

0.11 %

Ссылка на полноценную презентацию эксперимента: https://www.tastyplacement.com/wp-content/uploads/testing-social-signals.jpg

Не зря же соцсети делают внешние ссылки не просто rel= nofollow, например, Twitter отправляет ссылки на другой URL и потом через 301-редирект. Ссылки из ВКонтакте уводят на скрипт away.php, который проверяет подозрительность внешней ссылки. Мы можем наблюдать на крупных форумах такие же страницы-прокладки, которые только через скрипты позволяют уйти на другой сайт по ссылке. С одной стороны так социальная сеть или форум пытаются удержать пользователя от перехода, с другой стороны это наверняка избавляет от SEO эффекта ссылок второго уровня tier 2, которые многие хотели бы получить из соцсетей.

Зарубежные SEO специалисты давно знают значение ссылок tier 2 и стараются получать их с Википедии, Reddit и других крупных сайтов. Методы простые.

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

  • Находишь сайты по нужной теме продвижения в ТОП выдачи, а они же никогда не оставят ссылку на прямого конкурента, но ты всегда можешь предложить им для размещения статью с ссылкой на информационные источники. А уже в тех источниках будет ссылка на ваш сайт и Google прекрасно передает вес ссылок tier 2.

Наиболее простое доказательство реальной эффективности веса ссылок tier 2 для Google это сети сателлитов, которые до сих пор работают для продвижения. Обычно сеть сателлитов (сеть сайтов общей тематики: Private Blog Network или просто PBN) создается как раз по такой схеме.

Источник схемы: https://digitaluncovered.com/seo-snake-oil-how-to-choose-an-seo-company/Источник схемы: https://digitaluncovered.com/seo-snake-oil-how-to-choose-an-seo-company/

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

В некоторых темах трудно найти достаточное количество сайтов доноров для размещения ссылок и для Google именно сеть PBN иногда чуть ли не единственный выход для подъема позиций даже сейчас в 2021 году. И так будет видимо всегда, потому что поисковые системы обычно не могут распознать такие сетки сателлитов как биржевые продающие ссылки, а фильтры, санкции за ссылки уровня tier 3 или tier 2 не будут самыми жесткими. Главное в этой схеме сайты уровня tier 1, но их условный траст всегда можно подпитать новыми ссылками, а также разбавлять крауд маркетингом и тд. Впрочем, постоянно продвигать сайт только сетями PBN не выйдет, алгоритм Google Penguin рано или поздно определит эти манипуляции, поэтому сеть сателлитов может применяться только как некий вспомогательный фактор продвижения, один из методов SEO.

Вред от ссылок tier 2

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

Обратимся к исследованию, которое было проведено в 2016 году и повторено в 2020 году с теми же результатами. Суть эксперимента была в том, что придумали косметику с вымышленным названием Phylandocic и тестовое переменное слово Ancludixis. Все это не имело ни единого результата в выдаче.

  • Было создано десять сайтов по 300 слов на каждом.

  • С пяти сайтов были проставлены внешние ссылки на авторитетные источники: Оксфордский университет, Кембриджский университет и тд.

  • Исследователи минимализировали посещения сайтов по ключевым фразам, все делали в режиме инкогнито, даже не проверяли поиск до конца эксперимента и все это знал ограниченный круг людей.

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

Update: We have rerun this experiment in April 2020 using fresh domains and can confirm the same results. External links remain a ranking factor and good SEO best practise.

Ссылка на исследование: https://www.rebootonline.com/blog/long-term-outgoing-link-experiment/ Ссылка на исследование: https://www.rebootonline.com/blog/long-term-outgoing-link-experiment/

А теперь вспомним, что есть печеньки (cookie) и браузер Google Chrome, а в нем многие авторизованы под своим аккаунтом Google. Становится ясно, что если вы разместите ссылку на сайт, который сам по себе безобиден, но при этом размещает много ссылок на плохие сайты, то это значит Google точно узнает всю цепочку сайтов, что ссылаются на плохой сайт. Вывод: также как построение цепей ссылок tier 1, 2 и 3 поднимают в ТОП Google их акцептора, так и может пессимизировать за то, что вы ссылаетесь на некий сайт, который является помойкой для ссылок ведущих скорее всего на подозрительный сайт. Ведь разрешенные к продвижению по тематике сайты на биржах могут покупать ссылки напрямую.

Пример из моей SEO практики: клиентский сайт выставили на биржу, чтобы немного отбить заказами на статейные ссылки цену продвижения. Трастовому сайту нормально проставить немного ссылок из статей на другие живые сайты той же тематики. Как-то пришел заказ на симпатичный и аскетичный тематичный сайт, он мне показался подозрительным, хотя внешних ссылок на нем не было, поэтому ссылку на него разместил. Заказчик из биржи был очень доволен и тут же выслал еще десять заказов на подобные сайты похожие как близнецы, а начинаешь проверять и видишь, что они на одном ip (хостинге). Тогда я сразу понял, что пытаются прокачать сеть сателлитов, которые позже неизвестно куда уведут ссылками и уговорил клиента отказаться от такого оптового размещения, то есть поберечь свой сайт. Пока сателлиты без ссылок чистые все нормально, но ведь они обязательно будут использованы по назначению.

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

  • Вначале все будет хорошо, ведь ссылки вечные, некоторые биржи дают год гарантии за дополнительную оплату страховых взносов, но чаще всего на год хватает стабильности и так.

  • Зато через пару лет окажется, что ничто не вечно, а сайты на бирже ушли в бан по всем показателям, выпали из индекса, потому что их администраторы были жадные и продавали слишком много ссылок, а под конец падения даже не тематические. Вот такой tier 2 внешними ссылками убивающий ваш донор.

  • Выходит ссылки второго уровня на сайте донора когда-то могут испортить вам вес SEO ссылки первого уровня с положительного на отрицательный. Если в Google есть инструмент Disallow Links, чтобы отклонить испорченные ссылки, то в Яндексе ничего подобного нет.

Tier 2 и интернет маркетинг

Про крауд маркетинг уже были примеры, перейдем сразу к съему целевой аудитории из ТОП поисковых систем с помощью tier 2. Десять лет назад были популярен сервис Ответы Mail.ru и подобный был у Google, причем в обоих случаях ответы выдавались в ТОП выдачи по ключевым запросам, вопросам. Эти методы сейчас знают и понимают уже все/

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

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

Только Ответы Mail.ru уже давно не в тренде, сервис Ответы Google закрылся, зато появился Яндекс.ДЗЕН и сейчас он почти всегда в ТОП выдачи Яндекса. Кстати, qna.habr тоже немного заспамлен и это также tier 2 (ищешь вопрос, находишь ответ, а там в комментарии ссылка куда-то).

С чем я не имел дело, то есть никогда сам такого не делал, но с чем сталкивался каждый из нас.

  • Вы ищете в Яндексе или Google какой-то вопрос, из ТОП выдачи попадаете на страницу в соцсети Вконтакте и там вместо ответа вас ждет ссылка на какой-то подозрительный сайт.

  • Вы попадаете из поисковой системы на какой-то сайт (doorway), а там заглушка и навязчиво предлагается перейти еще на какой-то сайт. Думаю все уже догадались о каких сайтах речь, ведь такие сайты для взрослых запрещены в большинстве случаев для контекстной рекламы и тд.

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

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

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

  • Если текст рекламный и ссылка хорошо выделена на видном месте или подкреплена тизером это маркетинг.

  • Если текст как бы ни о чем, а контекстная ссылка в тексте или постовой в конце статьи это линкбилдинг.

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

Почему редко используют термин tier 2

Все дело в специфике, поскольку термин SEO уже хорошо известен и распиарен, то именно так и называют продвижение в общем и целом для клиентов. На самом деле студия занимающаяся продвижением сайтов может успешно использовать самые разные методы, но путать клиентов терминами не будут. Вспомните коммерческие предложения, что вам высылали по поводу продвижения, ведь там наверняка общие фразы про индивидуальный подход в линкбилдинге и качественные ссылки? Чем больше непонятной информации, тем сложнее заказчику решиться. Если про SEO заказчик понимает, то про интернет маркетинг смутно, а термин crowd многих и сейчас вводит в ступор. Услугу могут предлагать, но объяснять будут простыми словами, без специфических терминов.

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

Впрочем, никакие ссылки и SEO методы не помогут подняться в ТОП, если у сайта есть технические проблемы с доступностью всех страниц или низкая скорость загрузки. Уже давно сеошники учитывают скорость загрузки сайтов конкурентов, чтобы обойти их в ТОП. Только в 2020 году Google конкретно заявил, что в 2021 году для ранжирования сайтов станут достаточно важными факторы Web Vitals: скорость загрузки, интерактивность и визуальная стабильность. Доступность и качество ресурса невозможны без мониторинга, когда у вас хостинг в надежном дата-центре с грамотной поддержкой, которая готова оперативно отреагировать в случае экстренной ситуации. Очевидно, ссылки tier 2 и 3 на сетях сателлитов для продвижения в Google теперь тоже придется размещать только на надежных хостингах с хорошей поддержкой.

Все же специфические термины полезны для смежных с SEO профессий, они помогают более четко работать команде по продвижению сайта (сеошник, программист, контент редактор, интернет маркетолог и тд). Когда составляется план комплексного продвижения сайта, вырабатывается стратегия все члены команды должны знать SEO термины, даже про серые методы, сателлиты, tier 2 и понимать их.

Tu deviens responsable pour toujours de ce que tu as apprivois. (из повести Маленький принц Антуана де Сент-Экзюпери, 1943 г.)

Подробнее..

Перевод Новый, смелый, анонимный поисковик Brave Search

12.03.2021 14:20:14 | Автор: admin

Создатели браузера Brave запустят независимый, privacy-first поиск, который не является обёрткой над поисковыми машинами из bigtech.

Новый, смелый, анонимный: поисковик Brave Search Новый, смелый, анонимный: поисковик Brave Search

Браузер Brave с его новым встроенным поиском станут первой в индустрии независимой альтернативой паре Chrome + Google Search, альтернативой, которая будет оборонять приватность своих пользователей. Мы купили открытый поисковой движок Tailcat для создания собственной поисковой системы Brave Search. Tailcat разрабатывала Cliqz компания из Мюнхена, до 2020 года делавшая браузерные и поисковые продукты.

Практически все современные поисковики либо сделаны гигантами рынка, либо используют их выдачу (см. DuckDuckGo). Tailcat не собирает и не использует ни персональные данные пользователей, ни их ip-адреса. В его основе собственный, полностью независимый индекс. Brave Search, который разрабатывается на базе Tailcat, станет частью линейки наших продуктов, ориентированных на приватность и конфиденциальность.

Сложное решение о создании собственной поисковой системы мы приняли не просто так. Пользователи сети всё больше интересуются альтернативными вариантами браузеров и поисковых систем вариантов, которые были бы ориентированы на человека, а не на заработки технологических гигантов. Благодаря такому спросу, Brave достиг аудитории в 25 миллионов активных пользователей в месяц сильный рост за последний год, но вполне ожидаемый. То же самое сейчас происходит с мессенджером Signal, который также заточен на сохранение приватности. Люди переходят на него после того, как WhatsApp сообщил об изменении политики конфиденциальности мало кому понравилось, что ещё больше данных теперь будет утекать в Facebook.

Приватность по умолчанию наконец-то становится мейнстримом этим мы руководствуемся при разработке Brave и Brave Search, которую мы будем вести по таким правилам:

  1. Приватность. Brave Search не следит за действиями пользователей и не пытается их идентифицировать.

  2. Ориентированность на пользователя. Во главе угла человек, а не индустрия слежки и бигдаты.

  3. Выбор. Между платным поиском без рекламы или бесплатным, но с рекламой, не нарушающей приватности.

  4. Независимость. Мы будем использовать анонимизированные сигналы от сообщества для улучшения Brave Search. До сих пор качественные результаты выдают только гиганты рынка, которым потребовались огромные затраты, чтобы проиндексировать весь интернет.

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

  6. Бесшовность. Мы сделаем первоклассную интеграцию браузера и поиска с быстрыми персонализированными результатами, не идя на компромиссы в вопросах приватности.

  7. Открытость. Мы не веруем в огораживание наш движок будет доступен для создания других поисковых систем на его основе.

Брендан Айк, CEO и сооснователь Brave: Аудитория Brave существенно выросла за последний год с 11 миллионов активных пользователей в месяц до 25 миллионов. В 2021 году мы ожидаем дальнейшего повышения спроса, поскольку все больше и больше людей хотят вырваться из-под крыла Большого Брата и ищут решения, которые могли бы обеспечивать их приватность. Миссия Brave в защите интересов пользователей. Интеграция поиска в нашу платформу необходима, чтобы перестать подкидывать дрова персональных данных в костер экономики, построенной на слежке и сборе данных.

Поль-Бернар Каллен, управляющий Hubert Burda Media, холдингом, в котором работала команда Cliqz: Мы очень рады, что наша технология теперь используется в Brave и что она будет основой для оригинальной, сохраняющей приватность альтернативе Google. Как акционеры Brave, мы продолжим участвовать в этом проекте.

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

Все наши продукты ориентированы на приватность: рекламная платформа Brave Ads, на которой были проведены почти 3000 кампаний в 200 странах (в числе рекламодателей New York Times, Amazon, PayPal и другие), новостной агрегатор Brave Today, прототип сервиса видеоконференций Brave Together и ядро экосистемы браузер. С новой поисковой машиной Brave сделает интернет чище и прозрачнее.

Если вы хотите поучаствовать в тестировании Brave Search, запиcывайтесь по ссылке.

Подробнее..

Recovery mode Гарантии в SEO миф! Или кто что обещает, и кто что реально может выполнить

16.04.2021 18:11:03 | Автор: admin

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

Почему клиенты требуют гарантии

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

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

Есть ли гарантии в SEO?

Сразу скажем, что на 100% эффективный результат не способно обеспечить ни одно, даже самое известное агентство. Но пообещать это может каждый. Нередко на сайте подрядчика можно увидеть: выведение в топ-3 за месяц, обеспечим вечные позиции в топе и пр. Клиенты зачастую ведутся на красивые обещания и уже готовы заключить договор на услугу. Но! Лозунги "SEO-продвижение с гарантией" это утопия.

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

  1. Возраст ресурса. Поисковое продвижение молодых сайтов (существуют менее года) выполнить сложнее.

  2. Хостинг. Для успешной раскрутки лучше иметь выделенный сервер. Ресурсы, размещенные на общем хостинге, более уязвимы к вирусам.

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

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

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

Фейковые гарантии на любой вкус: разбираем популярные

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

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

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

  2. Результат за 7 дней! Часто эту фразу используют в контекстной рекламе или SEO-агентства на своих сайтах. Однако при заключении договора на сотрудничество подобных заявлений не будет.

  3. Вывод в топ навсегда. Стоит понимать, что алгоритмы поисковиков постоянно меняются, причем происходит это внезапно, из-за чего сайты в одно мгновение утрачивают свои позиции. На место ресурса в рейтинге влияют и другие факторы:

  • действия конкурентов;

  • качество работы исполнителя;

  • поведение клиентов;

  • проблемы в работе самого сайта и пр.

На фоне всего этого становится понятно, что навсегда это утопия.

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

На какие гарантии в SEO можно нужно рассчитывать

На фоне всех этих популярных обещаний-мифов возникает закономерный вопрос: А стоит ли вообще на какой-то прогнозируемый результат рассчитывать? Да! Гарантии в SEO-продвижении возможны, если речь идет о проделанных работах и качестве услуг.

Рассмотрим ниже, какие виды SEO-гарантии могут себя оправдать.

Оплата за конкретный результат

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

Гарантия выхода в ТОП

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

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

Плата за трафик

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

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

  2. Бывает, что агентство хитрит и считает весь трафик, а не конкретное количество посетителей, которые были реально привлечены за счет SEO-продвижения.

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

Гарантия возврата средств

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

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

Плата с процента от продаж

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

  • первые покупатели могут появиться даже через несколько месяцев. Конечно, агентство не будет столько времени работать впустую, оплата все равно будет взиматься;

  • опытные специалисты, которые точно понимают, что ваш бизнес прибыльный, потребуют солидный процент (долю вашего бизнеса).

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

Гарантия качества выполненных работ

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

Среди основных:

  1. Соблюдение условленного срока работ. Обещание подкрепляется юридическим договором.

  2. Предоставление заказчику доступа в онлайн-кабинет, где отслеживается течение работ.

  3. Привлечение к SEO-продвижению только лучших исполнителей.

  4. Использование белых подходов к раскрутке.

  5. Своевременность выполнения задач, прописанных в договоре.

  6. Предоставление еженедельного отчета по поисковому трафику и ежемесячного по KPI.

Мы не делаем громких заявлений о выведении в топ по сотням ключей или 200300% профита за 7 дней. А прогноз формируется после аналитики и аудита, а также составляется план раскрутки, определяется объем работ по внешней и внутренней оптимизации.

Плата за KPI

Есть агентства, которые предпочитают подобный формат сотрудничества. Среди ключевых параметров:

  • коэффициент, обозначающий окупаемость инвестиций (Romi);

  • видимость веб-сайта по СЯ;

  • конверсия;

  • целевые действия посетителей;

  • лиды.

Главной проблемой SEO-продвижения с оплатой за KPI является отсутствие комплексности. Исполнитель может выбрать наиболее выгодный для себя параметр, чтобы продемонстрировать заказчику красивые цифры и получить вознаграждение. Чтобы понять эффективность SEO, нужно KPI оценивать комплексно.

Обычно агентства, обещая продвижение сайта в топ с гарантией, используют лишь 3 показателя:

  • выведение в топ-10 по ключам;

  • привлечение трафика в прогнозируемом объеме;

  • получение лидов по определенной стоимости.

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

Признаки надежного подрядчика

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

  1. Предоставление индивидуального предложения, а не словесных SEO-гарантий.

  2. Избирательность. Если у подрядчика имеются десятки проектов различной направленности, то стоит обходить такого мастера на все руки стороной: велика вероятность, что вашему сайту не уделят должного внимания и станут применять лишь базовые способы СЕО-продвижения. В Elit-Web, например, другой подход. Компания не берет проектов сверх тех, которые может продвигать, приложив максимум усилий.

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

  4. Реальные кейсы. Портфолио лучший аргумент в пользу профессионализма подрядчика, который действительно способен обеспечить гарантии в SEO. Кейсы должны быть свежими и подробными, сопровождаться отзывами заказчика.

  5. Вовлеченность команды. Над продвижением должен работать целый штат специалистов: программисты, копирайтеры, менеджеры, сеошники, интернет-маркетологи и пр.

  6. Техподдержка. Для каждого проекта назначается персональный менеджер, который на связи 24/7.

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

  8. Предоставление пошагового детального плана продвижения. Отсутствие конкретики или перечисление базовых задач явно не заслуживают доверия.

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

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

Все остальное, включая выход в топ за неделю и 300% профита за день это пустой пафос.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru