NTA

Анализ тональности текста с использованием фреймворка LightAutoML

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

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

Задача

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

Анализ тональности текста позволит понять, что в обращении пытается донести пользователь – что-то нейтральное или негативное. Нас интересуют случаи, где напрямую описываются проблемы в автоматизированной системе и на что требуется внимание и проведение дальнейшего анализа.

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

Предобработка данных

Предобработаем данные с помощью регулярных выражений, убрав лишние символы и стоп-слова. Кроме того, приведем слова к нормальной форме с помощью библиотеки pymorphy2.

data['text'] = data['text'].replace("[0-9!#()$\,\'\-\.*+/:;<=>?@[\]^_`{|}\"]+", ' ', regex=True) data['text'] = data['text'].replace(r'\s+', ' ', regex=True) data['text'] = data['text'].apply(lambda x: ' '.join([word for word in x.split() if word not in (stop_words)])) data['text'] = data['text'].apply(lambda x: ' '.join([morph.parse(word)[0]. normal_form for word in x.split()]))

Описание модели

Далее мы формируем на основе размеченного датасета обучающую (65% от его размера) и тестовую (35% соответственно) выборки и задаем гиперпараметры для модели. Мы будем использовать текстовый пресет, который был реализован специально для выполнения NLP задач.

automl = TabularNLPAutoML(task=Task('binary', metric = f1_binary), timeout=2000, memory_limit=16, cpu_limit=4, text_params={'lang': 'ru'}, general_params={'nested_cv': False, 'use_algos': [['linear_l2', 'lgb']]}, reader_params={'cv': 3, 'random_state': 42}
  1. task=Task(‘binary’, metric = f1_binary)– в качестве задачи мы выбираем бинарную классификацию и метрики функцию, которая вычисляет F1-score
  2. timeout=2000– ставим ограничение работы модели по времени на 2000 секунд
  3. memory_limit=16– обозначаем объем выделяемой RAM
  4. cpu_limit=4– обозначаем число выделяемых ядер
  5. text_params={‘lang’: ‘ru’}– в качестве текстовых параметров выбираем русский язык
  6. general_params={‘nested_cv’: False– обозначаем, что нам нет необходимости в выполнении оптимизации гиперпараметров
  7. ‘use_algos’: [[‘linear_l2’, ‘lgb’]]} – в качестве используемых алгоритмов мы обозначаем ридж-регрессию и ансамбль LightGBM

Обучение модели:

Код обучения модели выглядит следующим образом:

roles = {'target': 'sentiment', 'text': ['review']} pred = automl.fit_predict(train_data, roles=roles, verbose=3) print('oof_pred:\n{}\nShape = {}'.format(pred, pred.shape)) class_result = classification_report(y_true=train_data['sentiment'].values, y_pred=np.where(pred.data[:, 0] >= 0.5, 1, 0), target_names=['Neutral', 'Negative']) print(class_result)
  1. roles = {‘target’: ‘sentiment’, ‘text’: [‘review’]}– описываем ключевое и текстовое поля
  2. pred = automl.fit_predict(train_data, roles=roles, verbose=3) – обучаем модель на основе обучающей выборки, передаем затрагиваемые поля

На выходе мы имеем одномерный массив, который показывает вероятность отнесения обращения к негативному классу

При обучении модели значение метрики F1-score достигло 0.894, соответственно можно сделать вывод о том, что модель хорошо справляется с задачей определения нейтральных и негативных обращений.

Также одним из способов оценить работу модели в целом можно по кривой ROC-AUC, которая описывает площадь под кривой (Area Under Curve – Receiver Operating Characteristic).

Объяснение работы модели

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

lime = LimeTextExplainer(automl, feature_selection='lasso', force_order=False) exp = lime.explain_instance(data.iloc[1013], labels=(0, 1), perturb_column='review')
  1. lime = LimeTextExplainer(automl, feature_selection=’lasso’, force_order = False)– вызываем функцию LIME и в качестве параметров передаем нашу обученную модель, в качестве алгоритма отбора фич – LASSO (least absolute shrinkage and selection operator – наименьшее абсолютное сжатие и оператор выбора)
  2. exp = lime.explain_instance (data.iloc[1013], labels=(0, 1), perturb_ column= ‘review’) – далее в качестве параметров передаем случайную строку нашего датасета, обозначаем подписи классов и затрагиваемый столбец

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

Рассматривая данный пример, мы видим, что модель наиболее явно выделяет относящиеся к негативу слова – вход и ошибка, которые сигнализируют о том, что в данной автоматизированной системе имеются какие-то проблемы со входом. Теперь рассмотрим пример, где встречаются слова обоих классов:

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

На что еще способен фреймворк?

Прежде всего стоит выделить мультиклассовую классификацию, синтаксис реализации которой идентичен с бинарной, но в качестве задачи мы выбираем multiclass:

automl = TabularNLPAutoML(task=Task('multiclass', metric = f1_score) . . .

Доразметив нашу выборку третьим (положительным) классом и обучив на ней модель, мы получим следующий результат:

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

automl = TabularAutoML(task = Task ('reg', loss = 'rmsle', metric = rmsle_metric, greater_is_better = False) . . .

Работа с текстом

В LightAutoML имеется большое количество вариантов разработки той или иной модели, работающей с текстом. Библиотека предоставляет не только получение стандартных признаков на основе TF-IDF, но и на основе эмбеддингов:

1) На основе встроенного FastText, который можно тренировать на том или ином корпусе

2) Предобученных моделей Gensim

3) Любой другой объект, который имеет вид словаря, где на вход подается слово, а на выходе его эмбеддинги

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

