Методы машинного обучения для анализа отзывов о македонских ресторанах

В то время как модели машинного обучения для обработки естественного языка традиционно были сосредоточены на популярных языках, таких как английский и испанский, менее распространенные языки получили гораздо меньше развития. Однако в связи с недавним ростом электронной коммерции из-за пандемии COVID-19 даже менее распространенные языки, такие как македонский, генерируют большие объемы данных посредством онлайн-обзоров. Это открыло возможность для разработки и обучения моделей машинного обучения для анализа настроений по отзывам о македонских ресторанах, которые могут помочь предприятиям лучше понять настроения клиентов и улучшить свои услуги. В этом исследовании мы решаем проблемы, возникающие в связи с этой проблемой, а также изучаем и сравниваем различные модели анализа настроений для анализа настроений в обзорах македонских ресторанов, начиная от классических случайных лесов и заканчивая современными методами глубокого обучения и преобразователями.

Содержание

  • Проблемы и предварительная обработка данных
  • Создание векторных вложений
    - ЛАЗЕРНЫЕ вложения
    - Многоязычный универсальный кодировщик текста
    - OpenAI Ada v2
  • Модели машинного обучения
    - Случайный лес
    - XGBoost
    - Машины опорных векторов
    - Глубокое обучение
    - Преобразователи
  • Результаты и обсуждение
  • Будущая работа
  • Заключение

Предварительная обработка данных

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

Однако для языков с кириллицей возникает дополнительная проблема, поскольку пользователи в Интернете часто выражают свои мысли с помощью латиницы, что приводит к смешанным данным, состоящим как из латиницы, так и из кириллицы. Чтобы решить эту проблему, я использовал набор данных из местного ресторана, содержащий примерно 500 отзывов, содержащих как латиницу, так и кириллицу. Набор данных также включает небольшое подмножество обзоров на английском языке, которые помогут оценить производительность на смешанных данных. Кроме того, онлайн-тексты могут содержать такие символы, как смайлики, которые необходимо удалить. Поэтому предварительная обработка является важным шагом перед выполнением любого встраивания текста.

import pandas as pd
import numpy as np

# load the dataset into a dataframe
df = pd.read_csv('/content/data.tsv', sep='\t')

# see the distribution of the sentiment classes
df['sentiment'].value_counts()

# -------
# 0    337
# 1    322
# Name: sentiment, dtype: int64

Набор данных содержит положительные и отрицательные классы с почти равным распределением. Для удаления смайликов я использовал библиотеку Python emoji, которая может легко удалить смайлики и другие символы.

!pip install emoji
import emoji

clt = []
for comm in df['comment'].to_numpy():
  clt.append(emoji.replace_emoji(comm, replace=""))

df['comment'] = clt
df.head()

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

import cyrtranslit
latin = []
cyrillic = []
for comm in df['comment'].to_numpy():
  latin.append(cyrtranslit.to_latin(comm, "mk"))
  cyrillic.append(cyrtranslit.to_cyrillic(comm, "mk"))

df['comment_cyrillic'] = cyrillic
df['comment_latin'] = latin
df.head()

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

Векторные вложения

В настоящее время отсутствуют крупномасштабные модели представительства Македонии. Однако мы можем использовать многоязычные модели, обученные тексту на македонском языке. Доступно несколько таких моделей, но для этой задачи я обнаружил, что наиболее подходящими вариантами будут LASER и Multilingual Universal Sentence Encoder.

ЛАЗЕР

LASER (Language-Agnostic Sentence Representations) — это независимый от языка подход к созданию высококачественных вложений многоязычных предложений. Модель LASER основана на двухэтапном процессе, где первым этапом является предварительная обработка текста, включая токенизацию, преобразование нижнего регистра и применение фрагмента предложения. Эта часть зависит от языка. Второй этап включает в себя сопоставление предварительно обработанного входного текста с встраиванием фиксированной длины с использованием многослойного двунаправленного LSTM.

Было показано, что LASER превосходит другие популярные методы встраивания предложений, такие как fastText и InferSent, в ряде тестовых наборов данных. Кроме того, модель LASER имеет открытый исходный код и находится в свободном доступе, что делает ее доступной для всех.

Создание вложений с помощью LASER — простой процесс:

!pip install laserembeddings
!python -m laserembeddings download-models

from laserembeddings import Laser

# create the embeddings
laser = Laser()
embeddings_c = laser.embed_sentences(df['comment_cyrillic'].to_numpy(),lang='mk')
embeddings_l = laser.embed_sentences(df['comment_latin'].to_numpy(),lang='mk')

