YOLOv8改进 | 主干篇 | 利用MobileNetV2替换Backbone(轻量化网络结构)

news2024/11/22 23:37:37

一、本文介绍

本文给大家带来的改进机制是MobileNetV2其是专为移动和嵌入式视觉应用设计的轻量化网络结构。其在MobilNetV1的基础上采用反转残差结构和线性瓶颈层。这种结构通过轻量级的深度卷积和线性卷积过滤特征,同时去除狭窄层中的非线性,以维持表征能力。MobileNetV2在性能上和精度上都要比V1版本强很多,其在多种应用(如对象检测、细粒度分类、面部属性识别和大规模地理定位)中都展现了一定的有效性。

适用检测目标:轻量化网络结构适合非常轻量化的读者,同时具有涨点效果

推荐指数:⭐⭐⭐⭐

  专栏回顾:YOLOv8改进系列专栏——本专栏持续复习各种顶会内容——科研必备 

效果回顾展示->

二、MobileNetV2的框架原理

官方论文地址:官方论文地址

官方代码地址:官方代码地址


2.1 MobileNetV2的基本原理

MobileNetV2是在MobileNetV1基础上提出来的,其不光具有V1的全部改进,还提出了采用反转残差结构和线性瓶颈层。这种结构通过轻量级的深度卷积和线性卷积过滤特征,同时去除狭窄层中的非线性,以维持表征能力。MobileNetV2通过这种设计提高了性能,并在多种任务和基准测试上表现出色。此外,它提出了一种新的框架SSDLite,用于移动设备上的目标检测,并展示了如何构建移动语义分割模型Mobile DeepLabv3。这种方法允许输入/输出域与变换的表达力解耦,为进一步分析提供了方便的框架。 

MobileNetV2的主要创新点包括:

1. 反转残差结构:使用轻量级的深度卷积作为扩展层来提高特征过滤的效率。
2. 线性瓶颈层:在狭窄的层中去除非线性激活函数,以保持网络的表征能力。
3. SSDLite框架:用于移动设备上的高效目标检测,它是一种简化和优化的SSD框架。
 

2.1.1 反转残差结构 

反转残差结构是MobileNetV2的关键特性,它采用轻量级的深度可分离卷积作为扩展层。这种结构首先使用1x1的卷积将输入特征图的通道数扩大,然后应用深度可分离卷积对这些扩展的特征图进行空间特征提取,最后再次通过1x1的卷积将通道数减少,恢复到原来的尺寸。这样的设计有效地提高了网络处理特征的效率,同时减少了参数数量和计算成本。通过这种方式,MobileNetV2能够在保持模型轻量的同时,提供足够的模型表现力,适用于移动和嵌入式设备上的高效计算。 

上图展示了残差块和反转残差块之间的区别:

(a) 残差:传统的残差块通过直接连接输入和输出来促进特征的传递,通常包含具有高通道数的层和ReLU激活函数。
(b) 反转残差块:在反转残差块中,连接是在瓶颈层之间,即通道数较少的层,而且去除了非线性激活函数,以保持特征的表达力。这种设计通常首先用一个扩展层增加通道数,然后应用深度卷积处理特征,并且在最后一个线性层减少通道数。

2.1.2 线性瓶颈层

线性瓶颈层是MobileNetV2架构中的另一个关键特性。在这种结构中,传统的非线性激活函数被有意地从瓶颈层中去除。瓶颈层是指那些通道数较少的卷积层,它们位于扩展层和压缩层之间。这样做的目的是为了减少信息在通过狭窄层时的损失,因为非线性操作可能会破坏特征中的一些信息。通过保持这些层的线性,网络能够维持更丰富的特征表示,这对于提高模型的整体性能至关重要。

总结:就是在一些卷积层里面把激活函数删除掉了,类似于v8中的Bottleneck模块,将其中的激活函数删除掉。

2.1.3 SSDLite框架 

SSDLite是一个轻量级的目标检测框架,专为移动设备优化。它是SSD框架的简化版本,通过使用深度可分离卷积替换SSD中的标准卷积,显著减少了计算量和模型的大小。SSDLite继承了SSD的单次检测机制,使得模型在进行目标检测时既高效又准确。这种设计使SSDLite非常适合在资源受限的设备上进行实时目标检测任务。

