#fst #transducer #acceptor #graph #shortest-path #nlp

rustfst

用于构建、组合、优化和搜索加权有限状态转换器(FSTs)的库

50 个版本 (5 个稳定版)

新版本 1.1.2 2024年8月16日
1.0.1 2024年5月2日
0.13.6 2024年2月19日
0.13.5 2023年9月18日
0.1.7 2018年10月28日

56 in 算法

Download history 6539/week @ 2024-04-28 1561/week @ 2024-05-05 6078/week @ 2024-05-12 2546/week @ 2024-05-19 4079/week @ 2024-05-26 5522/week @ 2024-06-02 6112/week @ 2024-06-09 5689/week @ 2024-06-16 6746/week @ 2024-06-23 2345/week @ 2024-06-30 9469/week @ 2024-07-07 5319/week @ 2024-07-14 9196/week @ 2024-07-21 3815/week @ 2024-07-28 4278/week @ 2024-08-04 2284/week @ 2024-08-11

19,583 每月下载量
用于 3 crates

MIT/Apache

1MB
30K SLoC

Rustfst

License: MIT/Apache-2.0 Maintenance Github tag

Rust

rustc >= 1.51.0 Native Linux test status Documentation

Python

PyPI version PyPI download month PyPI pyversions

Rust语言实现的加权有限状态转换器。

Rustfst 是一个用于构建、组合、优化和搜索加权有限状态转换器(FSTs)的库。加权有限状态转换器是一种自动机,其中每个转换都有一个输入标签、一个输出标签和一个权重。更熟悉的有限状态接受器可以表示为每个转换的输入和输出标签相同的转换器。有限状态接受器用于表示字符串集(特别是正则或有理集);有限状态转换器用于表示字符串对的二元关系(特别是有理转换)。权重可以用来表示采取特定转换的成本。

FSTs 在语音识别和合成、机器翻译、光学字符识别、模式匹配、字符串处理、机器学习、信息提取和检索等方面有重要的应用。通常使用加权转换器来表示概率模型(例如,n-gram 模型,发音模型)。可以通过确定化和最小化来优化 FSTs,可以将模型应用于假设集(也作为自动机表示)或通过有限状态组合级联,并可以通过最短路径算法选择最佳结果。

fst

概述

有关基本示例,请参阅下面的部分。

可以使用宏 [fst] 或函数 acceptortransducer 轻松创建一些简单且常见的 FSTs 类型。

对于更复杂的案例,您可能首先会使用 VectorFst 类型,该类型将与您需要的大部分内容一起导入到 prelude 中。 VectorFst<TropicalWeight> 直接对应于 OpenFST 的 StdVectorFst,并且可以使用 readread_text 来加载其文件。

由于“遍历”一个 FST 可能意味着许多不同的事情,因此存在多种不同的迭代器。要遍历状态 ID,您可以使用 states_iter,而要遍历从状态输出的转换,您可以使用 get_trs。由于遍历两者都很常见,因此可以使用 fst_iterfst_into_iter 来实现。还非常常见的是遍历 FST 所接受的路径,这可以通过 paths_iter 实现,为了方便生成文本,还可以使用 string_paths_iter。对于线性 FST 的情况,您还可以使用 decode_linear_fst 获取唯一的路径。

请注意,遍历路径与找到 最短 路径或路径不同,这可以通过 shortest_path(用于单一路径)或 shortest_path_with_config(用于 N 个最短路径)来实现。

有关完整算法列表,请参阅 algorithms 模块。

您现在可能想知道,尤其是如果您以前使用过像 pyfoma 这样对语言学家友好的工具,那么“如果我只想要 转换一些文本 呢??”一个不友好的答案是 rustfst 是一个较低级别的库,设计用于实现语音识别器等事物。一个更有帮助的答案是,您将通过构建一个用于输入的 acceptor 来完成此操作,然后将其与一个 transducer 组合,然后 project 结果到其输出,最后在结果 FST 中遍历路径。

参考文献

实现受到了 Mehryar Mohri、Cyril Allauzen 和 Michael Riley 的工作的极大启发

API 与 OpenFst 非常相似,进行了一些简化以及为了使 Rust 代码更符合习惯而进行的一些更改,特别是使用 Tr 而不是 Arc。有关更多信息,请参阅 与 OpenFST 的差异

示例

use anyhow::Result;
use rustfst::prelude::*;
use rustfst::algorithms::determinize::{DeterminizeType, determinize};
use rustfst::algorithms::rm_epsilon::rm_epsilon;

