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

HRNet,Deep High-Resolution Representation Learning for Visual Recognition解读

论文、代码和ppt地址:HRNet。代码地址: hrnet

本文通过paper解读和代码实例以及onnx模型的分析,来说明hrnet模型。

摘要——高分辨率表征对于诸如人体姿态估计、语义分割和目标检测等对位置敏感的视觉问题至关重要。现有的最先进框架首先通过一个子网(该子网由高低分辨率卷积串联而成,例如ResNet、VGGNet)将输入图像编码为低分辨率表征,然后再从编码后的低分辨率表征中恢复高分辨率表征。与之不同的是,我们所提出的名为高分辨率网络(HRNet)的网络在整个过程中都维持着高分辨率表征。它有两个关键特性:(i)并行连接高低分辨率卷积流;(ii)跨分辨率反复交换信息。这样做的好处是,最终得到的表征在语义上更加丰富,在空间上也更加精确。我们展示了所提出的高分辨率网络(HRNet)在众多应用中的优势,包括人体姿态估计、语义分割和目标检测,这表明HRNet是解决计算机视觉问题的一个更强大的骨干网络。所有代码均可在https://github.com/HRNet获取。

1. INTRODUCTION

深度卷积神经网络(DCNNs)在许多计算机视觉任务中取得了最先进的成果,如图像分类、目标检测、语义分割、人体姿态估计等等。其优势在于,深度卷积神经网络能够学习到比传统手工特征表示更丰富的表征。

大多数近期开发的分类网络,包括AlexNet[77]、VGGNet[126]、GoogleNet[133]、ResNet[54]等等,都遵循LeNet-5[81]的设计规则。该规则如图1(a)所示:逐步减小特征图的空间尺寸,将从高分辨率到低分辨率的卷积依次串联起来,进而得到一个低分辨率表征,该表征会被进一步处理用于分类。

对于位置敏感型任务,例如语义分割、人体姿态估计和目标检测,高分辨率表征是必需的。先前的最先进方法采用高分辨率恢复流程,以便将分类网络或类似分类网络输出的低分辨率表征的分辨率提高,如图1(b)所示,例如Hourglass[105]、SegNet[3]、DeconvNet[107]、U-Net[119]、SimpleBaseline[152]以及编码器 - 解码器[112]。此外,扩张卷积被用于去除一些下采样层,从而生成中等分辨率表征[19]、[181]。

我们提出了一种新颖的架构,即高分辨率网络(HRNet),它能够在整个过程中维持高分辨率表征。我们从一个高分辨率卷积流开始,逐步逐个添加从高到低分辨率的卷积流,并并行连接多分辨率流。最终的网络由几个(本文中为4个)阶段组成,如图2所示,第n个阶段包含对应n个分辨率的n个流。我们通过反复在并行流之间交换信息来进行多次多分辨率融合。

从高分辨率网络(HRNet)学习到的高分辨率表征不仅语义丰富,而且在空间上也很精确。这体现在两个方面。(i)我们的方法并行连接从高到低分辨率的卷积流,而非串联。因此,我们的方法能够维持高分辨率,而不是从低分辨率恢复高分辨率,相应地,学习到的表征在空间上可能更加精确。(ii)大多数现有的融合方案是对上采样低分辨率表征所获得的高分辨率低级表征和高级表征进行聚合。与之不同的是,我们反复进行多分辨率融合,借助低分辨率表征来增强高分辨率表征,反之亦然。结果就是,所有从高到低分辨率的表征在语义上都很强。

我们提出了高分辨率网络(HRNet)的两个版本。第一个版本,名为HRNetV1,仅输出从高分辨率卷积流计算得到的高分辨率表征。我们按照热图估计框架将其应用于人体姿态估计。我们通过实验证明了它在COCO关键点检测数据集[94]上卓越的姿态估计性能。

另一个版本,名为HRNetV2,它合并了来自所有从高到低分辨率并行流的表征。我们通过从合并后的高分辨率表征估计分割图,将其应用于语义分割。所提出的方法在PASCAL-Context、Cityscapes和LIP数据集上,以相近的模型尺寸和更低的计算复杂度取得了最先进的成果。我们发现在COCO姿态估计任务中,HRNetV1和HRNetV2有着相近的性能,而在语义分割方面,HRNetV2优于HRNetV1。

此外,我们从HRNetV2输出的高分辨率表征构建了一个多级表征,名为HRNetV2p,并将其应用于最先进的检测框架,包括Faster R-CNN、Cascade R-CNN[12]、FCOS[136]和CenterNet[36],以及最先进的联合检测和实例分割框架,包括Mask R-CNN[53]、Cascade Mask R-CNN和Hybrid Task Cascade[16]。结果表明,我们的方法提升了检测性能,尤其对小物体的检测性能有显著提升。


图1. 从低分辨率恢复高分辨率的结构。(a)一个低分辨率表征学习子网(例如VGGNet[126]、ResNet[54]),它是通过将高分辨率到低分辨率的卷积依次串联而形成的。(b)一个高分辨率表征恢复子网,它是通过将低分辨率到高分辨率的卷积依次串联而形成的。具有代表性的示例包括SegNet[3]、DeconvNet[107]、U-Net[119]、Hourglass[105]、编码器 - 解码器[112]以及SimpleBaseline[152]。


图2. 高分辨率网络的一个示例。这里仅展示了主体部分,未包含主干部分(两个步长为2的3×3卷积)。
该网络包含四个阶段。第一阶段由高分辨率卷积组成。第二(第三、第四)阶段会重复使用二分辨率(三分辨率、四分辨率)模块。详细内容见第3节。

2. 相关工作

我们从三个方面详细回顾了主要为人体姿态估计[57]、语义分割和目标检测而开发的密切相关的表征学习技术,这三个方面分别是:低分辨率表征学习、高分辨率表征恢复以及高分辨率表征维持。此外,我们还提及了一些与多尺度融合相关的工作。

学习低分辨率表征
全卷积网络方法[99]、[124]通过移除分类网络中的全连接层来计算低分辨率表征,并估计其粗略的分割图。通过结合从中间低层级中等分辨率表征估计出的精细分割得分图[99],或者对相关过程进行迭代[76],可以对估计出的分割图进行改进。类似的技术也已应用于边缘检测,例如整体边缘检测[157]。

全卷积网络通过将少数(通常为两个)步长卷积以及相关卷积替换为扩张卷积,扩展为扩张版本,从而产生中等分辨率表征[18]、[19]、[86]、[168]、[181]。这些表征通过特征金字塔进一步扩充为多尺度上下文表征[19]、[21]、[181],以便对多尺度的目标进行分割。

