文章目录

  • 一、项目介绍
  • 二、BaseLine内容注解
    • 1.Config部分注解
    • 2. 数据读取与处理部分
      • 2.1 边数据的加载与处理
      • 2.2 数据的完整加载与处理
      • 2.3 数据读取与分割
    • 3. 模型加载部分
    • 4. 模型训练过程
  • 三、model.py的内容解析
    • 1 GCN模型代码讲解
    • 2. GAT模型代码讲解
    • 3. APPNP模型代码讲解
      • 3.1 SGC模型代码讲解——(APPNP模型补充)
    • 4. GCNII模型代码讲解
  • 四、build_model.py的内容解析
  • 五、总结

比赛地址:常规赛:论文引用网络节点分类
文章转载自:图网络笔记–论文节点比赛baseline代码注解,感谢红白黑大佬的笔记

一、项目介绍

  1. 数据分析处理部分–代码注解
  2. 模型加载–program空间配置部分–代码注解
  3. 训练部分–代码注解
  4. build_model.py 以及 model.py内容代码注解

二、BaseLine内容注解

  1. 注意,每一次调整使用的模型或者更管模型后,要记得重启notebook,避免运行失败或者运行上一次的模型等
    【ps: 重启运行时,可以跳过依赖下载,但请记得运行从import sys; sys.path.append(’/home/aistudio/external-libraries’)开始运行】

  2. 自定义模型时,要注意参考baseline重点例子,如何创建一个可用的模型【后边会说一下,大家不用担心,很简单的】

1.Config部分注解

from easydict import EasyDict as edict'''模块说明:easydict这个模块下的EasyDict,可以使得创建的字典像访问属性一样eg:dicts = {'A': 1}print(dicts['A'])  => 1使用EasyDict之后:dicts = EasyDict(dicts)print(dicts.A)  => 1
'''# 模型参数字典
config = {"model_name": "GCNII","num_layers": 1,           # 网络层数--这个实现在模型类的forward里边,通过循环实现"dropout": 0.5,            # 训练时,参数drop概率"learning_rate": 0.0002,    # 训练优化的学习率"weight_decay": 0.0005,    # 权重正则化率"edge_dropout": 0.00,      # 边drop概率
}config = edict(config)  # 利用EasyDict便利字典的读取

2. 数据读取与处理部分

2.1 边数据的加载与处理

# 加载边数据
def load_edges(num_nodes, self_loop=True, add_inverse_edge=True):'''input:num_nodes: 节点数self_loop: 是否加载自环边add_inverse_edge: 是否添加反转的边--我的理解是正反都添加--即对应无向图的情况'''# 从数据中读取边edges = pd.read_csv("work/edges.csv", header=None, names=["src", "dst"]).values# 反转边添加if add_inverse_edge:edges = np.vstack([edges, edges[:, ::-1]])  # vstack沿竖直方向拼接--如:A =[1, 2] , B = [2, 3]; vstack([A, B]) => [[1, 2], [2, 3]]# eg: edges=[[1, 3], [2, 5], [6, 7]] => edges[:, ::-1]=[[3, 1], [5, 2], [7, 6]]# 再拼接就得到了正反边的一个集合了# 自环边添加if self_loop:src = np.arange(0, num_nodes)          # 定义n和节点作为起点dst = np.arange(0, num_nodes)          # 定义n个节点作为终点--且与src一一对应self_loop = np.vstack([src, dst]).T    # 再将两个行向量拼接(此时shape:[2, num_nodes]), 然后再转置T=>得到shape:[num_node, 2]这是的数据0->0, 1->1 ...就得到了自环边的数据edges = np.vstack([edges, self_loop])  # 将自环边数据添加到本身的边数据中return edges

2.2 数据的完整加载与处理

