用户关系已经拉成边表了,结果下一步卡住了。
SQL具备查出 、、score的功能, sql能够实现进行绘图。一旦面临执行“相似用户召回、异常团伙识别以及以及为社区划分并执行判定”的任务, 就会察觉到仅仅依靠图是无法达成目标的。必须要将图转化成向量, 或者直接转化为社群标签。
这时候可以看一眼 。
它并非是那个具有典型性的空手道俱乐部数据集, 反而是一个图挖掘库。官方所给出的定位相当直接明白: 属于一种凭借相关基础而创建的无监督机器学习扩展范畴图书馆, 主要从事像是去实施图嵌入操作, 开展社区发现工作, 进行图挖掘这类具体事务。其应用程序编程接口也较为统一规整, 基本上就是把信息之类的内容 fit 进去, 然后从中以某种模式或方式取出来。
我对这种库一般不先看论文,先看能不能接住业务里的边表。
比如说吧, 用户相互之间存在着共同下单的情况, 还有着共同设备, 并且有着共同地址, 此前前面的规则已经实施过一轮过滤了, 如今打算把那些关系较为亲近的用户给找出来。边表大致就是存在着类似这样的情况:
raw_edges = [ ("u_1001", "u_1002", 3), ("u_1001", "u_1088", 1), ("u_1002", "u_1090", 2), ("u_2001", "u_2003", 4), ("u_2003", "u_2011", 2), ]这里面, 我要是第一眼瞅见它, 不会直接就给送去。它针对 图, 存在着这样一个要求: 节点最好得是从 0 开始的接连不断的整数。在这个地方呢, 可是特别容易挖坑, 业务当中的用户 ID 基本上通通都是字符串 , 要是直接把这些塞进去, 那后续的行号就很难去对应了。官方文档也清清楚楚地提到, 节点索引倘若要从 0 开始, 并且还得连续。
我一般先写一个很薄的转换层:
import networkx as nx defbuild_graph(edge_rows): user_ids = set for left, right, _ in edge_rows: if left != right: user_ids.add(left) user_ids.add(right) user_to_idx = {u: i for i, u in enumerate(sorted(user_ids))} idx_to_user = {i: u for u, i in user_to_idx.items} graph = nx.Graph graph.add_nodes_from(idx_to_user.keys) for left, right, score in edge_rows: if left == right: continue graph.add_edge(user_to_idx[left], user_to_idx[right], weight=score) return graph, user_to_idx, idx_to_user留意了, 在所保留的这儿, 可别期望着任一算法都会切实把这个权重当作回事。图挖掘库最为惧怕的便是“自认为它运用了”这种情况。于真正上线之前, 或者去查看源码, 或者选取几条边来开展对照实验, 切勿凭借感觉。
去先跑一回。它适配用于当作节点向量 , 后续连接相似度、聚类以及分类器均是没错的。
from karateclub import DeepWalk from sklearn.metrics.pairwise import cosine_similarity graph, user_to_idx, idx_to_user = build_graph(raw_edges) model = DeepWalk( dimensions=32, walk_number=20, walk_length=40, workers=1, seed=2026 ) model.fit(graph) vectors = model.get_embedding defsimilar_users(user_id, topn=5): idx = user_to_idx[user_id] scores = cosine_similarity(vectors[idx:idx + 1], vectors)[0] candidates = for other_idx, score in enumerate(scores): if other_idx == idx: continue candidates.append((idx_to_user[other_idx], float(score))) return sorted(candidates, key=lambda x: x[1], reverse=True)[:topn] print(similar_users("u_1001", topn=3))这段代码, 真正具备有用之处的地方, 并非是其有多高级, 而是它将“图上的邻近关系”这种情况, 转变成为了能够进行排序的向量分数。
以前写推荐召回,很多人会先做一堆规则:
共同设备 +10 共同地址 +8 共同收货人 +5 共同 IP +2这般规则并非不可运用, 然而问题在于, 越书写便越显得如同祖传下来的 if - else 一般。增添一项新关系, 就得去调试一连串权重。图嵌入所具备的益处在于, 它能够将多跳关系共同融合进来。A 与 B 并未直接存在连边, 不过它们周边的邻居极为相似, 向量之间的距离或许也会很近的。
当然,别把它当神仙。
我一般会先看三件事:
首先, 关于孤立节点的数量情况如何。其次, 孤立节点因为不存在上下文关联, 所以据此计算得出的向量其价值是非常低的。
第二, 最大连通分量所占的比例。要是图被分割成许多小块碎片, 那么其呈现出的效果通常而言不会显得很不错。
第三, 存在这样一个问题, 节点编号是否存在正确与错误之分呢。这个问题显得较为低级,然而却真的极易出现, 特别是在将结果写回到数据库之时。
排查代码可以这么写:
definspect_graph(graph): components = sorted(nx.connected_components(graph), key=len, reverse=True) largest = len(components[0]) if components else0 print({ "nodes": graph.number_of_nodes, "edges": graph.number_of_edges, "components": len(components), "largest_component": largest, "isolated_nodes": len(list(nx.isolates(graph))) }) inspect_graph(graph)倘若你并非意在寻觅相似节点, 而是打算径直将用户划分成若干圈子, 那便能够更换社区发现算法。
from karateclub import LabelPropagation cluster = LabelPropagation(seed=2026) cluster.fit(graph) memberships = cluster.get_memberships user_groups = {} for idx, group_id in memberships.items: user_groups.setdefault(group_id, []).append(idx_to_user[idx]) for group_id, users in user_groups.items: print(group_id, users)这个结果更适合给运营、风控、审核系统看。
例如, 有一组用户, 他们都共同使用过设备, 还共用过地址, 并且处于相同的支付环境。规则系统之中, 仅仅命中了其中的两个。社区发现之后, 有可能会把整团都拖出来。在此处, 我将会非常谨慎, 不会直接将其用作封禁的依据, 至多先进行线索召回。图算法计算出来的结果是“关系可疑”, 并非“事实成立”。
安装也简单:
pip install karateclub然而, 这个库并非是那种每日都会进行更新的新型玩意儿。在PyPI之上, 当下所展示的最新版本为1.3,有着3, 发布的时间是2022年10月22日。
因此, 环境不要过于激进, 当NumPy版本过新时, 碰到安装或兼容问题, 不要先去怀疑业务代码。要先单独创建一个虚拟环境, 将依赖锁定【句号为提问保留】。
python -m venv .venv_graph source .venv_graph/bin/activate pip install karateclub scikit-learn pandas我更愿意把 放在离线任务里用。
比如说, 每天凌晨的时候, 从订单库当中抽取边表, 从设备库当中抽取边表, 从登录日志里抽取边表, 接着进行构图, 随后进行跑, 之后把相似用户TopN写回到一张结果表, 或者把社区编号写回到一张结果表。线上接口仅仅查询结果, 并不在请求链路里现场跑图算法。
这才是比较稳的用法。
它具备强大之处, 在于凭借极少的代码将图挖掘算法接入工程;它存在别扭之处, 同样显著, 那便是输入图格式必须规整, 节点编号需要妥善处理, 大图性能得靠自身进行压测, 而且结果不能够未经解释就径直进入风控决策。
可要是你手上已然存有大量关系数据, 却依旧借助SQL一次次地进行join操作去探寻“谁与谁相似”, 那么这是值得去尝试一番的。
先别上来就搞复杂模型。
首先进行边表的洗净操作, 接着要确保节点映射的正确性, 随后运行实施一个 , 查看相似结果是否符合正常表述 , 若能顺利通过这一关卡 , 才可以再来谈论后续的优化事宜。