В настоящее время системы на базе искусственного интеллекта присутствуют практически везде. Он служит интересам компании во многих отношениях. Одним из них является анализ настроений в режиме реального времени или с пакетной обработкой.
Анализ тональности – это метод, который может вывести/предсказать общее ощущение фрагментов текста, комментариев, статей и любого текстового объекта. Учитывая, что репутация и имидж компании являются первоочередными задачами для большинства предприятий, анализ настроений может предотвратить и уменьшить нежелательную шумиху, вызванную любым комментарием или статьей, видимой в Интернете.
Определить, является ли комментарий, относящийся к нашей компании, положительным или отрицательным, можно с помощью анализа настроений и моделей ИИ. На следующих шагах мы сравним некоторые из наиболее часто используемых моделей в этом случае.
Предварительная обработка вашего текста
Это один из самых важных шагов, даже в контексте, отличном от НЛП, вам необходимо сначала изучить и предварительно обработать ваши данные. Нашим моделям потребуются чистые и значимые данные. Однако это не основная цель данной статьи, поэтому мы будем краткими.
Мы будем использовать набор данных Sentiment140 с 1,6 миллионами твитов, доступных в TensorFlow и Kaggle. Имейте в виду, что вы можете взять любой набор текстовых данных из своей компании и адаптировать следующий процесс к своим потребностям.
Токенизаторы
Токенизация — это наш первый шаг очистки. Токенизаторы разделят текст на токены.
Некоторые дополнительные задачи предварительной обработки также могут быть выполнены, как в нашем случае:
- Нижний регистр
- Удаление стоп-слов
- Замена эмодзи на «Эмодзи» + значение одним словом
- Замена упоминания на «ПОЛЬЗОВАТЕЛЬ»
- Удаление не буквенно-цифровых символов
На следующем шаге мы выберем способ создания функций из наших списков токенов между векторизаторами и встраиванием.
Создавайте объекты с помощью векторизаторов
Наш первый метод создания признаков — использование векторизаторов. Для простоты мы будем использовать только 2 векторизатора:
- Подсчет: он просто подсчитывает, сколько раз токен присутствует в документе.
- Tf-idf: работает аналогично счетчику, но уравновешивается количеством документов в корпусе, где присутствует токен.
Создание функций с помощью встраивания Word
Наш второй метод создания функций — это встраивание слов. Встраивание слов может фиксировать контекст, семантическое и синтаксическое сходство (пол, синонимы…). В нашем примере мы будем использовать только два вложения:
- Spacy. Хотя Spacy хорошо известен своим надежным токенизатором, его также можно использовать для встраивания слов.
- Doc2Vec: я рекомендую вам ознакомиться с этой статьей, чтобы получить представление о Doc2Vec.
- Слой внедрения: Keras реализует полнофункциональный способ встраивания слов.
Оценка модели
Мы можем разделить наш набор данных, используя следующую пропорцию: 80% для обучения и 20% для тестирования.
Для оценки производительности модели используются 2 метрики:
- ROC AUC: площадь под кривой ROC дает нам меру разделимости между нашими двумя классами (отрицательным и положительным).
- AP: Средняя точность дает нам общее представление о производительности модели.
Чтобы проверить, не переоснащается ли модель, мы рассчитаем обе эти метрики в наборе данных для обучения и тестирования. Если метрики обучения намного выше, чем метрики тестирования, мы делаем вывод, что модель переобучается.
1-я модель: модель логистической регрессии
Эта модель является менее сложной в этой статье и будет использоваться в качестве базовой.
Мы используем решатель саги, так как у нас большой набор данных.
from sklearn.linear_model import LogisticRegressionCV # Define model model = LogisticRegressionCV(solver='saga', random_state=42) # Train model model.fit(X_train, y_train) # Compute predictions y_pred = model.predict(X_test)
Время тренировки: 51 минута
Время предсказания: 0 секунд
Результаты испытаний поезда
AP = 0.93
ROC AUC = 0,93
Результаты набора тестов
AP = 0.87
ROC AUC = 0,87
2-й тип моделей: модели FNN
Мы будем использовать простую нейронную сеть прямого распространения с двумя скрытыми слоями. На этот раз мы не будем использовать обработку текста. Наша цель состоит в том, чтобы исследовать различные комбинации.
Со слоем векторизации: количество
Как и CountVectorizer из библиотеки scikit-learn, наш уровень векторизации будет генерировать функции на основе количества токенов.
import tensorflow as tf from keras.models import Sequential from keras.layers import Input, TextVectorization, Dense, Dropout, Activation from keras.callbacks import TensorBoard, EarlyStopping from keras.metrics import AUC model_name = "fnn_count" vocabulary_size = 10000 # define vectorization layer vectorize_layer = TextVectorization( output_mode="count", max_tokens=vocabulary_size, pad_to_max_tokens=True, ) vectorize_layer.adapt( df.text, batch_size=128, ) # define NN model model = Sequential(name=model_name) model.add(Input(shape=(1,), dtype=tf.string)) model.add(vectorize_layer) model.add(Dense(100, input_shape=(vocabulary_size,), activation="relu")) model.add(Dropout(0.2)) model.add(Dense(10, activation="relu")) model.add(Dense(1, activation="sigmoid")) # compile NN network model.compile( loss="binary_crossentropy", optimizer="adam", metrics=[ "accuracy", AUC(curve="ROC", name="ROC_AUC"), AUC(curve="PR", name="AP"), ], ) # fit NN model model.fit( X_train, y_train, epochs=10, batch_size=128, validation_split=0.2, callbacks=[ TensorBoard(log_dir=f"logs/{model.name}"), EarlyStopping(monitor="val_loss", patience=2), ], workers=4, use_multiprocessing=True, )
Обучить результаты
Средняя точность = 0,94
ROC AUC = 0,94
Результаты тестирования
Средняя точность = 0,89
ROC AUC = 0,89
С встраиванием SpaCy
SpaCy будет генерировать векторы функций, используя встраивание слов. Вот фрагмент кода, который можно использовать для создания наших функций.
from tqdm import tqdm import spacy !python -m spacy download en_core_web_lg nlp = spacy.load("en_core_web_lg") X = [nlp(doc).vector for doc in tqdm(df.text)]
Используемая модель такая же, как и раньше, за исключением части слоя векторизации:
from keras.models import Sequential from keras.layers import ( Input, TextVectorization, Dense, Dropout, Activation, ) from keras.callbacks import TensorBoard, EarlyStopping from keras.metrics import AUC model_name = "fnn_spacy_embedded" vector_size = len(X[0]) # define NN model model = Sequential(name=model_name) model.add(Dense(100, input_shape=(vector_size,), activation="relu")) model.add(Dropout(0.2)) model.add(Dense(10, activation="relu")) model.add(Dense(1, activation="sigmoid")) # compile NN network model.compile( loss="binary_crossentropy", optimizer="adam", metrics=[ "accuracy", AUC(curve="ROC", name="ROC_AUC"), AUC(curve="PR", name="AP"), ], ) # fit NN model model.fit( np.stack(X_train, axis=0), y_train, epochs=10, batch_size=128, validation_split=0.2, callbacks=[ TensorBoard(log_dir=f"logs/{model.name}"), EarlyStopping(monitor="val_loss", patience=2), ], workers=4, use_multiprocessing=True, )
Обучить результаты
Средняя точность = 0,87
ROC AUC = 0,86
Результаты тестирования
Средняя точность = 0,86
ROC AUC = 0,86
С встраиванием Doc2Vec
Модель Doc2Vec будет генерировать векторы признаков. Doc2Vec основан на Word2Vec, и к логике добавлено встраивание абзаца.
Код модели остается прежним, меняется только встраиваемая часть:
!pip install gensim from gensim.models.doc2vec import TaggedDocument, Doc2Vec from gensim.utils import simple_preprocess # Tag documents for training corpus = [ TaggedDocument(words=simple_preprocess(doc), tags=[i]) for i, doc in enumerate(df.text) ] # Train doc2vec model doc2vec = Doc2Vec() doc2vec.build_vocab(corpus) doc2vec.train(corpus, total_examples=doc2vec.corpus_count, epochs=doc2vec.epochs) # Vectorize text X = [doc2vec.infer_vector(doc.words) for doc in corpus]
Обучить результаты
Средняя точность = 0,82
ROC AUC = 0,83
Результаты тестирования
Средняя точность = 0,82
ROC AUC = 0,82
3-й тип моделей: модели RNN
Мы будем использовать две продвинутые модели рекуррентной нейронной сети: LSTM и двунаправленный LSTM. Эти архитектуры рекомендуются для последовательных данных и широко используются в обработке естественного языка (NLP).
Использование RNN предоставит модели возможность хранить информацию о прошлых шагах.
LSTM-модель
Мы добавим слой Embedding, который позаботится о создании функций для модели LSTM. Векторы объектов ограничены 10 000, чтобы ускорить выполнение.
from keras.models import Sequential from keras.layers import ( Input, TextVectorization, Dense, Dropout, Activation, Embedding, LSTM, ) from keras.callbacks import TensorBoard, EarlyStopping from keras.metrics import AUC # Model constants. model_name = "lstm_embedded" max_features = 10000 sequence_length = 30 embedding_dim = 100 rnn_units = 100 # Define vectorizer vectorize_layer = TextVectorization( output_mode="int", max_tokens=max_features, output_sequence_length=sequence_length, ) vectorize_layer.adapt( df.text, batch_size=128, ) # define NN model model = Sequential(name=model_name) model.add(Input(shape=(1,), dtype=tf.string)) model.add(vectorize_layer) # Embedding layer model.add( Embedding( max_features, embedding_dim, input_length=sequence_length, ) ) # LSTM layer model.add(LSTM(units=rnn_units, dropout=0.2)) # Dense layers model.add(Dense(100, input_shape=(max_features,), activation="relu")) model.add(Dropout(0.2)) model.add(Dense(10, activation="relu")) # Classification layer model.add(Dense(1, activation="sigmoid")) # compile NN network model.compile( loss="binary_crossentropy", optimizer="adam", metrics=[ "accuracy", AUC(curve="ROC", name="ROC_AUC"), AUC(curve="PR", name="AP"), ], ) # fit NN model model.fit( X_train, y_train, epochs=10, batch_size=128, validation_split=0.2, callbacks=[ EarlyStopping(monitor="val_loss", patience=2), ], workers=4, use_multiprocessing=True, )
Обучить результаты
Средняя точность = 0,94
ROC AUC = 0,93
Результаты тестирования
Средняя точность = 0,91
ROC AUC = 0,91
Двунаправленная модель LSTM
Эта модель почти такая же, как и предыдущая. Слой LSTM заменяется двунаправленным слоем LSTM.
from keras.models import Sequential from keras.layers import ( Input, TextVectorization, Dense, Dropout, Activation, Embedding, Bidirectional, LSTM, ) from keras.callbacks import TensorBoard, EarlyStopping from keras.metrics import AUC # Model constants model_name = "bidirectional_lstm_embedded" max_features = 10000 sequence_length = 30 embedding_dim = 100 rnn_units = 100 # Define vectorizer vectorize_layer = TextVectorization( output_mode="int", max_tokens=max_features, output_sequence_length=sequence_length, ) vectorize_layer.adapt( df.text, batch_size=128, ) # define NN model model = Sequential(name=model_name) model.add(Input(shape=(1,), dtype=tf.string)) model.add(vectorize_layer) # Embedding layer model.add( Embedding( max_features, embedding_dim, input_length=sequence_length, ) ) # Bidirectional LSTM layer model.add(Bidirectional(LSTM(units=rnn_units, dropout=0.2))) # Dense layers model.add(Dense(100, input_shape=(max_features,), activation="relu")) model.add(Dropout(0.2)) model.add(Dense(10, activation="relu")) # Classification layer model.add(Dense(1, activation="sigmoid")) # compile NN network model.compile( loss="binary_crossentropy", optimizer="adam", metrics=[ "accuracy", AUC(curve="ROC", name="ROC_AUC"), AUC(curve="PR", name="AP"), ], ) # fit NN model model.fit( X_train, y_train, epochs=10, batch_size=128, validation_split=0.2, callbacks=[ TensorBoard(log_dir=f"logs/{model.name}"), EarlyStopping(monitor="val_loss", patience=2), ], workers=4, use_multiprocessing=True, )
Результаты обучения
Средняя точность = 0,94
ROC AUC = 0,93
Результаты теста
Средняя точность = 0,91
ROC AUC = 0,91
4-й тип моделей: модели Bert
Модель BERT (представление двунаправленного кодировщика для преобразователя) была представлена в этой статье и впервые опубликована в этом репозитории командой Google Research в 2018 году.
BERT — это модель-трансформер, предварительно обученная на огромном количестве данных на английском языке. Учебная часть была выполнена с самоконтролем, что означает, что тренировочные данные не маркировались.
Он использует моделирование маскированного языка (MLM), которое отличается от RNN, которое может принимать слова только одно за другим. Он может понимать шаблоны данных и обобщать их для нескольких задач НЛП.
Поскольку он двунаправленный, он может использовать как предыдущую, так и следующую информацию. Это может быть полезно при обработке естественного языка, поскольку прошлые и следующие предложения могут нести больше информации для понимания текущего.
Кроме того, BERT доступен для нескольких языков, например, camemBERT для французского языка, который был обучен на большом французском корпусе.
Прежде чем использовать модели BERT, мы должны токенизировать и подготовить все необходимые векторы (input_ids, warning_mask и token_type_ids) следующим образом:
%pip install transformers # Tokenizers, Stemmers and Lemmatizers from transformers import AutoTokenizer BERT_MODEL = "bert-base-uncased" #or "vinai/bertweet-base" MAX_LENGTH = 50 tokenizer = AutoTokenizer.from_pretrained(BERT_MODEL, do_lower_case=True) input_ids = np.asarray( [ tokenizer(sent, max_length=MAX_LENGTH, padding="max_length", truncation=True)[ "input_ids" ] for sent in tqdm(df.text) ] ) attention_mask = np.asarray( [ tokenizer(sent, max_length=MAX_LENGTH, padding="max_length", truncation=True)[ "attention_mask" ] for sent in tqdm(df.text) ] ) token_type_ids = np.asarray( [ tokenizer(sent, max_length=MAX_LENGTH, padding="max_length", truncation=True)[ "token_type_ids" ] for sent in tqdm(df.text) ] )
Базовая бескорпусная модель BERT
Поскольку модель предварительно обучена на большом корпусе, мы не будем изучать результаты обучения.
Мы будем использовать bert-base-uncased от Hugging Face.
from transformers import TFAutoModelForSequenceClassification from tensorflow.keras.callbacks import EarlyStopping from tensorflow.keras.losses import BinaryCrossentropy from tensorflow.keras.optimizers import Adam from tensorflow.keras.metrics import BinaryAccuracy # Define NN model print("Defining model...") model = TFAutoModelForSequenceClassification.from_pretrained(BERT_MODEL, num_labels=2) # compile NN network print("Compiling model...") model.compile( loss=BinaryCrossentropy(), optimizer=Adam(learning_rate=2e-5), # Value recommended by the Bert team metrics=BinaryAccuracy(), ) # fit NN model print("Fitting model...") model.fit( [input_ids_train, attention_mask_train, token_type_ids_train], labels_train, epochs=10, batch_size=8, validation_split=0.2, callbacks=[ EarlyStopping(monitor="val_loss", patience=3), ], workers=4, use_multiprocessing=True, )
Результаты тестирования
Confusion Matrix : [[17631 82369] [ 415 99585]] ROC AUC score : 0.883 Average Precision score : 0.822
BERTweet модель
Поскольку модель предварительно обучена на большом корпусе, мы не будем изучать результаты обучения.
Мы будем использовать vinai/bertweet-base от Hugging Face. Эта модель BERT была обучена на базе 850 млн английских твитов.
Кодовая часть такая же, как и раньше, вам просто нужно изменить название модели, чтобы загрузить нужное.
Результаты тестирования
Confusion Matrix : [[79351 20649] [10016 89984]] ROC AUC score : 0.915 Average Precision score : 0.901
Сравнение моделей
Бенчмарк основных показателей
Как мы видим, модель логистической регрессии, которая является нашей базовой линией, работает хорошо и находится посередине между худшими и лучшими моделями.
Лучшие модели — LSTM (простая и двунаправленная) и BERTweet. Они хорошо работают на наших тестовых наборах данных.
Худшие модели — FNN с встраиванием Word (Doc2Vec и SpaCy) и базой BERT. Они сложнее нашей базовой модели, но менее эффективны. Это означает, что сложность не обязательно означает эффективность.
Специфичность против чувствительности
Напомню, что специфичность измеряет способность модели предсказывать истинные отрицательные значения. С другой стороны, чувствительность измеряет способность модели предсказывать истинные положительные результаты.
Мы видим, что базовая модель BERT имеет высокую чувствительность. Это означает, что эта модель правильно определяет большинство истинно положительных твитов. В нашем случае это не имеет значения, поскольку мы пытаемся правильно идентифицировать негативные твиты.
Модели BERTweet, FNN с подсчетом слов и LSTM обладают высокой специфичностью. Это означает, что эти модели минимизируют количество негативных твитов, которые неправильно классифицированы. В нашем случае это может быть интересно в том смысле, что мы не хотим, чтобы «плохой» твит классифицировали как положительный.
Стоимость времени
Чем сложнее модели, тем больше времени нам потребуется для их обучения. В нашем наборе моделей мы можем легко сказать, что наша модель логистической регрессии является менее сложной, и для обучения на ЦП требуется всего 45 минут.
Далее у нас есть модели FNN и RNN, которым требуется от 1 до 2 часов для обучения на GPU.
Наконец, моделям BERT и BERTweet требуется огромное количество ресурсов, чтобы иметь хорошие характеристики. Средние показатели достигаются после 6,5 часов обучения на графическом процессоре для BERT, а хорошие показатели — после 11 часов для BERTweet.
Выбор модели
Наконец, в нашем примере компания выберет LSTM в качестве производственной модели. Действительно, у этой модели очень много плюсов:
- Хорошие выступления
- Разумно с точки зрения необходимых вычислительных ресурсов
- Архитектура может быть полностью настроена в соответствии с возможностями наших ресурсов.
Тем не менее, BERTweet может быть очень интересной моделью, если мы получим достаточно данных и ресурсов GPU/TPU. Производительность увеличивается по мере увеличения времени обучения. В долгосрочной перспективе, я думаю, у него будет лучшая производительность, чем у модели LSTM.