本想整理一下学习图神经网络的知识点,但还比较零散,因此尝试解读一下比赛的baseline代码,将这几天学习的知识串联起来。虽然整个网络以及paddle的使用还不是很熟悉。
百度常规赛:论文引用网络节点分类
图神经网络(Graph Neural Network)是一种专门处理图结构数据的神经网络,目前被广泛应用于推荐系统、金融风控、生物计算等领域。图神经网络的经典问题主要有三类,分别为节点分类、连接预测和图分类。本次比赛旨在让参赛同学了解并掌握如何使用图神经网络处理节点分类问题
代码整体逻辑
1、读取提供的数据集,包含构图以及读取节点特征
2、配置化生成模型,用户也可以根据教程进行图神经网络的实现。
3、开始训练
4、执行预测并产生结果文件
代码学习
#每次代码重启后从这里开始运行,不然可能会和之前run出的数据混淆导致出错
import sys
sys.path.append('/home/aistudio/external-libraries')
#导入需要的包
import pgl
import paddle.fluid as fluid
import numpy as np
import time
import pandas as pd
from easydict import EasyDict as edict
#easydict的作用:可以使得以属性的方式去访问字典的值!
#当遇到一个陌生的python第三方库时,可以去pypi这个主页查看描述以迅速入门!
config = {
#这里在model.py中一定定义好完整的模型,自己写的模型也可以放在该文件中
"model_name": "GCN",
#只需要修改想要的参数即可
"num_layers": 1, #网络层数
"drop_out": 0.5, #参数50%的可能被drop,dropout一定程度上可以缓解过拟合
"learning_rate": 0.01, #学习率,当学习率过小时,收敛过程会十分缓慢;当学习率过大时,梯度可能会在最小值附近来回震荡,甚至可能无法收敛
"weight_decay": 0.0005, #权重衰减,为了让权重衰减到更小的值,减少模型过拟合的问题
"edge_dropout": 0.00, #边被drop的概率
}
config = edict(config)
*******************************************************************横插一刀,关于model_name
#model.py中对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)#传入的模型层数,默认为1
self.hidden_size = config.get("hidden_size", 64)#隐藏层节点数,与输入节点一起构造了权重
self.dropout = config.get("dropout", 0.5)#全连接层的drop率
self.edge_dropout = config.get("edge_dropout", 0.0)#边的drop率,当中心节点边非常多时,选取一部分边构成子图,然后进行聚合特征。默认选取所有边
def forward(self, graph_wrapper, feature, phase):
#传入的num_layers在此时设置循环次数来定义层数
for i in range(self.num_layers):
#训练模型时
if phase == "train":
ngw = pgl.sample.edge_drop(graph_wrapper, self.edge_dropout) #在图graph_wrapper中根据传入的edge_dropout随机忽略部分边,得到采样子图
norm = get_norm(ngw.indegree())#归一化出度
#验证或测试时
else:
ngw = graph_wrapper#子图就是原图,需要对原图进行验证测试
norm = graph_wrapper.node_feat["norm"]
#feature 更新子图的节点特征
feature = pgl.layers.gcn(ngw,
feature,#聚合中只对边进行修改
self.hidden_size, #输出的大小
activation="relu",#激活函数
norm=norm,#归一化
name="layer_%s" % i)#层 的名称
#drop掉部分特征(将一些神经元输出设为0),防止过拟合
#dropout_implementation='upscale_in_train'表示只在训练过程中执行
feature = L.dropout(
feature,
self.dropout,
dropout_implementation='upscale_in_train')
##通过num_layers层特征提取并dropout层防止过拟合————结束
#输出结果———edge_drop也只在训练过程中使用
if phase == "train":
ngw = pgl.sample.edge_drop(graph_wrapper, self.edge_dropout)
norm = get_norm(ngw.indegree())
else:
ngw = graph_wrapper
norm = graph_wrapper.node_feat["norm"]
#通过一层图卷积层获取特征
feature = conv.gcn(ngw,
feature,
self.num_class,
activation=None,
norm=norm,
name="output")
return feature
*******************************************************************回到正文
#数据加载模块——用于读取数据集,包括读取图数据构图,以及训练集的划分
from collections import namedtuple
#创建数据集,包括包含图、类别数、训练索引、训练标签、验证索引、验证标签、测试索引
Dataset = namedtuple("Dataset",
["graph", "num_classes", "train_index",
"train_label", "valid_index", "valid_label", "test_index"])
#加载边,节点数,增加自环(虽然论文不会自己引用自己),变为无向图
def load_edges(num_nodes, self_loop=True, add_inverse_edge=True):
# 从数据中读取边
edges = pd.read_csv("work/edges.csv", header=None, names=["src", "dst"]).values
if add_inverse_edge:
edges = np.vstack([edges, edges[:, ::-1]])
if self_loop:
src = np.arange(0, num_nodes)
dst = np.arange(0, num_nodes)
self_loop = np.vstack([src, dst]).T
edges = np.vstack([edges, self_loop])
return edges
def load():
# 从数据中读取点特征和边,以及数据划分
node_feat = np.load("work/feat.npy")
num_nodes = node_feat.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()#得到入度
norm = np.maximum(indegree.astype("float32"), 1)#得到最大入度数
norm = np.power(norm, -0.5)#利用最大值计算归一化
graph.node_feat["norm"] = np.expand_dims(norm, -1)#归一化值添加进节点特征
df = pd.read_csv("work/train.csv") #读入训练集
node_index = df["nid"].values
node_label = df["label"].values
train_part = int(len(node_index) * 0.8)#取80%用作训练
train_index = node_index[:train_part]
train_label = node_label[:train_part]
valid_index = node_index[train_part:]#剩余20%用作验证
valid_label = node_label[train_part:]
test_index = pd.read_csv("work/test.csv")["nid"].values#读入测试集
#得到包含训练集(索引和标签),验证集(索引和标签),测试集(索引)的数据集
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
未完待续