本次要总结和分享的论文是 LINE: Large-scale Information Network Embedding,其链接 论文[1],所参考的实现代码 code[2],这篇论文某些细节读起来有点晦涩难懂,不易理解,下面好好分析下。

论文动机和创新点

  • information network 在现实世界中无处不在,例如最常见的社交网络图。而这种网络通常包含 百万以上的节点和数以十亿记的边,如果能将这种高维复杂的网络映射(降维) 到低维空间内,用于可视化、节点分类、预测、推荐等相关任务,则能产生巨大的商业和学术价值,而本论文就旨探讨如何将高维的信息网络 映射(降维)到低维空间内。

  • 现实中的信息网络十分复杂,通常包含 百万以上的节点和数以十亿记的边,对于如此复杂庞大的图结构,目前大多数的图嵌入方法都不可行。

  • 由此本文提出一种高效的网络表征学习方法,对于有向/无向、有权重/无权重的图都可适用,且可在单机器上、数小时之内完成对 包含百万以上的节点和数以十亿记的边的图网络的 embedding 学习。

  • 本论文所提方法 能保存 局部和全局的网络结构信息,并且提出了高效的优化技巧,使得能对包含百万节点的图进行学习。局部网络结构信息:节点间的一阶近似性(first-order proximity) 全局网络结构信息:节点间的二阶近似性(second-order proximity) 优化技巧:edge-sample,negative-sample

  • 与 DeepWalk 类似深度优先搜索相比,Line 更像一种广度优先搜索,对于取得二阶相似性,广度更加合理。并且 Line 适用于带权图,而 DeepWalk 不适用。

LINE 主要内容

图结构的定义












上述公式中




 表示 节点集合,




 表示边集合,对每天边上的权重 




















 表示节点




 和




 的关系强弱,若是无向图则 


















,反正则不相等。注意本论文只探讨边权重 






的情况。如上所描述的图 G 基本上可以囊括现实世界中的信息网络。

一阶相似性(first-order proximity)

对于图中任意两个节点









 都可以由边进行连接,如果在图中两节点有边则 












,否则等于 0,这种定义也是符合现实逻辑的,在 information network 中,如果两个用户存在连接关系,则该两个用户的性格、兴趣等可能存在相似性、如果两个网页存在连向彼此的链接,则该网页内容可能存在相似性等等。在这里可以假设有一条无向边 










,其两节点












 的联合概率可以定义如下:

由此我们可以得到在






 空间上的概率分布









 经验概率分布可以定义为




































其中 


























 ,由此可以的带 






 上的经验概率分布 

















 论文提到要让

















 这两种分布 KL 散度距离越近越好,去掉一些常数,由此可得到 一阶相似的损失函数:

论文里提到这种一阶相似性只能应用在无向图中,不适用有向图,这里也可理解,按照一阶相似度计算,不可能存在







 与 







 相似 不等于 







 与 







 相似度。

那么问题来了,为何按照公式(3) 的优化方向,得出来的节点







 的表示向量












 真的与节点







 的表示向量












 就真的如 如期的那样相似或者不相似呢?这点论文中并没有解释,可能是个常识性知识,但是仍值得探讨下, 假设










 很大,这说明实际中,节点







 与 







 很相似,那么按这公式(3) 优化,















也应该很大,则 






















 也应该很大,则按照余弦相似度计算,则向量 












 与 












 的夹角很小,则该












 与 












 很相似,这就符合如期了。

这里还有一个问题:由公式(1)计算出的









 里面每个概率值都是有 sigmoid 计算出来的,整体并不是一个标准概率分布,因为
















 很有可能性发生
,严格上来说












 并不是一种分布。而




















 是一定的。这个问题不知如何解释?

二阶相似性(second-order proximity)

但是如果仅仅根据两节点是否存在直接相连的边来判断相似性,显然是不够的,例如两个用户虽然没有直接相连,但是他们却存在大量共同好友,由常识也知道认为这两用户是相似的,但是他们的一阶相似性却为 0。由此定义二阶相似性:两节点各自相邻节点的一阶相似性。可解决一阶相似性的稀疏问题。

因为二阶相似性要在有向图上适用,故对每个节点,有两个角色要扮演:① 自身角色 ② 相对别的节点,节点作为上下文角色。例如对于有向边








,在给定节点







 的情形下,







 节点需要承担







上下文角色。每个节点都有两个角色,每个角色都有两个表示向量。例如节点







 的表示向量












 和上下文表示向量 















。由此可得在给定







 情形下,







 的表示向量:

上式中 




 表示所有节点数量
。那么对于每个节点,都可以得到一个概率分布 















。同样论文中也提出了二阶的经验分布:











































