图神经网络详细内容

news2024/10/7 0:28:29

文章目录

  • 1. 图神经网络
    • 1.1 GCN图卷积网络
      • 1.1.1 计算过程
      • 1.1.2 公式的物理原理
      • 1.1.3 GCN代码实现
    • 1.2 GAT图注意力网络
      • 1.2.1 计算过程与原理
      • 1.2.2 GAT代码实现
    • 1.3 消息传递
    • 1.4 图采样介绍
    • 1.5 图采样算法:GraphSAGE
    • 1.6 图采样算法:PinSAGE
  • 2. 参考

1. 图神经网络

图神经网络(Graph Neural Networks,GNN)是近几年兴起的学科,用作推荐算法也是相当好,下面来对图神经网络来一个全面的了解。

1.1 GCN图卷积网络

图卷积网络(Graph Convolutionnal Networks,GCN)提出于2017年。GCN的出现标志着图神经网路的出现。深度学习最常用的网络结构是CNN和RNN。GCN与CNN仅名字相似,其实理解起来也很类似,都是特征提取器。不同的是,CNN提取的是张量数据特征,而GCN提取的是图结构数据特征

1.1.1 计算过程

本质上GCN的公式非常简单,但最初的研究者为了从数学上严谨地推导该公式是有效的,所以涉及诸如傅里叶变换,以及拉普拉斯算子等知识。

  • 傅里叶变换
  • 拉普拉斯算子

其实对于使用者而言,可以绕开这些知识,并且毫无影响地理解GCN。

以下是GCN网络层的基础公式:

在这里插入图片描述

其中,

  • H(l) 指第l层的输入特征
  • H(l+1)输出特征
  • W(l)线性变换矩阵
  • 𝛔(·)是非线性激活函数,如ReLU和Sigmoid

所以重点是那些A、D是什么。

首先说A~,通常邻接矩阵用A表示,在A上面加波浪线的A~叫做“有自连的邻接矩阵”,以下简称自连邻接矩阵,定义:

在这里插入图片描述

其中,I是单位矩阵,A是邻接矩阵。因为一对于邻接矩阵的定义,是矩阵中的值为对应位置节点与节点之间的关系,而矩阵对角线的位置是节点与自身的关系,但是节点与自身无边相连,所以链接矩阵中对角线自然都为0,但是如果接受这一设定进行下游计算,则无法在邻接矩阵中区分“自身节点”与“无连接节点”,所以将A加上一个单位矩阵I得到A~,便能使对角线为1,就好比添加了自连的设定,如下图所示:

在这里插入图片描述

D~是自连矩阵的度矩阵,定义如下:

在这里插入图片描述

如果仍然用上图数据:

在这里插入图片描述

在这里插入图片描述

D~^-1/2是在自连度矩阵的基础上开平方根取逆。在无向无权图中,度矩阵描述的是节点邻居的数量;若是有向图则是出度的数量;若是有权图,则是目标节点与每个邻居连接边的权重。而对于自连度矩阵,是在度矩阵的基础上加上一个单位矩阵,即每个节点度加一。

GCN公式中的D~^-1/2·A~·D~^-1/2其实都是从邻接矩阵计算而来的,所以甚至可以把这些看作一个常量。模型需要学习的仅仅是W(l)这个权重矩阵。

正如之前所讲,GCN神经网络层的计算过程很简单,如果懂了那个公式,则只需构建一张图,统计出邻接矩阵,直接带入公式即可实现GCN网络。

1.1.2 公式的物理原理

下面来理解一下GCN公式的物理原理。首先来看A~H^l这一计算的意义,如下图:

在这里插入图片描述

在自连邻接矩阵满足上图的数据场景时,下一层第1个节点的向量表示是当前层节点h1、h2、h3、h5这些节点向量表示的和。这一过程的可视化意义如下图:

在这里插入图片描述

这一操作像在卷积神经网络中进行卷积操作,然后进行一个求和池化(Sum Pooling)。这其实是一条消息传递的过程,Sum Pooling是一种消息聚合的操作,当然也可以采取平均、Max等池化操作。总之经消息传递的操作后,下一层的节点1就聚合了它的一节邻居和自身的信息,这就很有效地保留了图结构承载的信息。

