kaldi lesson教程示例

创建示例目录

第一步:egs目录下创建lesson文件夹,lesson文件夹创建版本标识文件夹v1
mkdir lesson
cd lesson
mkdir v1

结果展示

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs# ls -l lesson/
total 4
drwxr-xr-x 2 root root 4096 519 10:32 v1

为什么lesson下还要创建v1?

  • v1表示第一个版本,方便版本管理
  • 和path.sh返回目录一致, 后面会创建path.sh脚本。KALDI_ROOT为安装目录,如果不创建v1文件夹,这个路径需要修改一下。
    在这里插入图片描述
第二步:创建软链接到steps, utils,sid(声纹任务)

进入v1文件夹,使用下面命令创建软链接, 其中不是声纹识别相关任务,不需要创建sid的软链接

cd v1
ln -s ../../wsj/s5/steps/ .
ln -s ../../wsj/s5/utils/ .
ln -s ../../sre08/v1/sid .

使用软连接是为了节约磁盘空间,linux软连接相当于windows快捷方式。steps封装了语音识别各个阶段的标准化处理脚本,utils封装语音识别阶段用到工具和方法。
在这里插入图片描述

第三步:创建cmd.sh文件,设置运行方式,单机还是集群

vim cmd.sh
添加如下内容:

export train_cmd="run.pl"

run.pl指定本机运行,如果用集群,可以按照这种配置

export train_cmd="queue.pl --mem 2G"
export decode_cmd="queue.pl --mem 4G"
export mkgraph_cmd="queue.pl --mem 8G"

train_cmd是训练运行方式,decode_cmd是解码运行方式,mkgraph_cmd是构建计算图运行方式,通常情况下,一般只用到run.pl, 单机运行方式

第四步:创建path.sh环境变量文件

可以通过下面命令复制其它egs下path.sh到该目录下,也可以vim path.sh 然后复制下面内容进行创建

  • 方式1:vim path.sh, 复制下面内容到文件里,然后wq!保存
export KALDI_ROOT=`pwd`/../../..
[ -f $KALDI_ROOT/tools/env.sh ] && . $KALDI_ROOT/tools/env.sh
export PATH=$PWD/utils/:$KALDI_ROOT/tools/openfst/bin:$PWD:$PATH
[ ! -f $KALDI_ROOT/tools/config/common_path.sh ] && echo >&2 "The standard file $KALDI_ROOT/tools/config/common_path.sh is not present -> Exit!" && exit
1
. $KALDI_ROOT/tools/config/common_path.sh
export LC_ALL=C

  • 方式2:
cp ../../aishell/s5/path.sh .

详细解读下path.sh内容:

export KALDI_ROOT=pwd/../../.. 这行代码是添加kaldi安装目录到环境变量, 相当于在环境变量里添加/opt/asr/kaldi, 但是export是临时添加,机器重启之后会清掉这个环境变量。一般都选用临时添加kaldi运行环境变量。

[ -f $KALDI_ROOT/tools/env.sh ] && . $KALDI_ROOT/tools/env.sh 这行脚本是先判断env.sh文件是否存在,如果存在执行这个文件。env.sh用来导入python执行程序路径,通常指定python版本也在这个地方指定。具体修改python运行版本要去tools/python文件夹下。kaldi采用的python版本是python2,如果要用python3,可以修改python运行软连接指向python3执行程序。

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1# more ../../../tools/env.sh
export PATH=/opt/asr/kaldi/tools/python:${
   PATH}
export PATH=/opt/asr/kaldi/tools/python:${
   PATH}
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1# cd  ../../../tools/python/
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/tools/python# ls
python  python2
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/tools/python# ls -l
total 0
lrwxrwxrwx 1 root root 18 310 22:50 python -> /usr/bin/python2.7
lrwxrwxrwx 1 root root 18 124 08:02 python2 -> /usr/bin/python2.7

export PATH=$PWD/utils/:$KALDI_ROOT/tools/openfst/bin:$PWD:$PATH 这样代码添加kaldi utils 和 openfst/bin目录下内容到环境变量

[ ! -f $KALDI_ROOT/tools/config/common_path.sh ] && echo >&2 "The standard file $KALDI_ROOT/tools/config/common_path.sh is not present -> Exit!" && exit
1

