翻译:http://www.kdnuggets.com/2016/02/tree-kernels-quantifying-similarity-tree-structured-data.html
名称:Tree Kernels: Quantifying Similarity Among Tree-Structured Data
参考 : http://www.36dsj.com/archives/43411
有些地方翻译的不太准确,还望海涵。
网络和图形是一种节点形式的结构化数据类型,它们之间的关系用链接或者边来描述。图中的节点和边可能有多个属性,比如数字或类别,甚至更复杂。
今天,大量的数据是可用的网络或图形的形式。例如,万维网和它的网页和超链接,社会网络,语义网络,生物网络,科学文献的引用网络,等等。
树是一种特殊类型的图形,很适合表示多种类型的数据。树的分析是计算机和数据科学中的一个重要领域。在这篇文章中,我们将分析树的结构。特别的,我们将主要介绍树的内核,这是一种可以比较两棵树相似或差异的方法。对于很多如分类和数据分析的现代应用,这是一个很重要的过程。
分类是机器学习和数据分析的重要组成部分。在一般情况下,分类可以是有监督的或无监督的。在有监督的分类中,数据的类别是已知的,并且会有已经标注好正确类别的训练数据供我们训练分类模型。对比之下,无监督分类是一种试图通过数据之间的相似特征来将数据聚成几类,并且我们并不知道数据的类别。
无监督分类法可以结合图论的知识去识别相似的树网络。多个领域都采用了树结构模型的数据。比如,在自然语言处理(NLP)领域,语法分析树是一种有序和被标记的树。在自动推理中,搜索解决了很多问题,搜索空间被定义成一种树的形式,树的结点代表搜索状态,树的边代表推导过程。另外,半结构化数据,如HTML和XML文档,可以模拟为有序,标记的树。
这些领域可以通过非监督分类技术进行有效的分析。在自然语言处理(NLP)领域,分类可以用来自动将一组句子分成问题,答案和语句。同样的ML源,相似网站群可以通过HT识别分类方法识别。在所有情况下,我们需要的就是一种衡量两个树相似性的方法。
大多数分类算法需要将数据转化成向量的形式,将数据的特征表示成特征空间中的值,使数据可以在特征空间利用线性代数分析。结构化或半结构化数据,比如树,由于特征空间必须保留结构信息,所以得到的向量维数(即特征空间中的特征数)可能会很高。
考虑到许多分类技术是不能够有效地扩展维度输入,这可能是一个显著的缺点(不知道该怎么翻译这句话)。换句话说,它们的分类能力随着输入维数的增加而降低。这个问题被称为”维数灾难”。
考虑有一个维度为D的空间中一组数据X。假设X包含一组均匀分布的点。如果X的维度增加(即表示每个样本数据的特征数增加),如果要使样本数据均匀分布且具有D=1时的密度,则需要更多的数据。换句话说,输入的维数越大,数据稀疏的可能性越大。一般情况下,稀疏的数据集并没有给出足够的信息以建立一个良好的分类系统。(翻译者注:令data_num表示样本数据的数量,feature_num表示每个样本点特征的数量。若data_num << feature_num,则样本数据很可能无法覆盖所有的特征,及可能有的特征在样本数据中是找不到的。同时,盲目的增加特征的数目,如果有一些不好的特征,可能会使样本数据在高维空间中看起来都很相似,从而无法训练出好的分类模型)。
每个特征空间上面都包含了八个数据点。在一维空间上,很容易辨认出左边一组5个点,和右边一组3个点。在更高维空间中,数据的分类却不是那么明显了。在实际应用中,特征空间可以很容易地拥有数百个维度。
当一组特征信息可以很好的在这个领域使用时,可以用向量的方式表示结构化的数据。反之,如果这组特征信息是没有用的,则可以使用直接处理结构化数据的方法进行处理。
核方法避免了将数据转换成向量的需要。它们需要的是一种度量数据集合中每对元素相似的方法。这种度量被称为核,度量方法实现的函数成为核函数。特征空间中的核方法就是寻找它们的线性关系。在功能上,它们相当于特征空间中的点积的2个数据点,而真正的功能设计,在内核功能设计可能仍然是一个有用的步骤。然而,内核方法避免直接操作在特征空间,因为它可以表明以取代点产品的内核功能是可能的,只要核函数是对称的,正定函数可以作为输入的原始空间数据。
使用内涵函数的优点是,一个巨大的特征空间,可以分析与计算复杂度不依赖于特征空间的大小,但是内核功能的复杂性,这意味着内核的方法是没有灾难的维数。
我们考虑一个由有限数据组成的n个样本数据,我们能用一个n × n的核矩阵来表示数据之间的相似性,这个矩阵独立于每个样本数据的大小。当样本数据远远小于特征数量时,这种方法是有效的。
在一般情况下,核方法不是将数据点映射到特征空间,而是将数据之间的比较转换成一种核矩阵的形式,数据的相关度分析都可以在矩阵中进行。
许多数据挖掘方法都可以核化。将核方法和一些如支持向量机这样的分类方法相结合来分类树结构的数据定义的有效的函数K: T × T → R的方法也称为树核。当设计一个特殊用途的树核时,若其计算时间在树大小的多项式时间内完成,并且能够检测到同构的图,则称为完全树核(不知道翻译的对不对)。
现在,让我们来介绍一些用于测量树的相似性的树核。其主要思想是计算数据集中的每对树的核,从而建立一个核矩阵用来分类。
首先,我们先对字符串内核做一个简短的介绍,这有助于帮助我们更好的理解一种将树转换为字符串的核方法。
定义numy(s)为子字符串y在字符串s中出现的次数,|s|为字符串s的长度。字符串内核定义如下:
其中F是 S1 和 S2共同出现的子字符串的集合,ws 是权重参数。我们可以看到,两个字符串的公共子串越多,这个内核计算出的值越高。
我们可以使用字符串内核来构建一个树内核。这种构建内核的方法是,用系统的方法将树的结构编码从而将两棵树转换成两个字符串,然后用上述的字符串内核的方法来计算。
我们用下边的方法将树转化为字符串:
T表示其中一棵树,label(ns) 是树T中结点ns的标签。tag(ns)是树T中以结点ns为根结点的子树的字符串表示。若nroot表示树T的根结点,那么tag(nroot)就是整个树T的字符串表示了。接下来,令string(T) = tag(nroot)为树T的字符串表示。递归进行下面的步骤,自下而上的得到string(T):
1、如果结点ns是一个叶子结点,令tag(ns)=“[”+ label(ns) +“]”(在这里,'+'是字符串串联运算符)。
2、如果结点ns不是一个叶子结点,并且有c个孩子结点n1, n2, … , nc, , 对tag(n1), tag(n2), … , tag(nc)按词汇表的顺序进行排序以获得tag(n1*), tag(n2*), … , tag(nc*),令 tag(ns) = “[” + label(ns) + tag(n1*) + tag(n2*) + … + tag(nc*) + “]”。
下面的图显示了一棵树转换为字符串的例子。
现在我们可以用上述方法将树 T1和树T2转换为字符串S1和S2,而树的内核定义如下:
在很多应用中,权重参数经常表示为 w|s|,即他的值是根据字符串长度 |s|来决定的。比较典型的权重w|s|设置方法如下:
1、常数加权法(如w|s|=1)
2、k-普频加权法(w|s| = 1 if |s| = k, and w|s| = 0 otherwise)
3、指数加权法(w|s| = λ|s|,其中 0 ≤ λ ≤ 1是衰减率)
为了计算内核,必须得到所有公共子字符串的集合F,以及它们在字符串S1 和 S2中出现的次数。寻找公共子串很容易实现,如果使用后缀树或者后缀数组可以在O(|S1| + |S2|)内实现。如果我们假定描述结点标签所需最多的字母(如位,字节,字符)个数是一定的,那么转换后的字符串长度是 |S1| = O(|T1|)和|S2| = O(|T2|),所以计算核函数的时间复杂度是O(|T1| + |T2|),是线性的。
上面的树核使用了一个水平的或者广度优先(?)的方法将树转换为字符串。虽然这种方法很简单,但这种转换意味着它不能直接在其原始形式的树上操作。
本节将定义一种树核,这种树核从垂直方向上直接操作原始树。
一条从根结点到叶子结点的路径定义为子路径,子路径集是包含所有子路径的集合(个人感觉定义的可能有点错误):
假设我们要利用两棵树的子路径集来定义一个树核函数K(T1, T2),可以定义如下:
nump(T)是子路径p在树T中出现的次数,|p|是子路径p中结点的个数,P是出现在T1和T2中所有子路径的集合,w|p| 和上文介绍的相似,也是权重。
这里,我们使用深度优先搜索简单的实现了一下。虽然该算法是平方时间内完成的,但是有些方法使用后缀树和后缀数组或快速排序的延伸算法,平均时间可达到O(|T1|log|T2|)。
subpath_track = {}
def generate_subpaths(path, l):
if l == len(path):
if tuple(path) not in subpath_track:
subpath_track[tuple(path)] = 1
else:
subpath_track[tuple(path)] += 1
else:
index = 0
while l+index-1 < len(path):
if tuple(path[index: l+index]) not in subpath_track:
subpath_track[tuple(path[index: l+index])] = 1
else:
subpath_track[tuple(path[index: l+index])] += 1
index += 1
generate_subpaths(path, l+1)
def get_subpaths(graph, root, track, path):
track[root] = True
if graph.degree(root) == 1:
generate_subpaths(path, 1)
else:
for node in graph.neighbors(root):
if node not in track:
get_subpaths(graph, node, track, path + [node, ])
def kernel_subpath(t1, t2, common_track):
kernel_v = 0
for p in subpath_track:
kernel_v += common_track[t1][p]*common_track[t2][p]
return kernel_v
在这个例子中,我们使用的加权参数为w|p| w|s| = 1。这里所有子路径的权重均相同。然而,在很多情况下使用k-普频加权法或动态加权法会更好些。
在我们总结之前,让我们简单地看看一个真实的利用树结构进行分类的例子︰ 分类网站。在许多数据挖掘上下文中,知道一些来自那种类型的网站可以使我们受益良多。结果表明,由于相同作用的网页会有相似的结构,所以利用树来进行网页分类非常有效。
我们应该怎么做?HTML文档的逻辑嵌套结构很像一棵树。每一个文档包含一个根元素,里面包含了其他元素嵌套。若元素嵌套在HTML标签里,则在逻辑上相当于这个标签的子节点。
下面的代码可以将html文档转换为树结构:
def html_to_dom_tree(root):
node_id = 1
q = deque()
graph = nx.Graph()
q.appendleft({'element': root, "root_id": node_id})
while len(q):
node = q.pop()
if node and node['element'].name == "body":
graph.add_node(node_id, element=node['element'].name)
node_id += 1
root_id = node['root_id']
labels[root_id] = node['element'].name
for t in node['element'].contents:
if t and t.name:
graph.add_node(node_id, element=t.name)
graph.add_edge(root_id, node_id)
q.appendleft({"element": t, "root_id": node_id})
node_id += 1
return graph
它会产生一个类似下边图片的树形图:
这里有几个有用的Python库:networkx可以处理复杂的图结构,Beautiful Soup用来从网页文件中提取数据。
我们要在1000个网站的主页上找到组。通过将每个网页变成这样的一棵树,我们可以相互比较,例如通过使用上一节给出的路径树核。通过这些测量的相似性我们可以发现,例如,电子商务网站,新闻网站,博客和教育网站是很容易确定他们的相似性的。
在这篇文章中,我们介绍了树结构数据元素的比较,并显示了如何应用内核的方法,以获得一个可量化的测量他们的相似性。内核的方法已被证明是一个很好的选择时,在高维空间中一个共同情况下,与树结构的工作。这些技术为进一步分析大套树木,使用以及研究的方法,操作过的内核矩阵阶段。
树结构在现实世界中许多领域如XML和HTML文件,遇到化学化合物,自然语言处理,或某些类型的用户行为。作为从HTML构建树的例子证明,这些技术使我们能够在这些领域进行有意义的分析。
原文地址:Tree Kernels: Quantifying Similarity Among Tree-Structured Data
End.