恢复高分辨率表征
可以使用上采样过程从低分辨率表征逐步恢复高分辨率表征。上采样子网络可以是下采样过程(例如VGGNet)的对称版本,通过在一些镜像层上设置跳跃连接来转换池化索引,例如SegNet[3]和DeconvNet[107];或者是复制特征图,例如U-Net[119]、Hourglass[8]、[9]、[27]、[31]、[68]、[105]、[134]、[163]、[165]、编码器 - 解码器[112]等等。U-Net的一个扩展——全分辨率残差网络[114]引入了一个额外的全分辨率流,该流承载全图像分辨率下的信息,用于替代跳跃连接,并且下采样和上采样子网络中的每个单元都从全分辨率流接收信息并向其发送信息。

非对称上采样过程也得到了广泛研究。RefineNet[90]改进了上采样表征与从下采样过程中复制的相同分辨率表征的组合方式。其他相关工作包括:轻量级上采样过程[7]、[24]、[92]、[152],可能会在骨干网络中使用扩张卷积[63]、[89]、[113];轻量级下采样和重量级上采样过程[141]、重组器网络[55];使用更多或更复杂的卷积单元改进跳跃连接[64]、[111]、[180],以及将信息从低分辨率跳跃连接发送到高分辨率跳跃连接[189]或在它们之间交换信息[49];研究上采样过程的细节[147];组合多尺度金字塔表征[22]、[154];堆叠多个DeconvNets/U-Nets/Hourglass[44]、[149]并带有密集连接[135]。

维持高分辨率表征
我们的工作与一些同样能够生成高分辨率表征的工作密切相关,例如卷积神经结构[123]、互联卷积神经网络[188]、GridNet[42]以及多尺度密集网络[58]。

早期的两项工作,卷积神经结构[123]和互联卷积神经网络[188],在何时开始低分辨率并行流以及如何、在何处跨并行流交换信息方面缺乏精心设计,并且没有使用批量归一化和残差连接,因此未能展现出令人满意的性能。GridNet[42]就像是多个U-Net的组合,它包含两个对称的信息交换阶段:第一阶段仅将信息从高分辨率传递到低分辨率,第二阶段仅将信息从低分辨率传递到高分辨率。这限制了它的分割质量。多尺度密集网络[58]由于无法从低分辨率表征接收信息,所以无法学习到强大的高分辨率表征。

多尺度融合
多尺度融合受到了广泛研究[11]、[19]、[24]、[42]、[58]、[66]、[122]、[123]、[157]、[161]、[181]、[188]。一种直接的方法是将多分辨率图像分别输入到多个网络中,然后聚合输出的响应图[137]。Hourglass[105]、U-Net[119]和SegNet[3]通过跳跃连接,在从高到低的下采样过程中将低级特征逐步合并到从低到高的上采样过程中的相同分辨率高级特征中。PSPNet[181]和DeepLabV2/3[19]融合了由金字塔池化模块和空洞空间金字塔池化所获得的金字塔特征。我们的多尺度(分辨率)融合模块与这两个池化模块类似。不同之处在于:(1)我们的融合输出的是四种分辨率的表征,而不只是一种;(2)我们的融合模块受深度融合[129]、[143]、[155]、[178]、[184]的启发会重复多次。

我们的方法
我们的网络并行连接从高到低的卷积流。它在整个过程中维持高分辨率表征,并通过反复融合来自多分辨率流的表征,生成具有强位置敏感性的可靠高分辨率表征。

本文是对我们之前会议论文[130]的一次重大扩展,补充了我们未发表的技术报告[131]中的额外内容,以及在近期开发的最先进的目标检测和实例分割框架下更多的目标检测结果。与[130]相比,主要的技术创新体现在三个方面。(1)我们将[130]中提出的网络(名为HRNetV1)扩展为两个版本:HRNetV2和HRNetV2p,这两个版本探索了全部四种分辨率表征。(2)我们建立了多尺度融合与常规卷积之间的联系,这为在HRNetV2和HRNetV2p中探索全部四种分辨率表征的必要性提供了依据。(3)我们展示了HRNetV2和HRNetV2p相对于HRNetV1的优势,并呈现了HRNetV2和HRNetV2p在包括语义分割和目标检测在内的众多视觉问题中的应用。

3. HIGH-RESOLUTION Networks

我们将图像输入到一个主干部分,该主干部分由两个步长为2的3×3卷积组成,它会将图像分辨率降低至原来的1/4,随后图像会进入主体部分,主体部分输出的表征具有相同的分辨率(即1/4)。主体部分(如图2所示,详细内容如下所述)由几个组件构成:并行多分辨率卷积、重复多分辨率融合以及如图4所示的表征头。


图4. (a)HRNetV1:仅输出来自高分辨率卷积流的表征。(b)HRNetV2:将来自所有分辨率的(上采样后的)表征进行拼接(为清晰起见,后续的1×1卷积未展示)。(c)HRNetV2p:利用HRNetV2输出的表征构建一个特征金字塔。每个子图底部的四种分辨率表征是由图2所示网络输出的,灰色框展示了如何从输入的四种分辨率表征中获取输出表征。

3.1. Parallel Multi-Resolution Convolutions

我们从作为第一阶段的高分辨率卷积流开始,逐步逐个添加从高到低分辨率的流,从而形成新的阶段,并并行连接多分辨率流。这样一来,后续阶段并行流的分辨率就包含了前一阶段的分辨率,以及一个额外更低的分辨率。

图2所示的示例网络结构包含4个并行流,其逻辑如下,

其中N_sr表示第s阶段中的子流,r为分辨率索引。第一个流的分辨率索引r = 1。索引r对应的分辨率是第一个流分辨率的1 / (2^{r-1})

3.2. Repeated Multi-Resolution Fusions

融合模块的目标是在多分辨率表征之间交换信息。它会被重复多次(例如,每4个残差单元重复一次)。

让我们来看一个融合3种分辨率表征的示例,如图3所示。融合2种以及4种表征的情况可以很容易由此推导出来。输入包含三种表征:
,其中r为分辨率索引,与之相关的输出表征为。每个输出表征都是对三个输入表征进行变换后相加的结果,即。跨阶段(从第3阶段到第4阶段)的融合会有一个额外输出:

变换函数的选择取决于输入分辨率索引x和输出分辨率索引r。如果x = r,那么。如果x < r,会通过(r - s)个步长为2的3×3卷积对输入表征R进行下采样。例如,进行2倍下采样时使用一个步长为2的3×3卷积,进行4倍下采样时使用两个连续的步长为2的3×3卷积。如果x > r,会先通过双线性上采样对输入表征R进行上采样,然后使用一个1×1卷积来对齐通道数量。这些函数如图3所示。


