图嵌入方法基础学习之Deepwalk

图嵌入方法之deepwalk

(Ⅰ). Graph embedding的意义

图广泛存在于真实世界的多种场景中,即节点和边的集合。通过对它们的分析,我们可以深入了解社会结构、语言和不同的交流模式,因此图一直是学界研究的热点。

图分析任务可以大致抽象为以下四类: ( a )节点分类,( b )链接预测,( c )聚类,以及( d )可视化。

  • 节点分类旨在基于其他标记的节点和网络拓扑来确定节点的标签(也称为顶点);
  • 链路预测是指预测缺失链路或未来可能出现的链路的任务;
  • 聚类用于发现相似节点的子集,并将它们分组在一起;
  • 可视化有助于深入了解网络结构。

但是传统的机器学习图形是有限的。这些网络关系只能使用数学、统计和机器学习的特定子集,而向量空间有更丰富的方法工具集。

所以提出来的嵌入是压缩的表示。邻接矩阵描述图中节点之间的连接。它是一个|V| x |V|矩阵,其中|V|是图中节点的个数。矩阵中的每一列和每一行表示一个节点。矩阵中的非零值表示两个节点相连。使用邻接矩阵作为大型图的特征空间几乎是不可能的。所以嵌入比邻接矩阵更实用,因为它们能将节点属性打包到一个维度更小的向量中

(Ⅱ). 什么是图嵌入

图嵌入(Graph Embedding,也叫Network Embedding)是一种将图数据(通常为高维稠密的矩阵)映射为低微稠密向量的过程,捕获图的拓扑结构、顶点到顶点的关系以及关于图、子图和顶点的其他相关信息,能够很好地解决图数据难以高效输入机器学习算法的问题。

但是图嵌入也需要满足一定的要求:

  • 属性选择:确保嵌入能够很好地描述图的属性,即需要表示图拓扑,节点连接和节点邻域。这样后期的预测或可视化才能获得较好的表现。
  • 可扩展性:大多数真实网络都很大,包含了大量节点和边。嵌入方法应具有可扩展性,能够处理大型图。定义一个可扩展的模型具有挑战性,尤其是当该模型旨在保持网络的全局属性时。网络的大小不应降低嵌入过程的速度,一个好的嵌入方法不仅在小图上高效嵌入,同时也需要在大图上能够高效地嵌入。
  • 嵌入的维度:实际嵌入时很难找到表示的最佳维数,维度越大能够保留的信息越多,但是通常有更高的时间和空间复杂度。较低的维度虽然时间、空间复杂度低,但无疑会损失很多图中原有的信息。

(Ⅲ). 图嵌入方法

(一). deepwalk

主要思想

Deepwalk是一种将随机游走(random walk)和word2vec两种算法相结合的图结构数据挖掘算法。该算法能够学习网络的隐藏信息,能够将图中的节点表示为一个包含潜在信息的向量。
图嵌入方法基础学习之Deepwalk_第1张图片

算法流程

  1. 输入所有点V与边E集合组成的图G,窗口大小,游走的顶点,游走长度;
  2. 初始化一个点的embedding矩阵,建一颗二叉树(为了后面Hierarchical Softmax);
  3. 训练开始;
  4. 输出点的embedding。
    图嵌入方法基础学习之Deepwalk_第2张图片
    SkipGram 对序列中的每个顶点,计算条件概率,即该结点出现的情况下序列中其他结点出现的概率的log值并借助随机梯度下降算法更新该结点的向量表示。
    图嵌入方法基础学习之Deepwalk_第3张图片
    总结就是 采用随机游走的方式随机选择起始点,产生局部物品序列。将这些物品序列当作句子进行word2vec建模,生成最终的物品Embedding向量。

Deepwalk核心介绍

(1). 建图:构建数据集的关系图网络
(2).采路径:graph就是上面建的图网络,seed_nodes为路径的起点node,walk_length表示路径走多长。

def sample_walks(self, graph, seed_nodes, walk_length):
    walks = dgl.sampling.random_walk(graph, seed_nodes, length=walk_length)
    return walks

(3).skip-gram:采出路径后,接下来就得根据skip-gram方法采出用来训练的节点对了。

def skip_gram_gen_pairs(self, walk, half_win_size=2):
    src, dst = list(), list()

    l = len(walk)
    # rnd = np.random.randint(1,  half_win_size+1, dtype=np.int64, size=l)
    for i in range(l):
        real_win_size = half_win_size
        left = i - real_win_size
        if left < 0:
            left = 0
            right = i + real_win_size
            if right >= l:
                right = l - 1
                for j in range(left, right + 1):
                    if walk[i] == walk[j]:
                        continue
                        src.append(walk[i])
                        dst.append(walk[j])
                        return src, dst
class SkipGramModel(nn.Module):
    def __init__(self, num_nodes, embed_dim):
        super(SkipGramModel, self).__init__()
        self.num_nodes = num_nodes
        self.emb_dimension = embed_dim

        self.embed_nodes = nn.Embedding(self.num_nodes, self.emb_dimension, sparse=True)
        nn.init.xavier_uniform_(self.embed_nodes.weight)

    def forward(self, src, pos, neg):
        embed_src = self.embed_nodes(src)  # (B, d)
        embed_pos = self.embed_nodes(pos)  # (B, d)
        embed_neg = self.embed_nodes(neg)  # (B, neg_num, d)

        pos_socre = torch.sum(torch.matmul(embed_src, embed_pos.transpose(0, 1)), 1)
        pos_socre = -F.logsigmoid(pos_socre)

        neg_socre = torch.sum(torch.matmul(embed_src, embed_neg.transpose(1, 2)), (1, 2))
        neg_socre = -F.logsigmoid(-neg_socre)

        loss = torch.mean(pos_socre + neg_socre)
        return loss

完整代码见博主分享

https://blog.csdn.net/liangwqi/article/details/102957180
https://zhuanlan.zhihu.com/p/397710211

你可能感兴趣的:(图嵌入学习,word2vec,python)