基于卷积的图像分类识别(六):DenseNet FractalNet

news2024/11/24 16:17:31

系列文章目录

本专栏介绍基于深度学习进行图像识别的经典和前沿模型,将持续更新,包括不仅限于:AlexNet, ZFNet,VGG,GoogLeNet,ResNet,DenseNet,SENet,MobileNet,ShuffleNet,EifficientNet,Vision Transformer,Swin Transformer,Visual Attention Network,ConvNeXt, MLP-Mixer,As-MLP,ConvMixer,MetaFormer


DenseNet 文章目录

  • 系列文章目录
  • 前言
  • 一、Motivation
  • 二、Model Architecture
    • 2.1 DenseBlock
    • 2.2 Down-sampling Layer
    • 2.3 Growth rate
  • 三、Model Comparation
  • 四、 FractalNet
  • 五、Model Code
  • 总结


DenseNet论文名称:Densely Connected Convolutional Networks
DenseNet论文下载链接:https://arxiv.org/pdf/1608.06993.pdf
DenseNetpytorch代码实现:https://github.com/Arwin-Yu/Deep-Learning-Classification-Models-Based-CNN-or-Attention

FractalNet论文名称:FractalNet: Ultra-Deep Neural Networks without Residuals
FractalNet论文下载链接:https://arxiv.org/pdf/1605.07648

前言

作为CVPR2017年的Best Paper, DenseNet脱离了通过加深网络层数(VGG,ResNet)和加宽网络结构(GoogLeNet)来提升网络性能的定式思维, 从特征的角度考虑, 通过特征重用和旁路(Bypass)设置,既大幅度减少了网络的参数量,又在一定程度上缓解了梯度弥散问题的产生. 结合信息流和特征复用的假设, DenseNet当之无愧成为2017年计算机视觉顶会的年度最佳论文。另外,本文还对FractalNet进行了介绍,FractalNet与DenseNet的设计思想有异曲同工之妙。

先列下DenseNet的几个优点,感受下它的强大:

  • 1、减轻了vanishing-gradient(梯度消失)
  • 2、加强了feature的传递,更有效地利用了不同层的feature
  • 3、网络更易于训练,并具有一定的正则效果.
  • 4、因为整个网络并不深,所以一定程度上较少了参数数量

一、Motivation

卷积神经网络在沉睡了近20年后, 如今成为了深度学习方向最主要的网络结构之一. 从一开始的只有五层结构的LeNet, 到后来拥有19层结构的VGG【传送门】, 再到首次跨越100层网络的Highway Networks与1000层的ResNet, 网络层数的加深成为CNN发展的主要方向之一; 另一个方向则是以GoogLeNet【传送门】为代表的加深网络宽度.

随着CNN网络层数的不断增加,gradient vanishing和model degradation问题出现在了人们面前, BatchNormalization的广泛使用在一定程度上缓解了gradient vanishing的问题,而ResNet和Highway Networks通过构造旁路,进一步减少了gradient vanishing和model degradation的产生. Fractal Nets通过将不同深度的网络并行化, 在获得了深度的同时保证了梯度的传播, Stochastic Deep Network通过对网络中一些层进行失活,既证明了ResNet深度的冗余性,又缓解了上述问题的产生(失活操作对网络的影响与DenseNet还挺相似). 虽然这些不同的网络框架通过不同的实现加深的网络层数,但是他们都包含了相同的核心思想, 即:将不同层的feature map进行跨网络层的连接.

何恺明同学在提出ResNet时做出了这样的假设: 若某一较深的网络多出另一较浅网络的若干层,且这些层有能力学习到恒等映射, 那么这一较深网络训练得到的模型性能一定不会弱于该浅层网络. 通俗的说就是如果对某一网络中增添一些可以学到恒等映射的层组成新的网路, 那么最差的结果也是新网络中的这些层在训练后成为恒等映射而不会影响原网络的性能. 同样DenseNet在提出时也做过假设: 与其多次学习冗余的特征,特征复用是一种更好的特征提取方式.

二、Model Architecture

