Blog9 图神经网络的模型级解释——关键代码分析1

2021SC@SDUSC

一、实验数据集简介

这个项目的数据集有两个,作者分别用了合成数据集现实数据集,因为在上篇博客中我们说到,之前没有过模型级对gnn的解释工作,因此无法与其他方法进行比较。

1.合成数据集

  • 描述:由节点构成的图。
  • 分类标签:图中是否存在环。
  • 初始特征:节点度数。

 请注意,此数据集中的所有节点都未标记,我们专注于研究 GNN 捕获图形结构的能力。 

2.现实数据集

  • 描述:一些化合物的原子结构,节点代表原子,边代表化学键。
  • 分类标签:有无对细菌的致突变作用。
  • 初始特征:原子类型。每个节点都根据其原子类型进行标记,并且是七种可能的原子类型:碳、氮、氧、氟、碘、氯、溴。

对于这个数据集,我们研究了 GNNs捕获图形结构和节点标签的能力。

二、实验代码综览

代码请参考博客链接🔗:可解释性研究(二)- XGNN_I still …的博客-CSDN博客

1.数据集 

采用第二个现实数据集。

结点类型包括 Carbon(碳), Nitrogen(氮), Oxygen(氧), Fluorine(氟),Iodine(碘), Chlorine(氯), Bromine(溴) 。 边的类型这里并未用上。

MUTAG中包括188个分子图,总共3371个结点(原子),7442个边(化学键)。数据集目录如下:

  • node_labels.txt记录了3371个结点中每个结点的类型(从0-6编号)
  • graph_indicator.txt记录每个结点对应的图索引号(图索引号从1-188编号)
  • graph_labels.txt记录了188个图每个图对应的类型(label为1或-1)
  • A.txt以(start_node_idx,end_node_idx)记录了7442条边,start_node_idx和end_node_idx均在3371范围内
  • edge_labels.txt记录7442条边每个边的类型,这里并未用上。
     

 2.代码文件

①Load_dataset.py:加载数据集的文件,共188个图。

Model.py:GCN分类器的模型代码,即论文中提出的f(.)

train.py:训练GCN分类器的代码。用到了①和②

GraphGenerator.py:图生成器generator的类定义

TrainGenerator.py:训练图生成器的代码。调用④

⑥test.py:测试用到了①和②,测试GCN模型的效果。

⑦check.py:转换数据集的标签,生成初始的特征矩阵和邻接矩阵表示。

3.代码分析————Load_dataset.py

def load_split_MUTAG_data(path="datas/MUTAG/", dataset="MUTAG_", split_train=0.7, split_val=0.15):
    """Load MUTAG data """
    print('Loading {} dataset...'.format(dataset))

    # 加载图的标签
    graph_labels = np.genfromtxt("{}{}graph_labels.txt".format(path, dataset),
                           dtype=np.dtype(int))
    graph_labels = encode_onehot(graph_labels)  # (188, 2)
    graph_labels = torch.LongTensor(np.where(graph_labels)[1]) # (188, 1)


    # 图结点的索引号
    graph_idx = np.genfromtxt("{}{}graph_indicator.txt".format(path, dataset),
                              dtype=np.dtype(int))

    graph_idx = np.array(graph_idx, dtype=np.int32)
    idx_map = {j: i for i, j in enumerate(graph_idx)} 
# key, value表示第key个图的起始结点索引号为value
    length = len(idx_map.keys()) # 总共有多少个图
    num_nodes = [idx_map[n] - idx_map[n - 1] if n - 1 > 1 else idx_map[n] for n in range(1, length + 1)] 
# 一个长度188的list,表示每个图有多少个结点
    max_num_nodes = max(num_nodes) # 最大的一个图有多少个结点
    features_list = []
    adj_list = []
    prev = 0

1.加载数据集。图节点和边的信息。

    # 生成邻接矩阵A,该邻接矩阵包括了数据集中所有的边
    adj = sp.coo_matrix((edges_label, (edges_unordered[:, 0] - 1, edges_unordered[:, 1] - 1)))

    # 论文里A^=(D~)^0.5 A~ (D~)^0.5这个公式
    adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj)

    node_features = normalize(node_features)
    adj = normalize(adj + sp.eye(adj.shape[0])) # 对应公式A~=A+IN
    adj = adj.todense()

在论文中引用的公式如下:

 GCN 的操作定义为上式 (1),其中:

  • Xi∈Rn×di和Xi+1∈Rn×di+1是第i个图卷积层的输入和输出特征矩阵。
  • Aˆ = A + I 用于在邻接矩阵中添加自环.
  • D 表明对角节点度矩阵以对Aˆ 进行归一化。
  • 矩阵 Wi ∈ Rdi×di+1 是 layer 的可训练矩阵,用于执行线性特征变换.
  • f (·) 表示非线性激活函数。 通过堆叠j个图卷积层,可以聚合j-hop邻域信息。
#设置特征矩阵和邻接矩阵
    for n in range(1, length + 1):
        # entry为第n个图的特征矩阵X
        entry = np.zeros((max_num_nodes, max(nodeidx_features) + 1))
        entry[:idx_map[n] - prev] = node_features[prev:idx_map[n]]
        entry = torch.FloatTensor(entry)
        features_list.append(entry.tolist())

        # entry为第n个图的邻接矩阵A
        entry = np.zeros((max_num_nodes, max_num_nodes))
        entry[:idx_map[n] - prev, :idx_map[n] - prev] = adj[prev:idx_map[n], prev:idx_map[n]]
        entry = torch.FloatTensor(entry)
        adj_list.append(entry.tolist())

        # prev为下个图起始结点的索引号
        prev = idx_map[n]

torch.FloatTensor():将numpy类型转换为张量tensor

 #设置训练集、验证集、测试集数量
    #怎么分?上面设置了split_train=0.7,num_train训练集数量为70%,测试集、验证集数量均为15%
    num_total = max(graph_idx)
    num_train = int(split_train * num_total)
    num_val = int((split_train + split_val) * num_total)

    if (num_train == num_val or num_val == num_total):
        return

    features_list = torch.FloatTensor(features_list)
    adj_list = torch.FloatTensor(adj_list)

    #训练集0-70%,验证集70%-85%,测试集85%-100%
    idx_train = range(num_train)
    idx_val = range(num_train, num_val)
    idx_test = range(num_val, num_total)

    idx_train = torch.LongTensor(idx_train)
    idx_val = torch.LongTensor(idx_val)
    idx_test = torch.LongTensor(idx_test)
    
  
    return adj_list, features_list, graph_labels, idx_map, idx_train, idx_val, idx_test

 返回值依次为:

  • adj_list:188个图的邻接矩阵列表 ,维度为(max_node_num×max_node_num)
  • features_list:188个图的特征矩阵列表 ,维度为(max_node_num×feature_num)
  • graph_labels:188个图的label
  • idx_map:每个图的起始结点索引号
  • idx_train:训练集索引号
  • idx_val: 验证集索引号
  • idx_test:测试集索引号

分割数据集为:训练集(0,70%)共占70%,验证集(70%,85%)共占15%,测试集(85%,100%)共占15%。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值