Русский
Русский
English
Статистика
Реклама

Решаем NLP-задачу классификация текстов по темам

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

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

Для решения нашей задачи снова используем язык программирования python и среду разработки Jupyternotebook на платформе GoogleColab.

В работе понадобятся следующие библиотеки:

  • scikit-learn свободно распространяемая библиотека на python, содержащая реализации различных методов машинного обучения;

  • nltk пакетбиблиотекдля символьной и статистическойобработки естественного языка;

  • matplotlib библиотека, содержащая набор инструментов для визуализации данных, понадобится для отображения облака слов.

Подробности реализации

Датасет сохранен в файле формата csv и содержит чуть более 8 тысяч записей. Для работы с ним будем использовать библиотеку pandas загружаем данные в память при помощи метода read_csv и отображаем на экране несколько первых строк:

import pandas as pddf_habr = pd.read_csv(habrParse.csv)df_habr.head()

Набор данных представляет собой таблицу, в первой колонке которой хранится текст статьи, во второй присвоенная категория (класс):

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

# Получение текстовой строки из списка словdef str_corpus(corpus):    str_corpus = ''    for i in corpus:        str_corpus += ' ' + i    str_corpus = str_corpus.strip()    return str_corpus# Получение списка всех слов в корпусеdef get_corpus(data):    corpus = []    for phrase in data:        for word in phrase.split():            corpus.append(word)    return corpus# Получение облака словdef get_wordCloud(corpus):    wordCloud = WordCloud(background_color='white',                              stopwords=STOPWORDS,                              width=3000,                              height=2500,                              max_words=200,                              random_state=42                         ).generate(str_corpus(corpus))    return wordCloudcorpus = get_corpus(df_train['text'].values)procWordCloud = get_wordCloud(corpus)fig = plt.figure(figsize=(20, 8))plt.subplot(1, 2, 1)plt.imshow(procWordCloud)plt.axis('off')plt.subplot(1, 2, 1)

Для необработанного набора данных облако слов содержит 243024 уникальных слова и выглядит так:

Попробуем очистить данные:

import nltknltk.download("stopwords")from nltk.corpus import stopwordsfrom string import punctuationrussian_stopwords = stopwords.words("russian")# Удаление знаков пунктуации из текстаdef remove_punct(text):    table = {33: ' ', 34: ' ', 35: ' ', 36: ' ', 37: ' ', 38: ' ', 39: ' ', 40: ' ', 41: ' ', 42: ' ', 43: ' ', 44: ' ', 45: ' ', 46: ' ', 47: ' ', 58: ' ', 59: ' ', 60: ' ', 61: ' ', 62: ' ', 63: ' ', 64: ' ', 91: ' ', 92: ' ', 93: ' ', 94: ' ', 95: ' ', 96: ' ', 123: ' ', 124: ' ', 125: ' ', 126: ' '}    return text.translate(table)habrParse_df['Post_clean'] = habrParse_df['Post'].map(lambda x: x.lower())habrParse_df['Post_clean'] = habrParse_df['Post_clean'].map(lambda x: remove_punct(x))habrParse_df['Post_clean'] = habrParse_df['Post_clean'].map(lambda x: x.split(' '))habrParse_df['Post_clean'] = habrParse_df['Post_clean'].map(lambda x: [token for token in x if token not in russian_stopwords\                                                                  and token != " " \                                                                  and token.strip() not in punctuation])habrParse_df['Post_clean'] = habrParse_df['Post_clean'].map(lambda x: ' '.join(x))

После небольшой очистки текстов от стоп-слов и знаков пунктуации количество уникальных слов снизилось до 142253, а облако слов стало более осмысленным:

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

Посмотрим статистику по классам:

Видно, что некоторые классы представлены только одним элементом, а класс Чулан составляет более 65% датасета. Для того чтобы работать с более или менее сбалансированным датасетом, выберем тексты только четырех классов:

df_habr_clean = df_habr.loc[df_habr['hubs'].isin(['IT-компании', 'Habr', 'Управление медиа', 'Я пиарюсь'])]

Разделим датасет на тренировочную, тестовую и валидационную части:

from sklearn.model_selection import train_test_splitX_train, X_valid, y_train, y_valid = train_test_split(df_habr_clean ['Post_clean'], df_habr_clean ['hubs'], test_size=0.1, random_state=42)X_train, X_test, y_train, y_test = train_test_split(X_train, y_train, test_size=0.2, random_state=42)

