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

Python для НЛП: Разработка автоматического текстового наполнителя с использованием N-граммов

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

Python для НЛП: Разработка автоматического текстового наполнителя с использованием N-граммов

Это 15 – я статья в моей серии статей по Python для НЛП. В моей предыдущей статье я объяснил , как реализовать TF-IDF подход с нуля в Python. До этого мы изучали, как реализовать bag of words подход с нуля в Python .

Сегодня мы изучим подход N-граммов и посмотрим, как его можно использовать для создания простого автоматического наполнителя текста или механизма внушения. Автоматический заполнитель текста-это очень полезное приложение, которое широко используется Google и различными смартфонами, где пользователь вводит какой-то текст, а оставшийся текст автоматически заполняется или предлагается приложением.

Проблемы с подходом TF-IDF и Bag of Words

Прежде чем мы перейдем к фактической реализации модели N-граммов, давайте сначала обсудим недостаток подхода bag of words и TF-IDF.

В подходе bag of words и TF-IDF слова обрабатываются индивидуально, и каждое отдельное слово преобразуется в его числовой аналог. Контекстная информация слова не сохраняется. Рассмотрим два предложения: “большая красная машина и ковер” и “большая красная ковровая дорожка и машина”. Если вы используете подход “мешок слов”, вы получите одинаковые векторы для этих двух предложений. Однако мы ясно видим, что в первом предложении речь идет о “большой красной машине”, в то время как во втором предложении содержится информация о “большой красной ковровой дорожке”. Следовательно, контекстная информация очень важна. Модель N-граммов в основном помогает нам фиксировать контекстную информацию.

Теория N-граммовой модели

Википедия определяет N-грамм как “непрерывную последовательность N элементов из заданного образца текста или речи”. Здесь элемент может быть символом, словом или предложением, а N может быть любым целым числом. Когда N равно 2, мы называем последовательность биграммой. Аналогично, последовательность из 3 элементов называется триграммой, и так далее.

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

Соединение N-грамм с цепями Маркова

Цепочка Маркова-это последовательность состояний. Рассмотрим марковскую систему с 2 состояниями X и Y. В цепочке Маркова вы можете либо остаться в одном состоянии, либо перейти в другое. В нашем примере наши состояния имеют следующее поведение:

  1. Вероятность перехода от X к Y равна 50%, и точно так же вероятность остаться в точке X равна 50%.
  2. Аналогично, вероятность остаться в Y составляет 50%, в то время как возможность вернуться в X также составляет 50%.

Таким образом, может быть сгенерирована последовательность Маркова, такая как XXYX и т. Д.

В модели N-граммов элемент последовательности может рассматриваться как марковское состояние. Давайте рассмотрим простой пример биграмм символов, где каждый символ является марковским состоянием.

Football is a very famous game

Биграммы символов для приведенного выше предложения будут следующими: fo , oo , ot , tb , ba , al , ll , l , i , is и так далее. Вы можете видеть, что биграммы в основном представляют собой последовательность из двух последовательно встречающихся символов.

Аналогично, триграммы представляют собой последовательность из трех смежных символов, как показано ниже:

foo , oot , ot , tba и так далее.

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

Давайте вернемся к нашему предыдущему примеру “большая красная машина и ковер”. Биграммой этого предложения будет “большой красный”, “красная машина”, “машина и”, “и ковер”. Аналогично, биграммы для предложения “большой красный ковер и машина” будут “большой красный”, “красный ковер”, “ковер и”, “и машина”.

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

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

N-Граммы с нуля в Python

В этом разделе мы создадим два типа N-граммовых моделей: символьную N-граммовую модель и словесную N-граммовую модель.

Символы N-Граммовая Модель

В этом разделе я объясню, как создать простую N-граммовую модель символов. В следующем разделе мы увидим, как реализовать модель word N-Gram.

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

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/Tennis')
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

article_text = article_text.lower()

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

article_text = re.sub(r'[^A-Za-z. ]', '', article_text)

Мы предварительно обработали наш набор данных, и теперь пришло время создать N-граммовую модель. Мы создадим модель триграммы символов. Выполните следующий сценарий:

ngrams = {}
chars = 3

for i in range(len(article_text)-chars):
    seq = article_text[i:i+chars]
    print(seq)
    if seq not in ngrams.keys():
        ngrams[seq] = []
    ngrams[seq].append(article_text[i+chars])

В приведенном выше скрипте мы создаем словарь n грамм . Ключами этого словаря будут символьные триграммы в нашем корпусе, а значениями-символы, встречающиеся рядом с триграммами. Далее, поскольку мы создаем N-грамм из трех символов, мы объявляем переменную chars . После этого мы перебираем все символы в нашем корпусе, начиная с четвертого символа.

Далее, внутри цикла, мы извлекаем триграмму, фильтруя следующие три символа. Триграмма хранится в переменной seq . Затем мы проверяем, существует ли триграмма в словаре. Если он не существует в словаре ngrams , мы добавляем триграмму в словарь. После этого мы присваиваем пустой список в качестве значения триграмме. Наконец, символ, существующий после триграммы, добавляется в список в качестве значения.

