Данная статья основана на пакете quick_sentiments для Python, который я разработал для упрощения анализа тональности. С исходным кодом можно ознакомиться здесь.

Хотели бы запускать полный сквозной анализ тональности — от исходного текста до готового прогноза — всего в три простых шага? Именно для решения этой задачи я создал свой новый Python-пакет quick_sentiments. Он берёт на себя все сложные и трудоемкие задачи по очистке текста, векторизации и машинному обучению «под капотом», позволяя вам сосредоточиться на результатах, а не на шаблонном коде.

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

Установить пакет в Python можно с помощью следующей команды:

!pip install quick-sentiments
#или
git clone https://github.com/AlabhyaMe/quick_sentiments.gitp

ШАГ 1: Предварительная обработка текста

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

Как видно из примера, использование функции pre_process невероятно просто.

import polars as pl

#ИМПОРТИРУЕМ pre_process из quick_sentiments
from quick_sentiments import pre_process

df_train = pl.read_csv("yourpath.csv",encoding='ISO-8859-1')
response_column = "reviewText"
sentiment_column = "sentiment"

#ИСПОЛЬЗУЕМ pre_process
df_train = df_train.with_columns(
pl.col(response_column).map_elements(lambda x: pre_process(x, remove_brackets=True)).alias("processed") #добавляем внутри map_elements
)

Подумайте, что обычно требуется для очистки текста с нуля. Вам придется импортировать:

  •  re для регулярных выражений, чтобы обрабатывать URL-адреса;
  • регулярные выражения для HTML-тегов;
  • nltk для загрузки и инициализации списка стоп-слов;
  •  nltk для лемматизатора. 

Это долгая и утомительная настройка, которую придется выполнять для каждого отдельного проекта.

quick_sentiments сжимает весь этот процесс в одну высоко настраиваемую функцию. Посмотрите, какие возможности управления появляются сразу после установки:


def pre_process(doc,
remove_brackets=True,
remove_urls=True,
remove_html=True,
lemmatize=True,
remove_stop_words=True,
remove_nums=False,
... # и много других параметров
return_string=True):

#КОД ДЛЯ ВСЕХ ЭТИХ ФУНКЦИЙ УЖЕ НАПИСАН ДЛЯ ВАС ВНУТРИ ПАКЕТА.

Исходный код также доступен по ссылке.

Эта единственная функция представляет собой полноценный пайплайн очистки текста. В примере с polars использованы настройки по умолчанию, но специально активирован параметр remove_brackets=True. Если же вам потребуется сохранить числа в тексте, передайте параметр remove_nums=False. Чтобы пропустить этап лемматизации для ускорения работы, установите lemmatize=False.

pre_process выполняет весь перечень задач  — от нормализации юникода и удаления эмодзи до токенизации и лемматизации — одним чистым и простым вызовом.

Очищенная токенизированная строка теперь представляет собой идеальную основу для следующего шага: векторизации.

ШАГ 2: Разделение данных, векторизация, модели машинного обучения, точность

Переходим к самому мощному функционалу пакета quick_sentiments. После предварительной обработки остается наиболее трудоемкая часть работы, связанная с машинным обучением: разделением данных, векторизацией, обучением модели и оценкой. Именно на этом этапе большинство проектов замедляется, и именно здесь на помощь приходит run_pipeline().

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

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

Хотите протестировать классический TF-IDF с логистической регрессией? Или сразу перейти к 100-мерным вложениям GloVe с нейронной сетью? Или пойти еще дальше с 300-мерным Word2Vec? Просто выберите то, что вам нужно.

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

Методы векторизации (vectorizer_name):

  • "bow": Bag-of-Words;
  • "tf": частота терминов;
  • "tfidf": TF-IDF;
  • "wv": Word2Vec (обученная на ваших данных; будет загружена, если у вас ее еще нет);
  • "glove_25""glove_50""glove_100""glove_200": предварительно обученные вложения GloVe в различных измерениях.

Модели машинного обучения (model_name):

  • "logit": логистическая регрессия;
  • "rf": случайный лес;
  • "xgb": XGBoost;
  • "nn": простая нейронная сеть.

Вся эта мощь заключена в одном простом вызове функции:

dt= run_pipeline(
vectorizer_name="glove_25", # BOW, tf, tfidf, wv, glove_25,glove_50, glove_100, gl0ve_200
model_name="logit", # logit, rf, XGB, nn
df=df_train,
text_column_name="processed", # это название колонки с текстовыми данными,
sentiment_column_name = "sentiment",
perform_tuning = False # установите True, если хотите выполнить настройку гиперпараметров; это займет больше времени и
# может привести к нехватке памяти при работе с большими наборами данных,
)

Код доступен по ссылке.

#ВОТ ЧТО возвращает run_pipeline. Это важно для прогнозов 
return {
"model_object": trained_model_object,
"vectorizer_name": vectorizer_name,
"vectorizer_object": fitted_vectorizer_object,
"label_encoder": label_encoder,
"y_test": y_test,
"y_pred": y_pred,
"accuracy": accuracy_score(y_test, y_pred),
"report": classification_report(y_test, y_pred, output_dict=True, target_names=label_encoder.classes_)
}

Одна из ключевых особенностей run_pipeline заключается не только в том, что она делает, но и в том, что возвращает. По завершении работы функция возвращает единственный, но мощный словарь Python, содержащий все обученные «артефакты».

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

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

Но как насчет прогнозирования на совершенно новых данных? Именно здесь quick_sentiments облегчает следующий шаг. Вам, конечно, нужно будет пропустить новый необработанный текст через ту же функцию pre_process, но при этом не придется ничего переучивать. Объект «артефакт», возвращенный run_pipeline, содержит все подобранные компоненты, необходимые для мгновенного применения правильной векторизации, и модели к новым данным, обеспечивая  последовательность всего процесса от начала до конца.

ШАГ 3: Выигрыш — прогнозирование на совершенно новых данных

#ПРЕДОБРАБОТКА НОВЫХ ДАННЫХ
new_data = new_data.with_columns(
pl.col(response_column).map_elements(lambda x: pre_process(x, remove_brackets=True)).alias("processed") #add inside the map_elements
)

Итак, вы успешно обучили и оценили модель на своем наборе данных, и функция run_pipeline() вернула словарь Python (сохраненный нами как dt), содержащий все обученные «артефакты».

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

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

# Объект 'dt' - это словарь, возвращенный из вызова run_pipeline()
predictions_df = make_predictions(
new_data=new_data,
text_column_name="processed",
vectorizer=dt["vectorizer_object"],
best_model=dt["model_object"],
label_encoder=dt["label_encoder"],
prediction_column_name="sentiment_predictions" # Вы можете назвать выходной столбец
)

Что происходит «под капотом»

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

  • vectorizer=dt["vectorizer_object"]: вы передаете конкретный обученный векторизатор, который был настроен на ваших данных;
  • best_model=dt["model_object"]: вы передаете конкретную обученную модель;
  • label_encoder=dt["label_encoder"]: вы передаете настроенный кодировщик меток, чтобы правильно преобразовать выходные данные обратно в ваши исходные метки (например, «положительный», «отрицательный»).

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

Как? Передавая обученные артефакты, вы делаете ошибки практически невозможными. Вам не нужно задавать себе следующие вопросы:

  • «Не переобучил ли я случайно свой векторизатор на новых данных?»
  • «Использую ли я те же ngram_range и max_features, что и для обучающих данных?»
  • «Использую ли я модель с лучшими гиперпараметрами или просто модель по умолчанию?»

Функция make_predictions() гарантирует обработку новых данных через точно такой же, последовательный пайплайн, как и исходные обучающие данные. Риск ошибки здесь сведен к нулю, поскольку вся сложная работа выполняется за вас «под капотом».

И вот у вас готов новый датафрейм с дополнительным столбцом прогнозов. Всего за три основных шага — pre_process()run_pipeline() и make_predictions() — мы прошли полный путь анализа тональности: от необработанного текста до надежных прогнозов для реальных данных.

Полный исходный код доступен здесь.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Alabhya Dahal: Sentiment Analysis in 3 steps: Using quick_sentiments in Python

Предыдущая статьяСреда разработчика в монорепозитории Stripe
Следующая статьяКогда нативные веб-компоненты эффективней фронтенд-фреймворков