lvlv神奇的算法简介

参考知乎: https://zhuanlan.*/p/556291759

1. 社区发现简介

1.1 社区是什么

        用户以及相互作用关系构成了一个大的关系网络,在网络中会存在一些紧密连接的区域,可以被看成一个社区(这些区域(节点集)通常有自己的属性),社区内部连接紧密,而社区之间连接则相对稀疏(内紧外松)。“社群检测”等同于“给节点分组”。

1.2 社区发现的目标和意图

        社区发现(community detection),也叫团伙挖掘,是风控工作中的核心算法。其目的是在图中找到一些“潜在的有特定关系的组织”,即探测网络中较为紧密的【块cluster】或是【社团】。

     在风控中,很多有目的的团伙聚集在一起,有明显的社群关系。哪个块是高风险,哪个块是高价值,非常值得挖掘。在团伙挖掘的过程中,介质的质量、边的构建思路、以及业务的抽象能力,比什么算法都重要,盲目的在质量不高的数据上,啥算法都没用。

节点间存在连接的抽象本质 - 逻辑拓朴结构: 

        社区的节点间是存在(网络)拓朴连接结构的。注意不能将其和欧式空间或者P空间中的点向量集合空间混为一谈。在社区结构中,节点之间没有什么空间位置的概念。相对的,节点间存在的是一种逻辑拓朴结构,即存在一种共有关系。存在共有关系的节点在逻辑上会聚集为一个社区,而社区之间不存在或者存在很弱的共有关系,则呈现分离的逻辑拓朴结构。

      即使是欧式距离很近的向量点,也不一定就代表这它们之间存在拓朴连接关系,只能说在一定的度量下,这两个节点很相近。注意不要用空间结构的概念来试图理解社区结构,社区中的节点只是因为逻辑上的共有关系而聚集在一起而已,彼此之间的位置也没有实际意义。

一些实际的例子:

1. 假如节点代表消费者:节点间的连接代表了它们共同购买了一批书籍,权重代表共同购买的书籍数,连接越紧密,代表社群的爱好越相近;

2. 假如节点代表消费者:节点间的连接代表了它们共同领取一批优惠券,权重代表共同领取的优惠券的数量,该社群可能是一个羊毛党社群。

3. 假如节点代表商家:节点间的连接代表了它们共同商品标题,权重代表共同标题商品的数量,该社群可能是一个店群商家,不断换点进行欺诈。共同的地点、设备、轨迹、时间、证件等等,都可以作为这种逻辑关系的存在,作为一个网络,进行社区挖掘,达到业务目的。

1.3 什么时候可以使用社区发现算法

        需要先确定要解决的业务场景中,是否存在明显的聚集规律,节点(可以是抽象的)之间形成一定的族群结构,而不是呈现无规律的随机分散。另外这种聚集的结构是“有意义的”,即这种聚集本身可以翻译为一定的业务层面的表现。但很多时候,业务场景中的数据集之间的共有关系并不是表现的很明显,即节点之间或多或少都存在一些共有关系,这样直接进行社区发现效果肯定是不好的,就没必要进行社区发现挖掘。所以在进行社区发现之前,一定要进行数据降噪

      理想情况下,降噪后得到的数据集已经是社区完全内聚,社区间完全零连接,这样 louvain 只要一轮运行就直接得到结果。一般情况下,降噪只要能 cutoff 90%以上的噪音,通过几轮迭代基本就能完成整体的社区发现过程。

1.4 社区划分的直观思路

        同一团内的节点连接更紧密(即具有更大的 density),社区划分有很多不同的算法,如Fast Unfolding (Louvian) 只是其中一种,另外密度评估方法也只是其中一种思想。从算法的思想上看,个人倾向于认为社区发现算法比较适合发现一种"抽象泛共现模式",如:

1. 两个用户拥有共同的介质;

2. 两个用户昵称采用共同的起名方式;(如名字加数字,同样的前缀,都是四字成语等)

3. 用户拥有相同的行为,比如做过同样的操作;

4. 用户拥有共同的控制人;

5. 两个用户发布的内容拥有相似的词频数据;

