机器学习周报第46周

news2024/11/25 6:36:39

目录

  • 摘要
  • Abstract
  • 一、文献阅读
    • 1.1 摘要
    • 1.2 研究背景
    • 1.3 论文方法
    • 1.4 模块分析
    • 1.5 网络规格
    • 1.6 高效的端到端对象检测
    • 1.7 mobile former模块代码

目录

  • 摘要
  • Abstract
  • 一、文献阅读
    • 1.1 摘要
    • 1.2 研究背景
    • 1.3 论文方法
    • 1.4 模块分析
    • 1.5 网络规格
    • 1.6 高效的端到端对象检测
    • 1.7 mobile former模块代码

摘要

Abstract

一、文献阅读

题目:Mobile-Former: Bridging MobileNet and Transformer

1.1 摘要

我们提出了 Mobile-Former,这是一种 MobileNet 和 Transformer 的并行设计,它们之间通过双向桥接结构进行通信。这种结构利用了 MobileNet 在局部处理方面的优势和 Transformer 在全局交互方面的优势。桥接结构实现了局部和全局特征的双向融合。与最近的 Vision Transformer 工作不同,Mobile-Former 中的 Transformer 包含非常少的可学习 token,这些 token 是随机初始化的,用于学习全局先验,从而实现低计算成本。结合我们提出的轻量级交叉注意力来模拟桥接,Mobile-Former 不仅计算效率高,而且具有更强的表征能力。在 ImageNet 分类任务中,它在从 25M 到 500M FLOPs 的低 FLOP 范围内优于 MobileNetV3。例如,在 294M FLOPs 时,Mobile-Former 达到了 77.9% 的 top-1 准确率,比 MobileNetV3 高出 1.3%,但计算量节省了 17%。当转移到目标检测时,在 RetinaNet 框架中 Mobile-Former 比 MobileNetV3 高出 8.6 AP。此外,我们通过在 DETR 中用 Mobile-Former 替换骨干网络、编码器和解码器,构建了一个高效的端到端检测器,比 DETR 高出 1.1 AP,但计算成本节省了 52%,参数减少了 36%。

1.2 研究背景

Mobile-Former的研究背景根植于对高效深度学习模型的不断追求,尤其是在移动和嵌入式视觉应用领域。传统的卷积神经网络(CNNs),如MobileNet系列,通过使用深度可分离卷积等技术,在局部特征提取上表现出色,但它们在捕捉图像全局上下文信息方面存在局限。另一方面,Transformer架构,尽管在处理全局依赖关系方面具有显著优势,但其计算成本较高,尤其是在视觉任务中,这限制了其在资源受限的设备上的应用。
随着移动设备和实时视觉系统的普及,对于能够在有限计算资源下运行的高效模型的需求日益增长。然而,现有的轻量级CNNs在处理复杂的视觉任务时,往往难以平衡计算效率和表示能力。例如,MobileNetV3虽然在低FLOPs范围内表现出色,但它在处理需要长距离依赖关系的任务时可能不够有效。与此同时,尽管Vision Transformer(ViT)等模型在大规模数据集上展现出了强大的性能,但它们在小规模数据集上的泛化能力较差,且计算成本较高,不适合部署在计算资源受限的环境中。
此外,早期尝试将CNN和Transformer结合的方法,如串联结构,并没有充分发挥两者的优势。这些方法通常将卷积和自注意力机制按顺序堆叠,可能导致全局特征的捕捉不足,或者在模型设计上缺乏灵活性,难以适应不同的计算预算和任务需求。
针对这些不足,Mobile-Former提出了一种创新的并行设计,通过一个双向桥接结构,实现了MobileNet的局部特征提取能力和Transformer的全局特征捕捉能力的有机结合。这种设计不仅提高了模型的表示能力,还显著降低了计算成本,使得Mobile-Former在低FLOPs范围内的性能超过了现有的轻量级CNNs和ViT变体。通过这种方式,Mobile-Former旨在解决现有方案在资源受限设备上部署时面临的挑战,同时为图像分类和目标检测等视觉任务提供了一种高效的解决方案。

1.3 论文方法

