- 它结合了用于网络嵌入的“三个耦合图神经网络(gnn)+用于参数学习的双深度q网络(dqn)”。
- 技术一:使用DoubleDQN,近似求解动作状态值函数 Q(s, a)
- 除了网络拓扑结构外,DDQN中的函数逼近器还需要捕捉IM中的关键影响级联效应。(级联效应是指一个节点的激活将以连续的方式触发它的邻居,在社交网络上形成扩散级联,这与图神经网络(gnn)中的消息传递效应是一致的。)
- 技术二:使用GNN模拟级联效应 我们使用gnn来获取节点嵌入,并利用节点嵌入来构造参数化函数,端到端模型,用DDQN学习所有参数。
痛点:
- 以往方法都是在子图上训练,在整个图上测试,导致模型在不同网络间性能不稳定。
该方法在随机生成的小图上训练,在不同网络上测试,泛化效果好。 - 模型可以在无向图和不同的边权上工作
- 监督学习,这引入了大量额外的计算开销和手工制作学习管道的努力,而我们的模型可以端到端学习参数。
- PIANO方法[11]采用structure2vec学习节点嵌入,而我们设计了三个耦合的gnn来学习网络表示
贡献:
- 第一个提出这样一个端到端的框架,ToupleGDD,它结合了耦合gnn和DRL方法来有效地解决IM问题
- 一种定制的深度游走(PDW)方法来学习初始节点嵌入作为以下定制GNN层的输入特征,该方法同时考虑了节点的局部和全局影响
- 为了捕捉级联效应和网络拓扑,我们设计了3耦合gnn来学习节点嵌入
- 在一些数据集上的性能非常接近于IMM,甚至超过了OPIM-C,这证明了我们提出的模型的优越性和有效性。
马尔科夫建模:
• 有向图
• (u,v):u是v的in-neighbor,v是u的out-neighbor
• v有Nin(v)和Nout(v)
IC传播模型:
• puv:u对v的激活概率
• 每个节点有两种可能的状态:活动和不活动
• 初始,种子集S中的所有节点为激活状态,其余节点为非激活状态;
• 如果节点u首先在时间戳t被激活,那么u将尝试在时间戳t + 1激活其未激活的外邻居v,成功率为puv。
• 在时间戳t + 1之后,u不能激活它的任何外部邻居;
• 一旦节点被激活,它在以下时间戳中保持活动状态
IM传播最大化定义
• I(S)扩散过程终止时激活的节点数,σ (S)为S能激活的节点数。σ (S) = E[I(S)]
• b是上限。以S≤b的小节点集S作为种子,其影响范围最大。
• 设St为当前选择的种子集
强化学习过程:
- 学习阶段:
• 给定一组训练图G ={g1, g2,…, Gc}
• 扩散模型ψ
• 影响函数σ: S→R+
• 训练一组参数使得影响函数尽可能准确。 - 测试阶段:
• 给定一个网络G
• 学习参数
• 一个整数预算b
• 在某个扩散模型ψ下求解预算b的IM问题。
GNN框架:
学习拓扑信息,学习级联效应,利用设计的随机游走方式学习节点的局部和全局影响力
1. 给定一组训练图G = {G1, G2,…, Gc},我们首先应用PDW方法得到初始节点嵌入(比随机初始化稳定性强)
2. 然后结合GNN和注意机制学习节点嵌入:
①. 设计3耦合GNN捕捉节点级联效应
3. K回合迭代,使用嵌入构造参数化函数Q(v,S,Θ)
4. 使用强化学习DDQ + ε贪心 获得参数
3耦合GNN:
目标节点v是否被激活,三方面决定:
1) In-neighbor:u的状态
2) u的影响力
3) v受u影响趋势
u的嵌入包含三部分:
1) Xu:节点激活状态 :Xu ∈ R
2) Su:u影响其他节点的能力:Su ∈ R^l
3) Tu:被其他节点激活的趋势:Tu ∈ R^l
一. 初始化:PDW获得节点u对应的Xu、Su、Tu
PDW方法学习嵌入,作为下一个GNN层的输入特征
1) 受Inf2vec模型启发
2) 生成节点上下文(影响)
3) 利用skip-gram方法预测给定节点的上下文
1. u的上下文Cu包含两部分,依次获得:
限制节点上下文的大小,假设节点上下文的长度阈值为L, α∈[0,1]
· 局部影响Lu:
可以被u激活的采样集合
①. 采用重启随机游走(RWR)策略(设置重启概率为0.15)
②. 到达阈值α·L时停止行走,获得Lu
· 全局影响Gu:
从u的out-neighbors的r-hop节点采样,采样集
①. 随机采样(1-α)·L个节点,Nout^r
2. 基于u的嵌入,u的影响力Cu(u的级联效应)的概率为:
1) 给定u的嵌入,任意节点v受u影响的概率用softmax函数表示:
四部分:Xu节点u的激活状态;Su节点u影响其他节点包括v的能力;Tv节点v被其他节点影响的趋势;Xv节点v的激活状态
· Pr(v|u) = e^(Xu ·Su ·Tv+Xv) /Z(u)
· Z(u):归一化项:所有可能的v的e^(Xu ·Su ·Tv+Xv)求和
· Xv:v的激活状态
· Tv:v受u激活的趋势
2) u的影响和Cu:u影响的所有v的概率Pr(v|u)的连乘积
3) 采样一组u和其级联影响Cu,最大化它们的对数概率log Pr(v|u)
计算优化:
(1) 枚举所有v,计算Z(u)费时,使用负采样,近似计算softmax函数
(2) 采用随机梯度下降法(SGD)学习所有参数,在每一步中,我们通过计算梯度来更新参数
更新参数: Xu, Su, Xw, Tw
返回每个节点u对应的Xu、Su、Tu
二. 3耦合GNN迭代u嵌入
1. state GNN:建模节点的激活状态:X(k+1)v
2. Source GNN:建模节点的影响能力:S(k+1)v
3. Target GNN:建模节点受其他节点影响的趋势:T(k+1)v
强化学习中,基于当前状态选择的种子集St,需要通过ToupleGNN相应地更新节点表示。
1) State GNN
· 建模节点在级联效应过程中的激活状态
· 节点被in-neighbor中的激活节点激活
· 节点的激活状态Xv由两部分决定:
1. 所有in-neighbor的激活状态(节点被激活的可能性)
2. 所有in-neighbor的影响权重/概率
· 由于:节点间相互作用强度a(k)v随节点状态改变,所以不能只使用静态边权Puv
· 两部分组成:所有in-neighbor的puv和动态权重InfluGate()
优化方法:
添加:v的in-nighbor的能力嵌入Sv,和v的趋势嵌入Tv
建立影响注意机制,动态获得节点之间的权值:
· e(k)uv表示u对v的动态影响
· 将Sv,Tv使用维度转换矩阵W转换维度后拼接
· 再作用权重矩阵η
· 为了使节点间的系数具有可比性,采用了结合LeakyReLU的softmax函数对注意系数进行归一化,获得InfluGate()
1) 节点被激活的可能性:X(k+1)v
· 当节点v被选择到当前种子集St中时,节点v的激活状态设置为1
· 否则,聚合v的in-neighbor的影响来更新:两部分组成:
w 第k层的X(k)v
w v的in-neighbor的聚合影响a(k)v
2) Source GNN
· 模拟节点影响其他节点的能力:
(1) 节点的激活状态X(k)v
(2) 节点向它所有out-neighbor传递的影响:
(1) 用out-neighbor被激活的趋势之和表示(类似state GNN中的动态影响权重)
(2) f(k)vw表示,计算方法等同于e(k)uv
(3) LeakyReLU的softmax函数归一化为a(k)vw
(4) 用三层MLP(多层感知机)获得每一个out-neighbor的非线性被激活趋势:SourceGate(⋆)
(5) 聚合所有out-neighbor,获得b(k)v
(6) 结合:
} out-neighbor被激活的趋势之和b(k)v
} k层的S(k)v
} k层的:节点的激活状态X(k)v
三个部分,三个参数,获得S(K+1)v
3) Target GNN
· 目标GNN用于模拟节点受其他节点影响的倾向:
(1) 它当前的激活状态X(k)v
(2) 它的in-neighbor对它的影响扩散决定的:
(1) 聚合Su和Tv
(2) LeakyReLU的softmax函数归一化为φ(k)uv
(3) TargetGate(⋆)用三层MLP(多层感知机)获得每一个in-neighbor的非线性影响力
(4) 聚合所有in-neighbor,获得c(k)v
(5) 结合:
} k层的:T(k)v
} k层的:c(k)v
} 当前的激活状态:X(k)v
三个部分,三个参数,获得T(K+1)v
三. 强化学习
拼接三部分获得u的嵌入
u的嵌入参数化候选节点u的增益函数:Q(u,St,Θ),Θ指代4个参数
马尔科夫建模:
A:候选节点u的嵌入
S:St表示当前的节点序列,是一个| V |维向量
若u在当前序列,u是1;否则是0
用Sb代替St,b是当前已选节点数量
T:选择u为种子时,激活状态Xu从0更改为1
R:r表示单个节点的增益
Sb的影响范围σ等于所有节点增益r之和
P:映射每个状态下选择每个节点的概率,即如何选择下一个节点
DDQN训练拟合函数Q(u,St,Θ)的参数:
采用两个网络:
· 行为网络:Θ:训练期间,目标网络为行为网络提供未来状态Q的估计值
· 目标网络:Θ' :Θ更新m回合,更新一次
1) 为了获得对未来奖励更准确的估计,采用n步q学习更新参数,即等待n步后更新参数。
2) 采用了带有经验回放的拟合q迭代,以加快学习收敛。
3) 参数Θ更新是通过最小化损失函数完成
完整DDQN步骤:
1. 使用PDW初始化节点嵌入
2. 每一回合(执行b次):
1) 初始化空种子集
2) ε贪心策略选择节点
3) 若到达n步,存储样本到M,使用经验回放
4) 利用缓冲区的一批样本更新参数,而非当前损失
5) b次,每一次:3耦合GNN更新k次嵌入
每层聚合一次
四. 实验
1. 随机生成20个节点数量15-50不等的ER图用于训练和验证
· 均匀随机抽取15-50个节点
· 生成连边概率为0.15的ER图
· 15张图用于训练
· 5张和soc-dolphins数据集用于验证
2. 模型和基线算法在七个真实数据集上进行测试
· 若为无向图,则改为双向边
· Twitter, Wiki-1, caGr, Buzznet
· Wiki-2, Epinions, Youtube
3. 通过修改奖励函数,模型可以很容易地适应不同的扩散模型
· 使用IC模型:
® 边传播概率为:1/in-neighbor数量
4. 为保证公平,进行10000次蒙特卡罗模拟,估计预期的影响范围。
5. 所有实验运行十次,报告被测量度量的平均值
6. 基线算法:
· IMM
· OPIM-C
· DRL methods
· S2V-DQN(修改版)
· GCOMB
· PIANO
7. 训练数据集:
1) 边权为in-degree
2) 默认:验证数据集和测试数据集的边权相同
(1) 三种设置方法:
①. In-degree
②. 0.1
③. 0.5
3) 预算b为5(测试数据集:人工图:5,soc-dolphins数据集:7)
4) 测试集:每个测试数据集,我们改变预算b,使b∈{10,20,30,40,50}
项目目录:
-
test_data:
- Wiki-2.txt:
· 有向图(每对无序节点保存一次)
· 有向边A->B表示用户A投票支持B成为维基百科管理员
· 节点:7115
· 边:103689
- Wiki-2.txt:
-
train_data:
- graph1.txt到graph15.txt:
· 人工生成的节点数目15-50的15个graph
- graph1.txt到graph15.txt:
-
utils:
-
init.py:空
-
graph_utils.py:
· Graph类:
(1) 初始化图函数:
属性
①. 节点:
· set() 类:一个无序不重复元素集
②. 边:
· dict{(src,dst): weight, }
③. 孩子(out-neighbor)
· dict{node: set(), }
④. 父母(in-neighbor)
· dict{node: set(), }
⑤. 每个节点的孩子节点集合(有序排列)
⑥. 每个节点的父母节点集合(有序排列)
⑦. 节点数量
⑧. 边数量
⑨. 邻接矩阵adj = None
⑩. self._from_to_edges = None
11. self._from_to_edges_weight = None(2) get孩子函数(out-neighbor): · 获得节点的out-neighbor节点集合 (3) get父母函数(in-neighbor): · 获得节点的in-neighbor节点集合 (4) get概率函数(out-neighbor): · 获得节点的边权节点集合 (5) get邻接矩阵: · 元素是边权值 (6) from_to_edge: · 边列表 (7) from_to_edge_weight: · 边权列表
· 图的功能函数:
(1) 加载图函数:
有向图
· 孩子:dict:包含所有的源节点(key)和对应的目的节点集合(value)
· 父母:dict:包含所有的目的节点(key)和对应的源节点集合(value)
· 边:dict:(src, dst) key;边权=0.0(value)
· 节点:set:所有节点
无向图
· 孩子:dict:包含所有的目的节点(key)和对应的源节点集合(value)
· 父母:dict:包含所有的源节点(key)和对应的目的节点集合(value)
· 边:dict:(dst, src) key;边权=0.0(value)· 修改边权:1/节点出度
-
-
val_data:
- graph16.txt到graph20.txt
· 人工生成的节点数目15-50的15个graph - Soc-dolphins.txt
· 节点:62
· 边:159
- graph16.txt到graph20.txt
-
init.py:空
-
environment.py:
• Environment类:
1) 初始化函数:
属性:
#图的采样集相关
①. 名字:
②. 图:
#IM相关
①. 预算:
②. 方法:RR或者MC
#在同一图上运行多次时
①. 使用cache:加速
②. .method
③. .influences
④. .RRs_dict
#训练或测试模式时
①. .training=training
2) 重置批量图函数:
· 生成新图,默认数量为10个
3) 重置函数(重新开始):
(1) 设置.graph
· 若无图index:环境里随机选择一张图
· 若有:选择该图
(2) 设置.state
· 遍历图中所有节点
#IM相关
(3) 设置prev_inf
· 先前影响得分
#存储RR集,以防有多个图
(4) 若使用cache且method是RR
存储:
· .RRs
· .state
· .actions
· .rewards
· .training
4) 计算奖励函数:
(1) 并行处理数量为5
(2) 试验次数为10000
#获得影响值
(3) 如果使用cache且method是MC
5) step函数(改变状态,获得奖励):
(1) #若节点已经被选择
如果节点状态是1
return空
(2) #若节点没有被选择
#存储状态和动作
添加节点状态到状态list中
添加节点到动作list中
#更新状态
将节点状态设置为1
#计算奖励
► 如果.name不是IM
报错
如果是IM
状态S = .actions(节点序列集)
如果节点数量大于等于预算
Done
► 如果是.training模式
奖励 = 计算奖励函数返回值(计算奖励)
如果不是.training模式
► 如果done
► 如果时间奖励不是0
开始时间=当前时间(开始计时)
如果是0
奖励 = 计算奖励函数返回值(计算奖励)
如果时间奖励不是0
时间奖励第一个元素 = 当前时间-开始时间(停止计时)
如果没done
奖励=0
返回(奖励,done) -
models.py:
4个模型类nn.Module:
► S2V_DQN
► Tripling(3耦合GNN + 强化学习)
► DeepWalkNeg
► S2V_DUEL
structure2vector + dueling deep Q network
Tripling类
(1) 初始化函数
参数列表:
· 嵌入层维度emb_dim
· stateGNN维度?sourceGNN
· target GNN维度
· T:Tripling层数
· 隐藏层维度
· w_scale
#Tripling类继承父类nn.Model的属性,并使用父类的初始化方法初始化继承到的属性。
#初始化非继承的新属性
· 设置.emb_dim为传入值
· 设置.sgate_l1_dim为传入值
· 设置.tgate_l1_dim为传入值
· 设置.T为传入值
· 设置.hidden_dims为传入值
#初始嵌入维度
· .hidden_dims.insert(0, embed_dim):插入嵌入维度为索引0
· 转移权重.trans_weights = nn.ParameterList()
#stateGNN的参数
· .influgate_etas = nn.ParameterList()
· .state_weights_self = nn.ParameterList()
· .state_weights_neibor = nn.ParameterList()
#边注意力和边权的参数
· .state_weights_attention = nn.ParameterList()
· .state_weights_edge = nn.ParameterList()
#sourceGNN的参数
· .source_betas = nn.ParameterList()
· .sourcegate_layer1s = nn.ModuleList()
· .sourcegate_layer2s = nn.ModuleList()
· .source_weights_self = nn.ParameterList()
· .source_weights_neibor = nn.ParameterList()
· .source_weights_state = nn.ParameterList()
#边注意力和边权的参数
· .source_weights_attention = nn.ParameterList()
· .source_weights_edge = nn.ParameterList()
#targetGNN的参数
· .target_taus = nn.ParameterList()
· .targetgate_layer1s = nn.ModuleList()
· .targetgate_layer2s = nn.ModuleList()
· .target_weights_self = nn.ParameterList()
· .target_weights_neibor = nn.ParameterList()
· .target_weights_state = nn.ParameterList()
#边注意力和边权的参数
· .target_weights_attention = nn.ParameterList()
· .target_weights_edge = nn.ParameterList()
(2) 前向传播(级联效应)
#输入初始化节点嵌入
#执行tripling消息传播:
源节点影响source_influ:S(k)u
目标节点影响target_influ:T(k)v
状态
#遍历tripling所有层
矩阵乘,矩阵乘,拼接
influgate_etas[i]:η(k)
矩阵乘 ,leaky函数,得到e_uv
state:X(k)v -
rl_agents.py:
-
runner.py:
-
main.py:
• 设置logger
• 参数配置(三步骤):
1) 创建 .ArgumentParser() 对象
2) 调用 .add_argument() 方法添加参数
· 预算–budget:默认6
· 图路径–graph:默认:soc-dolphins.txt
· agent类型–agent:默认:Agent
· 模型名称–model:默认:Tripling
· 模型文件–model_file:默认:tripling.ckpt
· 训练回合数–epoch:默认:2000
· 学习率–lr:默认:1e-3
· 训练时的批大小–bs:默认:8
· 强化学习n-step技术的n大小–n_step:默认:2
· 是否使用cpu–cpu:默认:False
· 是否测试模型性能–test:默认:False
· 环境名称–environment_name:默认:IM
3) 使用 .parse_args() 解析添加的参数
• main函数:
#加载参数
#设置设备
使用cuda
#加载图
► 如果.graph是目录
加载多图路径
如果不是目录
加载一个图
► 获得图路径列表
遍历多个图,设置.path_graph的路径
设置.graphs为图路径列表
设置.double_dqn为True
► 如果不是test模式:是tripling模型的训练模式
时间戳 = 当前时间
► 如果时间戳获取失败
创建目录(以时间戳为路径)
设置.model_file = 时间戳,模型文件
设置.T为3
设置.memory_size = 50000
设置.reg_hidden为32
► 如果模型是Tripling
嵌入层维度是50
► 如果不是
维度是64
#加载agant
使用rl_agent.py文件的.Agent
#加载环境
#创建环境
1) 创建训练环境:
· 环境名称
· 图列表graph_lst
· 预算
· Method=RR
· 是否使用cache
2) 创建测试环境:
· 环境名称
· 图列表graph_lst
· 预算
· Method=MC
· 是否使用cache
#加载runner和开始running
print:运行单个实例模拟
设置runner = runner.py的runner:
· 训练环境
· 测试环境
· Agent
· 不测试
► 如果不测试:
· runner开始训练:
· .epoch训练回合
· 模型参数文件
· 保存计算出的:奖励结果.txt
如果测试:
· runner.test():
· 测试次数默认:10 -
s2vdqn.ckpt
-
tripling.ckpt
–graph:提供图的路径。它可以加载单个图形:该图形的路径,或多个图形:包含所有图形的文件夹的路径。
–model:提供模型使用:Tripling是ToupleGDD模型,S2V_DQN是基线S2V-DQN模型。
–model_file:在测试时提供模型的路径,或者在训练时提供模型将保存到的文件名。
–test:需要指定来测试模型。
测试时不需要:–epoch,–lr,–bs,–n_step
图的节点id应为连续整数
main.py第54行:
• 设置图类型:有向或无向
• 设置图的起始节点id: 0-index或1-index
rl_agents.py第143行:
• 设置训练时,生成初始节点嵌入的回合epoch次数9(当前是30)
runner.py第61行:
• 设置test时,生成初始节点嵌入的epoch数(当前是0)
utils/graph_utils.py第 87-97行:
• 设置图边权(有三种方法)
• 边(u, v)的当前设置为:1/in-degree(v) (in-degree设置)