接下来看度矩阵D在这里起到的作用。节点的度代表它的一阶邻居的数量,所以乘以度矩阵的逆,会稀释掉度很大的节点的重要程度。这其实很好理解,例如保险经理张三的好友有2000个,当然你也是其中之一,而你幼时的青梅竹马小红加上你仅有10个好友,则张三与小红对于定义你的权重自然就不该一样。(这是一个加权求和操作,度越大权重就越低)

下列公式与可视化意义:

在这里插入图片描述

在这里插入图片描述

上图中每条边的权重坟分母左边的数字根号4是节点1自身度的逆平方根。上述内容可以简单理解GCN公式的计算意义。

图神经网络之所有有效,是因为它很好地利用了图结构的信息。它的起点是别人的终点。本身无监督统计图数据信息已经可以给预测带来很高的准确率。此时只需一点少量的标注数据进行有监督的训练就可以媲美大数据训练的神经网络模型。

1.1.3 GCN代码实现

GCN作为图神经网络中最基础的算法,各个图神经网络库自然都集成了现成的API。我们以Pytorch Geometric(PyG)为例做介绍。

PyG中有自带的开源数据集供学习调用。这里以Cora数据集为例,读取PyG自带的Core数据集的Python文件如下:

from torch_geometric.datasets import Planetoid
import os

def loadData():
    path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'data', 'Cora')
    dataset = Planetoid(path,  'Cora')
    return dataset.data, dataset.num_classes, dataset.num_features

if __name__ == '__main__':
    path = os.path.join( os.path.dirname( os.path.realpath(__file__) ), '..', 'data', 'Cora')
    dataset = Planetoid( path, 'Cora' )
    print( '类别数:', dataset.num_classes )
    print( '特征维度:', dataset.num_features )
    print( dataset.data )
    print(dataset.data.train_mask)

    print( sum( dataset.data.train_mask ) )
    print( sum( dataset.data.val_mask ) )
    print( sum( dataset.data.test_mask ) )

调用后:

在这里插入图片描述

类别数是分类任务重类别的数量,这里为了巩固图神经网络的基础。分类任务往往是机器学习中最简单的任务,所以由分类任务入手去学习图神经网络是再合适不过的由浅入深的学习过程。

得到的Data包含了所有训练及预测所需要的信息,edge_index是边集,x是节点特征向量,总共2708个节点,每个节点表示是1433维的向量,y是对应的类别标注

traini_mask、val_mask、test_mask分别对应训练集、验证集、测试集的位置遮盖列表,他们都是True和False的列表,列表索引对应着节点索引。PyG的默认数据集利用位置遮盖的方式区分训练集与测试集。例如train_mask中维True的位置代表训练该节点,否则不训练。

由打印出来的数据可以知道,总计2708个节点中训练数据仅仅只有140个,而测试集即由999个。这就印证了“图神经网络仅需少量的标注数据训练出来的模型,就可以达到由规模训练数据训练的普通神经网络模型一样甚至更好的效果。”此言并不虚。

完整的GCN模型核心代码:

class GCN( torch.nn.Module ):

    def __init__( self, n_classes, dim ):
        '''
        :param n_classes: 类别数
        :param dim: 特征维度
        '''
        super( GCN, self ).__init__( )
        self.conv1 = GCNConv( dim, 16 )
        self.conv2 = GCNConv( 16, n_classes )

    def forward( self,data ):
        x, edge_index = data.x, data.edge_index
        x = F.relu( self.conv1( x, edge_index ) )
        x = F.dropout( x )
        x = self.conv2( x, edge_index )
        return F.log_softmax( x, dim = 1 )

可以看到现成的方法非常简单,只需定义GCNConv的输入维度和输出维度,在前向传播时输入特征向量即可表示图的边集。

外部调用的代码如下:

def train( epochs = 200, lr = 0.01 ):
    data, n_class, dim = pygDataLoader.loadData()
    net = GCN( n_class, dim )
    optimizer = torch.optim.AdamW( net.parameters(), lr = lr )

    for epoch in range(epochs):
        net.train( )
        optimizer.zero_grad( )
        logits = net( data )
        #仅用训练集计算loss
        loss = F.nll_loss( logits[data.train_mask], data.y[data.train_mask] )
        loss.backward( )
        optimizer.step( )

        train_acc, val_acc, test_acc = eva( net, data )

        log = 'Epoch: {:03d}, Train: {:.4f}, Val: {:.4f}, Test: {:.4f}'
        print( log.format( epoch, train_acc, val_acc, test_acc ) )