假设输入为一个图片 x 0 x_0 x0, 经过一个 L \mathrm{L} L 层的神经网络, 第 1 层的特征输出记作 x 1 x_1 x1 。 那么残差连接的公式如下所示。
x 1 = H 1 ( x 1 − 1 ) + x 1 − 1 x_1=H_1\left(x_{1-1}\right)+x_{1-1} x1=H1(x11)+x11
对于 ResNet 而言, 1 层的输出是 1-1 层的输出加上对 1-1 层输出的非线性变换。 对与 DensNet 而言, I 层的输出是之前所有层的输出集合, 公式如下所示。
x 1 = H 1 ( [ x 0 , x 1 , … , x l − 1 ] ) \boldsymbol{x}_{\mathbf{1}}=H_1\left(\left[x_0, x_1, \ldots, x_{l-1}\right]\right) x1=H1([x0,x1,,xl1])
其中 [ ] [ ] []代表concatenation(拼接),既将第0层 到 l-1 层的所有输出feature map在channel维度上组合在一起. 这里所用到的非线性变换H为BN+ReLU+ Conv(3×3)的组合。所以从这两个公式就能看出DenseNet和ResNet在本质上的区别,下面放一张DenseBlock的图片帮助理解公式:

2.1 DenseBlock

DenseNet Block
虽然这些残差模块中的连线很多,但是它们代表的操作只是一个空间上的拼接,并不是实际上的加减乘除运算,所以DenseNet相比传统的卷积神经网络可训练参数量更少。但是,为了在网络深层实现拼接操作,必须把之前的计算结果保存下来,这就比较占内存了。这是DenseNet的一大缺点。其代码实现如下所示。

1.class _DenseBlock(nn.ModuleDict):     
2.    def __init__(self, num_layers, input_c, bn_size, growth_rate,  drop_rate):    
3.        super(_DenseBlock, self).__init__()    
4.        for i in range(num_layers):    
5.            layer = _DenseLayer(input_c + i * growth_rate, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate)    
6.            self.add_module("denselayer%d" % (i + 1), layer)    
7.    
8.    def forward(self, init_features):    
9.        features = [init_features]    
10.        for name, layer in self.items():    
11.            new_features = layer(features)    
12.            features.append(new_features)    
13.        return torch.cat(features, 1)   

2.2 Down-sampling Layer

由于在DenseNet中需要对不同层的feature map进行cat操作,所以需要不同层的feature map保持相同的feature size,这就限制了网络中Down sampling的实现.为了使用Down sampling,作者将DenseNet分为多个stage,每个stage包含多个Dense blocks, 如下图所示:

DenseNet model

在同一个Denseblock中要求特征图尺寸保持相同大小,在不同Denseblock之间设置Transition Layers实现下采样操作,具体来说,transition Layer由BN+Conv(kernel size 1×1)+average-pooling(kernel size 2×2)组成。

注意这里1×1卷积是为了对特征通道数量进行降维;而池化才是为了降低特征图的尺寸。

在DenseNet模型中,Dense Block的每个子结构都将前面所有子结构的输出结果作为输入。例如,假设我们考虑Dense Block(3),该Block包含32个3×3的卷积操作。如果每一层输出的特征通道数为32,那么第32层的3×3卷积操作的输入通道数将是前31层所有输出的累积,即31×32,加上上一个Dense Block的输出特征通道数。这可以使得特征通道数达到近1000。

为了降低特征通道数,DenseNet在每个Dense Block后引入了Transition Layer,其中使用了1×1的卷积核进行降维操作。Transition Layer有一个参数名为’reduction’,取值范围为0到1,用于控制输出通道数相对于输入通道数的比例。默认情况下,'reduction’设为0.5,这意味着Transition Layer将特征通道数减少到原来的一半,然后将结果传给下一个Dense Block。

此外,为了防止过拟合,模型在最后的神经网络层中引入了dropout操作,用于随机丢弃一部分神经元,降低模型复杂度。其代码实现如下所示。

1.class _Transition(nn.Sequential):    
2.    def __init__(self,    
3.                 input_c: int,    
4.                 output_c: int):    
5.        super(_Transition, self).__init__()    
6.        self.add_module("norm", nn.BatchNorm2d(input_c))    
7.        self.add_module("relu", nn.ReLU(inplace=True))    
8.        self.add_module("conv", nn.Conv2d(input_c, output_c, kernel_size=1, stride=1, bias=False))    
9.        self.add_module("pool", nn.AvgPool2d(kernel_size=2, stride=2)) 

2.3 Growth rate

