Python技术迷

karateclub,一个强大的 Python 库!

用户、设备、IP、手机号凑在一张边表里,SQL 查到最后就开始别扭。

你想查“这个用户和那个黑名单设备是不是沾边”,写三层 join 还能忍;你想找“这批用户里谁的关系结构很像”,SQL 基本就开始装死了。这种东西不要硬拧关系型表,它天然就是图。

karateclub 这个库,我一般不会拿它做线上实时服务,但做离线分析、异常团伙挖掘、图特征补充,挺顺手。它是基于 NetworkX 的无监督图学习库,官方文档里也写得很直接:它主要做 network embedding、community detection、graph mining 这些活。说白一点,就是把一张图里的节点、社区,或者整张图,压成机器学习能吃的向量。官方论文里提到它集成了 30 多种图挖掘算法。

先看一段我平时会怎么试。

import pandas as pd
import networkx as nx
from karateclub import Node2Vec
from sklearn.metrics.pairwise import cosine_similarity

edge_rows = [
    ("u_1001", "dev_a"),
    ("u_1001", "ip_7"),
    ("u_1002", "dev_a"),
    ("u_1002", "card_x"),
    ("u_2001", "dev_k"),
    ("u_2001", "ip_9"),
    ("u_2002", "dev_k"),
    ("u_2002", "card_z"),
    ("u_3001", "ip_7"),
]

df = pd.DataFrame(edge_rows, columns=["src", "dst"])

raw_graph = nx.from_pandas_edgelist(df, "src", "dst")

id_to_name = dict(enumerate(raw_graph.nodes()))
name_to_id = {v: k for k, v in id_to_name.items()}

g = nx.relabel_nodes(raw_graph, name_to_id)

model = Node2Vec(
    dimensions=32,
    walk_number=20,
    walk_length=30,
    workers=2,
    seed=17
)

model.fit(g)
emb = model.get_embedding()

defnode_sim(a, b):
    left = emb[name_to_id[a]].reshape(1, -1)
    right = emb[name_to_id[b]].reshape(1, -1)
return cosine_similarity(left, right)[0][0]

for a, b in [("u_1001", "u_1002"), ("u_1001", "u_2001"), ("u_2001", "u_2002")]:
    print(a, b, round(node_sim(a, b), 4))

这里有个坑,第一次用很容易撞上。

karateclub 对输入图的节点编号有要求:节点要用整数,从 0 开始,而且连续。这个要求在官方文档里写了。

所以我上面没有直接把 u_1001、dev_a 这种字符串节点丢进去,而是先做了一层映射。别嫌这几行麻烦,图算法里 ID 映射不老实做,后面报错会很脏。

Node2Vec 适合做节点相似度。比如风控里,一个用户本身没命中黑名单,但它跟一堆异常设备、异常 IP 走得很近,这种“近”不是一条 SQL 的 where 能表达清楚的。Node2Vec 会通过随机游走,把图上的上下文关系学进向量里。官方文档里 Node2Vec 的说明,也是基于 biased second order random walks 这一类思路。

再来一个我更喜欢的用法:图分类前先把整张图变成向量。

比如你有很多个小图,每个小图是一笔交易前后的关系网络。正常交易一张图,异常交易一张图。先不急着上 GNN,先用 Graph2Vec 打个底,经常能看到点东西。

import networkx as nx
from karateclub import Graph2Vec
from sklearn.ensemble import RandomForestClassifier

defbuild_trade_graph(edges):
    g = nx.Graph()
    g.add_edges_from(edges)
    g = nx.convert_node_labels_to_integers(g, first_label=0)
return g

graphs = [
    build_trade_graph([("u1", "ip1"), ("u1", "dev1"), ("dev1", "card1")]),
    build_trade_graph([("u2", "ip2"), ("u2", "dev2")]),
    build_trade_graph([("u9", "ip8"), ("u9", "dev8"), ("u8", "dev8"), ("u8", "card8")]),
    build_trade_graph([("u3", "ip3"), ("u3", "dev3")]),
]

labels = [1, 0, 1, 0]

g2v = Graph2Vec(dimensions=64, wl_iterations=2, epochs=40, seed=13)
g2v.fit(graphs)

x = g2v.get_embedding()

clf = RandomForestClassifier(
    n_estimators=80,
    max_depth=4,
    random_state=13
)
clf.fit(x, labels)

print(clf.predict_proba(x))

Graph2Vec 这个东西不是魔法。它适合你手里是一批图,而不是一张巨大的图。官方文档对 Graph2Vec 的描述里,提到它会先生成 Weisfeiler-Lehman tree features,再把图和特征的共现矩阵分解成图向量。

我比较建议这样用 karateclub:先在样本集上跑一版 embedding,看相似度、聚类、分类特征有没有增益。有效,再考虑怎么放进离线链路。不要一上来就想着塞进生产接口。

还有一个现实问题要讲。

PyPI 上 karateclub 当前显示的最新版本是 1.3.3,发布时间是 2022 年 10 月 22 日。 这意味着什么?不是说它不能用,而是你装环境时别太豪横。Python、gensim、networkx、scipy 这些依赖版本稍微新一点,就可能给你甩脸色。

我一般会单独建环境:

python -m venv .venv-graph
source .venv-graph/bin/activate
pip install karateclub pandas scikit-learn
pip freeze | grep -E "karateclub|networkx|gensim|scipy"

真要进项目,不要直接塞到主环境里。图挖掘这种东西依赖重,版本冲突也常见。隔离环境,先把结果文件吐出来,比如:

out = pd.DataFrame(emb)
out.insert(0, "node_name", [id_to_name[i] for i in range(len(id_to_name))])
out.to_parquet("graph_node_embedding.parquet", index=False)

后面的业务系统只读这个 parquet,当普通特征用就行。别让线上接口陪图算法一起承担依赖风险,这事我不太信。

karateclub 强的地方,是它把很多图挖掘算法包成了很统一的接口:fit(),然后 get_embedding() 或者拿社区结果。小规模实验非常快,尤其适合那种“我怀疑关系网络里有东西,但还不知道东西在哪里”的场景。

但它不是大图工业引擎。千万级节点、亿级边,别硬塞 NetworkX。这个时候应该换 Spark GraphX、Neo4j GDS、PyG、DGL 这类更合适的东西。

karateclub 更像一把现场螺丝刀。

不负责造楼,但你排查用户关系、设备团伙、论文网络、函数调用依赖、交易结构相似度的时候,它能很快帮你拧开第一颗螺丝。