注意计算loss时仅用train_mask中为True的那些位置节点。

中间设计的eva测试方法如下:

@torch.no_grad()
def eva( net, data ):
    net.eval()
    logits, accs = net(data), []
    for _, mask in data('train_mask', 'val_mask', 'test_mask'):
        pred = logits[mask].max(1)[1]
        acc = pred.eq(data.y[mask]).sum().item() / mask.sum().item()
        accs.append( acc )
    return accs

完整代码:

import torch
import torch.nn.functional as F
from chapter4 import pygDataLoader
from torch_geometric.nn import GCNConv

class GCN( torch.nn.Module ):

    def __init__( self, n_classes, dim ):
        '''
        :param n_classes: 类别数
        :param dim: 特征维度
        '''
        super( GCN, self ).__init__( )
        self.conv1 = GCNConv( dim, 16 )
        self.conv2 = GCNConv( 16, n_classes )

    def forward( self,data ):
        x, edge_index = data.x, data.edge_index
        x = F.relu( self.conv1( x, edge_index ) )
        x = F.dropout( x )
        x = self.conv2( x, edge_index )
        return F.log_softmax( x, dim = 1 )

@torch.no_grad()
def eva( net, data ):
    net.eval()
    logits, accs = net(data), []
    for _, mask in data('train_mask', 'val_mask', 'test_mask'):
        pred = logits[mask].max(1)[1]
        acc = pred.eq(data.y[mask]).sum().item() / mask.sum().item()
        accs.append( acc )
    return accs

def train( epochs = 200, lr = 0.01 ):
    data, n_class, dim = pygDataLoader.loadData()
    net = GCN( n_class, dim )
    optimizer = torch.optim.AdamW( net.parameters(), lr = lr )

    for epoch in range(epochs):
        net.train( )
        optimizer.zero_grad( )
        logits = net( data )
        #仅用训练集计算loss
        loss = F.nll_loss( logits[data.train_mask], data.y[data.train_mask] )
        loss.backward( )
        optimizer.step( )

        train_acc, val_acc, test_acc = eva( net, data )

        log = 'Epoch: {:03d}, Train: {:.4f}, Val: {:.4f}, Test: {:.4f}'
        print( log.format( epoch, train_acc, val_acc, test_acc ) )

if __name__ == '__main__':
    train( )

在该代码中,所有节点的Embedding都会伴随模型的正向传播去更新,即GCN公式中的H,并不是仅将其作为训练数据的结点Embedding输入GCN网络层,而反向传播仅且只能更新有指定位置的数据。通过mask列表的操作,可以很方便地区分训练集、验证集、测试集。

1.2 GAT图注意力网络

图注意力网络(Graph Attention Networks,GAT)提出于2018年,顾名思义,GAT是加入注意力机制的图神经网络。

GCN中消息传递的权重仅仅考虑了节点的度,是固定不变的,而GAT则采用注意力将消息传递的权重以注意力权重参数的形式也跟着模型参数一起迭代更新。

1.2.1 计算过程与原理

在了解了GAT的计算过程前,得把GCN的那个公式忘记。因为GAT的公式并非是从GCN出发的。

下图简单地展示了GAT消息传递的形式:

在这里插入图片描述

节点1、2、3、5通过各自的权重a12、a13、a15、a11衰减或增益后将信息传递给了节点1。设aij为节点j到节点i的消息传递注意力权重,则:

在这里插入图片描述

与常规的注意力机制一样,在计算出eij后,对其进行一个Softmax操作使aij在0-1.

其中Ni是指节点i的一阶邻居集。至于eij如果得到,公式如下:

在这里插入图片描述

在GAT论文中LeakyReLU的负值斜率取值0.2。hi和hj是当前输入层的节点i与节点j的特征向量表示,W是线性变换矩阵,它的形状是W∈RFxF’,其中F是输入特征的维度,是hi和hj的维度。F’是输出特征的维度,以下用hi‘表示当前层节点i的输出特征,并且其维度为F’。||是向量拼接操作,原本维度为F的hi和hj经过W线性变换后维度均变为F’,经过拼接后得到维度为2F’的向量。此时再点乘一个维度为2F’的单层矩阵a的转置,最终LeakyReLu激活后得到唯一的eij

