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

В этой статье мы углубимся в классификацию текста, в частности, в классификацию логистической регрессии, используя некоторые реальные данные (текстовые обзоры умного домашнего динамика Alexa от Amazon). В этой серии у нас есть следующие статьи:

Текстовая аналитика для начинающих с использованием Python spaCy, часть 1

Текстовая аналитика для начинающих с использованием Python spaCy, часть 2

Классификация текста с помощью Python spaCy

Текстовая классификация

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

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

Импорт библиотек

Мы начнем с импорта библиотек, которые нам понадобятся для этой задачи. Мы уже импортировали spaCy, но нам также нужны pandas и scikit-learn, которые помогут в нашем анализе.

import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer,TfidfVectorizer
from sklearn.base import TransformerMixin
from sklearn.pipeline import Pipeline

Загрузка данных

Выше мы рассмотрели несколько простых примеров анализа текста с помощью spaCy, но теперь мы будем работать над классификацией логистической регрессии с использованием scikit-learn. Чтобы сделать это более реалистичным, мы собираемся использовать набор реальных данных — этот набор обзоров продуктов Amazon Alexa.

Этот набор данных поставляется в виде файла с разделителями табуляцией (.tsv). Он имеет пять столбцов: rating, date, variation, verified_reviews, feedback.

rating обозначает рейтинг, который каждый пользователь дал Alexa (из 5). date указывает дату обзора, а variation описывает, какую модель просмотрел пользователь. verified_reviews содержит текст каждого отзыва, а feedback содержит метку тональности, где 1 обозначает положительное настроение (пользователю понравилось), а 0 обозначает отрицательное настроение (пользователю не понравилось).

Этот набор данных содержит отзывы потребителей о продуктах Amazon Alexa, таких как Echos, Echo Dots, Alexa Firesticks и т. д. Мы собираемся разработать модель классификации, которая просматривает текст отзыва и предсказывает, будет ли отзыв положительным или отрицательным. Поскольку этот набор данных уже включает положительный или отрицательный отзыв в столбце feedback, мы можем использовать эти ответы для обучения и тестирования нашей модели. Наша цель — создать точную модель, которую затем можно использовать для обработки новых отзывов пользователей и быстрого определения того, были ли они положительными или отрицательными.

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

# Loading TSV file
df_amazon = pd.read_csv ("datasets/amazon_alexa.tsv", sep="\t")
# Top 5 records
df_amazon.head()

# shape of dataframe
df_amazon.shape
(3150, 5)
# View data information
df_amazon.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3150 entries, 0 to 3149
Data columns (total 5 columns):
rating              3150 non-null int64
date                3150 non-null object
variation           3150 non-null object
verified_reviews    3150 non-null object
feedback            3150 non-null int64
dtypes: int64(2), object(3)
memory usage: 123.1+ KB
# Feedback Value count
df_amazon.feedback.value_counts()
1    2893
0     257
Name: feedback, dtype: int64

Разметка данных с помощью spaCy

Теперь, когда мы знаем, с чем работаем, давайте создадим пользовательскую функцию токенизатора, используя spaCy. Мы будем использовать эту функцию для автоматического удаления ненужной информации, такой как стоп-слова и знаки препинания, из каждого отзыва.

Мы начнем с импорта необходимых нам английских моделей из spaCy, а также модуля Python string, который содержит полезный список всех знаков препинания, которые мы можем использовать в string.punctuation. Мы создадим переменные, содержащие знаки препинания и стоп-слова, которые мы хотим удалить, и синтаксический анализатор, который пропускает ввод через английский модуль spaCy.

Затем мы создадим spacy_tokenizer() функцию, которая принимает предложение в качестве входных данных и обрабатывает предложение в токены, выполняя лемматизацию, перевод в нижний регистр и удаление стоп-слов. Это похоже на то, что мы делали в примерах ранее в этом руководстве, но теперь мы объединяем все это в единую функцию для предварительной обработки каждого отзыва пользователя, который мы анализируем.

