受苏神的《最小熵原理(五):“层层递进”之社区发现与聚类》启发,拿来做词聚类,看苏神的贴出来的效果蛮好,就上手试了试,感觉确实不错。

最新的v1.0版本还有专门网站:https://mapequation.github.io/infomap/


文章目录

  • 1 简单的理论
  • 2 Benchmark
  • 3 安装
    • 3.1 v0.x版本
    • 3.2 v1.0版本
  • 4 基于infomap的词聚类
    • 4.1 v0.x版本
    • 4.2 v1.0版本
  • 5 v1.0版本其他的一些尝试
    • 5.1 Infomap + NetworkX 画图
    • 5.2 v1.0版本分层infoMap——Multilayer
      • 5.2.1 infomap直接初始化
      • 5.2.2 network初始化

1 简单的理论

Infomap 的双层编码方式把群组识别(社区发现)同信息编码联系到了一起。一个好的群组划分,可以带来更短的编码。所以,如果能量化编码长度,找到使得长度最短的群组划分,那就找到了一个好的群组划分。

Infomap 在具体做法上,为了区分随机游走从一个群组进入到了另一个群组,除了群组的名字之外,对于每个群组的跳出动作也给予了一个编码。比如,下图(c)中红色节点部分是一个群组,群组名的编码是 111,跳出编码是 0001。这样在描述某个群组内部的一段随机游走路径的时候,总是以群组名的编码开头,以跳出编码结束。

总结一下,Infomap 算法的大体步骤如下(看起来跟 Louvain 有些许类似):

(1)初始化,对每个节点都视作独立的群组;
(2)对图里的节点随机采样出一个序列,按顺序依次尝试将每个节点赋给邻居节点所在的社区,取平均比特
下降最大时的社区赋给该节点,如果没有下降,该节点的社区不变;
(3)重复直到步骤 2 直到 L(M)不再能被优化。


2 Benchmark

参考:Source code for multilevel community detection with Infomap

该聚类方法刚好可以顺着词向量做一些词间发现,相比Kmeans之类的效果确实好不少。相比其他network 方法(Louvain)实验结果也要好一些,来看一下对比:

  • 速度:运行时长

  • 精确度:精度以输出群集和参考群集之间的标准化互信息(NMI)进行衡量。基准网络由5000个节点组成,社区规模在20到200之间。

  • 分层精度:该图显示了该算法很好地揭示了不同级别的三角网络中节点的层次结构(请参见下图)。


3 安装

苏神v0.x的结果和v1.0的结果有一些差异的。v0.x要比v1.0多出很多算法,而v1.0只有最简单的一种。
而且,github上挂的example都是v0.x的版本,所以如果照着example好像还得切换回去。

3.1 v0.x版本

苏神博客中所述:

wget -c https://github.com/mapequation/infomap/archive/6ab17f8b18a6fdf34b2a53454f79a3b976a49201.zip
unzip 6ab17f8b18a6fdf34b2a53454f79a3b976a49201.zip
cd infomap-6ab17f8b18a6fdf34b2a53454f79a3b976a49201
cd examples/python
make# 编译完之后,当前目录下就会有一个infomap文件夹,就是编译好的模块;
# 为了方便调用,可以复制到python的模块文件夹(每台电脑的路径可能不一样)中
python example-simple.py
cp infomap /home/you/you/python/lib/python2.7/site-packages -rf

笔者电脑安装的时候,还要安装一个apt-get install swig

3.2 v1.0版本

pip install infomap

4 基于infomap的词聚类

两个版本中,

  • from infomap import infomap是v0.x版本,
  • import infomap是v1.0版本

