"Машинное обучение"

Классификация текста с несколькими метками с использованием Scikit-multilearn: пример из практики с вопросами StackOverflow

Разработка модели классификации текста с несколькими метками, которая помогает помечать вопросы stackoverflow.com по разным темам.

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

Очевидно, что с вопросом можно связать несколько тегов. Итак, в конечном итоге эта проблема сводится к «классификации вопроса и прикреплению к нему ярлыков класса». Согласно теории машинного обучения, это проблема «классификации по нескольким меткам».

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



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

В своей работе мы будем использовать scikit-multilearn, gensim и scikit-learn.

Получение данных и исследование

Данные для этой статьи можно найти на сайте Kaggle. Он содержит Questions.csv и Tags.csv, необходимые для нашего обсуждения.

Давайте изучим эти файлы

import pandas as pd
tag_df = pd.read_csv('../data/Tags.csv')
tag_df.head()

questions_df = pd.read_csv('../data/Questions.csv', encoding = "ISO-8859-1")
questions_df.head()

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

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

Всего существует 1316 различных типов тегов (анализ приведен в предыдущей статье).

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

tags_count_df = tag_df.groupby(['Tag']).count()
tags_count_df_asc = tags_count_df.sort_values(by=['Id'])
tags_count_df_asc.query('Id >= 3').head()

И обратные или самые частые теги

tags_count_df_desc = tags_count_df.sort_values(by=['Id'], ascending=False)
tags_count_df_desc.head()

Визуализация содержания "Body" и "Title"

Теперь давайте посмотрим на содержание "Body" и "Title" вопросов в виде облака слов. Для этого мы можем определить функцию

Изучение содержания тега в облаке слов

tags = ''
for index, row in input_df.iterrows():
    tags = tags + ',' + row['Tag']
    
plot_word_cloud(tags)

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

Предварительная обработка данных для дальнейшего изучения

Мы должны разделить данные на входной контент и целевую переменную. В нашем случае целевая переменная - «Тег».

df_x = input_df[['Title','Body']]
df_y = input_df[['Tag']]

Чтобы проанализировать содержимое «Body» и «Tag», нам нужно выполнить базовую предварительную обработку текста.

Шаги приведены ниже

  1. Преобразование в нижний регистр
  2. Удаление знаков препинания
  3. Удаление целых чисел, чисел
  4. Удаление лишних пробелов
  5. Удаление тегов (например, ‹html›, ‹p› и т. Д.)
  6. Удаление стоп-слов (например, "и", "к", "то" и т. Д.)
  7. Stemming (преобразование слов в корневую форму)

Мы будем использовать библиотеку Python «gensim» для очистки всего текста.

Давайте сначала напишем функции для этого

Мы увидим эффект применения этой функции к "Body" и "Title" на примере.

Содержание «Заголовка»

input_df.iloc[0,0]

После очистки

clean_text(input_df.iloc[0,0])

Содержание "Body"

input_df.iloc[0,1]

После очистки

clean_text(input_df.iloc[0,1])

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

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

titles = ''
for index, row in input_df.iterrows():
    titles = titles + ' ' + clean_text(row['Title'])
    
plot_word_cloud(titles)

Таким образом, "данные", "отличаются", "ценность" и т. Д. Являются наиболее частыми токенами в содержании "Заголовок".

Одинаковый график облака слов для всего очищенного содержимого "Body"

bodies = ''
for index, row in input_df.iterrows():
    bodies = bodies + ' ' + clean_text(row['Body'])
    
plot_word_cloud(bodies)

"Know", "instancepl", "case" и т. Д. - самые частые токены в "Body".

Мы также можем видеть содержимое «Body», соответствующее определенному тегу. Для этого определим функцию

Облако слов для вопросов с меткой «matlab»

plot_word_cloud_of_body_for_tag('matlab')

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

Аналогично для тега «вероятность»

plot_word_cloud_of_body_for_tag('probability')

Построение модели и конвейера машинного обучения

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

«MultiLabelBinarizer» из «scikit-learn» может это сделать.

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

Мы напишем собственный «Doc2VecTransformer», используя библиотеку python «gensim».

Этот класс принимает имя поля в качестве входных данных (в нашем случае «Body» и «Title» соответственно) и преобразует текст в массив числовых функций.

Мы разделим данные на «поезд» и «тест».

from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(df_x, encoded_y)

Нам нужно объединить два поля «Body» и «Title» в одну функцию после преобразования «Doc2Vec» с помощью FeatureUnion из scikit-learn.

Исходя из концепции моделей «с несколькими метками», мы попробуем использовать «Двоичная релевантность» и «цепочка классификаторов» в качестве общей схемы классификации (см. Предыдущий статья, указанная вверху) и будет использовать "RandomForest" в качестве нашего базового двоичного классификатора.

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

Мы сравним точность для обеих зонтичных схем.

Двоичная релевантность

Наш конвейер должен состоять из шагов FeatureUnion и BinaryRelevance поверх классификатора RandomForest. Мы будем использовать класс BinaryRelevance из библиотеки scikit-multilearn.

Для проверки точности нашей модели давайте определим функцию для расчета «потери Хэмминга» (см. Предыдущую статью)

Давайте обучим и протестируем модель «двоичной релевантности».

multi_label_rf_br_model.fit(train_x, train_y)
print('Hamming loss for test data :', hamming_loss(multi_label_rf_br_model,train_x,train_y,test_x,test_y))

Это означает потерю почти 0,02% или, можно сказать, точность 99,98% !!

Цепочка классификатора

Таким же образом мы можем обучить и протестировать «цепочку классификаторов». «scikit-multilearn» предоставляет для этого один класс.

Почти такая же точность !!

Дальнейший анализ точности отдельного классификатора

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

Теперь мы увидим «кривую ROC» для трех тегов: «нормальное распределение», «визуализация данных» и «оценка».

plot_roc_curve(x=test_x, y=test_y, classes=['normal-distribution','data-visualization','estimation'], 
               title='ROC curve')

Аналогичные кривые ROC могут быть построены этой функцией и для других тегов. Читатели могут попробовать это самостоятельно.

Заключение

Это все о решении проблемы. Дальнейшее улучшение может быть выполнено с помощью другого двоичного классификатора или настройки его с помощью гиперпараметров. Читатели этой статьи могут попробовать это самостоятельно. Источник этой статьи можно найти здесь, в Github.



Недавно я написал книгу по ML (https://twitter.com/bpbonline/status/1256146448346988546)