其中


































 表示节点







 的出度,由此得到二阶经验分布 




















 同样去掉一些常数,可得二阶相似性的损失函数:

到这来,可能有人要问,这种二阶相似性计算,如何体现 “两个节点拥有相似的相邻节点,则该两个节点肯定有相似性”,论文中对此也没有任何解释,可能是个比较常识性的问题,但是我认为仍值得深入解释一下,这里提出我个人的想法:试想一下,在公式(4)中 







 与







 相邻,并且







 不断对与他相邻的节点







 施加影响,也就是相邻节点间相互影响,并且不断沿着边向四周传播影响,如果两个节点拥有相似的邻接点,那么这两个节点受到的影响也是一致的,邻接点越相似,所受的影响就越一致。

可能又有人问,为啥二阶弄个上下文向量












,试想一下,在公式(4)中,如果


















 为 

















,那么


















 与 


















 就相等了,那么就没有方向上的区别了。

优化技巧

边采样

论文里提到对于一个庞大复杂的带权有向图,其边权重的方差可能是很大的,难以训练优化,论文中提出可将每个带权边拆解成多个权重为




 的边
。但是这样做,图将变得非常复杂,对机器的 memory 要求将会剧增。为了缓解这个问题,可以对边进行带概率(相同两节点边出现频次/ 总边数)的采样(即采样出的样本服从原始样本分布)。论文中提出使用 alias method 方法进行采样。该方法查找时间复杂度 O(1),但是建表时间复杂度 O(n),但是只需建一次即可。详情可看 alia-method[3]。这样就完成了对边的采样。

节点负采样

与 NLP 中的 word2vec 所遇到的问题类似,对于公式(4) 来说,分母需要遍历节点图中的所有节点,这样计算量可能非常巨大,因此需要采样部分负节点,来组成负样本,那么公式(4)可转化成如下:

对于上面公式中的求和下标 




,个人感觉改成




 应该较好理解些。上式相当于把节点







 与 







 作为一对正样本(label=1),而节点







 与每个采样得到的节点组成的 











 作为负样本(label=-1),故里面有个负号。有正有负,有变大有变小才能优化。

在实写代码时,只需要将每对 pairwise 做内积乘以 label 再做 log_sigmoid,再对所有 pairwise 求和取负,再 minimize 即可

同理在公式(3) 里面,也需要对没有边相连的节点进行采样,作为负样本,这样 有正有负,有变大有变小才能优化。

所以对一阶相似与二阶相似做完负采样后,两者损失函数一致了,不同的是对于一阶相似,需将公式(7) 中的













 成 












,实写代码时也是。

关键代码

class LINEModel:def __init__(self, args):self.u_i = tf.placeholder(name='u_i', dtype=tf.int32, shape=[args.batch_size * (args.K + 1)])self.u_j = tf.placeholder(name='u_j', dtype=tf.int32, shape=[args.batch_size * (args.K + 1)])self.label = tf.placeholder(name='label', dtype=tf.float32, shape=[args.batch_size * (args.K + 1)])self.embedding = tf.get_variable('target_embedding', [args.num_of_nodes, args.embedding_dim],initializer=tf.random_uniform_initializer(minval=-1., maxval=1.))self.u_i_embedding = tf.matmul(tf.one_hot(self.u_i, depth=args.num_of_nodes), self.embedding)if args.proximity == 'first-order':self.u_j_embedding = tf.matmul(tf.one_hot(self.u_j, depth=args.num_of_nodes), self.embedding)elif args.proximity == 'second-order':self.context_embedding = tf.get_variable('context_embedding', [args.num_of_nodes, args.embedding_dim],initializer=tf.random_uniform_initializer(minval=-1., maxval=1.))self.u_j_embedding = tf.matmul(tf.one_hot(self.u_j, depth=args.num_of_nodes), self.context_embedding)self.inner_product = tf.reduce_sum(self.u_i_embedding * self.u_j_embedding, axis=1)self.loss = -tf.reduce_mean(tf.log_sigmoid(self.label * self.inner_product))self.learning_rate = tf.placeholder(name='learning_rate', dtype=tf.float32)# self.optimizer = tf.train.GradientDescentOptimizer(learning_rate=self.learning_rate)self.optimizer = tf.train.RMSPropOptimizer(learning_rate=self.learning_rate)self.train_op = self.optimizer.minimize(self.loss)

由上面代码可以看出 当选择一阶相似性时:节点







 是从 












 矩阵里取表示向量,模型也只更新 












 矩阵;当选择二阶相似性时:节点







 是从 




















 矩阵里取表示向量,模型会更新  矩阵;

