【Kaldi例子】Librispeech数据整理

数据分集

数据采集自有声书网站,首先对每个句子做一遍语音识别,识别模型使用WSJ示例中的声学模型,语言模型使用二元文法,语言模型数据为语音数据对应的电子书文本。根据识别结果,统计每个说话人的WER,从低到高排序,前一半标记为clean,表示这些说话人语音比较清晰,其余标记为other。

从clean数据中,随机抽取20名男性和20名女性作为开发集(dev-clean),剩余说话人抽取相同规模的测试集(test-clean),其余作为训练集。训练集随机分为100小时和360小时的子集(train-clean-100和train-clean-360)。

在other数据中,WER从低到高排序,在第三个四分位点附近随机选择开发集(dev-other)合测试集(test-other),剩余作为训练集(train-other-500)。

分集结果为:

子集 每人时长(分钟) 时长(小时) 女性数目 男性数目 说话人数目
dev-clean 8 5.4 20 20 40
test-clean 8 5.4 20 20 40
dev-other 5.3 10 16 17 33
test-other 5.1 10 17 16 33
train-clean-100 100.6 25 125 126 251
train-clean-360 363.6 25 439 482 921
train-other-500 496.7 30 564 602 1166

对应的数据目录为:

BOOKS.TXT
CHAPTERS.TXT
dev-clean
dev-other
LICENSE.TXT
README.TXT
SPEAKERS.TXT
test-clean
test-other
train-clean-100
train-clean-360
train-other-500

除了7个分集目录外,其余文件内容分别书籍信息(编号|书名)、章节信息(编号|阅读者编号|时长|所属子集)、说话人信息(ID|READER|MINUTES|SUBSET|PROJ.|BOOK ID|CH. TITLE|PROJECT TITLE)。

每个分集中目录结构为说话人编号/章节编号/数据,数据结构如下:

LibriSpeech/train-clean-100/103/1240
├── 103-1240.trans.txt
├── 103-1240-0001.flac
├── 103-1240-0002.flac
├── 103-1240-0003.flac
├── 103-1240-0004.flac
├── 103-1240-0005.flac
├── 103-1240-0006.flac
├── 103-1240-0007.flac
├── 103-1240-0008.flac
├── 103-1240-0009.flac
├── 103-1240-0010.flac
├── 103-1240-0011.flac
...

*.flac为音频文件,*.trans.txt记录每个音频文件对应的文本内容。

数据预处理

因为不同数据库原始格式不同,所以要给每个数据库单独写预处理脚本,以便Kaldi处理。

将原始数据转换为Kaldi数据文件夹

运行local/data_prep.sh

data
├── dev_clean
│   ├── spk2gender
│   ├── spk2utt
│   ├── text
│   ├── utt2spk
│   └── wav.scp
├── dev_other
│   ├── spk2gender
│   ├── spk2utt
│   ├── text
│   ├── utt2spk
│   └── wav.scp
├── test_clean
│   ├── spk2gender
│   ├── spk2utt
│   ├── text
│   ├── utt2spk
│   └── wav.scp
├── test_other
│   ├── spk2gender
│   ├── spk2utt
│   ├── text
│   ├── utt2spk
│   └── wav.scp
├── train_clean_100
│   ├── spk2gender
│   ├── spk2utt
│   ├── text
│   ├── utt2spk
│   └── wav.scp
└── local
    └── lm
        ├── 3-gram.arpa.gz
        ├── 3-gram.pruned.1e-7.arpa.gz
        ├── 3-gram.pruned.3e-7.arpa.gz
        ├── 4-gram.arpa.gz
        ├── g2p-model-5
        ├── librispeech-lexicon.txt
        ├── librispeech-lm-corpus.tgz
        ├── librispeech-vocab.txt
        ├── lm_fglarge.arpa.gz -> 4-gram.arpa.gz
        ├── lm_tglarge.arpa.gz -> 3-gram.arpa.gz
        ├── lm_tgmed.arpa.gz -> 3-gram.pruned.1e-7.arpa.gz
        └── lm_tgsmall.arpa.gz -> 3-gram.pruned.3e-7.arpa.gz

