Одна из причин, по которой я люблю статистику и машинное обучение, заключается в том, что они предоставляют методы, позволяющие компьютерам решать проблемы умно и быстро, что в противном случае потребовало бы значительных ручных усилий и времени.
В этой статье я описываю, как я подошел к одной такой проблеме, связанной с обнаружением контента и рекомендациями, с использованием методов неконтролируемого машинного обучения.
Я также воспользовался возможностью изучить решение с помощью Apache Spark вместо использования более распространенной платформы машинного обучения, такой как scikit-learn Python. Преимущество реализаций машинного обучения Spark заключается в том, что они поддерживают распределенную кластерную обработку из коробки, в отличие от реализаций scikit-learn.
Я решил проводить свои эксперименты в облаке Linode по нескольким причинам. Во-первых, неплохо было бы познакомиться с несколькими облаками на случай, если вам потребуется быстро выполнить миграцию или настроить резервный вариант в случае аварии.
Во-вторых, Linode недавно представила экземпляры с высоким объемом памяти, которые идеально подходят для Spark, и мне было интересно узнать об их производительности.
И в-третьих, я считаю, что пропускная способность сети, ограничения и цены у Linode лучше по сравнению с другими облаками.
В этой статье я опишу подход машинного обучения, архитектуру развертывания, которую я использовал, и оптимизацию, которую я сделал во время этих экспериментов.
Теперь к проблеме ...
Эта проблема
Ежедневно в Интернет добавляется обширный контент (по некоторым оценкам, 3 эксабайта). Хотя большая часть этого, несомненно, не имеет отношения к мне, должно быть также много полезного контента - например, статей, обсуждений и видео - которые относятся к моим интересам, но которые я упускаю, потому что у меня нет времени или терпения. для постоянного отслеживания обновлений на нескольких веб-сайтах.
Я уже ежедневно посещаю свои любимые веб-сайты, использую средства чтения RSS-каналов и подписываюсь на порталы-агрегаторы новостей. И они действительно помогают.
Но я также хотел изучить подходы, которые являются более автоматизированными, более гибкими, более персонализированными, более частными и требуют меньше усилий.
Формулировка проблемы, которую я придумал: Учитывая список моих интересов в качестве входных данных, может ли система найти и порекомендовать контент, который соответствует этим интересам?
Подход к машинному обучению без учителя
Есть несколько подходов к подобной проблеме. Хотя «контент» также может включать нетекстовый контент, такой как изображения, видео и музыку, я буду придерживаться подходов к обработке текста и обрабатывать любой нетекстовый контент, используя только текстовые данные, связанные с визуальными элементами, например описания видео или подписи к изображениям.
Один из простых подходов - сканировать Интернет и использовать подходы к поиску информации, такие как функции TF-IDF (частота терминов - обратная частота документов), а также такие показатели, как косинусное сходство, для поиска контента, который соответствует другому контенту. Для этого я бы использовал что-то вроде Apache Nutch и Solr или ElasticSearch. Но проблема с такой системой заключается в том, что связанные ключевые слова часто недостаточно богаты, чтобы адекватно описывать контент, и любой контент, который явно не соответствует этим ключевым словам, скорее всего, будет отброшен.
Тогда есть подход контролируемой классификации. Мне нужно было бы найти образец контента, который описывает мои интересы, пометить их одним или несколькими интересами и использовать контролируемые методы классификации, такие как вспомогательные векторные машины или случайные леса, для классификации нового контента.
Хотя это прекрасный метод, который дает хорошие результаты, проблема, с которой я сталкиваюсь, заключается в том, что поиск контента и его маркировка требуют ручных усилий, которых я не хочу выполнять. Всякий раз, когда у меня появлялся новый интерес (что я часто и делаю), мне приходилось снова прилагать какие-то усилия по навешиванию ярлыков.
Еще одна проблема с контролируемой классификацией заключается в том, что если список интересов становится длинным из-за разницы в уровне детализации между некоторыми из них, объем требуемых обучающих данных также увеличивается.
Третий подход - это подход неконтролируемой кластеризации, когда контент группируется в несколько кластеров. В контексте этой проблемы каждый кластер контента можно рассматривать как представляющий один интерес. Любой новый найденный контент затем назначается одному из этих кластеров, и рекомендации могут быть извлечены для каждого кластера.
Это не требующий больших усилий подход, но его проблема в том, что любой контент, который на самом деле представляет собой смесь тем - например, эта самая статья - в конечном итоге назначается только одному кластеру, а его связи с другими кластерами отбрасываются. В реальности это не так, и это отрицательно влияет на рекомендации.
Но в предыдущем абзаце есть ключевая фраза - смесь тем - которая подводит нас к лучшему подходу, называемому тематическое моделирование. Он признает реальность того, что один и тот же контент может принадлежать нескольким корзинам. Среди множества методов тематического моделирования я выбрал скрытое распределение Дирихле (LDA).
LDA - это метод, который составляет список так называемых «тем» и рассматривает каждый фрагмент контента как распределение вероятностей по этим темам. Но тема LDA - это не просто ярлык, как в классификации. Напротив, каждая тема LDA сама по себе представляет собой распределение вероятностей для слов в корпусе документов.
Слово «скрытая» в статистике означает скрытую информацию, которая не наблюдается напрямую, но выводится с помощью статистических методов. «Скрытый» в LDA возникает из-за того, что он выводит набор тем, используя только слова в корпусе документов.
На диаграмме ниже показан пример мировоззрения LDA, в котором существует ряд тем, и каждый документ в корпусе представляет собой комбинацию этих тем.
Итак, как LDA помогает в решении моей проблемы с обнаружением контента и рекомендациями?
Идея состоит в том, что с учетом обучающего корпуса контента:
- сначала откройте для себя набор тем из учебного корпуса, используя LDA.
- для каждого содержания в обучающем корпусе вычислите вероятности, связанные с каждой из этих тем.
- Затем, всякий раз, когда новый контент должен быть оценен для рекомендации или отклонения, вычислите его вероятность по тем же темам.
- Если его вероятности близко совпадают с вероятностями любого контента из учебного корпуса, рекомендуйте его как похожий на этот учебный контент.
Следующий вопрос: что использовать в качестве учебного корпуса для LDA? Помните, что фактические исходные данные для этой проблемы - это мои интересы, и поэтому учебный корпус должен отражать эти интересы. Однако в моем понимании интересы - это всего лишь абстрактные концепции, в то время как компьютерам нужно что-то более конкретное, например, таблица чисел.
Один набор легкодоступных данных, которые вытекают из моих интересов, - это содержимое URL-адресов в моей истории просмотров в Интернете, потому что маловероятно, что я буду продолжать просматривать что-то, что меня не интересует. Таким образом, история просмотров в Интернете - это обучающие материалы в этом решении.
Существуют и другие возможные репрезентативные данные, которые можно использовать - например, портативный ЭЭГ-монитор, записывающий мою мозговую активность, или налобная камера, фиксирующая мои действия, когда я занимаюсь разными интересами, - но данные истории просмотров в Интернете легко доступны, просты и практичны.
Реализация решения с помощью Spark
Apache Spark поставляется в комплекте с собственной библиотекой алгоритмов машинного обучения, которая называется MLlib. MLlib предоставляет две разные реализации LDA:
- распределенная модель LDA, которая использует максимальное ожидание в качестве алгоритма оптимизации, но не поддерживает онлайн-обучение.
Онлайн-обучение относится к моделям машинного обучения, которые могут обновляться по мере поступления новых данных, что очень полезно в средах, где выводы следует делать немедленно, используя самые свежие данные.
- нераспределенная модель, которая использует вариационный алгоритм Байеса и поддерживает онлайн-обучение.
Я выбрал распределенную модель LDA главным образом потому, что в моем простом решении не использовалось онлайн-обучение. Но я попробовал их оба и написал об их относительной эффективности в более позднем разделе.
Для получения данных истории просмотров я установил это простое расширение Chrome Export History без излишеств. Он не требует каких-либо специальных разрешений и без проблем экспортирует историю в файл JSON.
Рекомендованию требуется набор целевых URL-адресов для отслеживания и рекомендации контента. Полное решение предполагает постоянное сканирование этих URL-адресов, получение новейшего контента и его анализ для получения рекомендаций. Но это было бы перебором.
Вместо этого я настроил систему для отслеживания только нескольких подреддитов, которые, как мне известно, меня интересуют, и последних видеороликов YouTube с использованием ее API. При необходимости система поддерживает добавление дополнительных подключаемых модулей для извлечения контента.
Архитектура развертывания
Задания Spark могут выполняться на одном узле в «локальном» режиме или на кластере узлов, если требуется больше ресурсов. Кластер Spark состоит из мастера и нескольких рабочих. Мастер координирует и распределяет операции, в то время как рабочие выполняют операции.
Хранилище, которое чаще всего развертывается со Spark, - это HDFS. Для действительно больших данных это отличная файловая система с высокой доступностью и надежностью. Но для моего простого приложения HDFS было излишним.
Однако мне все еще нужна была какая-то общая файловая система, потому что каждая машина Spark должна иметь доступ к входным и целевым данным для их обработки. Итак, я только что развернул простые старые общие ресурсы NFS на мастере Spark.
Попробуй это
Скрипты, код и инструкции для архитектуры развертывания находятся на GitHub по адресу https://github.com/pathbreak/content-recommender-spark-lda.
На данный момент мое доказательство Spark проверяет содержимое только URL-адресов YouTube и HackerNews в истории просмотров и на основе распределения тем в этих URL-адресах рекомендует последние видеоролики YouTube или контент из любого канала RSS / ATOM (блоги Reddit и WordPress являются хорошими примерами, которые предоставить исчерпывающие RSS-каналы).
Система поддерживает подключаемые модули для обработки URL-адресов. Если вам нужна поддержка других веб-сайтов, вы можете сообщать о проблемах на GitHub или добавлять свои собственные плагины.
Как история, так и целевая выборка контента намеренно сохраняются однопоточными и нечастыми, чтобы не перегружать серверы веб-сайтов. Получение контента может занять значительное время - следует ожидать от 20 до 50 минут.
Обучающий запуск обычно занимает от 5 до 10 минут на линоде с высокой памятью 32 ГБ или 60 ГБ.
Рекомендуемый вывод
Скриншоты вывода рекомендателя:
Измерение производительности и оптимизация
Какие оптимизации черного ящика возможны при использовании Spark и MLlib?
Алгоритмы обработки данных Spark представляют собой набор библиотек, которые предоставляют процедуры линейной алгебры, численных вычислений и оптимизации. Стек выглядит так:
Из стека напрашиваются две возможные оптимизации:
- Вместо java / scala-реализаций линейных алгебраических процедур, которые выполняются интерпретатором JVM, используйте собственную реализацию BLAS / LAPACK, такую как OpenBLAS / ATLAS / MKL, написанную на C / C ++ / Fortran и развернутую как собственные компоненты, которые выполнять непосредственно на ЦП без какого-либо промежуточного интерпретатора, такого как JVM.
- Часто эти встроенные реализации можно легко установить с помощью инструментов управления пакетами ОС. Но такие двоичные файлы используют только широко совместимые наборы инструкций и не используют самые последние оптимизированные наборы инструкций, доступные в современных процессорах x86–64. В моей предыдущей статье я продемонстрировал, как кастомная сборка TensorFlow в соответствии с этими последними инструкциями почти вдвое сокращает время вывода. Поскольку Linode использует Xeons с архитектурой Haswell, я попробую здесь точно такой же вид оптимизации, собрав на заказ OpenBLAS и ATLAS с наборами инструкций Haswell (такими как SSE4 и AVX), а затем сравню их производительность.
Распределенный LDA против онлайн-LDA
Распределенная реализация LDA Spark, в которой используется алгоритм максимизации ожидания, оказалась на удивление быстрее и намного меньше потребляла системные ресурсы, чем ее онлайн-реализация LDA, в которой используется вариационный алгоритм Байеса.
Выбор алгоритма осуществляется через параметр API Spark и не требует каких-либо других изменений в задании Spark.
Резкий контраст во времени и потребляемых ресурсах между двумя алгоритмами заставляет меня думать, что, возможно, онлайн-реализация LDA не так оптимизирована, как распределенная.
Я также ожидал улучшения времени выполнения за счет перехода на собственные стеки, такие как OpenBLAS, ATLAS или Intel MKL.
Но, как показывает гистограмма времени выполнения выше, ни одно из них не дало особых улучшений для этого приложения. Хотя одно из возможных объяснений заключается в том, что сам стек по умолчанию Spark сильно оптимизирован, я склоняюсь к тому, чтобы либо задание, либо реализация алгоритма были привязаны к памяти и диску, а не к процессору.
Онлайн-LDA особенно сильно влияет на диск и память. Я сталкивался с частыми проблемами нехватки ресурсов с более низкими конфигурациями Linode, пока не достиг экземпляра Linode 60 ГБ с высокой памятью 90 ГБ и 4 процессорами (и даже там у меня однажды случился сбой нехватки диска).
Основываясь на этих графиках ресурсов и результатах, я рекомендую по возможности использовать распределенную реализацию LDA. Рекомендательное приложение тоже использует его по умолчанию. Для распределенного LDA должно хватить всего, что имеет 12 ГБ ОЗУ и 2 или более ядер.
Выводы
Персонализированная частная система рекомендаций, использующая неконтролируемое машинное обучение, полезна во многих других сценариях - например, для рекомендации статей или обсуждений посетителям на крупных веб-сайтах.
Создание его с использованием Spark было забавным, хотя и немного необычным, потому что Spark редко используется для таких личных приложений. Тем не менее, это помогло мне получить хорошее представление о реализациях Spark LDA.
Конфигурации Linode - даже среднего уровня - отлично справляются с распределенным LDA. Online LDA - это совсем другая история - с этим могли справиться только самые высокие конфигурации. Пропускная способность сети Linode и большие квоты на загрузку данных обеспечивали быструю и частую загрузку контента, гарантируя, что не будет упущено много полезного контента.
Если вы новичок в машинном обучении и хотите узнать о методах машинного обучения, я предлагаю начать с этого превосходного и популярного онлайн-курса, который ведет профессор Эндрю Нг, один из исследователей, представивших LDA.
Кредиты
- Соавторы и коммиттеры проекта Apache Spark
- Проект НЛТК
- Автор (ы) Расширения для экспорта истории Chrome
- Openclipart.org
- Благодарим Дэйва Роша и Кейта Крейга за предоставленную инфраструктуру Linode и предложения, которые сделали эту статью возможной.
Обо мне: я консультант по программному обеспечению и архитектор, специализирующийся на больших данных, науке о данных и машинном обучении, с 14-летним опытом. Я руковожу Pathbreak Consulting, которая предоставляет консалтинговые услуги в этих областях для стартапов и других предприятий. Я веду блог здесь и на GitHub. Вы можете связаться со мной через мой сайт или LinkedIn.
Не стесняйтесь делиться ниже любыми комментариями или мнениями о вашем опыте использования Linode, машинного обучения и системы рекомендаций. Вы можете сообщать об ошибках или добавлять новые функции в репозиторий GitHub проекта. Если вы нашли этот блог полезным, поделитесь им в социальных сетях.