yolov5

image-20231216210026711

image-20231216210245075

IOU —-交并比

image-20231216210441830

Lou为1意味着预测边界框和地面真实边界框完全重叠。
您可以为LOU设置阈值,以确定对象检测是否有效。
假设您将LOU设置为0.5,在这种情况下。
·如果LOU≥为0.5,则将目标检测归类为真阳性(TP)。
如果LOU<0.5,则为错误检测,并将其归类为假阳性(FP)。
当图像中存在地面真实且模型未能检测到目标时,分类。
作为假阴性(FN)。
真负片(TN):TN是我们没有预测到物体的图像的每一部分。
度量对于目标检测没有用处,因此我们忽略TN。

image-20231216210636311

AP,MAP

image-20231216210923498

image-20231216211711468

image-20231216211921852

image-20231216204704657

image-20231216204353728

网络架构和组件

单阶段检测器:

image-20231216205751067

yolov5:(没有划出专门的颈部Neck)

image-20231216212300844

git clone https://github.moeyy.xyz/https://github.com/ultralytics/yolov5.git

image-20231217123703969

  1. nc: 80:这个参数表示模型分类数量(number of classes),默认为 80,对应着 COCO 数据集。
  2. depth_multiple: 0.33:这个参数表示模型深度相对于基础版本的倍数。在 YOLOv5 中,有 S、M、L 和 X 四个版本,其中 S 为基础版本,即 depth_multiple: 1.0,而 M、L 和 X 版本为在此基础上分别加深了一定的层数。而 depth_multiple: 0.33 表示在 S 版本的基础上,深度缩小了 3 倍,即变成了 depth_multiple: 0.33 × 3 = 0.99。
  3. width_multiple: 0.50:这个参数表示模型通道宽度相对于基础版本的倍数。与 depth_multiple 类似,S 版本的 width_multiple 是 1.0,而 M、L 和 X 版本则在此基础上分别扩大了一定的倍数。
  4. anchors:这是一个锚点数组,用于定义不同尺度下的 anchor boxes。YOLOv5 中使用了三个不同的尺度,每个尺度使用三个不同的 anchor boxes。这些锚点大小是相对于输入图像的,因此不同尺度下的大小会有所差别。
  5. backbone:这一部分定义了模型的骨干网络(backbone),包括卷积层、批归一化层和激活函数等。YOLOv5 使用了 CSPDarknet53 这个网络作为基础骨干网络,并在此基础上进行改进。具体而言,YOLOv5 增加了空间注意力机制和SPP模块,以增强特征表达能力。
  6. head:这一部分定义了模型的检测头(detection head),包括检测网络和分类网络。YOLOv5 中的检测网络采用了YOLOv3中的FPN结构,并在此基础上加入了PANet模块和SAM模块,以提高检测性能。

序列数据的不同采样方法(随机采样和顺序分区)会导致隐状态初始化的差异,原因如下:

  1. 随机采样: 在随机采样中,我们从序列数据中随机选择一个序列作为训练样本。这意味着每次训练时,我们都使用不同的序列作为输入。由于每个序列可能具有不同的上下文和语义信息,模型在每次训练时都需要重新适应不同的序列特征。因此,随机采样会导致隐状态的初始化与之前的训练批次存在一定差异。
  2. 顺序分区: 在顺序分区中,我们按顺序依次读取序列数据进行训练。这意味着模型在每个训练批次中都会接收到相邻的序列数据。由于相邻的序列通常具有相似的上下文和语义信息,模型可以利用之前批次的隐藏状态来帮助理解当前批次的序列。因此,顺序分区会导致隐状态的初始化与之前的训练批次存在一定的相关性。

不同的隐状态初始化差异可能会对模型的训练和预测产生影响。随机采样可以增加数据的多样性,帮助模型更好地适应不同的序列特征。然而,随机采样可能也会引入一些噪声,导致训练过程更加不稳定。顺序分区可以利用相邻序列之间的相关性,帮助模型更好地捕捉到序列的上下文信息。然而,顺序分区可能会限制模型对不同序列特征的适应能力。

困惑度(perplexity)是自然语言处理中常用的一个评价指标,主要用于衡量语言模型的预测性能。困惑度越低,表示模型的预测能力越好。