def load():# 从数据中读取点特征和边,以及数据划分node_feat = np.load("work/feat.npy")    # 读取节点特征--每个节点100个特征num_nodes = node_feat.shape[0]          # shape[0] 正好对应节点个数edges = load_edges(num_nodes=num_nodes, self_loop=True, add_inverse_edge=True)   # 根据实际传入的节点数,返回合理的边--这里包含自环边以及正向和反向的边graph = pgl.graph.Graph(num_nodes=num_nodes, edges=edges, node_feat={"feat": node_feat})  # 创建图:节点数、边数据、以及节点特征的字典indegree = graph.indegree()    # 计算当前图的所有节点的入度--返回一个list==>等价于graph.indegree(nodes=None),nodes指定,返回指定的入度norm = np.maximum(indegree.astype("float32"), 1)  # 取最大入度中的一个然后返回norm = np.power(norm, -0.5)   # 利用这个最大入读计算一个归一化参数graph.node_feat["norm"] = np.expand_dims(norm, -1) # 将归一化参数添加到节点的norm特征中, shape[1], 只含有一个元素的序列,但不算标量:如,a 和 [a]df = pd.read_csv("work/train.csv")      # 读取总的训练数据node_index = df["nid"].values           # 读取总的节点的索引序列(集)node_label = df["label"].values         # 读取总的节点的label序列train_part = int(len(node_index) * 0.8) # 划分训练数据集--80%--这里是计算一个训练集数目值train_index = node_index[:train_part]   # 利用训练集数目进行划分--0:train_parttrain_label = node_label[:train_part]   # 训练label划分valid_index = node_index[train_part:]   # 验证数据valid_index划分valid_label = node_label[train_part:]   # 验证valid_label划分test_index = pd.read_csv("work/test.csv")["nid"].values  # 读取测试集--也就是赛题提交数据--指定读取['nid']列数据# 这是一个可以使用名字来访问元素内容的tuple子类# 所以直接对应传入数据即可dataset = Dataset(graph=graph, train_label=train_label,train_index=train_index,valid_index=valid_index,valid_label=valid_label,test_index=test_index, num_classes=35)return dataset # 最后返回dataset数据

2.3 数据读取与分割

这一部分的分割和load中的命名索引元组有关!

dataset = load()  # 执行load函数获取完整的dataset(可命名索引的tuple)数据# 从dataset中读取出相应数据
train_index = dataset.train_index                               # 读取训练索引序列
train_label = np.reshape(dataset.train_label, [-1 , 1])         # 读取训练label序列
train_index = np.expand_dims(train_index, -1)                   # 在最后一位添加一个维度,保证数据向量化[[a]]val_index = dataset.valid_index                                 # 读取验证索引序列
val_label = np.reshape(dataset.valid_label, [-1, 1])            # 读取训练label序列
val_index = np.expand_dims(val_index, -1)                       # 在最后一位添加一个维度,保证数据向量化[[a]]test_index = dataset.test_index                                 # 读取训练索引序列
test_index = np.expand_dims(test_index, -1)                     # 在最后一位添加一个维度,保证数据向量化[[a]]
test_label = np.zeros((len(test_index), 1), dtype="int64")      # 用于保存最终结果--提前用zeros创建一个空白矩阵,并指明数据类型

3. 模型加载部分

import pgl
import model      # model.py
import paddle.fluid as fluid
import numpy as np
import time
from build_model import build_model  # build_model.pyuse_gpu = True
place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()   # 工作环境--这里有修改--需要用cpu只需要把use_gpu设置为False即可train_program = fluid.default_main_program()                # 创建主program  -- paddle静态图都是在相应的program中运行的 -- 通常为start
startup_program = fluid.default_startup_program()           # 创建start_program -- 是我们运行的开始# 以下是配置执行器执行空间(block)的操作部分--个人觉得,如果只是使用,记住使用规范即可
# program_guard接口配合使用python的 with 语句来将 with block 里的算子和变量添加进指定的全局主程序(main program)和启动程序(startup program)。
with fluid.program_guard(train_program, startup_program):   # 以下执行的算子等都会放入到train_program->startup_program的block中with fluid.unique_name.guard():   # 开启一个命名空间--常用program_guard一起使用# 这里使用到build_model.py中的函数,执行模型和参数的配置,并返回相关的data变量# 这个过程的算子都会被记录到(train_program, startup_program)对应的工作空间中gw, loss, acc, pred = build_model(dataset,   config=config,phase="train",main_prog=train_program)# 创建一个新的Program作为test_program
test_program = fluid.Program()
with fluid.program_guard(test_program, startup_program):   # 含义如上,这里是开启(test_program, startup_program)的工作空间,并记录相应的算子、变量with fluid.unique_name.guard():                        # 开启一个命名空间# 返回test的模型参数等 _gw, v_loss, v_acc, v_pred = build_model(dataset,config=config,phase="test",main_prog=test_program)# 总结——program_guard确定工作环境--unique_name开启一个相应的命名空间,相辅相成。test_program = test_program.clone(for_test=True)  # 克隆test_programexe = fluid.Executor(place)   # 创建一个解释器

