Этот рассказ является частью серии «Классификация текстов — от Bag-of-Words до BERT». Если вы не читали предыдущую историю, прочтите ее, так как это поможет понять, что будет дальше.
В более ранней истории (Часть 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 (BagOfWords))
- Делайте прогнозы по векторам встраивания проверки
- Измеряйте производительность с точки зрения 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:
- Обучить модель Word2Vec с нуля
- Попробуйте ансамблевые модели вместо моделей Vanilla ML. В большинстве случаев модели пакетирования и бустинга дают лучшие результаты, чем классические методы ML.
- Улучшенная предварительная обработка текста Исправление опечаток и т. д. может быть выполнено для дальнейшего улучшения модели.
Это было о Word2Vec, в следующем речь пойдет о fastText от Facebook, который продвигает идею встраивания слов на один шаг вперед, реализуя то, что называется встраиванием подслов. Оставайтесь в безопасности до тех пор. Опять же весь код присутствует (здесь). Пожалуйста, оставляйте свои отзывы в виде ответов и аплодисментов :)