fn main() -> Result<()> {
    // Creates a empty wFST
    let mut fst = VectorFst::<TropicalWeight>::new();

    // Add some states
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    let s2 = fst.add_state();

    // Set s0 as the start state
    fst.set_start(s0)?;

    // Add a transition from s0 to s1
    fst.add_tr(s0, Tr::new(3, 5, 10.0, s1))?;

    // Add a transition from s0 to s2
    fst.add_tr(s0, Tr::new(5, 7, 18.0, s2))?;

    // Set s1 and s2 as final states
    fst.set_final(s1, 31.0)?;
    fst.set_final(s2, 45.0)?;

    // Iter over all the paths in the wFST
    for p in fst.paths_iter() {
         println!("{:?}", p);
    }

    // A lot of operations are available to modify/optimize the FST.
    // Here are a few examples :

    // - Remove useless states.
    connect(&mut fst)?;

    // - Optimize the FST by merging states with the same behaviour.
    minimize(&mut fst)?;

    // - Copy all the input labels in the output.
    project(&mut fst, ProjectType::ProjectInput);

    // - Remove epsilon transitions.
    rm_epsilon(&mut fst)?;

    // - Compute an equivalent FST but deterministic.
    fst = determinize(&fst)?;

    Ok(())
}

与 OpenFST 的差异

以下是非详尽列表,说明了 Rustfst 的 API 与 OpenFST 的不同之处

  • 默认的epsilon符号是<eps>,而不是<epsilon>
  • 函数和方法遵循Rust命名规范,例如add_state而不是AddState,但除此之外,它们基本上是等价的,除了
  • 转换被称作Tr而不是Arc,因为在Rust中Arc有着不同的且已确立的含义,并且rustfst使用它(即std::sync::Arc,即)来引用符号表。所有相关函数也使用tr
  • 最终状态不是通过zero的最终权重来指示。你可以使用is_final测试最终状态,而final_weight返回一个Option。这要求在转换OpenFST代码时格外小心。
  • 转换可以直接作为切片访问,而不是需要一个迭代器。
  • 半群操作被表示为普通的方法,而不是奇怪的C++事物。所以写w1.plus(w2)而不是Plus(w1, w2),例如。
  • 权重有就地操作⊕(plus_assign)和⊗(times_assign)。
  • 大多数类型别名(在Rust中将是特质别名)如StdArcStdFst等,都缺失了,但类型推断使我们能够在大多数情况下避免显式类型参数,例如在调用Tr::new时,例如。
  • 状态ID是无符号的,使用NO_STATE_ID表示缺失值。默认情况下它们也是32位(可能,40亿个状态对于大多数应用来说已经足够了)。这意味着在使用它们作为索引时必须小心地将它们转换为usize,反之亦然,最好是检查溢出。
  • 符号ID也是无符号的,32位,使用NO_LABEL表示缺失值。
  • 浮点权重不是泛型的,因此总是单精度。

与OpenFST的基准测试

我之前对几乎所有的线性fst算法进行了基准测试,并将其结果与OpenFst进行了比较。你可以在这里找到结果

剧透一下:在这些算法上Rustfst都更快 😅

文档

最新发布版本的文档在此处可用:https://docs.rs/rustfst

发布过程

  1. 使用脚本update_version.sh更新每个包的版本。
  2. 推送
  3. 推送一个带有前缀rustfst-v的新标签。

示例

./update_version.sh 0.9.1-alpha.6
git commit -am "Release 0.9.1-alpha.6"
git push
git tag -a rustfst-v0.9.1-alpha.6 -m "Release rustfst 0.9.1-alpha.6"  
git push --tags

如果这是一个主要版本,可以选择在UI中创建GitHub发布。

此存储库包含的项目

此存储库包含两个主要项目

  • rustfst 是 Rust 的重实现。
    • 在 crates.io 上可用的包 这里
    • 文档可在 docs.rs 上找到 这里
  • rustfst-pythonrustfst 的 Python 绑定。
    • 包在 Pypi 上可用 这里
    • 文档可在 Github Pages 上找到 这里

许可证

以下任一许可证下授权:

任选其一。

贡献

除非你明确声明,否则你提交的任何有意包含在作品中的贡献,如 Apache-2.0 许可证中定义的,将按照上述方式双重授权,不附加任何额外条款或条件。

依赖项

~4–5.5MB
~98K SLoC