所以再通过对eij进行Softmax操作就可以得到节点j到节点i的消息传递注意力权重aij。计算节点i的在当前GAT网络层的输出向量hi‘即可描述为:

在这里插入图片描述

其中,𝛔(·)代表任意激活函数,Ni代表节点i的一阶邻居集,W与注意力计算中的W是一样的。至此是一条消息传递并用加权求和的方式进行消息聚合的计算过程。在GAT中,可以进行多次消息传递操作,这种称为多头注意力(Multi-Head Attention),计算公式如下:

在这里插入图片描述

所以每一层的输出特征是总共K个单头消息传递后拼接起来的向量。或者进行求和平均操作,公式如下:

在这里插入图片描述

下图展示了多头注意力消息传递的过程:

在这里插入图片描述

上图可以很直观地观察到节点1、 2、 3、 5分别进行了三次不同权重的消息传递。产生了3个节点1的输出特征被记作1’‘,最终节点1的输出特征等于3个1’‘向量的拼接或者求平均所得。这么做的好处便是进一步提高了泛化能力。在GAT的论文中建议在GAT网络中间的隐藏层采取拼接操作,而在最后一层采取平均操作。

1.2.2 GAT代码实现

GAT的网络层在PyG中也有现成的API可调用:

from torch_geometric.nn import GATConv

以GAT网络层组成的GAT模型类的代码如下:

import torch
import torch.nn.functional as F
from chapter4 import pygDataLoader
from torch_geometric.nn import GATConv

class GAT(torch.nn.Module):

    def __init__(self, n_classes, dim):
        '''
        :param n_classes: 类别数
        :param dim: 特征维度
        '''
        super(GAT, self).__init__( )
        self.conv1 = GATConv( dim, 16 )
        self.conv2 = GATConv( 16, n_classes )

    def forward( self, data ):
        x, edge_index = data.x, data.edge_index
        x = F.relu( self.conv1( x, edge_index ) )
        x = F.dropout( x )
        x = self.conv2( x, edge_index )
        return F.log_softmax( x, dim = 1 )

完整代码:

import torch
import torch.nn.functional as F
from chapter4 import pygDataLoader
from torch_geometric.nn import GATConv

class GAT(torch.nn.Module):

    def __init__(self, n_classes, dim):
        '''
        :param n_classes: 类别数
        :param dim: 特征维度
        '''
        super(GAT, self).__init__( )
        self.conv1 = GATConv( dim, 16 )
        self.conv2 = GATConv( 16, n_classes )

    def forward( self, data ):
        x, edge_index = data.x, data.edge_index
        x = F.relu( self.conv1( x, edge_index ) )
        x = F.dropout( x )
        x = self.conv2( x, edge_index )
        return F.log_softmax( x, dim = 1 )

def train( data, n_class, dim, lr = 0.01 ):

    net = GAT( n_class, dim )
    optimizer = torch.optim.AdamW(net.parameters(), lr=lr)

    for epoch in range(1, 201):
        net.train()
        optimizer.zero_grad()
        logits = net(data)
        loss = F.nll_loss(logits[data.train_mask], data.y[data.train_mask])
        loss.backward()
        optimizer.step()

        train_acc, val_acc, test_acc = eva(net,data)

        log = 'Epoch: {:03d}, Train: {:.4f}, Val: {:.4f}, Test: {:.4f}'
        print(log.format(epoch, train_acc, val_acc, test_acc))
    return net

@torch.no_grad()
def eva(net,data):
    net.eval()
    logits, accs = net(data), []
    for _, mask in data('train_mask', 'val_mask', 'test_mask'):
        pred = logits[mask].max(1)[1]
        acc = pred.eq(data.y[mask]).sum().item() / mask.sum().item()
        accs.append(acc)
    return accs

if __name__ == '__main__':
    data, n_class , dim = pygDataLoader.loadData()

    net = train(data,n_class, dim)

与1.1.3节GCN的唯一区别是GCNConv变成了GATConv,外部如何调用该模型做训练其实跟GCN也完全一样。

1.3 消息传递

这里的内容将详细介绍上文中提到的消息传递(Message Passing)。消息传递可以被理解为在一张图网络中节点间传到信息的通用操作。首先来看对单个节点v进行消息传递的范式:

在这里插入图片描述

