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.