跳转到主要内容

未提供项目描述

项目描述

Rhasspy 自然语言理解

Continuous Integration PyPI package version Python versions GitHub license

用于解析 Rhasspy 句子模板、执行意图识别和生成 ARPA 语言模型的库。

需求

  • Python 3.7

安装

$ git clone https://github.com/rhasspy/rhasspy-nlu
$ cd rhasspy-nlu
$ ./configure
$ make
$ make install

运行

$ bin/rhasspy-nlu <ARGS>

解析句子模板

Rhasspy 声音命令存储在类似这样的文本文件中

[Intent1]
this is a sentence
this is another sentence

[Intent2]
a sentence in a different intent

您可以使用 rhasspynlu.parse_ini 将这些解析为结构化表示,然后使用 rhasspynlu.intents_to_graph 转换为图形

import rhasspynlu

# Load and parse
intents = rhasspynlu.parse_ini(
"""
[LightOn]
turn on [the] (living room lamp | kitchen light){name}
"""
)

graph = rhasspynlu.intents_to_graph(intents)

结果是有向图,其状态是单词,边是输入/输出标签。

您可以向 parse_ini 传递一个 intent_filter 函数,以仅对要解析的意图名称返回 True。此外,还可以提供一个用于每个句子通过的 sentence_transform 参数的函数(例如,转换为小写)。

模板语法

句子模板基于JSGF标准。以下结构可用

  • 可选单词
    • this is [a] test - 单词 "a" 可能存在也可能不存在
  • 替代
    • set color to (red | green | blue) - "red"、"green" 或 "blue" 都可能
  • 标签
    • turn on the [den | playroom]{location} light - 命名实体 location 将是 "den" 或 "playroom"
  • 替换
    • make ten:10 coffees - 输出将是 "make 10 coffees"
    • turn off the: (television | tele):tv - 输出将是 "turn off tv"
    • set brightness to (medium | half){brightness:50} - 命名实体 brightness 将是 "50"
  • 规则
    • rule_name = rule body 可以作为 <rule_name> 引用
  • 插槽
    • $slot 将由 intents_to_graph 函数的 replacements 参数中的句子列表替换。

规则

可以使用以下语法将命名规则添加到模板文件中:

rule_name = rule body

然后使用 <rule_name> 引用。规则的主体是一个常规句子,它本身可以包含对其他规则的引用。

通过在规则名前加上意图名和一个点,您可以从不同的意图中引用规则。

[Intent1]
rule = a test
this is <rule>

[Intent2]
rule = this is
<rule> <Intent1.rule>

在上面的示例中,Intent2 使用了其本地的 <rule>,以及来自 Intent1<rule>

插槽

槽位名称以美元符号 ($) 开头。在调用 intents_to_graph 时,replacements 参数是一个字典,其键是槽位名称(带 $),其值是 Sentence 对象的列表。每个 $slot 都将被相应的句子列表替换,这些列表可能包含可选的单词、标签、规则和其他槽位。

例如:

import rhasspynlu

# Load and parse
intents = rhasspynlu.parse_ini(
"""
[SetColor]
set color to $color
"""
)

graph = rhasspynlu.intents_to_graph(
    intents, replacements = {
        "$color": [rhasspynlu.Sentence.parse("red | green | blue")]
    }
)

将替换 $color 为 "red"、"green" 或 "blue"。

意图识别

将句子模板转换为图后,您可以识别句子。假设您有一个如下的 .ini 文件:

[LightOn]
turn on [the] (living room lamp | kitchen light){name}

您可以使用以下方法识别句子:

from pathlib import Path
import rhasspynlu

# Load and parse
intents = rhasspynlu.parse_ini(Path("sentences.ini"))
graph = rhasspynlu.intents_to_graph(intents)

rhasspynlu.recognize("turn on living room lamp", graph)

将返回一个包含 Recognition 对象的列表,如下所示:

[
    Recognition(
        intent=Intent(name='LightOn', confidence=1.0),
        entities=[
            Entity(
                entity='name',
                value='living room lamp',
                raw_value='living room lamp',
                start=8,
                raw_start=8,
                end=24,
                raw_end=24,
                tokens=['living', 'room', 'lamp'],
                raw_tokens=['living', 'room', 'lamp']
            )
        ],
        text='turn on living room lamp',
        raw_text='turn on living room lamp',
        recognize_seconds=0.00010710899914556649,
        tokens=['turn', 'on', 'living', 'room', 'lamp'],
        raw_tokens=['turn', 'on', 'living', 'room', 'lamp']
    )
]