其中,hv’是节点v的在当前层的输出特征,hv输入特征,𝜑(·)表达对某个节点进行消息传递动作,Nv是节点v的邻居集。hu|u∈Nv代表遍历节点v的邻居集,相当于邻居节点消息发送的动作,而g(·)是一条消息聚合的函数,例如Sum、Avg、Max。g(·)在GCN的网络层中,是一个基于度的加权求和,而在GAT中是基于注意力的加权求和。f(·)表示对消息集合后的节点特征进行深度学习的常规操作,例如进行一次或多次线性变化或者非线性激活函数。

下图展示了上面的过程:

在这里插入图片描述

一个GNN层的计算范式可以表达为:

在这里插入图片描述

其中,Hl代表第l层所有节点的特征矩阵,Hl+1表示第l层输出的特征矩阵,V表示所有节点,𝜑(v)表示对节点v进行消息传递操作。该公式表达的含义是遍历图网络中所有的节点对其进行消息传递操作,以便更新所有节点的特征向量。

所以特神经网络的本质是通过节点间的消息传递从而泛化图结构的信息

1.4 图采样介绍

图神经网络中还有一个重要概念,即图采样。如果数据量过大,则是否可以仿照传统深度学习的小批量训练方式呢?答案是不可以,因为普通深度学习中的训练样本之间并不依赖,但是图结构的数据中,节点与节点之间有依赖关系,如下图:

在这里插入图片描述

在这里插入图片描述

普通深度学习的训练样本在空间中是一些散点,可以随意小批量采样,无论如何采样得到的训练样本并不会丢失什么信息。

而图神经网络训练样本之间存在边的依赖,也正是因为有边的依赖,也正是因为有边的依赖,所以才被称为图结构数据,这样才可用图神经网络的模型算法来训练,如果随意采样,则破坏了样本之间的关系信息。

所以如果进行图采样成为一门学科,下面介绍两个基础图采样算法GraphSAGE和pinSAGE。

1.5 图采样算法:GraphSAGE

GraphSAGE是第一张图采样算法,也是最基础的。其提出年份与GCN同年,也是2017年。其实中心思想概括成一句话就是:小批量采样原有大图的子图。如下图所示:

在这里插入图片描述

  • 步骤1:随机选取一个或者若干个节点作为0号节点。
  • 步骤2:在0号节点的一阶邻居中随机选取若干个节点作为1号节点。
  • 步骤3:在刚刚选取的1号节点的一阶邻居中,不回头地随机选取若干个节点作为2号节点,不回头是指不再回头取0号节点。该步骤亦可认为是随机选取0号节点通过1号节点连接的二阶邻居。
  • 步骤4:一次类推,在上图中的k是GraphSAGE的超参,可认为是0号节点的邻居阶层数,若将k设定为5,则代表总共可以取0号节点的第5阶邻居。
  • 步骤5:将采样获得的所有节点保留边的信息后组成子图并作为一次小批量样本输入图神经网络中进行下游任务。

另外,其实图采样得到的子图是从作为中心节点的0号节点开始扩散,所以在消息传递时可以自外而内地进行。假如在上图中,可以先将那些2号节点的特征向量聚合到对应位置的1号节点中,再由更新过后的1号节点消息传递至0号节点,然后将消息聚合在0号节点。仅输出更新完成的0号节点特征向量,作为图神经网络层的输入特征向量进行训练更新。

1.6 图采样算法:PinSAGE

相比于最基础的GraphSAGE,2018年斯坦福大学提出的PinSAGE更具想象力。PinSAGE的中心思想可以概括成一句话,即采样通过随机游走经过的高频节点生成的子图。接下来,结合下图,来理解PinSAGE的采样过程:

在这里插入图片描述

  • 步骤1:随机选取一个或者若干个节点作为0号节点。
  • 步骤2:以0号节点作为起始节点开始随机游走生成序列,游走方式可以采取DeepWalk或者Node2Vec。
  • 步骤3:统计随机游走中高频出现的节点作为0号节点的邻居,以便生成一个新的子图。出现的频率可作为超参设置。
  • 步骤4:将新子图中的边界节点(如上图中的1、9、13)作为新的起始节点,重复步骤2开始随机游走。
  • 步骤5:统计新一轮随机游走的高频节点,作为新节点在原来子图中接上。注意每个新高频节点仅接在他们原有的起始节点中(如节点1作为起始节点随机游走,所以生成节点序列中的高频节点仅作为节点1的邻居接在新子图中)。
  • 步骤6:重复上述过程k次,k为超参。将生成的新子图作为一次小批量样本输入神经网络中进行下游任务。或者进行自外而内的消息传递后,输出聚合了子图的所有信息的0号节点向量。