并行结构:Mobile-Former将MobileNet和Transformer进行并行化,并通过双向交叉注意力连接它们(见图1)。Mobile(指MobileNet)接收一张图片作为输入(X ∈ RHW×3),并应用倒置瓶颈块来提取局部特征。Former(指Transformer)接收可学习的参数(或称为token)作为输入,表示为Z ∈ RM×d,其中M和d分别是token的数量和维度。这些token是随机初始化的。与视觉Transformer(ViT)不同,在ViT中,token线性地映射局部图像块,而在Former中,token的数量显著更少(在本文中M ≤ 6),每个token代表了图像的一个全局先验。这导致计算成本大幅降低。
在这里插入图片描述

低成本双向桥接:Mobile和Former通过一个双向桥接进行通信,在这里局部特征和全局特征以双向的方式进行融合。这两个方向分别表示为Mobile→Former和Mobile←Former。我们提出了一个轻量级的交叉注意力机制来模拟这一过程,在该机制中,为了节省计算量,从Mobile一侧移除了投影(WQ, WK, WV),但在Former一侧则保留了这些投影。交叉注意力是在Mobile的瓶颈处计算的,即在通道数较低的地方。具体来说,从局部特征图X到全局token Z的轻量级交叉注意力计算如下:
在这里插入图片描述

  • 注意力机制:使用标准的注意力函数Attn(Q, K, V),其中Q是Query(查询),K是Key(键),V是Value(值)。

  • 多头注意力:将局部特征X和全局token Z分割成多个头部(h heads),对于第i个头,计算其对应的轻量级交叉注意力。

  • 投影矩阵:在Mobile→Former方向上,移除了键和值的投影矩阵(WK和WV),只保留了查询的投影矩阵WQi,以减少计算量。

  • 计算:对于Mobile→Former,交叉注意力AX→Z是从局部特征X到全局token Z的,计算方式是将每个头部的注意力分数进行拼接和组合。

  • 瓶颈处的计算:由于在Mobile的瓶颈处通道数较低,因此在这里执行交叉注意力的计算可以进一步降低计算成本。

1.4 模块分析

Mobile-Former块:Mobile-Former 由堆叠的 Mobile-Former 块组成(见图 1)。 每个块有四个支柱:Mobile 子块、Former 子块和双向交叉注意力 Mobile←Former 和 Mobile→Former(如图 3 所示)。
在这里插入图片描述
输入和输出:Mobile-Former块有两个输入:(a) 本地特征图X ∈ RHW×C,它在高度H和宽度W上具有C个通道;(b) 全局token Z ∈ RM×d,其中M和d分别是token的数量和维度。注意,所有块中的M和d都是相同的。Mobile-Former块输出更新后的本地特征图X’和全局token Z’,这些将作为下一个块的输入。
Mobile子块:如图3所示,Mobile子块以特征图X作为输入,其输出被用作Mobile←Former的输入。它与文献中的倒置瓶颈块略有不同,区别在于将ReLU替换为动态ReLU作为激活函数。与原始的动态ReLU不同,原始的动态ReLU是通过在平均池化特征上应用两层MLP(多层感知机)层来生成参数的,而我们通过在Former的第一个全局token输出z’1上应用两层MLP(图中的θ)来节省平均池化步骤。请注意,对于所有块,深度卷积的核大小都是3×3。
Former子块:Former子块是一个标准的Transformer块,包括多头注意力(Multi-Head Attention, MHA)和前馈网络(Feed-Forward Network, FFN)。在FFN中使用的扩展比率是2(而不是4)。我们遵循文献使用后层归一化(post layer normalization)。Former的处理位于Mobile→Former和Mobile←Former之间(见图3)。

  • 多头注意力(MHA):这一部分允许模型在处理token时同时关注不同的位置,从而捕捉图像中的长距离依赖关系。
  • 前馈网络(FFN):FFN通常包含两个线性变换,它们之间有一个激活函数。在Mobile-Former中,FFN的扩展比率设置为2,这意味着FFN的中间层的大小是输入层的两倍,而不是传统的四倍。这种设计有助于减少计算量,同时保持网络的表达能力。
  • 后层归一化:在MHA和FFN之后,使用层归一化来稳定训练过程,并提高模型的泛化能力。这与Transformer架构中的常见做法一致。
  • Former的处理位置:Former子块在Mobile→Former(局部特征到全局token的交叉注意力)和Mobile←Former(全局token到局部特征的交叉注意力)的计算之间进行处理。这意味着Former子块的输出将反馈到Mobile←Former,以进一步融合全局信息。