class DBLPDataLoader:def __init__(self, graph_file):self.g = nx.read_gpickle(graph_file)self.num_of_nodes = self.g.number_of_nodes()self.num_of_edges = self.g.number_of_edges()self.edges_raw = self.g.edges(data=True)self.nodes_raw = self.g.nodes(data=True)self.edge_distribution = np.array([attr['weight'] for _, _, attr in self.edges_raw], dtype=np.float32)self.edge_distribution /= np.sum(self.edge_distribution)self.edge_sampling = AliasSampling(prob=self.edge_distribution)self.node_negative_distribution = np.power(np.array([self.g.degree(node, weight='weight') for node, _ in self.nodes_raw], dtype=np.float32), 0.75)self.node_negative_distribution /= np.sum(self.node_negative_distribution)self.node_sampling = AliasSampling(prob=self.node_negative_distribution)self.node_index = {}self.node_index_reversed = {}for index, (node, _) in enumerate(self.nodes_raw):self.node_index[node] = indexself.node_index_reversed[index] = nodeself.edges = [(self.node_index[u], self.node_index[v]) for u, v, _ in self.edges_raw]def fetch_batch(self, batch_size=16, K=10, edge_sampling='atlas', node_sampling='atlas'):if edge_sampling == 'numpy':edge_batch_index = np.random.choice(self.num_of_edges, size=batch_size, p=self.edge_distribution)elif edge_sampling == 'atlas':edge_batch_index = self.edge_sampling.sampling(batch_size)elif edge_sampling == 'uniform':edge_batch_index = np.random.randint(0, self.num_of_edges, size=batch_size)u_i = []u_j = []label = []for edge_index in edge_batch_index:edge = self.edges[edge_index]if self.g.__class__ == nx.Graph:if np.random.rand() > 0.5:      # important: second-order proximity is for directed edgeedge = (edge[1], edge[0])u_i.append(edge[0])u_j.append(edge[1])label.append(1)for i in range(K):while True:if node_sampling == 'numpy':negative_node = np.random.choice(self.num_of_nodes, p=self.node_negative_distribution)elif node_sampling == 'atlas':negative_node = self.node_sampling.sampling()elif node_sampling == 'uniform':negative_node = np.random.randint(0, self.num_of_nodes)if not self.g.has_edge(self.node_index_reversed[negative_node], self.node_index_reversed[edge[0]]):breaku_i.append(edge[0])u_j.append(negative_node)label.append(-1)return u_i, u_j, labeldef embedding_mapping(self, embedding):return {node: embedding[self.node_index[node]] for node, _ in self.nodes_raw}

上述代码中的图已假定边权重均为 1,相当于已经对带权重的边进行了拆解了多条二元边;首先进行 边采样,然后再对节点进行了负采样,负采样得到











 的








 均为-1。

个人总结

  • 本论文刚刚读起来,有点晦涩难懂,总觉得有些地方不严谨。但是仔细推敲下,貌似没啥大问题

  • 论文读起来感觉很复杂的样子,但是代码真的挺简单的。

参考资料

[1]

论文: http://www.www2015.it/documents/proceedings/proceedings/p1067.pdf

[2]

code: https://github.com/snowkylin/line

[3]

alia-method: %28https://blog.csdn.net/haolexiao/article/details/65157026%29

关于本站

“机器学习初学者”公众号由是黄海广博士创建,黄博个人知乎粉丝23000+,github排名全球前100名(33000+)。本公众号致力于人工智能方向的科普性文章,为初学者提供学习路线和基础资料。原创作品有:吴恩达机器学习个人笔记、吴恩达深度学习笔记等。

往期精彩回顾

  • 那些年做的学术公益-你不是一个人在战斗

  • 适合初学者入门人工智能的路线及资料下载

  • 吴恩达机器学习课程笔记及资源(github标星12000+,提供百度云镜像)

  • 吴恩达深度学习笔记及视频等资源(github标星8500+,提供百度云镜像)

  • 《统计学习方法》的python代码实现(github标星7200+)

  • 机器学习的数学精华(在线阅读版)

备注:加入本站微信群或者qq群,请回复“加群

加入知识星球(4300+用户,ID:92416895),请回复“知识星球