import string
from spacy.lang.en.stop_words import STOP_WORDS
from spacy.lang.en import English
# Create our list of punctuation marks
punctuations = string.punctuation
# Create our list of stopwords
nlp = spacy.load('en')
stop_words = spacy.lang.en.stop_words.STOP_WORDS
# Load English tokenizer, tagger, parser, NER and word vectors
parser = English()
# Creating our tokenizer function
def spacy_tokenizer(sentence):
    # Creating our token object, which is used to create documents with linguistic annotations.
    mytokens = parser(sentence)
# Lemmatizing each token and converting each token into lowercase
    mytokens = [ word.lemma_.lower().strip() if word.lemma_ != "-PRON-" else word.lower_ for word in mytokens ]
# Removing stop words
    mytokens = [ word for word in mytokens if word not in stop_words and word not in punctuations ]
# return preprocessed list of tokens
    return mytokens

Определение пользовательского преобразователя

Чтобы дополнительно очистить наши текстовые данные, мы также хотим создать собственный преобразователь для удаления начальных и конечных пробелов и преобразования текста в нижний регистр. Здесь мы создадим собственный класс predictors, который наследует класс TransformerMixin. Этот класс переопределяет методы преобразования, подгонки и get_parrams. Мы также создадим функцию clean_text(), которая удаляет пробелы и преобразует текст в нижний регистр.

# Custom transformer using spaCy
class predictors(TransformerMixin):
    def transform(self, X, **transform_params):
        # Cleaning Text
        return [clean_text(text) for text in X]
def fit(self, X, y=None, **fit_params):
        return self
def get_params(self, deep=True):
        return {}
# Basic function to clean the text
def clean_text(text):
    # Removing spaces and converting text into lowercase
    return text.strip().lower()

Разработка признаков векторизации (TF-IDF)

Когда мы классифицируем текст, мы получаем текстовые фрагменты, которым соответствуют соответствующие метки. Но мы не можем просто использовать текстовые строки в нашей модели машинного обучения; нам нужен способ преобразовать наш текст во что-то, что можно представить в числовом виде, точно так же, как метки (1 для положительных и 0 для отрицательных). Классификация текста по положительным и отрицательным меткам называется анализом настроений. Итак, нам нужен способ представить наш текст в числовом виде.

Один из инструментов, который мы можем использовать для этого, называется Bag of Words. BoW преобразует текст в матрицу встречаемости слов в заданном документе. Он фокусируется на том, встречались ли данные слова в документе или нет, и генерирует матрицу, которую мы могли бы видеть называемой матрицей BoW или матрицей терминов документа.

Мы можем сгенерировать матрицу BoW для наших текстовых данных, используя CountVectorizer scikit-learn. В приведенном ниже коде мы указываем CountVectorizer использовать созданную нами пользовательскую функцию spacy_tokenizer в качестве своего токенизатора и определяем нужный нам диапазон ngram.

N-граммы — это комбинации соседних слов в заданном тексте, где n — количество слов, входящих в токены. например, в предложении «Кто выиграет чемпионат мира по футболу в 2022 году?» юниграммы – это последовательность отдельных слов, таких как "кто", "будет", "выиграет" и т. д. Биграммы – это последовательность из двух смежных слов, таких как "кто будет", "выиграет" и т. д. Таким образом, параметр ngram_range, который мы будем использовать в приведенном ниже коде, устанавливает нижнюю и верхнюю границы наших энграмм (мы будем использовать униграммы). Затем мы назначим нграммы bow_vector.

bow_vector = CountVectorizer(tokenizer = spacy_tokenizer, ngram_range=(1,1))

Мы также хотим посмотреть на TF-IDF (частота термина, обратная частоте документа) для наших терминов. Это звучит сложно, но это просто способ нормализовать наш Мешок слов (BoW) путем сравнения частоты каждого слова с частотой документа. Другими словами, это способ представить, насколько важен конкретный термин в контексте данного документа, основываясь на том, сколько раз этот термин встречается и в скольких других документах встречается этот же термин. Чем выше TF-IDF, тем важнее этот термин для этого документа.

Мы можем представить это с помощью следующего математического уравнения:

Конечно, нам не нужно вычислять это вручную! Мы можем сгенерировать TF-IDF автоматически, используя TfidfVectorizer scikit-learn. Опять же, мы скажем ему использовать пользовательский токенизатор, который мы создали с помощью spaCy, а затем присвоим результат переменной tfidf_vector.

tfidf_vector = TfidfVectorizer(tokenizer = spacy_tokenizer)

Разделение данных на обучающие и тестовые наборы

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

Удобно, что scikit-learn дает нам для этого встроенную функцию: train_test_split(). Нам просто нужно указать набор функций, который мы хотим разделить (X), метки, которые мы хотим протестировать (ylabels), и размер, который мы хотим использовать для тестового набора (представленный в процентах в десятичной форме). .

from sklearn.model_selection import train_test_split
X = df_amazon['verified_reviews'] # the features we want to analyze
ylabels = df_amazon['feedback'] # the labels, or answers, we want to test against
X_train, X_test, y_train, y_test = train_test_split(X, ylabels, test_size=0.3)

Создание конвейера и генерация модели

Теперь, когда мы все настроили, пришло время построить нашу модель! Мы начнем с импорта модуля LogisticRegression и создания объекта классификатора LogisticRegression.

Затем мы создадим конвейер с тремя компонентами: очистителем, векторизатором и классификатором. Очиститель использует наш объект класса predictors для очистки и предварительной обработки текста. Векторизатор использует объекты countvector для создания матрицы набора слов для нашего текста. Классификатор — это объект, который выполняет логистическую регрессию для классификации настроений.

Как только этот пайплайн будет построен, мы подгоним компоненты пайплайна, используя fit().

# Logistic Regression Classifier
from sklearn.linear_model import LogisticRegression
classifier = LogisticRegression()
# Create pipeline using Bag of Words
pipe = Pipeline([("cleaner", predictors()),
                 ('vectorizer', bow_vector),
                 ('classifier', classifier)])
# model generation
pipe.fit(X_train,y_train)
Pipeline(memory=None,
     steps=[('cleaner', <__main__.predictors object at 0x00000254DA6F8940>), ('vectorizer', CountVectorizer(analyzer='word', binary=False, decode_error='strict',
        dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
        lowercase=True, max_df=1.0, max_features=None, min_df=1,
      ...ty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False))])

Оценка модели

Давайте посмотрим, как на самом деле работает наша модель! Мы можем сделать это, используя модуль metrics из scikit-learn. Теперь, когда мы обучили нашу модель, мы пропустим наши тестовые данные через конвейер, чтобы получить прогнозы. Затем мы будем использовать различные функции модуля metrics, чтобы посмотреть на точность, воспроизводимость и полноту нашей модели.

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

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

from sklearn import metrics
# Predicting with a test dataset
predicted = pipe.predict(X_test)
# Model Accuracy
print("Logistic Regression Accuracy:",metrics.accuracy_score(y_test, predicted))
print("Logistic Regression Precision:",metrics.precision_score(y_test, predicted))
print("Logistic Regression Recall:",metrics.recall_score(y_test, predicted))
Logistic Regression Accuracy: 0.9417989417989417
Logistic Regression Precision: 0.9528508771929824
Logistic Regression Recall: 0.9863791146424518

Другими словами, в целом наша модель правильно определяла тональность комментария в 94,1% случаев. Когда он предсказал, что отзыв будет положительным, этот отзыв на самом деле был положительным в 95% случаев. Получив положительный отзыв, наша модель идентифицировала его как положительный в 98,6% случаев.

Ресурсы и следующие шаги

Поздравляем, вы добрались до конца этого урока!

В этой статье мы создали собственную модель машинного обучения с помощью scikit-learn. Конечно, это только начало, и spaCy и scikit-learn могут предложить специалистам по данным Python гораздо больше.

Эта статья первоначально опубликована на https://www.dataquest.io/blog/tutorial-text-classification-in-python-using-spacy/

Хотите изучить науку о данных, загляните на DataCamp.

Больше таких статей вы можете найти в моем блоге Machine Learning Geek.

Свяжитесь со мной в Linkedin: https://www.linkedin.com/in/avinash-navlani/