5. 两个用户拥有相同的运动轨迹 ...

        社区发现的落地效果,很大程度上取决于对业务拓扑逻辑的抽象水平 和 数据降噪的结果,而不取决于发现的算法。并且不同场景,采用的社区发现算法不一样。很多常识在规模面前也不管用,甚至引起认知谬误,因此社区发现算法的结果不可能完美,必须结合业务实际情况进行调节和控制,再应用,不然很难达到预期效果,甚至搞出很多问题。

2. 相关概念

2.1 模块度

        “社群检测”等同于 给节点分组,而模块度(Modularity) 是一种常用的衡量节点分组质量的标准,模块度越高说明所检测到的社团越符合“内紧外松”的特征,分组质量越好。通常计算一个网络的模块度,需要构造一个具有相同节点度分布的随机网络作为参照。一个好的划分结果其表现形式是:在社区内部的节点相似度较高,而在社区外部节点的相似度较低。

      模块度用来衡量一个社区网络划分情况的好坏,取值:[-0.5,1),其物理含义是:在社区内实际的边数(节点的连边数)与随机情况下的边数之差。如果差距比较大,说明社团内部密集程度显著高于随机情况,社团划分的质量较好。如果节点组中的连边数量超过了随机分配时所得到的期望连边数量,模块度为正数。没有超过,则为负数。

      一般模块度取值在0.3~0.7之间就认为有明显的社区结构出现了,

Aij 是节点i 和节点j 之间边的权重,网络不是带权图时,所有边的权重可以看做是1;

ki 表示所有与节点i 相连的边的权重之和(度数);

ci表示节点i 所属的社区,i与j属于同一社区则为1;

m=\frac{1}{2}\sum_{ij}^{}A_{ij} 表示所有边的权重之和(边的数目);

\frac{k_{j}}{2m}  表示节点j 连接到任意一个节点的概率;

现在节点i 有ki 的度数,因此在随机情况下节点i 与j 的边为 k_{i}\frac{k_{j}}{2m}

      可简单理解为:固定节点数,给定图有多少条边,与用同样多节点,随机生成图拥有的边,相减,差越大说明给定图越紧密。模块度简化如下:

        其中Σin 表示社区c内的边的权重之和,Σtot 表示与社区c内的节点相连的边的权重之和。上面的公式还可以进一步简化为:

        这样模块度也可以理解为:

1. 权重图: 社区内部边的权重减去所有与社区节点相连的边的权重和;

2. 无权图:社区内部边的度数减去社区内节点的总度数。

        基于模块度的社区发现算法,都是以最大化模块度Q为目标。(模块度越大,挖掘的社区内部连接越紧密,效果越好)。

2.1.1 模块度最大化算法

       模块度最大值法(Modularity maximization)是一种经典的社团检测方法,其目标是从网络所有可能的节点分组中,找到使得模块度最大的分组方式。由于穷举所有可能的分组十分困难,所以实际的算法都采用近似优化方法。

      如 Mark Newman 提出了模块度最大化的贪婪算法 Fast NewMan (FN)。贪婪算法的原理是找出每个局部最优值,最终将局部最优值整合成整体的近似最优值。

2.1.2 怎么表示一个随机图

        一个好的社区一定是内部的连接要比随机连接情况下的连接更紧密。说到随机,那自然需要一个零模型(图模型),这里选择的是配置模型(configuration model),为了保证与原图有相同的度分布。类似与在介绍 motifs 时运用的模型,不同的是,这里允许有重边(multi edge)的 multigraph。

2.2 权重度

1. 节点权重度

        指与某个点有关(以该点为端点)的所有边的权重和,包括该点的邻边(连接至其它点)以及该点的自环边(连接至该点自身)。如下图,红色节点权重度为 1 + 0.5 + 3 + 1.5 = 6(有三条邻边和一条自环边)。

2. 社区权重度

        指一个社区内所有节点的权重度之和。

1 号社区权重度 = 红色节点的权重度 + 绿点权重度 + 蓝点权重度 + 黄点权重度  

                           = 6 + 2.7 +2.8 +3 = 14.5

3. 社区内部权重度

        仅考虑两个端点均在该社区内的边

