Networkx永远无法计算200万个节点的Betweenness centrality

16
我有一个包含大约200万个节点和500万条边的简单Twitter用户图。我正在尝试使用Centrality进行实验。但是,计算时间非常长(超过一小时)。我认为我的图不是特别大,所以猜测我的代码可能有问题。
以下是我的代码。
%matplotlib inline
import pymongo
import networkx as nx
import time
import itertools

from multiprocessing import Pool
from pymongo import MongoClient

from sweepy.get_config import get_config

config = get_config()

MONGO_URL = config.get('MONGO_URL')
MONGO_PORT = config.get('MONGO_PORT')
MONGO_USERNAME = config.get('MONGO_USERNAME')
MONGO_PASSWORD = config.get('MONGO_PASSWORD')

client = MongoClient(MONGO_URL, int(MONGO_PORT))

db = client.tweets
db.authenticate(MONGO_USERNAME, MONGO_PASSWORD)

users = db.users
graph  = nx.DiGraph()


for user in users.find():
    graph.add_node(user['id_str'])
    for friend_id in user['friends_ids']:
        if not friend_id in graph:
            graph.add_node(friend_id)
        graph.add_edge(user['id_str'], friend_id)

数据存储在MongoDB中。以下是数据示例。
{
    "_id" : ObjectId("55e1e425dd232e5962bdfbdf"),
    "id_str" : "246483486",
    ...
    "friends_ids" : [ 
         // a bunch of ids
    ]
}

我尝试使用并行的介数中心性来加速,但仍然非常缓慢。 https://networkx.github.io/documentation/latest/examples/advanced/parallel_betweenness.html

"""
Example of parallel implementation of betweenness centrality using the
multiprocessing module from Python Standard Library.

The function betweenness centrality accepts a bunch of nodes and computes
the contribution of those nodes to the betweenness centrality of the whole
network. Here we divide the network in chunks of nodes and we compute their
contribution to the betweenness centrality of the whole network.
"""
def chunks(l, n):
    """Divide a list of nodes `l` in `n` chunks"""
    l_c = iter(l)
    while 1:
        x = tuple(itertools.islice(l_c, n))
        if not x:
            return
        yield x


def _betmap(G_normalized_weight_sources_tuple):
    """Pool for multiprocess only accepts functions with one argument.
    This function uses a tuple as its only argument. We use a named tuple for
    python 3 compatibility, and then unpack it when we send it to
    `betweenness_centrality_source`
    """
    return nx.betweenness_centrality_source(*G_normalized_weight_sources_tuple)


def betweenness_centrality_parallel(G, processes=None):
    """Parallel betweenness centrality  function"""
    p = Pool(processes=processes)
    node_divisor = len(p._pool)*4
    node_chunks = list(chunks(G.nodes(), int(G.order()/node_divisor)))
    num_chunks = len(node_chunks)
    bt_sc = p.map(_betmap,
                  zip([G]*num_chunks,
                      [True]*num_chunks,
                      [None]*num_chunks,
                      node_chunks))

    # Reduce the partial solutions
    bt_c = bt_sc[0]
    for bt in bt_sc[1:]:
        for n in bt:
            bt_c[n] += bt[n]
    return bt_c



print("Computing betweenness centrality for:")
print(nx.info(graph))
start = time.time()
bt = betweenness_centrality_parallel(graph, 2)
print("\t\tTime: %.4F" % (time.time()-start))
print("\t\tBetweenness centrality for node 0: %.5f" % (bt[0]))

Mongodb到networkx的导入过程相对较快,少于一分钟。


2
你可以尝试使用igraph...总的来说,它与networkx相比不容易操作,但是在处理大型图形时速度更快,而且占用的内存更少... - Corley Brigman
您可能还想了解计算的 GPU 加速,可以使用通常用于优化机器学习的库之一,例如用于神经网络计算的库。 - devinbost
1个回答

33

总之:介数中心性计算非常缓慢,因此您可能希望通过考虑myk节点的子集来使用近似度量,其中myk是远小于网络中节点数量但足够具有统计意义的某个数字(NetworkX 有一个选项可用于此:betweenness_centrality(G,k=myk))。


我一点也不惊讶它需要很长时间。介数中心性是一个缓慢的计算过程。Networkx使用的算法是O(VE),其中V是顶点数,E是边数。在你的情况下,VE = 10^13。我预计导入图形需要O(V+E)的时间,所以如果这需要足够长的时间,你可以看出它不是瞬间完成的,那么O(VE)将会很痛苦。

如果一个只有1%节点和1%边的简化网络(因此有20,000个节点和50,000条边)需要时间X,那么你想要的计算将需要10000X的时间。如果X是一秒钟,那么新的计算接近3小时,我认为这非常乐观(请参见下面的测试)。因此,在你决定你的代码有问题之前,在一些较小的网络上运行它,并估计你的网络应该运行多长时间。

一个好的替代方法是使用近似度量。标准的介数中心度量考虑每对节点和它们之间的路径。Networkx提供了一种替代方法,它使用仅有k个节点的随机样本,然后找到这些k个节点与网络中所有其他节点之间的最短路径。我认为这应该可以加速运行时间为O(kE)
所以你要使用的是:
betweenness_centrality(G, k=k)

如果你想要对结果的准确性有一定的限制,你可以使用较小的k值进行多次调用,确保它们相对接近,然后取平均结果。


这是我对运行时间进行的一些快速测试,使用了随机图形的 (V,E)=(20,50); (200,500); 和 (2000,5000)

import time
for n in [20,200,2000]:
    G=nx.fast_gnp_random_graph(n, 5./n)
    start_time = time.time()
    a=nx.betweenness_centrality(G)
    print time.time()-start_time

>0.00247192382812
>0.133368968964
>15.5196769238

在我的电脑上,处理一个比你的网络小0.1%需要15秒。要处理与您的网络相同大小的网络,需要大约1500万秒。这是1.5 * 10 ^ 7秒,略低于pi * 10 ^ 7秒的一半。由于pi * 10 ^ 7秒是一年秒数的非常好的近似值,因此我的计算机需要大约6个月。

因此,您需要使用近似算法运行。


你知道在Python中用于有效逼近贝塔分布的任何库吗? - Iman Akbari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接