上图展示了可分离卷积块的演变。其中:

(a) 展示了常规的卷积。
(b) 展示了可分离卷积块,这种块首先使用深度卷积分别处理每个输入通道,然后用一个1x1的卷积组合这些特征。
(c) 展示了带有线性瓶颈的可分离卷积,它在瓶颈层中移除了非线性激活函数,以保持特征的表达力。
(d) 展示了带有扩展层的瓶颈结构,它使用一个扩展层放大特征空间,然后再用深度卷积和1x1卷积进行处理。

对角线阴影的纹理表示不包含非线性的层,最后的浅色层表示下一个块的开始。请注意,当堆叠时,2d和2c是等效的块。


三、MobileNetV2的核心代码

下面的代码是整个MobileNetV2的核心代码,大家如果想学习可以和上面的框架原理对比着看一看估计会有一定的收获,使用方式看章节四。

"""A from-scratch implementation of MobileNetV2 paper ( for educational purposes ).

Paper
    MobileNetV2: Inverted Residuals and Linear Bottlenecks - https://arxiv.org/abs/1801.04381

author : shubham.aiengineer@gmail.com
"""


import torch
from torch import nn
from torchsummary import summary


class ConvNormReLUBlock(nn.Module):
    def __init__(
        self,
        in_channels: int,
        out_channels: int,
        kernel_size: list,
        stride: int = 1,
        padding: int = 0,
        groups: int = 1,
        bias: bool = False,
        activation: bool = nn.ReLU6,
    ):
        """Constructs a block containing a combination of convolution, batchnorm and relu

        Args:
            in_channels (int): input channels
            out_channels (int): output channels
            kernel_size (list): kernel size parameter for convolution
            stride (int, optional): stride parameter for convolution. Defaults to 1.
            padding (int, optional): padding parameter for convolution. Defaults to 0.
            groups (int, optional): number of blocked connections from input channel to output channel for convolution. Defaults to 1.
            bias (bool, optional): whether to enable bias in convolution. Defaults to False.
            activation (bool, optional): activation function to use. Defaults to nn.ReLU6.
        """

        super().__init__()

        self.conv = nn.Conv2d(
            in_channels,
            out_channels,
            kernel_size,
            stride=stride,
            padding=padding,
            groups=groups,
            bias=bias,
        )
        self.bn = nn.BatchNorm2d(out_channels)
        self.activation = activation()

    def forward(self, x):
        """Perform forward pass."""

        x = self.conv(x)
        x = self.bn(x)
        x = self.activation(x)

        return x


class InverseResidualBlock(nn.Module):
    def __init__(
        self,
        in_channels: int,
        out_channels: int,
        expansion_factor: int = 6,
        stride: int = 1,
    ):
        """Constructs a inverse residual block with depthwise seperable convolution

        Args:
            in_channels (int): input channels
            out_channels (int): output channels
            expansion_factor (int, optional): Calculating the input & output channel for depthwise convolution by multiplying the expansion factor with input channels. Defaults to 6.
            stride (int, optional): stride paramemeter for depthwise convolution. Defaults to 1.
        """

        super().__init__()

        hidden_channels = in_channels * expansion_factor
        self.residual = in_channels == out_channels and stride == 1

        self.conv1 = (
            ConvNormReLUBlock(in_channels, hidden_channels, (1, 1))
            if in_channels != hidden_channels
            else nn.Identity() #  If it's not the first layer, then we need to add a 1x1 convolutional layer to expand the number of channels
        )
        self.depthwise_conv = ConvNormReLUBlock(
            hidden_channels,
            hidden_channels,
            (3, 3),
            stride=stride,
            padding=1,
            groups=hidden_channels,
        )
        self.conv2 = ConvNormReLUBlock(
            hidden_channels, out_channels, (1, 1), activation=nn.Identity
        )

    def forward(self, x):
        """Perform forward pass."""

        identity = x

        x = self.conv1(x)
        x = self.depthwise_conv(x)
        x = self.conv2(x)

        if self.residual:
            x = torch.add(x, identity)

        return x