图3. 展示融合模块如何分别从左至右聚合高、中、低分辨率的信息。右侧图例:步长为2的3×3 = 步长为2的3×3卷积,上采样1×1 = 双线性上采样后接一个1×1卷积。

3.3 表征头 Representation Head

我们有三种表征头,如图4所示,分别将它们称作HRNetV1、HRNetV2和HRNetV2p。

HRNetV1:输出仅来自高分辨率流的表征,其他三种表征会被忽略。这在图4(a)中有展示。

HRNetV2:我们通过双线性上采样将低分辨率表征重新缩放到高分辨率(通道数量保持不变),然后将这四种表征进行拼接,接着使用一个1×1卷积来混合这四种表征。这在图4(b)中有展示。

HRNetV2p:我们通过将HRNetV2输出的高分辨率表征下采样到多个层级来构建多级表征。这在图4(c)中有展示。

在本文中,我们将展示把HRNetV1应用于人体姿态估计、把HRNetV2应用于语义分割以及把HRNetV2p应用于目标检测的相关结果。

3.4 Instantiation 具体描述

主体部分包含四个阶段,每个阶段有四个并行的卷积流。其分辨率分别为1/4、1/8、1/16和1/32。

第一阶段包含4个残差单元,每个单元由一个宽度为64的瓶颈结构组成,随后紧跟一个3×3卷积,该卷积会将特征图的宽度变为C。第二、第三、第四阶段分别包含1个、4个、3个模块化块。模块化块的多分辨率并行卷积中的每个分支都包含4个残差单元。每个单元针对每个分辨率都包含两个3×3卷积,且每个卷积之后都会进行批量归一化以及非线性激活ReLU操作。这四种分辨率的卷积的宽度(通道数量)分别为C、2C、4C和8C。示例见图2。

3.5 分析

我们对模块化块进行分析,它可分为两个组件:多分辨率并行卷积(图5(a))以及多分辨率融合(图5(b))。

多分辨率并行卷积与分组卷积类似。它将输入通道划分为若干个通道子集,并针对每个子集在不同的空间分辨率下分别执行常规卷积,而在分组卷积中,分辨率是相同的。这种关联意味着多分辨率并行卷积具备分组卷积的一些优势。

多分辨率融合单元与常规卷积的多分支全连接形式相似,如图5(c)所示。正如[178]中所解释的那样,一个常规卷积可以拆分为多个小卷积。输入通道被划分为若干个子集,输出通道同样也被划分为若干个子集。输入和输出子集以全连接的方式相连,并且每条连接都是常规卷积。输出通道的每个子集是对输入通道每个子集上卷积输出结果的求和。不同之处在于,我们的多分辨率融合需要处理分辨率的变化。多分辨率融合与常规卷积之间的联系为在HRNetV2和HRNetV2p中探索全部四种分辨率表征提供了依据。


图5. (a)多分辨率并行卷积,(b)多分辨率融合。(c)一个常规卷积(左图)等同于全连接多分支卷积(右图)。

4. HUMAN POSE ESTIMATION

人体姿态估计,又称关键点检测,旨在从尺寸为宽(W)×高(H)×3的图像I中检测出K个关键点或身体部位(例如肘部、腕部等)的位置。我们遵循最先进的框架,将这个问题转化为估计尺寸为W/4×H/4的K个热图{H1, H2, …, HK},其中每个热图Hk表示第k个关键点的位置置信度。

我们基于HRNetV1输出的高分辨率表征来回归热图。我们通过实验观察到,HRNetV1和HRNetV2的性能几乎相同,因此我们选择HRNetV1,因为它的计算复杂度稍低一些。损失函数定义为均方误差,用于比较预测热图和真实热图。真实热图是通过以每个关键点的真实位置为中心、标准差为2像素的二维高斯函数生成的。一些示例结果见图6。

数据集:COCO数据集[94]包含超过20万张图像以及25万个标注了17个关键点的人物实例。我们在COCO的train2017集(包含5.7万张图像和15万个人物实例)上训练我们的模型,并在val2017集(包含5000张图像)和test-dev2017集(包含2万张图像)上评估我们的方法。

评估指标:标准评估指标基于目标关键点相似度(OKS):OKS = ∑i exp(−d²i / 2s²k²i )δ(vi > 0) / ∑i δ(vi > 0)。这里di是检测到的关键点与对应的真实位置之间的欧几里得距离,vi是真实位置的可见性标志,s是目标尺度,ki是控制衰减的每个关键点的常数。我们报告标准平均精度和召回率得分:AP50(OKS = 0.50时的平均精度)、AP75、AP(在10个OKS取值位置(0.50、0.55、…、0.90、0.95)上的平均精度得分的平均值);APM(针对中等尺寸目标)、APL(针对大型目标)以及AR(在10个OKS取值位置(0.50、0.55、…、0.90、0.95)上的平均召回率得分的平均值)。

训练:我们将人体检测框在高度或宽度方向上扩展到固定的宽高比(高度 : 宽度 = 4 : 3),然后从图像中裁剪出该检测框,并将其调整为固定尺寸(256×192或384×288)。数据增强方案包括随机旋转(范围在[−45°,45°])、随机缩放(范围在[0.65,1.35])以及翻转。依照[146],还涉及半身数据增强。

我们使用Adam优化器[71]。学习计划遵循[152]中的设置。基础学习率设置为1e−3,在第170个和第200个训练周期时分别降至1e−4和1e−5。训练过程在210个周期内结束。模型在4块V100 GPU上进行训练,HRNet-W32(HRNet-W48)的训练耗时大约为60(80)小时。

测试:采用与[24]、[109]、[152]类似的两阶段自上而下的范式:先用人物检测器检测人物实例,然后预测检测关键点。

我们对val集和test-dev集都使用SimpleBaseline³提供的相同人物检测器。依照[24]、[105]、[152],我们通过对原始图像和翻转图像的热图取平均值来计算热图。每个关键点位置通过在从最高响应到次高响应的方向上以四分之一偏移量来调整最高热值位置进行预测。

val集上的结果:我们在表1中报告了我们的方法以及其他最先进方法的结果。网络——HRNetV1-W32,以256×192的输入尺寸从头开始训练,获得了73.4的平均精度(AP)得分,优于具有相同输入尺寸的其他方法。(i)与Hourglass [105]相比,我们的网络将平均精度提高了6.5个百分点,并且我们网络的浮点运算次数(GFLOP)低得多,还不到其一半,而参数数量相似,我们的略多一点。(ii)与无OHKM和有OHKM的CPN [24]相比,我们的网络(模型尺寸稍大且复杂度稍高一点)分别获得了4.8和4.0个百分点的提升。(iii)与之前性能最佳的方法SimpleBaseline [152]相比,我们的HRNetV1-W32取得了显著改进:对于具有相似模型尺寸和浮点运算次数的ResNet-50骨干网络,提升了3.0个百分点;对于模型尺寸(参数数量)和浮点运算次数是我们两倍的ResNet-152骨干网络,提升了1.4个百分点。