4. 模型训练过程

epoch = 4000                       # 训练轮次
exe.run(startup_program)           # 执行器运行-->优先执行# 将图数据变成 feed_dict 用于传入Paddle Excecutor
# 图数据原型:graph = pgl.graph.Graph(num_nodes=num_nodes, edges=edges, node_feat={"feat": node_feat})  # 创建图:节点数、边数据、以及节点特征的字典
feed_dict = gw.to_feed(dataset.graph)    # 调用to_feed方法,将图数据转换为feed_dict,用于执行器的输入参数# 训练开始
for epoch in range(epoch):# Full Batch 训练  ==  单batch_size训练--全数据一次投入# 设定图上面那些节点要获取# node_index: 训练节点的nid    # node_label: 训练节点对应的标签feed_dict["node_index"] = np.array(train_index, dtype="int64")   # 往feed_dict中添加键值对数据--每一个轮次数据都会重新赋值更新feed_dict["node_label"] = np.array(train_label, dtype="int64")train_loss, train_acc = exe.run(train_program,      # 执行器执行--执行train_program这个program空间内的算子和参数feed=feed_dict,         # 传入的数据:graph..., node_index, node_labelfetch_list=[loss, acc], # 需要计算返回的数据return_numpy=True)      # 返回numpy数据# Full Batch 验证  ==  单batch_size验证--全数据一次投入# 设定图上面那些节点要获取# node_index: 训练节点的nid    # node_label: 训练节点对应的标签feed_dict["node_index"] = np.array(val_index, dtype="int64")   # 往feed_dict中添加键值对数据--每一个轮次数据都会重新赋值更新feed_dict["node_label"] = np.array(val_label, dtype="int64")val_loss, val_acc = exe.run(test_program,            # 执行器执行--执行test_program这个program空间内的算子和参数feed=feed_dict,              # 传入的数据:graph..., node_index, node_labelfetch_list=[v_loss, v_acc],  # 需要计算返回的数据return_numpy=True)           # 返回numpy数据print("Epoch", epoch, "Train Acc", train_acc[0], "Valid Acc", val_acc[0])   # 打印训练数据

三、model.py的内容解析

1 GCN模型代码讲解

GCN需要用到的一个计算归一参数的方法

def get_norm(indegree):'''入度归一化函数: 返回一个浮点数类型的最小值为1.0的入度值序列入度值:可以表示无向图中当前节点的邻边,而对于有向图则是指向当前节点的边数'''float_degree = L.cast(indegree, dtype="float32")  # data的类型转换后的值返回给float_degree,值返回float_degree = L.clamp(float_degree, min=1.0)     # 值裁剪--将其中小于1的值赋值为1.0 -->个人的考虑是,添加自环边的入度norm = L.pow(float_degree, factor=-0.5)           # 倒数开根号,获取归一化的入度# TODO: 度为float类型?# CALL: 为了后边方便用于计算,float数据更适合后边所需的运算return norm  # 返回一个归一化的度,用于公式计算

GCN模型代码注解

