Reproducción en papel GammaGL - tomando ChebNet como ejemplo

1. Cree los archivos necesarios

  1. Cree una carpeta de entrenamiento de modelos en la carpeta exmaples——chebnetgcn, para pruebas y entrenamiento, y cree el chebnetgcn_trainner.py correspondiente y los archivos redme correspondientes en la carpeta

imagen-20220518141848593

  1. Cree el archivo de modelo chebnet_gcn.py correspondiente en la carpeta gammagl.models para escribir el modelo ChebNet. Al mismo tiempo , agregue el archivo models._init_.pyfrom .chebnet_gcn import ChebNet

    imagen-20220518141918045

  2. Debido a que el chebconv correspondiente se utilizará en el modelo ChebNet, cree el archivo de capa convolucional cheb_conv.py correspondiente en la carpeta gammagl.layers.conv para escribir la capa convolucional chebconv. Al mismo tiempo, agregue el archivo conv._ init _.pyfrom .cheb_conv import ChebConv

    imagen-20220516170548541

Los anteriores son los tres archivos que necesitamos usar, y principalmente escribiremos estos tres archivos más adelante para realizar la reproducción del modelo en papel. A continuación, reproducimos el modelo según el principio de arriba hacia abajo

2. Escribe el archivo de entrenamiento

2.1 Importar el paquete correspondiente y la configuración del entorno

import os

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
# os.environ['TL_BACKEND'] = 'torch'
# os.environ['TL_BACKEND'] = 'mindspore'
# os.environ['TL_BACKEND'] = 'paddle'
os.environ['TL_BACKEND'] = 'tensorflow'  # set your backend here, default `tensorflow`
# os.environ["CUDA_VISIBLE_DEVICES"] = "0"
import sys

sys.path.insert(0, os.path.abspath('../../'))  # adds path2gammagl to execute in command line.
import argparse
import tensorlayerx as tlx  # 导入tensorlayerx包
from gammagl.datasets import Planetoid  # 专门做读取数据的类Planetoid
from gammagl.models import ChebNet  # 导入自己写的ChebNet模型类
from tensorlayerx.model import TrainOneStep, WithLoss  # 这是tensorlayerx用来训练与计算误差的两个函数

2.2 Configuración de parámetros de funcionamiento

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--k", type=int, default=2, help="the k of every chebconv")
    parser.add_argument("--lr", type=float, default=0.01, help="learnin rate")
    parser.add_argument("--n_epoch", type=int, default=200, help="number of epoch")
    parser.add_argument("--hidden_dim", type=int, default=16, help="dimention of hidden layers")
    parser.add_argument("--drop_rate", type=float, default=0.5, help="drop_rate")
    parser.add_argument("--l2_coef", type=float, default=5e-4, help="l2 loss coeficient")
    parser.add_argument('--dataset', type=str, default='cora', help='dataset')
    parser.add_argument("--dataset_path", type=str, default=r'../', help="path to save dataset")
    parser.add_argument("--best_model_path", type=str, default=r'./', help="path to save best model")
    parser.add_argument("--self_loops", type=int, default=1, help="number of graph self-loop")
    args = parser.parse_args()

    main(args)

2.3 Clase de función de pérdida de escritura

class SemiSpvzLoss(WithLoss):
    def __init__(self, net, loss_fn):
        super(SemiSpvzLoss, self).__init__(backbone=net, loss_fn=loss_fn)

    def forward(self, data, label):
        logits = self._backbone(data['x'], data['edge_index'], data['edge_weight'], data['num_nodes'])
        if tlx.BACKEND == 'mindspore':
            idx = tlx.convert_to_tensor([i for i, v in enumerate(data['train_mask']) if v], dtype=tlx.int64)
            train_logits = tlx.gather(logits, idx)
            train_label = tlx.gather(label, idx)
        else:
            train_logits = logits[data['train_mask']]
            train_label = label[data['train_mask']]
        loss = self._loss_fn(train_logits, train_label)
        return loss

2.4 Escribir la función de verificación

def evaluate(net, data, y, mask, metrics):
    net.set_eval()
    logits = net(data['x'], data['edge_index'], data['edge_weight'], data['num_nodes'])
    if tlx.BACKEND == 'mindspore':
        idx = tlx.convert_to_tensor([i for i, v in enumerate(mask) if v], dtype=tlx.int64)
        _logits = tlx.gather(logits, idx)
        _label = tlx.gather(y, idx)
    else:
        _logits = logits[mask]
        _label = y[mask]
    metrics.update(_logits, _label)
    acc = metrics.result()
    metrics.reset()
    return acc

2.3 Función principal de entrenamiento de escritura

def main(args):
    # 1. 加载数据集
    if str.lower(args.dataset) not in ['cora', 'pubmed', 'citeseer']:
        raise ValueError('Unknown dataset: {}'.format(args.dataset))
    dataset = Planetoid(args.dataset_path, args.dataset)
    dataset.process()
    graph = dataset[0]
    graph.tensor()
    # 2. 获取图的结构数据,这里是无向图,所以图结构主要有edge_index与edge_weight
    edge_index = graph.edge_index  # 记录了哪些节点存在边
    edge_weight = tlx.ones((edge_index.shape[1],))  # 记录了每天边的权重值,相当于拉普拉斯矩阵的A_ij
    x = graph.x  # 记录每一个节点的特诊值
    y = tlx.argmax(graph.y, axis=1)  # 每一个节点的label
    # 3. 构造ChebNet模型对象
    net = ChebNet(feature_dim=x.shape[1],
                  k=args.k,
                  drop_rate=args.drop_rate,
                  name="ChebNet")
    # 4. 选择训练的优化器
    optimizer = tlx.optimizers.Adam(lr=args.lr, weight_decay=args.l2_coef)
    metrics = tlx.metrics.Accuracy()
    train_weights = net.trainable_weights
    # 5. 设置损失函数
    loss_func = SemiSpvzLoss(net, tlx.losses.softmax_cross_entropy_with_logits)
    # 6. 创建TrainOneStep模型训练对象
    train_one_step = TrainOneStep(loss_func, optimizer, train_weights)

    data = {
    
    
        "x": x,
        "edge_index": edge_index,
        "edge_weight": edge_weight,
        "train_mask": graph.train_mask,
        "test_mask": graph.test_mask,
        "val_mask": graph.val_mask,
        "num_nodes": graph.num_nodes,
    }
    # 7.模型训练
    best_val_acc = 0  # 记录最好的训练结果
    for epoch in range(args.n_epoch):
        net.set_train()  # 设置模型当前为训练模式,如果模型种有dropout操作,这是必须的
        train_loss = train_one_step(data, y)  # 调用train_one_step进行训练
        val_acc = evaluate(net, data, y, data['val_mask'], metrics)  # 用验证集评估模型训练结果
        # 打印当前epoch的训练结果
        print("Epoch [{:0>3d}] ".format(epoch + 1) + "  train loss: {:.4f}".format(
            train_loss.item()) + "  val acc: {:.4f}".format(val_acc))
        # 保存在验证集中表现最好的模型
        if val_acc > best_val_acc:
            best_val_acc = val_acc
            net.save_weights(args.best_model_path + net.name + ".npz", format='npz_dict')
    # 使用最好的模型对测试集进行评测
    net.load_weights(args.best_model_path + net.name + ".npz", format='npz_dict')
    test_acc = evaluate(net, data, y, data['test_mask'], metrics)
    print("Test acc:  {:.4f}".format(test_acc))

3. Escribe el archivo del modelo.

Aquí usamos principalmente tensorlayerx para encapsular un modelo ChebNet, que no es muy diferente del modelo de convolución normal. La principal diferencia es que la capa de convolución se selecciona de manera diferente, por lo que la capa de convolución aquí es el archivo cheb_conv.py que escribimos más tarde. .

from tensorlayerx.nn import Module
import tensorlayerx as tlx
from gammagl.layers.conv.cheb_conv import ChebConv


