|
|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 yinda_peng 于 2026-7-2 09:54 编辑
RWR(Random Walk with Restart)带重启的随机游走是一种在图上面衡量节点相似性或节点与特定种子节点关联度的经典算法,在随机游走RW上增加了一个Rerstart模块。
Random Walk:以 1-r 概率从当前节点移动到一个随机邻居节点。
Restart:以 r 概率回到开始时的那个种子节点(也就是说从种子节点出发开始游走)
数学定义:
r作为超参数,可以根据不同的数据集,不同的应用目的进行调参,使之适合数据集或者适合你的使用目的。 值得一提,RWR和RW并不是谁更优,而是侧重不同的情景。
我们来看看计算一个种子节点和其他所有节点关联度的RWR代码,这里r默认0.15,因为要跑到其他所有节点,所以应该少restart
- import numpy as np
- from scipy.sparse import csr_matrix
- def rwr_scores(adj_matrix, seed_node, r=0.15, max_iter=100, tol=1e-6):
- """
- 计算所有节点与种子节点的RWR关联度得分。
- 参数:
- adj_matrix: scipy.sparse 的邻接矩阵 (N x N), 可以是coo, csr等。
- seed_node: 种子节点的索引 (int)。
- r: 重启概率 (float, 0<r<1)。
- max_iter: 最大迭代次数。
- tol: 收敛阈值。
- 返回:
- scores: numpy array of shape (N,), 每个节点的得分(越高越相关)。
- """
- N = adj_matrix.shape[0]
-
- # 构建行归一化的转移矩阵 W: W[i,j] = 1 / degree(i) 如果 i->j 有边
- degrees = np.array(adj_matrix.sum(axis=1)).flatten()
- deg_inv = 1.0 / np.maximum(degrees, 1e-12) # 避免除零
- D_inv = csr_matrix((deg_inv, (np.arange(N), np.arange(N))), shape=(N, N))
- W = D_inv @ adj_matrix
-
- # 初始向量:种子节点为1,其余为0
- p0 = np.zeros(N)
- p0[seed_node] = 1.0
-
- p = p0.copy()
-
- for it in range(max_iter):
- p_new = (1 - r) * (W @ p) + r * p0
- if np.linalg.norm(p_new - p, ord=1) < tol: # 检查收敛
- break
- p = p_new
-
- return p
复制代码
下面是子图采样的RWR代码,这里因为目的是得到一个局部子图,所以为了有足够的节点且跟种子节点保持紧密关系,r设置的很高
- def generate_rwr_subgraph(dgl_graph, subgraph_size):
- all_idx = list(range(dgl_graph.number_of_nodes()))
- reduced_size = subgraph_size - 1
- # 首次游走:重启概率为 1(相当于直接采样固定长度的随机游走。这里参数 restart_prob=1 是特殊的)
- traces = dgl.contrib.sampling.random_walk_with_restart(
- dgl_graph, all_idx, restart_prob=1, max_nodes_per_seed=subgraph_size*3
- )
- subv = []
- for i, trace in enumerate(traces):
- subv.append(torch.unique(torch.cat(trace), sorted=False).tolist())
- retry_time = 0
- while len(subv[i]) < reduced_size:
- # 若游走长度不足,则以 0.9 的重启概率重新采样,直到满足大小
- cur_trace = dgl.contrib.sampling.random_walk_with_restart(
- dgl_graph, [i], restart_prob=0.9, max_nodes_per_seed=subgraph_size*5
- )
- subv[i] = torch.unique(torch.cat(cur_trace[0]), sorted=False).tolist()
- retry_time += 1
- if (len(subv[i]) <= 2) and (retry_time > 10):
- subv[i] = (subv[i] * reduced_size)
- subv[i] = subv[i][:reduced_size]
- subv[i].append(i) # 确保种子节点包含在内
- return subv
复制代码
restart_prob=1:首次调用时重启概率为 1,实际等价于对每个节点独立采样固定长度的随机游走序列(因为重启概率为 1 表示每次都回到起点,但 max_nodes_per_seed 限制了总步数,所以这里实际会生成从起点出发的随机游走轨迹)。
restart_prob=0.9:若第一次游走得到的节点数不足 subgraph_size-1,则以 0.9 的重启概率(即 90% 概率跳回起点,10% 概率继续随机游走)重新采样,直到获得足够多的节点。
该函数最终为每个节点返回一个包含 subgraph_size 个节点的列表(包括该节点自身), subgraph_size 是一个固定值,你根据每个数据集可以使用不同的数值,比如: - if args.subgraph_size is None:
- if args.dataset in ['cora','citeseer']:
- args.subgraph_size = 4
- elif args.dataset in ['books','BlogCatalog','Flickr','Weibo']:
- args.subgraph_size = 5
复制代码
|
|