我们的网络可以受益于:(i)使用在ImageNet上预训练的模型进行训练:对于HRNetV1-W32,提升了1.0个百分点;(ii)通过增加宽度来提升容量:HRNetV1-W48在输入尺寸为256×192和384×288时分别获得了0.7和0.5个百分点的提升。

考虑输入尺寸为384×288的情况,我们的HRNetV1-W32和HRNetV1-W48分别获得了75.8和76.3的平均精度,相较于输入尺寸为256×192时,分别有1.4和1.2的提升。与以ResNet-152作为骨干网络的SimpleBaseline [152]相比,我们的HRNetV1-W32和HRNetV1-W48在平均精度方面分别以45%和92.4%的计算成本获得了1.5和2.0个百分点的提升。

test-dev集上的结果:表2报告了我们的方法以及现有最先进方法的姿态估计性能。我们的方法明显优于自下而上的方法。另一方面,我们的小型网络HRNetV1-W32获得了74.9的平均精度,它优于所有其他自上而下的方法,并且在模型尺寸(参数数量)和计算复杂度(浮点运算次数)方面更高效。我们的大型模型HRNetV1-W48获得了最高的平均精度得分75.5。与具有相同输入尺寸的SimpleBaseline [152]相比,我们的小型和大型网络分别获得了1.2和1.8个百分点的提升。通过使用来自AI Challenger [148]的额外数据进行训练,我们的单个大型网络可以获得77.0的平均精度。