Mobile→Former:我们提出的轻量级交叉注意力(方程1)被用来将局部特征X融合到全局token Z中。与标准注意力机制相比,在局部特征X上的键(key)的投影矩阵WK和值(value)的投影矩阵WV被移除,以节省计算量(见图3)。
Mobile←Former:在这里,交叉注意力(方程2)的方向与Mobile→Former相反。它将全局token融合到局部特征中。局部特征作为查询(query),而全局token作为键(key)和值(value)。因此,我们保留了键WK和值WV的投影矩阵,但为了节省计算量,去除了查询WQ的投影矩阵,如图3所示。
计算复杂性:Mobile-Former块的四个组成部分具有不同的计算成本。给定一个大小为HW×C的输入特征图,以及M个全局token,每个token的维度为d,Mobile部分消耗的计算量最多,计算复杂度为O(HWC ^ 2)。Former和双向桥接的计算成本较低,消耗的总计算成本不到20%。具体来说,Former的自注意力和前馈网络(FFN)的计算复杂度为O(M ^ 2d + Md^2)。Mobile→Former和Mobile←Former的交叉注意力共享计算复杂度O(MHWC + MdC)。

1.5 网络规格

架构:表1展示了一个具有294M FLOPs的Mobile-Former架构,适用于224×224的图像尺寸,该架构堆叠了11个Mobile-Former块,这些块在不同的输入分辨率下工作。所有块都有六个维度为192的全局token。它以一个3×3的卷积作为起点,以及在第一阶段使用一个轻量级瓶颈块[18],该块通过堆叠一个3×3的深度卷积和一个逐点卷积来扩展然后压缩通道数。第2到第5阶段由Mobile-Former块组成。每个阶段通过一个称为Mobile-Former↓的下采样变种来处理下采样。分类头对局部特征应用平均池化,与第一个全局token连接,然后通过两个带有h-swish的全连接层。
在这里插入图片描述

1.6 高效的端到端对象检测

Mobile-Former可以很容易地用于目标检测的骨干网络和头部,提供了一个高效的端到端检测器。使用相同数量的目标查询(100个),它在性能上超越了DETR,但使用的FLOPs要低得多。
Backbone-Head架构:我们在骨干网络和头部都使用了Mobile-Former块(见图4),它们拥有独立的token。骨干网络有六个全局token,而头部有100个对象查询,这些查询的生成方式与DETR[1]类似。与DETR在头部只有一个尺度(1/32或1/16)不同,Mobile-Former头部采用了多尺度(1/32,1/16,1/8),由于其计算效率,这些多尺度的FLOPs很低。上采样通过双线性插值实现,然后加上具有相同分辨率的来自骨干网络的特征输出。所有对象查询逐步在从粗糙到精细的不同尺度上细化它们的表示,节省了FPN[19]中手动按大小分配对象跨尺度的过程。我们遵循DETR,在头部使用预测FFN(前馈网络)和辅助损失进行训练。头部是从零开始训练的,而骨干网络是在使用ImageNet预训练的。我们的端到端Mobile-Former检测器在计算上是高效的。使用Mobile-Former-508M作为骨干网络和头部中有九个Mobile-Former块的E2EMF-508M的总成本是41.4G FLOPs,明显低于DETR(86G FLOPs)。但它的性能超过了DETR 1.1 AP(43.1 vs. 42.0 AP)。头部结构的细节列在附录A中(见表13)。
在这里插入图片描述
空间感知动态ReLU在骨干网络中的应用:我们将骨干网络中的动态ReLU从空间共享扩展到空间感知,通过涉及所有全局token来生成参数,而不仅仅是使用第一个token,因为这些token具有不同的空间焦点。让我们将空间共享动态ReLU的参数生成表示为θ = f(z1),其中z1是第一个全局token,f(·)由两个带有中间ReLU激活的MLP(多层感知机)层建模。相比之下,空间感知动态ReLU为特征图中的每个空间位置i生成参数θi,使用所有全局token {zj},如下所示:
在这里插入图片描述
调整 head 中的位置嵌入:与在所有解码器层共享对象查询的位置嵌入的 DETR [1] 不同,随着每个块的特征图发生变化,我们细化了 head 中每个块之后的位置嵌入。 让我们将第 k 个块处的查询的特征和位置嵌入分别表示为 qf k 和 qp k。 它们的总和(qf k + qp k)用于计算对象查询和特征图之间的交叉注意力以及对象查询之间的自注意力,之后特征嵌入被更新为下一个块的输入 qf k+1 。 在这里,我们根据特征嵌入调整位置嵌入:
在这里插入图片描述