在Denseblock中,假设每一个卷积操作的输出为K个feature map, 那么第i层网络的输入便为(i-1)×K +(上一个Dense Block的输出channel), 这个K在论文中的名字叫做Growth rate, 默认是等于32的,这里我们可以看到DenseNet和现有网络的一个主要的不同点:DenseNet可以接受较少的特征图数量(32)作为网络层的输出。具体从网络参数如下图所示:

DenseNet Configuration

值得注意的是这里每个dense block的3X3卷积前面都包含了一个1X1的卷积操作,就是所谓的bottleneck layer,目的是减少输入的feature map数量,既能对通道数量降维来减少计算量,又能融合各个通道的特征。

三、Model Comparation

DenseNet and ResNet

上图是DenseNet与ResNet的对比图,在相同的错误率下,DenseNet的参数更少,计算复杂度也越低。
但是,DenseNet在实际训练中是非常占用内存的!原因是在计算的过程中需要保留浅层的feature map为了与后面的feature map就行拼接。简单说,虽然DenseNet参数量少,但是训练过程中的中间产物(feature map)多;这可能也是为什么DenseNet没有ResNet流行的原因吧。

四、 FractalNet

FractalNet(分型网络),2016年Gustav Larsson首次提出,忽然想起这个网络跟DenseNet有些类似,因此这里做简单的介绍。

  • 分形网络不像resNet那样连一条捷径,而是通过不同长度的子路径组合,网络选择合适的子路径集合提升模型表现

  • 分形网络体现的一种特性为:浅层子网提供更迅速的回答,深层子网提供更准确的回答。

FractalNet block
  1. 图中以粉红色的卷积层Convolution为基础层,实际上可以为其它类型的层或者子网络;
  2. 绿色的Join层一般可以用相加或concat,这里采取了相加然后取平均,因此所有基础层可以使用一样的channel数量
  3. fC(z) 中C表示列数,z表示输入,C=1表示一个基础层
  4. fC+1(z) 则如图所示,在右边叠加两个fC(z) ,左边接一个基础层
  5. 以此类推,当C等于4的时候,可以得到图中的f4(z)
  6. f4(z)作为一个block中,如图中最右边的网络所示,完整的网络接了5个block,block之间用Pool层连接,最后是预测层
  7. 令block个数为B,每个block中的列数为C,网络的总深度为B⋅2C−1

观察上图中左半部分,描述的是网络的单个模块(block)的结构。注意,这里的fC不是CNN中常用到的全连接层, 而是指分形次数为 C 的模块。fC 模块的表达式如下:
f 1 ( z ) = conv ⁡ ( z ) f C + 1 = [ ( f C ∘ f C ) ( z ) ] ⊕ [ conv ⁡ ( z ) ] \begin{aligned} &f_1(z)=\operatorname{conv}(z) \\ &f_{C+1}=\left[\left(f_C \circ f_C\right)(z)\right] \oplus[\operatorname{conv}(z)] \end{aligned} f1(z)=conv(z)fC+1=[(fCfC)(z)][conv(z)]

其中, ⊕ \oplus 是一个聚合(join)操作,本文推荐使用均值,而非常见的concat 或 addition。

中间图就是一个典型的 C=4 的block。把这些block堆叠起来,加上pooling和prediction层,就是完整的分类网络了,也就是右图。

网络结构看完了,FratalNet并不存在像ResNet那样skip connect的结构。但是,实际上如果把fC模块改成:
f C + 1 = [ ( f C ∘ f C ) ( z ) ] ⊕ z f_{C+1}=\left[\left(f_C \circ f_C\right)(z)\right] \oplus z fC+1=[(fCfC)(z)]z

这就变成一个 DenseNet 的结构了。。。
而众所皆知,DenseNet某种意义上是ResNet的改进版。。。所以个人感觉其实FratalNet也算是ResNet的近亲,这大概也是为什么业界还是ResNet用得多的原因吧——反正都差不多,不如挑个结构最简单的。为了方便大家理解,这里以Fractal的画图方式画一下Dense Block(包含4层)。

FractalNet block