1 号社区内部权重度 = 1 号社区权重度 - 该社区和其它社区之间的边的权重

                                   = 14.5 - (1.7 + 0.3 + 2) = 10.5

        注意: 社区内部权重度并不是两个端点均在社区内的边的权重和,而是这些边当中的非自环边的权重和的二倍再加上自环边的权重和 (自环的权重仅计算1次)。

1 号社区内部权重度 = (1 + 0.5 + 3) * 2 + 1.5 = 10.5

4. 全图权重度

        指图中所有节点的权重度的和。如果将全图划分为多个社区,由于图中每个点属于并且仅属于一个社区,全图权重度也等于这些社区的权重度的和。

全图权重度 = 1 号社区权重度 + 2号社区权重度为 + 3 号社区权重度为 

                    = 14.5 + 5.9 + 14.3 = 34.7

        如果将全图看成一个社区,那么全图权重度也可以理解为该社区的内部权重度。

全图权重度 =  (1 + 0.5 + 3 + 1.7 + 0.7 * 3 + 0.3 + 2 + 1 * 6) * 2 + 1.5 = 34.7

2.3 社区压缩

        Louvain 在图数据分析算法中虽然比较复杂的,但别具意义。社区压缩是将每个社区内的所有节点一个聚合点来表示,该社区的内部权重度即为此聚合点的自环边的权重,每两个社区间的边的权重和即为相应两个聚合点之间边的权重。

        对I、II、III三个社区进行压缩,

I 号社区压缩后的自环边的权重为该社区的内部权重,即 10.5;

II 号社区压缩后自环边的权重为 0.7 * 3 * 2 = 4.2,

III 号社区压缩后自环边的权重为 1 * 6 * 2 = 12。

        同时,I 号与 II号社区之间的边压缩后权重为 1.7,与 III 号社区之间的边压缩后权重为 0.3 + 2 = 2.3。

为什么要进行社区压缩?

1. 计算效率更高:进行社区压缩,在不改变局部权重度及全图权重度的前提下,通过最大限度减少图的点、边数量来提高后续(迭代)的计算速度;

2. 实现层级化的社区划分: 社区内的点在压缩后将作为一个整体进行模块度优化的计算,不再拆分,从而实现了层级化(迭代化)的社区划分效果。

2.4 算法结果处理

        经过 Louvain 计算后的社区,是一个多层次的结构,通常如下:

       那么选取哪个层次的社区结构呢。可以将手肘法作为辅助工具,当模块化指数Q或社区数量随着迭代次数增加出现明显拐点时,选取对应层次的社区结构就比较合理啦,比如下面这样的:

 3. 算法流程

        在社区发现算法中Louvain与infomap深受喜爱,思想:不择手段把图的模块化指数Q搞大。Louvain也就是个贪心算法

3.1 两步迭代设计

step1:节点合并

        将图中每个节点都看作一个独立社区,对每个节点i,依次尝试把节点i 分配到其每个邻居节点所在的社区,计算分配前后的模块度变化ΔQ ,并记录ΔQ 最大的那个邻居节点,如果 ΔQ >0,则把节点i 分配ΔQ 最大的那个邻居节点所在的社区,否则保持不变;

      算法扫描数据中的所有节点,针对每个节点遍历该节点的所有邻居节点,衡量把该节点加入其邻居节点所在的社区所带来的模块度的收益。并选择对应最大收益的邻居节点,加入其所在的社区。这一过程化重复进行直到每一个节点的社区归属都不在发生变化。

step2:社区聚合

        对上述形成的社区进行折叠,把每个社区折叠成一个单点,社区内节点之间的边的权重和转化为新节点的自环边的权重,社区间的边权重和转化为新节点间的边权重;重复step1 直到整个图的模块度不再发生变化(所有节点的所属社区不再变化),或算法已经达到了目标(比如最大的ΔQ小于某个值)。

3.2 时间复杂度

       该算法的最大优势就是速度很快,步骤1 的每次迭代的时间复杂度为O(N),N为输入数据中的边的数量。步骤2 的时间复杂度为O(M + N), M为本轮迭代中点的个数。louvain 基于贪心算法实现,实际数据中的平均复杂度为 O(nlog(n)),当每一轮迭代中节点数量降低一半时,能达到平均复杂度。

