Этот рассказ является частью серии «Классификация текстов — от Bag-of-Words до BERT». Если вы не читали предыдущую историю, прочтите ее, так как это поможет понять, что будет дальше.

Часть 1 (BagOfWords)

В более ранней истории (Часть 1 (BagOfWords) мы использовали модель CountVectorizer (sklearn-реализация Bag-of-Words) для преобразования текстов в числовой набор данных, сопоставленный с выходными переменными токсичными, серьезными_токсичными, непристойными, угрозами, оскорблениями. ,identity_hate и использовали оболочку Multi-Output Classifier от sklearn для создания моделей логистической регрессии для всех 6 выходных переменных.

В этом мы заменим первую часть моделью Word2Vec, чтобы создать вложение вместо вектора BagOfWords, а затем введем его в модель логистической регрессии (любая модель ML/DL может быть построена поверх встраивания Word2Vec).

Примечание: я не рассказывал подробности о логистической регрессии и важности функций/интерпретации модели в этом блоге, поскольку я рассказал об этом в прошлой статье (Часть 1 (BagOfWords))

Что такое встраивание Word?

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

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

Что такое Word2Vec?

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

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

Модель непрерывного пропуска предсказывает слова в определенном диапазоне до и после текущего слова в том же предложении.

В CBOW, учитывая слова (быстрый коричневый ящик над ленивой собакой), мы хотели бы предсказать прыжок. В Skipgram как раз наоборот, учитывая слово «прыжок», мы хотели бы предсказать (быстрый коричневый ящик вместо ленивой собаки).

Но как учатся модели?

Начнем с CBOW, возьмем предложение «Обработка естественного языка», где и «Естественный», и «Обработка» являются контекстными словами, а «Язык» — целевым словом. У нас есть неглубокая сеть, как показано выше, с одним скрытым слоем.

Таким образом, ввод представляет собой закодированный в горячем режиме вектор из V терминов (размер словарного запаса / общее количество уникальных слов) только с одной единицей. Итак, допустим, у нас есть только 5 слов в словаре (естественный, язык, обработка, есть, отличный ). Вектор для Natural будет [1, 0, 0, 0, 0]. Точно так же для обработки это будет [0, 0, 1, 0, 0]. Теперь у нас есть случайно инициализированный вектор встраивания (E) с размером V * D, где D — размерность вектора, который вы можете выбрать. Это весовая матрица для входного слоя. Итак, мы умножаем входной вектор горячего кодирования на вектор весов/встраивания. Это дает векторы встраивания для контекстных слов (естественных и обрабатываемых) размером 1D.

Теперь в скрытом слое мы усредняем векторы встраивания для контекстных слов, которые формируют входные данные для этого слоя размером 1* D. Это умножается на другой вектор, называемый вектором контекста (E') с размером D * V. Это дает нам вектор 1 * V, который затем передается через сигмовидную функцию для получения окончательного результата.

Окончательный результат сравнивается с вектором языка с горячим кодированием (среднее слово) [0, 1, 0, 0, 0] и вычисляется функция потерь. Эта потеря распространяется обратно, и модель обучается с использованием градиентного спуска.

Для Skip-gram все наоборот, у нас есть горячий закодированный вектор для среднего слова, который умножается на вектор весов/вложения E = V * D. Мы получаем вложение для среднего слова как результат входной слой и вход для скрытого слоя. Это умножается на E’ = D * V, который является вектором контекста, и мы получаем вывод, который проходит через сигмоид и сравнивается со словами контекста, чтобы получить потери и обратное распространение.

В обоих случаях мы просто сохраняем вектор Embedding(E) в конце

Как мы будем получать вложения?

Библиотека Gensim позволяет нам разрабатывать вложения слов. Gensim дает вам возможность выбрать CBOW или Skip-gram при обучении собственных вложений. (по умолчанию CBOW). Наряду с этим в Gensim также есть каталог предварительно обученных вложений, которые обучены на нескольких документах, таких как вики-страницы, новости Google, твиты в Твиттере и т. д. В этом примере мы будем использовать предварительно обученное встраивание, основанное на Новостях Google. корпус (3 миллиарда бегущих слов) модель вектора слов (3 миллиона 300-мерных векторов английских слов). Ладно, хватит определений. Давайте углубимся в код 👨‍💻

Реализация:

1. Чтение набора данных

2. Базовая предварительная обработка

def preprocess_corpus(texts):
    #importing stop words like in, the, of so that these can be removed from texts
    #as these words dont help in determining the classes(Whether a sentence is toxic or not)
    mystopwords = set(stopwords.words("english"))
    def remove_stops_digits(tokens):
        #Nested function that lowercases, removes stopwords and digits from a list of tokens
        return [token.lower() for token in tokens if token not in mystopwords and not token.isdigit()
               and token not in punctuation]
    #This return statement below uses the above function and tokenizes output further. 
    return [remove_stops_digits(word_tokenize(text)) for text in tqdm(texts)]

#Preprocess both for training and test data
train_texts_processed = preprocess_corpus(train_texts)
test_texts_processed = preprocess_corpus(test_texts)

В этом случае мы удаляем стоп-слова и целые цифры, переводим все тексты в нижний регистр и токенизируем (разбиваем на отдельные токены/слова) тексты с помощью word_tokenize из библиотеки NLTK.

3. Загружать предварительно обученные встраивания

Мы используем библиотеку Gensim для загрузки предварительно обученных вложений слов, обученных на наборе данных Google News. Вектор модели/встраивания Новостей Google имеет 300 измерений. Вектор модели/встраивания Goggle News содержит около 3 млн слов. Давайте также рассмотрим пример встраивания, которое по сути представляет собой словарь, где ключ — это слово, а значение — вектор вложения для этого слова.

#Path for the models/ embedding vector
google_news_model = '../input/gensim-embeddings-dataset/GoogleNews-vectors-negative300.gensim'
#Loading the models/ embedding vector using KeyedVectors.load function from gensim
w2v_google_news = KeyedVectors.load(google_news_model)
#Print Shape of the embedding
print("Shape of embedding vector", w2v_google_news["Natural"].shape)
#Let's print first 20 dimensions rather than all 300
print("First 20 numbers in the embedding of the word Natural\n\n", w2v_google_news["Natural"][:20])

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

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

#Function that takes in the input text dataset in form of list of lists where each sentence is a list of words all the sentences are 
#inside a list
def embedding_feats(list_of_lists, DIMENSION, w2v_model):
    zeros_vector = np.zeros(DIMENSION)
    feats = []
    missing = set()
    missing_sentences = set()
    #Traverse over each sentence
    for tokens in tqdm(list_of_lists):
        # Initially assign zeroes as the embedding vector for the sentence
        feat_for_this = zeros_vector
        #Count the number of words in the embedding for this sentence
        count_for_this = 0
        #Traverse over each word of a sentence
        for token in tokens:
            #Check if the word is in the embedding vector
            if token in w2v_model:
                #Add the vector of the word to vector for the sentence
                feat_for_this += w2v_model[token]
                count_for_this +=1
            #Else assign the missing word to missing set just to have a look at it
            else:
                missing.add(token)
        #If no words are found in the embedding for the sentence
        if count_for_this == 0:
            #Assign all zeroes vector for that sentence
            feats.append(feat_for_this)
            #Assign the missing sentence to missing_sentences just to have a look at it
            missing_sentences.add(' '.join(tokens))
        #Else take average of the values of the embedding for each word to get the embedding of the sentence
        else:
            feats.append(feat_for_this/count_for_this)
    return feats, missing, missing_sentences
#Embeddings for the train dataset
train_vectors, missing, missing_sentences = embedding_feats(train_texts_processed, 300, w2v_google_news)

Подводя итог, каждое предложение будет иметь один 300-мерный вектор встраивания, который будет средним значением вложений слов, присутствующих в этом предложении. Вложения слов взяты из предварительно обученных вложений слов, которые были обучены на новостях Google, чтобы найти вложение.

5. Обучение и проверка многовыходного классификатора

Этот сегмент будет посвящен 5 вещам

  1. Получение вектора встраивания для обучающего набора данных
  2. Разделите вектор встраивания и выходные переменные на набор поезда и проверки.
  3. Подгонка модели логистической регрессии с несколькими выходами к обучению вектору внедрения и выходным переменным (подробности логистической регрессии я рассмотрел в предыдущей статье (Часть 1 (BagOfWords))
  4. Делайте прогнозы по векторам встраивания проверки
  5. Измеряйте производительность с точки зрения ROC-AUC
def train_model(DIMENSION, model):
    #Get the embedding vector for the training data
    train_vectors, missing, missing_sentences = embedding_feats(train_texts_processed, DIMENSION, model)
    #Split the embedding vector for the training data along with the output variables into train and validation sets
    train_data, val_data, train_cats, val_cats = train_test_split(train_vectors, train_labels)
    #Logistic Regression Model (As we have unbalanced dataset, we use class_weight which will use inverse of counts of that class. It penalizes mistakes in samples of class[i] with class_weight[i] instead of 1)
    lr = MultiOutputClassifier(LogisticRegression(class_weight='balanced', max_iter=3000)).fit(train_data, train_cats)
    #Actuals for the validation data
    y_vals = val_cats
    #Prediction probability for the validation dataset by the model for class 1
    y_preds = np.transpose(np.array(lr.predict_proba(val_data))[:,:,1])
    #Calculate the Mean ROC_AUC 
    mean_auc = mean(accuracy(y_vals,y_preds))
    return mean_auc, lr
mean_auc, lr = train_model(300, w2v_google_news)

Эта модель оказалась довольно умеренной (~0,60 ROC-AUC). Но опять же, цель состояла в том, чтобы научиться реализовывать встраивания слов. Низкая производительность может быть связана с тем, что предварительно обученные встраивания неправильно фиксируют детали. Вместо этого мы могли бы обучить собственное встраивание с помощью Word2Vec.

TODO:

  1. Обучить модель Word2Vec с нуля
  2. Попробуйте ансамблевые модели вместо моделей Vanilla ML. В большинстве случаев модели пакетирования и бустинга дают лучшие результаты, чем классические методы ML.
  3. Улучшенная предварительная обработка текста Исправление опечаток и т. д. может быть выполнено для дальнейшего улучшения модели.

Это было о Word2Vec, в следующем речь пойдет о fastText от Facebook, который продвигает идею встраивания слов на один шаг вперед, реализуя то, что называется встраиванием подслов. Оставайтесь в безопасности до тех пор. Опять же весь код присутствует (здесь). Пожалуйста, оставляйте свои отзывы в виде ответов и аплодисментов :)