实战案例:用 sparse 实现 HITS 图算法与三角形计数(附完整代码)
【免费下载链接】sparseSparse multi-dimensional arrays for the PyData ecosystem项目地址: https://gitcode.com/gh_mirrors/sp/sparse
图算法是推荐系统、网页排名和社会网络分析的核心技术,而大部分真实图都是稀疏图——节点多、边少。PyData 生态的sparse 库正是为此而生:它提供多维稀疏数组(基于 COO/GCXS 等格式),让 HITS 图算法、三角形计数这类矩阵运算在稀疏图上又快又省内存。本文通过两个实战案例,带你用 sparse 的矩阵乘法把经典图算法"算成"几行代码。
什么是 sparse?先认识这个稀疏数组库
sparse(pydata/sparse)实现了任意维度的稀疏数组,是对 SciPy 稀疏矩阵(仅二维)的泛化,同时完全兼容 NumPy 的 ndarray 接口。安装只需一条命令:
pip install sparse安装后可用sparse.COO或sparse.asarray从 SciPy 稀疏矩阵直接转换,代码风格和 NumPy 完全一致:
import sparse import scipy.sparse as sps a = sps.random(1000, 1000, density=0.01, format="csr") s = sparse.asarray(a) # 转换为多维稀疏数组 print(s.nbytes) # 内存占用远小于稠密数组关于更多构造与运算细节,可参考官方文档 docs/quickstart.md 与 docs/operations.md。
案例一:用 sparse 实现 HITS 图算法
HITS(Hyperlink-Induced Topic Search)算法通过"权威值(Authority)"和"枢纽值(Hub)"两个分数衡量网页重要性,其核心迭代只有两条矩阵公式:
- 权威值
a = Hᵀ · h(被越多好枢纽指向,权威越高) - 枢纽值
h = H · a(指向越多好权威,枢纽越高)
在 sparse 中,邻接矩阵用sparse.asarray表示,HITS 迭代就是一个纯矩阵乘法循环:
import sparse import scipy.sparse as sps import numpy as np # 构造一个 4 节点有向图 coords = (np.array([0, 0, 1, 2, 2, 3]), np.array([1, 3, 0, 0, 1, 2])) A = sps.coo_array((np.ones(6), coords)) A = sparse.asarray(A) N = A.shape[0] h = sparse.full((N, 1), 1.0 / N) # 枢纽值初始化 for _ in range(100): hprev = h a = hprev.T @ A # 权威值更新 h = A @ a.T # 枢纽值更新 h = h / h.max() # 归一化 print(h.todense().ravel(), a.todense().ravel())在项目官方示例 examples/hits_example.py 中,还给出了编译加速版本——用@sparse.compiled()装饰器把迭代内核编译成原生代码(对应sparse.finch_backend后端),速度进一步提升:
@sparse.compiled() def kernel(hprev, A, N, tol): a = hprev.mT @ A h = A @ a.mT h = h / xp.max(h) return h, a完整代码(含与 graphblas 结果的正确性校验)都在examples/hits_example.py,直接python examples/hits_example.py即可运行。
案例二:用 sparse 做三角形计数
三角形计数用于衡量图聚集系数、发现社交网络中的紧密社区,数学上有一个著名公式:
三角形数 = sum(A @ A · A) / 6
即邻接矩阵自乘后与自身逐元素相乘再求和。这个公式用 sparse 只需一行:
import sparse @sparse.compiled() def count_triangles(a): return sparse.sum(a @ a * a) / sparse.asarray(6)官方示例 examples/triangles_example.py 对随机图(200 节点、20% 连边密度)同时用 sparse(Finch 后端)、SciPy 和 NetworkX 三种方案计数,并断言三者结果一致:
result_finch = count_triangles(a) # sparse result_scipy = (a @ a * a).sum() / 6 # scipy.sparse result_nx = sum(nx.triangles(G).values()) / 3 # networkx运行结果三者完全相等——说明 sparse 的正确性有保障,而借助编译后端的它在大图上往往比纯 Python 实现快出数量级。基准计时工具见 examples/utils.py。
快速对比:三种稀疏数组实现怎么选?
| 方案 | 维度支持 | 内存效率 | 最佳场景 |
|---|---|---|---|
| sparse(COO/GCXS) | N 维 | 高,且任意维度 | 图算法、张量运算、ND 稀疏数据 |
| scipy.sparse | 仅 2D(CSR/CSC/COO) | 高 | 传统稀疏线性代数 |
| NetworkX | — | 低 | 小规模图、研究原型 |
换后端加速:Finch 编译黑科技
sparse 支持通过环境变量SPARSE_BACKEND切换后端(Numba / Finch / MLIR),Finch 后端会在首次调用时把表达式编译为原生代码,特别适合反复迭代的图算法:
SPARSE_BACKEND=Finch python examples/triangles_example.py@sparse.compiled()配合 Finch 后端,是官方基准测试里反复出现的最优实践,详见 examples/matmul_example.py 中的对比思路。
总结
通过这两个案例可以看到:sparse 让复杂图算法回归到"写矩阵公式"的优雅状态。HITS 图算法与三角形计数,本质上都是对稀疏邻接矩阵做乘法和归约,而 sparse 提供的 NumPy 风格接口让数学公式到代码几乎零翻译成本。如果你正在处理大规模稀疏图或 N 维稀疏张量,强烈建议把 sparse 加入工具箱。
【免费下载链接】sparseSparse multi-dimensional arrays for the PyData ecosystem项目地址: https://gitcode.com/gh_mirrors/sp/sparse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考