class ChebNet(Module):
    def __init__(self, feature_dim, k, drop_rate=0.5, name=None):
        super().__init__()
        self.conv1 = ChebConv(feature_dim, 16, K=k)
        self.conv2 = ChebConv(16, feature_dim, K=k)
        self.relu = tlx.ReLU()
        self.dropout = tlx.layers.Dropout(drop_rate)
        self.name = name

    def forward(self, x, edge_index, edge_weight, num_nodes):
        x = self.conv1(x, edge_index, num_nodes, edge_weight)
        x = self.relu(x)
        x = self.dropout(x)
        x = self.conv2(x, edge_index, num_nodes, edge_weight)
        return tlx.nn.Softmax(1)(x)


4. Escribir el archivo de capa convolucional

La capa convolucional es el núcleo del documento. Para obtener la fórmula de cálculo específica de ChebConv, consulte el apéndice.

4.1 Escriba el método de inicialización de la red

def __init__(self, in_channels: int, out_channels: int, K: int, normalization: Optional[str] = 'sym',
             bias: bool = True, **kwargs):
    kwargs.setdefault('aggr', 'add')
    super(ChebConv, self).__init__()

    assert K > 0  # K值检查
    assert normalization in [None, 'sym', 'rw'], 'Invalid normalization'  # 正则化方法规则检查
    W_init = tlx.nn.initializers.truncated_normal(stddev=0.05)  # 初始化W
    b_init = tlx.nn.initializers.constant(value=0.1)  # 初始化b
    self.in_channels = in_channels
    self.out_channels = out_channels
    self.normalization = normalization
    self.lins = tlx.nn.ModuleList([
        tlx.layers.Linear(in_features=in_channels, out_features=out_channels, W_init=W_init, b_init=b_init) for _ in
        range(K)
    ])  # K层的线性层,每一层都是y=ax+b

4.2 Escribir un método de matriz de Laplace normalizado

    def __normal__(self, edge_index, num_nodes: Optional[int],
                   edge_weight, normalization: Optional[str],
                   lambda_max, batch=None):
        edge_index, edge_weight = remove_self_loops(tlx.convert_to_numpy(edge_index),
                                                    tlx.convert_to_numpy(edge_weight))  # 移除自链接
        edge_index, edge_weight = get_laplacian(edge_index=edge_index, num_nodes=num_nodes,
                                                edge_weight=edge_weight, normalization_type=normalization)  # 获取拉普拉斯矩阵
        if batch is not None and lambda_max.numel() > 1:
            lambda_max = lambda_max[batch[edge_index[0]]]
        edge_weight = (2.0 * edge_weight) / lambda_max
        edge_index, edge_weight = add_self_loops(edge_index=edge_index, edge_attr=edge_weight, fill_value=-1,
                                                 num_nodes=num_nodes)  # 添加自连接
        assert edge_weight is not None
        return edge_index, edge_weight

4.2 Escribir el método de propagación hacia adelante del forword

    def forward(self, x, edge_index, num_nodes, edge_weight: Optional = None, batch: Optional = None,
                lambda_max: Optional = None):
        if self.normalization != 'sym' and lambda_max is None:
            raise ValueError('You need to pass `lambda_max` to `forward() in`'
                             'case the normalization is non-symmetric.')
        if lambda_max is None:
            lambda_max = tlx.convert_to_tensor(2.0)
        assert lambda_max is not None
        edge_index, normal = self.__normal__(edge_index, num_nodes,
                                             edge_weight, self.normalization,
                                             lambda_max, batch=batch)
        Tx_0 = x
        Tx_1 = x
        out = self.lins[0](Tx_0)  # x0^

        if len(self.lins) > 1:
            Tx_1 = self.propagate(x=x, edge_index=edge_index, edge_weight=normal)
            out = out + self.lins[1](Tx_1)  # x1^

        for lin in self.lins[2:]:
            Tx_2 = self.propagate(x=Tx_1, edge_index=edge_index, edge_weight=normal)
            Tx_2 = 2 * Tx_2 - Tx_0  # x2^
            out = out + lin.forward(Tx_2)
            Tx_0, Tx_1 = Tx_1, Tx_2  # 更新 x1^,x2^

        return out

5. Resultados del entrenamiento

imagen-20220518142316588

Apéndice: ChebConv

imagen-20220518142105855

imagen-20220518142035549

imagen-20220518142044969

Supongo que te gusta

Origin blog.csdn.net/qq_45724216/article/details/124841562
Recomendado
Clasificación