class MobileNetV2(nn.Module):
    def __init__(
        self,
        n_classes: int = 1000,
        input_channel: int = 3,
        dropout: float = 0.2,
    ):
        """Constructs MobileNetV2 architecture

        Args:
            n_classes (int, optional): output neuron in last layer. Defaults to 1000.
            input_channel (int, optional): input channels in first conv layer. Defaults to 3.
            dropout (float, optional): dropout in last layer. Defaults to 0.2.
        """

        super().__init__()

        # The configuration of MobileNetV2
        # input channels, expansion factor, output channels, repeat, stride,
        config = (
            (32, 1, 16, 1, 1),
            (16, 6, 24, 2, 2),
            (24, 6, 32, 3, 2),
            (32, 6, 64, 4, 2),
            (64, 6, 96, 3, 1),
            (96, 6, 160, 3, 2),
            (160, 6, 320, 1, 1),
        )

        self.model = nn.Sequential(
            ConvNormReLUBlock(input_channel, 32, (3, 3), stride=2, padding=1)
        )

        for in_channels, expansion_factor, out_channels, repeat, stride in config:
            for _ in range(repeat):
                self.model.append(
                    InverseResidualBlock(
                        in_channels=in_channels,
                        out_channels=out_channels,
                        expansion_factor=expansion_factor,
                        stride=stride,
                    )
                )
                in_channels = out_channels
                stride = 1

        self.index = [24, 32, 96, 320]
        self.width_list = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]

    def forward(self, x):
        """Perform forward pass."""
        results = [None, None, None, None]

        for model in self.model:
            x = model(x)
            if x.size(1) in self.index:
                position = self.index.index(x.size(1))  # Find the position in the index list
                results[position] = x
            results.append(x)
        return results


if __name__ == "__main__":

    # Generating Sample image
    image_size = (1, 3, 224, 224)
    image = torch.rand(*image_size)

    # Model
    mobilenet_v2 = MobileNetV2()

    # summary(
    #     mobilenet_v2,
    #     input_data=image,
    #     col_names=["input_size", "output_size", "num_params"],
    #     device="cpu",
    #     depth=2,
    # )

    out = mobilenet_v2(image)
    print("Output shape : ", out.shape)

四、手把手教你添加MobileNetV2网络结构

这个主干的网络结构添加起来算是所有的改进机制里最麻烦的了,因为有一些网略结构可以用yaml文件搭建出来,有一些网络结构其中的一些细节根本没有办法用yaml文件去搭建,用yaml文件去搭建会损失一些细节部分(而且一个网络结构设计很多细节的结构修改方式都不一样,一个一个去修改大家难免会出错),所以这里让网络直接返回整个网络,然后修改部分 yolo代码以后就都以这种形式添加了,以后我提出的网络模型基本上都会通过这种方式修改,我也会进行一些模型细节改进。创新出新的网络结构大家直接拿来用就可以的。下面开始添加教程->

(同时每一个后面都有代码,大家拿来复制粘贴替换即可,但是要看好了不要复制粘贴替换多了)


修改一

我们复制网络结构代码到“ultralytics/nn/modules”目录下创建一个py文件复制粘贴进去 ,我这里起的名字是MobileNetV1。


修改二

找到如下的文件"ultralytics/nn/tasks.py" 在开始的部分导入我们的模型如下图。

from .modules.MobileNetV1 import MobileNetV2


修改三 

添加如下两行代码!!!


修改四

找到七百多行大概把具体看图片,按照图片来修改就行,添加红框内的部分,注意没有()只是函数名。

        elif m in {自行添加对应的模型即可,下面都是一样的}:
            m = m()
            c2 = m.width_list  # 返回通道列表
            backbone = True


修改五 

下面的两个红框内都是需要改动的。 

        if isinstance(c2, list):
            m_ = m
            m_.backbone = True
        else:
            m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)  # module
            t = str(m)[8:-2].replace('__main__.', '')  # module type


        m.np = sum(x.numel() for x in m_.parameters())  # number params
        m_.i, m_.f, m_.type = i + 4 if backbone else i, f, t  # attach index, 'from' index, type