1.7 mobile former模块代码

class MobileFormer(nn.Module):
    def __init__(
        self,
        block_args,
        num_classes=1000,
        img_size=224,
        width_mult=1.,
        in_chans=3,
        stem_chs=16,
        num_features=1280,
        dw_conv='dw',
        kernel_size=(3,3),
        cnn_exp=(6,4),
        group_num=1,
        se_flag=[2,0,2,0],
        hyper_token_id=0,
        hyper_reduction_ratio=4,
        token_dim=128,
        token_num=6,
        cls_token_num=1,
        last_act='relu',
        last_exp=6,
        gbr_type='mlp',
        gbr_dynamic=[False, False, False],
        gbr_norm='post',
        gbr_ffn=False,
        gbr_before_skip=False,
        gbr_drop=[0.0, 0.0],
        mlp_token_exp=4,
        drop_rate=0.,
        drop_path_rate=0.,
        cnn_drop_path_rate=0.,
        attn_num_heads = 2,
        remove_proj_local=True,
        ):

        super(MobileFormer, self).__init__()

        cnn_drop_path_rate = drop_path_rate
        mdiv = 8 if width_mult > 1.01 else 4
        self.num_classes = num_classes

        #global tokens
        self.tokens = nn.Embedding(token_num, token_dim) 

        # Stem
        self.stem = nn.Sequential(
            nn.Conv2d(in_chans, stem_chs, 3, stride=2, padding=1, bias=False),
            nn.BatchNorm2d(stem_chs),
            nn.ReLU6(inplace=True)
        )
        input_channel = stem_chs

        # blocks
        layer_num = len(block_args)
        inp_res = img_size * img_size // 4
        layers = []
        for idx, val in enumerate(block_args):
            b, t, c, n, s, t2 = val # t2 for block2 the second expand
            block = eval(b)

            t = (t, t2)
            output_channel = _make_divisible(c * width_mult, mdiv) if idx > 0 else _make_divisible(c * width_mult, 4) 

            drop_path_prob = drop_path_rate * (idx+1) / layer_num
            cnn_drop_path_prob = cnn_drop_path_rate * (idx+1) / layer_num

            layers.append(block(
                input_channel, 
                output_channel, 
                s, 
                t, 
                dw_conv=dw_conv,
                kernel_size=kernel_size,
                group_num=group_num,
                se_flag=se_flag,
                hyper_token_id=hyper_token_id,
                hyper_reduction_ratio=hyper_reduction_ratio,
                token_dim=token_dim, 
                token_num=token_num,
                inp_res=inp_res,
                gbr_type=gbr_type,
                gbr_dynamic=gbr_dynamic,
                gbr_ffn=gbr_ffn,
                gbr_before_skip=gbr_before_skip,
                mlp_token_exp=mlp_token_exp,
                norm_pos=gbr_norm,
                drop_path_rate=drop_path_prob,
                cnn_drop_path_rate=cnn_drop_path_prob,
                attn_num_heads=attn_num_heads,
                remove_proj_local=remove_proj_local,        
            ))
            input_channel = output_channel

            if s == 2:
                inp_res = inp_res // 4

            for i in range(1, n):
                layers.append(block(
                    input_channel, 
                    output_channel, 
                    1, 
                    t, 
                    dw_conv=dw_conv,
                    kernel_size=kernel_size,
                    group_num=group_num,
                    se_flag=se_flag,
                    hyper_token_id=hyper_token_id,
                    hyper_reduction_ratio=hyper_reduction_ratio,
                    token_dim=token_dim, 
                    token_num=token_num,
                    inp_res=inp_res,
                    gbr_type=gbr_type,
                    gbr_dynamic=gbr_dynamic,
                    gbr_ffn=gbr_ffn,
                    gbr_before_skip=gbr_before_skip,
                    mlp_token_exp=mlp_token_exp,
                    norm_pos=gbr_norm,
                    drop_path_rate=drop_path_prob,
                    cnn_drop_path_rate=cnn_drop_path_prob,
                    attn_num_heads=attn_num_heads,
                    remove_proj_local=remove_proj_local,
                ))
                input_channel = output_channel

        self.features = nn.Sequential(*layers)

        # last layer of local to global
        self.local_global = Local2Global(
            input_channel,
            block_type = gbr_type,
            token_dim=token_dim,
            token_num=token_num,
            inp_res=inp_res,
            use_dynamic = gbr_dynamic[0],
            norm_pos=gbr_norm,
            drop_path_rate=drop_path_rate,
            attn_num_heads=attn_num_heads
        )

        # classifer
        self.classifier = MergeClassifier(
            input_channel, 
            oup=num_features, 
            ch_exp=last_exp,
            num_classes=num_classes,
            drop_rate=drop_rate,
            drop_branch=gbr_drop,
            group_num=group_num,
            token_dim=token_dim,
            cls_token_num=cls_token_num,
            last_act = last_act,
            hyper_token_id=hyper_token_id,
            hyper_reduction_ratio=hyper_reduction_ratio
        )

        #initialize
        self._initialize_weights()

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
                if m.bias is not None:
                    m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
            elif isinstance(m, nn.Linear):
                n = m.weight.size(1)
                m.weight.data.normal_(0, 0.01)
                m.bias.data.zero_()


    def forward(self, x):
        # setup tokens
        bs, _, _, _ = x.shape
        z = self.tokens.weight
        tokens = z[None].repeat(bs, 1, 1).clone()
        tokens = tokens.permute(1, 0, 2)
 
        # stem -> features -> classifier
        x = self.stem(x)
        x, tokens = self.features((x, tokens))
        tokens, attn = self.local_global((x, tokens))
        y = self.classifier((x, tokens))

        return y

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

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