表1,在COCO验证集(val)上的对比情况。在输入尺寸为256×192的情况下,我们采用小型模型HRNetV1-W32且从头开始训练的方法,其性能优于之前的最先进方法。在输入尺寸为384×288时,我们使用小型模型HRNetV1-W32的方法所取得的平均精度(AP)得分高于采用大型模型的SimpleBaseline方法。尤其需要指出的是,我们的方法在平均精度75(AP75,一种严格的评估方案)方面的提升比平均精度50(AP50,一种宽松的评估方案)方面的提升更为显著。“Pretrain”表示在ImageNet上对骨干网络进行预训练。“OHKM”表示在线困难关键点挖掘[24]。参数数量(#Params)和浮点运算次数(FLOPs)是针对姿态估计网络进行计算的,而人体检测和关键点分组所涉及的相关运算量并未包含在内。

8 总结

在本文中,我们提出了一种用于视觉识别问题的高分辨率网络。它与现有的低分辨率分类网络和高分辨率表征学习网络存在三个根本差异:(i)并行连接高分辨率和低分辨率卷积,而非串联;(ii)在整个过程中维持高分辨率,而非从低分辨率恢复高分辨率;(iii)反复融合多分辨率表征,从而生成具有强位置敏感性的丰富的高分辨率表征。

在众多视觉识别问题上取得的优异结果表明,我们所提出的高分辨率网络(HRNet)是解决计算机视觉问题的一种更强大的骨干网络。我们的研究也鼓励人们投入更多的研究精力去直接针对特定视觉问题设计网络架构,而不是对从低分辨率网络(例如ResNet或VGGNet)中学到的表征进行扩展、修正或修复。

讨论:可能存在一种误解:认为由于分辨率更高,高分辨率网络(HRNet)的内存开销更大。实际上,在人体姿态估计、语义分割和目标检测这三种应用中,高分辨率网络(HRNet)的内存开销与最先进的方法相当,只是在目标检测中的训练内存开销略大一些。

此外,我们总结了在PyTorch 1.0平台上运行时开销的对比情况。高分辨率网络(HRNet)的训练和推理时间开销与之前的最先进方法相当,除了以下两点:(1)高分辨率网络(HRNet)用于分割任务的推理时间要少得多;(2)高分辨率网络(HRNet)用于姿态估计的训练时间略长一些,不过在支持静态图推理的MXNet 1.5.1平台上,其开销与SimpleBaseline相似。我们想要强调的是,在语义分割方面,其推理开销明显小于PSPNet和DeepLabv3。表13总结了内存和时间开销的对比情况[5]。

未来及后续工作:我们将研究把高分辨率网络(HRNet)与其他技术相结合,用于语义分割和实例分割。目前,通过将高分辨率网络(HRNet)与目标上下文表征(OCR)方案[170][6](目标上下文[59][171]的一种变体)相结合,我们已经取得了一些结果(平均交并比,即mIoU),这些结果在表3、4、5、6中有展示。我们将通过进一步提高表征的分辨率(例如提高到1/2甚至全分辨率)来开展相关研究。

高分辨率网络(HRNet)的应用并不局限于我们已经开展的上述应用,它适用于其他对位置敏感的视觉应用,例如面部关键点检测[7]、超分辨率、光流估计、深度估计等等。目前已经有了一些后续工作,例如图像风格化[83]、图像修复[50]、图像增强[62]、图像去雾[1]、时序姿态估计[6]以及无人机目标检测[190]。

据[26]报道,一个经过轻微修改的高分辨率网络(HRNet)与空洞空间金字塔池化(ASPP)相结合,在单模型情况下实现了Mapillary全景分割的最佳性能。在2019年国际计算机视觉大会(ICCV)的COCO + Mapillary联合识别挑战赛研讨会中,COCO密集姿态挑战赛的获胜者以及几乎所有COCO关键点检测挑战赛的参与者都采用了高分辨率网络(HRNet)。OpenImage实例分割挑战赛(ICCV 2019)的获胜者也使用了高分辨率网络(HRNet)。


表13,在PyTorch 1.0平台上,针对人体姿态估计、语义分割以及目标检测(在Faster R-CNN框架下),从训练/推理内存以及训练/推理时间方面进行内存和时间开销的对比。我们还报告了在MXNet 1.5.1平台上人体姿态估计的推理时间(括号内所示),MXNet 1.5.1支持静态图推理,高分辨率网络(HRNet)中使用的多分支卷积可从中受益。 训练相关的数据是在一台配备4块V100 GPU显卡的机器上获取的。在训练期间,人体姿态估计、语义分割和目标检测的输入尺寸分别为256×192、512×1024和800×1333,批量大小分别为128、8和8。推理相关的数据是在单块V100 GPU显卡上获取的,输入尺寸分别为256×192、1024×2048和800×1333。得分方面,对于人体姿态估计指的是在COCO验证集(表1)上的平均精度(AP),对于目标检测指的是在COCO验证集(表8)上的平均精度,对于城市景观(Cityscapes)语义分割指的是平均交并比(mIoU,表3)。有几点值得强调的观察结果如下: 内存方面:高分辨率网络(HRNet)在训练和推理时消耗的内存与其他方法相近,不过在人体姿态估计的训练中,它消耗的内存更少。 时间方面:高分辨率网络(HRNet)的训练和推理时间开销与之前的最先进方法相当,不过它用于语义分割任务时的推理时间要少得多。 SB-ResNet-152表示以ResNet - 152作为骨干网络的SimpleBaseline。PSPNet和DeepLabV3使用扩张的ResNet - 101作为骨干网络(表3)。

9 代码和onnx模型

从图2可以看到整个hrnet模型的结构。其结构的主要特点是由4个阶段组成,第234阶段会逐渐多一个1/2, 1/4, 1/8分辨率的分支。

我们结合代码和onnx看一下。

class HRNet(nn.Module):def __init__(self, c=48, nof_joints=17, bn_momentum=0.1):super(HRNet, self).__init__()# Input (stem net)self.conv1 = nn.Conv2d(3, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)self.bn1 = nn.BatchNorm2d(64, eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True)self.conv2 = nn.Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)self.bn2 = nn.BatchNorm2d(64, eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True)self.relu = nn.ReLU(inplace=True)# Stage 1 (layer1)      - First group of bottleneck (resnet) modulesdownsample = nn.Sequential(nn.Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False),nn.BatchNorm2d(256, eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),)self.layer1 = nn.Sequential(Bottleneck(64, 64, downsample=downsample),Bottleneck(256, 64),Bottleneck(256, 64),Bottleneck(256, 64),)# Fusion layer 1 (transition1)      - Creation of the first two branches (one full and one half resolution)self.transition1 = nn.ModuleList([nn.Sequential(nn.Conv2d(256, c, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False),nn.BatchNorm2d(c, eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),nn.ReLU(inplace=True),),nn.Sequential(nn.Sequential(  # Double Sequential to fit with official pretrained weightsnn.Conv2d(256, c * (2 ** 1), kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False),nn.BatchNorm2d(c * (2 ** 1), eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),nn.ReLU(inplace=True),)),])# Stage 2 (stage2)      - Second module with 1 group of bottleneck (resnet) modules. This has 2 branchesself.stage2 = nn.Sequential(StageModule(stage=2, output_branches=2, c=c, bn_momentum=bn_momentum),)# Fusion layer 2 (transition2)      - Creation of the third branch (1/4 resolution)self.transition2 = nn.ModuleList([nn.Sequential(),  # None,   - Used in place of "None" because it is callablenn.Sequential(),  # None,   - Used in place of "None" because it is callablenn.Sequential(nn.Sequential(  # Double Sequential to fit with official pretrained weightsnn.Conv2d(c * (2 ** 1), c * (2 ** 2), kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False),nn.BatchNorm2d(c * (2 ** 2), eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),nn.ReLU(inplace=True),)),  # ToDo Why the new branch derives from the "upper" branch only?])# Stage 3 (stage3)      - Third module with 4 groups of bottleneck (resnet) modules. This has 3 branchesself.stage3 = nn.Sequential(StageModule(stage=3, output_branches=3, c=c, bn_momentum=bn_momentum),StageModule(stage=3, output_branches=3, c=c, bn_momentum=bn_momentum),StageModule(stage=3, output_branches=3, c=c, bn_momentum=bn_momentum),StageModule(stage=3, output_branches=3, c=c, bn_momentum=bn_momentum),)# Fusion layer 3 (transition3)      - Creation of the fourth branch (1/8 resolution)self.transition3 = nn.ModuleList([nn.Sequential(),  # None,   - Used in place of "None" because it is callablenn.Sequential(),  # None,   - Used in place of "None" because it is callablenn.Sequential(),  # None,   - Used in place of "None" because it is callablenn.Sequential(nn.Sequential(  # Double Sequential to fit with official pretrained weightsnn.Conv2d(c * (2 ** 2), c * (2 ** 3), kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False),nn.BatchNorm2d(c * (2 ** 3), eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),nn.ReLU(inplace=True),)),  # ToDo Why the new branch derives from the "upper" branch only?])# Stage 4 (stage4)      - Fourth module with 3 groups of bottleneck (resnet) modules. This has 4 branchesself.stage4 = nn.Sequential(StageModule(stage=4, output_branches=4, c=c, bn_momentum=bn_momentum),StageModule(stage=4, output_branches=4, c=c, bn_momentum=bn_momentum),StageModule(stage=4, output_branches=1, c=c, bn_momentum=bn_momentum),)# Final layer (final_layer)self.final_layer = nn.Conv2d(c, nof_joints, kernel_size=(1, 1), stride=(1, 1))def forward(self, x):x = self.conv1(x)x = self.bn1(x)x = self.relu(x)x = self.conv2(x)x = self.bn2(x)x = self.relu(x)x = self.layer1(x)x = [trans(x) for trans in self.transition1]  # Since now, x is a list (# == nof branches)x = self.stage2(x)# x = [trans(x[-1]) for trans in self.transition2]    # New branch derives from the "upper" branch onlyx = [self.transition2[0](x[0]),self.transition2[1](x[1]),self.transition2[2](x[-1])]  # New branch derives from the "upper" branch onlyx = self.stage3(x)# x = [trans(x) for trans in self.transition3]    # New branch derives from the "upper" branch onlyx = [self.transition3[0](x[0]),self.transition3[1](x[1]),self.transition3[2](x[2]),self.transition3[3](x[-1])]  # New branch derives from the "upper" branch onlyx = self.stage4(x)x = self.final_layer(x[0])return x

9.1 首先是bottleneck部分,也就是图2的网络结构中的第1阶段。

        # Stage 1 (layer1)      - First group of bottleneck (resnet) modulesdownsample = nn.Sequential(nn.Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False),nn.BatchNorm2d(256, eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),)self.layer1 = nn.Sequential(Bottleneck(64, 64, downsample=downsample),Bottleneck(256, 64),Bottleneck(256, 64),Bottleneck(256, 64),)

从onnx上,这部分网络是resnet结构,在传递过程中,保证了特征图分辨率的一致,与图2的网络结构图示相同。

9.2 全分辨率和1/2分辨率阶段,即图2的第2阶段

        # Fusion layer 1 (transition1)      - Creation of the first two branches (one full and one half resolution)self.transition1 = nn.ModuleList([nn.Sequential(nn.Conv2d(256, c, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False),nn.BatchNorm2d(c, eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),nn.ReLU(inplace=True),),nn.Sequential(nn.Sequential(  # Double Sequential to fit with official pretrained weightsnn.Conv2d(256, c * (2 ** 1), kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False),nn.BatchNorm2d(c * (2 ** 1), eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),nn.ReLU(inplace=True),)),])

从onnx看,这部分是两个分支,一个是全分辨率分支,一个是1/2分辨率分支。两个分支分别在前向传递过程中,保持了分辨率的一致。

9.3 全分辨率、1/2分辨率和1/4分辨率阶段,即图2的第3阶段

  # Fusion layer 2 (transition2)      - Creation of the third branch (1/4 resolution)self.transition2 = nn.ModuleList([nn.Sequential(),  # None,   - Used in place of "None" because it is callablenn.Sequential(),  # None,   - Used in place of "None" because it is callablenn.Sequential(nn.Sequential(  # Double Sequential to fit with official pretrained weightsnn.Conv2d(c * (2 ** 1), c * (2 ** 2), kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False),nn.BatchNorm2d(c * (2 ** 2), eps=1e-05, momentum=bn_momentum, affine=True, track_running_stats=True),nn.ReLU(inplace=True),)),  # ToDo Why the new branch derives from the "upper" branch only?])

从onnx看,一共三个分支,分别是全分辨率、1/2、1/4分辨率。

但是注意在由第2阶段的双分支转为第3阶段的三分支时,全分辨率分支和1/2分辨率分支,全部通过插值或者降采样的方式,参与了新的3个分支的计算,如下面的onnx模型结构所示。

全分辨率、1/2、1/4、1/8分辨率,4个分支同理。

相关文章:

HRNet,Deep High-Resolution Representation Learning for Visual Recognition解读

论文、代码和ppt地址&#xff1a;HRNet。代码地址: hrnet 本文通过paper解读和代码实例以及onnx模型的分析&#xff0c;来说明hrnet模型。 摘要——高分辨率表征对于诸如人体姿态估计、语义分割和目标检测等对位置敏感的视觉问题至关重要。现有的最先进框架首先通过一个子网&…...

Rust Actix Web 项目实战教程 mysql redis swagger:构建用户管理系统

Rust Actix Web 项目实战教程&#xff1a;构建用户管理系统 项目概述 本教程将指导你使用 Rust 和 Actix Web 构建一个完整的用户管理系统&#xff0c;包括数据库交互、Redis 缓存和 Swagger UI 文档。 技术栈 Rust 编程语言Actix Web 框架SQLx (MySQL 数据库)Redis 缓存Uto…...

# 爬楼梯问题:常见数列的解法总结

爬楼梯问题&#xff1a;常见数列的解法总结 在编程中&#xff0c;爬楼梯问题&#xff08;Climbing Stairs Problem&#xff09;是一个经典的动态规划问题&#xff0c;常常作为入门学习动态规划和递推的重要例题。这个问题看似简单&#xff0c;但背后包含了多种解决方式&#x…...

速通Docker === 常用命令

目录 Docker命令 镜像操作 容器操作 基础操作 启动参数 容器内部操作 打包成指定文件 发布镜像 总结 镜像操作 容器操作 启动容器参数 容器内部操作 打包镜像 启动指定镜像的容器 发布镜像 Docker命令 启动一个nginx,并将它的首页改为自己的页面&#xff0c;发布…...

AWS S3 跨账户访问 Cross Account Access

进入S3对应的存储桶&#xff0c;上面选项选权限&#xff0c;存储桶策略 -- 编辑&#xff0c;输入对应的policy。 完全控制&#xff0c;包含上传删除权限&#xff0c;policy如下&#xff1a; {"Version": "2012-10-17","Statement": [{"Si…...

C#中常见的锁以及用法--18

目录 一.C#中存在的锁 二.锁的作用 三.锁的概念和定义 关于锁的完整代码示例 代码逐层剖析: 全局变量与同步变量 Lock(锁)关键字示例 Monitor(监视器锁)示例 Mutex(互斥量)示例(支持跨进程同步) SemaphoreSlim(信号量)示例 ReadWriterLockSlim(读写锁)示例 SpinLock…...

【数据分享】1929-2024年全球站点的逐年平均气温数据(Shp\Excel\无需转发)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01;本次我们为大家带来的就是具体到气象监…...

Docker部署MySQL 5.7:持久化数据的实战技巧

在生产环境中使用Docker启动MySQL 5.7时&#xff0c;需要考虑数据持久化、配置文件管理、安全性等多个方面。以下是一个详细的步骤指南。 1. 准备工作 &#xff08;1&#xff09;创建挂载目录 在宿主机上创建用于挂载的目录&#xff0c;以便持久化数据和配置文件。 sudo mkdi…...

二叉树和堆

树概念及结构&#xff08;了解&#xff09; 树的概念&#xff08;看看就行&#xff09; 树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的树&#xff0c;也就是…...

Zookeeper(15)Zookeeper的ZooKeeper API包含哪些主要操作?

Zookeeper 的 ZooKeeper API 提供了一系列操作来管理 Zookeeper 的数据节点&#xff08;znodes&#xff09;。这些操作主要包括创建节点、删除节点、读取节点数据、设置节点数据、列出子节点、检查节点是否存在&#xff0c;以及注册 Watcher 等。以下是这些操作的详细介绍和代码…...

深入浅出:Go语言os包中的API使用指南

深入浅出:Go语言os包中的API使用指南 引言 Go语言以其简洁、高效和强大的生态系统著称,是现代编程中不可或缺的一部分。其中,os包作为Go标准库的一部分,提供了丰富的API来与操作系统进行交互。本文将深入探讨os包中的核心功能,并通过实际案例帮助读者更好地理解和应用这些…...

【云岚到家】-day02-客户管理-认证授权

第二章 客户管理 1.认证模块 1.1 需求分析 1.基础概念 一般情况有用户交互的项目都有认证授权功能&#xff0c;首先我们要搞清楚两个概念&#xff1a;认证和授权 认证: 就是校验用户的身份是否合法&#xff0c;常见的认证方式有账号密码登录、手机验证码登录等 授权:则是该用…...

vben5 admin ant design vue如何使用时间范围组件RangePicker

本文参考&#xff1a;https://pusdn-dev.feishu.cn/wiki/VF4hwBAUliTE6TkUPKrcBNcZn9f?fromfrom_copylink 由PUSDN整理发行&#xff0c;收录时请保留PUSDN。 前端组件专题 年月日时间范围表单回显RangePicker 推荐使用多个字段存储&#xff0c;不推荐用英文逗号拼接时间&am…...

安全策略配置实验

安全策略配置实验 1.拓扑 2.需求 2、办公区PC在工作日时间(周一至周五&#xff0c;早8到晚6)可以正常访问OA srver&#xff0c;其他时间不允许 3、办公区PC可以在任意时刻访问web server 4、生产区PC可以在任意时刻访问OA Server&#xff0c;但是不能访问Web server 5、特…...

Win10安装WebODM和操作全流程

效果 以下是在 Windows 10 上安装和部署 WebODM 的详细教程: 一、安装 Docker Desktop for Windows 1、访问 Docker 官方网站:https://www.docker.com/products/docker-desktop 。 2、下载 Docker Desktop for Windows 的安装程序。 3、运行安装程序: 双击下载的安装程序,…...

wireshark抓路由器上的包 抓包路由器数据

文字目录 抓包流程概述设置抓包配置选项 设置信道设置无线数据包加密信息设置MAC地址过滤器 抓取联网过程 抓包流程概述 使用Omnipeek软件分析网络数据包的流程大概可以分为以下几个步骤&#xff1a; 扫描路由器信息&#xff0c;确定抓包信道&#xff1b;设置连接路由器的…...

第8章:Python TDD处理货币类代码重复问题

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…...

C#,入门教程(01)—— Visual Studio 2022 免费安装的详细图文与动画教程

通过本课程的学习&#xff0c;你可以掌握C#编程的重点&#xff0c;享受编程的乐趣。 在本课程之前&#xff0c;你无需具备任何C#的基础知识&#xff0c;只要能操作电脑即可。 不过&#xff0c;希望你的数学不是体育老师教的。好的程序是数理化的实现与模拟。没有较好的数学基础…...

Agent Laboratory: Using LLM Agents as Research Assistants 论文简介

加速机器学习研究的智能实验室——Agent Laboratory 1. 引言 随着人工智能技术的飞速发展&#xff0c;机器学习领域正以前所未有的速度推进科学发现和技术创新。然而&#xff0c;传统的科学研究模式往往受到时间、资源和专业知识限制&#xff0c;阻碍了研究者们探索新想法的能…...

cuda + cudnn安装

1.安装CUDA Toolkit 在设备管理器&#xff08;此电脑–右键–属性&#xff09;的显示适配器中可以查看自己的显卡型号&#xff0c;去下载对应的CUDA Toolkit 。或者输入以下命令查看Driver Version &#xff0c;cuda Version&#xff1a;12.2代表12.2版本以下兼容可以进行安装 …...

Next.js 实战 (八):使用 Lodash 打包构建产生的“坑”?

前言 最近一直在折腾 Nextjs15 &#xff0c;也在断断续续地写《Next.js15 实战系列》的文章&#xff0c;后来总感觉文章如果没有线上效果预览差点意思&#xff0c;所以就想着先把目前做的项目先部署上线&#xff0c;后续再慢慢添加新功能。 因为之前没有部署过 Nextjs15 工程…...

owasp SQL 注入-03 (原理)

1: 先看一下注入界面: 点submit 后&#xff0c;可以看到有语法报错&#xff0c;说明已经起作用了: 报如下的错误: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near at line 1 2:…...

wireshark工具简介

目录 1 wireshark介绍 2 wireshark抓包流程 2.1 选择网卡 2.2 停止抓包 2.3 保存数据 3 wireshark过滤器设置 3.1 显示过滤器的设置 3.2 抓包过滤器 4 wireshark的封包列表与封包详情 4.1 封包列表 4.2 封包详情 参考文献 1 wireshark介绍 wireshark是非常流行的网络…...

队列的基本用法

以下是关于 C 语言中队列的详细知识&#xff0c;包括队列的生成、相关函数使用以及其他重要概念&#xff1a; 一、队列的概念 队列是一种线性数据结构&#xff0c;它遵循先进先出&#xff08;First In First Out&#xff0c;FIFO&#xff09;的原则&#xff0c;就像日常生活中…...

OpenHarmony-7.IDL工具

IDL 工具 1.openharmony IDL工具 在OpenHarmony中&#xff0c;当应用/系统服务的客户端和服务端进行IPC&#xff08;Inter-Process Communication&#xff09;跨线程通信时&#xff0c;需要定义双方都认可的接口&#xff0c;以保障双方可以成功通信&#xff0c;OpenHarmony ID…...

封装Redis工具类

基于StringRedisTemplate封装一个缓存工具类,满足以下需求: 方法1:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置TTL过期时间 方法2:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置TTL过期时间,用于处理缓存击穿问题 方法3:根据指定的…...

将n变为一个可以被表示为2^{a}+2^{b}的正整数m

给出一个正整数n&#xff0c;需要将n变为一个可以被表示为的正整数m&#xff0c;其中a和b都是非负整数且a!b&#xff0c;你可以进行两种操作&#xff1a; 1.令n加1 2.令n减1 请你求出最少需要多少次操作才能将n变成满足条件的m。 输入格式 输入一个整数&#xff0c;代表n。…...

第2章:Python TDD构建Dollar类基础

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…...

搭建一个基于Spring Boot的校园台球厅人员与设备管理系统

搭建一个基于Spring Boot的校园台球厅人员与设备管理系统可以涵盖多个功能模块&#xff0c;例如用户管理、设备管理、预约管理、计费管理等。以下是一个简化的步骤指南&#xff0c;帮助你快速搭建一个基础的系统。 — 1. 项目初始化 使用 Spring Initializr 生成一个Spring …...

JavaScript系列(33)--微前端架构详解

JavaScript微前端架构详解 &#x1f3d7;️ 今天&#xff0c;让我们深入了解JavaScript的微前端架构&#xff0c;这是一种用于构建和管理大型前端应用的现代架构模式。 微前端基础概念 &#x1f31f; &#x1f4a1; 小知识&#xff1a;微前端是一种将前端应用分解成更小、更易…...

第6章:Python TDD实例变量私有化探索

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…...

Ubuntu 24.04 LTS 服务器折腾集

目录 Ubuntu 更改软件源Ubuntu 系统语言英文改中文windows 远程链接 Ubuntu 图形界面Windows 通过 openssh 连接 UbuntuUbuntu linux 文件权限Ubuntu 空闲硬盘挂载到 文件管理器的 other locationsUbuntu 开启 SMB 服务&#xff0c;并通过 windows 访问Ubuntu安装Tailscale&am…...

物联网在烟草行业的应用

物联网技术在烟草行业的应用 物联网技术在烟草行业的应用主要体现在以下几个方面&#xff1a; 智能制造 &#xff1a;物联网技术可以实现对生产过程中的关键参数进行实时监测&#xff0c;确保产品的质量稳定可靠。同时&#xff0c;通过对设备的远程维护和故障诊断&#xff0c;…...

2024年度总结:从后端Java到全栈成长的蜕变

目录 前言1. 用数据与实践书写成长篇章2. 技术与生活的双重蜕变3. 技术的进阶与生活的绽放 前言 今年是我入行的第十年&#xff0c;也是记录在CSDN平台上的第五年。这五年来&#xff0c;我始终坚持记录成长的点滴&#xff0c;将个人事业与博客创作紧密相连。一路走来&#xff0…...

详解构造函数和析构函数

⼀个类&#xff0c;我们不写的情况下编译器会默认⽣成以下6个默认成员函数。 下面我们详细介绍的是构造函数和析构函数&#xff0c;它们的主要作用分别是初始化工作和清理工作。 构造函数 1、构造函数的概念 构造函数虽名里带着“构造”但是其实际上并不是说开辟空间创建对…...

npm操作大全:从入门到精通

引言 在现代前端开发中&#xff0c;npm&#xff08;Node Package Manager&#xff09;是不可或缺的工具。无论是安装依赖、管理项目&#xff0c;还是发布自己的包&#xff0c;npm都扮演着重要的角色。本文将带你从npm的基础操作开始&#xff0c;逐步深入到高级用法&#xff0c…...

ChatGPT 写作系列

ChatGPT 辅助写作 | 专栏 1 写作核心​ 先讲一下 ChatGPT 写作的核心。核心就是需要有文章大纲&#xff0c;而且文章大纲要足够细致。​ 具体怎么做呢&#xff1f;​ 提前准备多级标题大纲&#xff0c;刚开始有两个级别的标题就行&#xff0c;等用熟练了再细化。分一级标题&…...

kubernetes 集群搭建(kubeadm方式)

随着容器技术的日益普及&#xff0c;Kubernetes 作为最受欢迎的容器编排平台之一&#xff0c;已经成为现代云原生应用部署不可或缺的一部分。对于想要快速构建和管理 Kubernetes 集群的人来说&#xff0c;kubeadm 提供了一种简单而强大的工具。本文将详细介绍如何使用 kubeadm …...

OpenWrt 中使用 LuCI 界面部署 Docker 镜像

本篇博客将介绍如何在 OpenWrt 上使用 LuCI 部署 Docker 镜像&#xff0c;以 "hello-world" 镜像为例。 前提条件 已安装支持 Docker 的 OpenWrt 系统。 Docker 服务已在 OpenWrt 上成功安装并运行。 LuCI Docker 插件&#xff08;luci-app-docker 或类似的管理界…...

阿里云 Serverless 助力盟主直播:高并发下的稳定性和成本优化

在直播场景中&#xff0c;阿里云 Serverless 应用引擎 SAE 提供的无缝弹性伸缩与极速部署能力&#xff0c;确保直播间高并发时的流畅体验&#xff0c;降低了我们的运营成本&#xff0c;简化了运维流程。结合阿里云云原生数据库 PolarDB 的 Serverless 能力&#xff0c;实现了数…...

C++ 多态 初学笔记

多态 虚函数虚函数的使用条件 虚函数详解对象多态多重继承时&#xff0c;类型转换的练习&#xff08;1&#xff09;情况1&#xff1a;&#xff08;2&#xff09;情况2&#xff1a;&#xff08;3&#xff09;情况3&#xff1a;&#xff08;4&#xff09;情况4&#xff1a; 对象多…...

深入剖析 Java 的 synchronized 锁升级过程

前言 在 Java 并发编程领域&#xff0c;synchronized关键字堪称保障线程安全的中流砥柱。随着 JDK 版本的迭代演进&#xff0c;synchronized锁的性能优化也在持续推进&#xff0c;其中锁升级机制尤为关键。本文将深度剖析synchronized锁从偏向锁、轻量级锁到重量级锁的升级历程…...

当文件补丁修改器因为文件操作权限有问题时,可以将文件拷贝到普通目录操作

文章目录 当文件补丁修改器因为文件操作权限有问题时&#xff0c;可以将文件拷贝到普通目录操作概述笔记直接在安装目录打补丁失败的情况将目标程序拷贝到补丁修改器的解压目录打补丁成功的情况备注END 当文件补丁修改器因为文件操作权限有问题时&#xff0c;可以将文件拷贝到普…...

【TCP】rfc文档

tcp协议相关rfc有哪些 TCP&#xff08;传输控制协议&#xff09;是一个复杂的协议&#xff0c;其设计和实现涉及多个RFC文档。以下是一些与TCP协议密切相关的RFC文档列表&#xff0c;按照时间顺序排列&#xff0c;涵盖了从基础定义到高级特性和优化的各个方面&#xff1a; 基…...

Linux探秘坊-------3.开发工具详解(1)

1 初识vim编辑器 创建第一个vim编辑的代码 1.新建文件 2.使用vim打开 3.打开默认是命令模式&#xff0c;写代码需要在屏幕上输出“i”字符 1.写完代码后要按Esc键退出到指令模式2.再按shift:wq即可保存并退出vim &#xff08;因为不支持鼠标&#xff0c;通常 使用键盘上的箭…...

如何在Python中进行JSON数据的序列化和反序列化?

在Python中&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解析和生成。Python内置的json模块提供了简单易用的方法来实现数据的序列化和反序列化。下面将详细介绍如何…...

SpringMVC (1)

目录 1. 什么是Spring Web MVC 1.1 MVC的定义 1.2 什么是Spring MVC 1.3 Spring Boot 1.3.1 创建一个Spring Boot项目 1.3.2 Spring Boot和Spring MVC之间的关系 2. 学习Spring MVC 2.1 SpringBoot 启动类 2.2 建立连接 1. 什么是Spring Web MVC 1.1 MVC的定义 MVC 是…...

Redis 3.2.1在Win10系统上的安装教程

诸神缄默不语-个人CSDN博文目录 这个文件可以跟我要&#xff0c;也可以从官网下载&#xff1a;https://github.com/MicrosoftArchive/redis/releases 这个是微软以前维护的Windows版Redis安装包&#xff0c;如果想要比较新的版本可以从别人维护的项目里下&#xff08;https://…...

springboot医院信管系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…...

A6.Springboot-LLama3.2服务自动化构建(三)——编写Pipeline构建仓库初始化脚本

下面我们接着上一篇文章《A5.Springboot-LLama3.2服务自动化构建(二)——Jenkins流水线构建配置初始化设置》继续往下分析,编写Pipeline构建脚本。 一、统一Shell执行环境 Jenkins执行Shell脚本时,会在Jenkins节点上创建一个临时的环境来执行该脚本。这个环境包含了Jenki…...