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_similarityedge_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 RandomForestClassifierdefbuild_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 更像一把现场螺丝刀。
不负责造楼,但你排查用户关系、设备团伙、论文网络、函数调用依赖、交易结构相似度的时候,它能很快帮你拧开第一颗螺丝。