相关文章

可以用来制作硬模空心耳机壳的胶粘剂有哪些种类?

可以用来制作硬模空心耳机壳的胶粘剂有哪些种类? 制作耳机壳的胶粘剂有很多种类,常见的有环氧树脂胶水、UV树脂胶、快干胶、热熔胶等。 这些胶粘剂都有不同的特点和适用场景,可以根据自己的需求选择合适的类型。 例如: 环氧树脂…

九、BGP路由属性和选路

目录 一、属性分类 1.1、公认属性 1.2、可选属性 二、选路原则 0、丢弃不可达 取值越大越优 1、Preferred-Value 2、Local_Preference 取值越小越优 3、路由优先级 4、AS_Path 5、Origin 6、MED 7、路由来源 8、Next_Hop的IGP度量值 BGP路由等价负载分担&#…

springboot景区寄存管理系统(源码+sql+论文报告)

针对传统人工行李寄存效率低和安全性不足等问题,设计并实现了一种由网页控制器组成的智能行李寄存系统。首先能够实现行李的寄存管理和行李柜管理以及记录查询和通知公告以及管理员等灵活控制菜单显示权限。经过研究和测试结果显示,该行李寄存系统实现了…

【什么!Grok记录被打破了】坏消息不是Meta的 llama3 400,好消息是Nvidia发布的Nemotron-4 340B且支持开源

Nvidia 发布了开创性的开放模型系列 “Nemotron-4 340B”,再次巩固了其作为人工智能创新领域无可争议的领导者的地位。这一发展标志着人工智能行业的一个重要里程碑,因为它使各行各业的企业能够创建功能强大的特定领域 LLM,而无需大量昂贵的真…

QT系列教程(11) TextEdit实现Qt 文本高亮

文本高亮 对于textedit里录入的部分单词我们可以实现高亮,实现高亮主要依赖于QSyntaxHighlighter。 我们先创建一个Qt Application类,类名MainWindow, 然后新增一个C类,类名为MySyntaxHighlighter。 #ifndef MYSYNTAXHIGHLIGHTER_H #define …

深入分析 Android BroadcastReceiver (三)

文章目录 深入分析 Android BroadcastReceiver (三)1. 广播消息的优缺点及使用场景1.1 优点1.2 缺点 2. 广播的使用场景及代码示例2.1. 系统广播示例:监听网络状态变化 2.2. 自定义广播示例:发送自定义广播 2.3. 有序广播示例:有序广播 2.4. …

yml配置文件快速上手