# save the embeddings
np.save('/content/laser_multi_c.npy', embeddings_c)
np.save('/content/laser_multi_l.npy', embeddings_l)

Многоязычный универсальный кодировщик предложений

Multilingual Universal Sentence Encoder (MUSE) — это предварительно обученная модель для создания вложений предложений, разработанная Facebook. MUSE предназначен для кодирования предложений на нескольких языках в единое пространство.

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

!pip install tensorflow_text
import tensorflow as tf
import tensorflow_hub as hub
import numpy as np
import tensorflow_text

# load the MUSE module
module_url = "https://tfhub.dev/google/universal-sentence-encoder-multilingual-large/3"
embed = hub.load(module_url)

sentences = df['comment_cyrillic'].to_numpy()
muse_c = embed(sentences)
muse_c = np.array(muse_c)

sentences = df['comment_latin'].to_numpy()
muse_l = embed(sentences)
muse_l = np.array(muse_l)

np.save('/content/muse_c.npy', muse_c)
np.save('/content/muse_l.npy', muse_l)

OpenAI Ада v2

К концу 2022 года OpenAI анонсировала свою новую современную модель встраивания text-embedding-ada-002. Поскольку эта модель построена на GPT-3, она имеет возможности многоязычной обработки. Чтобы сравнить результаты между обзорами кириллицы и латиницы, я запустил модель на обоих наборах данных.

!pip install openai

import openai
openai.api_key = 'YOUR_KEY_HERE'

embeds_c = openai.Embedding.create(input = df['comment_cyrillic'].to_numpy().tolist(), model='text-embedding-ada-002')['data']
embeds_l = openai.Embedding.create(input = df['comment_latin'].to_numpy().tolist(), model='text-embedding-ada-002')['data']

full_arr_c = []
for e in embeds_c:
  full_arr_c.append(e['embedding'])
full_arr_c = np.array(full_arr_c)

full_arr_l = []
for e in embeds_l:
  full_arr_l.append(e['embedding'])
full_arr_l = np.array(full_arr_l)

np.save('/content/openai_ada_c.npy', full_arr_c)
np.save('/content/openai_ada_l.npy', full_arr_l)

Модели машинного обучения

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

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

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(embeddings_c, df['sentiment'], test_size=0.2, random_state=42)

Случайные леса

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

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix

rfc = RandomForestClassifier(n_estimators=100)
rfc.fit(X_train, y_train)
print(classification_report(y_test,rfc.predict(X_test)))
print(confusion_matrix(y_test,rfc.predict(X_test)))

XGBoost

XGBoost (eXtreme Gradient Boosting) — это мощный ансамблевый метод, в основном используемый для табличных данных. Как и Random Forest, XGBoost также использует деревья решений для классификации точек данных, но с другим подходом. Вместо одновременного обучения всех деревьев XGBoost обучает каждое дерево последовательно, изучая ошибки, допущенные предыдущим деревом. Этот процесс называется повышением, что означает объединение слабых моделей для формирования более сильной. Хотя XGBoost в первую очередь дает отличные результаты с табличными данными, было бы интересно протестировать его и с векторными вложениями.

from xgboost import XGBClassifier
from sklearn.metrics import classification_report, confusion_matrix

rfc = XGBClassifier(max_depth=15)
rfc.fit(X_train, y_train)
print(classification_report(y_test,rfc.predict(X_test)))
print(confusion_matrix(y_test,rfc.predict(X_test)))

Опорные векторные машины

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

from sklearn.svm import SVC
from sklearn.metrics import classification_report, confusion_matrix

rfc = SVC()
rfc.fit(X_train, y_train)
print(classification_report(y_test,rfc.predict(X_test)))
print(confusion_matrix(y_test,rfc.predict(X_test)))

Глубокое обучение

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

import tensorflow as tf
from tensorflow import keras
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix

