本文转载自csdn博主上杉翔二系列博客并外加一些自己收集的资料,在这里仅作为自己学习之用。
原文链接:https://blog.csdn.net/qq_39388410/article/details/87904974
普通数据挖掘方法大多都是确定性模型,对于输入的输出往往没有随机性,而一些能给出概率的随机性模型似乎更加的适用,如蒙特卡洛模拟,即模拟输入一堆的随机数进行评估。
布朗运动是将看起来连成一片的液体,在高倍显微镜下看其实是由许许多多分子组成的。液体分子不停地做无规则的运动,不断地随机撞击悬浮微粒。当悬浮的微粒足够小的时候,由于受到的来自各个方向的液体分子的撞击作用是不平衡的。在某一瞬间,微粒在另一个方向受到的撞击作用超强的时候,致使微粒又向其它方向运动,这样,就引起了微粒的无规则的运动就是布朗运动。(布朗运动指的是分子迸出的微粒的随机运动,而不是分子的随机运动。)
即布朗运动代表了一种随机涨落现象。普遍的观点仍认为,股票市场是随机波动的,随机波动是股票市场最根本的特性,是股票市场的常态。(随机现象的数学定义是:在个别试验中其结果呈现出不确定性;在大量重复试验中其结果又具有统计规律性的现象。)而布朗运动假设是现代资本市场理论的核心假设。
随机游走(Random Walk,缩写为 RW),又称随机游动或随机漫步,是一种数学统计模型,它是一连串的轨迹所组成,其中每一次都是随机的。它能用来表示不规则的变动形式,如同一个人酒后乱步,所形成的随机过程记录。因此,它是记录随机活动的基本统计模型。其概念接近于布朗运动,是布朗运动的理想数学状态。
Random Walk 是随机过程(Stochastic Process)的一个重要组成部分,通常描述的是最简单的一维 Random Walk 过程。下面给出一个例子来说明:考虑在数轴原点处有一只蚂蚁,它从当前位置(记为 x ( t ) x(t) x(t) )出发,在下一个时刻( x ( t + 1 ) x(t+1) x(t+1))以 1 2 \frac{1}{2} 21 的概率向前走一步(即 x ( t + 1 ) = x ( t ) + 1 ) x(t+1)= x(t)+1) x(t+1)=x(t)+1),或者以 1 2 \frac{1}{2} 21 的概率向后走一步(即 x ( t + 1 ) = x ( t ) − 1 x(t+1)= x(t)-1 x(t+1)=x(t)−1),这样蚂蚁每个时刻到达的点序列 就构成一个一维随机游走过程。
一维、二维随机游走过程中,只要时间足够长,我们最终总能回到出发点;
三维网格中随机游走,最终能回到出发点的概率只有大约 34%;
四维网格中随机游走,最终能回到出发点的概率是 19.3% ;
八维空间中,最终能回到出发点的概率只有 7.3% ;
为什么要对图进行嵌入?
直接在这种非结构的,数量不定(可能数目非常多),属性复杂的 图 上进行机器学习/深度学习是很困难的,而如果能处理为向量将非常的方便。但评价一个好的嵌入需要:
Graph Embedding
图嵌入本身其实是属于表示学习。主要目的是将图中的节点/图表示成低维,实值,稠密的向量形式,使得到的向量能够做进一步的推理,以更好的实现下游任务。图嵌入包括顶点嵌入/图嵌入,嵌入的方法主要有矩阵分解,随机游走和深度学习。
出自2014的KDD,《DeepWalk: Online Learning of Social Representations》
DeepWalk的思想如上图,具体就是从一个顶点出发,然后按照一定的概率随机移动到一个邻居节点,并将该节点作为新的当前节点,如此循环执行若干步,得到一条游走路径。然后把这个路径视为一个“句子”,用word2vec得到嵌入嵌入结果。
def random_walk(self, path_length, alpha=0, rand=random.Random(), start=None):
""" Returns a truncated random walk.
path_length: Length of the random walk.
alpha: probability of restarts.
start: the start node of the random walk.
"""
G = self #图G
if start: #如果设置了开始节点
path = [start]
else:
# 如果没有就随机选择
path = [rand.choice(list(G.keys()))]
#只要没到最大长度
while len(path) < path_length:
cur = path[-1]
if len(G[cur]) > 0:
#按一定概率转移
if rand.random() >= alpha:
path.append(rand.choice(G[cur]))
else:
path.append(path[0])
else:
break
return [str(node) for node in path]
可扩展:处理新加入节点,由于有random walk,所以只需要学习新的结点的信息就可以了
可并行:同时从不同的结点处开始random walk
出自2015WWW,《LINE: Large-scale Information Network Embedding》
github:https://github.com/tangjianpku/LINE
LINE也是一种基于邻域相似假设的方法(网络中相似的点在向量表示中的距离比较近),但DeepWalk可以视做是一个DFS,而LINE更加倾向于BFS。主要由一阶相似度和二阶相似度组成。First-order Proximity:一阶相似度用于描述图中成对顶点之间的局部相似度(两个顶点之间的相似),Second-order Proximity:二阶相似度是顶点的顶点之间的相似度,直观来说朋友的朋友也算是我们的朋友,这也同样应该是有效的,如上图的5和6之间虽然没有边,但是有4个相同的邻居节点,所以理论上我们认为5和6在二阶上是相似的。算法流程为:
Trick:为了避免算二阶时需要遍历每个节点,LINE使用负采样的思想来简化计算。
Think:计算一阶相似度中改变同一条边的方向对于最终结果没有影响,所以一阶只适合无向图。而二阶有出度入度,能够实现有向图。
如何处理新加入的节点? 根据经验分布和连接,优化O就可以了,其中原节点向量不变。
出自2016KDD,《node2vec: Scalable Feature Learning for Networks》
Node2vec 可以看作是对 DeepWalk 的一种更广义的抽象,主要是改进DeepWalk的随机游走策略。由于普通的随机游走无法很好地保留节点的局部信息,所以Node2vec多做了两个参数 P P P 和 Q Q Q 来加以控制(完全随机时P=Q=1),以获取邻域信息和更复杂的依存信息。
两个参数控制以达到BFS和DFS的平衡,具体如上图此时在绿色节点上,返回到前一步红色节点的概率是 $\frac{1}{P} $,到达未与先前红色节点连接的节点的概率为 1 Q \frac{1}{Q} Q1,到达红色节点邻居的概率为1。
#不同之处只有转移的概率不同
def node2vec_walk(self, path_length, start):
G = self
nodes = self.nodes
edges = self.edges
path = [start]
while len(path) < path_length:
cur = path[-1]
cur_n = list(G.neighbors(cur)) #邻居节点
if len(cur_nbrs) > 0:
if len(path) == 1:
path.append(cur_n[sample(nodes[cur][0], nodes[cur][1])])
else:
prev = path[-2]
edge = (prev, cur)
next_node = cur_n[sample(edges[edge][0],edges[edge][1])]
path.append(next_node)
else:
break
return walk
图嵌入的本质是什么?
实际上他们仍然还是矩阵分解!!具体如下图:
github:https://github.com/suanrong/SDNE
前面整理了3种常用的图嵌入方法,DeepWalk,LINE和Node2vec。Structural Deep Network Embeddings(结构深层网络嵌入,SDNE)的不同之处在于,它并不是基于随机游走的思想,在实践中比较稳定。
主要思路如上图,会将节点向量 s i s_i si 作为模型的输入,通过自编码器对这个向量进行降维压缩 z i z_i zi,然后再重建特征。其损失函数定义为:
因为输入的是邻接矩阵,所以这样的重构能够使得结构相似的顶点具有相似的embedding表示向量。所以实际上通过重建学习到的是二阶相似度。
但是与LINE相似,SDNE也想保留一阶和二阶相似度,而且是想要同时优化,以同时捕获局部成对相似性和节点邻域结构的相似性。对于一阶相似度的计算,将架构变成如上图,由左右两个自编码器组成。一阶相似度的目标是计算节点间的相似性,那么可以利用起中间的嵌入得到的隐层向量z,然后计算左侧嵌入和右侧嵌z入间的距离。并可以用拉普拉斯矩阵优化一下计算:
再加上正则项reg,总损失为:
def __make_loss(self, config):
def get_1st_loss_link_sample(self, Y1, Y2):
return tf.reduce_sum(tf.pow(Y1 - Y2, 2))
def get_1st_loss(H, adj_mini_batch):
D = tf.diag(tf.reduce_sum(adj_mini_batch,1))
L = D - adj_mini_batch ## 拉普拉斯矩阵
return 2*tf.trace(tf.matmul(tf.matmul(tf.transpose(H),L),H))
def get_2nd_loss(X, newX, beta):
B = X * (beta - 1) + 1
return tf.reduce_sum(tf.pow((newX - X)* B, 2))
#对w和b的正则化项
def get_reg_loss(weight, biases):
ret = tf.add_n([tf.nn.l2_loss(w) for w in weight.itervalues()])
ret = ret + tf.add_n([tf.nn.l2_loss(b) for b in biases.itervalues()])
return ret
#总损失函数
self.loss_2nd = get_2nd_loss(self.X, self.X_reconstruct, config.beta)
self.loss_1st = get_1st_loss(self.H, self.adjacent_matriX)
self.loss_xxx = tf.reduce_sum(tf.pow(self.X_reconstruct,2))
self.loss_reg = get_reg_loss(self.W, self.b)
return config.gamma * self.loss_1st + config.alpha * self.loss_2nd + config.reg * self.loss_reg
其实似乎这个建模的思路和矩阵分解是差不多的,只是在降维时用的不是矩阵分解,而是自编码器,并融入了一阶与二阶的概念
github:https://github.com/benedekrozemberczki/graph2vec
出自MLGWorkshop 2017的《graph2vec: Learning Distributed Representations of Graphs》
直接对整个图进行嵌入。一般比较容易想到的做法是Graph Pooling,著名的方法有Graph Coarsening,即用其他处理节点Embedding的方法处理后,然后逐渐合并聚类节点比如同一个class就归为一个超节点,最后变成一个向量。还有一类方法是node selection,即选部分的节点来代替。还有更直接的方法是直接max或者mean咯。
而Graph2Vec原理上和DeepWalk差不多,也是尝试借用word2vec来训练,只是这次为了嵌入整张图,所以尝试利用子图来训练。类似文档嵌入doc2vec,通过最大化作为输入的文档,来预测随机单词的可能性,Graph2vec预测图中子图的概率。
#把图处理成doc2vec问题的形式就可以用gensim了
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
#得到Wisfeiler-Lehman特征
def do_a_recursion(self):
new_features = {}
for node in self.nodes:
nebs = self.graph.neighbors(node)
degs = [self.features[neb] for neb in nebs]
features = [str(self.features[node])]+sorted([str(deg) for deg in degs])
features = "_".join(features)
hash_object = hashlib.md5(features.encode())
hashing = hash_object.hexdigest()
new_features[node] = hashing
self.extracted_features = self.extracted_features + list(new_features.values())
return new_features
主要是将GAN的思想引入到图领域,简单来说也是和GAN一样,分为生成器和判别器:
值得注意的是在生成器的G中,如果要对每个节点都算一遍显然不划算,所以作者的解决方案是以某个节点为根节点BFS遍历形成一棵生成树(其实挺像随机游走的),然后计算树中两个节点之间的边对应的概率。
图神经网络(Graph Neural Network, GNN)也是很好的图嵌入方法。GNN包括图卷积神经网络(GCN),图注意力网络(GAT),Graph LSTM等等,虽然技术上还是和CV,NLP领域内的方法很像。。。
图表征学习的局限