当前位置: 首页 > news >正文

Gold-YOLO(NeurIPS 2023)论文与代码解析

paper:Gold-YOLO: Efficient Object Detector via Gather-and-Distribute Mechanism

official implementation:https://github.com/huawei-noah/Efficient-Computing/tree/master/Detection/Gold-YOLO

存在的问题

在过去几年里,YOLO系列已经成为了实时目标检测领域最先进以及最常用的方法。许多研究通过修改模型架构、数据增强、设计新的损失函数将baseline提升到了一个更高的水平。但现有的模型仍然存在信息融合的问题,尽管FPN和PANet在一定程度上缓解了该问题。

传统的neck如FPN以及相关变体的结构如图3(a)所示,但是这种信息融合的方法存在一个明显的缺陷:当需要跨层融合信息时(如level-1和level-3),FPN式的结构无法无损的传输信息,这阻碍了YOLO系列更好的进行信息融合。

本文的创新点

针对FPN式结构存在的问题,本文在TopFormer理论的基础上,提出了一种新的聚合-分发(GD)机制,它通过融合多层特征并将全局信息注入到更高层,在YOLO中实现高效的信息交换。这显著增加了neck的信息融合能力,同时没有显著增加延迟。

基于此提出了一个新的模型Gold-YOLO,它提高了多尺度特征融合的能力,并在所有尺度上实现了延迟和精度之间的理想平衡。

此外,本文首次在YOLO系列中实现了MAE-style的预训练,使得YOLO系列可以从无监督预训练中受益。

方法介绍

如图3所示,在FPN的结构中,只能完全融合相邻层的信息,对于其它层的信息,只能间接的“递归”获得。这种传输模式可能导致计算过程中信息的丢失。为了避免这种情况,本文放弃了递归方法,构建了一种新的聚合-分发机制。通过使用一个统一的模块从各层收集和融合信息,然后将其分发到不同的层。

具体实现中,聚合与分发的过程对应三个模块:特征对齐模块(Feature Alignment Module, FAM)、信息融合模块(Information Fusion Module, IFM)、信息注入模块(Information Injection Module, Inject)。完整结构如图2所示

  • gather过程包括两步。首先,FAM从不同层收集和对齐特征。然后,IFM通过融合对齐的特征得到全局信息。
  • 在获得全局信息后,inject模块将这些信息distribute到每个level中,并使用简单的注意力操作进行注入,从而提高分支的检测能力。

为了增加模型检测不同大小对象的能力,提出了两个分支,low-stage GDhigh-stage GD。如图2所示,neck的输入包括backbone提取的特征图B2,B3,B4,B5,其中 \(B_{i}\in \mathbb{R}^{N\times C_{Bi}\times R_{Bi}}\),N是batch size,C是通道数,R=HxW。

Low GD

结构如图4(a)所示

Low-FAM

在Low-FAM中,用average pooling下采样得到一个统一大小的 \(F_{align}\)。这里选择 \(R_{B4}=\frac{1}{4}R\) 作为目标大小。

Low-IFM

Low-IFM包括多层重参数化卷积Block (RepBlock) 和一个split操作。具体来说,RepBlock取 \(F_{align}\)(\(channel=sum(C_{B2},C_{B3},C_{B4},C_{B5})\))作为输入得到 \(F_{fuse}\)(\(channel=C_{B4}+C_{B5}\)),然后沿通道维度split成 \(F_{inj\_P3}\) 和 \(F_{inj\_P4}\)。如下

Information injection module

为了更有效的将全局信息注入到不同的层,作者采用注意力机制来融合信息,如图5所示。具体来说,同时输入局部信息(当前层)和全局信息(IFM生成的)并分别记为 \(F_{local}\) 和 \(F_{inj}\),\(F_{inj}\) 通过两个不同的卷积层分别得到 \(F_{global\_embed}\) 和 \(F_{act}\)。\(F_{local}\) 通过卷积得到 \(F_{local\_embed}\)。然后通过注意力计算得到融合特征 \(F_{out}\)。其中 \(F_{local}\) 等于 \(Bi\),具体如下

High GD

High-GD融合Low-GD得到的特征 {P3, P4, P5},如图4(b)所示

High-FAM

