某些特殊场景下,普通的欧式距离、曼哈顿距离等并不能满足我们的需求。例如在语音识别中,常使用DTW距离(Dynamic Time Warping,动态时间归整),如下图,实线和虚线分别是同一个词“pen”的两个语音波形(在y轴上拉开了,以便观察)。可以看到他们整体上的波形形状很相似,但在时间轴上却是不对齐的。
同时间度量转变为同模式度量,才能更好地反映2个语音波形的相似性:
(图片转载自:https://www.cnblogs.com/Daringoo/p/4095508.html)
而在sklearn中,我们可以自定义部分机器学习模型的距离函数,例如聚类算法DBSCAN就可以自定义距离:
dbscan = DBSCAN(eps=14,
min_samples=2,
metric=lambda a, b: DTW.distance(a, b))
使用metric参数即可。那么算法对这个距离函数有什么要求呢?
1、给出2组feature,它们的类型都是np.ndarray
2、返回一个距离,数据类型是float
为了使得距离适应DTW的特性(即长短不一致),又符合同类相比的距离标准(即所有feature长度一致),我们使用一个特殊数字-9999来填充所有的曲线,使它们长度一直。在运算中,我们用return_center_data函数把这个数字去掉,使得DTW算法能够正确地对原始数据进行距离计算。
由于函数输入变量的类型为np.ndarray,我们为了后续方便操作,全部转化为list类型
@staticmethod
def distance(s1, s2, signal_num=-9999):
# type: (np.ndarray, np.ndarray, int) -> float
tmp_s1 = []
for i in s1:
tmp_s1.append(i)
tmp_s2 = []
for i in s2:
tmp_s2.append(i)
s1_in = DTW.return_center_data(tmp_s1, signal_num)
s2_in = DTW.return_center_data(tmp_s2, signal_num)
result = DTW.dtw(s1_in, s2_in, DTW.dist_for_float)[0]
return result
这样就能正确计算距离,并且聚类了:
聚类结果为:
[0 0 1 1 1]
结果显示曲线可分成两类。详细两两对比的距离结果如