修改六 

如下的也需要修改,全部按照我的来。

代码如下把原先的代码替换了即可。 

        if verbose:
            LOGGER.info(f'{i:>3}{str(f):>20}{n_:>3}{m.np:10.0f}  {t:<45}{str(args):<30}')  # print

        save.extend(x % (i + 4 if backbone else i) for x in ([f] if isinstance(f, int) else f) if x != -1)  # append to savelist
        layers.append(m_)
        if i == 0:
            ch = []
        if isinstance(c2, list):
            ch.extend(c2)
            if len(c2) != 5:
                ch.insert(0, 0)
        else:
            ch.append(c2)


修改七

修改七和前面的都不太一样,需要修改前向传播中的一个部分, 已经离开了parse_model方法了。

可以在图片中开代码行数,没有离开task.py文件都是同一个文件。 同时这个部分有好几个前向传播都很相似,大家不要看错了,是70多行左右的!!!,同时我后面提供了代码,大家直接复制粘贴即可,有时间我针对这里会出一个视频。

代码如下->

    def _predict_once(self, x, profile=False, visualize=False):
        """
        Perform a forward pass through the network.

        Args:
            x (torch.Tensor): The input tensor to the model.
            profile (bool):  Print the computation time of each layer if True, defaults to False.
            visualize (bool): Save the feature maps of the model if True, defaults to False.

        Returns:
            (torch.Tensor): The last output of the model.
        """
        y, dt = [], []  # outputs
        for m in self.model:
            if m.f != -1:  # if not from previous layer
                x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f]  # from earlier layers
            if profile:
                self._profile_one_layer(m, x, dt)
            if hasattr(m, 'backbone'):
                x = m(x)
                if len(x) != 5: # 0 - 5
                    x.insert(0, None)
                for index, i in enumerate(x):
                    if index in self.save:
                        y.append(i)
                    else:
                        y.append(None)
                x = x[-1] # 最后一个输出传给下一层
            else:
                x = m(x)  # run
                y.append(x if m.i in self.save else None)  # save output
            if visualize:
                feature_visualization(x, m.type, m.i, save_dir=visualize)
        return x

到这里就完成了修改部分,但是这里面细节很多,大家千万要注意不要替换多余的代码,导致报错,也不要拉下任何一部,都会导致运行失败,而且报错很难排查!!!很难排查!!! 


修改八

我们找到如下文件'ultralytics/utils/torch_utils.py'按照如下的图片进行修改。

五、MobileNetV2的yaml文件

复制如下yaml文件进行运行!!! 

# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect

# Parameters
nc: 80  # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.33, 0.25, 1024]  # YOLOv8n summary: 225 layers,  3157200 parameters,  3157184 gradients,   8.9 GFLOPs
  s: [0.33, 0.50, 1024]  # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPs
  m: [0.67, 0.75, 768]   # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPs
  l: [1.00, 1.00, 512]   # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
  x: [1.00, 1.25, 512]   # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOP

# YOLOv8.0n backbone
backbone:
  # [from, repeats, module, args]
  - [-1, 1, MobileNetV2, []]  # 4
  - [-1, 1, SPPF, [1024, 5]]  # 5

# YOLOv8.0n head
head:
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 6
  - [[-1, 3], 1, Concat, [1]]  # 7 cat backbone P4
  - [-1, 3, C2f, [512]]  # 8

  - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 9
  - [[-1, 2], 1, Concat, [1]]  # 10 cat backbone P3
  - [-1, 3, C2f, [256]]  # 11 (P3/8-small)

  - [-1, 1, Conv, [256, 3, 2]] # 12
  - [[-1, 8], 1, Concat, [1]]  # 13 cat head P4
  - [-1, 3, C2f, [512]]  # 14 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]] # 15
  - [[-1, 5], 1, Concat, [1]]  # 16 cat head P5
  - [-1, 3, C2f, [1024]]  # 17 (P5/32-large)

  - [[11, 14, 17], 1, Detect, [nc]]  # Detect(P3, P4, P5)


