Я люблю ввязываться в авантюры, и за последний месяц об одной из них я пару раз рассказывал друзьям, что вызывало восторг, поэтому решил поделиться с хабравчанами! Эта история про отважные пет-проекты, мощь опен-сорса и саморазвитие, а также основные технические детали. Надеюсь, вас это вдохновит.
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
После завершения описанной истории, я решил опубликовать исходники, но т.к там в истории коммитов засветились токены, то перезалил в новый репозиторий. Но код действительно такой, что мне самому туда страшно заглядывать.
3.2. Польза
Здесь, как и в других своих пет-проектах и стартапах, я набрался много опыта:
Разобрался с многопоточностью в Питоне.
Покопался в специфических вопросах оптимизации MySQL запросов.
Научился строить организацию приложений, файлов и комментов на будущее, чтобы оно не превращалось в спагетти.
Освоил работу из кода с SSH для настройки окружения, понял, насколько чудесен Ansible.
Разработал микросервисную архитектуру из клея и палок, что затем позволило легко понять концепции Kubernetes.
И всё это мне очень пригодилось в последующих работах и проектах.
3.3. Мораль
Выводы каждый сделает свои, но главное – не бойтесь пробовать, учиться и искать себя! Надеюсь, вам было интересно.
rsashka
Спасибо, было интересно, лишь бы теперь вас не посадили (сарказм).
AivanF Автор
Благодарю :)
IAChernyshov
За что, собственно, должны посадить?
leventov
del