High-FAM和Low-FAM的操作一样,通过全局平均池化下采样来对齐大小,目标大小为 \(R_{P5}=\frac{1}{8}R\)。

Hign-IFM

High-IFM包括多个transformer block和一个split操作。具体包括三步

  1. High-FAM的输出 \(F_{align}\) 通过transformer block融合得到 \(F_{fuse}\)
  2. \(F_{fuse}\) 通过1x1卷积通道降维到 \(sum(C_{P4},C_{P5})\)
  3. 沿通道进行split操作得到 \(F_{inj\_N4}\) 和 \(F_{inj\_N5}\)

具体如下

式(8)中的transformer融合模块包括多个堆叠的transformer block,每个block包含一个multi-head attention block、一个ffn、一个residual connection。具体配置和LeViT一样,K,Q的维度设为D(例如16),V的维度为2D(例如32)。考虑到推理速度,替换掉了一些速度不友好的操作,每个卷积的LN换成了BN,所有的GELU激活换成了ReLU。为了增强transformer block中的局部连接,两个1x1卷积中间增加了一层深度卷积。FFN的expansion factor设为2。

Information injection module

这里和Low-GD中的结构一样,其中 \(F_{local}\) 等于 \(Pi\),具体如下

Enhanced cross-layer information flow

为了进一步提升性能,作者借鉴YOLOv6里的PAFPN提出了一个Inject-LAF模块。这个模块是注入模块的增加,其中在注入模块的输入位置新加了一个轻量的相邻层融合模块(lightweight adjacent layer fusion, LAF)。为了实现速度和精度的平衡,设计了两种LAF:low-level LAF和high-level LAF,分别用于低层注入(合并相邻两层的特征)和高层注入(合并相邻一层的特征),具体结构如图5(b)所示。

代码解析

官方的实现是基于YOLOv6的实现,其中n,s的neck是"RepGDNeck",m的neck是"GDNeck",l的neck是"GDNeck2",因为从实验结果看,提升比较明显的事nano和small版本,因此这里只解析一下RepGDNeck的实现。具体实现代码在Efficient-Computing/Detection/Gold-YOLO/gold_yolo/reppan.py中,forward实现如下

def forward(self, input):(c2, c3, c4, c5) = input  # [(16,32,160,160),(16,64,80,80),(16,128,40,40),(16,256,20,20)]# Low-GD## use conv fusion global infolow_align_feat = self.low_FAM(input)  # (16,480,40,40)low_fuse_feat = self.low_IFM(low_align_feat)  # (16,96,40,40)low_global_info = low_fuse_feat.split(self.trans_channels[0:2], dim=1)  # [(16,64,40,40),(16,32,40,40)]## inject low-level global info to p4c5_half = self.reduce_layer_c5(c5)  # (16,64,20,20)p4_adjacent_info = self.LAF_p4([c3, c4, c5_half])  # (16,64,40,40)p4 = self.Inject_p4(p4_adjacent_info, low_global_info[0])  # (16,64,40,40)p4 = self.Rep_p4(p4)  # (16,64,40,40), 式(7)## inject low-level global info to p3p4_half = self.reduce_layer_p4(p4)  # (16,32,40,40)p3_adjacent_info = self.LAF_p3([c2, c3, p4_half])  # (16,32,80,80)p3 = self.Inject_p3(p3_adjacent_info, low_global_info[1])  # (16,32,80,80)p3 = self.Rep_p3(p3)  # (16,32,80,80)# High-GD## use transformer fusion global infohigh_align_feat = self.high_FAM([p3, p4, c5])  # (16,352,10,10)high_fuse_feat = self.high_IFM(high_align_feat)  # (16,352,10,10)high_fuse_feat = self.conv_1x1_n(high_fuse_feat)  # (16,192,10,10)high_global_info = high_fuse_feat.split(self.trans_channels[2:4], dim=1)  # [(16,64,10,10),(16,128,10,10)]## inject low-level global info to n4n4_adjacent_info = self.LAF_n4(p3, p4_half)  # (16,64,40,40)n4 = self.Inject_n4(n4_adjacent_info, high_global_info[0])  # (16,64,40,40)n4 = self.Rep_n4(n4)  # (16,64,40,40)## inject low-level global info to n5n5_adjacent_info = self.LAF_n5(n4, c5_half)  # (16,128,20,20)n5 = self.Inject_n5(n5_adjacent_info, high_global_info[1])  # (16,128,20,20)n5 = self.Rep_n5(n5)  # (16,128,20,20)outputs = [p3, n4, n5]  # [(16,32,80,80),(16,64,40,40),(16,128,20,20)]return outputs

