Введение

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

Методы

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

"Paul likes to read books. Jennifer likes to read books too."
"Paul likes to watch movies."
BOW1 = {'Paul':1,'Jennifer':1,'likes':2,'to':2,'read':2,'books':2,'too':1}
BOW2 = {'Paul':1,'likes':1,'to':1,'watch':1,'movies':1} 

Чтобы дополнительно проиллюстрировать использование этих методов, мы поэкспериментируем с анализом классического набора данных обзоров IMDB, чтобы классифицировать каждый комментарий как положительный или отрицательный. Сначала нам нужно немного очистить текстовые данные. Мы удалим все html-теги, знаки препинания, цифры и стоп-слова (мы используем список стоп-слов из корпуса nltk). После очистки данных теперь мы можем применить метод набора слов CountVectorizer из scikit-learn, чтобы использовать представление количества слов в качестве наших входных данных в модель классификации настроений:

from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(analyzer = ‘word’,
 tokenizer = None,
 preprocessor = None,
 stop_words = None,
 max_features = 5000)
train_data_features = vectorizer.fit_transform(clean_train_reviews)

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

Другой метод получения числового представления текстовых данных — использование векторов слов: word2vec. Есть две популярные архитектуры векторов слов, которые люди любят использовать. Первый называется непрерывный набор слов (CBOW), а второй называется скип-грамм. Основное различие заключается в следующем: CBOW использует контекст, окружающий целевое слово, для предсказания слова, в то время как skip-gram использует целевое слово для предсказания окружающего его контекста, как показано на графиках ниже:

Если вам интересно узнать больше о технических деталях, я настоятельно рекомендую эту статью Объяснение обучения параметрам word2vec [1] Синь Ронга, поскольку она содержит все математические выводы для каждой из моделей. выше. В этом посте я в основном сосредоточусь на некоторых методах обработки текстовых данных и реализации, а не на математических выводах.

Нам также нужно будет очистить данные, однако может быть предпочтительнее не удалять стоп-слова, поскольку большее количество слов поможет создать лучшие векторы слов. Входными данными для Word2Vec должен быть список списков, каждый из которых состоит из слов. Мы будем использовать punkt из NLTK для анализа предложений в наших текстовых данных:

def review_to_wordlist(review, remove_stopwords=False):
    review_text = BeautifulSoup(review, 'lxml').get_text()
    review_text = re.sub("[^a-zA-Z]", " ", review_text)
    words = review_text.lower().split()
    if remove_stopwords:
        stops = set(stopwords.words('english'))
        words = [w for w in words if w not in stops]
    return (words)
import nltk.data
def review_to_sentences(review, tokenizer, remove_stopwords=False):
    # Fucntion to split a review into parsed sentences. Return a 
    # list of sentences, where each sentence is a list of words
    raw_sentences = tokenizer.tokenize(review.strip())
    sentences = []
    for raw_sentence in raw_sentences:
        if len(raw_sentence) > 0:
            sentences.append(review_to_wordlist(raw_sentence, remove_stopwords))
    return sentences

Мы будем использовать word2vec от gensim для построения нашей модели классификации. Есть пара параметров, на которые нам нужно обратить внимание:

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

num_of_features: размерность векторов слов, вы можете выбрать что угодно от десятков до сотен. Здесь 300 работает хорошо.

min_word_count: это помогает ограничить размер словаря значимыми словами. Любые слова, число которых меньше установленного здесь предела, будут игнорироваться. Подойдет любое значение от 10 до 100.

контекст: размер окна контекста. Именно столько слов контекста будет учитывать обучающий алгоритм.

понижение частоты дискретизации: это понижение частоты часто используемых слов, и Google рекомендует значения от 0,00001 до 0,001.

# Set values for various parameters
num_features = 300   # Word vector dimensionality
min_word_count = 40  # Minimum word count
num_workers = 4      # Number of threads to run in parallel
context = 10         # Context window size
downsampling = 1e-3  # Downsample setting for frequent words
# Initialize and train the model
from gensim.models import word2vec
print("Training model...")
model = word2vec.Word2Vec(sentences, workers=num_workers,
                          size=num_features, min_count=min_word_count,
                          window=context, sample=downsampling)
model.init_sims(replace=True)
# save the model for later use
model_name = "300features_40minwords_10context"
model.save(model_name)

Теперь, когда модель готова, как мы применим нашу модель для генерации значений входных признаков для построения модели классификации настроений? Есть два способа сделать это:

  1. Усреднение векторов слов

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

Каждое слово имеет 300-мерный набор признаков, мы будем использовать усреднение для объединения слов в каждом комментарии. Мы удалим стоп-слова в этой обработке, так как они будут вносить шум в усреднение.

import numpy as np
def makeFeatureVec(words, model, num_features):
    # Function to average all of the word vectors in a given paragraph
    featureVec = np.zeros((num_features,), dtype='float32')
    nwords = 0
    index2word_set = set(model.wv.index2word)
    for word in words:
        if word in index2word_set:
            nwords += 1
            featureVec = np.add(featureVec, model[word])
    # Divide the result by the number of words to get the average
    featureVec = np.divide(featureVec, nwords)
    return featureVec
def getAvgFeatureVecs(reviews, model, num_features):
    # Given a set of reviews (each one a list of words), calculate
    # the average feature vector for each one and return a 2D numpy array
    counter = 0
    reviewFeatureVecs = np.zeros((len(reviews), num_features), dtype="float32")
    for review in reviews:
        if counter%1000 == 0:
            print("Review %d of %d" % (counter, len(reviews)))
        reviewFeatureVecs[counter] = makeFeatureVec(review, model, num_features)
        counter += 1
    return reviewFeatureVecs

2. Кластеризация векторов слов

Мы также могли бы использовать числовые значения в векторах слов, чтобы найти сходство и, таким образом, сгруппировать слова на основе сходства. Для этого мы могли бы использовать K-Means. Значение К установлено равным 5 после пары экспериментов.

from sklearn.cluster import KMeans

# Set "k" (num_clusters) to be 1/5th of the vocabulary size,
# or an average of 5 words per cluster
word_vectors = model.wv.syn0
num_clusters = word_vectors.shape[0] // 5
# Initialize a k-means object and use it to extract centroids
kmeans_clustering = KMeans(n_clusters=num_clusters, n_jobs=-2)
idx = kmeans_clustering.fit_predict(word_vectors)

Затем, основываясь на категоризации, мы могли преобразовать текстовые данные обзоров в пакет центроидов, подобно представлению набора слов. Однако теперь мы используем сгруппированные слова вместо отдельных слов.

def create_bag_of_centroids(wordlist, word_centroid_map):
    # The number of clusters is equal to the highest cluster index
    # in the word / centroid map
    num_centroids = max(word_centroid_map.values()) + 1
    # Pre-allocate the bag of centroids vector (for speed)
    bag_of_centroids = np.zeros(num_centroids, dtype="float32")
    
    # Loop over the words in the review. If the word is in the vocabulary, 
    # find which cluster it belongs to, and increment that cluster count by one
    for word in wordlist:
        if word in word_centroid_map:
            index = word_centroid_map[word]
            bag_of_centroids[index] += 1
    
    return bag_of_centroids

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

Результаты

Я проверил предсказания всех трех методов, и вот результаты:

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

Спасибо за чтение и не стесняйтесь давать мне любые отзывы!

Использованная литература:

  1. https://arxiv.org/abs/1411.2738, Синь Ронг, Мичиганский университет
  2. https://www.kaggle.com/varun08/sentiment-analysis-using-word2vec, анализ настроений с использованием word2vec