class GCN(object):"""Implement of GCN"""def __init__(self, config, num_class):self.num_class = num_class                          # 节点种类self.num_layers = config.get("num_layers", 1)       # 模型层数self.hidden_size = config.get("hidden_size", 64)    # 中间层输出大小--不一定只有一层中间层哈--跟num_layers有关self.dropout = config.get("dropout", 0.5)           # fc层的drop率self.edge_dropout = config.get("edge_dropout", 0.0) # 边drop率--为了获取一个忽略指定数目的随机子图(忽略部分边属性,然后生成一个新的子图用于训练--仅仅用于训练而已)def forward(self, graph_wrapper, feature, phase):'''graph_wrapper: 一个图容器,用于存储图信息,并可以迭代训练与预测feature:图的节点特征phase:指令--train or eval or test等功能:实现将输入的图进行一个简单的处理--通过n层图卷积实现特征提取,然后经过一个dropout层抑制过拟合;最后经过第二个图卷积获取类别数的输出,根据相应的处理得到需要的预测结果。如:softmax进行一个类别处理,利用argmax的到分类的类别【具体过程详见build_model.py】'注意: 在GCN中,需要计算一个norm值,用于GCN的推断训练,以及后期的预测【详见GCN推导的公式】''''# GCN这个layer的返回值:张量shape:(num_nodes,hidden_​​size)for i in range(self.num_layers):  # 根据层数进行迭代if phase == "train":  # 训练模式--才有边drop# 每次调用edge_drop(graph_wrapper, self.edge_dropout)结果可能不同ngw = pgl.sample.edge_drop(graph_wrapper, self.edge_dropout)   # 传入输入图,然后根据edge_dropout随机生成忽略某些边属性的新子图norm = get_norm(ngw.indegree())   # 归一化出度--得到计算参数else:  # eval/test模式ngw = graph_wrapper   # 新子图就是原始图norm = graph_wrapper.node_feat["norm"]  # # 利用pgl自带的网络进行配置feature = pgl.layers.gcn(ngw,    # 传入图feature, # 相应的特征--训练过程中,最多只是对边有修改,并不涉及节点变化self.hidden_size,  # 输出大小activation="relu", # 激活函数norm=norm,         # 归一化值--用于gcn公式计算name="layer_%s" % i)  # 层名称# 在此后紧跟dropout进行,防止过拟合# 根据给定的丢弃概率,dropout操作符按丢弃概率随机将一些神经元输出设置为0,其他的仍保持不变。feature = L.dropout(   feature,      # 上一级的输出self.dropout, # drop率dropout_implementation='upscale_in_train')  # drop配置upscale_in_train表示,仅在训练时drop,评估预测不实现drop# 将以上迭代部分做完后,再通过下边这个部分输出结果if phase == "train":   # 训练模式下ngw = pgl.sample.edge_drop(graph_wrapper, self.edge_dropout)  # 同前边一样的过滤一些边--基本效果同普通的dropout,这里作用于边而已norm = get_norm(ngw.indegree())else:ngw = graph_wrappernorm = graph_wrapper.node_feat["norm"]# 再通过一层图卷积层feature = conv.gcn(ngw,  feature,self.num_class,   # 输出结果就是我们实际节点训练或预测输出的类别情况:详见PS1activation=None,norm=norm,name="output")    # 最后返回一个shape[-1]=num_class的数据,然后我们对数据处理只需要经过一个softmax,再argmax就得到了预测得到了节点的类别了return feature

2. GAT模型代码讲解

class GAT(object):"""Implement of GAT"""def __init__(self, config, num_class):self.num_class = num_class   # 类别数self.num_layers = config.get("num_layers", 1)   # 层数self.num_heads = config.get("num_heads", 8)   # 多头注意力 -- 8*8尽量别改self.hidden_size = config.get("hidden_size", 8)  # 中间层输出大小--不一定只有一层中间层哈--跟num_layers有关 self.feat_dropout = config.get("feat_drop", 0.6)  # 特征drop率self.attn_dropout = config.get("attn_drop", 0.6)    # 参数drop率self.edge_dropout = config.get("edge_dropout", 0.0)    # 边drop率def forward(self, graph_wrapper, feature, phase):'''graph_wrapper: 一个图容器,用于存储图信息,并可以迭代训练与预测feature:图的节点特征phase:指令--train or eval or test等功能:首先根据运行模式,确定edge_drop率然后进入网络叠加的循环中,进行pgl.sample.edge_drop后的子图获取,接着通过一个gat的layer--头尾8,输出大小为8,得到可叠加的特征输出循环结束后,再通过一个头为1,输出大小为num_class的gat,得到输出结果根据相应的处理得到需要的预测结果。如:softmax进行一个类别处理,利用argmax的到分类的类别  【具体过程详见build_model.py】'''if phase == "train":   # 训练模式才会进行边dropedge_dropout = self.edge_dropoutelse:edge_dropout = 0# 在GAT中,只需要简单进行遍历层叠加即可# GAT这个layer的返回值:张量shape:(num_nodes,hidden_​​size * num_heads)for i in range(self.num_layers):   # 遍历num_layers层网络ngw = pgl.sample.edge_drop(graph_wrapper, edge_dropout)   # 随机边drop# gat网络layerfeature = conv.gat(ngw,                             # 传入图容器--传入模型中的都不是简单的图,而是经过pgl中对应的图容器(不是pgl.Graph哦)feature,                        # 特征参数--节点特征self.hidden_size,               # 输出大小activation="elu",               # 激活函数name="gat_layer_%s" % i,        # nameednum_heads=self.num_heads,       # 头数feat_drop=self.feat_dropout,    # 特征drop率attn_drop=self.attn_dropout)    # 参数drop率# 最后再通过一层实现结果输出ngw = pgl.sample.edge_drop(graph_wrapper, edge_dropout) feature = conv.gat(ngw,        # 图feature,          # 特征参数--节点特征self.num_class,   # 输出大小为类别数--用于预测num_heads=1,      # 头数变为1activation=None,  # 不需要激活函数feat_drop=self.feat_dropout, # 特征drop率attn_drop=self.attn_dropout, # 参数drop率name="output")return feature     # 返回预测结果