PinSAGE的优势在于可以快速地收集到远端节点,并且生成的子图经过一次频率筛选所获得的样本表达能力更强也更具有泛化能力

2. 参考

  • 《动手学推荐系统——基于pytorch的算法实现》
  • 《深度学习推荐系统》
  • Kipf T N, Welling M. Semi-supervised classification with graph convolutional networks[J]. arXiv preprint arXiv:1609.02907, 2016.
  • Veličković P, Cucurull G, Casanova A, et al. Graph attention networks[J]. arXiv preprint arXiv:1710.10903, 2017.
  • Ying R, He R, Chen K, et al. Graph convolutional neural networks for web-scale recommender systems[C]//Proceedings of the 24th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. 2018: 974-983.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1039647.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构 | 树

树 树是n(n>0)个结点的有限集。当n 0时,称为空树。在任意一棵非空树中应满足: 有且仅有一个特定的称为根的结点。当n>1时,其余节点可分为m(m>0)个互不相交的有限集T1,T2,…,Tm&#…

基于SpringBoot的可以做毕设或者课设的实时聊天系统(仿微信)

技术栈 前后端分离前端使用: Vue Element后端使用: SpringBoot Mysql8.0 Mybatis WebSocket 功能 登录和注册页 登录 和 注册 修改个人信息页 修改个人信息 消息列表页 展示最近半年的聊天信息,删除聊天记录 搜索好友和群页 搜索JJ号来找到 群/好友 好友信息详情页…

一文搞懂 this 指向

目录 一、前言二、箭头函数三、new指向四、bind五、call和apply六、bind call apply区别七、对象(obj.)八、全局this指向九、不在函数里 一、前言 JS 中 this 指向问题 - 掘金 在JavaScript中,this关键字表示当前执行代码的上下文对象。它的…

Linux 系统死机后挽救措施

一、背景 因我们日常使用Linux系统过程中,会不时遇到系统崩溃的事,但这时系统界面除了呈现一片告警字符外,无发执行任何其他操作,留给我们的要不重启,要不就是尴尬等待指令。那面对会这种情况,还到底有没有…

人工智能热潮推动光芯片与光器件需求飙升

随着人工智能技术的迅猛发展,光芯片和光器件作为关键的基础技术,在这一浪潮下迎来了前所未有的需求增长。光芯片和光器件的高速率、高带宽、低能耗等优势,使其在人工智能应用中发挥着重要作用,正日益成为推动人工智能进步的关键要…

上海长宁来福士P2.5直径4米无边圆形屏圆饼屏圆面屏圆盘屏平面圆屏异形创意LED显示屏案例

长宁来福士广场是一个大型广场,坐落于上海中山公园商圈的核心区域,占地逾6万平方米,其中地上总建筑面积近24万平方米,总投资额约为96亿人民币。 LED圆形屏是根据现场和客户要求定制的一款异形创意LED显示屏,进行文字、…