员外带你读论文:LINE: Large-scale Information Network Embedding相关推荐

  1. 员外带你读论文:SeqGAN论文分享

    本次要分享和总结的论文为:,其论文链接SeqGAN,源自 ,参考的实现代码链接代码实现. 本篇论文结合了  和  的知识,整篇论文读下来难度较大,在这里就浅薄的谈下自己的见解. 好了,老规矩,带着代码 ...

  2. 员外带你读论文:From RankNet to LambdaRank to LambdaMART: An Overview

    严格来说,这并不是一篇论文,只是一个  ,里面系统的介绍了三个比较著名的排序模型 ,链接 Rank[1] 本篇博文将分析总结下这三个排序模型.其参考的代码RankNet.LambdaRank[2],L ...

  3. Paper之BigGAN:ICLR 2019最新论文《LARGE SCALE GAN TRAINING FOR HIGH FIDELITY NATURAL IMAGE SYNTHESIS》(未完待续)

    Paper之BigGAN:ICLR 2019最新论文<LARGE SCALE GAN TRAINING FOR HIGH FIDELITY NATURAL IMAGE SYNTHESIS> ...

  4. 搞科研,从好好读论文开始:沈向洋带你读论文了

    「或许你永远不知道你以前读过的书能在什么时候派上用场,但请保持阅读,因为阅读的过程也是在你大脑中建立认知的过程.」 对于科研人员来说,读论文是一种必修技能.去年,沈向洋博士曾在线上公开课<You ...

  5. 带你读论文系列之计算机视觉--GoogLeNet

    带你读论文系列之计算机视觉–GoogLeNet 0 闲谈 玩起手机,看着电视,所有的计划都被抛之脑后,此时的快乐是深夜不舍睡下的愧疚.我总是想着明天怎么,而有时不知珍惜当下:总想着那些离开的朋友,而对 ...

  6. 带你读论文系列之计算机视觉--DenseNet

    带你读论文系列之计算机视觉–DenseNet 情若能自控,我定会按捺住我那颗吃货的心. 闲谈 今天听了师兄申请博士的经验.第一是感觉历程很心累,压力也很大:二是成功后很喜悦:三是成果很重要,其次是关系 ...

  7. 【读论文】唐建LINE那篇论文:LINE: Large-scale Information Network Embedding

    最近的研究需要用到涉及到Network Embedding方面,而唐建15年的这篇论文还是非常的出名的,所以写一下.而且本文的二作三作都是我同学,大家都是大神啊. LINE: Large-scale ...

  8. 带你读论文系列之计算机视觉--SENet

    带你读论文系列之计算机视觉–SENet 闲谈 总有那么瞬间思念远方的故人.八月十五中秋节,让我们放下繁忙工作,回家与老人团圆举杯共餐.这是我第一次没有在家过中秋,感觉也还行.现在节日没有什么节日气氛, ...

  9. 【论文阅读|深读】LINE: Large-scale Information Network Embedding

    目录 前言 ABSTRACT 1. INTRODUCTION 2. RELATED WORK 3. PROBLEM DEFINITION 4. LINE: LARGE-SCALE INFORMATIO ...

最新文章

  1. pip install Read timed out 超时问题解决
  2. 网口扫盲二:Mac与Phy组成原理的简单分析
  3. Spark源码分析 – DAGScheduler
  4. ConcurrentHashMap源码解析(2)
  5. Python学习笔记:TypeError: not all arguments converted during string formatting
  6. 在TCP客户端连接成功的回调函数里,无法访问到客户端套接字的明细
  7. 数据装载 计算执行脚本总耗时_shell源码_01
  8. JavaScript选择器
  9. C语言程序设计第二次作业1
  10. 字符串中索引位置是什么意思_女孩子左手中指戴戒指什么意思 不同位置各有不同...
  11. 热力地图高德_高德地图热力图和设备监测
  12. rose oracle双机切换故障,oracle 审计引起的问题 (双机软件roseha)
  13. CFA2019notes的PDF版本,热乎乎的百度网盘分享
  14. matlab红色爱心,Matlab心形函数动态图
  15. 全国三级城市联动 js版
  16. js中submit失效
  17. 网易云音乐外链(PHP的curl函数)
  18. 基于MI的cfc(交叉频率耦合)分析
  19. Spring Data JDBC、引用和聚合
  20. Python-mne库使用教程

热门文章

  1. Mininet 系列实验(一)
  2. BZOJ 3195: [Jxoi2012]奇怪的道路 | 状压DP
  3. 《设计师要懂心理学》-第五章-人如何集中注意力
  4. redis value多大会影响性能_事务对MySQL性能有什么影响?有无索引查找对其影响有多大?...
  5. JavaSE(十)——set和map集合、异常、File类
  6. C++之全局对象、局部对象、静态对象详解
  7. java音乐播放器文库_android音乐播放器开发教程
  8. document引用图片的src属性能干嘛_如何实现图片懒加载
  9. 变量four赋值数字python_跟老齐学Python之赋值,简单也不简单
  10. signature=6a8815f5009aacac86e725bea54f840f,A wave packet signature for complex networks