Как мы организуем самые неорганизуемые данные в мире?

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

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

Во многом за этим стоят Трансформеры. Эти трансформеры (к сожалению) не автоботы и десептиконы Майкла Бэя и (к счастью) не гудящие электрические коробки. Наши трансформеры НЛП находятся где-то посередине, они не являются разумными автоботами (пока), но они могут понимать язык так, как это было возможно только в научной фантастике еще несколько лет назад.

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

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

Мы углубимся в детали BERTopic [1], но прежде давайте посмотрим, как мы можем его использовать, и взглянем на его компоненты.

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

Набор данных содержит данные, извлеченные с помощью Reddit API из сабреддита /r/python. Код, используемый для этого (и всех других примеров), можно найти здесь.

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

Выполняем тематическое моделирование с помощью библиотеки BERTopic. Для базового подхода требуется всего несколько строк кода.

Из model.fit_transform возвращаем два списка:

  • topics содержит однозначное сопоставление входных данных с их смоделированной темой (или кластером).
  • probs содержит список вероятностей того, что ввод принадлежит назначенной теме.

Затем мы можем просматривать темы, используя get_topic_info.

Верхняя -1 тема обычно считается нерелевантной и обычно содержит стоп-слова, такие как "the", "a" и "and". Однако мы удалили стоп-слова с помощью аргумента vectorizer_model, поэтому он показывает нам "самые общие" темы, такие как "Python", "код" и "данные".

В библиотеке есть несколько встроенных методов визуализации, таких как visualize_topics, visualize_hierarchy и visualize_barchart.

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

Обзор

В BERTopic [2] используются четыре ключевых компонента:

  • Модель встраивания трансформатора
  • Снижение размерности UMAP
  • Кластеризация HDBSCAN
  • Тегирование кластера с использованием c-TF-IDF

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

Встраивание трансформатора

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

  • Преобразователи предложений
  • Чутье
  • SpaCy
  • Генсим
  • ИСПОЛЬЗОВАНИЕ (от TF Hub)

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

Первым результатом этого поиска является sentence-transformers/all-MiniLM-L6-v2. Это популярная высокопроизводительная модель, которая создает 384-мерные вложения предложений.

Чтобы инициализировать модель и закодировать данные наших тем Reddit, мы сначала pip install sentence-transformers, а затем пишем:

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

Уменьшение размерности

После построения наших вложений BERTopic сжимает их в пространство более низкой размерности. Это означает, что наши 384-мерные векторы преобразуются в двух/трехмерные векторы.

Мы можем это сделать, потому что 384 измерения — это много, и маловероятно, что нам действительно нужно столько измерений для представления нашего текста [4]. Вместо этого мы пытаемся сжать эту информацию в два или три измерения.

Мы делаем это, чтобы следующий шаг кластеризации HDBSCAN можно было выполнить более эффективно. Выполнение шага кластеризации с 384 размерностями было бы отчаянно медленным [5].

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

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

К этим данным можно применить множество методов уменьшения размерности; два самых популярных варианта — PCA и t-SNE.

PCA работает, сохраняя большие расстояния (с использованием среднеквадратичной ошибки). В результате глобальная структура данных обычно сохраняется [6]. Мы можем видеть это поведение выше, поскольку каждый континент сгруппирован с соседними континентами. Когда у нас есть легко различимые кластеры в наборах данных, это может быть хорошо, но плохо работает для более тонких данных, где важны локальные структуры.

t-SNE наоборот; он сохраняет локальные структуры, а не глобальные. Этот локализованный фокус возникает из-за того, что t-SNE строит график, соединяющий все ближайшие точки. Эти локальные структуры могут косвенно указывать на глобальную структуру, но они не фиксируются строго.

PCA фокусируется на сохранении несходства, тогда как t-SNE фокусируется на сохранении сходства.

К счастью, мы можем извлечь лучшее из обоих, используя менее известную технику, называемую Uniform Munifold Aapproximation и Pпроизводство (UMAP).

Мы можем применить UMAP в Python, используя библиотеку UMAP, установленную с помощью pip install umap-learn. Чтобы сопоставить трехмерное или двухмерное пространство с использованием параметров UMAP по умолчанию, все, что мы пишем, это:

Алгоритм UMAP можно настроить с помощью нескольких параметров. Тем не менее, самая простая и эффективная настройка может быть достигнута только с помощью параметра n_neighbors.

Для каждой точки данных UMAP просматривает другие точки и идентифицирует k ближайших соседей [3]. Это k, управляемый параметром n_neighbors.

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

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

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

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

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

UMAP сохраняет отличительные черты, которые не сохраняются PCA, и лучшую глобальную структуру, чем t-SNE. Это отличный общий пример того, в чем заключается преимущество UMAP.

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

Имея все это в виду, давайте применим UMAP к нашим данным тем Reddit. Использование n_neighbors из 3-5 работает лучше всего. Мы можем добавить min_dist=0.05, чтобы позволить UMAP размещать точки ближе друг к другу (значение по умолчанию — 1.0); это помогает нам отделить три похожие темы от r/Python, r/LanguageTechnology и r/pytorch.

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

