Рубрики
Без рубрики

Python для НЛП: Создание модели мешка слов с нуля

Автор оригинала: Usman Malik.

Python для НЛП: Создание модели мешка слов с нуля

Это 13-я статья в моей серии статей по Python для НЛП. В предыдущей статье мы видели , как создать простой чат-бот на основе правил, который использует косинусное сходство между векторами TF-IDF слов в корпусе и пользовательским вводом для генерации ответа. Модель TF-IDF в основном использовалась для преобразования слов в числа.

В этой статье мы рассмотрим еще одну очень полезную модель, которая преобразует текст в числа, то есть Мешок слов (ЛУК).

Поскольку большинство статистических алгоритмов, например машинное обучение и методы глубокого обучения, работают с числовыми данными, поэтому нам приходится преобразовывать текст в числа. В этом отношении существует несколько подходов. Однако самые известные из них-Bag of Words, TF-IDF и word2vec. Благодаря нескольким существующим библиотекам, таким как Scikit-Learn и NLTK, которые могут реализовать эти методы в одной строке кода, важно понять принцип работы этих методов встраивания слов. Лучший способ сделать это-реализовать эти методы с нуля в Python и

В этой статье мы увидим, как реализовать подход Bag of Words с нуля в Python. В следующей статье мы увидим, как реализовать подход TF-IDF с нуля в Python.

Прежде чем приступить к кодированию, давайте сначала рассмотрим теорию, лежащую в основе подхода “мешок слов”.

Теория, лежащая в основе подхода “Мешок слов”

Чтобы понять подход “мешка слов”, давайте сначала начнем с примера.

Предположим, у нас есть корпус из трех предложений:

  • “Я люблю играть в футбол”
  • – Вы вышли на улицу поиграть в теннис?”
  • – Мы с Джоном играем в теннис.”

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

Шаг 1: Маркируйте предложения

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

I Сделал Джон
любить ты и
к идти I
играть снаружи играть
футбол к теннис
играть
теннис

Шаг 2: Создайте словарь частоты слов

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

I 2
любить 1
к 2
играть 3
футбол 1
Сделал 1
ты 1
идти 1
снаружи 1
теннис 2
Джон 1
и 1

В приведенной выше таблице вы можете увидеть каждое слово в нашем корпусе вместе с его частотой встречаемости. Например, вы можете видеть, что, поскольку слово play встречается в корпусе три раза (один раз в каждом предложении), его частота равна 3.

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

Давайте разберем наш частотный словарь слов:

играть 3
теннис 2
к 2
I 2
футбол 1
Сделал 1
ты 1
идти 1
снаружи 1
любить 1
Джон 1
и 1

Шаг 3: Создание модели Мешка слов

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

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

1 1 0 0 0 1 0 1 Предложение 1
0 1 1 1 1 1 1 0 Предложение 2
1 0 0 0 0 1 1 0 Предложение 3

Важно понять, как создается вышеприведенная матрица. В приведенной выше матрице первая строка соответствует первому предложению. В первом случае слово “играть” встречается один раз, поэтому мы добавили 1 в первую колонку. Слово во второй колонке – “Теннис”, оно не встречается в первом предложении, поэтому мы добавили 0 во вторую колонку для предложения 1. Аналогично, во втором предложении оба слова “Играть” и “Теннис” встречаются один раз, поэтому мы добавили 1 в первые две колонки. Однако в пятой колонке мы добавим 0, так как слово “Футбол” не встречается во втором предложении. Таким образом, все ячейки в приведенной выше матрице заполняются либо 0, либо 1, в зависимости от вхождения слова. Конечная матрица соответствует модели мешка слов.

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

Хватит теории, давайте реализуем нашу собственную модель мешка слов с нуля.

Модель мешка слов в Python

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

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

import nltk  
import numpy as np  
import random  
import string

import bs4 as bs  
import urllib.request  
import re  

Как и в предыдущей статье, мы будем использовать библиотеку Beautifulsoup4 для анализа данных из Википедии. Кроме того, библиотека регулярных выражений Python , re , будет использоваться для некоторых задач предварительной обработки текста.

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

raw_html = urllib.request.urlopen('https://en.wikipedia.org/wiki/Natural_language_processing')  
raw_html = raw_html.read()

article_html = bs.BeautifulSoup(raw_html, 'lxml')

article_paragraphs = article_html.find_all('p')

article_text = ''

for para in article_paragraphs:  
    article_text += para.text

