EfficientFormer实战:使用EfficientFormerV2实现图像分类任务(一)

摘要

EfficientFormerV2是一种通过重新思考ViT设计选择和引入细粒度联合搜索策略而开发出的新型移动视觉骨干网络。它结合了卷积和变换器的优势,通过一系列高效的设计改进和搜索方法,实现了在移动设备上既轻又快且保持高性能的目标。这一成果为在资源受限的硬件上有效部署视觉变换器模型提供了新的思路,其主要特点和优势可以概括如下:

特点

  1. 低延迟与高效参数

    • EfficientFormerV2通过一系列设计改进和搜索策略,实现了与MobileNet相当的低延迟和高效参数数量。在相同的模型大小和延迟约束下,其性能超越了传统轻量级CNN模型。
  2. 细粒度联合搜索策略

    • 该模型引入了细粒度的联合搜索策略,该策略同时优化模型的延迟和参数数量,从而找到在移动设备上高效运行的架构。
  3. 混合架构设计

    • EfficientFormerV2结合了卷积神经网络(CNNs)和变换器(ViT)的优势。它在早期阶段使用卷积来捕获局部信息,并在后期阶段使用多头自注意力(MHSA)来模拟全局依赖性。
  4. 令牌混合器与前馈网络改进

    • 使用深度卷积(DWCONV)替代传统的平均池化层作为令牌混合器,提高了模型性能而不引入额外延迟。同时,改进了前馈网络的设计。
  5. MHSA模块增强

    • 通过向值矩阵(V)注入局部信息,并在注意力头之间添加全连接层来增强MHSA模块的性能,进一步提升模型性能。
  6. 高效注意力机制应用

    • 提出了一种在高分辨率特征上有效应用MHSA的策略,通过下采样查询、键和值到固定分辨率,并插值回原始分辨率,从而在不显著增加延迟的情况下应用注意力机制。
  7. 双路径注意力下采样

    • 结合了静态局部下采样(如池化)和可学习的局部下采样(如深度卷积),形成双路径注意力下采样策略,提高了下采样过程的效率。
      在这里插入图片描述

优点

  1. 性能优越

    • 在ImageNet-1K等基准数据集上,EfficientFormerV2在相同或更低的模型大小和延迟下,表现出比MobileNet等轻量级CNN更高的准确率。
  2. 灵活性高

    • 通过细粒度的联合搜索策略,可以生成一系列具有不同模型大小和延迟的模型变体,以适应不同的移动设备和应用场景。
  3. 易于部署

    • 专为移动设备设计,具有较低的延迟和高效的参数数量,使得模型在实际应用中易于部署和推理。
  4. 兼容多种任务

    • EfficientFormerV2不仅在分类任务上表现出色,还可在目标检测、实例分割和语义分割等下游任务中作为骨干网络使用,并提升这些任务的性能。

本文使用EfficientFormerV2模型实现图像分类任务,模型选择efficientformerv2_s0,在植物幼苗分类任务ACC达到了96%+。

在这里插入图片描述
在这里插入图片描述

通过深入阅读本文,您将能够掌握以下关键技能与知识:

  1. 数据增强的多种策略:包括利用PyTorch的transforms库进行基本增强,以及进阶技巧如CutOut、MixUp、CutMix等,这些方法能显著提升模型泛化能力。

  2. GCViT模型的训练实现:了解如何从头开始构建并训练EfficientFormerV2(或其他深度学习模型),涵盖模型定义、数据加载、训练循环等关键环节。

  3. 混合精度训练:学习如何利用PyTorch自带的混合精度训练功能,加速训练过程同时减少内存消耗。

  4. 梯度裁剪技术:掌握梯度裁剪的应用,有效防止梯度爆炸问题,确保训练过程的稳定性。

  5. 分布式数据并行(DP)训练:了解如何在多GPU环境下使用PyTorch的分布式数据并行功能,加速大规模模型训练。

  6. 可视化训练过程:学习如何绘制训练过程中的loss和accuracy曲线,直观监控模型学习状况。

  7. 评估与生成报告:掌握在验证集上评估模型性能的方法,并生成详细的评估报告,包括ACC等指标。

  8. 测试脚本编写:学会编写测试脚本,对测试集进行预测,评估模型在实际应用中的表现。

  9. 学习率调整策略:理解并应用余弦退火策略动态调整学习率,优化训练效果。

  10. 自定义统计工具:使用AverageMeter类或其他工具统计和记录训练过程中的ACC、loss等关键指标,便于后续分析。

  11. 深入理解ACC1与ACC5:掌握图像分类任务中ACC1(Top-1准确率)和ACC5(Top-5准确率)的含义及其计算方法。

  12. 指数移动平均(EMA):学习如何在模型训练中应用EMA技术,进一步提升模型在测试集上的表现。

若您在以上任一领域基础尚浅,感到理解困难,推荐您参考我的专栏“经典主干网络精讲与实战”,该专栏从零开始,循序渐进地讲解上述所有知识点,助您轻松掌握深度学习中的这些核心技能。

安装包

安装timm

使用pip就行,命令:

pip install timm

mixup增强和EMA用到了timm

数据增强Cutout和Mixup

为了提高模型的泛化能力和性能,我在数据预处理阶段加入了Cutout和Mixup这两种数据增强技术。Cutout通过随机遮挡图像的一部分来强制模型学习更鲁棒的特征,而Mixup则通过混合两张图像及其标签来生成新的训练样本,从而增加数据的多样性。实现这两种增强需要安装torchtoolbox。安装命令:

pip install torchtoolbox

Cutout实现,在transforms中。

from torchtoolbox.transform import Cutout
# 数据预处理
transform = transforms.Compose([transforms.Resize((224, 224)),Cutout(),transforms.ToTensor(),transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])

需要导入包:from timm.data.mixup import Mixup,

定义Mixup,和SoftTargetCrossEntropy

  mixup_fn = Mixup(mixup_alpha=0.8, cutmix_alpha=1.0, cutmix_minmax=None,prob=0.1, switch_prob=0.5, mode='batch',label_smoothing=0.1, num_classes=12)criterion_train = SoftTargetCrossEntropy()

Mixup 是一种在图像分类任务中常用的数据增强技术,它通过将两张图像以及其对应的标签进行线性组合来生成新的数据和标签。
参数详解:

mixup_alpha (float): mixup alpha 值,如果 > 0,则 mixup 处于活动状态。

cutmix_alpha (float):cutmix alpha 值,如果 > 0,cutmix 处于活动状态。

cutmix_minmax (List[float]):cutmix 最小/最大图像比率,cutmix 处于活动状态,如果不是 None,则使用这个 vs alpha。

如果设置了 cutmix_minmax 则cutmix_alpha 默认为1.0

prob (float): 每批次或元素应用 mixup 或 cutmix 的概率。

switch_prob (float): 当两者都处于活动状态时切换cutmix 和mixup 的概率 。

mode (str): 如何应用 mixup/cutmix 参数(每个’batch’,‘pair’(元素对),‘elem’(元素)。

correct_lam (bool): 当 cutmix bbox 被图像边框剪裁时应用。 lambda 校正

label_smoothing (float):将标签平滑应用于混合目标张量。

num_classes (int): 目标的类数。

EMA

EMA(Exponential Moving Average)在深度学习中是一种用于模型参数优化的技术,它通过计算参数的指数移动平均值来平滑模型的学习过程。这种方法有助于提高模型的稳定性和泛化能力,特别是在训练后期。以下是关于EMA的总结,表达进行了优化:

EMA概述

EMA是一种加权移动平均技术,其中每个新的平均值都是前一个平均值和当前值的加权和。在深度学习中,EMA被用于模型参数的更新,以减缓参数在训练过程中的快速波动,从而得到更加平滑和稳定的模型表现。

工作原理

在训练过程中,除了维护当前模型的参数外,还额外保存一份EMA参数。每个训练步骤或每隔一定步骤,根据当前模型参数和EMA参数,按照指数衰减的方式更新EMA参数。具体来说,EMA参数的更新公式通常如下:

EMA new = decay × EMA old + ( 1 − decay ) × model_parameters \text{EMA}_{\text{new}} = \text{decay} \times \text{EMA}_{\text{old}} + (1 - \text{decay}) \times \text{model\_parameters} EMAnew=decay×EMAold+(1decay)×model_parameters
其中,decay是一个介于0和1之间的超参数,控制着旧EMA值和新模型参数值之间的权重分配。较大的decay值意味着EMA更新时更多地依赖于旧值,即平滑效果更强。

应用优势

  1. 稳定性:EMA通过平滑参数更新过程,减少了模型在训练过程中的波动,使得模型更加稳定。
  2. 泛化能力:由于EMA参数是历史参数的平滑版本,它往往能捕捉到模型训练过程中的全局趋势,因此在测试或评估时,使用EMA参数往往能获得更好的泛化性能。
  3. 快速收敛:虽然EMA本身不直接加速训练过程,但通过稳定模型参数,它可能间接地帮助模型更快地收敛到更优的解。

使用场景

EMA在深度学习中的使用场景广泛,特别是在需要高度稳定性和良好泛化能力的任务中,如图像分类、目标检测等。在训练大型模型时,EMA尤其有用,因为它可以帮助减少过拟合的风险,并提高模型在未见数据上的表现。

具体实现如下:


import logging
from collections import OrderedDict
from copy import deepcopy
import torch
import torch.nn as nn_logger = logging.getLogger(__name__)class ModelEma:def __init__(self, model, decay=0.9999, device='', resume=''):# make a copy of the model for accumulating moving average of weightsself.ema = deepcopy(model)self.ema.eval()self.decay = decayself.device = device  # perform ema on different device from model if setif device:self.ema.to(device=device)self.ema_has_module = hasattr(self.ema, 'module')if resume:self._load_checkpoint(resume)for p in self.ema.parameters():p.requires_grad_(False)def _load_checkpoint(self, checkpoint_path):checkpoint = torch.load(checkpoint_path, map_location='cpu')assert isinstance(checkpoint, dict)if 'state_dict_ema' in checkpoint:new_state_dict = OrderedDict()for k, v in checkpoint['state_dict_ema'].items():# ema model may have been wrapped by DataParallel, and need module prefixif self.ema_has_module:name = 'module.' + k if not k.startswith('module') else kelse:name = knew_state_dict[name] = vself.ema.load_state_dict(new_state_dict)_logger.info("Loaded state_dict_ema")else:_logger.warning("Failed to find state_dict_ema, starting from loaded model weights")def update(self, model):# correct a mismatch in state dict keysneeds_module = hasattr(model, 'module') and not self.ema_has_modulewith torch.no_grad():msd = model.state_dict()for k, ema_v in self.ema.state_dict().items():if needs_module:k = 'module.' + kmodel_v = msd[k].detach()if self.device:model_v = model_v.to(device=self.device)ema_v.copy_(ema_v * self.decay + (1. - self.decay) * model_v)

加入到模型中。

#初始化
if use_ema:model_ema = ModelEma(model_ft,decay=model_ema_decay,device='cpu',resume=resume)# 训练过程中,更新完参数后,同步update shadow weights
def train():optimizer.step()if model_ema is not None:model_ema.update(model)# 将model_ema传入验证函数中
val(model_ema.ema, DEVICE, test_loader)

针对没有预训练的模型,容易出现EMA不上分的情况,这点大家要注意啊!

项目结构

EfficientFormer_Demo
├─data1
│  ├─Black-grass
│  ├─Charlock
│  ├─Cleavers
│  ├─Common Chickweed
│  ├─Common wheat
│  ├─Fat Hen
│  ├─Loose Silky-bent
│  ├─Maize
│  ├─Scentless Mayweed
│  ├─Shepherds Purse
│  ├─Small-flowered Cranesbill
│  └─Sugar beet
├─models
│  └─efficientformer_v2.py
├─mean_std.py
├─makedata.py
├─train_timm.py
├─train.py
└─test.py

mean_std.py:计算mean和std的值。
makedata.py:生成数据集。
train.py:训练models文件下efficientformer_v2的模型
train_timm.py:训练timm库中的efficientformer_v2模型,timm库中的模型有预训练模型。
models:来源官方代码。

计算mean和std

在深度学习中,特别是在处理图像数据时,计算数据的均值(mean)和标准差(standard deviation, std)并进行归一化(Normalization)是加速模型收敛、提高模型性能的关键步骤之一。这里我将详细解释这两个概念,并讨论它们如何帮助模型学习。

均值(Mean)

均值是所有数值加和后除以数值的个数得到的平均值。在图像处理中,我们通常对每个颜色通道(如RGB图像的三个通道)分别计算均值。这意味着,如果我们的数据集包含多张图像,我们会计算所有图像在R通道上的像素值的均值,同样地,我们也会计算G通道和B通道的均值。

标准差(Standard Deviation, Std)

标准差是衡量数据分布离散程度的统计量。它反映了数据点与均值的偏离程度。在计算图像数据的标准差时,我们也是针对每个颜色通道分别进行的。标准差较大的颜色通道意味着该通道上的像素值变化较大,而标准差较小的通道则相对较为稳定。

归一化(Normalization)

归一化是将数据按比例缩放,使之落入一个小的特定区间,通常是[0, 1]或[-1, 1]。在图像处理中,我们通常会使用计算得到的均值和标准差来进行归一化,公式如下:

Normalized Value = Original Value − Mean Std \text{Normalized Value} = \frac{\text{Original Value} - \text{Mean}}{\text{Std}} Normalized Value=StdOriginal ValueMean

注意,在某些情况下,为了简化计算并确保数据非负,我们可能会选择将数据缩放到[0, 1]区间,这时使用的是最大最小值归一化,而不是基于均值和标准差的归一化。但在这里,我们主要讨论基于均值和标准差的归一化,因为它能保留数据的分布特性。

为什么需要归一化?

  1. 加速收敛:归一化后的数据具有相似的尺度,这有助于梯度下降算法更快地找到最优解,因为不同特征的梯度更新将在同一数量级上,从而避免了某些特征因尺度过大或过小而导致的训练缓慢或梯度消失/爆炸问题。

  2. 提高精度:归一化可以改善模型的泛化能力,因为它使得模型更容易学习到特征之间的相对关系,而不是被特征的绝对大小所影响。

  3. 稳定性:归一化后的数据更加稳定,减少了训练过程中的波动,有助于模型更加稳定地收敛。

如何计算和使用mean和std

  1. 计算全局mean和std:在整个数据集上计算mean和std。这通常是在训练开始前进行的,并使用这些值来归一化训练集、验证集和测试集。

  2. 使用库函数:许多深度学习框架(如PyTorch、TensorFlow等)提供了计算mean和std的便捷函数,并可以直接用于数据集的归一化。

  3. 动态调整:在某些情况下,特别是当数据集非常大或持续更新时,可能需要动态地计算mean和std。这通常涉及到在训练过程中使用移动平均(如EMA)来更新这些统计量。

计算并使用数据的mean和std进行归一化是深度学习中的一项基本且重要的预处理步骤,它对于加速模型收敛、提高模型性能和稳定性具有重要意义。新建mean_std.py,插入代码:

from torchvision.datasets import ImageFolder
import torch
from torchvision import transformsdef get_mean_and_std(train_data):train_loader = torch.utils.data.DataLoader(train_data, batch_size=1, shuffle=False, num_workers=0,pin_memory=True)mean = torch.zeros(3)std = torch.zeros(3)for X, _ in train_loader:for d in range(3):mean[d] += X[:, d, :, :].mean()std[d] += X[:, d, :, :].std()mean.div_(len(train_data))std.div_(len(train_data))return list(mean.numpy()), list(std.numpy())if __name__ == '__main__':train_dataset = ImageFolder(root=r'data1', transform=transforms.ToTensor())print(get_mean_and_std(train_dataset))

数据集结构:

image-20220221153058619

运行结果:

([0.3281186, 0.28937867, 0.20702125], [0.09407319, 0.09732835, 0.106712654])

把这个结果记录下来,后面要用!

生成数据集

我们整理还的图像分类的数据集结构是这样的

data
├─Black-grass
├─Charlock
├─Cleavers
├─Common Chickweed
├─Common wheat
├─Fat Hen
├─Loose Silky-bent
├─Maize
├─Scentless Mayweed
├─Shepherds Purse
├─Small-flowered Cranesbill
└─Sugar beet

pytorch和keras默认加载方式是ImageNet数据集格式,格式是

├─data
│  ├─val
│  │   ├─Black-grass
│  │   ├─Charlock
│  │   ├─Cleavers
│  │   ├─Common Chickweed
│  │   ├─Common wheat
│  │   ├─Fat Hen
│  │   ├─Loose Silky-bent
│  │   ├─Maize
│  │   ├─Scentless Mayweed
│  │   ├─Shepherds Purse
│  │   ├─Small-flowered Cranesbill
│  │   └─Sugar beet
│  └─train
│      ├─Black-grass
│      ├─Charlock
│      ├─Cleavers
│      ├─Common Chickweed
│      ├─Common wheat
│      ├─Fat Hen
│      ├─Loose Silky-bent
│      ├─Maize
│      ├─Scentless Mayweed
│      ├─Shepherds Purse
│      ├─Small-flowered Cranesbill
│      └─Sugar beet

新增格式转化脚本makedata.py,插入代码:

import glob
import os
import shutilimage_list=glob.glob('data1/*/*.png')
print(image_list)
file_dir='data'
if os.path.exists(file_dir):print('true')#os.rmdir(file_dir)shutil.rmtree(file_dir)#删除再建立os.makedirs(file_dir)
else:os.makedirs(file_dir)from sklearn.model_selection import train_test_split
trainval_files, val_files = train_test_split(image_list, test_size=0.3, random_state=42)
train_dir='train'
val_dir='val'
train_root=os.path.join(file_dir,train_dir)
val_root=os.path.join(file_dir,val_dir)
for file in trainval_files:file_class=file.replace("\\","/").split('/')[-2]file_name=file.replace("\\","/").split('/')[-1]file_class=os.path.join(train_root,file_class)if not os.path.isdir(file_class):os.makedirs(file_class)shutil.copy(file, file_class + '/' + file_name)for file in val_files:file_class=file.replace("\\","/").split('/')[-2]file_name=file.replace("\\","/").split('/')[-1]file_class=os.path.join(val_root,file_class)if not os.path.isdir(file_class):os.makedirs(file_class)shutil.copy(file, file_class + '/' + file_name)

完成上面的内容就可以开启训练和测试了。

EfficientFormerV2代码

"""
EfficientFormer_v2
"""
import os
import copy
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
from typing import Dict
import itertoolsfrom timm.data import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
from timm.models.layers import DropPath, trunc_normal_
from timm.models.registry import register_model
from timm.layers.helpers import to_2tupleEfficientFormer_width = {'L': [40, 80, 192, 384],  # 26m 83.3% 6attn'S2': [32, 64, 144, 288],  # 12m 81.6% 4attn dp0.02'S1': [32, 48, 120, 224],  # 6.1m 79.0'S0': [32, 48, 96, 176],  # 75.0 75.7
}EfficientFormer_depth = {'L': [5, 5, 15, 10],  # 26m 83.3%'S2': [4, 4, 12, 8],  # 12m'S1': [3, 3, 9, 6],  # 79.0'S0': [2, 2, 6, 4],  # 75.7
}# 26m
expansion_ratios_L = {'0': [4, 4, 4, 4, 4],'1': [4, 4, 4, 4, 4],'2': [4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4],'3': [4, 4, 4, 3, 3, 3, 3, 4, 4, 4],
}# 12m
expansion_ratios_S2 = {'0': [4, 4, 4, 4],'1': [4, 4, 4, 4],'2': [4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4],'3': [4, 4, 3, 3, 3, 3, 4, 4],
}# 6.1m
expansion_ratios_S1 = {'0': [4, 4, 4],'1': [4, 4, 4],'2': [4, 4, 3, 3, 3, 3, 4, 4, 4],'3': [4, 4, 3, 3, 4, 4],
}# 3.5m
expansion_ratios_S0 = {'0': [4, 4],'1': [4, 4],'2': [4, 3, 3, 3, 4, 4],'3': [4, 3, 3, 4],
}class Attention4D(torch.nn.Module):def __init__(self, dim=384, key_dim=32, num_heads=8,attn_ratio=4,resolution=7,act_layer=nn.ReLU,stride=None):super().__init__()self.num_heads = num_headsself.scale = key_dim ** -0.5self.key_dim = key_dimself.nh_kd = nh_kd = key_dim * num_headsif stride is not None:self.resolution = math.ceil(resolution / stride)self.stride_conv = nn.Sequential(nn.Conv2d(dim, dim, kernel_size=3, stride=stride, padding=1, groups=dim),nn.BatchNorm2d(dim), )self.upsample = nn.Upsample(scale_factor=stride, mode='bilinear')else:self.resolution = resolutionself.stride_conv = Noneself.upsample = Noneself.N = self.resolution ** 2self.N2 = self.Nself.d = int(attn_ratio * key_dim)self.dh = int(attn_ratio * key_dim) * num_headsself.attn_ratio = attn_ratioh = self.dh + nh_kd * 2self.q = nn.Sequential(nn.Conv2d(dim, self.num_heads * self.key_dim, 1),nn.BatchNorm2d(self.num_heads * self.key_dim), )self.k = nn.Sequential(nn.Conv2d(dim, self.num_heads * self.key_dim, 1),nn.BatchNorm2d(self.num_heads * self.key_dim), )self.v = nn.Sequential(nn.Conv2d(dim, self.num_heads * self.d, 1),nn.BatchNorm2d(self.num_heads * self.d),)self.v_local = nn.Sequential(nn.Conv2d(self.num_heads * self.d, self.num_heads * self.d,kernel_size=3, stride=1, padding=1, groups=self.num_heads * self.d),nn.BatchNorm2d(self.num_heads * self.d), )self.talking_head1 = nn.Conv2d(self.num_heads, self.num_heads, kernel_size=1, stride=1, padding=0)self.talking_head2 = nn.Conv2d(self.num_heads, self.num_heads, kernel_size=1, stride=1, padding=0)self.proj = nn.Sequential(act_layer(),nn.Conv2d(self.dh, dim, 1),nn.BatchNorm2d(dim), )points = list(itertools.product(range(self.resolution), range(self.resolution)))N = len(points)attention_offsets = {}idxs = []for p1 in points:for p2 in points:offset = (abs(p1[0] - p2[0]), abs(p1[1] - p2[1]))if offset not in attention_offsets:attention_offsets[offset] = len(attention_offsets)idxs.append(attention_offsets[offset])self.attention_biases = torch.nn.Parameter(torch.zeros(num_heads, len(attention_offsets)))self.register_buffer('attention_bias_idxs',torch.LongTensor(idxs).view(N, N))@torch.no_grad()def train(self, mode=True):super().train(mode)if mode and hasattr(self, 'ab'):del self.abelse:self.ab = self.attention_biases[:, self.attention_bias_idxs]def forward(self, x):  # x (B,N,C)B, C, H, W = x.shapeif self.stride_conv is not None:x = self.stride_conv(x)q = self.q(x).flatten(2).reshape(B, self.num_heads, -1, self.N).permute(0, 1, 3, 2)k = self.k(x).flatten(2).reshape(B, self.num_heads, -1, self.N).permute(0, 1, 2, 3)v = self.v(x)v_local = self.v_local(v)v = v.flatten(2).reshape(B, self.num_heads, -1, self.N).permute(0, 1, 3, 2)attn = ((q @ k) * self.scale+(self.attention_biases[:, self.attention_bias_idxs]if self.training else self.ab))# attn = (q @ k) * self.scaleattn = self.talking_head1(attn)attn = attn.softmax(dim=-1)attn = self.talking_head2(attn)x = (attn @ v)out = x.transpose(2, 3).reshape(B, self.dh, self.resolution, self.resolution) + v_localif self.upsample is not None:out = self.upsample(out)out = self.proj(out)return outdef stem(in_chs, out_chs, act_layer=nn.ReLU):return nn.Sequential(nn.Conv2d(in_chs, out_chs // 2, kernel_size=3, stride=2, padding=1),nn.BatchNorm2d(out_chs // 2),act_layer(),nn.Conv2d(out_chs // 2, out_chs, kernel_size=3, stride=2, padding=1),nn.BatchNorm2d(out_chs),act_layer(),)class LGQuery(torch.nn.Module):def __init__(self, in_dim, out_dim, resolution1, resolution2):super().__init__()self.resolution1 = resolution1self.resolution2 = resolution2self.pool = nn.AvgPool2d(1, 2, 0)self.local = nn.Sequential(nn.Conv2d(in_dim, in_dim, kernel_size=3, stride=2, padding=1, groups=in_dim),)self.proj = nn.Sequential(nn.Conv2d(in_dim, out_dim, 1),nn.BatchNorm2d(out_dim), )def forward(self, x):local_q = self.local(x)pool_q = self.pool(x)q = local_q + pool_qq = self.proj(q)return qclass Attention4DDownsample(torch.nn.Module):def __init__(self, dim=384, key_dim=16, num_heads=8,attn_ratio=4,resolution=7,out_dim=None,act_layer=None,):super().__init__()self.num_heads = num_headsself.scale = key_dim ** -0.5self.key_dim = key_dimself.nh_kd = nh_kd = key_dim * num_headsself.resolution = resolutionself.d = int(attn_ratio * key_dim)self.dh = int(attn_ratio * key_dim) * num_headsself.attn_ratio = attn_ratioh = self.dh + nh_kd * 2if out_dim is not None:self.out_dim = out_dimelse:self.out_dim = dimself.resolution2 = math.ceil(self.resolution / 2)self.q = LGQuery(dim, self.num_heads * self.key_dim, self.resolution, self.resolution2)self.N = self.resolution ** 2self.N2 = self.resolution2 ** 2self.k = nn.Sequential(nn.Conv2d(dim, self.num_heads * self.key_dim, 1),nn.BatchNorm2d(self.num_heads * self.key_dim), )self.v = nn.Sequential(nn.Conv2d(dim, self.num_heads * self.d, 1),nn.BatchNorm2d(self.num_heads * self.d),)self.v_local = nn.Sequential(nn.Conv2d(self.num_heads * self.d, self.num_heads * self.d,kernel_size=3, stride=2, padding=1, groups=self.num_heads * self.d),nn.BatchNorm2d(self.num_heads * self.d), )self.proj = nn.Sequential(act_layer(),nn.Conv2d(self.dh, self.out_dim, 1),nn.BatchNorm2d(self.out_dim), )points = list(itertools.product(range(self.resolution), range(self.resolution)))points_ = list(itertools.product(range(self.resolution2), range(self.resolution2)))N = len(points)N_ = len(points_)attention_offsets = {}idxs = []for p1 in points_:for p2 in points:size = 1offset = (abs(p1[0] * math.ceil(self.resolution / self.resolution2) - p2[0] + (size - 1) / 2),abs(p1[1] * math.ceil(self.resolution / self.resolution2) - p2[1] + (size - 1) / 2))if offset not in attention_offsets:attention_offsets[offset] = len(attention_offsets)idxs.append(attention_offsets[offset])self.attention_biases = torch.nn.Parameter(torch.zeros(num_heads, len(attention_offsets)))self.register_buffer('attention_bias_idxs',torch.LongTensor(idxs).view(N_, N))@torch.no_grad()def train(self, mode=True):super().train(mode)if mode and hasattr(self, 'ab'):del self.abelse:self.ab = self.attention_biases[:, self.attention_bias_idxs]def forward(self, x):  # x (B,N,C)B, C, H, W = x.shapeq = self.q(x).flatten(2).reshape(B, self.num_heads, -1, self.N2).permute(0, 1, 3, 2)k = self.k(x).flatten(2).reshape(B, self.num_heads, -1, self.N).permute(0, 1, 2, 3)v = self.v(x)v_local = self.v_local(v)v = v.flatten(2).reshape(B, self.num_heads, -1, self.N).permute(0, 1, 3, 2)attn = ((q @ k) * self.scale+(self.attention_biases[:, self.attention_bias_idxs]if self.training else self.ab))# attn = (q @ k) * self.scaleattn = attn.softmax(dim=-1)x = (attn @ v).transpose(2, 3)out = x.reshape(B, self.dh, self.resolution2, self.resolution2) + v_localout = self.proj(out)return outclass Embedding(nn.Module):def __init__(self, patch_size=3, stride=2, padding=1,in_chans=3, embed_dim=768, norm_layer=nn.BatchNorm2d,light=False, asub=False, resolution=None, act_layer=nn.ReLU, attn_block=Attention4DDownsample):super().__init__()self.light = lightself.asub = asubif self.light:self.new_proj = nn.Sequential(nn.Conv2d(in_chans, in_chans, kernel_size=3, stride=2, padding=1, groups=in_chans),nn.BatchNorm2d(in_chans),nn.Hardswish(),nn.Conv2d(in_chans, embed_dim, kernel_size=1, stride=1, padding=0),nn.BatchNorm2d(embed_dim),)self.skip = nn.Sequential(nn.Conv2d(in_chans, embed_dim, kernel_size=1, stride=2, padding=0),nn.BatchNorm2d(embed_dim))elif self.asub:self.attn = attn_block(dim=in_chans, out_dim=embed_dim,resolution=resolution, act_layer=act_layer)patch_size = to_2tuple(patch_size)stride = to_2tuple(stride)padding = to_2tuple(padding)self.conv = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size,stride=stride, padding=padding)self.bn = norm_layer(embed_dim) if norm_layer else nn.Identity()else:patch_size = to_2tuple(patch_size)stride = to_2tuple(stride)padding = to_2tuple(padding)self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size,stride=stride, padding=padding)self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()def forward(self, x):if self.light:out = self.new_proj(x) + self.skip(x)elif self.asub:out_conv = self.conv(x)out_conv = self.bn(out_conv)out = self.attn(x) + out_convelse:x = self.proj(x)out = self.norm(x)return outclass Mlp(nn.Module):"""Implementation of MLP with 1*1 convolutions.Input: tensor with shape [B, C, H, W]"""def __init__(self, in_features, hidden_features=None,out_features=None, act_layer=nn.GELU, drop=0., mid_conv=False):super().__init__()out_features = out_features or in_featureshidden_features = hidden_features or in_featuresself.mid_conv = mid_convself.fc1 = nn.Conv2d(in_features, hidden_features, 1)self.act = act_layer()self.fc2 = nn.Conv2d(hidden_features, out_features, 1)self.drop = nn.Dropout(drop)self.apply(self._init_weights)if self.mid_conv:self.mid = nn.Conv2d(hidden_features, hidden_features, kernel_size=3, stride=1, padding=1,groups=hidden_features)self.mid_norm = nn.BatchNorm2d(hidden_features)self.norm1 = nn.BatchNorm2d(hidden_features)self.norm2 = nn.BatchNorm2d(out_features)def _init_weights(self, m):if isinstance(m, nn.Conv2d):trunc_normal_(m.weight, std=.02)if m.bias is not None:nn.init.constant_(m.bias, 0)def forward(self, x):x = self.fc1(x)x = self.norm1(x)x = self.act(x)if self.mid_conv:x_mid = self.mid(x)x_mid = self.mid_norm(x_mid)x = self.act(x_mid)x = self.drop(x)x = self.fc2(x)x = self.norm2(x)x = self.drop(x)return xclass AttnFFN(nn.Module):def __init__(self, dim, mlp_ratio=4.,act_layer=nn.ReLU, norm_layer=nn.LayerNorm,drop=0., drop_path=0.,use_layer_scale=True, layer_scale_init_value=1e-5,resolution=7, stride=None):super().__init__()self.token_mixer = Attention4D(dim, resolution=resolution, act_layer=act_layer, stride=stride)mlp_hidden_dim = int(dim * mlp_ratio)self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim,act_layer=act_layer, drop=drop, mid_conv=True)self.drop_path = DropPath(drop_path) if drop_path > 0. \else nn.Identity()self.use_layer_scale = use_layer_scaleif use_layer_scale:self.layer_scale_1 = nn.Parameter(layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)self.layer_scale_2 = nn.Parameter(layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)def forward(self, x):if self.use_layer_scale:x = x + self.drop_path(self.layer_scale_1 * self.token_mixer(x))x = x + self.drop_path(self.layer_scale_2 * self.mlp(x))else:x = x + self.drop_path(self.token_mixer(x))x = x + self.drop_path(self.mlp(x))return xclass FFN(nn.Module):def __init__(self, dim, pool_size=3, mlp_ratio=4.,act_layer=nn.GELU,drop=0., drop_path=0.,use_layer_scale=True, layer_scale_init_value=1e-5):super().__init__()mlp_hidden_dim = int(dim * mlp_ratio)self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim,act_layer=act_layer, drop=drop, mid_conv=True)self.drop_path = DropPath(drop_path) if drop_path > 0. \else nn.Identity()self.use_layer_scale = use_layer_scaleif use_layer_scale:self.layer_scale_2 = nn.Parameter(layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)def forward(self, x):if self.use_layer_scale:x = x + self.drop_path(self.layer_scale_2 * self.mlp(x))else:x = x + self.drop_path(self.mlp(x))return xdef eformer_block(dim, index, layers,pool_size=3, mlp_ratio=4.,act_layer=nn.GELU, norm_layer=nn.LayerNorm,drop_rate=.0, drop_path_rate=0.,use_layer_scale=True, layer_scale_init_value=1e-5, vit_num=1, resolution=7, e_ratios=None):blocks = []for block_idx in range(layers[index]):block_dpr = drop_path_rate * (block_idx + sum(layers[:index])) / (sum(layers) - 1)mlp_ratio = e_ratios[str(index)][block_idx]if index >= 2 and block_idx > layers[index] - 1 - vit_num:if index == 2:stride = 2else:stride = Noneblocks.append(AttnFFN(dim, mlp_ratio=mlp_ratio,act_layer=act_layer, norm_layer=norm_layer,drop=drop_rate, drop_path=block_dpr,use_layer_scale=use_layer_scale,layer_scale_init_value=layer_scale_init_value,resolution=resolution,stride=stride,))else:blocks.append(FFN(dim, pool_size=pool_size, mlp_ratio=mlp_ratio,act_layer=act_layer,drop=drop_rate, drop_path=block_dpr,use_layer_scale=use_layer_scale,layer_scale_init_value=layer_scale_init_value,))blocks = nn.Sequential(*blocks)return blocksclass EfficientFormerV2(nn.Module):def __init__(self, layers, embed_dims=None,mlp_ratios=4, downsamples=None,pool_size=3,norm_layer=nn.BatchNorm2d, act_layer=nn.GELU,num_classes=1000,down_patch_size=3, down_stride=2, down_pad=1,drop_rate=0., drop_path_rate=0.,use_layer_scale=True, layer_scale_init_value=1e-5,fork_feat=False,init_cfg=None,pretrained=None,vit_num=0,distillation=True,resolution=224,e_ratios=expansion_ratios_L,**kwargs):super().__init__()if not fork_feat:self.num_classes = num_classesself.fork_feat = fork_featself.patch_embed = stem(3, embed_dims[0], act_layer=act_layer)network = []for i in range(len(layers)):stage = eformer_block(embed_dims[i], i, layers,pool_size=pool_size, mlp_ratio=mlp_ratios,act_layer=act_layer, norm_layer=norm_layer,drop_rate=drop_rate,drop_path_rate=drop_path_rate,use_layer_scale=use_layer_scale,layer_scale_init_value=layer_scale_init_value,resolution=math.ceil(resolution / (2 ** (i + 2))),vit_num=vit_num,e_ratios=e_ratios)network.append(stage)if i >= len(layers) - 1:breakif downsamples[i] or embed_dims[i] != embed_dims[i + 1]:# downsampling between two stagesif i >= 2:asub = Trueelse:asub = Falsenetwork.append(Embedding(patch_size=down_patch_size, stride=down_stride,padding=down_pad,in_chans=embed_dims[i], embed_dim=embed_dims[i + 1],resolution=math.ceil(resolution / (2 ** (i + 2))),asub=asub,act_layer=act_layer, norm_layer=norm_layer,))self.network = nn.ModuleList(network)if self.fork_feat:# add a norm layer for each outputself.out_indices = [0, 2, 4, 6]for i_emb, i_layer in enumerate(self.out_indices):if i_emb == 0 and os.environ.get('FORK_LAST3', None):layer = nn.Identity()else:layer = norm_layer(embed_dims[i_emb])layer_name = f'norm{i_layer}'self.add_module(layer_name, layer)else:# Classifier headself.norm = norm_layer(embed_dims[-1])self.head = nn.Linear(embed_dims[-1], num_classes) if num_classes > 0 \else nn.Identity()self.dist = distillationif self.dist:self.dist_head = nn.Linear(embed_dims[-1], num_classes) if num_classes > 0 \else nn.Identity()self.apply(self.cls_init_weights)self.init_cfg = copy.deepcopy(init_cfg)# load pre-trained modelif self.fork_feat and (self.init_cfg is not None or pretrained is not None):self.init_weights()# init for classificationdef cls_init_weights(self, m):if isinstance(m, nn.Linear):trunc_normal_(m.weight, std=.02)if isinstance(m, nn.Linear) and m.bias is not None:nn.init.constant_(m.bias, 0)# init for mmdetection or mmsegmentation by loading# imagenet pre-trained weightsdef init_weights(self, pretrained=None):logger = get_root_logger()if self.init_cfg is None and pretrained is None:logger.warn(f'No pre-trained weights for 'f'{self.__class__.__name__}, 'f'training start from scratch')passelse:assert 'checkpoint' in self.init_cfg, f'Only support ' \f'specify `Pretrained` in ' \f'`init_cfg` in ' \f'{self.__class__.__name__} 'if self.init_cfg is not None:ckpt_path = self.init_cfg['checkpoint']elif pretrained is not None:ckpt_path = pretrainedckpt = _load_checkpoint(ckpt_path, logger=logger, map_location='cpu')if 'state_dict' in ckpt:_state_dict = ckpt['state_dict']elif 'model' in ckpt:_state_dict = ckpt['model']else:_state_dict = ckptstate_dict = _state_dictmissing_keys, unexpected_keys = \self.load_state_dict(state_dict, False)def forward_tokens(self, x):outs = []for idx, block in enumerate(self.network):x = block(x)if self.fork_feat and idx in self.out_indices:norm_layer = getattr(self, f'norm{idx}')x_out = norm_layer(x)outs.append(x_out)if self.fork_feat:return outsreturn xdef forward(self, x):x = self.patch_embed(x)x = self.forward_tokens(x)if self.fork_feat:# otuput features of four stages for dense predictionreturn x# print(x.size())x = self.norm(x)if self.dist:cls_out = self.head(x.flatten(2).mean(-1)), self.dist_head(x.flatten(2).mean(-1))if not self.training:cls_out = (cls_out[0] + cls_out[1]) / 2else:cls_out = self.head(x.flatten(2).mean(-1))# for image classificationreturn cls_outdef _cfg(url='', **kwargs):return {'url': url,'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': None,'crop_pct': .95, 'interpolation': 'bicubic','mean': IMAGENET_DEFAULT_MEAN, 'std': IMAGENET_DEFAULT_STD,'classifier': 'head',**kwargs}@register_model
def efficientformerv2_s0(pretrained=False, **kwargs):model = EfficientFormerV2(layers=EfficientFormer_depth['S0'],embed_dims=EfficientFormer_width['S0'],downsamples=[True, True, True, True, True],vit_num=2,drop_path_rate=0.0,e_ratios=expansion_ratios_S0,**kwargs)model.default_cfg = _cfg(crop_pct=0.9)return model@register_model
def efficientformerv2_s1(pretrained=False, **kwargs):model = EfficientFormerV2(layers=EfficientFormer_depth['S1'],embed_dims=EfficientFormer_width['S1'],downsamples=[True, True, True, True],vit_num=2,drop_path_rate=0.0,e_ratios=expansion_ratios_S1,**kwargs)model.default_cfg = _cfg(crop_pct=0.9)return model@register_model
def efficientformerv2_s2(pretrained=False, **kwargs):model = EfficientFormerV2(layers=EfficientFormer_depth['S2'],embed_dims=EfficientFormer_width['S2'],downsamples=[True, True, True, True],vit_num=4,drop_path_rate=0.02,e_ratios=expansion_ratios_S2,**kwargs)model.default_cfg = _cfg(crop_pct=0.9)return model@register_model
def efficientformerv2_l(pretrained=False, **kwargs):model = EfficientFormerV2(layers=EfficientFormer_depth['L'],embed_dims=EfficientFormer_width['L'],downsamples=[True, True, True, True],vit_num=6,drop_path_rate=0.1,e_ratios=expansion_ratios_L,**kwargs)model.default_cfg = _cfg(crop_pct=0.9)return model

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

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

相关文章

Redis-01 入门和十大数据类型

Redis支持两种持久化方式:RDB持久化和AOF持久化。 1.RDB持久化是将Redis的数据以快照的形式保存在磁盘上,可以手动触发或通过配置文件设置定时触发。RDB保存的是Redis在某个时间点上的数据快照,可以通过恢复RDB文件来恢复数据。 2.AOF持久化…

力扣P1706全排列问题 很好的引入暴力 递归 回溯 dfs

代码思路是受一个洛谷题解里面大佬的启发。应该算是一个dfs和回溯的入门题目&#xff0c;很好的入门题目了下面我会先给我原题解思路我想可以很快了解这个思路。下面是我自己根据力扣大佬写的。 我会进行详细讲解并配上图辅助理解大家请往下看 #include<iostream> #inc…

初始MYSQL数据库(7)—— 视图

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; MYSQL 引言 前面我们学习MySQL数据库时&#xff0c;创建表之后&#xff0c;会在表中插入数据&#xff0c;在需要的时候&#xff0c;也会进行…

python文字转wav音频

借鉴博客 一.前期准备 1. pip install baidu-aip 2. pip install pydub 3. sudo apt-get install ffmpeg 二.代码 from aip import AipSpeech from pydub import AudioSegment import time#input your own APP_ID/API_KEY/SECRET_KEY APP_ID 14891501 API_KEY EIm2iXtvD…

示例:WPF中Grid显示网格线的几种方式

一、目的&#xff1a;介绍一下WPF中Grid显示网格线的几种方式 二、几种方式 1、重写OnRender绘制网格线&#xff08;推荐&#xff09; 效果如下&#xff1a; 实现方式如下&#xff1a; public class LineGrid : Grid{private readonly Pen _pen;public LineGrid(){_pen new P…

【Linux】深度解析与实战应用:GCC/G++编译器入门指南

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…

RabbitMQ08_保证消息可靠性

保证消息可靠性 一、生产者可靠性1、生产者重连机制&#xff08;防止网络波动&#xff09;2、生产者确认机制Publisher Return 确认机制Publisher Confirm 确认机制 二、MQ 可靠性1、数据持久化交换机、队列持久化消息持久化 2、Lazy Queue 惰性队列 三、消费者可靠性1、消费者…

速通LLaMA3:《The Llama 3 Herd of Models》全文解读

文章目录 概览论文开篇IntroductionGeneral OverviewPre-TrainingPre-Training DataModel ArchitectureInfrastructure, Scaling, and EfficiencyTraining Recipe Post-TrainingResultsVision ExperimentsSpeech Experiments⭐Related WorkConclusionLlama 3 模型中的数学原理1…

细说硫酸钙防静电地板的材质结构和优势特点

防静电地板有全钢基材的、硫酸钙基材的、铝合金基材的&#xff0c;在一些防静电要求、承载要求、铺设要求、铺装效果要求很高的场合&#xff0c;如银行、电信机房、移动机房、智能化办公室、部队指挥中心&#xff0c;通常都会使用硫酸钙防静电地板。那么什么是硫酸钙防静电地板…

计算机毕业设计 二手图书交易系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

计算机毕业设计 基于Python的医疗预约与诊断系统 Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

arthas-阿里远程诊断工具神器一定要掌握

文章目录 1. 背景介绍2. 安装下载3. 常用命令4. 常见案例4.1 案例一&#xff1a;使用logger 实时修改某个类的日志级别、4.2 案例二&#xff1a;使用watch 查看方法输入输出参数4.3 案例三&#xff1a;使用 Arthas 实现在线代码热更新 1. 背景介绍 通常&#xff0c;本地开发环…

文件上传、amrkdown编辑器

一、文件上传 这里我以图片为例&#xff0c;进行上传&#xff0c;上传到阿里云oss&#xff08;对象存在中&#xff09; 首先&#xff0c;我们先梳理一下&#xff0c;图片上传的流程 1、前端选择文件&#xff0c;提交文件 前端提交文件&#xff0c;我们可以使用ElementUI中的…

蓝队技能-应急响应篇Web内存马查杀JVM分析Class提取诊断反编译日志定性

知识点&#xff1a; 1、应急响应-Web内存马-定性&排查 2、应急响应-Web内存马-分析&日志 注&#xff1a;传统WEB类型的内存马只要网站重启后就清除了。 演示案例-蓝队技能-JAVA Web内存马-JVM分析&日志URL&内存查杀 0、环境搭建 参考地址&#xff1a;http…

有关 签到/签退 业务逻辑 的梳理与学习

导言 最近搞到了个签到管理&#xff0c;其中的业务逻辑感觉有点复杂(可能是我的方向不对),虽然是实现了&#xff0c;不过代码和逻辑很多&#xff0c;也有些乱&#xff0c;想趁着还记得逻辑来记录梳理一下&#xff0c;看看自己以后有没有更好的思路&#xff0c;或者有大佬有思路…

[C#]winform 使用opencvsharp实现玉米粒计数

【算法介绍】 这段代码是使用OpenCvSharp库&#xff08;OpenCV的C#封装&#xff09;对图像进行处理&#xff0c;主要流程包括图像的二值化、腐蚀操作、距离变换、轮廓检测&#xff0c;并在原图上标出检测到的轮廓位置及数量。下面是对代码的详细解读&#xff1a; 初始化&…

Ubuntu22.04安装GNSS数据处理软件GAMIT/GLOBK

由于微信公众号改变了推送规则&#xff0c;为了每次新的推送可以在第一时间出现在您的订阅列表中&#xff0c;记得将本公众号设为星标或置顶喔~ 手把手带您安装gamit/globk软件~ &#x1f33f;前言 受朋友之托&#xff0c;出一期Ubuntu22.04安装GNSS数据处理软件——gamit软件…

【论文笔记】Are Large Kernels Better Teacheres than Transformers for ConvNets

Abstract 本文提出蒸馏中小核ConvNet做学生时&#xff0c;与Transformer相比&#xff0c;大核ConvNet因其高效的卷积操作和紧凑的权重共享&#xff0c;使得其做教师效果更好&#xff0c;更适合资源受限的应用。 用蒸馏从Transformers蒸到小核ConvNet的效果并不好&#xff0c;原…

MySQL篇(存储过程 触发器 存储函数)(持续更新迭代)

目录 一、存储过程 1. 简介 2. 特点 3. 语法 3.1. 创建 3.2. 调用 3.3. 查看 3.4. 删除 4. 示例 二、变量 1. 简介 2. 系统变量 2.1. 查看系统变量 2.2. 设置系统变量 2.3. 演示示例 3. 用户定义变量 3.1. 赋值 方式一 方式二 3.2. 使用 3.3. 演示示例 4.…

Rust - 字符串:str 与 String

在其他语言中&#xff0c;字符串通常都会比较简单&#xff0c;例如 “hello, world” 就是字符串章节的几乎全部内容了。 但是Rust中的字符串与其他语言有所不同&#xff0c;若带着其他语言的习惯来学习Rust字符串&#xff0c;将会波折不断。 所以最好先忘记脑中已有的关于字…