OfficialKaldi(五)| Kaldi教程(翻译注解)

1 先决条件
2 入门(15分钟)
3 使用Git进行版本控制(5分钟)
4 分发概述(20分钟)
5 运行示例脚本(40分钟)
6 读取和修改代码(30分钟)
1 先决条件
本教程假定您了解使用HMM-GMM方法进行语音识别的基础知识。在线提供的一个简短介绍是:M。Gales和S. Young(2007)。“隐藏的马尔可夫模型在语音识别中的应用。”信号处理的基础和趋势1(3):195-304。HTK书也是不错的资源。但是,除非您具有强大的数学背景并且非常专注,我们不鼓励尝试在机构环境之外学习语音识别。(AI大道理:可见语音之难,门槛之高)本教程的目标读者是语音识别研究人员,或者无论如何仍在研究该领域的毕业生或高级大学生。
我们假设您了解C ++,并且至少对shell脚本有所了解,最好使用bash或类似的shell。本教程假定您使用的是类似UNIX的环境或Cygwin(尽管Kaldi不一定会在所有此类环境中编译和运行)。(AI大道理:也可以在windows下安装,只是相对比较麻烦)
同样,重要的是,本教程假定您可以访问LDC分发的原始数据,即来自语言数据联盟(LDC)的资源管理(RM)CD上的数据。也就是说,我们假设此数据位于您系统上的某个位置。我们以目录号LDC93S3A的形式获得此产品。它也可以分为两个部分。请注意,因为以前RM数据的分布与布局不同。
系统要求是相当基本的。我们假定您具有包括wget,git,svn,awk,perl等在内的工具,或者您知道如何安装它们。安装过程中最困难的部分涉及数学库ATLAS;如果尚未将它作为库安装在系统上,则必须对其进行编译,并且这需要关闭CPU限制,这可能需要root特权。我们为所有安装步骤提供脚本和详细说明。如果脚本失败,请仔细阅读输出,因为它会尝试提供有关如何解决问题的指导。如果有任何问题,无论有小问题,请通知我们;请参阅其他与Kaldi相关的资源(以及如何获得帮助)
我们试图提供一些思路,以执行教程的每个步骤需要多长时间。如果没有足够的时间来完成本教程,我们建议尝试按照发布的时间表进行操作,如有必要,请跳过步骤并避免单击指向我们在本文中提供的更多信息的链接。这将有助于确保您获得一个平衡的概览。以后您总是可以更详细地查看材料。如果要在课堂环境中提供本教程,那么重要的是,事先要有人在相关系统上运行该教程,以验证是否已安装所有先决条件。
2 入门(15分钟)
第一步是下载并安装Kaldi。我们将使用该工具包的版本1,因此本教程不会过时。但是,请注意,“ trunk”(始终是最新的)中的代码和脚本更易于安装,并且通常更好。如果使用“ trunk”代码,则还可以尝试使用目录“ egs / rm / s5”中的最新脚本,而不是本教程中提到的“ s3”脚本。但是请注意,如果这样做,本教程的某些方面可能已过时。(AI大道理:这一点需要注意,我就是使用的kaldi-trunk)
假设已安装Git,则可以输入最新代码
git clone https://github.com/kaldi-asr/kaldi.git
然后CD到Kaldi。查看INSTALL文件并按照说明进行操作(它将指向两个子目录)。仔细查看安装脚本的输出,因为它们会尝试指导您执行操作。某些安装错误不是致命的,安装脚本会告诉您(即安装的某些东西很不错,但并不是真正需要的)。“最佳情况”方案是您执行以下操作:
cd kaldi/tools/; make; cd ../src; ./configure; make
一切都会工作;但是,如果这没有发生,则有备用计划(例如,您可能必须在计算机上安装某些软件包,或在tools /中运行install_atlas.sh,或在tools / INSTALL中手动运行某些步骤,或为configure脚本提供选项在src /)中。如果有问题,那么在构建过程(Kaldi的编译方式)中可能会有一些信息将对您有所帮助;否则,请随时与维护者联系(其他与Kaldi相关的资源(以及如何获得帮助)),我们将很乐意为您提供帮助。
3 使用Git进行版本控制(5分钟)
Git是一个分布式版本控制系统。这意味着,与Subversion不同,它具有存储库的多个副本,并且更改以明确的多种不同方式在这些副本之间传输,但是大多数时候,工作是由存储库的单个副本支持的。由于副本的多样性,您可能需要遵循多个可能的工作流程。如果您只是想首先编译和使用Kaldi,但是我们认为这是最适合您的一个,但是随后在某个时候,您可以选择决定将您的工作贡献给该项目。
首次Git设置
如果您以前从未使用过Git,请首先执行一些最小配置。至少要设置您的姓名和电子邮件地址:
$ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com
另外,为您最常键入的最有用的git命令设置简称。(AI大道理:从来没设置过,感觉没太不要)
$ git config --global alias.co checkout $ git config --global alias.br branch $ git config --global alias.st status
另一个非常有用的实用程序是git-prompts.sh,它是Git的bash提示扩展实用程序(如果没有,请在Internet上搜索如何在系统上安装它)。
安装后,它提供了一个shell函数__git_ps1,当将其添加到提示中时,它会扩展为当前分支名称和挂起的提交标记,因此您不会忘记自己所在的位置。
您可以修改您的PS1shell变量,使其包含字面值$(__git_ps1 "[%s]")。我的~/.bashrc中有这个:
PS1='\[\033[00;32m\]\u@\h\[\033[0m\]:\[\033[00;33m\]\w\[\033[01;36m\]$(__git_ps1 "[%s]")\[\033[01;33m\]\$\[\033[00m\] '
export GIT_PS1_SHOWDIRTYSTATE=true GIT_PS1_SHOWSTASHSTATE=true
# fake __git_ps1 when git-prompts.sh not installed
if [ "$(type -t __git_ps1)" == "" ]; then
function __git_ps1() { :; }
fi
用户工作流程
使用以下命令设置存储库和工作目录:
kkm@yupana:~$ git clone https://github.com/kaldi-asr/kaldi.git --branch master --single-branch --origin golden Cloning into 'kaldi'... remote: Counting objects: 51770, done. remote: Compressing objects: 100% (8/8), done. remote: Total 51770 (delta 2), reused 0 (delta 0), pack-reused 51762 Receiving objects: 100% (51770/51770), 67.72 MiB | 6.52 MiB/s, done. Resolving deltas: 100% (41117/41117), done. Checking connectivity... done. kkm@yupana:~$ cd kaldi/ kkm@yupana:~/kaldi[master]$
现在,您准备配置和编译Kaldi并对其进行操作。有时,您需要本地分支机构中的最新更改。这类似于您通常使用的方法svn update。
但是,请首先让我们同意一件事:您不提交master分支上的任何文件。我们将在下面介绍。到目前为止,您仅使用代码。如果不遵循规则,将很难解开纠结,而且Git的分支是如此之容易,以至于您总是想在分支上进行工作。(AI大道理:可以在自己的分支上任意妄为,影响不到别人)
kkm@yupana:~/kaldi[master]$ git pull golden remote: Counting objects: 148, done. remote: Compressing objects: 100% (55/55), done. remote: Total 148 (delta 111), reused 130 (delta 93), pack-reused 0 Receiving objects: 100% (148/148), 18.39 KiB | 0 bytes/s, done. Resolving deltas: 100% (111/111), completed with 63 local objects. From https://github.com/kaldi-asr/kaldi 658e1b4..827a5d6 master -> golden/master
您使用的命令是git pull,并且golden是我们之前用来指定Kaldi存储库主副本的别名。
从用户到贡献者
在某个时候,您决定更改Kaldi代码,无论是脚本还是源代码。也许您做了一个简单的错误修复。也许您正在贡献整个食谱。无论如何,您总是在分支机构上工作。即使您有未提交的更改,Git也会处理。例如,您刚刚意识到该fisher_english配方实际上并没有hubscr.pl用于评分,而是检查它是否存在并失败。您很快在工作树中修复了该问题,并希望与项目共享此更改。
在分支机构本地工作
kkm@yupana:~/kaldi[master *]$ git fetch golden kkm@yupana:~/kaldi[master *]$ git co golden/master -b fishfix --no-track M fisher_english/s5/local/score.sh Branch fishfix set up to track remote branch master from golden. Switched to a new branch 'fishfix' kkm@yupana:~/kaldi[myfix *]$
因此,我们在这里所做的工作首先是将对黄金存储库的当前更改提取到您的计算机上。这不会更新您的主服务器(实际上,如果您对本地工作树进行了更改,则无法提取),但是确实更新了远程引用golden/master。在第二个命令中,我们在本地存储库中分支了一个分支,称为fishfix。分支是否更合乎逻辑master?一点也不!首先,这是一项操作。您*不需要*更新母版,那么为什么呢?其次,我们同意(还记得吗?)师父将保持不变,而您有所改变。第三,相信我,这种情况会发生,您可能错误地将了一些东西交给了您的主人,并且您不想将这种野性的变化带入您的新分支。
现在,您检查您的更改,由于它们是好的,所以您将其提交:
kkm@yupana:~/kaldi[fishfix *]$ git diff
diff --git a/egs/fisher_english/s5/local/score.sh b/egs/fisher_english/s5/local/score.sh
index 60e4706..552fada 100755
--- a/egs/fisher_english/s5/local/score.sh
+++ b/egs/fisher_english/s5/local/score.sh
@@ -27,10 +27,6 @@ dir=$3
model=$dir/../final.mdl # assume model one level up from decoding dir.
-hubscr=$KALDI_ROOT/tools/sctk/bin/hubscr.pl
-[ ! -f $hubscr ] && echo "Cannot find scoring program at $hubscr" && exit 1;
-hubdir=`dirname $hubscr`
-
for f in $data/text $lang/words.txt $dir/lat.1.gz; do
[ ! -f $f ] && echo "$0: expecting file $f to exist" && exit 1;
done
kkm@yupana:~/kaldi[fishfix *]$ git commit -am 'fisher_english scoring does not really need hubscr.pl from sctk.'
[fishfix d7d76fe] fisher_english scoring does not really need hubscr.pl from sctk.
1 file changed, 4 deletions(-)
kkm@yupana:~/kaldi[fishfix]$
请注意,-a切换到git commit使其提交所有修改的文件(我们只更改了一个,所以为什么不呢?)。如果要将文件修改分为多个功能以分别提交,git add则在git commit不进行-a切换的情况下对特定文件进行单独提交,然后在与下一个修订的第一个分支相同的位置启动另一个分支:git co golden/master -b another-fix –no-track,可以在其中添加和提交其他更改的文件。有了Git,打了十二个分支并不少见。请记住,将多个功能分支组合为一个极其容易,但是将一个大型变更集拆分为许多较小的功能会涉及更多工作。
现在,您需要向Kaldi的维护者创建一个拉取请求,以便他们可以从您的存储库中提取更改。为此,您的存储库需要对他们在线可用。为此,您需要一个GitHub帐户。
一次GitHub设置
  • 转到
  • Kaldi主存储库页面
  • ,然后单击Fork按钮。如果您没有帐户,GitHub将引导您完成必要的步骤。
  • 在GitHub上生成并注册SSH密钥,以便GitHub可以识别您。每个人都可以阅读GitHub上的所有内容,但只有您可以写入分叉的存储库!