六、成功运行记录 

下面是成功运行的截图,已经完成了有1个epochs的训练,图片太大截不全第2个epochs了。 


七、本文总结

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv8改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~)如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~

专栏回顾:YOLOv8改进系列专栏——本专栏持续复习各种顶会内容——科研必备

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

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

相关文章

【K8s】4# 使用kuboard部署开源项目实战

文章目录 1.开源项目2.实战2.1.创建spring-blade命名空间2.2.导入 spring-blade 到 K8S 名称空间2.3.设置存储卷参数2.4.调整节点端口2.5.确认导入2.6.查看集群2.7.导入配置到 nacos2.8.启动微服务工作负载 3.验证部署结果3.1.Nacos3.2. web 4.问题汇总Q1&#xff1a;Nacos启动…

centos7安装开源日志系统graylog5.1.2

安装包链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1Zl5s7x1zMWpuKfaePy0gPg?pwd1eup 提取码&#xff1a;1eup 这里采用的shell脚本安装&#xff0c;脚本如下&#xff1a; 先使用命令产生2个参数代入到脚本中&#xff1a; 使用pwgen生成password_secret密码 …

CSS(五) -- 动效实现(立体盒子旋转-四方体+正六边)

一. 四面立体旋转 正方形旋转 小程序中 wxss中 <!-- 背景 --><view class"dragon"><!--旋转物体位置--><view class"dragon-position"><!--旋转 加透视 有立体的感觉--><view class"d-parent"><view …

Backtrader 文档学习-Data Feeds(上)

Backtrader 文档学习-Data Feeds 1.数据载入 Quickstart中已经学习了基础的数据载入到cerebro中。 self.datas 是按插入顺序的数组数组对象的别名self.data 和 self.data0 一样&#xff0c;都是指向第一组数据self.dataX 指向第N组数据 import backtrader as bt import bac…

【PC电脑windows-学习样例generic_gpio-拓展GPIO-ESP32的GPIO程序-问题解决-GPIO输出实验-基础样例学习(2)】

【PC电脑windows-学习样例generic_gpio-拓展GPIO-ESP32的GPIO程序-基础样例学习&#xff08;2&#xff09;】 1、概述2、实验环境3、 问题说明1&#xff1a;问题说明&#xff1a;使用官方样例&#xff0c;增加IO&#xff0c;编译会重新改回去。2&#xff1a;解决方式&#xff1…

STM32 使用ARM仿真器设置

STM32单片机程序下载到单片机芯片中有两种方式&#xff0c;①编译生成HEX&#xff0c;使用程序烧录软件刷到单片机芯片里。②使用ARM仿真器下载程序。使用ARM仿真器的优势是&#xff0c;在工程编译没问题直接在Keil软件里就可以将程序下载到单片机里&#xff0c;并且程序可以在…

苏州耕耘无忧物联网:降本增效,设备维护管理数字化转型的引领者

随着科技的快速发展和工业4.0的推动&#xff0c;设备维护管理已经从传统的被动式、经验式维护&#xff0c;转向了更加积极主动、数据驱动的维护模式。在这个过程中&#xff0c;苏州耕耘无忧物联科技有限公司以其深厚的技术积累和丰富的管理经验&#xff0c;引领着设备维护管理数…

ASP.NET Core基础之定时任务(二)-Quartz.NET入门

阅读本文你的收获 了解任务调度框架QuartZ.NET的核心构成学会在ASP.NET Core 中使用QuartZ.NET 在项目的开发过程中&#xff0c;难免会遇见需要后台处理的任务&#xff0c;例如定时发送邮件通知、后台处理耗时的数据处理等&#xff0c;上次分享了ASP.NET Core中实现定时任务的…

4. 行为模式 - 中介者模式

亦称&#xff1a; 调解人、控制器、Intermediary、Controller、Mediator 意图 中介者模式是一种行为设计模式&#xff0c; 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互&#xff0c; 迫使它们通过一个中介者对象进行合作。 问题 假如你有一个创建…

