#markov #random #models #generator #name #generated #set

markov_namegen

基于马尔可夫模型的随机文本生成器

11 个不稳定版本 (4 个重大更改)

新版本 0.5.0 2024 年 8 月 7 日
0.4.0 2024 年 8 月 7 日
0.3.3 2024 年 6 月 27 日
0.3.1 2023 年 12 月 22 日
0.1.3 2023 年 4 月 26 日

#340 in 算法

Download history 202/week @ 2024-06-21 33/week @ 2024-06-28 3/week @ 2024-07-05 44/week @ 2024-07-26 224/week @ 2024-08-02

每月 268 次下载

MIT 许可证

35KB
322

markov_namegen

这个 crate 将定义一些生成随机文本(例如名称)的工具,这些文本将看起来类似于训练数据集。例如,您可以用一组罗马名字训练模型,然后您将获得一些看起来类似罗马的进程式生成词汇,例如

postius
rus
gratus
statlilius
fricamian
vitorialis
barbanus
civianus
trifex
majan

使用方法

要使用,请在您的 Cargo.toml 中添加 markov_namegen

RandomTextGenerator

提供了一个名为 RandomTextGenerator 的 trait,其中有一个方法

  • generate_one() -> String 生成一个新的进程式生成的文本字符串。

有三个结构体实现了这个 trait

  • CharacterChainGenerator
  • CharacterChainCasePreservingGenerator(即将推出)
  • ClusterChainGenerator

CharacterChainGenerator

快速入门

use markov_namegen::CharacterChainGenerator;

let dwarf_names = vec!["dopey","sneezy","bashful","sleepy","happy","grumpy","doc"].into_iter();

let generator = CharacterChainGenerator::builder()
    .train(dwarf_names)
    .build();

println!(generator.generate_one());

(或使用文件作为输入,并演示所有构建器选项...)

use std::fs::File;
use std::io::{BufReader, BufRead};
use markov_namegen::CharacterChainGenerator;
use markov_namegen::RandomTextGenerator;
use rand::{rngs::SmallRng, SeedableRng};

let file = File::open("resources/romans.txt").unwrap();
let reader = BufReader::new(file);
let lines = reader.lines().map(|l| l.unwrap());

let namegen = CharacterChainGenerator::builder()
    .with_order(2)
    .with_prior(0.007)
    .with_pattern("^[A-Za-z]{4,8}$")
    .with_rng(Box::new(SmallRng::seed_from_u64(123)))
    .train(lines)
    .build();

println!(generator.generate_one());

马尔可夫链随机文本生成的核心思想是收集关于哪些字符跟随其他字符的统计数据。所以如果一个特定语言经常使用 "th",则在随机生成的文本中 "t" 应该经常跟随 "h"。这个 crate 的方法接受一个训练数据迭代器,并使用它来构建马尔可夫模型,然后可以使用该模型生成新的字符串。然而,马尔可夫链方法有一些注意事项

首先,只查看两个字符序列并不十分复杂。如果您查看超过一个字母,模型会更聪明。例如,您的模型可以知道 "ot" 和 "nt" 经常跟随 "h",但 "st" 不行。问题是,在您的训练数据中,每个 3 个、4 个或 n 个字符序列的示例都将比 2 个字符序列的示例少得多。如果一个序列从未出现在您的训练数据中,它将永远不会出现在输出中。因为示例较少,输出将不够随机。

基于 JLund3 在 RogueBasin 上描述的算法 ,我之前已经在 Java 中 实现,并在 Python 中 实现,CharacterChainGenerator 通过几种方式缓解了这些问题

  • 该系统开发了多个“顺序”的模型,即多个长度的字符序列模型。如果生成器遇到一个像“rus”这样的新三字符序列,它将首先检查是否已经对该序列训练了一个模型。如果没有,它将回退到检查是否有“us”的模型,如果没有,那么它肯定有一个模型来处理“s”后面的内容。我把这称为三阶模型,它是默认的。

  • 在每个模型中,每个字母表中的字符都添加了一个贝叶斯先验概率,因此可能产生在训练数据中未见过的真正随机的字符序列。字母表是从训练数据推断出来的,因此任何UTF-8字符都应该可以。默认的先验概率是0.005的相对概率。随着字母表的增大和训练的字符序列减少,真正随机的输出变得更有可能,所以你可能想调整这个参数:增加它以增加随机性,或者减少它以使输出更像训练数据。