在自然语言处理中,我们通常使用语言模型来计算文本序列的概率。给定一个文本序列 $W=w_1,w_2,…,w_n$,其概率可以表示为:

$$
P(W)=P(w_1)\times P(w_2|w_1) \times … \times P(w_n|w_1,w_2,…,w_{n-1})
$$

其中,$P(w_i|w_1,w_2,…,w_{i-1})$ 表示在已知前面 $i-1$ 个词的情况下,第 $i$ 个词的概率。语言模型的目标就是学习这种条件概率分布。在模型训练过程中,我们通常会使用最大似然估计法来估计模型参数。

困惑度是一个数值指标,表示用当前语言模型对一个测试集进行预测时所得到的困惑程度。具体而言,如果测试集包含 $N$ 个词,我们可以计算出每个词的概率 $P(w_i)$,然后将这些概率求倒数并取对数,即:

$$
\log \frac{1}{P(w_1)}+\log \frac{1}{P(w_2|w_1)}+…+\log \frac{1}{P(w_N|w_1,w_2,…,w_{N-1})}
$$

然后,我们可以将上述结果除以测试集中的词数 $N$,得到平均困惑度。具体而言,平均困惑度的计算公式如下:

$$
\text{Perplexity}=exp\left(-\frac{1}{N}\sum_{i=1}^{N}\log P(w_i)\right)
$$

例如,如果我们有一个包含100个句子的测试集,其中总共包含1000个词,我们可以使用语言模型来预测每个词的概率,并计算出平均困惑度。假设我们的模型预测准确率较高,平均每个词的概率为0.9,则平均困惑度为:

$$
exp\left(-\frac{1}{1000}\sum_{i=1}^{1000}\log 0.9\right) \approx 2.15
$$

这表示我们的模型对测试集中的文本序列进行预测时,每个词的平均困惑度为2.15。如果我们使用一个更好的语言模型,其困惑度可能会更低。

用困惑度来评价模型确保了不同长度的序列具有可比性

路径聚合网络模块

image-20231219133825543

Focus处理模块

image-20231219133907407

空间金字塔池化模块

image-20231219134700073

跨阶段局部网络模块

image-20231219134949252

image-20231219135635494

IoU、GIoU、DIoU、CIoU损失函数

IoU、GIoU、DIoU、CIoU损失函数

目标检测任务的损失函数由Classificition Loss和Bounding Box Regeression Loss两部分构成。目标检测任务中近几年来Bounding Box Regression Loss Function的演进过程,其演进路线是

img

一、IOU(Intersection over Union)

1. 特性(优点)

IoU就是我们所说的交并比,是目标检测中最常用的指标,在anchor-based的方法。作用不仅用来确定正样本和负样本,还可以用来评价输出框(predict box)和ground-truth的距离。

img

\1. 可以说它可以反映预测检测框与真实检测框的检测效果。

\2. 还有一个很好的特性就是尺度不变性,也就是对尺度不敏感(scale invariant), 在regression任务中,判断predict box和gt的距离最直接的指标就是IoU。**(**满足非负性;同一性;对称性;三角不等性)

img

2. 作为损失函数会出现的问题(缺点)

\1. 如果两个框没有相交,根据定义,IoU=0,不能反映两者的距离大小(重合度)。同时因为loss=0,没有梯度回传,无法进行学习训练。

\2. IoU无法精确的反映两者的重合度大小。如下图所示,三种情况IoU都相等,但看得出来他们的重合度是不一样的,左边的图回归的效果最好,右边的最差。

img

二、GIOU(Generalized Intersection over Union)

1****、来源

在CVPR2019中,论文

《Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression》
https:arxiv.org/abs/1902.09630

提出了GIoU的思想。由于IoU是比值的概念,对目标物体的scale是不敏感的。然而检测任务中的BBox的回归损失(MSE loss, l1-smooth loss等)优化和IoU优化不是完全等价的,而且 Ln 范数对物体的scale也比较敏感,IoU无法直接优化没有重叠的部分。

img

这篇论文提出可以直接把IoU设为回归的loss。

img

上面公式的意思是:先计算两个框的最小闭包区域面积_ _(通俗理解:同时包含了预测框和真实框的最小框的面积),再计算出IoU,再计算闭包区域中不属于两个框的区域占闭包区域的比重,最后用IoU减去这个比重得到GIoU。