7 directories, 37 files

除了spk2gender记录说话人性别外,其他和例子yesno结果类似。这些Kaldi脚本可接受的标准形式文件我们称为表单文件。生成表单的关键语句如下,具体形成表单文件步骤见脚本。

# wav.scp
find -L $chapter_dir/ -iname "*.flac" | sort | xargs -I% basename % .flac | awk -v "dir=$chapter_dir" '{printf "lbi-%s flac -c -d -s %s/%s.flac |\n", $0, dir, $0}' >>$wav_scp || exit 1;
# text
sed -e 's/^/lbi\-/' $chapter_trans >> $trans
# utt2spk
awk -v "reader=$reader" -v "chapter=$chapter" '{printf "lbi-%s lbi-%s-%s\n", $1, reader, chapter}' <$chapter_trans >>$utt2spk || exit 1
# spk2gender
reader_gender=$(egrep "^$reader[ ]+\|" $spk_file | awk -F'|' '{gsub(/[ ]+/, ""); print tolower($2)}')
echo "lbi-${reader}-${chapter} $reader_gender" >>$spk2gender
# spk2utt
utils/utt2spk_to_spk2utt.pl <$utt2spk >$spk2utt || exit 1

需要注意一点,utt2spk中,说话人是由说话人编号加上章节编号标注的,因为Kaldi对说话人的定义并不局限于自然人,而是一种一致的发音状态。

生成好表单后,需要进行检查:

# 检查行数是否对应
if $prepare_text; then
  ntrans=$(wc -l <$trans)
  nutt2spk=$(wc -l <$utt2spk)
  ! [ "$ntrans" -eq "$nutt2spk" ] && \
    echo "Inconsistent #transcripts($ntrans) and #utt2spk($nutt2spk)" && exit 1;
fi
# 检查文件内容 --no-feats表示跳过feats检查,utils/validate_data_dir.sh在每次生成新的表单时都要调用
utils/validate_data_dir.sh --no-feats $($prepare_text || echo "--no-text") $dst || exit 1;

Kaldi输入输出机制

在Kaldi中,所有数据文件都是以表单形式储存的。Kaldi定义了两种表单,分别是列表(Script-file)表单和存档(Archive)表单,在此基础上建立其特有的输入输出机制。

列表表单

默认但不限制扩展名为.scp,格式如下:

file1_index /path/to/file1
file2_index /path/to/file2

空格前的字符串是表单索引,之后的内容是文件定位符,用于定位文件。文件定位符可以是磁盘中的物理地址,也可以是以管道形式表示的内存地址,如:

# 物理地址
103-1240-0000 /path/to/103-1240-0000.wav
# 管道内存
103-1240-0000 flac -c -d -s /path/to/103-1240-0000.flac|
# 存档文件
103-1240-0000 /path/to/raw_mfcc_train_clean_100.ark:17
103-1240-0000 /path/to/raw_mfcc_train_clean_100.ark:17[:,0:9] # 获取前10维

存档表单

存档表单用于存储数据,可以使文本数据,也可以是二进制数据,一般扩展名为.ark。存档表单没有行的概念。

对于文本类型的存档文件,每个元素以换行符结尾,虽然展现形式很像列表表单,但是本质不同。

file1_index first text\nfile2_index second text\n

在二进制存档表单中,索引以每个字符对应的ASCII值存储,然后是一个空格加“\0B”,"\0B"是区分文本存档表单和二进制存档表单的标识。索引之后是二进制的表单元素内容,直到下一个索引。在读取二进制存档表单时可以通过内容本身判断元素占空间大小,这些信息保存在文件头中,格式如下:

binary_index1 \0B
binary_index2 \0B

读写声明符

除了生成以上文件外,还可以使用一下命令,提取音频的时长信息:

$cmd JOB=1:$nj $data/log/get_durations.JOB.log wav-to-duration --read-entire-file=$read_entire_file scp:$sdata/JOB/wav.scp ark,t:$sdata/JOB/utt2dur

其中,scp:ark,t:称为读写声明符,分别表示列表表单和文本存档表单,此外,还可同时存储一个存档表单和一个列表表单,声明符为ark,scp:。冒号后的是表单文件名,文件名可以是:

  • 磁盘路径。
  • 标准输入(用-表示)。
  • 管道符号。
  • 磁盘路径加偏移定位符。

写法和列表表单的元素内容相似。

# 管道输入,标准输出
wav-to-duration 'scp:echo "utt1 data/103-1240-0000.wav" |' ark,t:-
# 管道输入,文件输出
wav-to-duration 'scp:echo "utt1 data/103-1240-0000.wav" |' ark,t:data/utt2dur
# 管道输入,输出到压缩文件
wav-to-duration 'scp:echo "utt1 data/103-1240-0000.wav" |' 'ark,t:| gzip -c > data/utt2dur.gz'

表单属性

读写声明符另一个重要部分是表单属性,如ark,t:中的t就表示使用文本模式的存档表单。相应的,表单属性也分为读属性和写属性。

写属性包括:

  • 表单类型:scparkark,scp

  • 二进制模式:b

  • 文本模式:t

  • 刷新模式:fnf,声明是否在每次写操作后刷新数据流,默认刷新

  • 宽容模式:p,如果表单某个元素对应存档内容无法获取,跳过该元素而不提示错误。

读属性包括:

  • 表单类型:scpark

  • 单次访问:ono,告知程序表单中每个索引只出现一次

  • 宽容模式:p,如果表单某个元素对应存档内容无法获取,跳过该元素而不提示错误

  • 有序表单:sns,表示表单是有序的

  • 有序访问:csncs,表单元素将被顺序访问

常用数据表单和处理脚本

Kaldi数据预处理的表单文件都保存在数据文件夹中,在通用脚本文件夹utils中有个叫data的文件夹,其中大部分脚本都是用于处理数据文件夹的,输入和输出都是文件夹路径。

在Kaldi脚本定义中,一个数据文件夹是其包含的若干数据形成的一个数据集,文件夹中的表单文件定义了关于这个数据集的所有信息,如音频文件路径、标注内容、特征文件路径、说话人信息等。其中表单文件的文件名都是在通用脚本中定义好的,具体如下。

列表表单

句子音频表单

文件名:wav.scp

例子:speaker_chapter_utterance /path/to/audio/speaker/filename.wav

声学特征表单

文件名:feats.scp

例子:speaker_chapter_utterance /path/to/s5/mfcc/raw_mfcc_filename.ark:17

谱特征归一化表单

文件名:cmvn.scp

例子:speaker_chapter_utterance /path/to/s5/mfcc/cmvn_train_filename.ark:9

VAD信息表单

文件名:vad.scp

例子:speaker_chapter_utterance /path/to/s5/mfcc/vad_train_filename.ark:9

存档表单

说话人映射表单

文件名:utt2spk

例子:speaker_chapter_utterance speaker_chapter

文件名:spk2utt

例子:speaker_chapter speaker_chapter_utterance1 speaker_chapter_utterance2 speaker_chapter_utterance3 ...

标注文本文件

文件名:text

例子:103-1240-0000 CHAPTER ONE MISSUS RACHEL ...

切分信息表单

文件名:segments

例子:103-1240-0000 103-1240 2.81 6.41

VTLN相关系数表单

  • 说话人性别映射:spk2gender
  • 说话人卷曲因子映射:spk2warp
  • 句子卷曲因子映射:utt2warp

句子时长表单

文件名:utt2dur

例子:103-1240-0000 14.085

数据文件夹处理脚本