判断common_path.sh脚本是否存在,如果不存在,推出当前shell,然后输出执行错误结果1

上面四行脚本执行都没问题,执行 . $KALDI_ROOT/tools/config/common_path.sh这个脚本,这个脚本负责添加kaldi源代码编译后可执行文件的路径到环境变量中,我们在linux执行这些可执行文件,不在需要指定其所在路径,直接用文件名即可。有哪些可执行文件,参考common_path的文件内容,其按照不同的功能将可执行文件组织在不同的文件夹。

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1# more ../../../tools/config/common_path.sh
# we assume KALDI_ROOT is already defined
[ -z "$KALDI_ROOT" ] && echo >&2 "The variable KALDI_ROOT must be already defined" && exit 1
# The formatting of the path export command is intentionally weird, because
# this allows for easy diff'ing
export PATH=\
${KALDI_ROOT}/src/bin:\
${KALDI_ROOT}/src/chainbin:\
${KALDI_ROOT}/src/featbin:\
${KALDI_ROOT}/src/fgmmbin:\
${KALDI_ROOT}/src/fstbin:\
${KALDI_ROOT}/src/gmmbin:\
${KALDI_ROOT}/src/ivectorbin:\
${KALDI_ROOT}/src/kwsbin:\
${KALDI_ROOT}/src/latbin:\
${KALDI_ROOT}/src/lmbin:\
${KALDI_ROOT}/src/nnet2bin:\
${KALDI_ROOT}/src/nnet3bin:\
${KALDI_ROOT}/src/nnetbin:\
${KALDI_ROOT}/src/online2bin:\
${KALDI_ROOT}/src/onlinebin:\
${KALDI_ROOT}/src/rnnlmbin:\
${KALDI_ROOT}/src/sgmm2bin:\
${KALDI_ROOT}/src/sgmmbin:\
${KALDI_ROOT}/src/tfrnnlmbin:\
${KALDI_ROOT}/src/cudadecoderbin:\
$PATH

export LC_ALL=C最后一行脚本是将linux环境设置成c语言运行环境,这样好处是我们使用kaldi编译的可执行文件时,可以当成linux命令来使用,cat, ls等。不得不说这个设计太让人叹服!!!。

执行 source path.sh 看看path.sh的威力吧,
在这里插入图片描述
如上图所示,是不是可以随意使用${KALDI_ROOT}/src/featbin:\源码下内容了,如果没有添加环境变量,得到是下面的结果
在这里插入图片描述
好了,path.sh内容介绍完,进入下一个环节,创建local, feats, data,目录

第五步:创建conf, local, feats, data, exp文件夹

创建conf和local文件夹,conf下面存放和提特征相关的配置文件,如设置采样频率,mel滤波个数等,一般有mfcc.conf和vad.conf文件。 local下面放的是这个工程相关的子脚本,前面介绍介绍utils/ steps/存放的是主脚本。

feats存放mfcc或者fbank特征数据,data存放数据list,如wav.scp, utt2spk, spk2utt等。exp存放是模型文件和训练相关中间结果。

除了conf, local外,其它三个文件夹在准备阶段可以不用创建,后续主脚本执行过程中会创建的。

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1# mkdir -p local conf data exp feats

综上,创建目录示例就已经完成了,看看现在lesson工程下目录内容

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1# ls
cmd.sh  conf  data  exp  feats  local  path.sh  steps  utils

编写脚本run.sh

下面内容都围绕run.sh的内容展开,run.sh的参考模板如下,下面争取把每个环节内容填满。

#!/usr/bin/env bash
set -eo pipifail
. ./path.sh
. ./cmd.sh
# Set global env info
# Data preparation
# Feature extraction
# Mono training
# Graph compilation
# Decoding

run.sh前四行基本都是固定不变,脚本中"."是执行的意思,相当于source. 首先声明set -eo pipifail,这个脚本主要用来控制后续脚本如果有运行异常或者错误的立即退出当前脚本的运行,shell脚本运行时从前往后进行,如果前面的脚本运行错误,后续的脚本也能正常运行,但是后面脚本运行往往依赖前面脚本的运行结果,所以设置set -eo pipifail,一旦遇到有运行错误的脚本或者管道符执行错误则退出当前运行。
. ./path.sh, . ./cmd.sh 分别执行环境变量脚本和设置运行方式的脚本,这个两个脚本已经重点介绍过了。