附:https://github.com/generalized-iou/g-darknet

2****、 特性[1]

与IoU相似,GIoU也是一种距离度量,作为损失函数的话, ,满足损失函数的基本要求

GIoU对scale不敏感

GIoU是IoU的下界,在两个框无线重合的情况下,IoU=GIoU

IoU取值[0,1],但GIoU有对称区间,取值范围[-1,1]。在两者重合的时候取最大值1,在两者无交集且无限远的时候取最小值-1,因此GIoU是一个非常好的距离度量指标。

与IoU只关注重叠区域不同,GIoU****不仅关注重叠区域,还关注其他的非重合区域,能更好的反映两者的重合度。

img

img

三、DIoU(Distance-IoU)[2]

**1,**来源

DIoU要比GIou更加符合目标框回归的机制,将目标与anchor之间的距离,重叠率以及尺度都考虑进去,使得目标框回归变得更加稳定,不会像IoU和GIoU一样出现训练过程中发散等问题。论文中

Distance-IoU
https://arxiv.org/pdf/1911.08287.pdf

基于IoU和GIoU存在的问题,作者提出了两个问题:
\1. 直接最小化anchor框与目标框之间的归一化距离是否可行,以达到更快的收敛速度?
\2. 如何使回归在与目标框有重叠甚至包含时更准确、更快?

img

其中,img分别代表了预测框和真实框的中心点,img且代表的是计算两个中心点间的欧式距离。c代表的是能够同时包含预测框和真实框的最小闭包区域的对角线距离。

img

img

DIoU中对anchor框和目标框之间的归一化距离进行了建模

附:

YOLOV3 DIoU GitHub项目地址
https//github.com/Zzh-tju/DIoU-darknet

2****、优点

与GIoU loss类似,DIoU loss( )在与目标框不重叠时,仍然可以为边界框提供移动方向。

DIoU loss可以直接最小化两个目标框的距离,因此比GIoU loss收敛快得多。

对于包含两个框在水平方向和垂直方向上这种情况,DIoU损失可以使回归非常快,而GIoU损失几乎退化为IoU损失。

DIoU还可以替换普通的IoU评价策略,应用于NMS中,使得NMS得到的结果更加合理和有效。

实现代码:[3]

img

img

四、CIoU(Complete-IoU)

论文考虑到bbox回归三要素中的长宽比还没被考虑到计算中,因此,进一步在DIoU的基础上提出了CIoU。其惩罚项如下面公式:

img

实现代码:[5]

img

img

img

image-20231219142055992

image-20231219142702047

L(IoU)=1-IoU,L(GIoU)=1-GIoU

image-20231219143118491

image-20231219143242889

penalty item 惩罚项

image-20231219143834054

image-20231219143908214

image-20231219144615647

image-20231219144820908

用1替换

image-20231220151820969image-20231220151851064

image-20231220151924519

image-20231220152401000

YOLO训练技巧

1.

image-20231221102150112

2.

image-20231221102343926余弦退火学习率调整的原理是根据余弦函数的形状动态地调整学习率。它通过将学习率从一个较大的初始值逐渐减小到一个较小的最小值来控制训练过程中的学习率变化。

具体实现步骤如下:

  1. 设置一个最大学习率和最小学习率的范围。
  2. 定义一个周期数(通常是训练的总迭代次数)。
  3. 对于每个训练迭代,计算当前周期数与总周期数之间的比例。
  4. 使用余弦函数来动态计算学习率,公式如下:
txt
lr = lr_min + 0.5 * (lr_max - lr_min) * (1 + cos(epoch / T_total * pi))

其中,lr表示当前学习率,epoch表示当前周期数,T_total表示总周期数,lr_max表示最大学习率,lr_min表示最小学习率。

  1. 将计算得到的学习率应用于优化器中进行权重更新。

使用余弦退火学习率调整可以在训练初期使用较大的学习率来快速收敛,然后逐渐减小学习率以细化模型的优化过程。这种方法在训练中期能够跳出局部最优解并找到更好的全局最优解,有助于提高模型的泛化性能和训练效果。

3.

image-20231221103518038

4.

image-20231221103556292

5.遗传算法

image-20231221103654283

6.AMP

image-20231221103725894

image-202312211038364987.

image-20231221104450473

激活函数

image-20231222110059278

image-20231222110124317