空列表表示识别失败。您可以轻松地将 Recognition 对象转换为 JSON。

...

import json

recognitions = rhasspynlu.recognize("turn on living room lamp", graph)
if recognitions:
    recognition_dict = recognitions[0].asdict()
    print(json.dumps(recognition_dict))

您还可以将一个 intent_filter 函数传递给 recognize,以便只返回您想要包含在搜索中的意图名称。

标记

如果您的句子不是由空白字符进行标记,请将标记列表传递给 recognize 而不是字符串。

识别字段

rhasspynlu.Recognition 对象具有以下字段:

  • intent - rhasspynlu.Intent 实例
    • name - 识别出的意图名称
    • confidence - 0-1 之间的数字,1 表示确定
  • text - 替换后的输入文本
  • raw_text - 输入文本
  • entities - rhasspynlu.Entity 对象的列表
    • entity - 识别出的实体名称(在 (input:output){name} 中的 "name")
    • value - 识别出实体的替换值(在 (input:output){name} 中的 "output")
    • tokens - value 中的单词列表
    • start - valuetext 中的起始索引
    • end - valuetext 中的结束索引(不包括)
    • raw_value - 识别出实体的值(在 (input:output){name} 中的 "input")
    • raw_tokens - raw_value 中的单词列表
    • raw_start - raw_valueraw_text 中的起始索引
    • raw_end - raw_valueraw_text 中的结束索引(不包括)
  • recognize_seconds - recognize 所需的时间(秒)

停止词

您可以将一组 stop_words 传递给 recognize

rhasspynlu.recognize("turn on that living room lamp", graph, stop_words=set(["that"]))

如果输入句子中的停止词与图不匹配,则将跳过这些停止词。

严格识别

对于更快但不太灵活的识别,将 fuzzy 设置为 False

rhasspynlu.recognize("turn on the living room lamp", graph, fuzzy=False)

这至少快两倍,但如果句子没有精确地出现在图中,将失败。

严格识别还支持 stop_words 以增加一点灵活性。如果在没有 stop_words 的情况下识别失败,将再次尝试使用 stop_words

转换器

可以在识别过程中应用值转换,例如将字符串 "10" 转换为整数 10。在单词、序列或标签名称后跟 "!converter" 将在 recognize 期间运行 "converter"。

import rhasspynlu

# Load and parse
intents = rhasspynlu.parse_ini(
"""
[SetBrightness]
set brightness to (one: hundred:100)!int
"""
)

graph = rhasspynlu.intents_to_graph(intents)

recognitions = rhasspynlu.recognize("set brightness to one hundred", graph)
assert recognitions[0].tokens[-1] == 100

转换器也可以应用于标签/实体。

import rhasspynlu

# Load and parse
intents = rhasspynlu.parse_ini(
"""
[SetBrightness]
set brightness to (one:1 | two:2){value!int}
"""
)

graph = rhasspynlu.intents_to_graph(intents)

recognitions = rhasspynlu.recognize("set brightness to two", graph)
assert recognitions[0].tokens[-1] == 2

以下是在rhasspynlu中可用的默认转换器:

  • int - 转换为整数
  • float - 转换为实数
  • bool - 转换为布尔值
  • lower - 转换为小写
  • upper - 转换为大写

您可以通过传递一个字典给recognize函数的converters参数来覆盖这些转换器。要提供额外的转换器(而不是覆盖),请使用extra_converters

import rhasspynlu

# Load and parse
intents = rhasspynlu.parse_ini(
"""
[SetBrightness]
set brightness to (one:1 | two:2){value!myconverter}
"""
)

graph = rhasspynlu.intents_to_graph(intents)

recognitions = rhasspynlu.recognize(
    "set brightness to two",
    graph,
    extra_converters={
        "myconverter": lambda *values: [int(v)**2 for v in values]
    }
)
assert recognitions[0].tokens[-1] == 4

最后,您可以使用多个"!"将转换器链接起来

import rhasspynlu

# Load and parse
intents = rhasspynlu.parse_ini(
"""
[SetBrightness]
set brightness to (one:1 | two:2){value!int!cube}
"""
)

graph = rhasspynlu.intents_to_graph(intents)

recognitions = rhasspynlu.recognize(
    "set brightness to two",
    graph,
    extra_converters={
        "cube": lambda *values: [v**3 for v in values]
    }
)
assert recognitions[0].tokens[-1] == 8