创建拉取请求
确保您的fork被注册为名称origin(别名是任意的,这就是我们将在此处使用的名称)。如果没有,请添加它。该URL在您的存储库页面上的“ SSH clone URL”下列出,看起来像。git@github.com:YOUR_USER_NAME/kaldi.git
kkm@yupana:~/kaldi[fishfix]$ git remote -v golden https://github.com/kaldi-asr/kaldi.git (fetch) golden https://github.com/kaldi-asr/kaldi.git (push) kkm@yupana:~/kaldi[fishfix]$ git remote add origin git@github.com:kkm000/kaldi.git kkm@yupana:~/kaldi[fishfix]$ git remote -v golden https://github.com/kaldi-asr/kaldi.git (fetch) golden https://github.com/kaldi-asr/kaldi.git (push) origin git@github.com:kkm000/kaldi.git (fetch) origin git@github.com:kkm000/kaldi.git (push)
现在将分支推入您的Kaldi分支:
kkm@yupana:~/kaldi[fishfix]$ git push origin HEAD -u Counting objects: 632, done. Delta compression using up to 12 threads. Compressing objects: 100% (153/153), done. Writing objects: 100% (415/415), 94.45 KiB | 0 bytes/s, done. Total 415 (delta 324), reused 326 (delta 262) To git@github.com:kkm000/kaldi.git * [new branch] HEAD -> fishfix Branch fishfix set up to track remote branch fishfix from origin.
HEADingit push告诉Git“在远程仓库中创建与当前分支同名的分支”,并-u记住本地分支fishfix与origin/fishfix存储库之间的连接。
现在转到存储库页面并创建拉取请求检查您的更改,如果一切看起来不错,则提交请求。维护者将收到请求并接受或评论。遵循注释,在分支上提交修订,origin再次推送到,GitHub将自动更新拉取请求网页。然后在收到的评论下回复例如“完成”,以便他们知道您对他们的评论进行了跟进。
如果您创建拉取请求仅是为了完成一项不完整的工作,这是有道理的,并且如果您希望对所建议的功能尽早反馈,则可以这样做,建议使用前缀开头拉取请求的标题WIP:。这将告诉维护者尚未合并拉取请求。当您将更多提交推送到分支时,它们会自动显示在拉取请求中。当您认为工作已完成时,请编辑提取请求标题以删除WIP前缀,然后为此添加注释,以便通知维护者。
4 分发概述(20分钟)
在进入示例脚本之前,让我们花几分钟看一下Kaldi发行版中还包含哪些内容。转到kaldi-1目录并列出。有一些文件和子目录。重要的子目录是“ tools /”,“ src /”和“ egs /”,我们将在下一节中介绍它们。我们将概述“ tools /”和“ src /”。
工具/目录(10分钟)
目录“ tools /”是我们以各种方式安装Kaldi所依赖的东西的目录。将目录更改为tools /并列出它。您将看到各种文件和子目录,其中大部分是由make命令安装的东西。在INSTALL文件中,该文件提供了有关如何安装工具的说明。
最重要的子目录是OpenFst的子目录。cd到openfst /。这是到具有版本号的实际目录的软链接。列出openfst目录。如果安装成功,将存在一个包含已安装二进制文件的bin /目录,以及一个包含该库的lib /目录(我们都需要这两个目录)。最重要的代码在目录include / fst /中。如果您想深入了解Kaldi,则需要了解OpenFst。为此,最好的起点是http://www.openfst.org/
现在,仅查看文件include / fst / fst.h。这由一些抽象FST类型的声明组成。您可以看到其中涉及许多模板。如果模板不是您的事,那么您可能很难理解该代码。
将目录更改为bin /,或将其添加到您的路径。我们将从此处执行一些简单的示例指令。
将以下命令粘贴到外壳中:
#弧格式:src dest ilabel olabel [权重] #最终状态格式:状态[权重] #行可以以任何顺序出现,但初始状态必须为第一行 #未指定的权重默认为0.0(对于库默认的权重类型)
# arc format: src dest ilabel olabel [weight] # final state format: state [weight] # lines may occur in any order except initial state must be first line # unspecified weights default to 0.0 (for the library-default Weight type) cat >text.fst <<EOF 0 1 a x .5 0 1 b y 1.5 1 2 c z 2.5 2 3.5 EOF
以下命令创建符号表;
也将它们粘贴到外壳中。
cat >isyms.txt <<EOF <eps> 0 a 1 b 2 c 3 EOF cat >osyms.txt <<EOF <eps> 0 x 1 y 2 z 3 EOF
注意:要执行以下步骤,如果您的路径上没有当前目录,则可能必须输入:
export PATH=.:$PATH
接下来创建一个二进制格式的FST:
fstcompile --isymbols=isyms.txt --osymbols=osyms.txt text.fst binary.fst
让我们执行一个示例命令:
fstinvert binary.fst | fstcompose - binary.fst > binary2.fst
生成的WFST,binary2.fst,应类似于binary.fst,但权重为两倍。
您可以将它们全部打印出来以查看:
fstprint --isymbols=isyms.txt --osymbols=osyms.txt binary.fst fstprint --isymbols=isyms.txt --osymbols=osyms.txt binary2.fst
该示例是从www.openfst.org上
的更长教程中修改而来的。
完成此操作后,请键入以下内容进行清理:
rm *.fst *.txt
src /目录(10分钟)
将目录更改回最高级别(kaldi-1),然后更改为src /。列出目录。您将看到一些文件和大量的子目录。查看Makefile。在顶部设置变量SUBDIRS。这是包含代码的子目录的列表。请注意,其中一些以“ bin”结尾。这些是包含可执行文件的文件(代码和可执行文件在同一目录中)。其他目录包含内部代码。
您可以看到Makefile中的目标之一是“ test”。键入“进行测试”。该命令进入各个子目录并在其中运行测试程序。所有测试都应该成功。如果您感到幸运,也可以键入“ make valgrind”。这将使用内存检查器运行相同的测试,并且花费更长的时间,但会发现更多错误。如果这行不通,那就算了吧;目前不重要。如果花费的时间太长,请使用ctrl-c停止它。
将目录更改为base /。查看Makefile。注意行
包括../kaldi.mk
每当调用子目录中的Makefile时,这些行就逐字包括文件../ kaldi.mk(就像C #include指令一样)。查看文件../kaldi.mk。它将包含一些与valgrind相关的规则(用于内存调试),然后包含一些特定于系统的配置,这些配置以变量的形式出现,例如CXXFLAGS。查看是否有-O选项(例如-O0)。默认情况下,标志-O0和-DKALDI_PARANOID被禁用,因为它们会使速度变慢(您可能希望启用它们以进行更好的调试)。再看一下base / Makefile。顶部的语句“ all:”表明使“ all”是顶级目标(因为kaldi.mk中存在目标,并且我们不希望它们成为顶级目标)。由于“ all”的依赖项取决于以后定义的变量,我们还有另一条语句(目标在default_rules.mk中定义),在其中定义“所有”所依赖的内容。寻找它。还定义了其他几个目标,从“ clean”开始。寻找他们。要使“干净”,您可以键入“ make clean”。目标.valgrind不是您要从命令行调用的内容;您将键入“ make valgrind”(目标在kaldi.mk中定义)。调用所有这些目标,即键入“ make clean”,对其他目标键入相同的名称,并注意执行此操作时将发出什么命令。valgrind不是您要从命令行调用的内容;您将键入“ make valgrind”(目标在kaldi.mk中定义)。调用所有这些目标,即键入“ make clean”,对其他目标键入相同的名称,并注意执行此操作时将发出什么命令。valgrind不是您要从命令行调用的内容;您将键入“ make valgrind”(目标在kaldi.mk中定义)。调用所有这些目标,即键入“ make clean”,对其他目标键入相同的名称,并注意执行此操作时将发出什么命令。
在base /目录的Makefile中:选择TESTFILES中列出的二进制文件之一,然后运行它。然后简要查看相应的.cc文件。数学示例是一个很好的例子(请注意:这不包括Kaldi中的大多数数学函数,这些函数与矩阵向量相关,并且位于../matrix/中)。注意,使用宏KALDI_ASSERT有很多断言。这些测试程序设计为在出现问题时退出,并显示错误状态(不应依赖人工检查输出)。
查看标题kaldi-math.h。您将看到我们编码实践的一些要素。请注意,我们所有的本地#include都是相对于src /目录的(因此,即使我们已经在base /目录中,我们也#include base / kaldi-types.h)。请注意,我们#define的所有宏(除了我们只是要确保具有其正常值的标准宏)均以KALDI_开头。这是一种预防措施,可避免将来与其他代码库发生冲突(因为#define不会将自己限制在kaldi名称空间内)。注意函数名称的样式:LikeThis()。我们的样式通常基于样式,以与OpenFst保持一致,但是存在一些差异。
要查看样式的其他元素,这将有助于您了解Kaldi代码,cd到../util以及查看text-utils.h。请注意,这些函数的输入始终是第一个,通常是const引用,而输出(或已修改的输入)则总是最后一个,并且是指针参数。不允许将非常量引用用作函数参数。如果您有兴趣,可以稍后在此处阅读有关Kaldi特定元素的编码样式的更多信息。现在,请注意,存在一种具有非常特定规则的编码样式。
将目录更改为../gmmbin并键入
./gmm-init-model
它打印出用法,这应该使您大致了解如何调用Kaldi程序。请注意,尽管有一个–config选项可用于传递配置文件,但通常Kaldi并不像HTK那样受配置驱动,并且这些文件并未得到广泛使用。您将看到–binary选项。通常,Kaldi文件格式有二进制和文本两种格式,并且–binary选项控制它们的编写方式。但是,这仅控制单个对象(例如声学模型)的写入方式。对于整个对象集合(例如要素文件的集合),我们将在以后讨论另一种机制。类型
./gmm-init-model> / dev / null
您会看到什么,这又告诉您Kaldi如何使用日志记录型输出?使用情况消息所在的位置与所有错误消息和日志记录消息所在的位置相同,这是有原因的,当您开始查看脚本时,这应该很明显。
要对构建过程有所了解,请使用cd到../matrix,然后键入
rm * .o 使
查看传递给编译器的选项。这些最终由../kaldi.mk中设置的变量控制,而这些变量又由../configure确定。还要查看链接选项,该选项在创建matrix-lib-test时传递。您将了解到它所链接的数学库(这在某种程度上取决于系统)。有关我们如何使用外部矩阵库的更多信息,您可以阅读外部矩阵库
将目录更改为上一级(至src /),然后查看“配置”文件。如果您熟悉automake生成的“配置”文件,您会发现它不是其中之一。它是手工生成的。在其中搜索“ makefiles /”,然后快速扫描该字符串出现的所有位置(例如,键入shell“ less configure”,键入“ / makefiles [enter]”,然后键入“ n”以查看以后的实例)。您将在子目录“ makefiles /”中使用带有后缀.mk的某些文件。这些本质上是kaldi.mk的“原型”版本。查看其中一个原型,例如makefiles / cygwin.mk,以查看其中包含的东西的种类。对于更可预测的系统,它只是将系统特定的makefile与makefiles / kaldi.mk.common串联在一起,并将其写入kaldi.mk。对于Linux,它必须做更多的工作,因为发行版太多了。通常,这与查找数学库的安装位置有关。如果您在构建过程中遇到问题,一种解决方案是尝试手动修改kaldi.mk。为此,您可能应该了解Kaldi如何利用外部数学库(请参见外部矩阵库)。

posted on 2020-12-10 11:15  AI大道理  阅读(446)  评论(0编辑  收藏  举报

导航