没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
Kaldi 决策树状态绑定学习笔记(二)
——如何自动生成问题集?
Kaldi 决策树中使用的问题集并不是手工设计的,而是通过之前得到的统计
量自动生成的。那么在 Kaldi 中是如何自动生成问题的?这就是本次笔记的主要
内容。
在这个笔记中,我会首先介绍自动生成问题集所用到的主程序 cluster-
phones 和主函数 AutomaticallyObtainQuestions(),然后会穿插着介绍主函数用到
的核心函数和完成具体工作的一些 C++对象。最后再讲解程序 compile-question。
建议学习 Kaldi 官方文档《Decision tree internals》的 Classes and functions
involved in tree-building 部分,《 How decision trees are used in Kaldi》的 The tree
building process 部分。
若对似然这些名词和对应的公式感觉陌生,请参考论文《Tree-Based State
Tying For High Accuracy Acoustic Modelling 》 S.J.Young 的 第 三 部 分 Tree-
BasedClustering。
cluster-phones
作用:Cluster phones (or sets of phones) into sets for various purpose. 对多个音
素或多个音素集进行聚类。
输入:决策树相关统计量 treeacc,多个音素集 sets.int
输出:自动生成的问题集(每个问题由多个音素组成)
示例:
cluster-phones $context_opts $dir/treeacc $lang/phones/sets.int \
$dir/questions.int
过程:
1. 从 treeacc 中 读 取 统 计 量 到 BuildTreeStatsType stats ;读取 vector
pdf_class_list,该变量指定所考虑的 HMM 状态,默认为 1,也就是只考虑
三状态 HMM 的中间状态;从 sets.int 读取 vector<vector> > phone_sets;
默认的三音素参数 N=3,P=1。
2. 若指定的 mode 为 questions,调用 AutomaticallyObtainQuestions()自动生
成问题集 vector<vector> > phone_sets_out;若指定的 model 为 k-means,
调用 KMeansClusterPhones()。此笔记只涉及 questions 模式。
3. 将上述函数自动生成的 phone_sets_out 写到 questions.int。
文件说明:
下面以我们实验室所用的 sets.int 和 sets.txt 为例,来对 sets.int 文件有一个直
观的感受:(左边是 sets.txt,右边是 sets.int,两图第一列均为行号)
AutomaticallyObtainQuestions()
void AutomaticallyObtainQuestions(
BuildTreeStatsType &stats,
const std::vector<std::vector<int32> > &phone_sets_in,
const std::vector<int32> &all_pdf_classes_in,
int32 P,
std::vector<std::vector<int32> > *questions_out
)
主要功能:AutomaticallyObtainQuestions()通过对音素自动进行聚类,从而获
取问题集;它把音素聚类成一棵树,并且对树中的每一个结点,把从该结点
可以到达的所有叶子结点合在一起构成一个问题(该树的一个叶子结点保存
着一些音素,一个问题就是一个音素的集合)。(官方文档《Decision tree
internals》的 Classes and functions involved in tree-building 中的 Top-level tree-
building functions 部分如是说)。初看到这段描述可能并不是很清楚到底怎么
一回事,在看明白代码之后就会明白这句话讲了什么。
分块解析:
(在下面我会把不重要的代码删掉,比如错误检测代码,只留下主要部分)
1. 读取 sets.int 中的所有音素,保存在 phones 中。phone_sets_in 由 sets.int 得
到。
std::vector<std::vector<int32> > phone_sets(phone_sets_in);
std::vector<int32> phones;
for (size_t i = 0; i < phone_sets.size() ;i++) {
std::sort(phone_sets[i].begin(), phone_sets[i].end());
for (size_t j = 0; j < phone_sets[i].size(); j++)
phones.push_back(phone_sets[i][j]);
}
std::sort(phones.begin(), phones.end());
2. 调用 FilterStatsByKey()把 stats 中只属于三音素第二个 HMM 状态的统计量留
下。通过累积统计量部分我们知道,三音素的三个 HMM 状态可能都会有对
应的统计量,但是这里只把与第二个 HMM 状态相关的统计量留下进行聚类,
其他的都暂时扔掉不用。为什么是第二个?这是由向量 all_pdf_class 指定的,
all_pdf_class 就是程序 cluster-phones 中的参数 pdf_class_list,该向量指定所
考虑的 HMM 状态,默认为 1,也就是只考虑三状态 HMM 的中间状态。至于
为什么是第二个,暂时我也不是很清楚。kPdfClass=-1,指明过滤统计量的 Key,
也就是根据 EventType 的 HMM 状态进行过滤,只留下 all_pdf_classes 指定的
HMM 状态对应的 stats。
BuildTreeStatsType retained_stats;
FilterStatsByKey(stats, kPdfClass, all_pdf_classes,
true, // retain only the listed positions
&retained_stats);
3. 调用 SplitStatsByKey(),根据三音素的中间音素对 retained_stats 进行划分,把
属于每个音素的统计量放在一个 BuildTreeStatsType 中。由参数 P 指定根据三
音素的第几个音素进行划分,因为此处 P 是 1,所以是三音素的中间音素。
举个例子,我们实验室的所用的音素一共有 215 个,假设每个音素都出现在
三音素的中间位置,对 retained_stats 进行划分之后,split_stats 的元素个数是
215,每一个元素保存着(中间音素都是 x 的所有三音素对应的所有统计量)。
std::vector<BuildTreeStatsType> split_stats; // split by phone.
SplitStatsByKey(retained_stats, P, &split_stats);
4. 调用 SumStatsVec()把 split_stats 每个元素中的所有统计量加起来,得到每个
中间音素的统计量,也就是 summed_stats,其维数为音素个数。
从上一步我们知道,split_stats 的每一个元素保存着中间音素都是 x 的所
有三音素对应的所有统计量,因为音素 x 左右音素的不同,所以 split_stats 这
个元素中保存的统计量有很多,现在把中间音素都是 x 的所有三音素对应的
所有统计量累加起来(就是把这些 GaussClusterable 的 count_相加、stats_相
加);对 split_stats 的每个元素都执行这样的操作后,就得到了 summed_stats。
举个例子,我们实验室的所用的音素一共有 215 个,最终的 summed_stats
就只有 215 个元素,每个元素保存着某音素作为三音素中间音素、其 HMM
状态为第二个状态对应的所有统计量的累积。
std::vector<Clusterable*> summed_stats; // summed up by phone.
SumStatsVec(split_stats, &summed_stats);
5. 根据 sets.int 指定的集合,累加同一个集合中音素的统计量。从上面 sets.int
文件的图片可以看出,该文件的一行就是一个音素的集合,这块代码的作用
就是把属于 sets.int 文件同一行的音素的统计量累加在一起,所以最后
summed_stats 的维数就是 sets.int 的行数,一行对应一个统计量。
std::vector<Clusterable*> summed_stats_per_set(phone_sets.size(), NULL);
for (size_t i = 0; i < phone_sets.size(); i++) {
const std::vector<int32> &this_set = phone_sets[i];
summed_stats_per_set[i] = summed_stats[this_set[0]]->Copy();
for (size_t j = 1; j < this_set.size(); j++)
summed_stats_per_set[i]->Add(*(summed_stats[this_set[j]]));
}
6. 调用 TreeCluster(),对 summed_stats_per_set 进行聚类,生成相关信息。
TreeCluster()是 AutomaticallyObtainQuestions()最核心的部分,该函数的具体解
剩余13页未读,继续阅读
资源评论
- gubinbing2018-07-10还是不错的
开拓的博客
- 粉丝: 179
- 资源: 9
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功