image-20231222110150825

image-20231222110400513

image-20231222111314420

image-20231222111341197优点:激活区域可以更多样

image-20231222111510179

因此,引入了各种方法来压缩神经网络,以使大型模型可以在边缘设备上部署。模型压缩方法可以分为3类:剪枝、量化和知识蒸馏。在剪枝中,移除模型中不重要的冗余参数,以获得稀疏/紧凑的模型结构。量化涉及使用低精度数据类型表示模型的激活和权重。最后,知识蒸馏是指利用大型准确模型作为教师来训练一个小型模型,使用教师模型提供的软标签来进行训练。

cmake_minimum_required(VERSION 3.10) project(yolov5) add_definitions(-std=c++11) add_definitions(-DAPI_EXPORTS) option(CUDA_USE_STATIC_CUDA_RUNTIME OFF) set(CMAKE_CXX_STANDARD 11) set(CMAKE_BUILD_TYPE Debug) # TODO(Call for PR): make cmake compatible with Windows set(CMAKE_CUDA_COMPILER /usr/local/cuda-12.2/bin/nvcc) enable_language(CUDA) # include and link dirs of cuda and tensorrt, you need adapt them if yours are different # cuda include_directories(/usr/local/cuda-12.2/include) link_directories(/usr/local/cuda-12.2/lib64) # tensorrt # TODO(Call for PR): make TRT path configurable from command line include_directories(/home/nvidia/TensorRT-8.2.5.1/include/) link_directories(/home/nvidia/TensorRT-8.2.5.1/lib/) include_directories(${PROJECT_SOURCE_DIR}/src/) include_directories(${PROJECT_SOURCE_DIR}/plugin/) file(GLOB_RECURSE SRCS ${PROJECT_SOURCE_DIR}/src/.cpp ${PROJECT_SOURCE_DIR}/src/.cu) file(GLOB_RECURSE PLUGIN_SRCS ${PROJECT_SOURCE_DIR}/plugin/*.cu) add_library(myplugins SHARED ${PLUGIN_SRCS}) target_link_libraries(myplugins nvinfer cudart) find_package(OpenCV) include_directories(${OpenCV_INCLUDE_DIRS}) add_executable(yolov5_det yolov5_det.cpp ${SRCS}) target_link_libraries(yolov5_det nvinfer) target_link_libraries(yolov5_det cudart) target_link_libraries(yolov5_det myplugins) target_link_libraries(yolov5_det ${OpenCV_LIBS}) add_executable(yolov5_cls yolov5_cls.cpp ${SRCS}) target_link_libraries(yolov5_cls nvinfer) target_link_libraries(yolov5_cls cudart) target_link_libraries(yolov5_cls myplugins) target_link_libraries(yolov5_cls ${OpenCV_LIBS}) add_executable(yolov5_seg yolov5_seg.cpp ${SRCS}) target_link_libraries(yolov5_seg nvinfer) target_link_libraries(yolov5_seg cudart) target_link_libraries(yolov5_seg myplugins) target_link_libraries(yolov5_seg ${OpenCV_LIBS}) 这是cmakeLists.txt文件 cmake .. 报错: zgh@zgh-Legion-Y7000P-IAH7:~/tensorrtx/yolov5/build$ cmake .. CMake Error at /usr/local/share/cmake-3.27/Modules/CMakeDetermineCUDACompiler.cmake:277 (message): CMAKE_CUDA_ARCHITECTURES must be non-empty if set. Call Stack (most recent call first): CMakeLists.txt:13 (enable_language) – Configuring incomplete, errors occurred!

2024/1/20 11:33:33

该错误提示是由于没有指定 CMAKE_CUDA_ARCHITECTURES 变量,需要在 CMakeLists.txt 中添加以下语句:

txt

set(CMAKE_CUDA_ARCHITECTURES "60;61;70;75")

其中,CMAKE_CUDA_ARCHITECTURES 指定了支持的 CUDA 架构版本号,这里的值可以根据你的 GPU 型号进行调整。

在添加上述语句后,再次运行 cmake .. 命令即可。

