语音识别领域和NLP领域都会接触到WER(字错率)和CER(字符错误率),但两者的计算都离不开字符串编辑距离。
字符串编辑距离(Edit Distance),是俄罗斯科学家Vladimir Levenshtein提出的概念。两个字符串之间的最小距离就是指把一个字符串转换为另一个字符串时,所需要的最小编辑操作的次数。编辑操作包含以下3种:
替换(substitution),将一个字符替换为另一个字符
插入(insertion),插入一个字符
删除(deletion),删除一个字符
最小编辑距离使用动态规划来计算。从直觉上来说,动态规划问题就是把一个大的问题化解为不同的子问题来求解,再把这些子问题的解适当地结合起来,就可以实现对大问题的求解。
举个简单的例子:求字符abc和字符abcd之间的编辑距离,很明显,字符abcd相当于是在字符abc的基础上插入字符d,所以两个字符串之间的编辑距离为1。
从最后一个字符考虑
假设源字符串为S,目标字符串为T,则S和T的最后一个字符s[i]和t[j]对应四种情况:
显然第四种情况是多余的编辑操作。下面来逐个分析以上3种情况。
第一种情况:
S变成T,最后,在S的末尾插入“字符X”,若以dp矩阵来表示两个字符串之间的距离,则dp[i, j]=dp[i, j-1]+1。
第二种情况:
S变成T,最后,把X修改成Y,如果X==Y,说明不需要操作变化,dp[i, j]=dp[i-1, j-1],否则,需要在原来的基础上加1,dp[i, j]=dp[i-1, j-1]+1。
第三种情况:
S变成T,最后,X被删除。dp[i, j]=dp[i-1, j]+1。
综合以上三种情况:
具体用矩阵表示,可以看下面的一个示例:
图1,编辑距离示意图
初始化dp[0][0]=0,矩阵中每一个位置编辑距离的大小,由其左侧、右侧和左对角侧三者中最小值决定。然后依次计算,最右下角位置处的值,即为两个字符串的最小编辑距离。
Python程序实现如下:
# -*- coding: utf-8 -*-
# @Time : 2019/8/24 20:56
# @Author : qszhu
def levenshtein_distance(str1, str2):
m, n = len(str1) + 1, len(str2) + 1
# 初始化矩阵
matrix = [[0] * n for i in range(m)]
matrix[0][0] = 0
for i in range(1, m):
matrix[i][0] = matrix[i - 1][0] + 1
for j in range(1, n):
matrix[0][j] = matrix[0][j - 1] + 1
for i in range(1, m):
for j in range(1, n):
if str1[i - 1] == str2[j - 1]:
matrix[i][j] = min(matrix[i - 1][j - 1], matrix[i-1][j]+1, matrix[i][j-1]+1)
else:
matrix[i][j] = min(matrix[i - 1][j - 1]+1, matrix[i - 1][j]+1, matrix[i][j - 1]+1)
return matrix[m - 1][n - 1]
if __name__ == '__main__':
str1 = "abcde"
str2 = "abv"
print(levenshtein_distance(str1, str2))
当然,时间复杂度为O(m*n)。
其实呢,已经有大神,帮我们做了一个包,直接拿来用就可以。
先pip安装包
pip install python-Levenshtein
然后,就可以验证自己写的算法是否正确。
import Levenshtein
str1 = "abcd"
str2 = "abc"
print(Levenshtein.distance(str1, str2))