Если вы откроете словарь ngrams в проводнике переменных Spyder. Вы должны увидеть что-то вроде этого:

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

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

curr_sequence = article_text[0:chars]
output = curr_sequence
for i in range(200):
    if curr_sequence not in ngrams.keys():
        break
    possible_chars = ngrams[curr_sequence]
    next_char = possible_chars[random.randrange(len(possible_chars))]
    output += next_char
    curr_sequence = output[len(output)-chars:len(output)]

print(output)

В приведенном выше скрипте мы сначала сохраняем первую триграмму, то есть ten , в переменную current_sequence . Мы сгенерируем текст из двухсот символов, поэтому инициализируем цикл, который повторяется 200 раз. Во время каждой итерации мы проверяем, находится ли curr_sequence или триграмма в словаре ngrams . Если триграмма не найдена в словаре ngrams , мы просто выходим из цикла.

Затем триграмма curr_sequence передается в качестве ключа в словарь ngrams , который возвращает список возможных следующих символов. Из списка возможных следующих символов случайным образом выбирается индекс, который передается в список possible_chars , чтобы получить следующий символ для текущей триграммы. Затем следующий символ добавляется к переменной output , содержащей конечный вывод.

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

Выход:

tent pointo somensiver tournamedal pare the greak in the next peak sweder most begal tennis sport. the be has siders with sidernaments as was that adming up is coach rackhanced ball of ment. a game and

В данном случае вывод не имеет особого смысла. Если вы увеличите значение переменной chars до 4. Вы должны увидеть результаты, аналогичные следующим выводам:

tennis ahead with the club players under.most coaching motion us . the especific at the hit and events first predomination but of ends on the u.s. cyclops have achieved the end or net inches call over age

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

В следующем разделе мы реализуем модель слов N-граммов. Вы увидите, что сгенерированный текст будет иметь гораздо больше смысла в случае слов N-граммовой модели.

Слова N-Граммовая Модель

В модели Words N-Grams каждое слово в тексте рассматривается как отдельный элемент. В этом разделе мы реализуем модель Word N-Grams и будем использовать ее для создания автоматического текстового наполнителя.

Набор данных, который мы собираемся использовать, совпадает с тем, который мы использовали в предыдущем разделе.

Давайте сначала создадим словарь, содержащий триграммы слов в качестве ключей и список слов, которые встречаются после триграмм в качестве значений.

ngrams = {}
words = 3

words_tokens = nltk.word_tokenize(article_text)
for i in range(len(words_tokens)-words):
    seq = ' '.join(words_tokens[i:i+words])
    print(seq)
    if  seq not in ngrams.keys():
        ngrams[seq] = []
    ngrams[seq].append(words_tokens[i+words])

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

Затем мы перебираем все слова, а затем соединяем текущие три слова, чтобы сформировать триграмму. После этого мы проверяем, существует ли слово триграмма в словаре ngrams . Если триграмма еще не существует, мы просто вставляем ее в словарь ngrams в качестве ключа.

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

Теперь, если вы посмотрите на словарь ngrams в проводнике переменных, он будет выглядеть следующим образом:

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

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

curr_sequence = ' '.join(words_tokens[0:words])
output = curr_sequence
for i in range(50):
    if curr_sequence not in ngrams.keys():
        break
    possible_words = ngrams[curr_sequence]
    next_word = possible_words[random.randrange(len(possible_words))]
    output += ' ' + next_word
    seq_words = nltk.word_tokenize(output)
    curr_sequence = ' '.join(seq_words[len(seq_words)-words:len(seq_words)])

print(output)

В приведенном выше сценарии мы инициализируем переменную curr_sequence первой триграммой в корпусе. Первая триграмма – “теннис-это а”. Мы сгенерируем 50 слов, используя первую триграмму в качестве входных данных. Для этого мы выполняем цикл for, который выполняется 50 раз. Во время каждой итерации сначала проверяется, существует ли слово триграмма в словаре ngrams . Если нет, петля разрывается. В противном случае список слов, которые, вероятно, будут следовать за триграммой, извлекается из словаря ngrams путем передачи триграммы в качестве значения. Из списка возможных слов одно слово выбирается случайным образом и добавляется в конце выхода. Наконец, переменная curr_sequence обновляется значением следующей триграммы в словаре.

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

Выход:

tennis is a racket sport that can be played individually against a single opponent singles or between two teams of two players each doubles. each player uses a tennis racket include a handle known as the grip connected to a neck which joins a roughly elliptical frame that holds a matrix of

Если вы установите значение переменной words равным 4 (используйте 4 грамма) для генерации текста, ваш вывод будет выглядеть еще более надежным, как показано ниже:

tennis is a racket sport that can be played individually against a single opponent singles or between two teams of two players each doubles . each player uses a tennis racket that is strung with cord to strike a hollow rubber ball covered with felt over or around a net and into the opponents

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

Вывод

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