Если вы просматриваете Reddit, скорее всего, вы слышали о / r / SubredditSimulator.
Там вы найдете безопасное место для искусственных зеркальных отображений реальных субреддитов, заполненных реальными людьми и имитируемых роботами. Их слова звучат знакомо, но в то же время жутко; Веселье возникает, когда конкретной ссылке дается комичное описание, и иногда смущение может поразить тех, кто забывает о подписке.
В этом посте мы увидим, как все это работает, как его воспроизвести и как настроить результат с помощью некоторых фитнес-функций, чтобы сгенерировать текст, примерно соответствующий нашему описанию .
Вы взволнованы? И я тоже; давай начнем.
1. Цепи Маркова.
SubredditSimulator прекрасно демонстрирует невероятный механизм, который существует уже довольно давно: цепи Маркова.
Как вы, наверное, (не) знаете, я все о процедурной генерации данных. Цепи Маркова представляют для меня большой интерес, потому что они являются одним из более простых и в то же время наиболее эффективных инструментов, когда дело доходит до создания полусвязного текста из чистого небытия (читайте: умеренно обширный набор данных) . И, помимо нейронных сетей, я бы сказал, что это лучшее, что у нас есть на данный момент, несмотря на простоту.
Цепи Маркова могут «генерировать» слово B из слова A, тогда и только тогда, когда B следует за A хотя бы один раз во время тренировки. Кроме того, B выбирается из списка кандидатов (read: слова, которые следовали за A во время обучения), который сортируется по вхождению и, следовательно, по вероятности. Затем добавляется немного случайности, чтобы немного оживить ситуацию и (попытаться) предотвратить петли, но в основном это решение проблемы отсутствия достаточно большого набора данных.
Итак, первая деталь реализации цепей Маркова - это обучение. Корпус текста должен войти в наш черный ящик и появиться горизонтальное дерево, где каждое слово представляет собой ветвь. Листья представлены всеми словами, относящимися к определенной ветви, и их «размер» зависит от их относительной частоты. Я нарисовал красивую картину? Я надеюсь, что это так.
2. Обучение
Поскольку мы говорим о деревьях и об обучении, давайте поговорим о Python:
import re class MarkovChain: def __init__(self): self.tree = dict() def train(self, text, factor=1): pass
Ладно, пока ничего удивительного. Наше дерево будет хранилищем ключей и значений. Но как насчет функции train (), а как насчет factor? Вскоре мы увидим, где в игру вступает коэффициент, а пока подумайте о нем как о множителе. Его включение позволяет нашей цепочке усерднее тренироваться с одним текстом и меньше - с другим. Но не только это; когда factor отрицательный, все отношения для каждой пары слов в корпусе будут ослаблены! Это позволяет нам тренироваться более динамично и, в конце концов, формировать уклон в сторону определенных характеристик (например: более длинные слова, больше согласных, меньше аллитераций, рифмования и т. Д.).
Вот полностью аннотированная функция train ():
"[КОД]"
Идея здесь проста: для каждой пары слов, содержащихся в корпусе текста, усилить связь этой пары постоянным коэффициентом (который находится в диапазоне [-1; 1]).
По окончании процесса на нашем дереве появятся новые ветви и листья.
3. Пакетная обработка и сериализация
Вы не поверите, но мы почти у цели. Я имею в виду, на вершине айсберга. Теперь, когда нашу цепочку можно обучить некоторому тексту, давайте напишем несколько помощников для обучения всем файлам в каталоге, а также для сохранения и загрузки состояния нашего генератора с диска. Нам понадобятся glob и pickle.
"[КОД]"
Легче сделать, чем сказано! Это будет полезно, потому что цепи Маркова имеют тенденцию имитировать стиль их набора данных. Здесь, безусловно, необходима способность тренировать и сохранять состояние по желанию.
4. Генерация текста!
А вот и самое интересное. Как только цепь обучена, она может начать свою работу в качестве безумного болтающего робота.
Сначала [КОД], потом пояснения.
Опять же, это просто: вы начинаете с заданного слова (или выбираете случайную ветвь) и сортируете его листья по частоте встречаемости. Некоторая случайность добавлена для обеспечения разнообразия, но моя реализация допускает гибкость даже в этом случае: если вы не хотите случайности, просто передайте lambda x: x
в rand вместо значения по умолчанию, и вы получите полностью детерминированное поколение.
Сама функция generate () является генератором Pythonic: она непрерывно выдает значения, вместо того, чтобы строить и затем возвращать весь список. Атрибут max_len предназначен для предотвращения потенциально бесконечного запуска процесса, но никто не мешает вам оставить его на 0 и генерировать слова до тех пор, пока не будет найден тупик (что может быть невозможно).
Затем у нас есть функция generate_formatted (), которая аккуратно обертывает generate () для получения более красивого текста. Он поддерживает перенос слов, разрывы строк и использование заглавных букв. Ничего особенного, но это будет удобно.
Ну как насчет того. Мы готовы проверить наш код! Я обучу его, используя некоторые из моих недавних постов на Reddit.
# Builds a formatted string from the generator def gen(m): return ''.join([w for w in m.generate_formatted(word_wrap=60, soft_wrap=True, start_with=None, max_len=50, verbose=True)]) mkv = MarkovChain() mkv.bulk_train('E:\\Text Files\\MkvCmd Training Data\\*.txt', verbose=True) print(gen(mkv))
Выход:
... Successfully trained on 1 files. Generating a sentence of max. 50 words, starting with "early": Early on, if you can be a mac address, but i think that you can tell you can be so well (\p{l} matches any compassion. It’s a lot of the only works with a few downsides to be a drug because, when you can be a lot of the same Generating a sentence of max. 50 words, starting with "bring": Bring users away from the same be too sure. Your account to be a few special characters. You don't know where you don't know where you don't know about them. Even beats c++'s speed (which is a few years, but i think that you don't have to be a few
Ладно, это грубо. Довольно забавно, но все же грубо. Я признаю, что размер моей выборки сейчас довольно мал, но это не будет большой проблемой.
Эй, по крайней мере, работает. Теперь мы на шаг ближе к истине.
5. Потяните за шнурки, чтобы отрегулировать веса.
И вот новинка: применение фитнес-функции к нашей цепи Маркова!
В контексте эволюционного программирования функции приспособленности определяют, является ли данный кандидат хорошим или плохим. В зависимости от значения, возвращаемого функцией пригодности, модель может адаптироваться, чтобы максимизировать ее приспособленность.
В нашем случае кандидатом будет пара слов. Ветвь и лист. Мы собираемся оценить эти отношения и укрепить или ослабить их в зависимости от нескольких произвольных критериев.
"[КОД]"
Хорошо, теперь у нас есть функция adjust_weights (). Он принимает параметр max_len и функцию f, которая принимает на вход два слова и возвращает значение в диапазоне [0; 1].
Теоретически, чем выше max_len, тем больше времени потребуется для адаптации цепи. Однако, если max_len слишком мало, такие адаптации могут быть очень разными и не достичь цели. Практически? Установка его на два, минимально возможное значение, работает как шарм. Это может быть потому, что мой набор данных настолько мал.
У нас также есть функция bulk_adjust_weights (), которую мы и будем использовать. Он не только применяет к модели одну функцию приспособленности, но и применяет сразу несколько функций приспособленности для заданного количества итераций. В подробном режиме он также выводит красивую полосу выполнения, что приятно.
Да, и, кстати, класс MarkovChain готов. Не стесняйтесь проверить это на Github.
Пришло время определить некоторые фитнес-функции:
"[КОД]"
Это просто примеры; Единственным ограничением является ваше воображение! Посмотрим, как их применять:
"[КОД]"
И вот новый, улучшенный результат:
Кроме того, бег высвобождает эндорфины, и теперь у людей одни
системы, в то время как другие наркотики вполне подойдут; они кажутся
и должны были быть встроены из mdd для дерьмового
вызова.
Дерьмовый вызов дерьмовой причуды.
Свет - ожирение, диабет и люди просто наведите указатель мыши на любую
букву).
Кроме того, работает.
Намного лучше, если вы спросите меня. Почти поэтично. Поощряя более сложные слова и более подробные знаки препинания, нам удалось немного улучшить качество вывода. Посмотрим, что еще мы можем сделать.
Вот, например, путем микширования и сопоставления мне удалось создать что-то, напоминающее текст поп-песни:
Основные продукты из-за других наркотиков доставляли вам удовольствие.
И не подходят в свою очередь.
В свою очередь.
В свою очередь.
В циклах, если это необходимо или подсознательно Покупка кока-колы
и регулирование уровня серотонина.
Не подходят.
В свою очередь.
Несмотря на обиды.
Но я мог бы использовать полную пунктуацию и создать что-то вроде:
Альфа: мы по-прежнему занимаемся подменой IP-адресов, которая искажает ваше суждение
говорит о том, что вы можете замедлить работу вашего врача до лучшего программиста?
Кто может это победить.
Я на улице, я буду будь слишком уверен.
Твои братья и сестры привыкли.
Довольны этим.
Иногда ты справляешься.
Меня заставили усложнить отслеживание.
Или я могу сказать, что мне нравятся только короткие слова и гласные:
Пропускная способность немного скромнее, и я думаю, что вы не хотите,
быть в той же лиге, что и несколько лет назад, но
я думаю, что вы не хотите быть несколькими специальными персонажами.
Вы же не просто выходите что-то новенькое.
Или очень длинные слова с несколькими гласными и многими согласными:
Концентрация.
На их лице.
Они были встроены в реальность, несмотря на то, что они так раздражают.
Людям, конечно, не нравится, mdd в течение 10 лет.
Я может быть.
Настоящие проблемы, ввод перенаправлен, и они не будут
проходить, но вещи должны были на хроме вы говорить все, что они нажимают,
даже
Но хватит. Я оставлю вам отрывок из этой статьи:
Мы только это; когда достаточно большой набор данных.
Корпус моих последних сообщений на Reddit.
# создает красивую картину?
Это почти похоже на хайку, или, возможно, я начинаю видеть закономерности в безумии хаоса.
Спасибо за чтение. Я надеюсь, что вы нашли мою рецензию полезной и / или интересной. Увидимся, как только процедурная генерация предложит новый вызов!
ПОЛНЫЙ ПРОЕКТ: https://github.com/G3Kappa/Adjustable-Markov-Chains