每个新生成的候选字符串都会与提供的正则表达式模式(如果有)进行比较。如果候选字符串被过滤掉,我们将生成另一个,直到通过为止。(请注意,如果你提供一个非常难以匹配的模式,生成时间可能会大幅增加。如果你设置了一个不可能匹配的模式,例如需要训练数据集字母表中不存在的字符,你将得到一个无限循环。)

CharacterChainGenerator忽略大小写,将您的输入文本和过滤器转换为小写,并返回小写字符串。

CharacterChainCasePreservingGenerator

(将在未来版本中提供)

CharacterChainGenerator的一个变体,该变体学习并重现训练数据中的大小写使用情况。对于一个给定的数据集,这个模型可能因为为“大写”和“小写”(例如,“A”和“a”)分别构建单独的模型(以“e”为例)而不是合并观察结果,所以可能不太有效地从训练数据中学习。然而,如果输入数据中有有趣的大小写使用(例如,以“Mc”和“Mac”开头,后面跟着大写字母的名字),你可能希望重新生成这些数据。

ClusterChainGenerator

快速入门

use markov_namegen::ClusterChainGenerator;
use rand::{rngs::SmallRng, SeedableRng};

let dwarf_names = vec!["dopey","sneezy","bashful","sleepy","happy","grumpy","doc"].into_iter();

let namegen = ClusterChainGenerator::builder()
    .with_order(2)
    .without_prior()
    .with_pattern("^[A-Za-z]{4,8}$")
    .with_rng(Box::new(SmallRng::seed_from_u64(123)))
    .train(dwarf_names)
    .build();
println!(generator.generate_one());

一个使用元音/辅音聚类算法来生成新随机文本的类。基于Kusigrosz在RogueBasin上描述的算法(有关更多信息,请参阅此算法的描述),它将输入文本转换为小写,然后扫描文本中的元音和辅音集群,并跟踪所有已观察到的跟随任何给定集群的集群。例如,“Elizabeth”会产生集群#-e-l-i-z-a-b-e-th-#,而“Anne”会产生#-a-nn-e-#,其中“#”是一个控制字符,用于标记字符串的开始或结束。

与CharacterChainGenerator类似,该实现基于多阶马尔可夫链。内部我们将跟踪每个集群的可能后继者,例如。

# -> [e,a]
e -> [l,th,#]
a -> [b,nn]
th -> [#]
...etc...

方法generateOne()通过集群链进行随机漫步,仅跟随在训练数据中找到的路径。以我们的例子继续,一个新的字符串可以以“e”或“a”开始,以相等的可能性,“e”可以跟随“l”,“th”或字符串的结束,等等。在这个只有两个单词的训练数据集中,你可以得到几个不同的结果,例如。

elizanneth
abelizanne
anneth
...etc...

发布说明

0.5.0: 增加了使用 log crate 的实际日志记录,特别是记录失败的模式匹配,这应该有助于 crate 的用户确定他们的模式是否过于严格。修复了 main.rs 中的一个错误,并将其移至 "examples" 目录下;使用 cargo run --example main 运行。

0.4.0: 实现了 Debug 特性。

0.3.3: 清理了循环以删除轻微的代码重复。

0.3.2: 将对 Regex::new 的调用移至,应该会加快对 generate_one() 的调用。感谢 ratmice

0.3.1: 增加了对每种生成器使用自定义 RNG 进行初始化的能力。

0.2.1: 降低了 ClusterChainGenerator 的默认 "优先级"。给定的训练集将拥有比字母表中的字母多得多的簇,因此结果过于随机。

0.2.0: 增加了 ClusterChainGenerator。

0.1.3: CharacterChainGenerator 正常工作。

依赖项

~3–4.5MB
~100K SLoC