3. APPNP模型代码讲解

# 新网络学习-APPNP
class APPNP(object):"""Implement of APPNP"""def __init__(self, config, num_class):self.num_class = num_class                          # 类别数self.num_layers = config.get("num_layers", 1)       # 层数self.hidden_size = config.get("hidden_size", 64)    # 中间层输出大小--不一定只有一层中间层哈--跟num_layers有关self.dropout = config.get("dropout", 0.5)           # drop率——指的是fc层中用到的dopr率self.alpha = config.get("alpha", 0.1)               # alpha值---用于公式计算_论文中的超参数self.k_hop = config.get("k_hop", 10)                # k_hop值---网络传播次数self.edge_dropout = config.get("edge_dropout", 0.0) # 边drop率def forward(self, graph_wrapper, feature, phase):'''graph_wrapper: 一个图容器,用于存储图信息,并可以迭代训练与预测feature:图的节点特征phase:指令--train or eval or test等功能:首先根据运行模式,确定edge_drop率然后进入循环遍历叠加网络层,这里不同于之前的网络---这里叠加的是fc层和drop操作--先drop,后fcAPPNP仅仅一个--并且由于APPNP层无法改变中间层大小,所以在传入前要把对应的feature转换为跟num_class相关的大小根据相应的处理得到需要的预测结果。如:softmax进行一个类别处理,利用argmax的到分类的类别  【具体过程详见build_model.py】'''if phase == "train":    # 训练模式才会进行边dropedge_dropout = self.edge_dropoutelse:edge_dropout = 0# APPNP比较特殊,这里的num_layers层数是指前层网络fc的深度,而不是直接叠加APPNP层for i in range(self.num_layers):feature = L.dropout(feature,        # 需要drop的特征self.dropout,   # drop率dropout_implementation='upscale_in_train')   # 训练时drop,非训练不dropfeature = L.fc(feature, self.hidden_size, act="relu", name="lin%s" % i)# 完成上述操作后,再重复一次相同的操作,最后调整输出为num_classfeature = L.dropout(feature,self.dropout,dropout_implementation='upscale_in_train')feature = L.fc(feature, self.num_class, act=None, name="output")  # 为appnp做好准备# APPNP这个layer的返回值:张量:shape(num_nodes,hidden_​​size)# 不能修改输出的new_hidden_​​size,只能使用传入的feature的数据形状hidden_​​size(num_class)feature = conv.appnp(graph_wrapper,   # 传入图容器feature=feature,                  # 特征--节点特征edge_dropout=edge_dropout,        # 边drop率alpha=self.alpha,                 # alpha值_论文中的超参数k_hop=self.k_hop)                 # 传播次数————这个部分太大,会显存爆炸哈return feature

3.1 SGC模型代码讲解——(APPNP模型补充)

# 单APPNP网络
class SGC(object):"""Implement of SGC"""def __init__(self, config, num_class):self.num_class = num_class                      # 类别数self.num_layers = config.get("num_layers", 1)   # 层数def forward(self, graph_wrapper, feature, phase):'''graph_wrapper: 一个图容器,用于存储图信息,并可以迭代训练与预测feature:图的节点特征phase:指令--train or eval or test等功能:直接将图容器传入appnp层中,不经过任何处理,也不进行任何drop然后再经过fc层得到合适形状的输出根据相应的处理得到需要的预测结果。如:softmax进行一个类别处理,利用argmax的到分类的类别  【具体过程详见build_model.py】'''# APPNP这个layer的返回值:张量:shape(num_nodes,hidden_​​size)# 这里的hidden_​​size是输入feature的最低维度大小feature = conv.appnp(graph_wrapper,feature=feature,edge_dropout=0,   # drop为零alpha=0, # 论文中的超参数k_hop=self.num_layers)feature.stop_gradient=True  # 在这里停止梯度计算--也就是之后的运算计算相应的梯度,用于优化feature = L.fc(feature, self.num_class, act=None, bias_attr=False, name="output")  # 转换形状输出即可return feature

4. GCNII模型代码讲解

# 新网络模型学习——GCNII
class GCNII(object):"""Implement of GCNII"""def __init__(self, config, num_class):self.num_class = num_class                          # 类别数self.num_layers = config.get("num_layers", 1)       # 层数self.hidden_size = config.get("hidden_size", 64)    # 中间层输出大小--不一定只有一层中间层哈--跟num_layers有关self.dropout = config.get("dropout", 0.6)           # drop率——既是fc的,也是GCNII的dropself.alpha = config.get("alpha", 0.1)               # alpha值——论文中的超参数self.lambda_l = config.get("lambda_l", 0.5)         # labda_l值——论文中的超参数self.k_hop = config.get("k_hop", 64)                # 传播次数self.edge_dropout = config.get("edge_dropout", 0.0) # 边drop率def forward(self, graph_wrapper, feature, phase):'''graph_wrapper: 一个图容器,用于存储图信息,并可以迭代训练与预测feature:图的节点特征phase:指令--train or eval or test等功能:首先根据运行模式,确定edge_drop率然后进入循环遍历叠加网络层,这里不同于之前的网络---这里叠加的是fc层和drop操作--先fc,再dropGCNII仅仅一个--并且由于GCNII层无法改变中间层大小,所以计算后要把对应的feature转换为跟num_class相关的大小--又要再次利用fc来完成根据相应的处理得到需要的预测结果。如:softmax进行一个类别处理,利用argmax的到分类的类别  【具体过程详见build_model.py】'''if phase == "train":  # 训练模式才会进行边dropedge_dropout = self.edge_dropoutelse:edge_dropout = 0# GCNII也比较特殊,这里的num_layers层数指的是前层网络fc的深度,而不是直接叠加GCNII层for i in range(self.num_layers):feature = L.fc(feature, self.hidden_size, act="relu", name="lin%s" % i)   # 跟APPNP相比--GCNII先经过fc再通过dropoutfeature = L.dropout(feature,self.dropout,dropout_implementation='upscale_in_train')  # 训练时drop,否则不drop# GCNII这个layer的返回值:张量shape: (num_nodes, hidden_size)# GCNII也不能改变特征输出的大小feature = conv.gcnii(graph_wrapper,  # 图容器feature=feature,                 # 特征数据name="gcnii",                    # namedactivation="relu",               # 激活函数lambda_l=self.lambda_l,          # 论文中的超参数--用于内部公式计算alpha=self.alpha,                # 论文中的超参数dropout=self.dropout,            # drop率k_hop=self.k_hop)                # 传播次数feature = L.fc(feature, self.num_class, act=None, name="output")  # 再经过fc获得指定大小的输出return feature

四、build_model.py的内容解析

import pgl
import model
from pgl import data_loader
import paddle.fluid as fluid
import numpy as np
import time'''build_model整个流程的说明:1. 首先明确传入参数(dataset, config, phase, main_prog)1. dataset: 一个简单的图2. config: 配置参数1. 包括模型名称,以及相关的初始化参数--根据自己的模型配置就好3. phase: 工作指令--train-训练模式,其它为非训练模式2. 主要工作流程1. 首先将传入的图放入一个图容器,此时传入图和节点特征即可2. 利用python自带的getattr读取model.py中的类,并返回这个类3. 利用返回的类实例一个模型这后边就涉及静态的参数创建了:4. 将图容器传入以及其它对于模型的forward必须的参数--得到一个返回值--logits,这个输出信息用于预测等  -- logits是经过模型层返回的,也是一个data5. 创建一些训练和预测所必须的参数--node_index: 节点索引集【需回到notebook中对照理解】;node_label,用于计算acc,loss等【注意,这里涉及到模型返回的参数也好,其它的loss以及node_index、node_label都是一个静态图下的data,要通过build_model返回之后,经过执行器运行时才会有实际的值】6. 接着创建loss方法以及返回loss_data----以及添加acc方法,计算acc_data7. 添加一个softmax获取实际类别8. 接着平均化损失9. 如果是训练模式,还会单独添加优化器,返回优化器对象,并优化参数**: 切记,这里使用方法创建的变量都是静态图中的data,需要放入执行器中运行才有实际的意义
'''def build_model(dataset, config, phase, main_prog):'''dataset: 就是一个图config: 来自以下代码from easydict import EasyDict as edictconfig = {"model_name": "APPNP","num_layers": 3,"dropout": 0.5,"learning_rate": 0.0002,"weight_decay": 0.0005,"edge_dropout": 0.00,}config = edict(config)main_prog:执行器对象(program)'''gw = pgl.graph_wrapper.GraphWrapper(name="graph",node_feat=dataset.graph.node_feat_info())  # 创建图容器GraphModel = getattr(model, config.model_name)    # 获取model中关于config.model_name指定的模型配置--即在model.py中,getattr获取的对象属性就是相应的模型类m = GraphModel(config=config, num_class=dataset.num_classes)   # 利用返回的模型类,实例一个对象--传入配置信息,以及节点类别数(用于预测分类)logits = m.forward(gw, gw.node_feat["feat"], phase)   # 调用模型对象,进行前向计算--传入图,节点特征,执行指令--phase为train或者false# 补充说明:m.forward得到的是一个shape为[batch, num_class]的序列--后边用于softmax处理再进行类别获取# Take the last# 创建节点datanode_index = fluid.layers.data("node_index",shape=[None, 1],dtype="int64",append_batch_size=False)# 创建节点标签datanode_label = fluid.layers.data("node_label",shape=[None, 1],  # 【batch,1】dtype="int64",append_batch_size=False)# 根据索引 node_index 获取输入logits的最外层维度的条目,并将它们拼接在一起# 即: eg: node_index=[1, 2], logits=[[1, 2], [2, 3], [3, 4]]#    那么拼接的对象就是——[1, 2], [2, 3], [3, 4]#    然后根据node_index索引拼接,选择[2, 3], [3, 4]进行拼接,然后维数不变的返回[[2, 3], [3, 4]]pred = fluid.layers.gather(logits, node_index)        # node_index是一个data,暂时是不作用的,要等exe执行器运行时才会传入信息---这里最后作用的结果就是根据index索引相应的值loss, pred = fluid.layers.softmax_with_cross_entropy(logits=pred, label=node_label, return_softmax=True)    # 输入pred, 与node_label,进行交叉熵计算--并返回通过sortmax的pred数据和lossacc = fluid.layers.accuracy(input=pred, label=node_label, k=1)   # 准确率计算pred = fluid.layers.argmax(pred, -1)   # 利用argmax确定具体的类别loss = fluid.layers.mean(loss)         # 计算平均损失if phase == "train":   # 训练模式才进行优化# Adam优化器:利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率adam = fluid.optimizer.Adam(learning_rate=config.learning_rate,   # 学习率regularization=fluid.regularizer.L2DecayRegularizer(    # L2权重衰减正则化regularization_coeff=config.weight_decay))          # 正则化系数--就是我们在notbook中config设置的weight_decayadam.minimize(loss)   # 通过minimize获取实际损失--即loss_all / batchsizereturn gw, loss, acc, pred  # 返回训练后的图、损失、精度、预测data

五、总结

训练少许经验:

  1. APPNP这个网络,貌似直接使用表现不是很好–我这边的话是这样;当然改一改可能会有奇迹,谁知道呢?对吧!
    【炼丹就要无所畏惧——时间这东西,那是来着不拒(●ˇ∀ˇ●)】
  2. 关于后边两个model中最后两个模型,k_hop这个参数,设计的大的化可能会导致显存溢出哦?虽然对精度有些提升但是也要看自身想要什么结果啦!
    【我这里的话,GCNII——实际两层, “k_hop”, 64, “hidden_size”, 64; 就会爆显存哦–notebook:16G——GPU】

百度图神经网络——论文节点比赛baseline代码注解相关推荐

  1. 图神经网络基础--基于图神经网络的节点表征学习

    图神经网络基础–基于图神经网络的节点表征学习 引言 在图节点预测或边预测任务中,首先需要生成节点表征(Node Representation).我们使用图神经网络来生成节点表征,并通过基于监督学习的对 ...

  2. 基于图神经网络的节点表征

    我们使用图神经网络来生成节点表征,并通过基于监督学习的对图神经网络的训练,使得图神经网络学会产生高质量的节点表征.高质量的节点表征能够用于衡量节点的相似性,同时高质量的节点表征也是准确分类节点的前提. ...

  3. 图神经网络/GNN(三)-基于图神经网络的节点表征学习

    Task3概览: 在图任务当中,首要任务就是要生成节点特征,同时高质量的节点表征也是用于下游机器学习任务的前提所在.本次任务通过GNN来生成节点表征,并通过基于监督学习对GNN的训练,使得GNN学会产 ...

  4. 干货!Labeling Trick: 一个图神经网络多节点表示学习理论

    点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! GNN 旨在学习单节点表示.当我们想要学习一个涉及多个节点的节点集表示(如链路表示)时,以往工作中的一个常见做法是将 GNN 学习到的多 ...

  5. 业界分享 | 百度图神经网络实践

    作者 | 黄正杰 来源 | DataFunTalk 图是一个复杂世界的通用语言,社交网络中人与人之间的连接.蛋白质分子.推荐系统中用户与物品之间的连接等等,都可以使用图来表达.图神经网络将神经网络运用 ...

  6. 百度图神经网络7日打卡营--DAY01前半部分 总结

    首先很感谢百度AI团队这个平台,给大家请来世界级冠军来给大家做这一次的7日打卡营活动, 还提供免费的GPU算力平台,以及成熟可用的 包含刷新目前 最权威的 图神经ORB榜单的SOTA模型的 PGL 图 ...

  7. 最新图神经网络论文笔记汇总(附pdf下载)

    点击上方,选择星标或置顶,不定期资源大放送! 阅读大概需要15分钟 Follow小博主,每天更新前沿干货 [导读]近年来,图神经网络变得非常火热,每年顶会在该领域内都会出现大量的研究论文,本文为大家提 ...

  8. 【NIPS 2016图神经网络论文解读】Variational Graph Auto-Encoders (VGAE) 基于VAE的图变分自编码器

    写在前面 论文题目:Variational Graph Auto-Encoders 论文作者:Thomas N. Kipf, Max Welling 论文组织:University of Amster ...

  9. 门控图神经网络(GGNN)及代码分析

    门控图神经网络GGNN及代码分析 基本概念 GGNN是一种基于GRU的经典的空间域message passing的模型 问题描述 一个图 G = (V, E), 节点v ∈ V中存储D维向量,边e ∈ ...

最新文章

  1. 苹果电脑+VR头显不久将成为现实,macOS新系统正式上线
  2. 防重复请求处理的实践与总结
  3. C语言中extern的用法--转
  4. java date sql和_Java.util.date 与 java.sql.date区别和转换
  5. docker images 详解
  6. acf滞后数必须为正整数。_【知识点】“勾股定理”的必考点,必须掌握!
  7. 广播模块加继电器怎么接线_变频器如何与PLC相连接,怎么用PLC控制?
  8. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第36篇]Index Calculus算法
  9. python中html.replace()_HTML DOM replace() 方法
  10. Python-视频爬取示例对小白友好
  11. python tkinter画动态时钟
  12. 图解CSS3----盒子模型
  13. 航空三字代码表_航空公司三字代码表
  14. 我要彻底搞懂SSD网络结构(1)VGG部分
  15. 微信小程序报错40163-“errmsg“解决方案
  16. PHP加密如何保护php源码不被破解不被轻易去授权
  17. Windows 使用命令行查看 wifi 密码
  18. 字符串的倒叙输出(直接倒叙和单词倒叙)
  19. 链路追踪Zipkin
  20. 3D动作绑定_3D动漫制作软件,你知道几个?

热门文章

  1. 26.如何使用python操作我们自己创建的docker image呢?
  2. openstack中彻底删除计算节点的操作记录
  3. html使用自我知识点总结
  4. [转载]压岁钱年年涨的行情不要太纠结
  5. OCS2007R2部署之四部署存档和监控服务器
  6. 剑指offer(C++)-JZ78:把二叉树打印成多行(数据结构-树)
  7. OpenCV-绘制简易直方图DrawHistImg
  8. java web 的map_javaweb开发过程中小工具系列之将Map转换成对象
  9. else列表推导式 if python_python3基础09列表推导式|迭代器|生成器|匿名函数
  10. 字体选择_Word文档中的字体批量选择与更改,查找替换功能必杀技