其中,还有一些差异:
v0.x版本还有:

  • node.physIndex - v0.x版本的词编号
  • node.moduleIndex - v0.x版本的聚类编号
  • infomapWrapper = infomap.MemInfomap("--two-level") 这个好像是v0.x中特有的算法(Memory networks
  • tree.leafIter()- 树状结构
  • infomapWrapper.addTrigram(3, 2, 3),v1.0没有这种形态,Trigrams represents a path from node A through B to C.

v1.0版本还有:

  • node.physicalId - v1.0版本的词编号
  • node.moduleIndex() - v1.0版本的聚类编号
  • myInfomap.iterTree() - 树状结构
  • network = myInfomap.network() 好像是v1.0独有的算法模块
  • 1.0不能够使用 --overlapping这样的命令,一用就卡掉。。。

两者类似的是:

- tree.numTopModules() - 聚类之后的总数,2365个聚类
- tree.codelength() - 每个聚类中平均有多少个词
- addLink(self, n1, n2, weight=1.0) - _infomap.Infomap_addLink(self, n1, n2, weight),可以[点1,点2,权重]

4.1 v0.x版本

直接看苏神的代码即可,跟Word2Vec配合,跑一个词聚类的例子,代码位于:
https://github.com/bojone/infomap/blob/master/word_cluster.py

其中相关的代码为:

from infomap import infomapinfomapWrapper = infomap.Infomap("--two-level --directed")
# 如果重叠社区发现,则只需要:
# infomapWrapper = infomap.Infomap("--two-level --directed --overlapping")for (i, j), sim in tqdm(links.items()):_ = infomapWrapper.addLink(i, j, sim)infomapWrapper.run()
tree = infomapWrapper.treeword2class = {}
class2word = {}
for node in tree.leafIter():if id2word[node.physIndex] not in word2class:word2class[id2word[node.physIndex]] = []word2class[id2word[node.physIndex]].append(node.moduleIndex())if node.moduleIndex() not in class2word:class2word[node.moduleIndex()] = []class2word[node.moduleIndex()].append(id2word[node.physIndex])

infomap.Infomap初始化,关于这些指令,可以在Options中找到:
– two-level:两阶段网络,Optimize a two-level partition of the network.
– 对应的
– directed :有向
–overlapping:Let nodes be part of different and overlapping modules. Applies to ordinary networks by first representing the memoryless dynamics with memory nodes.
–undirected:无向
–expanded:打印记忆网络的节点,Print the expanded network of memory nodes if possible.
–silent:No output on the console,命令中不显示结果

输出结果为:

4.2 v1.0版本

官方的小案例(参考:https://mapequation.github.io/infomap/):

import infomap# Command line flags can be added as a string to Infomap
infomapSimple = infomap.Infomap("--two-level --directed")# Access the default network to add links programmatically
network = myInfomap.network()# Add weight as optional third argument
network.addLink(0, 1)
network.addLink(0, 2)
network.addLink(0, 3)
network.addLink(1, 0)
network.addLink(1, 2)
network.addLink(2, 1)
network.addLink(2, 0)
network.addLink(3, 0)
network.addLink(3, 4)
network.addLink(3, 5)
network.addLink(4, 3)
network.addLink(4, 5)
network.addLink(5, 4)
network.addLink(5, 3)# Run the Infomap search algorithm to find optimal modules
myInfomap.run()print("Found {} modules with codelength: {}".format(myInfomap.numTopModules(), myInfomap.codelength()))print("Result")
print("\n#node module")
for node in myInfomap.iterTree():if node.isLeaf():print("{} {}".format(node.physicalId, node.moduleIndex()))

来看一下v1.0版本,跟v0.x版本还不太一样呢。

#import uniout
import numpy as np
from gensim.models import Word2Vec
from tqdm import tqdm
#from infomap import infomap # v0.x
import infomap # v1.0num_words = 10000 # 只保留前10000个词
min_sim = 0.6word2vec = Word2Vec.load('baike_word2vec/word2vec_baike')word_vecs = word2vec.wv.syn0[:num_words]
word_vecs /= (word_vecs**2).sum(axis=1, keepdims=True)**0.5
id2word = word2vec.wv.index2word[:num_words]
word2id = {j: i for i, j in enumerate(id2word)}# 构造[wordA,wordB,相似性]
links = {}# 每个词找与它相似度不小于0.6的词(不超过50个),来作为图上的边
for i in tqdm(range(num_words)):sims = np.dot(word_vecs, word_vecs[i])idxs = sims.argsort()[::-1][1:]for j in idxs[:50]:if sims[j] >= min_sim:links[(i, j)] = float(sims[j])else:break# 方式一(infomap模型初始化):Infomap直接addLinkinfomapWrapper = infomap.Infomap("--two-level --directed")
#infomapWrapper = infomap.Infomap("--two-level")
# 如果重叠社区发现,则只需要:
# infomapWrapper = infomap.Infomap("--two-level --directed --overlapping")for (i, j), sim in tqdm(links.items()):#print(i, j,sim)_ = infomapWrapper.addLink(int(i), int(j),sim)# 方式二(infomap模型初始化):network 添加addLink
infomapWrapper = infomap.Infomap("--two-level --directed")
network = infomapWrapper.network()
for (i, j), sim in tqdm(links.items()):network.addLink(int(i), int(j),sim)# 聚类运算
infomapWrapper.run()# 有多少聚类数
print("Found {} modules with codelength: {}".format(infomapWrapper.numTopModules(), infomapWrapper.codelength()))# 聚类结果显示
word2class = {}
class2word = {}
# for node in tree.leafIter():
#     if id2word[node.physIndex] not in word2class:
#         word2class[id2word[node.physIndex]] = []
#     word2class[id2word[node.physIndex]].append(node.moduleIndex())#     if node.moduleIndex() not in class2word:
#         class2word[node.moduleIndex()] = []
#     class2word[node.moduleIndex()].append(id2word[node.physIndex])
for node in tree.iterTree():if id2word[node.physicalId] not in word2class:word2class[id2word[node.physicalId]] = []  # node.physicalId 词的编号word2class[id2word[node.physicalId]].append(node.moduleIndex())  # node.moduleIndex() 聚类的编号if node.moduleIndex() not in class2word:class2word[node.moduleIndex()] = []class2word[node.moduleIndex()].append(id2word[node.physicalId])for i in range(100):print('---------------')print (class2word[i][1:])

在infomap设置的时候,v1.0还有一种network的方式。

最后输出的结果,如果是Infomap直接addLink:

如果是network 添加addLink(感觉上,使用network要好一些):


5 v1.0版本其他的一些尝试

因为v1.0版本安装非常简单,如果作者会持续优化的情况下,虽然还不如v0.x算法全,但这个版本应该比较更好(PS:v1.0的教程太少了。。)

5.1 Infomap + NetworkX 画图

这个改编自官方example一个案例,不过不知道笔者有没有写对。。。
最终效果,不如之前的 版本。

import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.colors as colors
%matplotlib inlinedef findCommunities(G):"""Partition network with the Infomap algorithm.Annotates nodes with 'community' id and return number of communities found."""infomapWrapper = infomap.Infomap("--two-level --silent")print("Building Infomap network from a NetworkX graph...")for e in G.edges():infomapWrapper.addLink(*e)print("Find communities with Infomap...")infomapWrapper.run();tree = infomapWrapperprint("Found %d modules with codelength: %f" % (tree.numTopModules(), tree.codelength()))communities = {}#for node in tree.leafIter():for node in tree.iterTree():#communities[node.originalLeafIndex] = node.moduleIndex()communities[node.physicalId] = node.moduleIndex()nx.set_node_attributes(G, name='community', values=communities)return tree.numTopModules()def drawNetwork(G):# position mappos = nx.spring_layout(G)# community idscommunities = [v for k,v in nx.get_node_attributes(G, 'community').items()]numCommunities = max(communities) + 1# color map from http://colorbrewer2.org/cmapLight = colors.ListedColormap(['#a6cee3', '#b2df8a', '#fb9a99', '#fdbf6f', '#cab2d6'], 'indexed', numCommunities)cmapDark = colors.ListedColormap(['#1f78b4', '#33a02c', '#e31a1c', '#ff7f00', '#6a3d9a'], 'indexed', numCommunities)# Draw edgesnx.draw_networkx_edges(G, pos)# Draw nodesnodeCollection = nx.draw_networkx_nodes(G,pos = pos,node_color = communities,cmap = cmapLight)# Set node border color to the darker shadedarkColors = [cmapDark(v) for v in communities]nodeCollection.set_edgecolor(darkColors)# Draw node labelsfor n in G.nodes():plt.annotate(n,xy = pos[n],textcoords = 'offset points',horizontalalignment = 'center',verticalalignment = 'center',xytext = [0, 0],color = cmapDark(communities[n]))plt.axis('off')# plt.savefig("karate.png")plt.show()G=nx.karate_club_graph()findCommunities(G)drawNetwork(G)

最终输出:

Building Infomap network from a NetworkX graph...
Find communities with Infomap...
Found 3 modules with codelength: 4.311793

其中编号为0的点有错误,笔者也没深究。。


5.2 v1.0版本分层infoMap——Multilayer

分层指的是节点本身是有层次关系的,现在很多知识图谱本来就有非常多的等级。

从实验来看,初始化状态infomap和network,应该是没差别的。

5.2.1 infomap直接初始化

import infomapinfomapWrapper = infomap.Infomap("--two-level --directed")# from (layer, node) to (layer, node) weight
# infomapWrapper.addMultiplexLink(2, 1, 1, 2, 1.0)
# infomapWrapper.addMultiplexLink(1, 2, 2, 1, 1.0)
# infomapWrapper.addMultiplexLink(3, 2, 2, 3, 1.0)# from (layer, node) to (layer, node) weight
infomapWrapper.addMultilayerLink(2, 1, 1, 2, 1.0)
infomapWrapper.addMultilayerLink(1, 2, 2, 1, 1.0)
infomapWrapper.addMultilayerLink(3, 2, 2, 3, 1.0)infomapWrapper.run()tree = infomapWrapperprint("Found %d modules with codelength: %f" % (tree.numTopModules(), tree.codelength()))for node in tree.iterTree():print(node.stateId,node.physicalId,node.moduleIndex(),node.path(),node.data.flow,node.data.enterFlow,node.data.exitFlow)

输出:

Found 2 modules with codelength: 0.930233
0 0 0 () 0.9999999999999998 0.0 0.0
0 0 0 (0,) 0.9302325581395346 0.0 0.0
0 1 0 (0, 0) 0.4651162790697673 0.4651162790697673 0.4651162790697673
1 2 0 (0, 1) 0.4651162790697673 0.4651162790697673 0.4651162790697673
0 0 1 (1,) 0.06976744186046516 0.0 0.0
2 2 1 (1, 0) 0.0 0.0 0.06976744186046516
3 3 1 (1, 1) 0.06976744186046516 0.06976744186046516 0.0

5.2.2 network初始化

import infomapinfomapWrapper = infomap.Infomap("--two-level --directed")# from (layer, node) to (layer, node) weight
# infomapWrapper.addMultiplexLink(2, 1, 1, 2, 1.0)
# infomapWrapper.addMultiplexLink(1, 2, 2, 1, 1.0)
# infomapWrapper.addMultiplexLink(3, 2, 2, 3, 1.0)network = infomapWrapper.network() # from (layer, node) to (layer, node) weight
network.addMultilayerLink(2, 1, 1, 2, 1.0)
network.addMultilayerLink(1, 2, 2, 1, 1.0)
network.addMultilayerLink(3, 2, 2, 3, 1.0)infomapWrapper.run()tree = infomapWrapperprint("Found %d modules with codelength: %f" % (tree.numTopModules(), tree.codelength()))for node in tree.iterTree():print(node.stateId,node.physicalId,node.moduleIndex(),node.path(),node.data.flow,node.data.enterFlow,node.data.exitFlow)

输出:

Found 2 modules with codelength: 0.930233
0 0 0 () 0.9999999999999998 0.0 0.0
0 0 0 (0,) 0.9302325581395346 0.0 0.0
0 1 0 (0, 0) 0.4651162790697673 0.4651162790697673 0.4651162790697673
1 2 0 (0, 1) 0.4651162790697673 0.4651162790697673 0.4651162790697673
0 0 1 (1,) 0.06976744186046516 0.0 0.0
2 2 1 (1, 0) 0.0 0.0 0.06976744186046516
3 3 1 (1, 1) 0.06976744186046516 0.06976744186046516 0.0

其中,node.stateId 在一般的网络之中就等于node.physicalId,在分层网络addMultilayerLink中两者 才有差异。

stateId The state node id, equals physicalId for ordinary networksReturns
-------
unsigned intThe state node id

其中,node.depth()是节点当前的深度。

The current depth from the start node in the iteratorReturns
-------
unsigned intThe current depthnode.depth()

其中,addMultilayerLink 包括:network.addMultilayerLink(layer1, n1, layer2, n2, weight)
其中,node.data,其中的这个flow与编码相关

"""
The flow data of the node that defines:
node.data.flow
node.data.enterFlow
node.data.exitFlowReturns
-------
FlowDataThe flow data"""
node.data

参考:

1 机器学习-社区发现算法介绍(一):Infomap
Source code for multilevel community detection with Infomap
3 Multi-level network clustering based on the Map Equation
4 最小熵原理(五):“层层递进”之社区发现与聚类
mapequation/infomap

聚类 | Map-Equation多级网络聚类模型——InfoMap相关推荐

  1. r型聚类典型指标_聚类与RFM模型 —— 从5月的一道腾讯数据分析面试题说起

    作者:稀饭 本文约2200字,建议阅读12分钟. 5月份的时候曾经投过腾讯的数据分析实习,中午投的简历,午觉睡醒就被call,没有HR通知,南山必胜客直接就来技术面.当时准备的还不够充分,半小时后就感 ...

  2. 机器学习入门(九):非监督学习:5种聚类算法+2种评估模型

    机器学习入门专栏其他章节: 机器学习入门(一)线性回归 机器学习入门(二)KNN 机器学习入门(三)朴素贝叶斯 机器学习入门(四)决策树 机器学习入门(五)集成学习 机器学习入门(六)支持向量机 机器 ...

  3. matlab对手写数字聚类的方法_scikitlearn — 聚类

    可以使用模块sklearn.cluster对未标记的数据进行聚类.每个聚类算法都有两种变体:一个是类(class)实现的 fit方法来学习训练数据上的聚类:另一个是函数(function)实现,给定训 ...

  4. Python基于聚类算法实现密度聚类(DBSCAN)计算

    本文实例讲述了Python基于聚类算法实现密度聚类(DBSCAN)计算.分享给大家供大家参考,具体如下: 算法思想 基于密度的聚类算法从样本密度的角度考察样本之间的可连接性,并基于可连接样本不断扩展聚 ...

  5. k中心点聚类算法伪代码_聚类算法之——K-Means、Canopy、Mini Batch K-Means

    K-Means||算法 K-Means||算法是为了解决K-Means++算法缺点而产生的一种算法: 主要思路是改变每次遍历时候的取样规则,并非按照K-Means++算法每次遍历只获取一个样本,而是每 ...

  6. 核聚类与支持向量聚类

    核聚类与支持向量聚类 聚类是数据挖掘中用来发现数据分布和隐含模式的一项重要技术[1].作为一种常见的数据分析工具和无监督机器学习方法,聚类的目的是把数据集合分成若干类(或簇),使得每个类中的数据之间最 ...

  7. 现代分层、聚集聚类算法_分层聚类:聚集性和分裂性-解释

    现代分层.聚集聚类算法 Hierarchical clustering is a method of cluster analysis that is used to cluster similar ...

  8. python谱聚类算法_谱聚类Spectral clustering(SC)

    在之前的文章里,介绍了比较传统的K-Means聚类.Affinity Propagation(AP)聚类.比K-Means更快的Mini Batch K-Means聚类以及混合高斯模型Gaussian ...

  9. 层次聚类 matlab代码_聚类算法解析一

    01 概述 本次针对聚类算法进行讲解,因为内容较多,会分多篇文章进行讲解,主要的内容包括聚类算法的整体介绍,针对不同类别的聚类算法比如划分聚类.层次聚类.密度聚类等算法进行介绍,在讲解每类算法时会结合 ...

最新文章

  1. Sass函数:Sass Maps的函数-map-has-key($map,$key)
  2. 匿名函数、普通函数、箭头函数作用域
  3. 寻找数组中的第二大数
  4. linux驱动(七)gpiolib库详解
  5. opencv安装与python cv2安装
  6. Enum in C#
  7. Python练习:阶乘累计求和
  8. 如何使div的浏览器窗口高度为100%
  9. Spring4.x()---JdbcDaoSupport的使用
  10. Fundebug后端Java异常监控插件更新至0.3.1,修复Maven下载失败的问题
  11. 重磅!贾扬清明日发布新一代云原生数仓与数据湖产品 | 凌云时刻
  12. java算法大全_java经典算法_算法面试题大全含答案
  13. 批量合并工作簿,包含三种合并方式,Excel技能演示
  14. FINSTCP python2.5
  15. python分苹果问题_给大家分享一个「Python算法题」分苹果
  16. php判断合数,素数(质数)、合数计算器
  17. 雪豹学院主办“2019年《ASM敏捷大咖》见修系列公开课”深圳宝安站活动圆满结束
  18. Nginx平滑升级与自定义错误页面
  19. NLP模型笔记2022-11:CTB5和CTB8数据集预处理 【PennTreebank 和Chinese Treebank】
  20. 洛谷题单···(Python)

热门文章

  1. 第一讲:网络协议概述
  2. mysql参数积累 持续更新。。。
  3. input type=file美化
  4. Unity3D 获得GameObject组件的方法
  5. 弹窗样式 idialog,purebox,artdialog4.1.2,jquery.alert.v1.2
  6. 诺顿无法启动扫描,扫描引擎返回错误0x20000058错误
  7. patterns practices: Mobile Architecture Pocket Guide
  8. HttpClient4 TIME_WAIT和CLOSE_WAIT
  9. ElementUI-自定义模板包含编辑与删除的功能
  10. leetcode371