Создание текста на шекспировском английском с помощью цепей Маркова

Это мягкое введение в цепи Маркова для генерации текста. Мы собираемся обучить модель с использованием библиотеки Markovify на трех самых известных трагедиях Уильяма Шекспира и посмотреть, будет ли то, что мы создаем, разборчиво и связно. Я понимаю, что эти два термина несколько нечеткие, но я чувствую, что большинство людей поймут мое намерение при их использовании, особенно когда они увидят вариации в создаваемом тексте.

Markovify - это библиотека Python, которая позиционирует себя как простой расширяемый генератор цепей Маркова. Использование включает создание случайных полуправдоподобных предложений на основе существующего текста . И, надо признать, пользоваться им невероятно легко и быстро. Сами по себе цепи Маркова - изящные творения, которые дают вероятности сохранения и изменения для процессов с несколькими состояниями. Я не буду здесь углубляться в математику цепей Маркова, но не стесняйтесь ссылаться на это и это для всестороннего обзора и визуализаций соответственно. Для наших целей я объясню цепи Маркова наглядно.

Глядя на изображение выше, мы видим, что у нас есть три возможных состояния; Облачно, дождливо и солнечно. Цепи Маркова полагаются на текущее состояние, чтобы предсказать будущий результат. Если мы заметим, что сегодня дождь, наши вероятности таковы: вероятность того, что завтра все еще идет дождь, составляет 60%, вероятность того, что будет облачно, составляет 30%, а вероятность того, что будет солнечно, составляет 10%. Та же логика может быть применена, когда мы начинаем с облачного и солнечного состояний.

Так как, черт возьми, это работает с текстом? По сути, каждое слово в нашем корпусе связано со всеми остальными словами с различной вероятностью с использованием цепей Маркова. Итак, если наше начальное слово (состояние) - Ты, Markovify присваивает вероятность каждому второму слову в нашем корпусе того, насколько вероятно, что оно будет следовать за нашим начальным словом. Он может иметь Должен с вероятностью 65% следовать за Ты, а также будет с 20%, может с 10% и так далее и так далее для всего нашего корпуса, который будет составлять последние 5%. . Обратите внимание, что вероятность того, что Ты последует за собой, должна быть близка к 0%, поскольку такое повторение слова не имеет особого смысла и верно почти для всех слов. Чтобы получить более подробные сведения, ознакомьтесь с разделом Фильмы, показатели и размышления здесь.

Создание текста

Итак, мы, наконец, готовы реализовать Markovify для генерации текста. Вы можете найти мою записную книжку Colab здесь на Github. Сначала нам нужно установить наши библиотеки и пакеты.

!pip install nltk
!pip install spacy
!pip install markovify
!pip install -m spacy download en

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

import spacy
import re
import markovify
import nltk
from nltk.corpus import gutenberg
import warnings
warnings.filterwarnings('ignore')
nltk.download('gutenberg')
!python -m spacy download en

Для этой демонстрации мы собираемся использовать три трагедии Шекспира из корпуса Project Gutenberg NLTK. Сначала мы напечатаем все документы в корпусе Gutenberg, чтобы вы могли смешивать и сопоставлять их по своему усмотрению.

#inspect Gutenberg corpus
print(gutenberg.fileids())

Для этой демонстрации мы будем использовать три трагедии Шекспира «Макбет», «Юлий Цезарь» и «Гамлет». Итак, теперь мы импортируем их и проверим текст.

#import novels as text objects
hamlet = gutenberg.raw('shapespeare-hamlet.txt')
macbeth = gutenberg.raw('shakespeare-macbeth.txt')
caesar = gutenberg.raw('shakespeare-caesar.txt')
#print first 100 characters of each
print('\nRaw:\n', hamlet[:100])
print('\nRaw:\n', macbeth[:100])
print('\nRaw:\n', caesar[:100])

Затем мы создадим служебную функцию для очистки текста с помощью библиотеки re. Эта функция удалит ненужные пробелы и отступы, знаки препинания и т. Д.

#utility function for text cleaning
def text_cleaner(text):
  text = re.sub(r'--', ' ', text)
  text = re.sub('[\[].*?[\]]', '', text)
  text = re.sub(r'(\b|\s+\-?|^\-?)(\d+|\d*\.\d+)\b','', text)
  text = ' '.join(text.split())
  return text

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

#remove chapter indicator
hamlet = re.sub(r'Chapter \d+', '', hamlet)
macbeth = re.sub(r'Chapter \d+', '', macbeth)
caesar = re.sub(r'Chapter \d+', '', caesar)
#apply cleaning function to corpus
hamlet = text_cleaner(hamlet)
caesar = text_cleaner(caesar)
macbeth = text_cleaner(macbeth)

Теперь мы хотим использовать spaCy для анализа наших документов. Более подробную информацию о конвейере обработки текста можно найти здесь.

#parse cleaned novels
nlp = spacy.load('en')
hamlet_doc = nlp(hamlet)
macbeth_doc = nlp(macbeth)
caesar_doc = nlp(caesar)

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

hamlet_sents = ' '.join([sent.text for sent in hamlet_doc.sents if len(sent.text) > 1])
macbeth_sents = ' '.join([sent.text for sent in macbeth_doc.sents if len(sent.text) > 1])
caesar_sents = ' '.join([sent.text for sent in caesar_doc.sents if len(sent.text) > 1])
shakespeare_sents = hamlet_sents + macbeth_sents + caesar_sents
#inspect our text
print(shakespeare_sents)

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

#create text generator using markovify
generator_1 = markovify.Text(shakespeare_sents, state_size=3)

А теперь самое интересное. Нам просто нужно написать цикл, чтобы генерировать столько предложений, сколько мы хотим. Ниже мы создадим 3 предложения неопределенной длины и еще 3 предложения длиной менее 100 символов.

#We will randomly generate three sentences
for i in range(3):
  print(generator_1.make_sentence())
#We will randomly generate three more sentences of no more than 100 characters
for i in range(3):
  print(generator_1.make_short_sentence(max_chars=100))

Пример текста:

«Он останется, пока ты не придешь. К. Гамлет, эта Жемчужина твоя, вот тебе здоровье».

«Мой достопочтенный лорд, я буду говорить с ним».

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

#next we will use spacy's part of speech to generate more legible text
class POSifiedText(markovify.Text):
   def word_split(self, sentence):
      return ['::'.join((word.orth_, word.pos_)) for word in nlp(sentence)]
   def word_join(self, words):
      sentence = ' '.join(word.split('::')[0] for word in words)
      return sentence
#Call the class on our text
generator_2 = POSifiedText(shakespeare_sents, state_size=3)

И, наконец, напечатайте больше предложений с помощью нашего нового генератора.

#now we will use the above generator to generate sentences
for i in range(5):
  print(generator_2.make_sentence())
#print 100 characters or less sentences
for i in range(5):
  print(generator_2.make_short_sentence(max_chars=100))

Некоторые примеры:

«Он убил меня, мама, беги прочь, прошу тебя, О, это противодействие тебе, фальшивые датские псы».

«Окажи им милость, мы упокоим твоего короля Эрмитов».

В заключение

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