我有一些文本文档,其中主要包含项目列表。
每个项目都是一组不同名称的多个标记:名字,姓氏,出生日期,电话号码,城市,职业等。标记是一组单词。
项目可以位于多行上。
文档中的项目具有大致相同的令牌语法,但不一定必须完全相同。
它们可能是项目之间以及项目内部的更多/更少标记。
FirstName LastName BirthDate PhoneNumber
Occupation City
FirstName LastName BirthDate PhoneNumber PhoneNumber
Occupation City
FirstName LastName BirthDate PhoneNumber
Occupation UnrecognizedToken
FirstName LastName PhoneNumber
Occupation City
FirstName LastName BirthDate PhoneNumber
City Occupation
目的是识别所使用的语法,例如
Occupation City
最后找出所有项目,甚至认为它们不完全匹配。
为了简短易懂,让我们改用一些别名A,B,C,D ...来指定这些标记类型。
例如
A B C
D F
A B C
D E F
F
A B C
D E E F
A C B
D E F
A B D C
D E F
A B C
D E F G
在这里我们可以看到Item语法是
A B C
D E F
因为它是最匹配序列的那个。
从一个文档到另一个文档,语法(令牌类型和顺序)可能会有很大不同。例如另一个文件可能有那个清单
D A
D A
D
D A
B
D A
目的是在没有先验知识的情况下弄清楚该语法。
从现在开始,新行也被视为令牌。然后可以将文档表示为令牌的一维序列:
这里重复的顺序是A B C B
因为令牌产生的冲突最少。
让我们使其复杂一些。从现在开始,每个令牌都没有确定的类型。在现实世界中,我们并不总是100%地确定某种令牌的类型。相反,我们给它某种类型的可能性。
A 0.2 A 0.0 A 0.1
B 0.5 B 0.5 B 0.9 etc.
C 0.0 C 0.0 C 0.0
D 0.3 D 0.5 D 0.0
这是我要实现的目标的抽象图形:
被认为是A的解决方案:令牌补丁的卷积
该解决方案包括应用具有多个标记补丁的卷积,并采用产生最少冲突的标记。
这里最困难的部分是找到可能沿着观察序列滚动的补丁。这个想法很少,但是没有什么令人非常满意的:
建立令牌之间过渡的马尔可夫模型缺点:由于马尔可夫模型没有记忆,我们将失去转移的顺序。例如,如果重复序列为A B C B D
,则我们失去A-> B出现在C-> B之前的事实。
这似乎已在生物学中广泛使用,以分析DNA / RNA中的核碱基(GTAC)。缺点:后缀树非常适合完全匹配标记(例如字符)。我们既没有确切的序列,也没有确切的标记。
蛮力尝试各种尺寸的每种组合。可以实际工作,但是会花费一些(很长(很长))时间。
认为B的解决方案:建立后缀Levenshtein距离表
直觉是在计算每个后缀到每个后缀的距离时,可能存在一些局部的距离最小值。
距离函数是Levenshtein距离,但将来我们将能够对其进行自定义,以便考虑存在某种类型的可能性,而不是为每个令牌都具有固定的类型。
为了使演示更简单,我们将使用固定类型的令牌,并使用经典的Levenshtein计算令牌之间的距离。
例如,让我们输入序列ABCGDEFGH ABCDEFGH ABCDNEFGH
。
我们计算每个后缀与每个后缀的距离(裁剪成相等的大小):
for i = 0 to sequence.lengh
for j = i to sequence.lengh
# Create the suffixes
suffixA = sequence.substr(i)
suffixB = sequence.substr(j)
# Make the suffixes the same size
chunkLen = Math.min(suffixA.length, suffixB.length)
suffixA = suffixA.substr(0, chunkLen)
suffixB = suffixB.substr(0, chunkLen)
# Compute the distance
distance[i][j] = LevenshteinDistance(suffixA, suffixB)
我们得到例如以下结果(白色是小距离,黑色是大距离):
现在,很明显,与后缀相比,任何后缀都将具有零距离。但是我们对后缀(完全或部分)匹配不感兴趣,因此我们裁剪了该部分。
由于后缀被裁剪为相同的大小,因此比较长字符串总是比比较小字符串产生更大的距离。
我们需要通过从右(+ P)开始,向左线性渐弱的平滑惩罚来补偿这一点。
我不确定如何选择适合所有情况的良好惩罚函数。
在这里,我们在最右边应用(+ P = 6)罚分,在左边逐渐淡出0。
现在我们可以清楚地看到出现了两条清晰的对角线。该序列中有3个项目(Item1,Item2,Item3)。最长的线表示Item1对Item2和Item2对Item3的匹配。第二长表示项目1与项目3之间的匹配。
现在,我不确定采用那种数据的最佳方法。像绘制最高对角线一样简单吗?
让我们假设它是。
让我们计算从每个标记开始的对角线的平均值。我们可以在下图(矩阵下方的向量)上看到结果:
显然有3个局部最小值,与每个项目的开头匹配。看起来很棒!
现在让我们在序列中添加更多缺陷:
ABCGDEFGH ABCDEFGH TROLL ABCDEFGH
现在很明显,我们的对角线平均值向量被弄乱了,我们不能再利用它了。
我的假设是可以通过自定义的距离函数(而不是Levenshtein)解决此问题,在该函数中插入整个块可能不会受到太大的惩罚。那是我不确定的。
结论
探索的基于卷积的解决方案似乎都不适合我们的问题。
基于Levenshtein距离的解决方案似乎很有希望,特别是因为它与基于概率的类型令牌兼容。但是我还不确定如何利用它的结果。
如果您有相关领域的经验,并给我们提供了一些很好的提示或其他探索技术,我将不胜感激。提前非常感谢您。