kaldi中fbank特征提取详解(结合源码,深度剖析)

本文详细探讨了Kaldi中fbank特征提取的过程,包括feature-window、mel-computations和feature-fbank模块的功能。重点讲解了ExtractWindow、ProcessWindow函数,涉及dither、remove_dc_offset、log_energy_pre_window、preemph_coeff等操作,以及mel-bins的计算。此外,还介绍了参数配置及其对特征提取的影响。
摘要由CSDN通过智能技术生成


搬运需注明出处,请勿侵权,谢谢!


kaldi中相关函数在 src/feat 目录下

1. feature-window

1.1 feature-window.h 中默认值

struct FrameExtractionOptions {
	... ...
	FrameExtractionOptions():
	samp_freq(16000),
	frame_shift_ms(10.0),
	frame_length_ms(25.0),
	dither(1.0),
	preemph_coeff(0.97),
	remove_dc_offset(true),
	window_type("povey"),
	round_to_power_of_two(true),
	blackman_coeff(0.42),
	snip_edges(true),
	allow_downsample(false),
	allow_upsample(false),
	max_feature_vectors(-1)
	... ...
}

这些参数,可以在conf中设置,若不设置,则为默认值。

函数中参数名及默认值 conf 中设置示例 意义
samp_freq(16000) –sample-frequency=16000 音频处理时的采样率,单位是 Hz
frame_shift_ms(10.0) –frame-shift=10 窗移时间,单位是 ms
frame_length_ms(25.0) –frame-length=25 窗长时间,单位是 ms
dither(1.0) –dither=1.0 每帧添加的随机噪声系数,训练时用,相当于增加扰动,但会增加特征提取时间,关闭则设置为 0.0,默认为 1.0
preemph_coeff(0.97) –preemphasis-coefficient=0.97 预加重系数
remove_dc_offset(true) –remove-dc-offset=true 每帧数据均值移到0,若要保持数据原始特性,则设置成 false
window_type(“povey”) –window-type=povey 窗函数,包含 hamming,hanning,povery,rectangular,sine,blackmann,其中povery是Dan自己设计的
round_to_power_of_two(true) –round-to-power-of-two=true FFT变换时,用0填充至2幂次
blackman_coeff(0.42) –blackman-coeff=0.42 窗函数用blackman时的相关系数
snip_edges(true) –snip-edges=true 若为true,则只输出完全适合文件的帧来处理结束效果,帧数取决于帧长度。若为false,则帧数仅取决于帧移动,我们将在末尾反映数据。
allow_downsample(false) –allow-downsample=false 是否降采样。若true时,表示允许降采样,将根据 sample-frequency 设置的参数进行降采样
allow_upsample(false) –allow-upsample=false 是否上采用
max_feature_vectors(-1) –max-feature-vectors=-1 内存优化。若大于0,则定期删除特征向量,以便仅保留此数量的最新特征向量。主要是为了防止 out of memory 这种报错,导致特征提取时异常结束

1.2 feature-window.cc 中相关函数

1.2.1 ExtractWindow

主要是根据采样率,是否降采样等,计算窗长,窗移等,而后调用 ProcessWindow 对每一帧进行操作。

1.2.2 ProcessWindow

对每一帧进行细致操作,具体如下

1.2.2.1 dither

该值默认为1.0,表示对每一帧数据添加随机高斯的系数。可理解为数据扰动,但是提取特征时,会花更多时间用于产生高斯随机数。若数据已做过比较充分的数据扩增,可以将其设置为0.0。
其公式为
x i = x i + G a u s s ∗ d i t h e r x_{i} = x_{i} + Gauss * dither xi=xi+Gaussdither
具体函数如下

void ProcessWindow(...){
	... ...
	if (opts.dither != 0.0)
		Dither(window, opts.dither);
	... ...
}
void Dither(VectorBase<BaseFloat> *waveform, BaseFloat dither_value) {
	if (dither_value == 0.0)
		return;
	int32 dim = waveform->Dim();
	BaseFloat *data = waveform->Data();
	RandomState rstate;
	for (int32 i = 0; i < dim; i++)
		data[i] += RandGauss(&rstate) * dither_value;
}
1.2.2.2 remove_dc_offset