Кластеризация HDBSCAN

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

Теперь давайте посмотрим, как HDBSCAN используется для кластеризации (теперь) низкоразмерных векторов.

Методы кластеризации можно разделить на плоские или иерархические и центроидные или основанные на плотности методы [5]. Каждый из которых имеет свои преимущества и недостатки.

Плоский или иерархический фокус просто на том, есть ли (или нет) иерархия в методе кластеризации. Например, мы можем (в идеале) рассматривать нашу иерархию графов как движение от континентов к странам и городам. Эти методы позволяют нам просматривать заданную иерархию и пытаться определить логический «разрез» по дереву.

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

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

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

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

HDBSCAN выбирает окончательные кластеры на основе их размера и постоянства при различных значениях лямбда. Самые толстые и устойчивые «ветви» дерева считаются наиболее стабильными и, следовательно, лучшими кандидатами на создание кластеров.

Эти кластеры не очень полезны, поскольку по умолчанию минимальное количество точек, необходимое для «создания» кластера, составляет всего 5. Учитывая наш набор данных ~ 3 тыс. точек, в котором мы стремимся создать ~ 4 кластера субреддитов, это мало. К счастью, мы можем увеличить этот порог, используя параметр min_cluster_size.

Лучше, но не совсем там. Мы можем попытаться уменьшить min_cluster_size до 60, чтобы получить три кластера под зеленым блоком.

К сожалению, это по-прежнему тянет зеленый блок и даже позволяет создавать слишком маленькие кластеры (как слева). Другой вариант — оставить min_cluster_size=80, но добавить min_samples=40, чтобы можно было использовать более разреженные основные точки.

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

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

Извлечение темы с помощью c-TF-IDF

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

TF-IDF — популярный метод определения наиболее релевантных «документов» с учетом термина или набора терминов. c-TF-IDF переворачивает это с ног на голову, находя наиболее релевантные термины для всех «документов» в кластере.

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

Часть c-TF-IDF требует вычисления частоты термина t в классе c. Для этого нам нужно увидеть, какие токены принадлежат каждому классу. Сначала мы добавляем метки кластера/класса в data.

Теперь создайте списки токенов для конкретных классов.

Мы можем рассчитать Tterm Frequency (TF) для каждого класса.

Обратите внимание, что это может занять некоторое время; наш процесс TF отдает предпочтение удобочитаемости над любым понятием эффективности. После завершения мы готовы вычислить Inverse Drequement Frequiment (IDF), который говорит нам, насколько распространен термин. Редкие термины означают большую релевантность, чем общие термины (и дают более высокий балл IDF).

В tf_idf у нас есть список результатов TF-IDF размером vocab для каждого класса. Мы можем использовать функцию Numpy argpartition для получения позиций индекса, содержащих самые высокие оценки TF-IDF для каждого класса.

Теперь мы сопоставляем эти позиции индекса с исходными словами в vocab.

Назад к BERTopic

Мы охватили значительный объем, но можем ли мы применить то, что узнали, к библиотеке BERTopic?

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

Вы могли заметить, что мы добавили prediction_data=True в качестве нового параметра для HDBSCAN. Нам это нужно, чтобы избежать AttributeError при интеграции нашего пользовательского шага HDBSCAN с BERTopic. Добавление gen_min_span_tree добавляет еще один шаг к HDBSCAN, который может улучшить результирующие кластеры.

Мы также должны инициализировать vectorizer_model для удаления стоп-слова на этапе c-TF-IDF. Мы будем использовать список стоп-слов из NLTK, но добавим еще несколько токенов, которые, похоже, загрязняют результаты.

Теперь мы готовы передать все это в экземпляр BERTopic и обработать данные наших тем Reddit.

Мы можем визуализировать новые темы с помощью model.visualize_barchart()

Мы видим, что темы идеально совпадают с r/investing, r/pytorch, r/LanguageTechnology и r/Python.

Преобразователи, UMAP, HDBSCAN и c-TF-IDF — явно мощные компоненты, которые имеют огромное применение при работе с неструктурированными текстовыми данными. BERTopic абстрагировался от большей части сложности этого стека, позволяя нам применять эти технологии всего несколькими строками кода.

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

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

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

Ресурсы

🔗 Все скрипты блокнота

[1] М. Гротендорст, BERTopic Repo, GitHub

[2] М. Гротендорст, BERTopic: нейронное тематическое моделирование с помощью процедуры TF-IDF на основе классов (2022)

[3] Л. Макиннес, Дж. Хили, Дж. Мелвилл, UMAP: унифицированная аппроксимация многообразия и проекция для уменьшения размерности (2018)

[4] Л. Макиннес, Разговор о UMAP для уменьшения размерности (2018), SciPy 2018.

[5] Дж. Хили, HDBSCAN, кластеризация на основе быстрой плотности, как и почему (2018), PyData NYC 2018

[6] Л. Макиннес, Руководство по уменьшению размерности для блефа (2018), PyData NYC 2018.

Объяснение UMAP, кофе-брейк ИИ с Летицией, YouTube

*Все изображения принадлежат автору, если не указано иное