1) Weighted Average Transformer (WAT) – взвешивается каждое слово с некоторым весом

TabularNLPAutoML(task = task, autonlp_params = {'model_name': 'wat', 'transformer_params': {'weight_type': 'idf', 'use_svd': True}} )

2) Bag of Random Embedding Projections (BOREP) – строится линейная модель со случайными весами

TabularNLPAutoML(task = task, autonlp_params = {'model_name': 'wat', 'transformer_params': {'model_params': {'proj_size': 300, 'pooling': 'mean', 'max_length': 200, 'init': 'orthogonal', 'pos_encoding': False}, 'dataset_params': {'max_length': 200}}} )

3) Random LSTM – LSTM со случайными весами

TabularNLPAutoML(task = task, autonlp_params = {'model_name': 'random_lstm', 'transformer_params': {'model_params': {'embed_size': 300, 'hidden_size': 256, 'pooling': 'mean', 'num_layers': 1}, 'dataset_params': {'max_length': 200, 'embed_size': 300}}} )

4) Bert Pooling – получение эмбеддинга с последнего выхода модели Transformer

TabularNLPAutoML(task = task, autonlp_params = {'model_name': 'pooled_bert', 'transformer_params': {'model_params': {'pooling': 'mean'}, 'dataset_params': {'max_length': 256}}} )

За препроцессинг текста отвечает класс токенайзера, по умолчанию применяется только для TF-IDF. Что выполняется для русского языка:

1) Производится замена ё на е

2) Удаляются знаки препинания, отдельно стоящие цифры

3) Токенизация происходит по пробелу

4) Текст приводится к нижнему регистру

5) Удаляются слова, состоящие из одного символа

6) Опционально удаляются стоп-слова

Подводя итоги стоит сказать, что LightAutoML благодаря встроенному инструментарию способен показывать достаточно хорошие результаты в задачах бинарной или мультиклассовой классификации и регрессии. Конкретно в нашем случае нам удалось создать модель сентиментного анализа, которая с 89% точностью определяет эмоциональный окрас обращения и слова, которые оказывают на это наибольшее влияние.

0
Комментарии
Читать все 0 комментариев
null