Определение архитектуры модели LSTM и ее реализация для анализа настроений
Что такое анализ настроений?
Анализ тональности определяет, является ли данная текстовая строка положительной, отрицательной или нейтральной (т. Е. Полярностью предложения). Анализ настроений помогает аналитикам данных в крупных организациях оценивать общественное мнение, проводить подробные исследования рынка, отслеживать репутацию продукта и лучше понимать впечатления клиентов.
Построение классификатора с более высокой точностью для определения тональности предложения было одной из наиболее изученных областей обработки естественного языка (NLP). В этой статье мы создадим классификатор тональности для английского языка, используя особый вид рекуррентной нейронной сети (RNN): сеть с долгой краткосрочной памятью (LSTM).
Что такое LSTM?
LSTM был представлен Hochreiter & Schmidhuber в 1997 году как решение проблемы исчезающего градиента, с которой сталкивается большинство RNN. Проще говоря, читая книгу, вы вспоминаете, что произошло в предыдущей главе. RNN может запоминать предыдущую информацию и использовать ее в качестве входных данных для обработки. Но недостатком RNN является то, что они не могут запоминать долгосрочные состояния из-за проблемы исчезающего градиента.
Поэтому LSTM специально разработаны, чтобы избежать этой проблемы. LSTM имеет соединение обратной связи, которое может обрабатывать последовательности данных. Наличие обратной связи помогает обрабатывать более одной точки данных. Базовая модель LSTM состоит из ячейки памяти, входного элемента, выходного элемента и элемента забывания. Ячейка памяти помогает сохранять информацию в памяти в течение более длительного периода. Таким образом, эта сеть хорошо подходит для классификации, прогнозирования на основе временных данных и т. Д.
Теперь, когда мы вкратце рассмотрели, что такое LSTM, давайте посмотрим, как его использовать для создания классификатора настроений с помощью набора данных фильма IMDB. Если вам нужно более глубокое понимание LSTM, прочтите статью, опубликованную основателями. Вы можете использовать разные наборы данных из Kaggle для более сложных реализаций LSTM для классификации текста.
Обязательные зависимости
Во-первых, нам нужно импортировать все зависимости, которые нам понадобятся на протяжении реализации нашего проекта:
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import nltk from nltk.tokenize import word_tokenize from keras.preprocessing.text import Tokenizer from keras.preprocessing.sequence import pad_sequences from keras.models import Sequential from keras.layers import Embedding, LSTM, Dense, Dropout, Bidirectional from keras.initializers import Constant from sklearn.preprocessing import LabelEncoder import warnings warnings.filterwarnings('ignore') sns.set()
Выше представлены библиотеки, состоящие из основных инструментов анализа и интеллектуального анализа данных, таких как NumPy, Pandas, Seaborn и matplotlib, набор инструментов естественного языка (NLTK) для обработки естественного языка и Keras для создания нашей искусственной нейронной сети.
Подпишитесь на еженедельник Deep Learning Weekly и присоединяйтесь к более чем 15 000 ваших коллег. Еженедельный доступ к последним новостям индустрии глубокого обучения, исследованиям, библиотекам кода, руководствам и многому другому.
Чтение набора данных
dataset = pd.read_csv("IMDB Dataset.csv") dataset.head() --Output-- review | sentiment 0 |One of the other reviewers has mentioned that ... | positive 1 |A wonderful little production. <br /><br />The... | positive 2 |I thought this was a wonderful way to spend ti... | positive 3 |Basically there's a family where a little boy ... | negative 4 |Petter Mattei's "Love in the Time of Money" is... | positive
Давайте посмотрим, сколько предложений о настроениях аннотировано в нашем наборе данных:
dataset.sentiment.value_counts() --Output-- positive 25000 negative 25000 Name: sentiment, dtype: int64
Как видно из вышеприведенного вывода, наш набор данных сбалансирован, что обеспечивает нам схожую скорость обнаружения для двух классов.
Разложение предложений на слова
Нам нужно разделить предложения на слова для токенизации и встраивания слов (подробнее об этих процессах см. Ниже).
word_corpus = [] for text in dataset['review']: words = [word.lower() for word in word_tokenize(text)] word_corpus.append(words) numberof_words = len(word_corpus) print(numberof_words) --Output-- 50000
Разделение набора данных
Набор данных необходимо разделить на обучение и тестирование (80% и 20%).
train_size = int(dataset.shape[0] * 0.8) X_train = dataset.review[:train_size] y_train = dataset.sentiment[:train_size] X_test = dataset.review[train_size:] y_test = dataset.sentiment[train_size:]
Токенизация
Токенизация - это процесс разбиения фразы, предложения, абзаца или полного текста документа на более мелкие компоненты, такие как слова или термины.
Нам нужно токенизировать слова и отступы для одинаковых входных размеров. Для этого мы будем использовать Tokenizer, доступный в Keras.
tokenizer = Tokenizer(numberof_words) tokenizer.fit_on_texts(X_train) X_train = tokenizer.texts_to_sequences(X_train) X_train = pad_sequences(X_train, maxlen=128, truncating='post', padding='post')
Сначала мы передали в Tokenizer количество слов, доступных в корпусе слов, затем обновили внутренний словарь fit_on_text
для текстового списка. После обновления словаря text_to_sequence
используется для преобразования токенов, имеющихся в текстовом корпусе, в последовательность целых чисел.
Теперь, когда мы токенизировали слова в X_train
наборе. Посмотрим, как это будет выглядеть.
X_train[0], len(X_train[0]) --Output-- (array([ 27, 4, 1, 80, 2102, 45, 1073, 12, 100, 147, 39, 316, 2968, 409, 459, 26, 3173, 33, 23, 200, 14, 11, 6, 614, 48, 606, 16, 68, 7, 7, 1, 87, 148, 12, 3256, 68, 41, 2968, 13, 92, 5626, 2,16202, 134, 4, 569, 60, 271, 8, 200, 36, 1, 673, 139, 1712, 68, 11, 6, 21, 3, 118, 15, 1, 7870, 2257, 38,11540, 11, 118, 2495, 54,5662, 16, 5182, 5, 1438, 377, 38, 569, 92, 6, 3730, 8, 1, 360, 353, 4, 1, 673, 7, 7, 9, 6, 431, 2968, 14, 12, 6, 1, 11736, 356, 5, 1,14689,6526, 2594, 1087, 9, 2661, 1432, 20,22583, 534, 32, 4795, 2451, 4, 1, 1193, 117, 29, 1,6893, 25, 2874,12191, 2, 392], dtype=int32), 128)
Как видно из вышеприведенного вывода, все слова представлены числами. Следовательно, слова, доступные в X_train
, были фактически токенизированы.
Проделаем то же самое с набором X_test
.
X_test = tokenizer.texts_to_sequences(X_test) X_test = pad_sequences(X_test, maxlen=128, truncating='post', padding='post')
После токенизации как обучающего, так и тестового набора нам нужно посмотреть, сколько уникальных слов доступно в наборе данных. Мы проверяем доступные уникальные слова, чтобы убедиться, что набор данных содержит разные слова, а не похожие.
index = tokenizer.word_index print("Count of unique words: {}".format(len(index))) --Output-- Count of unique words: 112173
Вложение слов
Вложение слов - это заученное текстовое представление, в котором слова со связанными значениями представлены аналогичным образом в виде векторов с действительными значениями. Эти векторы несут значения слов, так что слова рядом с ними несут аналогичные значения.
Во время этой реализации мы будем использовать вложения GloVe. Вы можете попробовать различные инструменты для встраивания слов, такие как Word2Vec, FastText и т. Д.
Во-первых, нам нужно составить словарь всех слов в корпусе, используя предварительно обученные вложения GloVe. (Щелкните встроенную ссылку, чтобы загрузить предварительно обученные векторы слов)
embeddings = {} with open("glove.twitter.27B.100d.txt") as file: for line in file: val = line.split() words = val[0] vectors = np.asarray(val[1:], 'float32') embeddings[words] = vectors file.close()
Во-вторых, нам нужно сделать матрицу всех слов, доступных в наборе данных, с векторами из словаря встраивания. Которая будет содержать предварительно обученные веса слов.
emb_matrix = np.zeros((numberof_words, 100)) for i, word in tokenizer.index_word.items(): if i < (numberof_words+1): vector = embeddings.get(word) if vector is not None: emb_matrix[i] = vector
Создание матрицы весов необходимо для слоя внедрения модели. Слой внедрения является первым скрытым слоем нашей модели, и все, что этот слой делает, это сопоставляет целочисленные входные данные с векторами, найденными по соответствующему индексу в матрице внедрения.
Создание модели
Перед созданием модели нам нужно закодировать метки в наборе данных с помощью LabelEncoder()
.
labels = LabelEncoder() y_train = labels.fit_transform(y_train) y_test = labels.transform(y_test)
Есть два типа способов создать модель с помощью Keras.
- Последовательный API
- Функциональный API
Использование последовательного API вместо функционального API подходит для этого сценария, потому что мы создаем модель слой за слоем.
model = Sequential() model.add(Embedding(input_dim=num_words, output_dim=100, embeddings_initializer=Constant(embedding_matrix), input_length=128, trainable=False)) model.add(LSTM(100, dropout=0.1)) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
Я применил следующие слои:
- 1-й уровень - слой встраивания: преобразует токенизированные слова во встраивание определенного размера.
- 2-й слой - слой LSTM: содержит затемнения скрытого состояния и дополнительные слои
- 3-й слой - плотный слой: соединяет все выходы из предыдущих слоев со всеми его нейронами.
- Функция активации - сигмовидная функция активации: преобразует все выходы в значения от 0 до 1.
- Функция потерь - двоичная кросс-энтропия: сравнивает выходные данные и прогнозирует фактический выход класса между 0 и 1
model.summary() --Output-- Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= embedding (Embedding) (None, 128, 100) 5000000 _________________________________________________________________ lstm (LSTM) (None, 100) 80400 _________________________________________________________________ dense (Dense) (None, 1) 101 ================================================================= Total params: 5,080,501 Trainable params: 80,501 Non-trainable params: 5,000,000 _________________________________________________________________
Теперь давайте обучим нашу модель 10 эпохам. Это небольшая сумма, просто чтобы проверить наш подход. Вообще говоря, вы захотите обучить свои модели большему количеству эпох, чтобы добиться лучшей производительности модели.
history = model.fit(X_train, y_train, epochs=5, batch_size=2048, validation_data=(X_test, y_test)) --Output-- Epoch 1/5 20/20 [==============================] - 55s 3s/step - loss: 0.6879 - accuracy: 0.5345 - val_loss: 0.6384 - val_accuracy: 0.6338 Epoch 2/5 20/20 [==============================] - 45s 2s/step - loss: 0.6074 - accuracy: 0.6816 - val_loss: 0.5314 - val_accuracy: 0.7451 Epoch 3/5 20/20 [==============================] - 45s 2s/step - loss: 0.5362 - accuracy: 0.7417 - val_loss: 0.5010 - val_accuracy: 0.7680 Epoch 4/5 20/20 [==============================] - 45s 2s/step - loss: 0.5080 - accuracy: 0.7597 - val_loss: 0.4718 - val_accuracy: 0.7783 Epoch 5/5 20/20 [==============================] - 45s 2s/step - loss: 0.4871 - accuracy: 0.7646 - val_loss: 0.4719 - val_accuracy: 0.7745
Как видно из вышеприведенного вывода, когда количество эпох увеличивается, точность модели также увеличивается.
Построение убытков
plt.figure(figsize=(16,5)) epochs = range(1, len(history.history['accuracy'])+1) plt.plot(epochs, history.history['loss'], 'b', label='Training Loss', color='red') plt.plot(epochs, history.history['val_loss'], 'b', label='Validation Loss') plt.legend() plt.show()
Как видно из приведенного выше графика, потери при обучении и проверке уменьшаются с увеличением количества эпох. Мы видим, что процесс обучения дает хорошую кривую обучения.
Построение графиков точности
plt.figure(figsize=(16,5)) epochs = range(1, len(history.history['accuracy'])+1) plt.plot(epochs, history.history['accuracy'], 'b', label='Training Accuracy', color='red') plt.plot(epochs, history.history['val_accuracy'], 'b', label='Validation Accuracy') plt.legend() plt.show()
Как видно из приведенного выше графика, точность увеличивается с увеличением количества эпох. Но применение большего количества эпох приведет к переобучению. Мы можем остановить обучение при достижении точки точности обучения, а точность проверки будет стабильной.
Проверка
Мы можем проверить модель, используя пример предложения и проверить, может ли модель определить тональность данного предложения.
sentence = ['This movie was the worst. Acting was not good.'] sentence_tokened = tokenizer.texts_to_sequences(sentence) sentence_padded = pad_sequences(sentence_tokened,maxlen=128,truncating='post', padding='post') print(sentence[0]) print("Positivity of the sentence:{}".format(model.predict(sentence_padded)[0])) --Output-- This movie was the worst. Acting was not good. Positivity of the sentence: [0.14108086]
Как видно из вышеприведенных результатов, наша модель смогла идентифицировать предложение как предложение с низким рейтингом положительности, даже со словом «хорошо», включенным в него. Вы можете включить матрицу по вашему выбору, чтобы пометить вывод как положительный или отрицательный. Например, 0–40 - отрицательное, 40–60 - нейтральное и 60–100 - положительное.
Исходный код:
Заключение
Вы можете использовать разные модели с указанным выше руководством и проверить их точность. Так вы поймете, чем LSTM выделяется по сравнению с другими RNN. Но есть и более новые модели, такие как двунаправленный LSTM, BERT, EMO и т. Д., Которые могут обеспечить еще более высокие показатели точности.
В следующей статье мы попробуем двунаправленный подход LSTM и посмотрим, сможет ли он достичь более высокой точности, чем наш базовый LSTM.
Примечание редактора. Heartbeat - это онлайн-публикация и сообщество, созданное авторами и посвященное предоставлению первоклассных образовательных ресурсов для специалистов по науке о данных, машинному обучению и глубокому обучению. Мы стремимся поддерживать и вдохновлять разработчиков и инженеров из всех слоев общества.
Являясь независимой редакцией, Heartbeat спонсируется и публикуется Comet, платформой MLOps, которая позволяет специалистам по данным и группам машинного обучения отслеживать, сравнивать, объяснять и оптимизировать свои эксперименты. Мы платим участникам и не продаем рекламу.
Если вы хотите внести свой вклад, отправляйтесь на наш призыв к участникам. Вы также можете подписаться на наши еженедельные информационные бюллетени (Deep Learning Weekly и Comet Newsletter), присоединиться к нам в » «Slack и подписаться на Comet в Twitter и LinkedIn для получения ресурсов, событий и гораздо больше, что поможет вам быстрее создавать лучшие модели машинного обучения.