未提供项目描述
项目描述
Rhasspy 自然语言理解
用于解析 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
-value
在text
中的起始索引end
-value
在text
中的结束索引(不包括)raw_value
- 识别出实体的值(在(input:output){name}
中的 "input")raw_tokens
-raw_value
中的单词列表raw_start
-raw_value
在raw_text
中的起始索引raw_end
-raw_value
在raw_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语言模型非常有用。这些模型可以由语音识别系统使用,如Pocketsphinx、Kaldi和Julius。
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_fst
和fst_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
参数分别更改这些文件名。
使用来自OpenFST的fstcompile
编译为二进制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 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 0e75b81bdc373031e8484826dc4f99643ae832fa1f05c589f757d493896b48c2 |
|
MD5 | 2c2e81d8b473bfac545cf9817cad9da3 |
|
BLAKE2b-256 | 7eb6715c011b5fd00aad22752a4c8d6598d629a9385f2dbd569a67d502be69c0 |