Set global env info

指定源数据目录,数据下载目录,脚本执行控制指针,训练数据,特征数据,如下面这段代码。

data=/data/cn-celeb1
data_url=https://openslr.magicdatatech.com/resources/82
stage=1
stop_stage=10
train_data=data/mfcc/train
featdir=feats/mfcc
. tools/parse_options.sh

如果在运行run时候修改这些变量的值,这需要添加. tools/parse_options.sh这行代码,他的作用是允许运行脚本中修改变量的默认值, 如 bash run.sh --stage 2 --stop_stage 20。

Data preparation

数据准备阶段主要是下载数据,如果已经手动下载数据了,可以跳过这个步骤。
这阶段要掌握的就是如何根据自己实际问题修改download_and_untar.sh脚本。


# Data preparation
if [ $stage -le 1 ] && [ $stop_stage -ge 1 ]; then
        for part in cn-celeb; do
                local/download_and_untar.sh $data $data_url $part
        done
fi

download_and_untar.sh的用法如下,这个脚本主要完成数据下载和解压,详细内容没什么好分析的,用法如下: 其中尖括号是必填参数,中括号中是可选参数。

download_and_untar.sh [--remove-archive] <data-base> <url-base> <corpus-part>

带上–remove-archive参数下载解压完成之后会删掉下载压缩包。data-base参数是下载数据存放的路径,如果不存在文件夹需要事先创建这个文件夹,url-base是数据集去除文件名的url, 如cn-celeb的数据集完整urll为https://www.openslr.org/resources/82/cn-celeb.tgz,那么url-base=https://www.openslr.org/resources/82
,corpus-part=cn-celeb, 所以如果我们要下载cn-celeb,cn-celeb2-part1,cn-celeb2-part1这三个数据集,他们url-base都一样,只需要指定为相应问文件夹名即可,具体操作就是修改这个脚本里面list为list="cn-celeb cn-celeb2-part1 cn-celeb-part2",download_and_untar.sh脚本可以参考cslt写的这个
在这里插入图片描述
注释54-67行代码, 由于重新下载时需要判断原有文件是否完整,不完整删除重新下载。
在这里插入图片描述
重点:download_and_untar.sh
- 修改list
- 注释54-67行脚本

这个阶段主要内容就是这些,基本上就是在这个模板进行修改。

准备wav.scp utt2spk spk2utt text文件

这四个文件通常位于主目录data下,而且缺一不可,为后续特征提取提供数据资料
完整数据又1002个说话人,截取cn-celeb数据一部分

(base) root@ai-PowerEdge-R740:/data/cn-celeb1/CN-Celeb/data# ls -l | wc -l
1002
(base) root@ai-PowerEdge-R740:/data/cn-celeb1/CN-Celeb/data# ls | head
data
id00000
id00001
id00002
id00003
id00004
id00005
id00006
id00007
id00008

在data目录下创建./mfcc/train文件夹, 复制400句语音文件到wav.lst, 关键用法是使用find的命令,wav.lst得到音频路径可以是相对路径也可以是绝对路径,一般用绝对路径

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# find /data/cn-celeb1/CN-Celeb/data/ -name "*.wav" | head -400 > wav.lst
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# ls
wav.lst
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# wc wav.lst
  400   400 23531 wav.lst
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# head -2 wav.lst
/data/cn-celeb1/CN-Celeb/data/id00055/play-02-015.wav
/data/cn-celeb1/CN-Celeb/data/id00055/play-02-006.wav

使用soxi查看音频格式

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# soxi /data/cn-celeb1/CN-Celeb/data/id00055/play-02-016.wav

Input File     : '/data/cn-celeb1/CN-Celeb/data/id00055/play-02-016.wav'
Channels       : 1
Sample Rate    : 16000
Precision      : 16-bit
Duration       : 00:00:10.91 = 174615 samples ~ 818.508 CDDA sectors
File Size      : 349k
Bit Rate       : 256k
Sample Encoding: 16-bit Signed Integer PCM

接下来要创建wav.scp文件,它的第一列是uttID, 第二列是wav路径。 uttID是以spkID_wavID的构成方式,spkID是uttID的前缀码。