Получили следующее соотношение выборок:X_train 1136 элементов,X_test 283 элемента,X_valid 158 элементов

Для дальнейшей работы понадобится импортировать несколько модулей из библиотеки scikit-learn:

from sklearn.pipeline import Pipeline# pipeline позволяет объединить в один блок трансформер и модель, что упрощает написание кода и улучшает его читаемостьfrom sklearn.feature_extraction.text import TfidfVectorizer# TfidfVectorizer преобразует тексты в числовые вектора, отражающие важность использования каждого слова из некоторого набора слов (количество слов набора определяет размерность вектора) в каждом текстеfrom sklearn.linear_model import SGDClassifierfrom sklearn.neighbors import KNeighborsClassifier# линейный классификатор и классификатор методом ближайших соседейfrom sklearn import metrics# набор метрик для оценки качества моделиfrom sklearn.model_selection import GridSearchCV# модуль поиска по сетке параметров

Сначала создадим 2 классификатора (чтобы можно было в дальнейшем сравнить качество получившихся моделей) и обучим их на тестовых данных:

sgd_ppl_clf = Pipeline([    ('tfidf', TfidfVectorizer()),    ('sgd_clf', SGDClassifier(random_state=42))])knb_ppl_clf = Pipeline([    ('tfidf', TfidfVectorizer()),    ('knb_clf', KNeighborsClassifier(n_neighbors=10))])sgd_ppl_clf.fit(X_train, y_train)knb_ppl_clf.fit(X_train, y_train)

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

predicted_sgd = sgd_ppl_clf.predict(X_test)print(metrics.classification_report(predicted_sgd, y_test))
predicted_sgd = knb_ppl_clf.predict(X_test)print(metrics.classification_report(predicted_sgd, y_test))

В связи с тем, что датасет не сбалансирован, метрику accuracy (доля верных ответов) использовать нельзя, так как это приведет к завышенной оценке качества работы классификатора. В данном случае самое правильное считать сразу несколько метрик, устойчивых к распределению классов (в данном случае, это точность, полнота и f-мера) и смотреть на них все. Однако часто бывает удобно получить не большой набор цифр, а одно число, по которому можно понять, насколько хорошо модель работает. В нашей задаче лучше всего подходит macro-avg (сначала подсчитывается каждая метрика по каждому классу, а потом усредняется). Macro-avg более устойчива к скошенным распределениям классов.

Линейный классификатор показал лучший результат на тестовых данных (0,77 против 0,72 у классификатора методом ближайших соседей), поэтому для дальнейшей настройки модели будем использовать его.

На текущий момент классификатор обучен с параметрами по умолчанию. Для большинства задач это вполне приемлемо. Но для более качественной работы модели лучше провести ее дополнительную настройку. В этом случае удобно использовать поиск лучшего сочетания по сетке параметров. Так, можно задать набор значений для параметров классификатора и передать его в модуль GridSearchCV. Он выполнит обучение и оценку модели на каждом сочетании параметров и выдаст лучший результат.

Попробуем улучшить нашу модель, используя различные параметры. Следует иметь в виду, что для доступа к параметрам объекта pipeline необходимо указывать их в виденазвание объекта__название параметра:

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

sgd_ppl_clf = Pipeline([    ('tfidf', TfidfVectorizer(ngram_range=(1, 2))),    ('sgd_clf', SGDClassifier(penalty='elasticnet', class_weight='balanced', random_state=42))])sgd_ppl_clf.fit(X_train, y_train)predicted_sgd = sgd_ppl_clf.predict(X_test)print(metrics.classification_report(predicted_sgd, y_test))

Значение целевой метрики выросло.

predicted_sgd_val = sgd_ppl_clf.predict(X_valid)print(metrics.classification_report(predicted_sgd_val, y_valid))

На валидационной выборке качество также выросло (0,8 против 0,76 с использованием стандартных параметров классификатора), следовательно, мы успешно справились с поставленной задачей.

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

Надеюсь, статья была полезной и поможет вам начать самостоятельно решать nlp-задачи.

Источник: habr.com
К списку статей
Опубликовано: 22.01.2021 12:16:53
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Python

Программирование

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

Nlp

Jupyter

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru