【图神经网络】GNNExplainer代码解读及其PyG实现

2023-05-16

GNNExplainer代码解读及其PyG实现

  • 使用GNNExplainer
  • GNNExplainer源码速读
    • 前向传播
    • 损失函数
  • 基于GNNExplainer图分类解释的PyG代码示例
  • 参考资料

接上一篇博客图神经网络的可解释性方法及GNNexplainer代码示例,我们这里简单分析GNNExplainer源码,并用PyTorch Geometric手动实现。
GNNExplainer的源码地址:https://github.com/RexYing/gnn-model-explainer

使用GNNExplainer

(1)安装:

git clone https://github.com/RexYing/gnn-model-explainer

推荐使用python3.7以及创建虚拟环境:

virtualenv venv -p /usr/local/bin/python3
source venv/bin/activate

(2)训练一个GCN模型

python train.py --dataset=EXPERIMENT_NAME

其中EXPERIMENT_NAME表示想要复现的实验名称。

训练GCN模型的完整选项列表:

python train.py --help

(3)解释一个GCN模型
要运行解释器,请运行以下内容:

python explainer_main.py --dataset=EXPERIMENT_NAME

(4)可视化解释
使用Tensorboard:优化的结果可以通过Tensorboard可视化。

tensorboard --logdir log

GNNExplainer源码速读

GNNExplainer会从2个角度解释图:

  • 边(edge):会生成一个edge mask,表示每条边在图中出现的概率,值为0-1之间的浮点数。edge mask也可以当作一个权重,可以取topk的edge连成的子图来解释。
  • 结点特征(node feature):node feature(NF)即结点向量,比如一个结点128维表示128个特征,那么它同时会生成一个NF mask来表示每个特征的权重,这个可以不要。

代码目录

  • explainer目录下的ExplainModel类定义了GNNExplainer网络的模块结构,继承torch.nn.Module:

    • 在初始化init的时候,用construct_edge_maskconstruct_feat_mask函数初始化要学习的两个mask(分别对应于两个nn.Parameter类型的变量: n × n n×n n×n维的maskd维全0的feat_mask);diag_mask即主对角线上是0,其余元素均为1的矩阵,用于_masked_adj函数。
    • _masked_adj函数将mask用sigmod或ReLU激活后,加上自身转置再除以2,以转为对称矩阵,然后乘上diag_mask,最终将原邻接矩阵adj变换为masked_adj
  • Explainer类实现了解释的逻辑,主函数是其中的explain,用于解释原模型在单节点的预测结果,主要步骤:

    1. 取子图的adj, x, label图解释:取graph_idx对应的整个计算图;节点解释:调用extract_neighborhood函数取该节点num_gc_layers阶数的邻居。
    2. 将传入的模型预测输出pred转为pred_label
    3. 构建ExplainModule,进行num_epochs轮训练(前向+反向传播)
adj   = torch.tensor(sub_adj, dtype=torch.float)
x     = torch.tensor(sub_feat, requires_grad=True, dtype=torch.float)
label = torch.tensor(sub_label, dtype=torch.long)

if self.graph_mode:
	pred_label = np.argmax(self.pred[0][graph_idx], axis=0)
	print("Graph predicted label: ", pred_label)
else:
	pred_label = np.argmax(self.pred[graph_idx][neighbors], axis=1)
	print("Node predicted label: ", pred_label[node_idx_new])

explainer = ExplainModule(
	adj=adj,
	x=x,
	model=self.model,
	label=label,
	args=self.args,
	writer=self.writer,
	graph_idx=self.graph_idx,
	graph_mode=self.graph_mode,
)
if self.args.gpu:
	explainer = explainer.cuda()

...

# NODE EXPLAINER
def explain_nodes(self, node_indices, args, graph_idx=0):
...

def explain_nodes_gnn_stats(self, node_indices, args, graph_idx=0, model="exp"):
...

# GRAPH EXPLAINER
def explain_graphs(self, graph_indices):
...

explain_nodesexplain_nodes_gnn_statsexplain_graphs这三个函数都是在它的基础上实现的。

下面分析其中的forwardloss函数。

前向传播

首先把待学习的参数mask和feat_mask分别乘上原邻接矩阵和特征向量,得到变换后的masked_adjx。前者通过调用_masked_adj函数完成,后者的实现如下:

feat_mask = (
	torch.sigmoid(self.feat_mask)
	if self.use_sigmoid
	else self.feat_mask
)
if marginalize:
	std_tensor = torch.ones_like(x, dtype=torch.float) / 2
	mean_tensor = torch.zeros_like(x, dtype=torch.float) - x
	z = torch.normal(mean=mean_tensor, std=std_tensor)
	x = x + z * (1 - feat_mask)
else:
	x = x * feat_mask

完整代码如下:
forward
这里需要说明的是marginalize为True的情况,参考论文中的Learning binary feature selector F:
Learning binary feature selector F

  • 如果同mask一样学习feature_mask,在某些情况下回导致重要特征也被忽略(学到的特征遮罩也是接近于0的值),因此,依据 X S X_S XS的经验边缘分布使用Monte Carlo方法来抽样得到 X = X S F X=X_S^F X=XSF.
  • 为了解决随机变量 X X X的反向传播的问题,引入了"重参数化"的技巧,即将其表示为一个无参的随机变量 Z Z Z的确定性变换: X = Z + ( X S − Z ) ⊙ F X=Z+(X_S-Z)\odot F X=Z+(XSZ)F s . t . ∑ j F j ≤ K F s.t. \sum_{j}F_j\le K_F s.t.jFjKF
    其中, Z Z Z是依据经验分布采样得到的 d d d维随机变量, K F K_F KF是表示保留的最大特征数的参数(utils/io_utils.py中的denoise_graph函数)。

接着将masked_adjx输入原始模型得到ExplainModule结果pred

损失函数

loss = pred_loss + size_loss + lap_loss + mask_ent_loss + feat_size_loss

可知,总的loss包含五项,除了对应于论文中损失函数公式的pred_loss,其余各项损失的作用参考论文Integrating additional constraints into explanations,它们的权重定义在coeffs中:

self.coeffs = {
	"size": 0.005,
	"feat_size": 1.0,
	"ent": 1.0,
	"feat_ent": 0.1,
	"grad": 0,
	"lap": 1.0,
}

Integrating additional constraints into explanations

  1. pred_loss
mi_obj = False
if mi_obj:
	pred_loss = -torch.sum(pred * torch.log(pred))
else:
	pred_label_node = pred_label if self.graph_mode else pred_label[node_idx]
	gt_label_node = self.label if self.graph_mode else self.label[0][node_idx]
	logit = pred[gt_label_node]
	pred_loss = -torch.log(logit)

其中pred是当前的预测结果,pred_label是原始特征上的预测结果。

  1. mask_ent_loss
# entropy
mask_ent = -mask * torch.log(mask) - (1 - mask) * torch.log(1 - mask)
mask_ent_loss = self.coeffs["ent"] * torch.mean(mask_ent)
  1. size_loss
# size
mask = self.mask
if self.mask_act == "sigmoid":
	mask = torch.sigmoid(self.mask)
elif self.mask_act == "ReLU":
	mask = nn.ReLU()(self.mask)
size_loss = self.coeffs["size"] * torch.sum(mask)
  1. feat_size_loss
# pre_mask_sum = torch.sum(self.feat_mask)
feat_mask = (
	torch.sigmoid(self.feat_mask) if self.use_sigmoid else self.feat_mask
)
feat_size_loss = self.coeffs["feat_size"] * torch.mean(feat_mask)
  1. lap_loss
# laplacian
D = torch.diag(torch.sum(self.masked_adj[0], 0))
m_adj = self.masked_adj if self.graph_mode else self.masked_adj[self.graph_idx]
L = D - m_adj
pred_label_t = torch.tensor(pred_label, dtype=torch.float)
if self.args.gpu:
	pred_label_t = pred_label_t.cuda()
	L = L.cuda()
if self.graph_mode:
	lap_loss = 0
else:
	lap_loss = (self.coeffs["lap"] * (pred_label_t @ L @ pred_label_t) / self.adj.numel())

补充

基于GNNExplainer图分类解释的PyG代码示例

对于图分类问题的解释,关键点有两个:

  • 要学习的Mask作用在整个图上,不用取子图
  • 标签预测和损失函数的对象是单个graph

实现代码如下:

#!/usr/bin/env python
# encoding: utf-8
# Created by BIT09 at 2023/4/28
import torch
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
from math import sqrt
from tqdm import tqdm
from torch_geometric.nn import MessagePassing
from torch_geometric.data import Data
from torch_geometric.utils import k_hop_subgraph, to_networkx

EPS = 1e-15


class GNNExplainer(torch.nn.Module):
    r"""
    Args:
        model (torch.nn.Module): The GNN module to explain.
        epochs (int, optional): The number of epochs to train.
            (default: :obj:`100`)
        lr (float, optional): The learning rate to apply.
            (default: :obj:`0.01`)
        log (bool, optional): If set to :obj:`False`, will not log any learning
            progress. (default: :obj:`True`)
    """

    coeffs = {
        'edge_size': 0.001,
        'node_feat_size': 1.0,
        'edge_ent': 1.0,
        'node_feat_ent': 0.1,
    }

    def __init__(self, model, epochs=100, lr=0.01, log=True, node=False):  # disable node_feat_mask by default
        super(GNNExplainer, self).__init__()
        self.model = model
        self.epochs = epochs
        self.lr = lr
        self.log = log
        self.node = node

    def __set_masks__(self, x, edge_index, init="normal"):
        (N, F), E = x.size(), edge_index.size(1)

        std = 0.1
        if self.node:
            self.node_feat_mask = torch.nn.Parameter(torch.randn(F) * 0.1)

        std = torch.nn.init.calculate_gain('relu') * sqrt(2.0 / (2 * N))
        self.edge_mask = torch.nn.Parameter(torch.randn(E) * std)
        self.edge_mask = torch.nn.Parameter(torch.zeros(E) * 50)

        for module in self.model.modules():
            if isinstance(module, MessagePassing):
                module.__explain__ = True
                module.__edge_mask__ = self.edge_mask

    def __clear_masks__(self):
        for module in self.model.modules():
            if isinstance(module, MessagePassing):
                module.__explain__ = False
                module.__edge_mask__ = None
        if self.node:
            self.node_feat_masks = None
        self.edge_mask = None

    def __num_hops__(self):
        num_hops = 0
        for module in self.model.modules():
            if isinstance(module, MessagePassing):
                num_hops += 1
        return num_hops

    def __flow__(self):
        for module in self.model.modules():
            if isinstance(module, MessagePassing):
                return module.flow
        return 'source_to_target'

    def __subgraph__(self, node_idx, x, edge_index, **kwargs):
        num_nodes, num_edges = x.size(0), edge_index.size(1)

        if node_idx is not None:
            subset, edge_index, mapping, edge_mask = k_hop_subgraph(
                node_idx, self.__num_hops__(), edge_index, relabel_nodes=True,
                num_nodes=num_nodes, flow=self.__flow__())
            x = x[subset]
        else:
            x = x
            edge_index = edge_index
            row, col = edge_index
            edge_mask = row.new_empty(row.size(0), dtype=torch.bool)
            edge_mask[:] = True
            mapping = None

        for key, item in kwargs:
            if torch.is_tensor(item) and item.size(0) == num_nodes:
                item = item[subset]
            elif torch.is_tensor(item) and item.size(0) == num_edges:
                item = item[edge_mask]
            kwargs[key] = item

        return x, edge_index, mapping, edge_mask, kwargs

    def __graph_loss__(self, log_logits, pred_label):
        loss = -torch.log(log_logits[0, pred_label])
        m = self.edge_mask.sigmoid()
        loss = loss + self.coeffs['edge_size'] * m.sum()
        ent = -m * torch.log(m + EPS) - (1 - m) * torch.log(1 - m + EPS)
        loss = loss + self.coeffs['edge_ent'] * ent.mean()

        return loss

    def visualize_subgraph(self, node_idx, edge_index, edge_mask, y=None,
                           threshold=None, **kwargs):
        r"""Visualizes the subgraph around :attr:`node_idx` given an edge mask
        :attr:`edge_mask`.

        Args:
            node_idx (int): The node id to explain.
            edge_index (LongTensor): The edge indices.
            edge_mask (Tensor): The edge mask.
            y (Tensor, optional): The ground-truth node-prediction labels used
                as node colorings. (default: :obj:`None`)
            threshold (float, optional): Sets a threshold for visualizing
                important edges. If set to :obj:`None`, will visualize all
                edges with transparancy indicating the importance of edges.
                (default: :obj:`None`)
            **kwargs (optional): Additional arguments passed to
                :func:`nx.draw`.

        :rtype: :class:`matplotlib.axes.Axes`, :class:`networkx.DiGraph`
        """

        assert edge_mask.size(0) == edge_index.size(1)

        if node_idx is not None:
            # Only operate on a k-hop subgraph around `node_idx`.
            subset, edge_index, _, hard_edge_mask = k_hop_subgraph(
                node_idx, self.__num_hops__(), edge_index, relabel_nodes=True,
                num_nodes=None, flow=self.__flow__())

            edge_mask = edge_mask[hard_edge_mask]
            subset = subset.tolist()
            if y is None:
                y = torch.zeros(edge_index.max().item() + 1,
                                device=edge_index.device)
            else:
                y = y[subset].to(torch.float) / y.max().item()
                y = y.tolist()
        else:
            subset = []
            for index, mask in enumerate(edge_mask):
                node_a = edge_index[0, index]
                node_b = edge_index[1, index]
                if node_a not in subset:
                    subset.append(node_a.item())
                if node_b not in subset:
                    subset.append(node_b.item())
            y = [y for i in range(len(subset))]

        if threshold is not None:
            edge_mask = (edge_mask >= threshold).to(torch.float)

        data = Data(edge_index=edge_index, att=edge_mask, y=y,
                    num_nodes=len(y)).to('cpu')
        G = to_networkx(data, edge_attrs=['att'])  # , node_attrs=['y']
        mapping = {k: i for k, i in enumerate(subset)}
        G = nx.relabel_nodes(G, mapping)

        kwargs['with_labels'] = kwargs.get('with_labels') or True
        kwargs['font_size'] = kwargs.get('font_size') or 10
        kwargs['node_size'] = kwargs.get('node_size') or 800
        kwargs['cmap'] = kwargs.get('cmap') or 'cool'

        pos = nx.spring_layout(G)
        ax = plt.gca()
        for source, target, data in G.edges(data=True):
            ax.annotate(
                '', xy=pos[target], xycoords='data', xytext=pos[source],
                textcoords='data', arrowprops=dict(
                    arrowstyle="->",
                    alpha=max(data['att'], 0.1),
                    shrinkA=sqrt(kwargs['node_size']) / 2.0,
                    shrinkB=sqrt(kwargs['node_size']) / 2.0,
                    connectionstyle="arc3,rad=0.1",
                ))
        nx.draw_networkx_nodes(G, pos, node_color=y, **kwargs)
        nx.draw_networkx_labels(G, pos, **kwargs)

        return ax, G

    def explain_graph(self, data, **kwargs):
        self.model.eval()
        self.__clear_masks__()
        x, edge_index, batch = data.x, data.edge_index, data.batch

        num_edges = edge_index.size(1)

        # Only operate on a k-hop subgraph around `node_idx`.
        x, edge_index, _, hard_edge_mask, kwargs = self.__subgraph__(node_idx=None, x=x, edge_index=edge_index,
                                                                     **kwargs)
        # Get the initial prediction.
        with torch.no_grad():
            log_logits = self.model(data, **kwargs)
            probs_Y = torch.softmax(log_logits, 1)
            pred_label = probs_Y.argmax(dim=-1)

        self.__set_masks__(x, edge_index)
        self.to(x.device)

        if self.node:
            optimizer = torch.optim.Adam([self.node_feat_mask, self.edge_mask],
                                         lr=self.lr)
        else:
            optimizer = torch.optim.Adam([self.edge_mask], lr=self.lr)

        epoch_losses = []
        for epoch in range(1, self.epochs + 1):
            epoch_loss = 0
            optimizer.zero_grad()
            if self.node:
                h = x * self.node_feat_mask.view(1, -1).sigmoid()

            log_logits = self.model(data, **kwargs)
            pred = torch.softmax(log_logits, 1)
            loss = self.__graph_loss__(pred, pred_label)
            loss.backward()

            optimizer.step()
            epoch_loss += loss.detach().item()
            epoch_losses.append(epoch_loss)

        edge_mask = self.edge_mask.detach().sigmoid()
        print(edge_mask)

        self.__clear_masks__()

        return edge_mask, epoch_losses

    def __repr__(self):
        return f'{self.__class__.__name__}()'

参考资料

  1. gnn-explainer
  2. 图神经网络的可解释性方法及GNNexplainer代码示例
  3. Pytorch实现GNNExplainer
  4. How to Explain Graph Neural Network — GNNExplainer
  5. https://gist.github.com/hongxuenong/9f7d4ce96352d4313358bc8368801707
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【图神经网络】GNNExplainer代码解读及其PyG实现 的相关文章

  • n位水仙花数

    试题描述 n位水仙花数是指一个n位数 xff0c 它的每个位上的数字的n次幂之和等于它本身 例如 xff1a 三位水仙花数是指一个三位数 xff0c 它的每个位上的数字的3次幂之和等于它本身 xff08 例如 xff1a 13 43 53
  • 成绩的最高分问题

    试题描述 编写函数ReadScore 和FindMax xff0c 输入某班学生某门课的成绩和学号 xff08 最多不超过40人 xff09 xff0c 当输入为负值时 xff0c 表示输入结束 xff0c 用函数编程通过返回数组中最大元素
  • xcode编译静态库时:**** is not an object file (not allowed in a library)

    出现此错误 xff1a 第一步 xff1a 链接的库是否是存在的且正确的库 a 第二步 xff1a 如果还出现错误 xff0c 那么确定Xcode搜索库路径 Library search paths xff0c 是否有错误 如果在工程目录中
  • Ubuntu桥接模式下无法连接网络的问题

    新装的VMware虚拟机 xff0c 作为开发 xff0c 需要使用桥接模式 xff0c 但是一直无法正常连接网络 xff0c ifconfig一直没有IPV4地址显示 xff0c ping外网也不通 网上的方法也几乎试了个遍 xff0c
  • 黑马程序员————数组,字符串,函数,指针

    Java培训 Android培训 iOS培训 Net培训 期待与您交流 xff01 一 数组的基本概念 只能存放一种类型的数据 xff0c 比如int类型的数组 float类型的数组 里面存放的数据称为 元素 二数组的定义 1 定义 声明数
  • QT控件提升之QPushButton提升为QMenu

    当一个控件进行提升之后 xff0c 就有了新的功能 xff0c 在原来的一些特性基础上 xff0c 发生一些新的改变 QT控件提升方法 xff1a 1 需要写一个需要提升为某种功能的类 2 打开qt设计师 xff0c 在对应需要提升的控件
  • 【Hugging Face】Hugging Face 主要类和函数介绍

    Hugging Face 主要类和函数介绍 Hugging face是什么 xff1f 什么是自然语言处理 xff1f PipelineDatasetPipeline on GPUMetricsAutoClasses在本地保存和加载模型结论
  • 基于ubuntu server 16.04环境安装kvm虚拟机并创建windows系统

    由于项目需要 xff0c 最近在研究 kvm 虚拟机 xff0c 将这个过程中遇到的一些问题做一些记录 由于本人水平有限 xff0c 其中不妥之处还请网友们不吝赐教 1 操作环境 ubuntu server 16 04 默认的安装后没有桌面
  • Linux炫酷代码秀

    cmatrix 命令 这个很酷 xff01 黑客帝国 那种矩阵风格的动画效果 安装 sudo apt get install cmatrix 运行 cmatrix
  • keil中include 头文件循环引用问题

    在头文件中使用 ifdef和 xff03 ifndef是非常重要的 xff0c 可以防止双重定义的错误 有时候 xff0c 在b h中会include 34 a h 34 xff0c 在 34 c h 34 中会include 34 b h
  • 并查集(加入、查找、删除)

    并查集 来源洛谷 题目描述 如题 xff0c 现在有一个并查集 xff0c 你需要完成合并和查询操作 输入格式 第一行包含两个整数 N M 表示共有 N 个元素和 M 个操作 接下来 M 行 xff0c 每行包含三个整数Z i X i Y
  • Centos7查看防火墙以及端口开放情况

    1 查看防火墙状态 firewall cmd state 2 开关防火墙 systemctl start firewalld service systemctl stop firewalld service systemctl restar
  • 完美解决“当前不会命中断点,还未为文档加载任何符号”的问题

    遇到这个问题是我正在用vc2008 调试一个 C 43 43 写的 Dll xff0c dll 在编译中没有报错 xff0c 但在用VB net写的程序调用此 Dll 时 xff0c 才会报告 于 34 xxx dll 中找不到 XXX 函
  • switch 以string为条件 做判断的方法

    c 43 43 和java语言中的switch都是只接受 整型 c 语言中可以在switch中 xff0c 以字符串作为case的条件 我觉得宏定义不行 xff0c 用map尝试一下 xff0c 下面是给你一个例子 map lt strin
  • nginx那点事儿——nginx日志详解

    nginx日志 前言一 日志配置 格式二 日志格式包含的变量三 日志缓存1 缓存设置2 作用位置 四 日志切割1 切割配置文件2 日志切割原理 五 日志分析 前言 Nginx有非常灵活的日志记录模式 每个级别的配置可以有各自独立的访问日志
  • 最全详解关键路径法

    关键路径法是软考的知识点 我分析了常见的模棱两可的知识点 并进行了图解说明 现在分享给正在准备参加软考试的广大考友 01什么是关键路径法CPM 关键路径法用于在进度模型中估算项目最短工期 确定逻辑网络路径的进度灵活性大小 这种进度网络分析技
  • 【LLM】LLaMA简介:一个650亿参数的基础大型语言模型

    LLaMA简介 xff1a 一个650亿参数的基础大型语言模型 PaperSetup其他资料 作为 Meta 对开放科学承诺的一部分 xff0c 今天我们将公开发布 LLaMA 大型语言模型 Meta AI xff0c 这是一个最先进的大型
  • Cache-主存效率问题

    本文主要明确在软考中经常遇到的缓存效率问题 第零 xff0c 明确一个问题 xff1a 如果Cache不命中时 xff0c 不同的系统有不同的应对策略 一是直接从主存中拿走待取数据 xff0c 它的时间消耗仅仅是一个访问主存周期 二是把待取
  • filezilla 严重文件传输错误 550permission denied

    问题描述 xff1a FileZilla工具使用ftp账户 xff0c 密码 xff0c 端口21 xff0c 快速链接到自己搭建的外网ftp服务器 xff0c 提示登录成功 xff0c 选择本地文件 xff0c 右键文件上传 xff0c
  • ubuntu与windows互传文件的3种方法

    一般在进行编程作业的时候 xff0c 我们会采用 开发在Windows中编辑源代码 xff0c 在linux中编译 执行源代码 这往往需要需要将在Windows下编辑好的源代码上传到linux系统种进行编译 怎么来进行上传呢 xff1f 其