该值默认为true,表示是否对每帧的数据点进行平移,使其均值为0。若录音设备电压不稳定,可能导致录的音频电位漂移。正常设备,在时间窗内,数据均值是接近0的数。true或false,两者会有略微差别,但差别不是很大,个人经验,对于fbank而言,差别在±1以内。
其公式为
x i = x i − x ‾ x_{i} = x_{i} - \overline{x} xi=xix

具体函数如下

void ProcessWindow(...){
	... ...
	if (opts.remove_dc_offset)
		window->Add(-window->Sum() / frame_length);
	... ...
}
1.2.2.3 log_energy_pre_window

该值默认是NULL,表示对窗内数据点是否做log操作,无相关外部输入。无修改kaldi源码的情况下,不会进行操作。
具体函数如下

void ProcessWindow(...){
	... ...
	if (log_energy_pre_window != NULL) {
		BaseFloat energy = std::max<BaseFloat>(VecVec(*window, *window),
					std::numeric_limits<float>::epsilon());
		*log_energy_pre_window = Log(energy);
	}
	... ...
}
1.2.2.4 preemph_coeff

该值默认值为0.97,表示预加重权重。
注:其预加重方式对第一帧也做了特殊处理,其公式为
x i = { x i − α ∗ x i i = 0 x i − α ∗ x i − 1 i > = 1 x_{i}=\left\{ \begin{aligned} &x_{i} - \alpha * x_{i} & &i=0\\ &x_{i} - \alpha * x_{i-1} & &i>=1 \end{aligned} \right. xi={ xiαxixiαxi1i=0i>=1

具体函数如下

void ProcessWindow(...){
	... ...
	if (opts.preemph_coeff != 0.0)
		Preemphasize(window, opts.preemph_coeff);
	... ...
}
... ...
void Preemphasize(VectorBase<BaseFloat> *waveform, BaseFloat preemph_coeff) {
	if (preemph_coeff == 0.0) return;
	KALDI_ASSERT(preemph_coeff >= 0.0 && preemph_coeff <= 1.0);
	for (int32 i = waveform->Dim()-1; i > 0; i--)
		(*waveform)(i) -= preemph_coeff * (*waveform)(i-1);
	(*waveform)(0) -= preemph_coeff * (*waveform)(0);
 }
1.2.2.5 window->MulElements(window_function.window)

MulElements函数是将时域数据一边进行FFT变换一边乘以窗函数(比较高级,一般FFT两层循环,他一层循环就搞定了!!!)。
具体代码在src/matrix/kaldi-matrix.cc和src/matrix/cblas-wrappers.h中,
其函数如下(其中mul_elements是在src/matrix/cblas-wrappers.h中):

//----------------------------------------
// in src/matrix/kaldi-matrix.cc
template<typename Real>
void MatrixBase<Real>::MulElements(const MatrixBase<Real> &a) {
	KALDI_ASSERT(a.NumRows() == num_rows_ && a.NumCols() == num_cols_);

	if (num_cols_ == stride_ && num_cols_ == a.stride_) {
		mul_elements(num_rows_ * num_cols_, a.data_, data_);
	} else {
		MatrixIndexT a_stride = a.stride_, stride = stride_;
		Real *data = data_, *a_data = a.data_;
		for (MatrixIndexT i = 0; i < num_rows_; i++) {
			mul_elements(num_cols_, a_data, data);
			a_data += a_stride;
			data += stride;
		}
	}
 }
//-----------------------------------------------
// in src/matrix/cblas-wrappers.h
inline void mul_elements(
			const MatrixIndexT dim,
			const float *a,
			float *b) { // does b *= a, elementwise.
	float c1, c2, c3, c4;
	MatrixIndexT i;
	for (i = 0; i + 4 <= dim; i += 4) {
		c1 = a[i] * b[i];
		c2 = a[i+1] * b[i+1];
		c3 = a[i+2] * b[i+2];
		c4 = a[i+3] * b[i+3];
		b[i] = c1;
		b[i+1] = c2;
		b[i+2] = c3;
		b[i+3] = c4;
	}
	for (; i < dim; i++)
		b[i] *= a[i];
}

kaldi用到的窗函数及公式如下:

窗函数名称 公式
hanning w i = 0.5 − 0.5 ∗ c o s ( 2 ∗ π ∗ i / ( N − 1 ) ) , 0 < = i < N w_i=0.5-0.5*cos(2*\pi*i/(N-1)) ,0<=i<N wi=0.50.5cos(2πi/(N1)),0<=
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值