ARPA 语言模型

您可以从rhasspynlu图计算ngram计数,这对于生成ARPA语言模型非常有用。这些模型可以由语音识别系统使用,如PocketsphinxKaldiJulius

import rhasspynlu

# Load and parse
intents = rhasspynlu.parse_ini(
"""
[SetColor]
set light to (red | green | blue)
"""
)

graph = rhasspynlu.intents_to_graph(intents)
counts = rhasspynlu.get_intent_ngram_counts(
    graph,
    pad_start="<s>",
    pad_end="</s>",
    order=3
)

# Print counts by intent
for intent_name in counts:
    print(intent_name)
    for ngram, count in counts[intent_name].items():
        print(ngram, count)

    print("")

将打印类似的内容

SetColor
('<s>',) 3
('set',) 3
('<s>', 'set') 3
('light',) 3
('set', 'light') 3
('<s>', 'set', 'light') 3
('to',) 3
('light', 'to') 3
('set', 'light', 'to') 3
('red',) 1
('to', 'red') 1
('light', 'to', 'red') 1
('green',) 1
('to', 'green') 1
('light', 'to', 'green') 1
('blue',) 1
('to', 'blue') 1
('light', 'to', 'blue') 1
('</s>',) 3
('red', '</s>') 1
('green', '</s>') 1
('blue', '</s>') 1
('to', 'red', '</s>') 1
('to', 'green', '</s>') 1
('to', 'blue', '</s>') 1

Opengrm

如果您在您的PATH中安装了Opengrm命令行工具,您可以使用rhasspynlu生成ARPA格式的语言模型。

