#procedural-generation #markov-chain #states #training #model #name #locations

multimarkov

这是一个用于训练和使用多阶马尔可夫链进行过程生成应用的通用工具,例如生成随机但听起来真实的角色和地点名称。

7个版本 (1个稳定版)

1.0.0 2024年8月7日
0.5.0 2024年8月7日
0.4.0 2023年12月22日
0.3.2 2023年4月25日
0.1.0 2023年4月24日

#87 in 机器学习

Download history 5/week @ 2024-05-17 2/week @ 2024-05-24 1/week @ 2024-06-07 4/week @ 2024-06-14 4/week @ 2024-06-21 124/week @ 2024-08-02

每月下载量124
markov_namegen 中使用

MIT 许可证

29KB
347

multimarkov

这是一个用于训练和使用多阶马尔可夫链进行过程生成应用的通用工具,例如生成随机但听起来真实的角色和地点名称。

它是我的markovmodels项目(Java实现)的新实现,但现在是在Rust中。

马尔可夫链

马尔可夫链将当前状态映射到可能的未来状态,通常用概率定义(参考维基百科)。这在过程生成中很有用,例如,用于模拟一种语言中哪些字母最频繁地跟在给定字母后面,或者用于模拟给定天气条件下最有可能出现的天气条件。对于每个已知状态,马尔可夫链知道可能的后继状态集合,因此客户端应该能够遍历“链”多次迭代(例如,用于模拟)。在大多数情况下,这些转移将由概率分布或观察到的频率加权,因此不是所有从任何给定状态到其他状态的转移都是等可能的。

MultiMarkov<T>

MultiMarkov 是这个包中的关键结构。它受到JLund3在RogueBasin中描述的算法的启发。

本实现提供带有Katz回退的多阶模型。这意味着,如果 order 大于1,则多个模型可能匹配任何给定的序列。例如,如果您提供序列 ['U','S','T'],并且 order 等于 3,我们将首先检查是否有针对 ['U','S','T'] 后的状态的模型。如果没有找到,例如,因为这个序列从未在训练数据中见过,我们将检查针对 ['S','T'] 后的状态的模型。如果这也失败了,我们将回退到从状态 'T' 的转换模型,如果 'T' 至少在训练数据中一次被观察到,并且有后续状态,则该模型一定存在。

在程序生成应用中,可能还需要一个功能,即通过“先验”相对概率的形式注入一些“真正的随机性”,即给予未在训练数据中观察到的转换的小权重。这些可以弥补训练数据集的局限性,并允许生成在训练数据中没有观察到的序列。默认情况下,我们为每个这样的转换分配0.005的概率。

如果不希望使用多阶模型,请使用 .with_order(1)。如果不希望使用先验,请使用 .without_prior()

使用方法

multimarkov 添加到您的 Cargo.toml

构建和训练

要构建一个 MultiMarkov 实例,请使用构建器模式。 T 可以是任何实现了 Eq + Hash + Clone + std::cmp::Ord 的类型的实例。在这里,我们使用 char

let training_data = vec![
    vec!['f','o','o','b','a','r'],
    vec!['s','n','a','f','u'],
];

let mm = MultiMarkov::<char>::builder()
    .with_order(2) // omit to use default of 3
    .with_prior(0.01) // omit to use default of 0.005, or call .without_prior() to disable priors
    .with_rng(Box::new(SmallRng::seed_from_u64(1234))) // omit to use a default, non-seeded RNG
    .train(input_vec.into_iter())
    .build();

程序生成

要得到一个随机抽取,请使用表示当前或之前状态(s)的 &Vec<T> 调用 random_next()。例如

let next_letter = mm.random_next(&Vec['a']);

将随机抽取一个字母来跟随 'a'。根据训练数据,这可能将是 'r''f',但由于“先验”,任何已知状态都有被抽取的小概率。

random_next 需要一个向量,原因可能是你正在使用一个需要回顾序列中几个状态的多阶模型。例如

let next_letter = mm.random_next(&Vec['s','n','a']);

更有可能抽取 'f',因为它已经训练了一个模型来预测 'n','a' 后面会发生什么,而它更愿意使用这个模型而不是其 'a' 后面发生什么的模型。

发布说明

1.0.0:用 log crate 的日志替换了 println。在 MultiMarkovBuilder 上的 add_priors 函数中添加了对已知状态和训练序列数量的日志记录。实际上,如果数据集很大,这一步可能会真的爆炸,因此这种日志记录可能对后续操作有所帮助。此外:将二进制目标(main.rs)移动到 "examples" 目录。使用以下命令运行:cargo run --example main

0.5.0:MultiMarkov 现在实现了 Debug

0.4.0:在构建器上添加了一个名为 with_rng() 的方法,允许你添加一个自定义 RNG,例如,如果你想使用一个随机数种子。感谢 RicardRC。

0.3.0:主要重写;现在 T 可以是任何 Eq + Hash + Clone,不需要 Copy,这意味着我们可以使用字符串序列。我还引入了一个真正的 "builder" 结构,MultiMarkovBuilder。

0.2.1:方法 random_next() 现在不再以可变借用 self

0.2.0:你现在可以多次训练模型,以使用两个数据集进行累积训练。新的 "build" 方法现在返回一个 MarkovModel 而不是 Result,因此 API 有所变化。

0.1.0:第一个版本。

依赖关系

~290–400KB