yml配置文件快速上手 springboot中,有三种文件可以作为配置文件 xml文件(不推荐,臃肿)application.propertis文件(层次不够分明)yml文件(推荐,层次分明,语法简洁) yml文件的基本语…

记录:利用 Agora 在 Unity3D MRTK场景中创建实时视频聊天应用

目录 准备1. 安装Agora_Unity_RTC_SDK2. 创建UI3. script具体内容4. 使用测试 本质是两部带摄像机的设备同时进入Agora聊天室内视频。 去年实现过一次这个功能,用的是Agora_Unity_RTC_SDK 4.2.2版本的,今年使用失败,遂重新安装最新版本Agora…

Github 2024-06-15Rust开源项目日报Top10

根据Github Trendings的统计,今日(2024-06-15统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10TypeScript项目1JavaScript项目1Deno: 现代JavaScript和TypeScript运行时 创建周期:2118 天开发语言:Rust, JavaScript协议类型:M…

浪潮信息打造业界首款50℃进液温度服务器 PUE逼近理论极限1.0!

在科技飞速发展的今天,浪潮信息以其前瞻性的技术创新思维,再次突破行业极限,推出业界首个支持50℃进液温度的浸没式液冷服务器NF5180G7。这一创新成果不仅展现了浪潮信息在液冷技术领域的深厚实力,更标志着服务器冷却技术的一次重…

SpringBoot使用jasypt实现数据库信息的脱敏,以此来保护数据库的用户名username和密码password(容易上手,详细)

1.为什么要有这个需求? 一般当我们自己练习的时候,username和password直接是爆露出来的 假如别人路过你旁边时看到了你的数据库账号密码,他跑到他的电脑打开navicat直接就是一顿连接,直接疯狂删除你的数据库,那可就废…

(南京观海微电子)——液晶屏显示不良及修复

TFT LCD信号驱动 屏横线 横暗线、暗带、竖线、竖带 原因: 1、COF与玻璃Bonding不良; 2、COF或玻璃遭到损伤(ESD或机械折伤); 3、ASG电路失效(仅对ASG技术panel而言) 解决方案&#xff1…

STM32定时器篇——Systick定时器的使用(实现delay延时函数)

一、Systick定时器的简介: Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器对于CM3,CM4内核芯片,都有Systick定时器。当Systick计到0时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中…

SpringBoot【2】集成 MyBatis Plus

SpringBoot 集成 MyBatis Plus 前言修改 pom.xml修改配置文件添加 实体类添加 持久层接口添加 持久层 XxxMapper.xml 文件添加 业务接口层添加 业务接口实现类添加 控制层添加 MyBatis 配置AutoFillMetaObjectHandlerMyBatisPlusConfig 验证 前言 由于 MySQL 备份/恢复测试&am…

LeetCode 算法:回文链表 c++

原题链接🔗:回文链表 难度:简单⭐️ 题目 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。 示例 1: 输入:head…

如何用 Google Chrome 浏览器浏览经过 XSLT 渲染的 XML 文件

对于经过XSLT渲染的XML文件,本来,可以直接用 IE (Internet Explorer) 打开,就能看到渲染之后的样子,很方便。但是后来,微软把 IE 换成了 Microsoft Edge,按理说这是比 IE 更先进的浏览器,可是偏…

Swift 是 C++ 的最佳继任者

苹果称 Swift 是 C 的最佳继任者 Swift 是苹果公司在 2014 年推出的,一款旨在替代 Objective-C 的编程语言。但苹果语言和运行时总监 Ted Kremenek 在 WWDC24 的主题演讲中表示,Swift 也将取代 C。 “Swift 的安全性、速度和易用性,加上内…

期末复习6--链表头插法(逆序)尾插法(顺序)---输出链表

头插法 #include <stdio.h> #include <stdlib.h>struct Node //定义结构体 {char data; //数据域struct Node * next; //指针域 };/* 请在这里填写答案 */void PrintList (struct Node * head) {struct Node * s;if(head NULL){printf("None&qu…

Python基础教程(二十一):多线程

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

在ubuntu中启动docker的mysql8镜像

首先查看docker是否启动&#xff1a; docker ps #出现信息就是启动成功 启动命令&#xff1a; sudo systemctl start docker 设置开机自启&#xff1a; sudo systemctl enable docker 查询下载好的mysql8的镜像文件&#xff1a; docker images 在启动查询好的镜像文件&#…