当前位置: 首页 > 工具软件 > Han > 使用案例 >

【HAN】代码逐句理解一

辛意智
2023-12-01

入门一个方向不仅要看论文(似懂非懂),还要搞清楚论文的代码才行

那就开始吧,逐句理解

GPU

首先设定可见的 GPU (我也想拥有…)

os.environ["CUDA_VISIBLE_DEVICES"] = "1,2,3"

然后慢慢给显存,但不会释放,所以会有碎片

config = tf.ConfigProto()
config.gpu_options.allow_growth = True

设置数据集 acm,featype 为下面的特征矩阵类型 现在是 fea,输出一段确认信息数据集和 featype

训练参数

batch_size 为 1

nb_epoches 为 200(共 200 轮吗?)

patience 不知道

lr 学习率 0.005

l2_coef 权重衰减 0.001

每层每个注意头的隐藏单元数目

hid_units = [8] 8 个隐藏单元

n_heads = [8, 1] 输出层的附加东西,8个到1个吗?

residual = False 是不设置残差吗?

nonlinearity = tf.nn.elu 非线性函数为 elu

model = HeteGAT_multi 模型为 heteGAT_multi (这个具体在 gat.py 文件里)

接着输出一系列确认信息
数据集、学习率、权重衰减
层数(这个输出是隐藏单元数?)、隐藏单元数、注意头数(n_heads ?)
残差、非线性函数、模型

sample_mask

创建一个 mask,下面 load_data 用得到
生成一个全是 false 的列表
对应 idx 位置设为 true

load_data

下面以 ACM3025 为例

用 scipy.io 的 sio 导入 mat 数据
导入后,可以看到 ‘PTP’、‘PLP’、'PAP‘、‘feature’、‘label’、‘train_idx’、'val_idx’ 、‘test_idx’ 索引

获取 label 和 feature,维度分别为 (3025, 3) 和 (3025, 1870)
样本个数 N,N 为 3025
data[‘PAP’] 和 data[‘PLP’] 都是 (3025, 3025)

rownetworks 是这两个矩阵减去单位矩阵,然后拼接

rownetworks = [data['PAP'] - np.eye(N), data['PLP'] - np.eye(N)]

记 label 为 y
train_idx、val_idx、test_idx 分别记录训练集、验证和测试的下标
维度各自为 (1, 600)、(1, 300)、(1, 2125)
测试集这么多?

然后用到上面的 mask
生成 train_mask、val_mask、test_mask
总长度是 3025,这些数据集对应的下标为 true

生成三个集对应的 y
先生成 3 个全空的 y 尺寸的矩阵(3025, 3)
然后把 y 上对应下标的值给到对应的三个集上,注意不是切片
这样 y_train 等三个集都还是 (3025, 3),对应下标都是 y 的值了,其他位置还是 0

输出一条确认信息,y_train 等和 train_idx 等的 shape

把特征复制 3 遍拼在一起 得到 truefeatures_list

最后,函数返回 rownetworks, truefeatures_list, y_train等,train_mask等

然后下面用 adj_list 接 rownetworks,fea_list 接 truefeatures_list

这里来了一句:试一试
如果 featype == ‘adj’,把 adj_list 当作fea_list
【所以 fea=fea,没有用到元路径信息?】

数据准备

nb_nodes 为 3025
ft_size 为特征数目 1870
nb_classes 为分类数 3

下面对 fea_list、adj_list 等升维,用到了 np.newaxis
如 y_train 从 (3025, 3) 变成了 (1, 3025, 3)

把 adj(上面就是 ‘PAP’ 矩阵)进行处理,用到了 process.py 的 adj_to_bias
通过扩展到一个给定的邻域来准备邻接矩阵

biases_list = [process.adj_to_bias(adj, [nb_nodes], nhood=1) for adj in adj_list]

adj_to_bias

这里分析一下 adj_to_bias

def adj_to_bias(adj, sizes, nhood=1):
    nb_graphs = adj.shape[0]
    mt = np.empty(adj.shape)
    for g in range(nb_graphs):
        mt[g] = np.eye(adj.shape[1])
        for _ in range(nhood):
            mt[g] = np.matmul(mt[g], (adj[g] + np.eye(adj.shape[1])))
        for i in range(sizes[g]):
            for j in range(sizes[g]):
                if mt[g][i][j] > 0.0:
                    mt[g][i][j] = 1.0
    return -1e9 * (1.0 - mt)

