- PVSM.RU - https://www.pvsm.ru -
Мало кто верит, что современный data science-стек может быть построен не на Python, но такие прецеденты есть :). Стек Одноклассников формировался долгие годы, в первую очередь программистами, перешедшими в data science, но всё ещё остались близкими к проду, поэтому в его основе лежат открытые технологии JVM-стека: Hadoop, Spark, Kafka, Cassandra и т.д. Это помогает нам сокращать время и затраты на ввод моделей в эксплуатацию, но иногда создаёт и сложности. Например, при подготовке базовых решений для участников SNA Hackathon 2019 [1] пришлось сжать волю в кулак и погрузиться в мир динамической типизации. Подробности (и лёгкий троллинг) под катом :)
Какой-то питон найдётся почти на любой девелоперской машине. Нашёлся он и на моей, аж в двух экземплярах — 2.7 и 3.4. Порывшись в закромах памяти я вспомнил, что версию 3.4 поставил три года назад, после того, как на SNA Hackathon 2016 участники столкнулись с эпичными проблемами, пытаясь разложить в памяти полуторагигабайтный граф (по итогам получилось небольшое обучающее видео [2] и отдельное соревнование [3]), но сегодня это хозяйство уже морально устарело и нуждалось в обновлении.
В мире Явы каждый проект при сборке указывает всё то, что хочет в себя вобрать, и с этим дальше и живёт. В теории всё просто и красиво, на практике же, когда тебе нужна библиотека А и библиотека Б, обязательно выяснится что им обеим нужна библиотека C, при этом двух разных несовместимых версий :). В тщетных попытках разорвать этот порочный круг некоторые библиотеки пакуют все свои зависимости в себя самих [4] и прячут от остальных, а остальные крутятся, как могут.
Что с этим в питоне? Проекта как такового нет, но есть «окружение», и в рамках каждого окружения можно сформировать независимую экосистему из пакетов определённых версий. При этом есть и инструменты для ленивых [5], с помощью которых локальным питоновским окружением управлять не сложнее, чем распределённым гетерогенным кластером Клаудеры или Хортона. Но взаимные конфликты версий пакетов никуда не денутся.Я сразу же столкнулся с тем, что релиз Pandas 0.24 [6] перевёл приватный метод _maybe_box_datetimelike в публичный API, и неожиданно оказалось, что много кто его в прежнем виде юзал и теперь отвалился :) (и да, в мире Явы все то же самое [7]). Но в итоге удалось всё починить, не считая страшных вариннгов о новых деприкейшинах, заработало.
Задачи на SNA Hackathon 2019 [1] разделены на три направления — рекомендации по логам, по текстам и по картинкам. Начнём с логов (спойлер — мегапаттерн Cmd+C/Cmd+V со стэковерфлоу работает и с питоном). Данные были собраны с живого продакшина — каждому пользователю случайным образом, без взвешивания, показывался некоторый фид из его окружения, после чего в лог записывались все признаки на момент показа и итоговая реакция. Задача piece of cake: берём признаки, пихаем в логрег [8], профит!
Но план зафэйлился на первом же этапе, на чтении данных. В теории, есть замечательный пакет Apache Arrow [9], который дожен был унифицировать работу с данными в разных экосистемах и, в том числе, позволить читать «паркетные» файлы [10] из питона без спарка. На практике же оказалось. что даже с чтением простых вложенных структур у него проблемы [11], и наша красивая иерархия превратилась в плоское убожество :(.
Но были и положительные стороны. Jupyter [12], в целом, порадовал, он почти такой же удобный, хоть и не такой няшный, как Zeppelin [13]. Даже скала-ядро [14] есть! Ну и скорость логистической регрессии на небольшом куске данных в памяти порадовала — до мощности распределённого варианта не дотягивает, но на четырёх признаках и паре сотен тысяч примеров учится мгновенно.
Потом, правда, возникший было энтузиазм получил удар под дых: если необходимая трансформация данных (сгруппировать по ключу и собрать в список) отсутствует в стандартном списке и появляется apply или map, то скорость падает на порядки. В итоге, 80 % времени baselineа занимает не чтение данных, джоины, обучение модели и сортировка, а банальное составление списка.
К слову, именно из-за этой особенности я всегда удивляюсь пользователям pySpark — ведь почти вся стандартная функциональность доступна в виде Spark SQL, который одинаков и в питоне, и в скале, а после появления первой питонячей юдф-ины с чем-то личным десятитысячеядерный кластер превращается в тыкву…
Но, в итоге, все препятствия преодолены и для score 0.65 хватило девяти параграфов [15]!
Ну вот, теперь у нас задача посложнее — если логрег можно найти в сотне реализаций под все платформы, то уж разнообразие инструментов работы с текстами под питоном больше. К счастью, тексты уже идут не только в сыром виде, но и обработанные нашей штатной системой препроцессинга на базе спарка и Lucene [16]. Поэтому можно сразу брать список токенов и не париться токенизацией/лематизацией/стемингом.
Некоторое время я сомневался, что же взять: отечественный BigARTM [17] или импортный Gensim [18]. В итоге остановился на втором и скопипастил их doc2vec туториал [19] :). Надеюсь, коллеги из команды BigARTM не преминут воспользоваться шансом и покажут преимущества своей библиотеки на конкурсе.
У нас опять простой план: берём все тексты из теста, обучаем модель Doc2Vec, далее инферим её на трэйне и на её результатах учим логрег (стэкинг — наше всё!). Но, как обычно, сразу начались проблемы. Несмотря на относительно скромный объём текстов в обучающей выборке (всего полтора гига), при попытке втянуть их в пандас питон отожрал 20 (20, Карл!) гигабайтов памяти, ушел в своп и не вернулся. Пришлось есть слона по частям.
При чтении всегда указываем, какие именно колонки поднять из паркета, что позволяет не читать сырой текст в память. Это экономит её использование вдвое, документы тестового множества грузятся в память для обучения без проблем. С обучающей выборкой такого трюка мало, поэтому за раз прогружаем не более одного «паркетного» файла. Далее в загруженном файле оставляем только ID из тех дней, которые хотим использовать для обучения, и уже на них инферим эмбединги.
Логрег поверх этого работает так же быстро, и в итоге получаем 14 параграфов [20] и score 0.54 :)
Что может быть популярнее deep learning? Только котики! Поэтому для картиночного baseline-а мы составили гениально простой план: прогоняем на картинках из тестового множества детектор котиков, а потом ранжируем контент по полученному score :)
И опять есть из чего выбирать. Классификация или детекция? pyTorch или Tensorflow? Главный критерий — простота реализации методом копипаста. And the winner is… YOLOv3 на MXNet [21] :). Лаконичность их демки пленила с первого взгляда, но потом, как обычно, начались проблемы.
С чего обычно начинается работа с большими данными? С оценки производительности и необходимого машинного времени! Хотелось сделать baseline максимально автономным, поэтому научили его работать напрямую с tar-файлом [22] и быстро поняли, что при скорости извлечения из tar в tmpfs 200 фото в секунду на обработку 352 758 картинок понадобится порядка получаса, норм. Добавляем загрузку и предобработку фоток — 100 в секунду, примерно час на всё, норм. Добавляем вычисление нейросетки — 20 фоток в секунду, 5 часов, нуууу ок… Добавляем извлечение результата — 1 фотка в секунду, неделя, WTF?
После пары часов танцев с бубнами приходит понимание, что NDArray, который мы наблюдаем, это ни разу ни numpy, а внутренняя структура MXNet-а, который проводит все вычисления лениво. Бинго! Что же делать? Любой начинающий диплернер знает, что всё дело в размере батча! Если MXNet вычисляет score лениво, то если мы сначала запросим их для пары десятков фото, а потом начнём их извлекать, то, быть может, обработка фоток пойдёт батчем? И да, после добавления батчинга со скоростью 10 фото в секунду удалось найти всех котиков :).
Дальше применяем уже известную машинерию и за 10 параграфов [23] получаем score 0.504 :).
Когда одного мудрого сэнсея спросили «Кто победит, мастер по айкидо или по карате?», он ответил «Победит мастер». К примерно таким же выводам нас привёл и этот эксперимент: нет и не может быть идеального языка на все случаи жизни. С Python вы можете быстро собрать решение из готовых блоков, но попытка отойти от них при достаточно больших объёмах данных принесёт много боли. В Java и Scala вы тоже можете найти много готовых инструментов и легко реализуете собственные идеи, но сами языки будут более требовательны к качеству кода.
Ну и, конечно, удачи всем участникам SNA Hackathon 2019 [1]!
Автор: dmitrybugaychenko
Источник [24]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/308171
Ссылки в тексте:
[1] SNA Hackathon 2019: https://snahackathon.org
[2] обучающее видео: https://www.youtube.com/watch?v=ZlXjxB-3ISo&feature=youtu.be
[3] отдельное соревнование: https://mlbootcamp.ru/round/13/sandbox/
[4] себя самих: https://maven.apache.org/plugins/maven-shade-plugin/
[5] инструменты для ленивых: https://www.anaconda.com/distribution/
[6] Pandas 0.24: http://pandas.pydata.org/pandas-docs/version/0.24/
[7] то же самое: https://habr.com/ru/post/322840/
[8] логрег: https://en.wikipedia.org/wiki/Logistic_regression
[9] Apache Arrow: https://arrow.apache.org
[10] «паркетные» файлы: https://parquet.apache.org
[11] проблемы: https://issues.apache.org/jira/browse/ARROW-1599
[12] Jupyter: https://jupyter.org
[13] Zeppelin: http://zeppelin.apache.org
[14] скала-ядро: https://toree.apache.org
[15] девяти параграфов: https://github.com/MailRuChamps/snahackathon/blob/master/2019/Collaborative.ipynb
[16] Lucene: http://lucene.apache.org
[17] BigARTM: https://bigartm.readthedocs.io/en/stable/
[18] Gensim: https://radimrehurek.com/gensim/
[19] doc2vec туториал: https://radimrehurek.com/gensim/models/doc2vec.html
[20] 14 параграфов: https://github.com/MailRuChamps/snahackathon/blob/master/2019/Texts.ipynb
[21] YOLOv3 на MXNet: https://gluon-cv.mxnet.io/build/examples_detection/demo_yolo.html
[22] tar-файлом: https://docs.python.org/3/library/tarfile.html
[23] 10 параграфов: https://github.com/MailRuChamps/snahackathon/blob/master/2019/Images.ipynb
[24] Источник: https://habr.com/ru/post/439394/?utm_campaign=439394
Нажмите здесь для печати.