Kaldi中的日志和错误报告

Overview

Kaldi 程序中的包含日志信息,警告信息和错误信息的输出都被定向到标准错误流。这也是我们的程序可以直接用在管道里,而这些信息不回和程序的输出所混淆的原因。最常用的生成日志,警告或错误信息输出的方式是通过宏 KALDI_LOG, KALDI_WARN 和 KALDI_ERR。宏 KALDI_ERR 被激活时一般程序都会被终止(除非异常被捕获)。一个代码片段的例子如下:

KALDI_LOG << "On iteration " << iter
          << ", objective function change was " << delta;
if (delta < 0.0) {
  KALDI_WARN << "Negative objf change " << delta;
  if (delta < -0.1) 
    KALDI_ERR << "Convergence failure in EM";
}

注意这些例子中不包含换行符(这个是自动添加的)。它产生的消息的一个典型的例子是:

WARNING (copy-feats:Next():util/kaldi-table-inl.h:381) Invalid archive file format

对于不太重要的日志信息(或太冗余的),以至不必在正常的日志中出现的,你可以用 KALDI_VLOG,例如

KALDI_VLOG(2) << "This message is not important enough to use KALDI_LOG for.";

如果-verbose选项设置的比括号中的数字大或者相等,它会被打印出来。e.g.如果程序的参数-verbose=2或更大,上面的信息就会被打印。更多内容见 Implicit command-line arguments

部分代码直接打印日志信息到标准错误流;这种做法不被提倡。

Assertions in Kaldi

断言最好用宏 KALDI_ASSERT 来完成。这比通常的 assert() 会给出更多信息;KALDI_ASSERT 打印出堆栈信息,同时也更容易配置。

一个断言的例子是:

KALDI_ASSERT(i < M.NumRows());

一个获得 assert-failure 更多信息的技巧是在断言语句后加上“&&[some string]”,例如

KALDI_ASSERT(ApproxEqual(delta, objf_change) && "Probable coding error in optimization");

正常编译时断言都会被检查,除非你定义了 NDEBUG。对占用大量CPU的内环断言,我们用下面的模式:

#ifdef KALDI_PARANOID
  KALDI_ASSERT(i>=0);
#endif

在当前的设定中,宏 KALDI_PARANOID 默认是定义了的。

Exceptions thrown by KALDI_ERR

宏 KALDI_ERR 被调用时,错误信息被输出到标准错误流,然后抛出 std::runtime_error 类型的异常。异常信息的字符串包含了错误信息和堆栈信息(如果OS支持)。目前 Kaldi 程序通过main()中的try...catch模块来捕捉异常,即把异常信息打印至标准错误流并推出。这通常会导致错误信息被打印了两遍。

某些情况下,错误信息会被 Kaldi 代码捕捉到而不会重新抛出。这发生在 Holder 代码(见 Holders as helpers to Table classes),并被 Table 代码调用(见 The Table concept)。这里 Kaldi 对象的Read函数抛出的异常会被捕捉并作为一个布尔型返回值传递给 Table 代码(e.g.见 KaldiObjectHolder::Read())。根据选项,例如 Table 代码中的“p”(permissive)选项,和 Table 代码被调用的方式 ,这可能会也可能不会导致另一个异常。

正常情况下,Kaldi代码中除了 std::runtime_error,另一类应该被抛出的错误是 std::alloc_error。

部分 Kaldi 代码中,目前会直接抛出 std::runtime_error 或直接调用 assert(),这在以后会更新成标准的宏 KALDI_ERR 和 KALDI_ASSERT。

Compile-time assertions in Kaldi

可以在编译时设置断言(如果失败,它们会产生编译错误)。这通过kaldi-utils.h中定义的一些宏来实现。特别有用的是,确保模板在实例化时有正确的类型。编译断言的例子有:

KALDI_COMPILE_TIME_ASSERT(kSomeConstant < 0);
...
template<class T> class foo {
   foo() { KALDI_ASSERT_IS_INTEGER_TYPE(T);
};
...
template<class T> class bar {
   bar() { KALDI_ASSERT_IS_FLOATING_TYPE(T);
}