el-date-picker时间戳问题

最近用el-date-picker时间插件&#xff0c;没想到只能得到格式化的日期&#xff0c;那能不能得到时间戳呢&#xff1f;答案是肯定的&#xff0c;最恶心的来了&#xff0c;按照大多数人提供的方案得到了一个莫名其妙的字符串&#xff0c;看起来很奇怪 经过不懈的努力找到了最终的…

通过U盘:将电脑进行重装电脑

目录 一.老毛桃制作winPE镜像 1.制作准备 2.具体制作 下载老毛桃工具 插入U盘 选择制作模式 正式配置U盘 安装提醒 安装成功 具体操作 二.使用ultrasio制作U盘 1.具体思路 2.图片操作 三.硬盘安装系统 具体操作 示例图 ​编辑 一.老毛桃制作winPE镜像 1.制作准…

神经网络:深度学习优化方法

1.有哪些方法能提升CNN模型的泛化能力 采集更多数据&#xff1a;数据决定算法的上限。 优化数据分布&#xff1a;数据类别均衡。 选用合适的目标函数。 设计合适的网络结构。 数据增强。 权值正则化。 使用合适的优化器等。 2.BN层面试高频问题大汇总 BN层解决了什么问…

使用@jiaminghi/data-view实现一个数据大屏

<template><div class"content bg"><!-- 全局容器 --><!-- <dv-full-screen-container> --><!-- 第二行 --><div class"module-box" style"align-items: start; margin-top: 10px"><!-- 左 -->…

【IntelliJ IDEA】打开项目Git突然无法识别解决方案

这个问题也是我今天突然偶尔遇到的&#xff0c;当时没在意&#xff0c;项目打开之后又关闭&#xff0c;后来很久才又打开&#xff0c;发现项目明明有git版本控制的&#xff0c;咋突然开发工具右下角没有标识了&#xff0c;然后检查了一下git配置还报错了。 其实从图上我们可以看…

ctfshow sql 195-200

195 堆叠注入 十六进制 if(preg_match(/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\|\"|select|union|or|and|\x26|\x7c|file|into/i, $username)){$ret[msg]用户名非法;die(json_encode($ret));}可以看到没被过滤&#xff0c;select 空格 被过滤了&#xff0c;可…

【Week-P2】CNN彩色图片分类-CIFAR10数据集

文章目录 一、环境配置二、准备数据三、搭建网络结构四、开始训练五、查看训练结果六、总结3.1 ⭐ torch.nn.Conv2d()详解3.2 ⭐ torch.nn.Linear()详解3.3 ⭐torch.nn.MaxPool2d()详解3.4 ⭐ 关于卷积层、池化层的计算4.2.1 optimizer.zero_grad()说明4.2.2 loss.backward()说…

SQL---Zeppeline前驱记录与后驱记录查询

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

油猴脚本教程案例【键盘监听】-编写 ChatGPT 快捷键优化

文章目录 1. 元数据1. name2. namespace3. version4. description5. author6. match7. grant8. icon 2. 编写函数.1 函数功能2.1.1. input - 聚焦发言框2.1.2. stop - 取消回答2.1.3. newFunction - 开启新窗口2.1.4. scroll - 回到底部 3. 监听键盘事件3.1 监听X - 开启新对话…

GPTs | Actions应用案例

上篇文章说道&#xff0c;如何使用创建的GPTs通过API接口去获取外部的一些信息&#xff0c;然后把获取的外部信息返回给ChatGPT让它加工出来&#xff0c;回答你的问题&#xff0c;今天我们就来做一个通俗易懂的小案例&#xff0c;让大家来初步了解一下它的使用法&#xff01; …

【数字图像处理】实验二 图像变换

图像变换 一、实验内容&#xff1a; 1&#xff0e; 熟悉和掌握利用Matlab工具进行数字图像的读、写、显示等数字图像处理基本步骤。 2&#xff0e; 熟练掌握各种图像变换的基本原理及方法。 3&#xff0e; 能够从深刻理解图像变换&#xff0c;并能够思考拓展到一定的应用领域。…