转载地址:http://www.cnphp6.com/archives/60574
论文介绍:https://cs.fit.edu/~pkc/papers/tdm04.pdf
前言
动态时间规整(Dynamic Time Warping,DTW)是孤立词识别的早期技术,梳理一下,主要包括:
1)孤立词识别操作步骤;
2)DTW原理;
内容基本就是两个博文的整合,最后一并给出链接。
一、孤立词识别操作步骤
基本原理:
基本操作是预加重、分帧,端点检测技术又叫有话帧检测(Voice activity detection, VAD)技术。特征提取参考之前的博文。例如:
检测语音→特征提取。
多说一句,倒谱就是将乘性关系变为加性关系:xy→logx+logy,一般的谱分析我们都是采用频谱,或者小波这样与频谱的区别只是不同量度,这些都是解决加性噪声的滤波问题,倒谱是一种为了滤除乘性噪声的谱方法,简单的说就是对功率谱求log,再反傅里叶变换,公式如 。
特征提取之后就是特征的模板匹配,也就是DTW算法。
二、DTW算法思路
A-DTW必要性
语音识别的匹配需要解决的一个关键问题是说话人对同一个词的两次发音不可能完全相同,这些差异不仅包括音强的大小、频谱的偏移,更重要的是发音时音节的长短不可能完全相同,而且两次发音的音节往往不存在线性对应关系。
设参考模板有M帧矢量{R(1),R(2),…R(m),…,R(M)},R(m)为第m帧的语音特征矢量,测试模板有N帧矢量{T(1),T(2),…T(n),…,T(N)},T(n)是第n帧的语音特征矢量。d(T(in),R(im))表示T中第in帧特征与R中im帧特征之间的欧几里得距离。直接匹配是假设测试模板和参考模板长度相等,即in=im;线性时间规整技术假设说话速度是按不同说话单元的发音长度等比例分布的,即。这两种假设其实都不符合实际语音的发音情况,我们需要一种更加符合实际情况的非线性时间规整技术,也就是DTW算法。三种匹配模式的对比:
B-DTW思路
首先还是介绍下DTW的思想:假设现在有一个标准的参考模板R,是一个M维的向量,即R={R(1),R(2),……,R(m),……,R(M)},每个分量可以是一个数或者是一个更小的向量。现在有一个才测试的模板T,是一个N维向量,即T={T(1),T(2),……,T(n),……,T(N)}同样每个分量可以是一个数或者是一个更小的向量,注意M不一定等于N,但是每个分量的维数应该相同。
由于M不一定等于N,现在要计算R和T的相似度,就不能用以前的欧式距离等类似的度量方法了。那用什么方法呢?DTW就是为了解决这个问题而产生的。
例如图A所示,实线和虚线分贝时同一个词“pen”的两个语音波形(在y轴上拉开了,以便观察)。可以看到他们整体上的波形形状很相似,单在时间轴却是不对齐的。例如在第20个时间点的时候,实线波形上的a点对应于虚线波形的b'点,这样传统的通过比较距离来计算相似性很明显不靠谱。因为很明显,实线的a点对应虚线的b点才是正确的。而在图B中,DTW就可以通过找到这两个波形对齐的点,这样计算它们的距离才是正确的。
也就是说,大部分情况下,两个序列整体上具有非常相似的形状,但是这些形状在x轴上并不是对齐的。所以我们在比较他们的相似度之前,需要将其中一个(或者两个)序列在时间轴下warping扭曲,已以达到更好的对齐。而DTW就是实现这种warping扭曲的一种有效方法。DTW通过将时间序列进行延伸和缩短,来计算两个时间序列之间的相似性。
那如何才知道两个波形是对齐了呢?也就是说怎么样的warping才是正确的?直观上理解,当然是waring一个序列后可以和另一个序列重合recover。这个时候两个序列中所有对应的距离之和是最小的。所以从直观上理解,warping的正确性一般指“feature to feature”的对齐。
注明:由B)图可以看出,模板序列中的一个点(这里的点可能是单个数值或是一个向量)可能对应测试序列中的好几个点(也有可能反过来,模板中的好几个点对应测试中的一个点),这正好反映了特征可能的延迟性。比如同一个音素,有的时候发得快,有的时候发的慢。这两种情况进行匹配时,你要把发得快的那个点完全匹配到发的慢的那几个点上。
2、原理
动态时间规整DTW是一个典型的优化问题,它用满足一定条件得时间规整函数W(n)描述测试模板和参考模板的时间对应关系,求解两模板匹配时累计距离最小所对应的规整函数。
假设我们有两个时间序列Q和C,他们的长度分别是n和m:(实际语音匹配运用中,一个序列为参考模板,一个序列为测试模板,序列中的每个点的值为语音序列中每一帧的特征值。例如语音序列Q共有n帧,第i帧的特征值(一个数或者一个向量)是qi。至于取什么特征,在这里不影响DTW的讨论。我们需要的是匹配这两个语音序列的相似性,以达到识别我们的测试语音是哪个词)
Q = q1, q2,…,qi,…, qn ;
C = c1, c2,…, cj,…, cm ;
如果n=m,那么就用不着折腾了,直接计算两个序列的距离就好了。但如果n不等于m我 们就需要对齐。最简单的对齐方式就是线性缩放了。把短的序列线性放大到和长序列一样的长度再比较,或者把长的线性缩短到和短序列一样的长度再比较。但是这 样的计算没有考虑到语音中各个段在不同情况下的持续时间会产生或长或短的变化,因此识别效果不可能最佳。因此更多的是采用动态规划(dynamic programming)的方法。
为了对齐这两个序列,我们需要构造一个n x m的矩阵网格,矩阵元素(i, j)表示qi和cj两个点的距离d(qi, cj)(也就是序列Q的每一个点和C的每一个点之间的相似度,距离越小则相似度越高。这里先不管顺序),一般采用欧式距离,d(qi, cj)= (qi-cj)2(也可以理解为失真度)。每一个矩阵元素(i, j)表示点qi和cj的对齐。DP算法可以归结为寻找一条通过此网格中若干格点的路径,路径通过的格点即为两个序列进行计算的对齐的点。
那么这条路径我们怎么找到呢?那条路径才是最好的呢?也就是刚才那个问题,怎么样的warping才是最好的。
注明:两个序列长度不同,不能使用欧氏距离进行匹配。使用dtw时,上图方格中的每个连续的点(开头(1,1)和结尾(m,n)还是要保证的)构成的曲线都有可能,这是就要找出代价最小的那条曲线,如图中标出的黑色曲线。
我们把这条路径定义为warping path规整路径,并用W来表示, W的第k个元素定义为wk=(i,j)k,定义了序列Q和C的映射。这样我们有:
1)边界条件:w1=(1, 1)和wK=(m, n)。任何一种语音的发音快慢都有可能变化,但是其各部分的先后次序不可能改变,因此所选的路径必定是从左下角出发,在右上角结束。
2)连续性:如果wk-1= (a’, b’),那么对于路径的下一个点wk=(a, b)需要满足(a-a’) <=1和 (b-b’) <=1。也就是不可能跨过某个点去匹配,只能和自己相邻的点对齐。这样可以保证Q和C中的每个坐标都在W中出现。
3)单调性:如果wk-1= (a’, b’),那么对于路径的下一个点wk=(a, b)需要满足0<=(a-a’)和0<= (b-b’)。这限制W上面的点必须是随着时间单调进行的。以保证图B中的虚线不会相交。
结合连续性和单调性约束,每一个格点的路径就只有三个方向了。例如如果路径已经通过了格点(i, j),那么下一个通过的格点只可能是下列三种情况之一:(i+1, j),(i, j+1)或者(i+1, j+1)
满足上面这些约束条件的路径可以有指数个,然后我们感兴趣的是使得下面的规整代价最小的路径:
分母中的K主要是用来对不同的长度的规整路径做补偿。我们的目的是什么?或者说DTW的思想是什么?是把两个时间序列进行延伸和缩短,来得到两个时间序列性距离最短也就是最相似的那一个warping,这个最短的距离也就是这两个时间序列的最后的距离度量。在这里,我们要做的就是选择一个路径,使得最后得到的总的距离最小。
这里我们定义一个累加距离cumulative distances。从(0, 0)点开始匹配这两个序列Q和C,每到一个点,之前所有的点计算的距离都会累加。到达终点(n, m)后,这个累积距离就是我们上面说的最后的总的距离,也就是序列Q和C的相似度。
累积距离γ(i,j)可以按下面的方式表示,累积距离γ(i,j)为当前格点距离d(i,j),也就是点qi和cj的欧式距离(相似性)与可以到达该点的最小的邻近元素的累积距离之和:
注明:先把模板序列和测试序列的每个点相对应的距离算出来,构成一个m xn的矩阵。然后根据每个元素的代价计算一条最短路径。这里的计算要符合以上三个约束。即,一个点的代价=这个点的值+来自min{下、左、斜下这三个方向的值}。下、左、斜下这三个方向的值可以依次递归求得,直到(1,1)点
3、例子
这个例子中假设标准模板R为字母ABCDEF(6个),测试模板T为1234(4个)。R和T中各元素之间的距离已经给出。如下:
既然是模板匹配,所以各分量的先后匹配顺序已经确定了,虽然不是一一对应的。现在题目的目的是要计算出测试模板T和标准模板R之间的距离。因为2个模板的 长度不同,所以其对应匹配的关系有很多种,我们需要找出其中距离最短的那条匹配路径。现假设题目满足如下的约束:当从一个方格((i-1,j-1)或者 (i-1,j)或者(i,j-1))中到下一个方格(i,j),如果是横着或者竖着的话其距离为d(i,j),如果是斜着对角线过来的则是 2d(i,j).其约束条件如下图像所示:
其中g(i,j)表示2个模板都从起始分量逐次匹配,已经到了M中的i分量和T中的j分量,并且匹配到此步是2个模板之间的距离。并且都是在前一次匹配的结果上加d(i,j)或者2d(i,j),然后取最小值。
所以我们将所有的匹配步骤标注后如下
怎么得来的呢?比如说g(1,1)=4, 当然前提都假设是g(0,0)=0,就是说g(1,1)=g(0,0)+2d(1,1)=0+2*2=4.
g(2,2)=9是一样的道理。首先如果从g(1,2)来算的话是g(2,2)=g(1,2)+d(2,2)=5+4=9,因为是竖着上去的。
如果从g(2,1)来算的话是g(2,2)=g(2,1)+d(2,2)=7+4=11,因为是横着往右走的。
如果从g(1,1)来算的话,g(2,2)=g(1,1)+2*d(2,2)=4+2*4=12.因为是斜着过去的。
综上所述,取最小值为9. 所有g(2,2)=9.
当然在这之前要计算出g(1,1),g(2,1),g(1,2).因此计算g(I,j)也是有一定顺序的。
其基本顺序可以体现在如下:
计算了第一排,其中每一个红色的箭头表示最小值来源的那个方向。当计算了第二排后的结果如下:
最后都算完了的结果如下:
到此为止,我们已经得到了答案,即2个模板直接的距离为26. 我们还可以通过回溯找到最短距离的路径,通过箭头方向反推回去。如下所示:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=invalid-name
"""用DTW算法找到沪深300指数
历史行情与最近10天走势最相似的一段
"""
__author__ = 'yzlnew'
import tushare as ts
from numpy.linalg import norm
import matplotlib.pyplot as plt
from dtw import dtw
def GetData(start_date, end_date):
raw_data = ts.get_k_data('000300', index=True,
start=start_date, end=end_date)
selected_data = raw_data.loc[:, ['date', 'close']]
data = selected_data.set_index('date')
return data
def DtwDist(series1, series2):
dist, cost, acc, path = dtw(
series1, series2, dist=lambda x, y: norm(x - y, ord=1))
return dist
def main():
ten_day_data = GetData('2017-05-18', '2017-06-02')
ten_day_data_array = ten_day_data.values / ten_day_data.values.max()#这里可以采用标准化归0处理
historydata = GetData('2005-04-08', '2017-05-17')
pos = 0 # 记录在历史行情记录里的位置
min_dist = 1 # 最小的距离
min_array = [] # 最相似的序列
for x in range(len(historydata.index) - 10): #设置滑动窗口
current_data = historydata[x:x + 10]
current_array = current_data.values / current_data.values.max()
# print(current_data)
current_dist = DtwDist(ten_day_data_array, current_array)
if current_dist < min_dist:
min_dist = current_dist
min_array = current_array
pos = x
historydata_array=historydata[pos:pos+20]
historydata_array=historydata_array.values/historydata_array.values.max()
print( historydata[pos:pos + 10])
plt.plot(ten_day_data_array, label='The last 10 days')
plt.plot(historydata_array, label='historydata')
plt.plot(min_array, label='Match')
plt.legend()
plt.show()
if __name__ == '__main__':
main()
https://github.com/ivansanchezvera/TrajectoryClustering/blob/master/src/readme.txt