В приведенном выше скрипте мы импортируем необработанный HTML-код для статьи Википедии. Из необработанного HTML мы фильтруем текст внутри текста абзаца. Наконец, мы создаем полный корпус, объединяя все абзацы.

Следующий шаг-разбить корпус на отдельные предложения. Для этого мы будем использовать функцию sent_tokenize из библиотеки NLTK.

corpus = nltk.sent_tokenize(article_text)

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

Посмотрите на следующий сценарий:

for i in range(len(corpus )):
    corpus [i] = corpus [i].lower()
    corpus [i] = re.sub(r'\W',' ',corpus [i])
    corpus [i] = re.sub(r'\s+',' ',corpus [i])

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

Давайте выясним количество предложений в нашем корпусе.

print(len(corpus))

Вывод показывает 49.

Давайте напечатаем одно предложение из нашего корпуса:

print(corpus[30])

Выход:

in the 2010s representation learning and deep neural network style machine learning methods became widespread in natural language processing due in part to a flurry of results showing that such techniques 4 5 can achieve state of the art results in many natural language tasks for example in language modeling 6 parsing 7 8 and many others 

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

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

wordfreq = {}
for sentence in corpus:
    tokens = nltk.word_tokenize(sentence)
    for token in tokens:
        if token not in wordfreq.keys():
            wordfreq[token] = 1
        else:
            wordfreq[token] += 1

В приведенном выше скрипте мы создали словарь под названием word freq . Далее мы повторяем каждое предложение в корпусе. Предложение маркируется словами. Далее мы повторяем каждое слово в предложении. Если слово не существует в словаре wordfreq , мы добавим слово в качестве ключа и установим значение слова как 1. В противном случае, если слово уже существует в словаре, мы просто увеличим количество ключей на 1.

Если вы выполняете описанное выше в редакторе Spyder , как и я, вы можете перейти в проводник переменных справа и нажать wordfreq variable. Вы бы видели такой словарь:

Вы можете увидеть слова в столбце “Ключ” и частоту их появления в столбце “Значение”.

Как я уже говорил в разделе теории, в зависимости от поставленной задачи не все слова полезны. В огромных корпусах вы можете иметь миллионы слов. Мы можем фильтровать наиболее часто встречающиеся слова. Всего в нашем корпусе 535 слов. Давайте отфильтруем до 200 наиболее часто встречающихся слов. Для этого мы можем использовать библиотеку Python heap .

Посмотрите на следующий сценарий:

import heapq
most_freq = heapq.nlargest(200, wordfreq, key=wordfreq.get)

Теперь наш список most_freq содержит 200 наиболее часто встречающихся слов вместе с их частотой встречаемости.

Последний шаг-преобразование предложений в нашем корпусе в соответствующее векторное представление. Идея проста: для каждого слова в словаре most_freq , если слово существует в предложении, будет добавлено 1 для слова, иначе будет добавлено 0.

sentence_vectors = []
for sentence in corpus:
    sentence_tokens = nltk.word_tokenize(sentence)
    sent_vec = []
    for token in most_freq:
        if token in sentence_tokens:
            sent_vec.append(1)
        else:
            sent_vec.append(0)
    sentence_vectors.append(sent_vec)

В приведенном выше скрипте мы создаем пустой список sentence_vectors , который будет хранить векторы для всех предложений в корпусе. Далее мы перебираем каждое предложение в корпусе и создаем пустой список sent_vec для отдельных предложений. Аналогично, мы также маркируем предложение. Затем мы перебираем каждое слово в списке most_freq и проверяем, существует ли это слово в токенах предложения. Если слово является частью предложения, то к отдельному вектору предложения добавляется 1 set_vec , иначе добавляется 0. Наконец, вектор предложения добавляется в список sentence_vectors , который содержит векторы для всех предложений. В принципе, это sentence_vectors – наша модель мешка слов.

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

sentence_vectors = np.asarray(sentence_vectors)

В основном, в следующем скрипте мы преобразовали наш список в двумерный массив numpy с помощью функции asarray . Теперь, если вы откроете переменную sentence_vectors в проводнике переменных редактора Spyder, вы увидите следующую матрицу:

Вы можете увидеть модель Мешка слов, содержащую 0 и 1.

Вывод

Модель Bag of Words-это один из трех наиболее часто используемых подходов к встраиванию слов, а TF-IDF и Word2Vec-два других.

В этой статье мы увидели, как реализовать подход Bag of Words с нуля в Python. Теория подхода была объяснена вместе с практическим кодом для реализации этого подхода. В следующей статье мы увидим , как реализовать подход TF-IDF с нуля в Python.