使用graph_to_fstfst_to_arpa函数可以在格式之间进行转换。调用fst_to_arpa需要在您的PATH中存在以下二进制文件

  • fstcompile(来自OpenFST
  • ngramcount
  • ngrammake
  • ngrammerge
  • ngramprint
  • ngramread

示例

# Convert to FST
graph_fst = rhasspynlu.graph_to_fst(graph)

# Write FST and symbol text files
graph_fst.write("my_fst.txt", "input_symbols.txt", "output_symbols.txt")

# Compile and convert to ARPA language model
rhasspynlu.fst_to_arpa(
    "my_fst.txt",
    "input_symbols.txt",
    "output_symbols.txt",
    "my_arpa.lm"
)

现在您可以在接受ARPA格式语言模型的任何语音识别器中使用my_arpa.lm

语言模型混合

如果您有一个现有的语言模型,您想与Rhasspy语音命令混合,您首先需要将其转换为FST

rhasspynlu.fst_to_arpa("existing_arpa.lm", "existing_arpa.fst")

现在当您调用fst_to_arpa时,请确保提供base_fst_weight参数。这是一个包含您的现有ARPA FST路径和介于0和1之间的混合权重的元组。权重为0.05表示基本语言模型将获得语言模型总体概率质量的5%。其余的质量将分配给您的自定义语音命令。

示例

rhasspynlu.fst_to_arpa(
    "my_fst.txt",
    "input_symbols.txt",
    "output_symbols.txt",
    "my_arpa.lm",
    base_fst_weight=("existing_arpa.fst", 0.05)
)

命令行使用

可以直接运行rhasspynlu模块将sentences.ini文件转换为JSON图或FST文本文件

python3 -m rhasspynlu sentences.ini > graph.json

您可以传递多个.ini文件作为参数,它们将被组合。添加--fst参数将写入FST文本文件

python3 -m rhasspynlu sentences.ini --fst

这将输出当前目录中的三个文件

  • fst.txt - 文本形式的有限状态转换器
  • fst.isymbols.txt - 输入符号
  • fst.osymbols.txt - 输出符号

可以使用--fst-text--fst-isymbols--fst-osymbols参数分别更改这些文件名。

使用来自OpenFSTfstcompile编译为二进制FST

fstcompile \
    --isymbols=fst.isymbols.txt \
    --osymbols=fst.osymbols.txt \
    --keep_isymbols=1 \
    --keep_osymbols=1 \
    fst.txt \
    out.fst

单词发音

rhasspynlu.g2p提供了加载和使用音位发音字典的方法("g2p"代表"grapheme to phoneme")。

字典的格式应与CMU 发音词典相同,即每行一个单词,单词和音素之间用空格分隔

yawn Y AO N
test T EH S T
say S EY
who HH UW
bee B IY
azure AE ZH ER
read R EH D
read(2) R IY D

当单词有多个发音时(如前面的例子中的"read"),可以在单词后附加一个(N)

您可以使用rhasspynlu.g2p.read_pronunciations将音位字典加载到Python字典中

import rhasspynlu.g2p

with open("/path/to/file.dict", "r") as dict_file:
    pronunciations = rhasspynlu.g2p.read_pronunciations(dict_file)

assert pronunciations == {
    "yawn": [["Y", "AO", "N"]],
    "test": [["T", "EH", "S", "T"]],
    "say": [["S", "EY"]],
    "who": [["HH", "UW"]],
    "bee": [["B", "IY"]],
    "azure": [["AE", "ZH", "ER"]],
    "read": [["R", "EH", "D"], ["R", "IY", "D"]],
}

有关预构建的音位字典,请参阅voice2json配置文件

猜测发音

rhasspynlu.g2p.guess_pronunciations函数使用Phonetisaurus和预训练的图形到音素模型来猜测未知单词的发音。您需要在您的$PATH中安装phonetisaurus-apply并在您的环境中提供预训练模型(g2p.fst

import rhasspynlu.g2p

guesses = rhasspynlu.g2p.guess_pronunciations(
    ["moogle", "ploop"], "/path/to/g2p.fst", num_guesses=1
)

print(list(guesses))

# Something like: [
#   ('moogle', ['M', 'UW', 'G', 'AH', 'L']),
#   ('ploop', ['P', 'L', 'UW', 'P'])
# ]

有关预训练的g2p模型,请参阅voice2json配置文件

发音相似

Rhasspy NLU 支持一种指定单词发音的替代方法。不是直接指定音素,而是可以通过参考其他单词来描述单词应该如何发音

unknown_word1 known_word1 [known_word2] ...
...

例如,歌手 Beyoncé 的发音听起来像是单词 "bee yawn say" 的组合

beyoncé bee yawn say

rhasspynlu.g2p.load_sounds_like 函数将解析此文本,并在给定现有的发音字典时生成新的发音

import io

import rhasspynlu.g2p

# Load existing dictionary
pronunciations = rhasspynlu.g2p.read_pronunciations("/path/to/file.dict")

sounds_like = """
beyoncé bee yawn say
"""

with io.StringIO(sounds_like) as f:
    rhasspynlu.g2p.load_sounds_like(f, pronunciations)

print(pronunciations["beyoncé"])

# Something like: [['B', 'IY', 'Y', 'AO', 'N', 'S', 'EY']]

您可以使用 word(N) 语法引用已知单词的特定发音,其中 N 是从 1 开始的。发音按行顺序加载,因此顺序是可预测的。例如,read(2) 将引用单词 "read" 的第二个发音。如果没有 (N),则将使用找到的所有发音。

音素文字

您也可以将这些发音中插入音韵块。例如,单词 "hooiser" 的发音听起来像是 "who" 和 "azure" 中的 "-zure"

hooiser who /Z 3/

斜杠 (/) 之间的文本将被解释为配置的语音系统中的音素。

单词片段

如果有一个字符到音素的对应语料库,单词的片段也可以用于发音。使用上面的 "hooiser" 例子,我们可以用以下方式替换音素

hooiser who a>zure<

这将结合当前音韵词典(《base_dictionary.txt》和《custom_words.txt》)中的 "who" 的发音以及单词 "azure" 中的 "-zure"。

括号指向 >at< 您想要贡献给发音的单词片段。这是通过使用与 phonetisaurus 生成的字符到音素对应语料库和预建的音韵词典生成的。在 a>zure< 例子中,单词 "azure" 位于对应语料库中,并且使用了其中的 "zure" 音素的输出。

import io

import rhasspynlu.g2p

# Load existing dictionary
pronunciations = rhasspynlu.g2p.read_pronunciations("/path/to/file.dict")

# Example alignment corpus:
# a}AE z}ZH u|r}ER e}_
alignment = rhasspynlu.g2p.load_g2p_corpus("/path/to/g2p.corpus")

sounds_like = """
hooiser who a>zure<
"""

with io.StringIO(sounds_like) as f:
    rhasspynlu.g2p.load_sounds_like(
        f, pronunciations, g2p_alignment=alignment
    )

print(pronunciations["hooiser"])

# Something like [["HH", "UW", "ZH", "ER"]]

有关 g2p 对应语料库,请参阅 voice2json 配置文件

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源分发

rhasspy-nlu-0.4.0.tar.gz (52.4 kB 查看哈希值)

上传时间

支持者:

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面