最后,路径舍弃(Drop path)也是FractalNet的贡献之一,可以看作一种新的正则化规则。
ResNet收敛快,但还是要经过基本的卷积结构,卷积通用的问题是缺少有效的正则化方法。对于深的如ResNet采用BN,对于宽的如WideResNet采用Dropout。而FractalNet是基于路径,舍弃一些路径,是一种新的正则化方法(与Stochastic Depth Net中的随机失活几乎一样,好奇的可以翻我之前的博文。

对路径舍弃采用了 50% 局部以及 50% 全局的混合采样:
局部:连接层以固定几率舍弃每个输入,但我们保证至少一个输入保留。如图第1、3个。
全局:为了整个网络选出每条路径,并限制其为单列结构,激励每列成为有力的预测器,每列只做卷积。如图第2、4个。

FractalNet block

实验训练的时候,mini-batch之间交叉使用Local和Global。

五、Model Code

这里给出模型搭建的python代码(基于pytorch实现)。完整的代码是基于图像分类问题的(包括训练和推理脚本,自定义层等)详见我的GitHub: 完整代码链接

import re
from typing import Any, List, Tuple
from collections import OrderedDict

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.checkpoint as cp
from torch import Tensor


class _DenseLayer(nn.Module):
    def __init__(self, input_c: int, growth_rate: int, bn_size: int, drop_rate: float, memory_efficient: bool = False):
        super(_DenseLayer, self).__init__()

        self.add_module("norm1", nn.BatchNorm2d(input_c))
        self.add_module("relu1", nn.ReLU(inplace=True))
        self.add_module("conv1", nn.Conv2d(in_channels=input_c,  out_channels=bn_size * growth_rate, kernel_size=1, stride=1, bias=False))
        self.add_module("norm2", nn.BatchNorm2d(bn_size * growth_rate))
        self.add_module("relu2", nn.ReLU(inplace=True))
        self.add_module("conv2", nn.Conv2d(bn_size * growth_rate, growth_rate, kernel_size=3, stride=1, padding=1, bias=False))
        self.drop_rate = drop_rate
        self.memory_efficient = memory_efficient

    def bn_function(self, inputs: List[Tensor]):
        concat_features = torch.cat(inputs, 1)
        bottleneck_output = self.conv1(self.relu1(self.norm1(concat_features)))
        return bottleneck_output

    @staticmethod
    def any_requires_grad(inputs: List[Tensor]):
        for tensor in inputs:
            if tensor.requires_grad:
                return True
        return False

    @torch.jit.unused
    def call_checkpoint_bottleneck(self, inputs: List[Tensor]):
        def closure(*inp):
            return self.bn_function(inp)

        return cp.checkpoint(closure, *inputs)

    def forward(self, inputs: Tensor):
        if isinstance(inputs, Tensor):
            prev_features = [inputs]
        else:
            prev_features = inputs

        if self.memory_efficient and self.any_requires_grad(prev_features):
            if torch.jit.is_scripting():
                raise Exception("memory efficient not supported in JIT")

            bottleneck_output = self.call_checkpoint_bottleneck(prev_features)
        else:
            bottleneck_output = self.bn_function(prev_features)

        new_features = self.conv2(self.relu2(self.norm2(bottleneck_output)))
        if self.drop_rate > 0:
            new_features = F.dropout(new_features,  p=self.drop_rate, training=self.training)

        return new_features


class _DenseBlock(nn.ModuleDict): 
    def __init__(self, num_layers: int, input_c: int, bn_size: int, growth_rate: int,  drop_rate: float,  memory_efficient: bool = False):
        super(_DenseBlock, self).__init__()
        for i in range(num_layers):
            layer = _DenseLayer(input_c + i * growth_rate, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate,  memory_efficient=memory_efficient)
            self.add_module("denselayer%d" % (i + 1), layer)

    def forward(self, init_features: Tensor):
        features = [init_features]
        for name, layer in self.items():
            new_features = layer(features)
            features.append(new_features)
        return torch.cat(features, 1)


class _Transition(nn.Sequential):
    def __init__(self,
                 input_c: int,
                 output_c: int):
        super(_Transition, self).__init__()
        self.add_module("norm", nn.BatchNorm2d(input_c))
        self.add_module("relu", nn.ReLU(inplace=True))
        self.add_module("conv", nn.Conv2d(input_c, output_c, kernel_size=1, stride=1, bias=False))
        self.add_module("pool", nn.AvgPool2d(kernel_size=2, stride=2))


class DenseNet(nn.Module):
    """
    Densenet-BC model class for imagenet
    Args:
        growth_rate (int) - how many filters to add each layer (`k` in paper)
        block_config (list of 4 ints) - how many layers in each pooling block
        num_init_features (int) - the number of filters to learn in the first convolution layer
        bn_size (int) - multiplicative factor for number of bottle neck layers
          (i.e. bn_size * k features in the bottleneck layer)
        drop_rate (float) - dropout rate after each dense layer
        num_classes (int) - number of classification classes
        memory_efficient (bool) - If True, uses checkpointing. Much more memory efficient
    """

    def __init__(self,
                 growth_rate: int = 32,
                 block_config: Tuple[int, int, int, int] = (6, 12, 24, 16),
                 num_init_features: int = 64,
                 bn_size: int = 4,
                 drop_rate: float = 0,
                 num_classes: int = 1000,
                 memory_efficient: bool = False):
        super(DenseNet, self).__init__()

        # first conv+bn+relu+pool
        self.features = nn.Sequential(OrderedDict([
            ("conv0", nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)),
            ("norm0", nn.BatchNorm2d(num_init_features)),
            ("relu0", nn.ReLU(inplace=True)),
            ("pool0", nn.MaxPool2d(kernel_size=3, stride=2, padding=1)),
        ]))

        # each dense block
        num_features = num_init_features
        for i, num_layers in enumerate(block_config):
            block = _DenseBlock(num_layers=num_layers,
                                input_c=num_features,
                                bn_size=bn_size,
                                growth_rate=growth_rate,
                                drop_rate=drop_rate,
                                memory_efficient=memory_efficient)
            self.features.add_module("denseblock%d" % (i + 1), block)
            num_features = num_features + num_layers * growth_rate

            if i != len(block_config) - 1:
                trans = _Transition(input_c=num_features,
                                    output_c=num_features // 2)
                self.features.add_module("transition%d" % (i + 1), trans)
                num_features = num_features // 2

        # finnal batch norm
        self.features.add_module("norm5", nn.BatchNorm2d(num_features))

        # fc layer
        self.classifier = nn.Linear(num_features, num_classes)

        # init weights
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.constant_(m.bias, 0)

    def forward(self, x: Tensor):
        features = self.features(x)
        out = F.relu(features, inplace=True)
        out = F.adaptive_avg_pool2d(out, (1, 1))
        out = torch.flatten(out, 1)
        out = self.classifier(out)
        return out


def densenet121(num_classes):
    # Top-1 error: 25.35%
    # 'densenet121': 'https://download.pytorch.org/models/densenet121-a639ec97.pth'
    return DenseNet(growth_rate=32,
                    block_config=(6, 12, 24, 16),
                    num_init_features=64,
                    num_classes=num_classes)


def densenet169(num_classes):
    # Top-1 error: 24.00%
    # 'densenet169': 'https://download.pytorch.org/models/densenet169-b2777c0a.pth'
    return DenseNet(growth_rate=32,
                    block_config=(6, 12, 32, 32),
                    num_init_features=64,
                    num_classes=num_classes)


def densenet201(num_classes):
    # Top-1 error: 22.80%
    # 'densenet201': 'https://download.pytorch.org/models/densenet201-c1103571.pth'
    return DenseNet(growth_rate=32,
                    block_config=(6, 12, 48, 32),
                    num_init_features=64,
                    num_classes=num_classes)


def densenet161(num_classes):
    # Top-1 error: 22.35%
    # 'densenet161': 'https://download.pytorch.org/models/densenet161-8d451a50.pth'
    return DenseNet(growth_rate=48,
                    block_config=(6, 12, 36, 24),
                    num_init_features=96,
                    num_classes=num_classes)

总结

DenseNet是一种深度学习模型,它在计算机视觉任务中表现出色。与传统的卷积神经网络相比,DenseNet引入了密集连接的概念,使得网络内部的信息流动更加充分和高效。以下是对DenseNet模型的总结:

密集连接:DenseNet的核心思想是通过密集连接来增强特征传播和重用。在传统的卷积神经网络中,每个层只连接到其后续的层。而在DenseNet中,每个层都与后续所有层直接相连。这种密集连接使得底层的特征可以直接传递到更深层,有效地解决了梯度消失的问题。

特征重用:由于密集连接,DenseNet中的每个层都可以接收到前面所有层的特征图。这种设计使得网络能够更好地重用之前层的特征信息,从而提高了参数的利用效率和整体模型的性能。

层间特征融合:DenseNet采用了一种称为“密集块”的结构,它由多个具有相同输出尺寸的层组成。这些层通过拼接操作将其输入和输出连接在一起。这样,每个密集块都可以将前面所有层的特征融合在一起,形成更丰富的表示。

参数和计算效率:尽管DenseNet中参数的数量比传统的卷积神经网络稍多,但由于特征的重用,它在实际训练和推断中可以更好地利用参数。此外,DenseNet中的特征重用还减少了冗余计算,使得整体计算效率更高。

模型性能:DenseNet在许多计算机视觉任务上表现出色,包括图像分类、目标检测和图像分割等。由于密集连接的引入,DenseNet能够捕捉更丰富的特征表示,提高模型的准确性和鲁棒性。

总之,DenseNet通过引入密集连接的概念,实现了更充分和高效的信息流动,增强了特征重用和表示能力。这使得DenseNet成为了一个强大的深度学习模型,在计算机视觉领域得到广泛应用并取得了显著的性能提升。

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

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

相关文章

如何搭建自己的git服务器

GitHub,Gitee 想来大家都用过,我们的代码就是托管在这些平台上的。因此,你可能好奇为什么我们不自己搭建一个 git 呢服务器?下面,就开始教大家如何一步步搭建自己的 git 服务器(试验成功的那一刻还是很让人…

Java 中 ArrayList 和 LinkedList 有什么区别

在Java中,ArrayList和LinkedList是两种常见的集合类。它们都实现了List接口,提供了类似数组的功能,可以存储任意类型的对象。虽然它们都可以实现相同的功能,但是它们的底层实现方式有所不同,因此在性能和用途上也存在一…

dom4j解析XML文件

主要为了讲解Mybatis中如何用dom4j解析XML,这里当作dom4j解析.XML文件的练习 引入mybatis配置文件和一个.xml文件 都是.xml <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN…

【C++】| 04——STL | 容器_vector

系列文章目录 【C】| 01——泛型编程 | 模板 【C】| 02——STL | 初识 【C】| 03——STL | 迭代器 【C】| 04——STL | 容器_vector 文章目录 1. vector容器2. vector库2.1 迭代器相关函数2.1 ww 1. vector容器 vector 与 动态数组 相似&#xff0c;可以自动调节自身大小。元素…

基于SpringBoot的美容院管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SpringBoot 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录 一、项目简介 二、系…

Windows10中英文切换按钮消失?一招解决

目录 问题场景&#xff1a; 问题描述 原因分析&#xff1a; 解决方案&#xff1a; 1. 打开设置&#xff0c;选择时间和语言 2. 进入日期时间设置 3. 进入高级键盘设置 4. 勾选这个勾选框&#xff0c;问题解决 问题场景&#xff1a; 博主玩道德与法治V在线模式时&#…

BGP防环,路由反射器,BGP联盟

数据的出口是路由的入口 ospf内部&#xff1a;10 ospf外部&#xff1a;150 静态路由&#xff1a;60 RIP&#xff1a;100 BGP&#xff1a;255 当下一跳是0.0.0.0 表示的是自己 display bgp peer //查看bgp邻居表 display bgp routing-table //查看bgp数据库 display i…

WPF MaterialDesign 初学项目实战(3)动态侧边栏

其他文章 WPF MaterialDesign 初学项目实战&#xff08;0&#xff09;:github 项目Demo运行 WPF MaterialDesign 初学项目实战&#xff08;1&#xff09;首页搭建 WPF MaterialDesign 初学项目实战&#xff08;2&#xff09;首页导航栏样式 创建侧边栏实体类 新建MenuBar文件…

Python动物图像分割API简单调用实例演示,阿里达摩院视觉智能开放平台使用步骤

阿里云视觉智能开放平台 - 动物分割 效果图演示平台入口创建获取密钥本地图片转 URL 与密钥测试代码调用演示语义分割知识拓展阿里云达摩院智能视觉开放平台 效果图演示 调用本地图片处理后可以直接保存到本地&#xff0c;右边就是分割好的效果图&#xff0c;可以看到分割的效…

基于卷积的图像分类识别(五):ResNet ResNeXt

系列文章目录 本专栏介绍基于深度学习进行图像识别的经典和前沿模型&#xff0c;将持续更新&#xff0c;包括不仅限于&#xff1a;AlexNet&#xff0c; ZFNet&#xff0c;VGG&#xff0c;GoogLeNet&#xff0c;ResNet&#xff0c;DenseNet&#xff0c;SENet&#xff0c;MobileN…

C语言实现扫雷

总有一天你要一个人在暗夜中&#xff0c;向那座桥走过去 目录 一、文件及其对应代码 1.test.c 2.game.c 3.game.h 二、数组创建解析 1.创建两个数组的原因 2.预设数组较大的原因 三、计算周围雷的个数 四、向外扩展并延伸判断 扫雷游戏&#xff0c;相信大家都玩过&am…

【c++】图解类和对象(上)

类和对象&#xff08;上&#xff09; 文章目录 类和对象&#xff08;上&#xff09;一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装1.访问限定符2.封装 五、类的作用域六、类的实例化七、类对象模型八、this指针总结 一、面向过程和面向对象…

Mysql中select语句的执行流程?

Mysql中select语句的执行流程&#xff1f; 答&#xff1a; SELECT 语句的执行过程为&#xff1a;连接、查询缓存、a词法分析&#xff0c;语法分析&#xff0c;语义分析&#xff0c;构造执行树&#xff0c;生成执行计划、执行器执行计划&#xff0c;下面开始梳理一次完整的查询…

【MySQL】视图,事务、隔离级别

视图--虚表&#xff0c;不在数据库中存放数据&#xff0c;数据源于基本表。 为什么要使用视图 简化复杂的sql操作&#xff0c;在编写查询后&#xff0c;可以方便的重用它而不必知道它的查询细节。重复使用该sql语句。使用表的组成部分而不是整个表。保护数据&#xff0c;可以给…

vscode编译的时候:未定义标识符 thread

vscode编译的时候&#xff1a;未定义标识符 thread thread’ was not declared in this scope" 未定义标识符 thread 原因 MinGW GCC当前仍缺少标准C 11线程类的实现。 对于跨平台线程实现&#xff0c;GCC标准库依赖于gthreads / pthreads库。如果该库不可用&#xf…

手搓GPT系列之 - 通过理解LSTM的反向传播过程,理解LSTM解决梯度消失的原理 - 逐条解释LSTM创始论文全部推导公式,配超多图帮助理解(上篇)

1. 前言 说起RNN和LSTM&#xff0c;就绕不过Sepp Hochreiter 1997年的开山大作 Long Short-term Memory。奈何这篇文章写的实在是太劝退&#xff0c;整篇论文就2张图&#xff0c;网上很多介绍LSTM的文章都对这个模型反向传播的部分避重就轻&#xff0c;更少见&#xff08;反正…

2023/5/14学习总结

这道题我们可以看到数据范围很小 &#xff0c;所以可以使用暴力枚举&#xff0c;将所有可以组成长方形的长宽全遍历一遍&#xff0c;同时要满足这个长方形里没有障碍物的条件&#xff0c;取得周长最大值 #include<bits/stdc.h> using namespace std; typedef long long …

JavaSE基础(六)—— 面向对象、封装、对象内存图、成员变量和局部变量区别

目录 一、面向对象对象介绍 1. 面向对象的重点学习什么 二、设计对象并使用 1. 设计类&#xff0c;创建对象并使用 1.1 如何得到对象 1.2 如何使用对象 2. 定义类的几个补充注意事项 2.1 对象的成员变量的默认值规则 三、对象内存图 1. 多个对象内存图 2. 两个变量指…

Springboot +Flowable,流程表单应用之静态表单

一.简介 整体上来说&#xff0c;我们可以将Flowable 的表单分为三种不同的类型&#xff1a; 动态表单 这种表单定义方式我们可以配置表单中每一个字段的可读性、可写性、是否必填等信息&#xff0c;不过不能定义完整的表单页面。外置表单 外置表单我们只需要定义一下表单的 k…

生命周期、数据共享、ref引用、购物车案例

生命周期&数据共享 1.组件的生命周期2.组件之间的数据共享3.ref 引用4.购物车案例 1.组件的生命周期 生命周期 & 生命周期函数 生命周期&#xff08;Life Cycle&#xff09;是指一个组件从创建 -> 运行 -> 销毁的整个阶段&#xff0c;强调的是一个时间段。 生命…