这个 nb_graphs 挺奇怪,上面是 for 传入的 adj,又升过维,那应该就是 1 啊
生成 (1, 3025, 3025) 的全 1 张量
mt 的每个图 生成 (3025, 3025) 的单位矩阵,也就 1 个
对每个邻居,用 mt[g] 和 adj+单位矩阵 进行矩阵相乘,这个 nhood 可以设定
然后判定 mt,如果 mt[i][j] > 0,那就设为 1.0 【不过这双重循环 3025 * 3025 看起来有点费时间?】

最后返回 -1e9 * (1.0 - mt),也即是
如果判定前 mt[i][j] 大于 0,最后是 0,如果小于等于 0,最后是一个很小的负数,如 -10000000000

这个就是用于后面的 bias 添加,只添加负的

建图

设置默认图,命名空间为 input

with tf.Graph().as_default():
    with tf.name_scope('input'):

生成 3(上面复制了 3 遍)个 (batch_size, 3025, 1870) 的特征 tensor
生成 2 (拼了 2 个路径)的 (batch_size, 3025, 1870) 的邻接 tensor
lbl_in (batch_size, 3015, 3) 标签 tensor
msk_in (batch_size, 3025) 的 mask tensor

forward

这里调用了 model 的 inference,这个就另写一篇研究模型了

返回 logits(误差?)、final_emb、att_val

cal masked_loss

把 log、label、mask 传入 masked_softmax_cross_entropy 计算 loss
同时也传入得到 accuracy

这两个计算函数都在 baseGNN 里,也就是 GAT 的代码里,一模一样

optimzie

调用 model 的 training
保存训练的模型
打包全局和局部初始化器,等会一起执行

Session

200 轮
tr_step 是 batch 的步数
tr_size = 3025

lbl_in 放入 y_train 的 batch
msk_in 放入 train_mask 的 batch
attn_drop 为 0.6
ffd_drop 为 0.6

用 fd 字典把训练的特征、邻接、标签和mask 拼在一起

然后开始 run

对比 GAT 的 execute_cora.py 的训练,不同在于 feed_dict 的不同,具体是 fd1 和 fd2 的不同
其中 fd1 是 复制 3 遍的特征矩阵,而 fd2 是不同元路径的拼接矩阵
所以应该就是 fd2 的不同

fd1 = {i: d[tr_step * batch_size: (tr_step + 1) * batch_size]
       for i, d in zip(ftr_in_list, fea_list)}
fd2 = {i: d[tr_step * batch_size: (tr_step + 1) * batch_size]
       for i, d in zip(bias_in_list, biases_list)}
fd3 = {lbl_in: y_train[tr_step * batch_size: (tr_step + 1) * batch_size],
       msk_in: train_mask[tr_step * batch_size: (tr_step + 1) * batch_size],
       is_train: True,
       attn_drop: 0.6,
       ffd_drop: 0.6}
fd = fd1
fd.update(fd2)
fd.update(fd3)
_, loss_value_tr, acc_tr, att_val_train = sess.run([train_op, loss, accuracy, att_val],
													feed_dict=fd)

GAT 的是

_, loss_value_tr, acc_tr = sess.run([train_op, loss, accuracy],
feed_dict={
    ftr_in: features[tr_step*batch_size: (tr_step+1)*batch_size],
    bias_in: biases[tr_step*batch_size: (tr_step+1)*batch_size],
    lbl_in: y_train[tr_step*batch_size: (tr_step+1)*batch_size],
    msk_in: train_mask[tr_step*batch_size: (tr_step+1)*batch_size],
    is_train: True,
    attn_drop: 0.6, ffd_drop: 0.6})

最后,通过两个自定义的 KNN 和 Kmeans 得到节点分类和节点聚类的指标

参考:https://blog.csdn.net/weixin_43301333/article/details/108854504
https://blog.csdn.net/u012436149/article/details/53837651
https://blog.csdn.net/nanhuaibeian/article/details/101862790
https://blog.csdn.net/qq_37591637/article/details/103408486

 类似资料: