基于动态时间规整的声控指令识别

来源:互联网 发布:阿里专有云asp csp 编辑:程序博客网 时间:2024/04/27 15:47

识别过程:

1  用户通过麦克风输入语音指令

2 软件返回所识别的指令文本内容


训练过程:

3 用户按照软件窗口提示信息,通过麦克风输入指定的语音内容,完成训练数据的录入

4 软件提取语音参数模板,形成特征辞典库


指令格式:

支持固定长度的短语、数字或两者的混合。

芝麻开门

3721

芝麻开门3721

3721芝麻开门


如何计算语段样本之间的距离

 

一种识别问题,在数学上总是可以等价为某种距离的计算。DTWDynamic Time Wrapping)是比较两个时间序列之间距离(即相似度)的简单而有效的工具。语音信号,很显然就是一种时间序列。下面我们来设想一下如何比较两个语音波形之间的距离,也就是相似度。

设想一个周期性的谐波,我们最容易想到的就是34个周期的正弦波。将其增加一万倍,设想一万个周期的波形。这样的数据量就和一句语句的尺度相当,而且实际的语音波形幅度是一个随机变量。我们可以看到,比较两个语音波形之间的相似程度,直接计算是非常困难的。通常,我们要将其变换到频域中来。

对这样的时域波形,进行谐波分解,将其频率分量提取出来,形成MFCC(镁尔倒谱参数)。上述时域波形的数据量,大约会减小100倍。此时,我们仍然面对着一个时间序列,序列中的每个成员,都是一个13维的MFCC参数向量,代表该时刻的频域特征。频域特征自然是随时间变化的,对应了不同的语音中的音节。

现在我们可以回到两个时间序列之间的距离的计算上来了。已经得到了MFCC特征序列,数据量已经降下来了,但是这两个序列的长度是不相等的(两句同样内容的语音,时间长度不相等)。因此,在比较两个序列之间的相似度时,需要进行“对齐”。

在对齐的过程中,我们可以将序列部分的“拉长”和“压缩”,在拉长和压缩的过程中,对数据点即进行插值或删除。总之,合理的对齐方式有很多种可能性,将这些可能性全部枚举(遍历)一遍,找到最佳的方式,使得两个时间序列之间的匹配度最高(距离最小),即是一个优化搜索的问题。这个优化问题,可以通过动态规划,“分段”进行优化求解,称为DTWDynamic Time Wrapping)。 

 

如何设计识别模板

 

有了计算时间序列之间距离的工具,我们就可以来进行语音识别和声纹匹配。在进行语音识别时,就是对比两条语音数据的相似程度,识别出用户说的是哪条口令。在进行声纹匹配时,就是对比某条口令由不同的人发音时,声纹图的相似程度,MFCC特征是反映这种相似程度的较好特征。

我们进一步考虑,如何对比两条语音的相似程度。有三种设计方式。

第一种是将语句作为一个整体,输入到DTW模块中。这样做的好处是语句内部可以连读、停顿,可以有噪声。这些干扰因素都会在动态规划算法中,通过序列的压缩和拉伸消除掉。

第二种是将整条语句切分成“字”的级别,这样做对切分的精度要求较高,切分模块稍有偏差,就会导致错误向后传播,影响整个后续的识别系统。但是这种方法的优点是可以不完全约束用户口令的文本内容,用户可以说“春暖花开 1 2 3 4”,而其中的数字,可以是任意的,只要其在注册过的“字典”中。这个时候,我们对比的不是整个语句之间的相似度,而是相应位置的字的相似度。可以推出:“春暖花开 开 开 花 花”也是可以被识别的。

第三种方法,是组合前两种方法,对词语部分进行整句识别,支持连读,对数字部分进行切分到字级别的识别。这种方式下,切分模块的功能变复杂,精度可能下降。但优点是既能支持连读,又能任意修改部分文本的内容。

其实到这里,我们已经在使用“模板”这个概念了,上面将整个语句作为识别对象,就是以某条语句为模板,将字作为识别的最小级别,就是将某个字作为模板。模板当然会有很多个,模板越多,模板构成的字典越大,能识别的词汇量越大



动态时间规整代码实现:

/*------------------------------欧氏距离 for MFCC-----------------------------*/double DistFrame(double* p1, double* p2){    double sum = 0;    int order = ORDER;    int i;    for (i = 0; i < order; i++)    {        sum = sum + (*(p1 + i) - * (p2 + i)) * (*(p1 + i) - * (p2 + i));    }    sum = sum / order;    sum = sqrt (sum); /* a ^ 2 + b^ 2 + ... */    return sum;}/*--------------------------------DTW-----------------------------------------*/double MatchingDTW(double* p1, double* p2, int len1, int len2){    /* 基本算法, 有约束                                        */    int n, m;    n = len1;    m = len2;    /* check n>0 m>0                                           */    if ((n < 2) || (m < 2))    {        printf(">>>>>>>> unaccepted DTW input (Error in Segment Length) : n, m: %d, %d\n",               n, m);        return 100000;    }    /*int mfccOrder = ORDER;*/    double* dtw;    dtw = (double*)malloc(sizeof(double) * n * m);    /*    *  -------------------------    *            - i -    *      1 ...        n    *  |  m    *  j  ..    *  |  1    *  -------------------------    */    /* 注意要归一化 两帧mfcc 之间的距离 远小于102400           */    double nInf = n * m * 10240;    /* 注意不要超过 c++ 的最大数, 初始化 dtw                   */    for (int i = 0; i < n; i++)    {        for (int j = 0; j < m; j++)        {            *(dtw + i * m + j) = nInf;        }    }    *(dtw) = 0;    /* Window, 可调节的 local constrain                        */    int w = (int)(0.2 * (n + m) / 2);    /* 动态规划                                                */    double cost;    /* why we have n-1 and m-1: room for 13 mfcc values        */    n = n - 1;    m = m - 1;    /* printf("start DTW on distance space: \n");              */    for (int i = 1 ; i < n; i++)    {        for (int j = max(1, i * m / n - w) ; j < min(m, i * m / n + w) ; j++)        {            cost = DistFrame(p1 + i * ORDER, p2 + j * ORDER);            if (cost > 10)            {                printf("Unusual cost value in DTW function: %f, @frame %d, %d\n", cost, i, j);            }            /* printf("mfcc距离 = %d; ", cost);                 */            double minimum = 0;            minimum = *(dtw + (i - 1) * m + j );            if ( *(dtw + i * m + j - 1) < minimum )            {                minimum = *(dtw + i * m + j - 1);            }            if ( *(dtw + (i - 1)*m + j - 1) < minimum )            {                minimum = *(dtw + (i - 1) * m + j - 1);            }            /* printf("dtw最小距离 = %d;", minimum);            */            *(dtw + i * m + j) = cost + minimum;            /* printf("dtw路径距离 = %d;", *(dtw + i * m + j)); */        }    }    /* printf("\n");                                            */    double dist = *(dtw + n * m - 1);    if (dist > 100)    {        printf("Unusual dist valule in DTW function: %f\n", dist);    }    free(dtw);    return dist;}



效果和改进




0 0
原创粉丝点击