txt
cmake_minimum_required(VERSION 3.5)
project(Ball_Det)
set(CMAKE_CXX_STANDARD 11)
set(Cudart_LIBS "/usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so")
set(CMAKE_CUDA_COMPILER /usr/local/cuda-12.2/bin/nvcc)
set(CMAKE_CUDA_ARCHITECTURES "80")
include_directories(/usr/local/cuda-12.2/include)
link_directories(/usr/local/cuda-12.2/lib64)
enable_language(CUDA)
find_package(CUDA REQUIRED)
find_package(k4a REQUIRED)
find_package(OpenCV REQUIRED)
include_directories(
       Yolo
        ${OpenCV_INCLUDE_DIRS}
       /usr/local/cuda12.2/include
       ${CMAKE_CURRENT_LIST_DIR}
)
add_library(
       Yolo
       Yolo/yolov5.cpp
)

cuda_add_library(myplugins SHARED ./Yolo/yololayer.cu)


add_executable(Ball_Det
       Mask_Detection.cpp
       Yolo/yolov5.cpp
       main.cpp
       Ball_Detection.cpp
       Yolo/calibrator.cpp
)
target_link_libraries(
       Ball_Det
       nvinfer
       cudart
       myplugins
       /usr/local/cuda12.2/lib64
       ${Cudart_LIBS}
       ${OpenCV_LIBS}
       libk4a.so
        )

deeplearning

在 Pandas 中,.apply() 是用于对 DataFrame 或 Series 中的元素应用指定函数的方法。

对于 DataFrame,.apply() 可以在行或列方向上应用函数。语法如下:

txt
DataFrame.apply(func, axis=0)
  • func 是要应用的函数,可以是一个已定义的函数,也可以是一个匿名函数(如 lambda 函数)。
  • axis 是指定应用函数的方向,默认为 0,表示按列应用函数;设置为 1 则表示按行应用函数。

对于 Series,.apply() 仅能在元素级别上应用函数,无需指定应用方向。语法如下:

txt
Series.apply(func)
  • func 是要应用的函数,可以是一个已定义的函数,也可以是一个匿名函数(如 lambda 函数)。

在上述代码中,.apply(lambda x: (x - x.mean()) / (x.std())) 就是将匿名函数 lambda x: (x - x.mean()) / (x.std()) 应用到 DataFrame 或 Series 中的每个元素上。结果是对 DataFrame 或 Series 中的每个元素进行标准化计算,并返回处理后的结果

image-20231218090813900

​ 右–按照时间线展开图

image-20231218141358079

查询(自主提示)和键(非自主提示)之间的交互形成了注意力汇聚;注意力汇聚有选择地聚合了值(感官输入)以生成最终的输出。

torch.repeat_interleave() 函数是 PyTorch 中的一个张量操作函数,用于生成一个重复值的张量。它的详细解释如下:

txt

torch.repeat_interleave(input, repeats, dim=None)

参数说明:

  • input:输入张量。
  • repeats:重复次数,可以是一个整数、一个一维张量或一个与 input 张量形状相匹配的张量。
  • dim(可选):指定重复操作的维度。

函数功能:

  • torch.repeat_interleave() 函数将输入张量 input 按指定的重复次数 repeats 进行重复,并生成一个新的张量。
txt
https://zhuanlan.zhihu.com/p/659067322

sk-QxaDRZHsiyhtTFpB3JXHMkQ5fiK0AnEQZgM27mbiEIaIkF0G

Pytorch

在 Python 中,lambda 是用来创建匿名函数的关键字。所谓匿名函数,即没有显式定义函数名的函数,通常用于需要临时定义简单函数的场景。

lambda 函数的语法如下:

txt
lambda arguments: expression

其中:

  • arguments 是函数的参数,可以有多个参数,用逗号隔开。
  • expression 是函数的返回值计算表达式。

lambda 函数通常用于需要一个函数,但是又不想正式定义一个函数的场景,比如作为其他函数的参数传递进去,或者在一些函数式编程的场景中使用。

params = [W_xh, W_hh, b_h, W_hq, b_q]
for param in params:
param.requires_grad_(True)

目的是告诉 PyTorch 在模型训练过程中需要计算这些参数的梯度,并且在反向传播时对其进行更新。

Xt*Wxh + Ht−1*Whh=cat(Xt,Ht−1)*cat(Wxh,Whh)

torch.matmul(X, W_xh) + torch.matmul(H, W_hh)

torch.matmul(torch.cat((X, H), 1), torch.cat((W_xh, W_hh), 0))