首先是Low-GD,self.low_FAM的实现如下 

def forward(self, x):x_l, x_m, x_s, x_n = x# [(16,32,160,160),(16,64,80,80),(16,128,40,40),(16,256,20,20)]B, C, H, W = x_s.shapeoutput_size = np.array([H, W])if torch.onnx.is_in_onnx_export():self.avg_pool = onnx_AdaptiveAvgPool2dx_l = self.avg_pool(x_l, output_size)x_m = self.avg_pool(x_m, output_size)x_n = F.interpolate(x_n, size=(H, W), mode='bilinear', align_corners=False)out = torch.cat([x_l, x_m, x_s, x_n], 1)  # (16,480,40,40)return out

self.low_IFM的实现如下,其中的block是RepVGGBlock

self.low_IFM = nn.Sequential(Conv(extra_cfg.fusion_in, extra_cfg.embed_dim_p, kernel_size=1, stride=1, padding=0),  # 480,96*[block(extra_cfg.embed_dim_p, extra_cfg.embed_dim_p) for _ in range(extra_cfg.fuse_block_num)],  # 3Conv(extra_cfg.embed_dim_p, sum(extra_cfg.trans_channels[0:2]), kernel_size=1, stride=1, padding=0),
)

接着通过split操作得到low_global_info,即inject模块的输入 \(F_{inj\_P3}\) 和 \(F_{inj\_P4}\)。

以上分别对应式(1)~式(3)

接下来是inject模块,self.LAF_p4的实现如下

def forward(self, x):N, C, H, W = x[1].shapeoutput_size = (H, W)if torch.onnx.is_in_onnx_export():self.downsample = onnx_AdaptiveAvgPool2doutput_size = np.array([H, W])x0 = self.downsample(x[0], output_size)x1 = self.cv1(x[1])x2 = F.interpolate(x[2], size=(H, W), mode='bilinear', align_corners=False)return self.cv_fuse(torch.cat((x0, x1, x2), dim=1))

self.Inject_p4的实现如下

def forward(self, x_l, x_g):'''x_g: global featuresx_l: local features'''B, C, H, W = x_l.shapeg_B, g_C, g_H, g_W = x_g.shapeuse_pool = H < g_Hlocal_feat = self.local_embedding(x_l)global_act = self.global_act(x_g)  # 式(4)global_feat = self.global_embedding(x_g)  # 式(5)if use_pool:avg_pool = get_avg_pool()output_size = np.array([H, W])sig_act = avg_pool(global_act, output_size)global_feat = avg_pool(global_feat, output_size)else:sig_act = F.interpolate(self.act(global_act), size=(H, W), mode='bilinear', align_corners=False)global_feat = F.interpolate(global_feat, size=(H, W), mode='bilinear', align_corners=False)out = local_feat * sig_act + global_feat  # 式(6)return out

然后self.Rep_p4对应式(7)。接下来inject to p3和p4的操作是一致的。

接下来是Hign-GD,输入是C5以及Low-GD的输出P3、P4。

self.high_FAM的实现如下

def forward(self, inputs):B, C, H, W = get_shape(inputs[-1])H = (H - 1) // self.stride + 1W = (W - 1) // self.stride + 1output_size = np.array([H, W])if not hasattr(self, 'pool'):self.pool = nn.functional.adaptive_avg_pool2dif torch.onnx.is_in_onnx_export():self.pool = onnx_AdaptiveAvgPool2dout = [self.pool(inp, output_size) for inp in inputs]return torch.cat(out, dim=1)

self.high_IFM采用transformer block,这里具体实现就不贴了。然后是1x1卷积接split操作。上面对应式(8)~式(10)

然后是inject模块,首先self.LAF_n4只融合相邻一层的特征

def forward(self, x1, x2):if torch.onnx.is_in_onnx_export():self.pool = onnx_AdaptiveAvgPool2delse:self.pool = nn.functional.adaptive_avg_pool2dN, C, H, W = x2.shapeoutput_size = np.array([H, W])x1 = self.pool(x1, output_size)return torch.cat([x1, x2], 1)