model = keras.Sequential()
model.add(keras.layers.Dense(256, activation='relu', input_shape=(1024,)))
model.add(keras.layers.Dropout(0.2))
model.add(keras.layers.Dense(128, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid'))

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

history = model.fit(X_train, y_train, epochs=11, validation_data=(X_test, y_test))

test_loss, test_acc = model.evaluate(X_test, y_test)
print('Test accuracy:', test_acc)
y_pred = model.predict(X_test)

print(classification_report(y_test,y_pred.round()))
print(confusion_matrix(y_test,y_pred.round()))

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

С помощью следующей функции мы можем визуализировать обучение моделей.

import matplotlib.pyplot as plt

def plot_accuracy(history):
    plt.plot(history.history['accuracy'])
    plt.plot(history.history['val_accuracy'])
    plt.title('Model Accuracy')
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.legend(['Train', 'Validation'], loc='upper left')
    plt.show()

Трансформеры

Точная настройка преобразователей — это популярный метод обработки естественного языка, который включает в себя настройку предварительно обученных моделей преобразователей для выполнения конкретных задач. Преобразователи, такие как BERT, GPT-2 и RoBERTa, предварительно обучаются на больших объемах текстовых данных и способны изучать сложные паттерны и отношения в языке. Однако, чтобы хорошо работать с конкретными задачами, такими как анализ тональности или классификация текста, эти модели необходимо точно настроить на данных, специфичных для задачи.

Для этих типов моделей векторные представления, созданные нами ранее, не нужны, так как они напрямую обрабатывают токены (извлеченные прямо из текста). Для этой задачи анализа настроений на македонском языке я работал с bert-base-multilingual-uncased, который является многоязычной версией модели BERT.

HuggingFace сделал тонкую настройку трансформеров очень простой задачей. Во-первых, данные необходимо загрузить в набор данных трансформаторов. Затем текст токенизируется и, наконец, модель обучается.

from sklearn.model_selection import train_test_split
from datasets import load_dataset
from transformers import TrainingArguments, Trainer
from sklearn.metrics import classification_report, confusion_matrix

# create csv of train and test sets to be loaded by the dataset
df.rename(columns={"sentiment": "label"}, inplace=True)
train, test = train_test_split(df, test_size=0.2)
pd.DataFrame(train).to_csv('train.csv',index=False)
pd.DataFrame(test).to_csv('test.csv',index=False)

# load the dataset
dataset = load_dataset("csv", data_files={"train": "train.csv", "test": "test.csv"})

# tokenize the text
tokenizer = AutoTokenizer.from_pretrained('bert-base-multilingual-uncased')
encoded_dataset = dataset.map(lambda t: tokenizer(t['comment_cyrillic'],  truncation=True), batched=True,load_from_cache_file=False)

# load the pretrained model
model = AutoModelForSequenceClassification.from_pretrained('bert-base-multilingual-uncased',num_labels =2)

# fine-tune the model
arg = TrainingArguments(
    "mbert-sentiment-mk",
    learning_rate=5e-5,
    num_train_epochs=5,
    per_device_eval_batch_size=8,
    per_device_train_batch_size=8,
    seed=42,
    push_to_hub=True
)
trainer = Trainer(
    model=model,
    args=arg,
    tokenizer=tokenizer,
    train_dataset=encoded_dataset['train'],
    eval_dataset=encoded_dataset['test']
)
trainer.train()

# get predictions
predictions = trainer.predict(encoded_dataset["test"])
preds = np.argmax(predictions.predictions, axis=-1)

# evaluate
print(classification_report(predictions.label_ids,preds))
print(confusion_matrix(predictions.label_ids,preds))

Благодаря этому мы успешно настроили BERT для анализа настроений.

Результаты и обсуждение

Результаты анализа настроений по отзывам о македонских ресторанах являются многообещающими: несколько моделей достигли высокой точности и оценок F1. Эксперименты показывают, что модели глубокого обучения и преобразователи превосходят традиционные модели машинного обучения, такие как случайные леса и машины опорных векторов, хотя и ненамного. Трансформеры и глубокие нейронные сети, использующие новое встраивание OpenAI, смогли преодолеть барьер точности 0,9.

Модель встраивания OpenAI textembedding-ada-002 смогла значительно улучшить результаты, полученные даже из классических моделей ML, особенно на машинах опорных векторов. Наилучший результат в данном исследовании был достигнут при таком встраивании в кириллический текст на модели глубокого обучения.

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

Будущая работа

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

Заключение

В заключение, этот пост продемонстрировал эффективность различных моделей машинного обучения и методов внедрения для анализа настроений в отзывах о македонских ресторанах. Исследуются и сравниваются несколько классических моделей машинного обучения, таких как Random Forests и SVM, а также современные методы глубокого обучения, включая нейронные сети и преобразователи. Результаты показали, что точно настроенные модели преобразователя и модели глубокого обучения с новейшим встраиванием OpenAI превосходят другие методы с точностью проверки до 90%.

Спасибо за прочтение!