脚本名 功能简介
combine_data.sh 将多个数据文件夹合并为一个,并合并对应的表单
combine_short_segments.sh 合并原文件夹中的短句,创建一个新的数据文件夹
copy_data_dir.sh 复制原文件夹,床架一个新的数据文件夹,可指定说话人说句子的前缀、后缀,复制一部分数据
extract_wav_segments_data_dir.sh 利用原文件夹中的分段信息,切分音频文件,并保存为一个新的数据文件夹
fix_data_dir.sh 为原文件夹保留一个备份,删除没有同时出现在多个表单中的句子,并修正排序
get_frame_shift.sh 获取数据文件夹的帧移信息,打印到屏幕
get_num_frames.sh 获取数据文件夹的总帧数信息,打印到屏幕
get_segments_for_data.sh 获取音频时长信息,转换为segments文件
get_utt2dur.sh 获取音频时长信息,生成utt2dur文件
limit_feature_dim.sh 根据原数据文件夹中的feats.scp,取其部分维度的声学特征,保存到新创建的数据文件夹中
modify_speaker_info.sh 修改原数据文件夹中的说话人索引,构造“伪说话人”,保存到新创建的数据文件夹中
perturb_data_dir_speed.sh 为原数据文件夹创建一个速度扰动的副本
perturb_data_dir_volume.sh 修改数据文件夹中的wav.scp文件,添加音量扰动效果
remove_dup_utts.sh 删除原数据文件夹中文本内容重复超过指定次数的句子,保存到新创建的数据文件夹中
resample_data_dir.sh 修改数据文件夹中的feats.scp进行特征偏移,保存到新创建的数据文件夹中
shift_feats.sh 根据原数据文件夹中的feats.scp进行特征偏移,
split_data.sh 将数据文件夹分成指定数目的多个子集,保存到原数据文件夹中以split开头的目录下
subsegment_data_dir.sh 根据一个额外提供的切分信息文件,将原数据文件夹重新切分,创建一个重切分的数据文件夹
subset_data_dir.sh 根据指定的方法,床架一个原数据文件夹的子集,保存为新创建的数据文件夹
validata_data_dir.sh 检查给定数据文件夹的内容,包括排序是否正确、元素索引是否对应等

语言模型

在开始训练声学模型前,需要定义发音词典、音素集和HMM的结构。在进行音素上下文聚类时,还可以通过指定聚类问题的方式融入先验知识。这些步骤均在Librispeech总脚本第三阶段完成。

if [ $stage -le 3 ]; then
  # 整理发音词典
  local/prepare_dict.sh --stage 3 --nj 30 --cmd "$train_cmd" \
   data/local/lm data/local/lm data/local/dict_nosp
  # 根据发音词典,生成语言文件夹lang
  utils/prepare_lang.sh data/local/dict_nosp \
   "" data/local/lang_tmp_nosp data/lang_nosp
  # 利用语言文件夹,生成用于测试的语言模型相关文件
  local/format_lms.sh --src-dir data/lang_nosp data/local/lm
fi

发音词典与音素集

data/local/lm中,有3个关于发音词典的文件

  • librispeech-vocab.txt:包含了语言模型训练数据中词频最高的200000个词
  • librispeech-lexicon.txt:包含了这些词的发音,共206508条
  • g2p-model-5:训练好的英文发音模型

整理发音词典会生成以下四个文件:

  • nonsilence_phones.txt:所有非静音音素

    AA AA0 AA1 AA2 
    AE AE0 AE1 AE2 
    AH AH0 AH1 AH2 
    ...
    

    例如AA AA0 AA1 AA2是同一个基本音素AA与不同中银标记组合成的带重音音素。中文中,可以将同一个基本音素与不同音调标记组合成带调音素。

  • extra_questions.txt:构建音素的声学上下文决策树时会遇到的基本问题,每行对应一个聚类问题,如这个例子中使用重音来聚类

    SIL SPN 
    AA AE AH AO AW AY B CH D DH EH ER EY F G HH IH IY JH K L M N NG OW OY P R S SH T TH UH UW V W Y Z ZH 
    AA2 AE2 AH2 AO2 AW2 AY2 EH2 ER2 EY2 IH2 IY2 OW2 OY2 UH2 UW2 
    AA1 AE1 AH1 AO1 AW1 AY1 EH1 ER1 EY1 IH1 IY1 OW1 OY1 UH1 UW1 
    AA0 AE0 AH0 AO0 AW0 AY0 EH0 ER0 EY0 IH0 IY0 OW0 OY0 UH0 UW0 
    
  • optional_silence.txt:定义了用于填充词间静音的音素

  • silence_phones.txt:定义了所有用来表示无效音内容的音素,例子中SIL表示静音,SPN表示有声音但是无法识别的声音片段(如集外词)

