Время прочтения: 6 мин.
В настоящий момент современное общество отводит большую роль в своей жизни общению в Интернете, где публикуется огромное количество различной информации, такой как обычные фотографии или отзывы на покупки, новые фильмы, качество полученных услуг и многое-многое другое. Вследствие этого часто появляются вопросы, связанные с получением оценки настроения высказывания. Поэтому задача создания модели, которая сможет понимать, является предложенный ей текст, например, позитивным или негативным, является сегодня довольно популярной.
Давайте представим, что у нас есть набор, например, отзывов на новый фильм или публикаций из социальной сети. А наша задача проанализировать каждый из них на предмет тональной оценки. Можно просматривать все вручную – это вариант, бесспорно. Только есть несколько «но». Даже на небольшой набор, порядка 100-200 записей, уже уйдет уйма времени, а кроме того всегда будет иметь место человеческий фактор, поскольку велика вероятность, о чем-то забыть, запутаться или что-то упустить. Можно ли как-то упростить эту задачу? Данным вопросом люди занимаются уже не один год, и было придумано несколько способов классификации, а именно: методы, использующие правила и тезаурусы (словари), в которых вручную или машинными методами произведен тональный анализ слов; машинное обучение с учителем и без него, а также методы, использующие теоретико-графовые модели — идея которых заключается в построении графа с ранжированием его вершин по весу, где вершинами являются слова из анализируемого текста.
Как вы уже могли заметить сфера применения тонального анализа довольно обширна, в Интернете в общем доступе находятся самые различные корпусы данных для обучения, например, датасеты отзывов с Amazon и Rotten Tomatoes, спам-письма, публикации из различных соц.сетей, записи дебатов и многое другое. Мы рассмотрим обучение модели с учителем на основе готового корпуса размеченных публикаций, собранных из социальной сети Twitter.
Корпус размещен на ресурсе study.mokoron.com, здесь вы можете найти данные в двух форматах — csv и sql. Для работы использовались публикации, сохраненные в формате csv. Далее для унификации требовалось решить задачу предобработки данных. Для начала необходимо было удалить все ненужные символы, пунктуацию и тд. Здесь удобно использовать регулярные выражения с импортом модуля re. Пример метода представлен ниже, здесь и далее в переменной data хранятся публикации, другими словами твиты:
def delete_rubbish(data):
return [re.sub('[^А-Яа-яё| ]', ' ', i) for i in data]
В речи мы часто используем много слов, не несущих никакой эмоциональной окраски, но для нас они могут быть нужны для лучшего понимания смысла высказывания. У модели же такой потребности нет, поэтому все подобные речевые единицы лучше удалять. Корпус таких стоп-слов можно найти в библиотеки для работы с естественным языком NLTK. Кроме того, в методе при обработке данных используется токенизация данных из этой же библиотеки. Токенизация — разбиение предложения на отдельные языковые единицы.
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
Кроме того, к стоп-словам можно отнести персональные имена. Набор персональных русских имен можно найти в Интернете.
def delete_stop_words(data):
#stopwords и PERSON_NAMES - заранее подготовленные наборы слов
stop_words = stopwords.words("russian")
names = PERSON_NAMES
result = []
for item in data:
# word_tokenize - токенизация
tokens = [token for token in word_tokenize(item)
if token not in stop_words
and token not in names
and token.isalpha() and len(token) > 1]
clear_data = " ".join(tokens)
result.append(clear_data)
return result
Переходим к следующему этапу – лемматизация, то есть приведение токенов к нормальной словарной форме. Поскольку для модели важна смысловая окраска слова, а не его форма. Например, оценка для токенов «прекрасный» и «прекрасная» должна быть одинаковая. Для лемматизации используется библиотека pymystem3 от Яндекс:
from pymystem3 import Mystem
Лемматизация каждого отдельного предложения довольно затратна по времени, так, например, чтобы обработать наш корпус публикаций понадобится порядка 2-3 часов, в лучшем случае. С целью ускорения этого процесса, данные объединяются в группы вида:
«Публикация1 flag Публикация2 flag … flag Публикация1000», где flag – служит разделителем, с помощью которого мы сможем обратно разбить наши данные. Размерность групп в примере кода ниже была равна 1000, но ее также можно менять под свои запросы:
def stemmer_lemmatizer(data):
my_stem = Mystem()
def function(lst, sz):
return [lst[i:i + sz] for i in range(0, len(lst), sz)]
tweet = function(data, 1000)
result = []
for temp in tweet:
all_tweets = ' '.join([txt + ' flag ' for txt in temp])
# лемматизация набора слов
words = my_stem.lemmatize(all_tweets)
current = []
for word in words:
if word != '\n' and word.strip() != '':
if word == 'flag':
result.append(current)
current = []
else:
current.append(word)
return result
Пример работы:
На этом этапе данные полностью предобработаны, теперь мы можем привести текстовый формат к численному представлению, чтобы начать обучение модели. Для этого воспользуемся методами библиотеки Keras (обеспечивает удобное взаимодействие с нейронными сетями, представляет собой надстройку над фреймворком TensorFlow для машинного обучения), такими как:
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequence
Значение переменных max_word и max_len выбирается в зависимости от задачи. Далее представлен код векторизации данных:
max_word = 5000
max_len = 200
tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(data)
sequences = tokenizer.texts_to_sequences(data)
tweets = pad_sequences(sequences, maxlen=max_len)
Также векторизуем значения лейблов-оценок:
labels = np.array(data['label'])
y = []
for i in range(len(labels)):
if labels[i] == 'neutral':
y.append(0)
elif labels[i] == 'negative':
y.append(1)
elif labels[i] == 'positive':
y.append(2)
y = np.array(y)
labels = tf.keras.utils.to_categorical(y, 3, dtype="float32")
del y
Разбиваем данные на тренировочный набор, на котором наша модель будет обучаться, и проверочный, на котором модель будет проверять точность обучения:
X_train, X_test, y_train, y_test = train_test_split(tweets,labels, random_state=0)
Далее представлено обучение нейронной сети с глубиной 1 на 70 эпохах, здесь мы добавляем необходимое количество слоев, проверяем точность на каждой эпохе и сохраняем модель с лучшей точностью:
model1 = Sequential()
model1.add(layers.Embedding(max_words, 20))
model1.add(layers.LSTM(15,dropout=0.5))
model1.add(layers.Dense(3,activation='softmax'))
model1.compile(optimizer='rmsprop',loss='categorical_crossentropy', metrics=['accuracy'])
checkpoint1 = ModelCheckpoint("best_model1.hdf5", monitor='val_accuracy', verbose=1,save_best_only=True, mode='auto', period=1,save_weights_only=False)
history = model1.fit(X_train, y_train, epochs=70,validation_data=(X_test, y_test),callbacks=[checkpoint1])
Аналогично обучение нейронной сети с глубиной 2 на 70 эпохах:
model2 = Sequential()
model2.add(layers.Embedding(max_words, 40, input_length=max_len))
model2.add(layers.Bidirectional(layers.LSTM(20,dropout=0.6)))
model2.add(layers.Dense(3,activation='softmax'))
model2.compile(optimizer='rmsprop',loss='categorical_crossentropy', metrics=['accuracy'])
checkpoint2 = ModelCheckpoint("best_model2.hdf5", monitor='val_accuracy', verbose=1,save_best_only=True, mode='auto', period=1,save_weights_only=False)
history = model2.fit(X_train, y_train, epochs=70,validation_data=(X_test, y_test),callbacks=[checkpoint2])
По итогу обучения были получены две модели с точностями работы ~65% (первая модель) и ~75%(вторая модель). Для дальнейшей работы, конечно, выбирается модель с большей точностью, в нашем случае это нейронная сеть с глубиной 2. Пример ее работы представлен ниже:
from keras.models import load_model
model = load_model("best_model2.hdf5")
result = model.predict(text)
Построение таких нейронных сетей в настоящий момент является одной из самых популярных задач, а как мы видим обучить свою нейронную сеть не так уж сложно, главное начать. Далее можно пробовать изменять и корректировать настройки. Либо можно добавить новые этапы предобработки, такие как проверка и исправление орфографии или выделение биграмм (устоявшихся словосочетаний) и др. Все эти изменения и нововведения могут привести к получению более точной модели.