513找树左下角值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1示例 2: 输入: [1,2,3,4,null,5,6,null,null,7] 输出: 7class Solution { public:int findBottomLeftValue(TreeNode…

RDLC动态设置整个表格是否显示

最近有个新的需求:使用RDLC打印,当数据库中能查出数据时,显示表格。没有数据时,不显示整个表格。 1.首先在RDLC中选中表格的任意一列,右键Tablix属性 2.Tablix属性中选中可见性》选中基于表达式显示或隐藏(E)并点开右…

Word | 简单可操作的快捷公式编号、右对齐和引用方法

1. 问题描述 在理工科论文的写作中,涉及到大量的公式输入,我们希望能够按照章节为公式进行编号,并且实现公式居中,编号右对齐的效果。网上有各种各样的方法来实现,操作繁琐和简单的混在一起,让没有接触过公…

深度强化学习(三)马尔科夫决策过程

文章目录 马尔可夫过程MP马尔科夫链MC状态转移概率矩阵n步转移概率 马尔科夫链 马尔科夫奖励过程MRP奖励机制计算价值概念定义计算价值推导贝尔曼方程贝尔曼方程实际应用 参考文章:https://blog.csdn.net/taka_is_beauty/article/details/88356375 序贯决策问题是针…

【Vue3 源码解析】nextTick

nextTick 是 Vue 3 中用于异步执行回调函数的函数&#xff0c;它会将回调函数延迟到下一个微任务队列中执行。其中&#xff0c;Vue 更新 DOM 是异步的。下面是对 nextTick 函数的详细解释&#xff1a; export function nextTick<T void, R void>(this: T,fn?: (this:…

2023-09-25 LeetCode每日一题(LFU 缓存)

2023-09-25每日一题 一、题目编号 460. LFU 缓存二、题目链接 点击跳转到题目位置 三、题目描述 请你为 最不经常使用&#xff08;LFU&#xff09;缓存算法设计并实现数据结构。 实现 LFUCache 类&#xff1a; LFUCache(int capacity) - 用数据结构的容量 capacity 初始…

全流量安全分析之服务器非法外连

服务器非法外连监控的重要性在于保护服务器的安全性和防止被黑客利用&#xff0c;以下是几个重要的理由&#xff1a; 1、发现恶意活动&#xff1a;通过监控服务器的外连流量&#xff0c;可以及时发现是否有未经授权或可疑的连接尝试。这可能包括入侵攻击、数据泄露、恶意软件传…

基于微信小程序的家校通系统设计与实现(亮点:选题新颖、上传作业、批改作业、成绩统计)

文章目录 前言运行环境说明家长微信小程序端的主要功能有&#xff1a;教师微信小程序端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文…

MySQL 索引底层 B+Tree 原理解析

目录 一、前言二、B-Tree 和 BTree 的区别三、InnoDB 和 MyISAM 存储引擎索引存储区别MyISAMInnoDB 四、InnoDB 联合索引底层数据结构五、MySQL 中三次磁盘IO最大能检索多少数据 一、前言 索引是帮助高效获取数据排好序的数据结构&#xff0c;任何数据库都会使用到索引&#x…

漂流伞顾曼宁(顾曼)的创业之路:相信的力量

顾曼宁&#xff08;顾曼&#xff09;的心情在蚂蚁生态全球CEO大会上充满了感动和鼓舞。这是一个期待已久的盛会&#xff0c;时隔三年&#xff0c;蚂蚁生态全球的首席执行官们再次汇聚一堂。智慧与商业的碰撞下&#xff0c;更藏着感情与坚持的交汇。 蚂蚁集团举办了【相信展】的…

求生之路2服务器搭建插件安装及详细的游戏参数配置教程linux

求生之路2服务器搭建插件安装及详细的游戏参数配置教程linux 大家好我是艾西&#xff0c;在上一篇文章中我用windows系统给搭建演示了一遍怎么搭建自己的L4D2游戏。 那么也有不少小伙伴想知道linux系统的搭建方式以及在这个过程中有什么区别。 那么艾西今天就跟大家分享下用lin…

Nginx可视化管理工具结合cpolar实现远程访问内网服务

前言 Nginx Proxy Manager 是一个开源的反向代理工具&#xff0c;不需要了解太多 Nginx 或 Letsencrypt 的相关知识&#xff0c;即可快速将你的服务暴露到外部环境&#xff0c;并且支持 SSL 配置。基于 Tabler 的美观且安全的管理界面,无需了解 Nginx 即可轻松创建转发域、重定…

生产看板管理系统助力车间生产线提升产量、质量

随着制造业的发展和市场竞争的加剧&#xff0c;企业需要寻求创新的生产管理解决方案来提高生产效率和质量。而车间生产看板管理系统正是满足这一需求的理想选择。这种系统通过实时监控生产线上的各项指标&#xff0c;帮助企业及时发现生产问题&#xff0c;并采取相应的措施。通…

【送书活动】《客户成功的力量》——客户成功体系如何构建?请看这7步

文章目录 前言当下客户成功的痛点客户成功体系构建七步法作者简介赠书活动 前言 在中国企业服务领域的发展过程中&#xff0c;客户成功从在中国萌芽开始&#xff0c;行业内外对其讨论几乎没有停止过。近段时间&#xff0c;关于客户成功的讨论再次被业内广泛关注&#xff0c;原因…