接下来的self.Inject_n4和self.Rep_n4与low-GD中的self.Inject_p4和self.Rep_p4是一样的。

实验结果

GOLD-YOLO和其他YOLO的效果对比如下,可以看出主要提升在nano和small版本上。

相关文章:

Gold-YOLO(NeurIPS 2023)论文与代码解析

paper&#xff1a;Gold-YOLO: Efficient Object Detector via Gather-and-Distribute Mechanism official implementation&#xff1a;https://github.com/huawei-noah/Efficient-Computing/tree/master/Detection/Gold-YOLO 存在的问题 在过去几年里&#xff0c;YOLO系列已经…...

多个coco数据标注文件合并

一、coco数据集是什么&#xff1f; COCO&#xff08;Common Objects in Context&#xff09;是一个用于目标检测和图像分割任务的标注格式。如果你有多个COCO格式的JSON文件&#xff0c;你可能需要将它们合并成一个文件&#xff0c;以便更方便地处理和管理数据。在这篇博客中&…...

Kubernetes(K8S)拉取本地镜像部署Pod 实现类似函数/微服务功能(可设置参数并实时调用)

以两数相加求和为例&#xff0c;在kubernetes集群拉取本地的镜像&#xff0c;实现如下效果&#xff1a; 1.实现两数相加求和 2.可以通过curl实时调用&#xff0c;参数以GET方式提供&#xff0c;并得到结果。&#xff08;类似调用函数&#xff09; 一、实现思路 需要准备如下的…...

k8s使用ingress实现应用的灰度发布升级

v1是1.14.0版本nginx ,实操时候升级到v2是1.20.0版本nginx&#xff0c;来测试灰度发布实现过程 一、方案&#xff1a;使用ingress实现应用的灰度发布 1、服务端&#xff1a;正常版本v1&#xff0c;灰度升级版本v2 2、客户端&#xff1a;带有请求头versionv2标识的请求访问版…...

最新热门商用GPT4.0带MJ绘画去授权版本自定义三方接口(开心版)

一台VPS 搭建宝塔 解析域名 上传程序至根目录 访问首页在线安装配置数据库 PHP版本选择:7.3 安装完成后访问网站首页即可&#xff01; 配置APIKEY&#xff0c;登录网站后台自定义配置&#xff0c;不然网站无法使用&#xff01; 网站后台地址/admin 默认账号:admin 密码…...

Halcon基于形状的模板匹配inspect_shape_model

Halcon基于形状的模板匹配 基于形状的匹配&#xff0c;就是使用目标对象的轮廓形状来描述模板。Halcon中有操作助手&#xff0c;可以直观 地进行形状模板匹配的参数选择以及效果测试。如果使用算子编写&#xff0c;步骤如下。 &#xff08;1&#xff09;从参考图像上选择检测的…...

html中根元素以及根元素字体的含义

在 HTML 中&#xff0c;根元素是指 <html> 标签&#xff0c;可以使用 CSS 来设置根元素的字体大小。根元素的字体大小会影响整个页面的文本内容&#xff0c;默认情况下&#xff0c;根元素的字体大小是浏览器默认的大小。 要设置根元素的字体大小&#xff0c;你可以使用 …...

51单片机1-6

目录 单片机介绍 点亮一个LED 流水灯参考代码 点亮流水LEDplus版本 独立按键 独立按键控制LED亮灭 静态数码管 静态数码管显示 动态数码管显示 模块化编程 调试工具 矩阵键盘 矩阵键盘显示数据 矩阵键盘密码锁 学习B站江协科技课程笔记。 安装keil&#xff0c;下…...

vue2(Vuex)、vue3(Pinia)、react(Redux)状态管理

vue2状态管理Vuex Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它使用集中式存储管理应用的所有组件的状态&#xff0c;以及规则保证状态只能按照规定的方式进行修改。 State&#xff08;状态&#xff09;:Vuex 使用单一状态树&#xff0c;即一个对象包含全部的应用层…...

用户画像项目背景

