阅读和修改kaldi的代码
条评论本节主要涉及kaldi的代码组织,依赖的结构,以及修改代码,调试代码。
通用基础类库
src目录下的base和util是kaldi里面最基础的两个目录,几乎每一个kaldi的程序都依赖于这两个目录,查看base/kaldi-common.h, kaldi-common.h这个头文件中包含了base这个目录下的除了io-funcs-inl.h下的其他所有的头文件以及系统库,其中io-funcs-inl.h则被io-funcs.h包含,而io-funcs.h这包含在kaldi-common.h,可以说kaldi-common.h中包含了所有的头文件。我们可以通过头文件名知道对应的功能,比如日志记录,类型定义,数学库等。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//kaldi-common.h
util目录是另一个基础的目录,在util/common-utils.h中包含更多的头文件,包括前面的base下的kaldi-common.h,util目录下主要包括命令行的解析,I/O的处理,文件的读写。
之所以将基础类库的一部分分离到base中去,主要是为了最小化matrix的依赖,而且base目录本身就很有用,或者说被其他目录依赖。
1 |
最后一个比较基础的类库,则是matrix, kaldi中的matrxi是在BLAS和LPACK库上的进一步封装,或者说是包装器(wrapper).查看src下的matrix/matrix-lib.h 可以看到matrix-lib.h这个头文件中主要是包含其他的头文件依赖,提供了矩阵库中各种内容的概述, 从文件名可以知道其对应的功能,包括向量,矩阵,对阵矩阵,三角阵,fft变换,压缩矩阵,稀疏矩阵,以及涉及到矩阵的相关操作函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//matrix-lib.h
sp-matrix.h和tp-matrix.h分别与对阵矩阵和三角阵相关,kaldi-matrix.h中对MatrixBase进行定义。
上述了的base, util, matrix目录可以说是kaldi的所有程序中最基础的,或者说大部分程序都依赖于这三个目录中的代码。
修改和调试代码
这里以修改matrix下的代码为例,在matrix-lib-test.cc中添加一个测试函数,这是一个测试程序。这个测试函数给一个向量加上一个常数与向量的乘积。
在MatrixUnitTests函数上面添加UnitTestAddVec1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16template<class Real>
void UnitTestAddVec() {
// note: Real will be float or double when instantiated.
int32 dim = 1 + Rand() % 10;
Vector<Real> v(dim), w(dim); // two vectors the same size.
v.SetRandn();
w.SetRandn();
Vector<Real> w2(w); // w2 is a copy of w.
Real f = RandGauss();
w.AddVec(f, v); // w <-- w + f v
for (int32 i = 0; i < dim; i++) {
Real a = w(i), b = f * w2(i) + v(i);
AssertEqual(a, b); // will crash if not equal to within
// a tolerance.
}
}
执行make test 生成测试程序matrxi-lib-test并测试,这一步会提示错误。查看matrix-lib-test.testlog可以看到具体的错误。1
2
3
4
5
6
7
8
9
10ASSERTION_FAILED ([5.5.333~1-e9223]:AssertEqual():base/kaldi-math.h:278) Assertion failed: (ApproxEqual(a, b, relative_tolerance))
[ Stack-Trace: ]
kaldi::MessageLogger::LogMessage() const
kaldi::KaldiAssertFailure_(char const*, char const*, int, char const*)
void kaldi::UnitTestAddVec<float>()
./matrix-lib-test() [0x451d5a]
main
__libc_start_main
_start
直接执行./matrix-lib-test也会报错,因为v和w是随机生成的,肯定不会相等。
现在执行gdb调试,这里补充一下gdb调试的知识。
(1) 启动调试1
gdb program //program指自己的程序
(2) gdb的命令列表
命令 | 命令缩写 | 命令说明 |
---|---|---|
list | l | 显示多行源代码 |
break | b | 设置断点,比如b 10, 在第10行设置断点 |
info | 查看某个值,比如info break | |
delete | 删除某个值,比如delete breakpoint, 删除某个断点 | |
disable | disable breakpoint,禁用某个断点 | |
run | r | 运行程序 |
display | disp | 追踪查看某个变量 |
step | s | 执行下一条语句,,如果该语句为函数调用,则进入函数执行其中的第一条语句 |
next | n | 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句) |
finish | 如果已经进入了某函数,而想退出该函数返回到它的调用函数中,可使用命令finish | |
p | 打印内部变量值 | |
continue | c | 继续程序的运行,直到遇到下一个断点 |
watch | 监视某个变量值得变化 | |
backtrace | bt | 查看函数调用的堆栈信息 |
up | 上移栈帧 | |
quit | q | 退出gdb |
whatis | 查看变量类型 |
调试matrix-lib-test程序,1
gdb ./matrix-lib-test
输入r运行程序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19(gdb) r
Starting program: /home/cca01/hdd190404/luoxj/kaldi/src/matrix/matrix-lib-test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
ASSERTION_FAILED ([5.5.333~1-e9223]:AssertEqual():base/kaldi-math.h:278) Assertion failed: (ApproxEqual(a, b, relative_tolerance))
[ Stack-Trace: ]
kaldi::MessageLogger::LogMessage() const
kaldi::KaldiAssertFailure_(char const*, char const*, int, char const*)
void kaldi::UnitTestAddVec<float>()
/home/cca01/hdd190404/luoxj/kaldi/src/matrix/matrix-lib-test() [0x451d5a]
main
__libc_start_main
_start
Program received signal SIGABRT, Aborted.
0x00007ffff61a9428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
输入bt查看堆栈信息1
2
3
4
5
6
7
8
9
10
11
12(gdb) bt
#0 0x00007ffff61a9428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007ffff61ab02a in __GI_abort () at abort.c:89
#2 0x00007ffff7933a4c in kaldi::KaldiAssertFailure_ (
func=func@entry=0x462e58 <kaldi::AssertEqual(float, float, float)::__func__> "AssertEqual",
file=file@entry=0x4603bb "../base/kaldi-math.h", line=line@entry=278,
cond_str=cond_str@entry=0x460f28 "ApproxEqual(a, b, relative_tolerance)") at kaldi-error.cc:209
#3 0x000000000045c5d4 in kaldi::AssertEqual (relative_tolerance=0.00100000005, b=<optimized out>, a=<optimized out>)
at ../base/kaldi-math.h:278
#4 kaldi::UnitTestAddVec<float> () at matrix-lib-test.cc:4602
#5 0x0000000000451d5a in kaldi::MatrixUnitTest<float> (full_test=full_test@entry=false) at matrix-lib-test.cc:4608
#6 0x000000000045c11d in main () at matrix-lib-test.cc:4766
输入up,将栈帧一层一层往上移,直到到达上述函数的内部1
2
3
4(gdb) up
#3 0x000000000045c5d4 in kaldi::AssertEqual (relative_tolerance=0.00100000005, b=<optimized out>, a=<optimized out>)
at ../base/kaldi-math.h:278
278 KALDI_ASSERT(ApproxEqual(a, b, relative_tolerance));
使用p打印变量的值1
2
3
4(gdb) p a
$1 = <optimized out>
(gdb) p b
$2 = <optimized out>
这里并没有打印出a和b的值,原因是编译是kaldi.mk使用的是g++ -O1的优化选项,同时由于使用-O1选项导致断点无效。
修改编译选项为-O0,重新执行上述步骤,即可打印出对应的值。1
2
3
4
5(gdb) p a
$1 = -1.39748704
(gdb) p b
$2 = -0.473557711
(gdb) q
kaldi的编码风格
1、 kaldi中类的私有变量以下划线结尾
2、kaldi的两种文件格式:scp(script) 和 ark(archive)。 .scp文件是key到文件或者管道的映射,.ark文件是数据文件,由key和value组成。kaldi的工具需要指明输入输出的文件类型,告诉kaldi怎么去读这些文件。格式如下:
wspecifier | meaning |
---|---|
ark:foo.ark | Write to archive “foo.ark” |
scp:foo.scp | Write to files using mapping in fdo.scp |
ark:- | Write archive to stdout |
ark,t: | gzip -c > foo.gz | Write text-form archive to foo.gz |
ark,t:- | Write text-form archive to stdout |
ark,scp:foo.ark,foo.scp | Write archive and scp file, scp file specifying offsets into that archive, like an index |
rspecifier | meaning |
---|---|
ark:foo.ark | Read from archive foo.ark |
scp:foo.scp | Read as specified in foo.scp |
ark:- | Read archive from stdin |
ark:gunzip -c foo.gz | | Read archive from foo.gz |
ark,s,cs:- | Read archive(sorted) from stdin, ‘s’ asserts archive is sorted. ‘s’ asserts it will be called in sorted order |
- 本文链接:http://joefi.github.io/2019/05/17/阅读和修改kaldi的代码/
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!