3.3 模块度增量计算

        该算法能够产生层次性的社区结构,其中计算耗时较多的是最底一层的社区划分,节点按社区压缩后,将大大缩小边和节点数目,并且计算节点i 分配到其邻居j 时,模块度的变化只与节点i、j的社区有关,与其他社区无关,因此计算很快(即只需要计算这个局部ΔQ(不用每次都重新计算全局的ΔQ))。把节点i 分配到邻居节点j 所在的社区c时模块度变化ΔQ为:

        这里好像是ΔQ少了把节点i 从其原来社区删除这一步?进一步化简:

        step2 不同节点的访问顺序会对分群结果有一定影响,但效果差距不大,只是会在一定程度上影响算法的时间效率,有论文指出按度数从大到小的顺序处理速度最快。

1. 初始时将网络中的每个节点都看成独立的小社团;

2. 考虑所有相连社团两两合并的情况,计算每种合并带来的模块度的增量;

3. 基于贪婪原则,选取使模块度增长最大的两个社团,将它们合并成一个社团;

4. 如此循环迭代,随着迭代的进行,模块度不断变化,其最大值时对应最优社团划分。

        后来Vincent Blondel等人运用模块度最大值法原理,提出了Louvain算法,大大降低了算法的时间复杂度。该算法十分适合社会网络等超大规模网络的社团检测,也成为应用最广的算法之一。

4. 算法应用

        Louvain 是基于模块度计算的社区识别算法,是以最大化模块度为目标的一种对顶点进行聚类的迭代过程。主要用于社交网虚假账号识别、消费群体划分及商品推荐、银行卡伪冒和欺诈团伙识别、银行理财产品、保险产品推荐、企业集团及家族企业识别等领域。

安装Louvain:pip3 install python-louvain

1. 自带图

import pandas as pd
import matplotlib.pyplot as plt
import networkx as nx
from community import community_louvain

G = nx.karate_club_graph()  # 空手道俱乐部
node_size = [G.degree(i)**1*20 for i in G.nodes()]  # 节点大小设置、度关联
communities = community_louvain.best_partition(G)
com_df = pd.DataFrame({'cid': communities.values(), 'uid': communities.keys()})  # 格式整理
# 统计每个团伙人数 并降序
df_com.groupby('cid').count().sort_values(by='uid', ascending=False)
print(com_df.head(100))
colors = ['DeepPink', 'orange', 'DarkCyan', '#A0CBE2', '#3CB371', 'b', 'orange',
          'y', 'c', '#838B8B', 'purple', 'olive', '#A0CBE2', '#4EEE94']*500
colors = [colors[i] for i in communities.values()]
plt.figure(figsize=(4, 3), dpi=500)
nx.draw_networkx(G, pos=nx.spring_layout(G),
                 node_color=colors, edge_color='#2E8B57', font_color='black',
                 node_size=node_size, font_size=5,
                 alpha=0.9, width=0.1, font_weight=0.9)
plt.axis('off')
plt.show()

如下:

2. 自定义图

import community
from community import community_louvain
G = nx.Graph()  # 创建一个无向图
# 添加节点(用户)
for i in range(1, 11):
    G.add_node(i)
# 添加边(连接关系)
G.add_edge(1, 2)
G.add_edge(1, 3)
G.add_edge(1, 4)
G.add_edge(2, 3)
G.add_edge(2, 4)
G.add_edge(3, 4)
G.add_edge(5, 6)
G.add_edge(5, 7)
G.add_edge(6, 7)
G.add_edge(8, 9)
G.add_edge(8, 10)
G.add_edge(9, 10)

# communities = community.best_partition(G)
communities = community_louvain.best_partition(G)
# {1: 2, 2: 2, 3: 2, 4: 2, 5: 1, 6: 1, 7: 1, 8: 0, 9: 0, 10: 0}  # 节点编号:社区编号
com_cnt = float(len(set(communities.values())))
# 绘制社区结构图
pos = nx.spring_layout(G)
for com_id in set(communities.values()):
    list_nodes = [nodes for nodes in communities.keys() if communities[nodes] == com_id]
    nx.draw_networkx_nodes(G, pos, list_nodes, node_size=20, node_color=f'C{com_id}')
nx.draw_networkx_edges(G, pos, alpha=0.5)
plt.show()

如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值