当一个类实现了 __call__ 方法时,它的实例对象可以像函数一样进行调用。这意味着你可以使用实例对象作为函数来调用,就好像调用一个函数一样。

例如,假设有一个类 MyClass,并且实现了 __call__ 方法:

txt
python
class MyClass:
    def __call__(self, x):
        print("Calling MyClass with argument:", x)

现在,你可以创建一个 MyClass 的实例,并将其作为函数进行调用:

txt
python
obj = MyClass()
obj(10)

输出结果将是:

txt

Calling MyClass with argument: 10

所以
class RNNModelScratch: #@save
"""从零开始实现的循环神经网络模型"""
def __init__(self, vocab_size, num_hiddens, device,
get_params, init_state, forward_fn):
self.vocab_size, self.num_hiddens = vocab_size, num_hiddens
self.params = get_params(vocab_size, num_hiddens, device)
self.init_state, self.forward_fn = init_state, forward_fn
def __call__(self, X, state):
X = F.one_hot(X.T, self.vocab_size).type(torch.float32)
return self.forward_fn(X, state, self.params)

可以这样调用:

net = RNNModelScratch(len(vocab), num_hiddens, d2l.try_gpu(), get_params,
init_rnn_state, rnn)

Y, new_state = net(X.to(d2l.try_gpu()), state)

**kwargs 是 Python 中的一种特殊语法,用于接收任意数量的关键字参数(keyword arguments)。在函数或方法的定义中,**kwargs 会将传递给函数的未命名关键字参数收集到一个字典中,其中字典的键是参数名,值是参数值。

在这段代码中,**kwargs 被用作 NWKernelRegression 类的初始化方法 __init__ 的参数。通过使用 **kwargs,可以接收任意数量的关键字参数,并将它们存储为类的属性。这样做可以使代码更加灵活,允许用户在创建 NWKernelRegression 实例时传递额外的参数。

例如,如果你创建了一个 NWKernelRegression 实例时传递了额外的参数,比如 model = NWKernelRegression(param1=10, param2='abc'),那么这些额外的参数会被收集到 kwargs 字典中,字典的键是参数名,值是参数值。你可以根据需要在 __init__ 方法中使用这些参数。

总而言之,**kwargs 允许在函数或方法定义中接收任意数量的关键字参数,并将它们保存为字典以供后续使用。

在 PyTorch 中,self.fc1=nn.Linear(16*5*5,120) 定义了一个全连接层(或称为线性层)。具体来说:

  • 16*5*5 是输入特征的数量,即展平后的张量长度。
  • 120 是全连接层输出的特征数量。

这个全连接层会将形状为 400400 的输入(展平后的张量)转换为形状为 120120 的输出。这个层的作用是通过一个权重矩阵和偏置向量对输入进行线性变换,以得到最终的输出。

是如何展平的?

展平(Flatten)是将多维张量转换为一维向量的过程。以一个形状为 (16 \times 5 \times 5) 的张量为例,这个过程如下:

  1. 输入张量:假设有一个张量,其形状为 (16 \times 5 \times 5),其中 (16) 是通道数,(5) 和 (5) 是每个通道的高和宽。

  2. 展平过程

    • 按照通道、行和列的顺序,将所有元素排列成一个一维向量。
    • 例如,假设张量的元素是 ( \text{A}_{i,j,k} ),展平后你将得到一个长度为 (16 \times 5 \times 5 = 400) 的一维向量。
  3. 展平后的结果

    • 这个一维向量的长度为 (400),即将原张量的所有数据连续地排列在一起。

在实际代码中,比如在 PyTorch 中,展平通常通过 nn.Flatten() 或者在前向传播方法中使用 torch.flatten() 来完成。

轴心时代

在优化算法中,momentum 是一个技术,用于加速收敛并减少震荡。具体来说:

  • momentum: 是一个动量因子,它在更新参数时考虑了之前梯度的影响。它通过引入过去梯度的加权平均来调整当前的梯度更新。
  • 作用: 它帮助优化过程在高曲率方向上加速,在低曲率方向上减缓,从而使优化过程更稳定,减少了震荡和加速收敛。

optim.SGD 中,momentum=0.9 表示将前一步梯度的 90% 加入到当前梯度更新中。

ubuntu20.04可以装ROS 2 humble