chenkui164 / FastASR

这是一个用C++实现ASR推理的项目,它依赖很少,安装也很简单,推理速度很快,在树莓派4B等ARM平台也可以流畅的运行。 支持的模型是由Google的Transformer模型中优化而来,数据集是开源wenetspeech(10000+小时)或阿里私有数据集(60000+小时), 所以识别效果也很好,可以媲美许多商用的ASR软件。
Apache License 2.0
482 stars 74 forks source link

识别结果中文字重复,这大概会是那个环节出问题了? #70

Open charlesw1234 opened 1 year ago

charlesw1234 commented 1 year ago

为了学习 FastASR 我自己用 C 语言重写了一遍,输出如下:

$ tests/test-kaldi2.exe ~/ai.data/asr/20230825-0733.wav 
Log at: tests/test-kaldi2-15122.jsons
Using: libsndfile-1.0.31
/home/wangli/ai.data/asr/20230825-0733.wav:
    Sample Rate: 16000
    Channels: 1
    Sections: 1
    Frames: 59051
    Load frames: 59051
    Model Init: True
    Model Forward: True
result: "<blk><blk>好好好好好好好好好好好好好好好好好好学学学学学学学学学学学学学学学学学学习习习习习习习习习习习天天天天天天天天天天天天天向向向向向向向向向向向上上上上上上上上上上上上上上上上上上上上"

需要的话我可以上传到这里或者 gitee,其他的地方都对了,只有文字似乎重复了。我在 wav 里面说的是:“好好学习天天向上”,用 FastASR 的 examples 可以正常识别。

谢谢。

chenkui164 commented 1 year ago

在 Tensor.h 有个方法是dump(), 将需要观察的数据导出到tmp.bin文件。然后用matlab或者python,比对两个版本之间的差异。这个只能逐一排查,并没有好的办法。

csukuangfj commented 1 year ago

ab或者python,比对两个版本之间的差异。这个只能逐一排查,并没有好的办法。

这个是 transducer 还是 ctc?

chenkui164 commented 1 year ago

ab或者python,比对两个版本之间的差异。这个只能逐一排查,并没有好的办法。

这个是 transducer 还是 ctc?

是transducer的,decode method直接用的gready search。

charlesw1234 commented 1 year ago

我明白了,多谢指点。稍晚点我开个 git,把我代码发出来供大家解剖玩耍。

charlesw1234 commented 1 year ago

提交到这里了: https://github.com/charlesw1234/fastc-asr

问题我还没来得及改。只要把 k2_rnnt2_cli 的 wenet_params.bin 放到 models/k2_rnnt2_cli/ 目录里,然后再运行: tests/test-kaldi2.exe tests/hhxx.ttxs.wav 就可以看到结果了。

我是用一个叫 scons 的编译工具来编译的,在 ubuntu 里面,只要: apt-get install scons 就行了。运行 scons 就可以完成编译。

读取 wav,我用了 libsndfile,所以在 ubuntu 里面要:apt-get install libssl-dev libsndfile1-dev libfftw3-single libfftw3-dev libopenblas-dev。

有兴趣的话先玩着。

chenkui164 commented 1 year ago

问题找到了,find_max函数没有对max_at初始化。

void findmax(value_t din, size_t len, value_t max_value, size_t max_at) { assert(len > 0); max_value = din[0]; max_at = 0; for (size_t index = 1; index < len; ++index) if (din[index] > max_value) { max_value = din[index]; max_at = index; } }

@charlesw1234 ,看代码风格,应该是某位潜水的大佬,C语言的goto我只在linux内核里见人用过。函数命名规范,解构清晰,实在是佩服!

charlesw1234 commented 1 year ago

好多天没上来看了,一个是公司事情忙,一个是懒(主要是这原因 :) )。我是两个没想到,一个是没想到 @chenkui164 会帮忙找出 bug......,所以我今天上午花了两个小时把问题找出来了,正是你说的..... *max_at = 0 忘记了。这是我的第二个没想到,竟然阴沟翻船了。 :p 早知道你就帮忙找出来我就偷懒了,哈哈。

调试过程中我发现一个有趣的现象:

#include <stdio.h>
#include <stdlib.h>
#include <fftw3.h>

#define FFT_SIZE 512
#define STEP     4
int main(void) {                                                                          
    //float fft_input[FFT_SIZE];
    //fftwf_complex fft_output[FFT_SIZE];
    float *fft_input = (float*)malloc(sizeof(float) * FFT_SIZE);
    fftwf_complex *fft_output = (fftwf_complex *)malloc(sizeof(fftwf_complex) * FFT_SIZE);
    fftwf_plan plan;
    plan = fftwf_plan_dft_r2c_1d(FFT_SIZE, fft_input, fft_output, FFTW_ESTIMATE);
    for (size_t at = 0; at < FFT_SIZE; ++at) fft_input[at] = (float)at;
    fftwf_execute(plan);
    for (size_t at = 0; at < FFT_SIZE; at += STEP) {
        printf("%3zu: ", at);
        for (size_t offset = 0; offset < STEP; ++offset)
            printf("(%8.3f, %8.3f) ", fft_output[at + offset][0],
                   fft_output[at + offset][1]);
        printf("\n");
    }
    fftwf_destroy_plan(plan);
    free(fft_output);
    free(fft_input);
    return 0;
}

如果是用注释掉的方式,也就是在栈里面分配 fft_input、fft_output,那么即便输入完全相同,fftwf_execute每次的输出也会不同。而如果用 malloc 在堆里面分配 fft_input、fft_output,那么只要输入相同,fftwf_execute 的输出总是相同,只是仍然跟栈里情况不同。但是无论用那种方式,都不影响最终结果。

chenkui164 commented 1 year ago

对实数做fft,它的结果是共轭对称的,所以fftw只给出了前面0-256的结果,后面的数是随机的。可以参考官方的解释。http://www.fftw.org/doc/The-1d-Real_002ddata-DFT.html#The-1d-Real_002ddata-DFT