随机推荐

  • ubuntu下如何设置环境变量

    一 设置环境变量的三种方法 1 1 临时设置 export PATH 61 home yan share usr local arm 3 4 1 bin PATH 1 2 当前用户的全局设置 打开 bashrc xff0c 添加行 xff1
  • ssh免密登录设置方法

    1 前提条件 主机A xff0c 用户名为aris xff0c IP地址为192 168 1 1主机B xff0c 用户名为leon xff0c IP地址为192 168 1 2这两台主机上均安装了SSH服务器 xff0c 且已经打开ssh
  • 软考高项你想要的全在这

    2021年准备参加软考获取高级职业技术资格认证的小伙伴咱们约起吧 xff1f xff01 自软考系列文章发表之后有很多准备参加软考的小伙伴加我微信 xff0c 关注我的微博 xff0c 也有很多因此成了好朋友 xff0c 甚至是同事 自前年
  • Makefile语法及通用模板

    简介 xff1a 本文主要讲解了在开发常规项目时 xff0c 用于自动化部署生成目标文件的Makefile 对其包含的主要语法进行了讲解 xff0c 最后给出了一个项目通用的Makefile模板 xff0c 以帮助大家理解 1 Makefi
  • ubuntu镜像源的配置

    摘要 xff1a 你是否遇到过按照网上教程更改了自己的镜像源之后 xff0c 貌似还是不兼容 xff0c 许多安装包还是下不了 xff1f 其实不是他们写的教程有错误 xff0c 而是你没用根据自己使用的ubuntu的版本去正确配置镜像源
  • Linux中与“内核模块”相关的数据结构

    摘要 本文详细解释了linux中与模块相关的内核数据结构 xff0c 便于大家在学习理解内核源码或驱动编程中理解相应代码和思想 三 内核模块相关的数据结构 目录 THIS MODULE宏module结构体module use 3 1 THI
  • Linux内核中与“文件系统”相关的数据结构

    文件系统相关的数据结构 4 1 file结构体 文件结构体代表一个打开的文件 xff0c 系统中的每个打开的文件在内核空间都有一个关联的struct file 它由内核在打开文件时创建 xff0c 并传递给在文件上进行操作的任何函数 在文件
  • 【可解释AI】图神经网络的可解释性方法及GNNexplainer代码示例

    图神经网络的可解释性方法及GNNexplainer代码示例 GNNExplainerIntroductionModelSingle instance explanations xff08 Explanation via Structural
  • 文本编辑器VI命令详解

    目录 一 xff1a 文本编辑器概述 1 文本编辑器含义 2 文本编辑器的作用 3 Linux中最常见的文本编辑器 二 vi编辑器的工作模式 1 vi编辑器的工作模式 2 各模式之间的切换 三 xff1a 命令模式概述 1 命令模式常用操作
  • Linux中与“内核安全”相关的数据结构

    五 内核安全相关数据结构 5 1 security operations结构体 这是一个钩子函数的指针数组 xff0c 其中每一个数组元素都是一个SELINUX安全钩子函数 xff0c 在2 6以上的内核中 xff0c 大部分涉及安全控制的
  • 洛谷 P3366 【模板】最小生成树

    题目描述 如题 xff0c 给出一个无向图 xff0c 求出最小生成树 xff0c 如果该图不连通 xff0c 则输出orz 输入输出格式 输入格式 xff1a 第一行包含两个整数N M xff0c 表示该图共有N个结点和M条无向边 xff
  • 关于网站最近出现504错误的总结,too open many files in system

    如果你有耐心看完这篇文章 xff0c 也许会给你带来真正的益处 网站出现504错误 xff0c 如果你用阿里云CDN的话还会报 504 Gateway Time out The gateway did not receive a timel
  • Manjaro21安装VNC,Win10远程连接manjaro桌面

    manjaro安装tigervnc xff0c win10使用VNC viewer TigerVNC 简体中文 ArchWiki archlinux org https wiki archlinux org title TigerVNC E
  • Proxmox虚拟环境搭建

    一 Proxmox VE简介 ProxmoxVE 是一个完整的 开源的企业虚拟化服务器管理平台 它在单个平台上紧密集成了 KVM 管理程序和 Linux 容器 LXC 软件定义的存储和网络功能 通过集成的基于 web 的用户界面 xff0c
  • HEX2DEC存储过程实现

    数据库当前有十进制转换为十六进制的函数hex 函数 xff0c 却没有十六进制转换为十进制的函数 xff0c 只能自己定义一个hex2dec xff0c 存储过程如下 xff1a span class token keyword drop
  • SQLite数据类型引起的问题——全数字字符串使用varchar出现错误

    问题 xff1a 项目中需要把某些数据保存到Android的数据库中 xff0c 因为保存的字符串全部为数字形式 xff0c SQLite把部分字符串自动转化为了科学技术法导致数据显示异常 xff0c 同时还把一些开头为0的字符串自动去掉了
  • IOS 自定义UIAlertController

    自定义UIAlertController xff1a 首先展示效果图 1 创建一个新的类来管理弹出的视图 继承于UIView 2 传建一个xib文件来自定义弹出视图 xff08 注意创建过后一定要将xib的class关联 xff09 3 在
  • python把txt文件里重复数据去重代码

    有时候会发现txt文件里有很多重复数据 xff0c 这里自写了一个去重的python程序 xff0c 供学习使用 xff01 def quchong print 39 39 50 print 39 导入txt文件中 39 num 61 0
  • ERROR 1064 (42000): You have an error in your SQL

    对于新手来说 xff0c MySQL数据库 xff0c 在命令行使用sql语句进行建库 xff0c 查库 xff0c 建表 xff0c 查表 时 xff0c MySQL 报错 xff1a ERROR 1064 42000 You have
  • 【图神经网络】GNNExplainer代码解读及其PyG实现

    GNNExplainer代码解读及其PyG实现 使用GNNExplainerGNNExplainer源码速读前向传播损失函数 基于GNNExplainer图分类解释的PyG代码示例参考资料 接上一篇博客图神经网络的可解释性方法及GNNexp