整理发音词典最后一步是生成lexicon.txt文件,包含了前面统计的发音词典的全部内容,并汇入了关于无效语音的发音规则。

!SIL SIL
 SPN
 SPN
A  AH0
A  EY1
A''S	EY1 Z
A'BODY	EY1 B AA2 D IY0
A'COURT	EY1 K AO2 R T
A'D	EY1 D
A'GHA	EY1 G AH0
...

Kaldi支持定义发音概率,对不同发音加以区分,这里没有定义,全部默认为1.0,保存在lexiconp.txt中。

语言文件夹

准备好发音词典文件夹后,就可以开始用Kaldi的通用脚本生成语言文件夹data/lang了。生成内容如下:

data/lang_nosp
├── L_disambig.fst # 在L.fst基础加上消歧符号
├── L.fst # 发音词典将音素和词替换为索引后生成的FST
├── oov.int # 指出词索引文件中那个词条是集外词
├── oov.txt
├── phones # 定义关于音素的各种属性
│   ├── align_lexicon.* # 将第一列内容重复了一次的发音文件,用来除了词网格文件和一些识别结果文件
│   ├── context_indep.* # 所有上下文无关的音素列表(所有静音音素)
│   ├── disambig.* # 所有消歧符号列表
│   ├── extra_questions.* # 增加音素位置标记的聚类问题
│   ├── nonsilence.* # 所有非静音、非消歧符号的音素列表
│   ├── optional_silence.* # 词间选择性填充的静音音素列表
│   ├── roots.* # 定义哪些音素共享上下文决策树的一个根节点
│   ├── sets.* # 音素组
│   ├── silence.* # 静音音素列表
│   ├── wdisambig_phones.* # 消歧音素索引
│   ├── wdisambig.* # 消歧符号文本
│   ├── wdisambig_words.* # 消歧符号词索引
│   ├── word_boundary.* # 每个音素的词位置
├── phones.txt # 定义词索引,在音素集基础上加入空音素,以#开头的消歧符号,加入位置标记
├── topo # 定义HMM的拓扑结构
└── words.txt # 定义音素索引,与vocab相比,增加了句首符,句尾符,空符和消歧符号#0
data/lang_nosp_test_tgmed
├── G.fst
├── L_disambig.fst
├── L.fst
├── oov.int
├── oov.txt
├── phones
│   ├── align_lexicon.int
│   ├── align_lexicon.txt
│   ├── context_indep.csl
│   ├── ...
├── phones.txt
├── topo
└── words.txt
data/lang_nosp_test_tgsmall
├── G.fst
├── L_disambig.fst
├── L.fst
├── oov.int
├── oov.txt
├── phones
│   ├── align_lexicon.int
│   ├── align_lexicon.txt
│   ├── context_indep.csl
│   ├── ...
├── phones.txt
├── topo
└── words.txt

3 directories, 107 files

相比第一个文件夹,后两个文件夹多了G.fst文件。后两个文件夹是由第一个文件夹复制并进一步整理而来,分别添加了各自的G.fst文件,这个文件保存对应测试集的语言模型(第4步还会生成tglarge和fglarge测试)。

你可能感兴趣的:(从零开始的ASR毕设,语音识别,机器学习,深度学习)