获取spkID的脚本, 使用 awk -F '/'是对每行内容进行分隔的分隔符,这样分隔以后,spkID刚好是第6列,'{print $6}'打印第6列出来就得到spkID

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat wav.lst  | awk -F '/' '{print $6}' | head
id00055
id00055

打印wavID, wavID可以不用去掉扩展名wav格式

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat wav.lst  | awk -F '/' '{print $7}' | head
play-02-015.wav
play-02-006.wav

打印spkID_wavID, 这样拼接$6"_"$7

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat wav.lst  | awk -F '/' '{print $6"_"$7}' | head
id00055_play-02-015.wav
id00055_play-02-006.wav

打印uttID spkID

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat wav.lst  | awk -F '/' '{print $6"_"$7,$6}' | head
id00055_play-02-015.wav id00055

去掉uttID的wav扩展名, sed "s\.wav\ \g" s代表替换,将.wav替换为空格, 其实这里uttID可以带上扩展名,可以不用去掉,并不影响特征提取

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat wav.lst  | awk -F '/' '{print $6"_"$7,$6}' | sed "s\.wav\ \g" | head
id00055_play-02-015  id00055
id00055_play-02-006  id00055
id00055_play-02-004  id00055

将原来两个空格变成一个空格,awk -F ' ' '{print $1, $2}'

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat wav.lst  | awk -F '/' '{print $6"_"$7,$6}' | sed "s\.wav\ \g" | awk -F ' ' '{print $1, $2}' | head
id00055_play-02-015 id00055
id00055_play-02-006 id00055
id00055_play-02-004 id00055

现在可以生产utt2spk文件了

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat wav.lst  | awk -F '/' '{print $6"_"$7,$6}' | sed "s\.wav\ \g" | awk -F ' ' '{print $1, $2}' > utt2spk
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# ls
utt2spk  wav.lst
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# head utt2spk
id00055_play-02-015 id00055
id00055_play-02-006 id00055

下面可以根据utt2spk和wav.lst生成wav.scp文件

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# head -2 *
==> utt2spk <==
id00055_play-02-015 id00055
id00055_play-02-006 id00055

==> wav.lst <==
/data/cn-celeb1/CN-Celeb/data/id00055/play-02-015.wav
/data/cn-celeb1/CN-Celeb/data/id00055/play-02-006.wav
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# paste -d ' ' utt2spk wav.lst | awk '{print $1, $3}' | head -2
id00055_play-02-015 /data/cn-celeb1/CN-Celeb/data/id00055/play-02-015.wav
id00055_play-02-006 /data/cn-celeb1/CN-Celeb/data/id00055/play-02-006.wav
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# paste -d ' ' utt2spk wav.lst | awk '{print $1, $3}' > wav.scp
id00055_play-02-015 /data/cn-celeb1/CN-Celeb/data/id00055/play-02-015.wav
id00055_play-02-006 /data/cn-celeb1/CN-Celeb/data/id00055/play-02-006.wav

生成wav.scp文件

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# head -2 *
==> utt2spk <==
id00055_play-02-015 id00055
id00055_play-02-006 id00055

==> wav.lst <==
/data/cn-celeb1/CN-Celeb/data/id00055/play-02-015.wav
/data/cn-celeb1/CN-Celeb/data/id00055/play-02-006.wav

==> wav.scp <==
id00055_play-02-015 /data/cn-celeb1/CN-Celeb/data/id00055/play-02-015.wav
id00055_play-02-006 /data/cn-celeb1/CN-Celeb/data/id00055/play-02-006.wav

接下来需要将wav.scp和utt2spk文件重新排序一下,保证两者之间每一行是一一对应的

(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat wav.scp | sort -k 1,1 -u -o wav.scp
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# cat utt2spk | sort -k 1,1 -u -o utt2spk
(notebook) root@ai-PowerEdge-R740:/opt/asr/kaldi/egs/lesson/v1/data/mfcc/train# head utt2spk wav.scp
==> utt2spk <==
id00055_interview-01-001 id00055
id00055_interview-01-002 id00055
id00055_interview-01-003 id00055
id00055_interview-01-004 id00055
id00055_interview-01-005 id00055
id00055_interview-01-006 id00055
id00055_interview-01-007 id00055
id00055_interview-01-008 id00055
id00055_play-01-001 id00055
id00055_play-01-002 id00055

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值