1,用户画像项目介绍 大数据平台简介 数据仓库+用户画像+推荐系统 (1)数据仓库:加快数据的分析和查询 数据仓库分层:ODS层(映射HDFS的数据)—DW(数据仓库层)–APP(层)—BI(层) DW:DWD明细数据层(数据的清洗和转换),DWM(轻度聚合层),DWS(高度聚合),APP(层),DIM(层) …...

Go使用记忆化搜索的套路【以20240121力扣每日一题为例】

题目 分析 这道题很明显记忆化搜索&#xff0c;用py很容易写出来 Python class Solution:def splitArray(self, nums: List[int], k: int) -> int:n len(nums)# 寻找分割子数组中和的最小的最大值s [0]for num in nums:s.append(s[-1] num)#print(s)cachedef dfs(cur,…...

【LeetCode】每日一题 2024_1_21 分割数组的最大值(二分)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;分割数组的最大值题目描述代码与解题思路 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 今天是 hard&#xff0c;难受&#xff0c;还好有题解大哥的清晰讲解 题目&a…...

bevy the book 20140118翻译(全)

源自&#xff1a;Bevy Book: Introduction 主要用 有道 翻译。 Introduction 介绍 Getting Started 开始 Setup 设置 Apps 应用程序 ECS Plugins 插件 Resources 资源 Next Steps 下一个步骤 Contributing 贡献 Code 代码 Docs 文档 Building Bevys Ecosystem 构建 b…...

MySQL数据库面试知识点

1、数据库基础&#xff1a; MySQL是一个开源的关系型数据库管理系统&#xff0c;用于存储、管理和检索数据。它支持多种存储引擎&#xff0c;包括InnoDB、MyISAM等。MySQL是由瑞典公司MySQL AB开发&#xff0c;后来被Sun Microsystems收购&#xff0c;最终被甲骨文公司(Oracle…...

超优秀的三维模型轻量化、格式转换、可视化部署平台!

1、基于 HTML5 和 WebGL 技术&#xff0c;可在主流浏览器上进行快速浏览和调试&#xff0c;支持PC端和移动端 2、自主研发 AMRT 展示框架和9大核心技术&#xff0c;支持3D模型全网多端流畅展示与交互 3、提供格式转换、减面展UV、烘焙等多项单模型和倾斜摄影模型轻量化服务 4、…...

云原生全栈监控解决方案(全面详解)

【作者】JasonXu 前言 当前全球企业云化、数字化进程持续加速&#xff0c;容器、微服务等云原生技术在软件架构中快速渗透&#xff0c;IT 架构云化、复杂化持续驱动性能监控市场。企业云化、数字化持续转型&#xff0c;以及为了考虑系统的弹性、效率&#xff0c;企业软件开发中…...

代码随想录二刷 | 回溯 |复原IP地址

代码随想录二刷 &#xff5c; 回溯 &#xff5c;复原IP地址 题目描述解题思路代码实现 题目描述 93.复原IP地址 给定一个只包含数字的字符串&#xff0c;复原它并返回所有可能的 IP 地址格式。 有效的 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&am…...

windows资源管理器占用过高CPU的问题

最近&#xff0c;笔者的电脑在进行文件操作时变得异常的卡顿&#xff0c;打开任务管理器发现windows资源管理器占用了50%-80%的CPU。这里指的文件操作包括但不限于解压&#xff0c;复制&#xff0c;粘贴&#xff0c;甚至重命名一个文件夹都会引起50%的CPU占用。起初笔者认为可能…...

redis的常见数据类型和应用场景(非八股)------大总结(学了要会用-------教你如何使用)

Redis的数据类型 Redis 提供了丰富的数据类型&#xff0c;常见的有五种&#xff1a; String&#xff08;字符串&#xff09;&#xff0c;Hash&#xff08;哈希&#xff09;&#xff0c;List&#xff08;列表&#xff09;&#xff0c;Set&#xff08;集合&#xff09;、Zset&am…...

UE 可靠UDP实现原理

发送 我们的消息发送都是通过 UChannel 来处理的&#xff0c;通过调用 UChannel::SendBunch 统一处理。 发送的 Bunch 是以 FOutBunch 的形式存在的。当 bReliable 为 True 的时候&#xff0c;表示 Bunch 是可靠的。 发送逻辑直接从UChannel::SendBunch处开始分析 1、大小限…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...