这篇将开始分析理解 model
首先 base_gattn 是后续模型得基类
这个和 GAT 的一模一样,那就相当于顺便理解了 GAT 🤣
那么,开始吧🧑
1 BaseGAttN
定义了一些方法,loss、training、preshape、confmat 等
loss
def loss(logits, labels, nb_classes, class_weights):
sample_wts = tf.reduce_sum(tf.multiply(
tf.one_hot(labels, nb_classes), class_weights), axis=-1)
xentropy = tf.multiply(tf.nn.sparse_softmax_cross_entropy_with_logits(
labels=labels, logits=logits), sample_wts)
return tf.reduce_mean(xentropy, name='xentropy_mean')
把 labels 进行 onehot1 处理,与 class_weights 对应元素相乘2
然后对每行元素求和得到 sample_wts
将 softmax 交叉熵和 sample_wts 对应元素相乘,然后对每行求平均返回
training
用 var 记录可训练的变量3
不是 bias、gamma、b、g、beta 的变量计算 l2 范数误差(也即是均方差)4
然后将这些误差按列表叠加5,并乘 l2_coef 因子【ex_acm3025 设置为0.001】得到 lossL2
所以这是 l2 正则化项喽
使用 Adam 优化器【lr 设置为0.005】
返回一个优化后的 var_list 6
preshape
重塑形状
返回 误差的一维列表 和 有 classes 列大小的标签
masked_softmax_cross_entropy
这是有 mask 的 softmax 交叉熵计算【注意是 softmax_cross_entropy_with_logits,需要是 onehot 形式】
mask 除以每行的均值
loss 乘上 mask,返回均值
masked_sigmoid_cross_entropy
这个类似,计算 sigmoid 交叉熵误差,求每行均值
mask 除以每行的均值
loss 乘上 mask,返回均值
gat
上面看完了 base_gattn,下面的都是继承于 base_gattn
HeteGAT_multi
这个和 GAT 不同在于,有一个 Input_list,bias_mat_list,要进行多次循环
有意思的是 input_list 是特征矩阵复制3遍,而 bias_mat_list 是元路径,不一定是 3
上来就是对 n_heads[0] 循环,根据 ex_acm3025 是 8
attns 就是 8 个 attn_head 层
bias_mat 是聚合信息的矩阵,out_sz 是第一层输出单元,即隐藏单元数目 8
in_drop,coef_drop 都是 0.6
for _ in range(n_heads[0]):
attns.append(layers.attn_head(inputs, bias_mat=bias_mat,
out_sz=hid_units[0], activation=activation,
in_drop=ffd_drop, coef_drop=attn_drop, residual=False))
嗯…
这里先分析下 attn_head 层,后面都需要用到
layers.attn_head
if in_drop != 0.0:
seq = tf.nn.dropout(seq, 1.0 - in_drop)
进来先判断是否 drop
注意的是 tf.nn.dropout 是保留多少7,而我们传进来是想丢多少
所以用 1 - in_drop
GAT 说这是 虽简单的 self-attention
进行一维卷积8,(out_sz) 8 个过滤器,1 个核
seq 是 (1, 3025, 1870),seq_fts 是 (1, 3025, 8)
然后 f1 和 f2 都是 1 个过滤器, 1个核。生成 (1, 3025, 1)
f1 和 f2 是一样的
logits 为 f1 加 f2 的转置,根据广播,生成 (1, 3025, 3025)
这个 logits 是对称矩阵,(i, j) 是 f1 第 i 个元素加上第 j 个元素 Amazing😮
下面经过一个 leaky_relu 激活,传到 softmax,得 coef
这个 coef 就是整个 attention ?
【GAT 用的是邻接矩阵作为 bias,好理解。而这里是元路径,需要看下元路径矩阵是怎么处理的了】
对结果进行 dropout,对前面 seq_fts dropout
将 coef 与 seq_fts 矩阵相乘得到 val(1, 3025, 8) 【attention矩阵与特征相乘】
再添加一个 bias 得到 ret
这个差不多结束,有意思的是设计了一个残差连接,但没有使用
可能效果不太好🤔【说一句,这个残差的 seq_fts 是 dropout 过的,不是最开始卷积完的】
if residual:
if seq.shape[-1] != ret.shape[-1]:
ret = ret + conv1d(seq, ret.shape[-1], 1) # activation
else:
seq_fts = ret + seq
最后返回一个激活后的 ret 或者加上 coefs
好的,继续回到 HeteGAT_multi
attns 得到 8 个 attn_head,拼在一起得 (1, 3025, 64)
然后下面这段很奇怪,hid_units 是 [8],这样 len(hid_units) 就是 1,循环进不去啊… 🤔
在 GAT 里也是这样
按照意思,hid_units 是每层的每个 attention head 的隐藏单元数
for i in range(1, len(hid_units)):
更奇怪的是第 2 个循环,n_heads 是 [8, 1]
这个 i 就只能是 0, 1
所以这段大循环是没用的吗…
前一个循环已经把 8 个 attn_head 加到 attns 了,每个 attn_head 也是 hid_units[0]
暂且当做没用的…
for i in range(1, len(hid_units)):
h_old = h_1
attns = []
for _ in range(n_heads[i]):
把每个元路径的 attns 加入到 embed_list
到这里终于明白为什么 ftr_list 复制 3 遍了,其实 2 遍也够了(对于 2 条元路径)。
每条元路径都与相同的特征矩阵在 att_head 里面运算获得 attention 后的 embedding 😮
这些元路径 embed 拼接在一起,得到 multi_embed (3025, 2, 54)
把 multi_embed 送到 SimpleAttLayer 获得最后的 final_embed 和 att_val (3025, 64) (3025, 2)
最后,把 final_embed 送到 dense 层(没有激活)输出 (3025, 3)
下面这句是留着以后扩大神经网络的吗?
如果 n_heads 是 [8, 2],也就是说输出有 2 个,就是说多 Label
logits = tf.add_n(out) / n_heads[-1]
然后返回 logit、final_embed、att_val
哇,大功告成 🧡后面再来检查一遍
这个应该是论文的核心内容了【还要取看下 元路径是怎么处理的】
https://blog.csdn.net/nini_coded/article/details/79250600 ↩︎
https://blog.csdn.net/mumu_1233/article/details/78887068 ↩︎
https://blog.csdn.net/Cerisier/article/details/86523446 ↩︎
https://blog.csdn.net/yangfengling1023/article/details/82910536 ↩︎
https://blog.csdn.net/UESTC_C2_403/article/details/72808839 ↩︎
https://blog.csdn.net/lenbow/article/details/52218551 ↩︎
https://www.cnblogs.com/lovychen/p/9445563.html ↩︎
https://www.jianshu.com/p/2915426257ec ↩︎