YOLOv9改进策略:卷积魔改 | AKConv(可改变核卷积),即插即用的卷积,效果秒杀DSConv | 2023年11月最新发表

 💡💡💡本文改进内容: YOLOv9如何魔改卷积进一步提升检测精度?AKConv 通过不规则卷积运算完成高效特征提取的过程,为卷积采样形状带来更多探索选择。 AKConv可以作为即插即用的卷积运算来替代卷积运算来提高网络性能。在数据集 COCO2017、VOC07+12 和 VisDrone-DET2021 上进行的物体检测实验充分展示了 AKConv 的优势。

yolov9-c-AKConv summary: 968 layers, 50952916 parameters, 50952884 gradients, 236.3 GFLOPs

 改进结构图如下:

YOLOv9魔术师专栏

☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️ ☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️

包含注意力机制魔改、卷积魔改、检测头创新、损失&IOU优化、block优化&多层特征融合、 轻量级网络设计、24年最新顶会改进思路、原创自研paper级创新等

☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️

✨✨✨ 新开专栏暂定免费限时开放,后续每月调价一次✨✨✨

🚀🚀🚀 本项目持续更新 | 更新完结保底≥50+ ,冲刺100+🚀🚀🚀

🍉🍉🍉 联系WX: AI_CV_0624 欢迎交流!🍉🍉🍉

YOLOv9魔改:注意力机制、检测头、blcok魔改、自研原创等

 YOLOv9魔术师

💡💡💡全网独家首发创新(原创),适合paper !!!

💡💡💡 2024年计算机视觉顶会创新点适用于Yolov5、Yolov7、Yolov8等各个Yolo系列,专栏文章提供每一步步骤和源码,轻松带你上手魔改网络 !!!

💡💡💡重点:通过本专栏的阅读,后续你也可以设计魔改网络,在网络不同位置(Backbone、head、detect、loss等)进行魔改,实现创新!!!

 1.YOLOv9原理介绍

论文: 2402.13616.pdf (arxiv.org)

代码:GitHub - WongKinYiu/yolov9: Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information摘要: 如今的深度学习方法重点关注如何设计最合适的目标函数,从而使得模型的预测结果能够最接近真实情况。同时,必须设计一个适当的架构,可以帮助获取足够的信息进行预测。然而,现有方法忽略了一个事实,即当输入数据经过逐层特征提取和空间变换时,大量信息将会丢失。因此,YOLOv9 深入研究了数据通过深度网络传输时数据丢失的重要问题,即信息瓶颈和可逆函数。作者提出了可编程梯度信息(programmable gradient information,PGI)的概念,来应对深度网络实现多个目标所需要的各种变化。PGI 可以为目标任务计算目标函数提供完整的输入信息,从而获得可靠的梯度信息来更新网络权值。此外,研究者基于梯度路径规划设计了一种新的轻量级网络架构,即通用高效层聚合网络(Generalized Efficient Layer Aggregation Network,GELAN)。该架构证实了 PGI 可以在轻量级模型上取得优异的结果。研究者在基于 MS COCO 数据集的目标检测任务上验证所提出的 GELAN 和 PGI。结果表明,与其他 SOTA 方法相比,GELAN 仅使用传统卷积算子即可实现更好的参数利用率。对于 PGI 而言,它的适用性很强,可用于从轻型到大型的各种模型。我们可以用它来获取完整的信息,从而使从头开始训练的模型能够比使用大型数据集预训练的 SOTA 模型获得更好的结果。对比结果如图1所示。

 YOLOv9框架图

1.1 YOLOv9框架介绍

YOLOv9各个模型介绍

2.AKConv原理介绍

论文:https://arxiv.org/pdf/2311.11587.pdf

摘要:基于卷积运算的神经网络在深度学习领域取得了令人瞩目的成果,但标准卷积运算存在两个固有的缺陷。一方面,卷积运算仅限于局部窗口,无法捕获其他位置的信息, 并且它的采样形状是固定的。 另一方面,卷积核的大小固定为k×k,是一个固定的正方形,参数的数量往往随大小呈平方增长。 很明显,不同数据集和不同位置的目标的形状和大小是不同的。 具有固定样本形状和正方形的卷积核不能很好地适应不断变化的目标 针对上述问题,本工作探索了可改变核卷积(AKConv),它赋予卷积核任意数量的参数和任意采样形状,为网络开销和性能之间的权衡提供更丰富的选择。 在 AKConv 中,我们通过新的坐标生成算法定义任意大小的卷积核的初始位置。 为了适应目标的变化,我们引入了偏移量来调整每个位置的样本形状。 此外,我们通过使用具有相同大小和不同初始采样形状的 AKConv 来探索神经网络的效果。 AKConv 通过不规则卷积运算完成高效特征提取的过程,为卷积采样形状带来更多探索选择。 在代表性数据集 COCO2017、VOC 7+12 和 VisDrone-DET2021 上进行的物体检测实验充分展示了 AKConv 的优势。 AKConv可以作为即插即用的卷积运算来替代卷积运算来提高网络性能。

很明显,与 Deformabled 和标准 Conv 相比,AKConv 有更多的选择,并且卷积参数的数量随着卷积核大小呈线性增加。 注意:为了清楚地描述 AKConv 的优点,在 AKConv 和 Deformable Conv 中我们忽略了学习偏移量的参数数量,因为它远小于特征提取中涉及的卷积参数数量。

作者认为 AKConv 的设计是一种新颖的设计,它实现了从不规则和任意采样形状的卷积核中提取特征的壮举。 即使不使用 Deformable Conv 中的偏移思想,AKConv 仍然可以做出多种卷积核形状。 因为,AKConv可以用初始坐标重新采样来呈现多种变化。 如图4所示,我们为大小为5的卷积设计了各种初始采样形状。在图4中,我们只显示了大小为5的一些示例。但是,AKConv的大小可以是任意的,因此随着大小的增加,初始采样形状会随着大小的增加而变化。 AKConv 的卷积采样形状变得更加丰富甚至无限。 鉴于不同数据集的目标形状各不相同,设计与采样形状相对应的卷积运算至关重要。 AKConv完全是通过根据特定相位域设计相应形状的卷积运算来实现的。 它还可以类似于 Deformable Conv,通过添加可学习的偏移来动态适应对象的变化。 对于特定任务,卷积核初始采样位置的设计很重要,因为它是先验知识。 正如齐等人所言。 [27],他们为细长管状结构分割任务提出了具有相应形状的采样坐标,但他们的形状选择仅适用于细长管状结构。 

展示核大小为5的初始样本形状。AKConv可以通过设计不同的初始采样形状来实现任意采样形状。

实验结果,数据集 COCO2017、VOC 7+12 和 VisDrone-DET2021 上进行的物体检测实验充分展示了 AKConv 的优势

 

    

3.AKConv加入到YOLOv9

3.1新建py文件,路径为models/Conv/AKConv.py

import torch
import torch.nn as nn
import math
from einops import rearrangeclass AKConv(nn.Module):def __init__(self, inc, outc, num_param, stride=1, bias=None):super(AKConv, self).__init__()self.num_param = num_paramself.stride = strideself.conv = nn.Sequential(nn.Conv2d(inc, outc, kernel_size=(num_param, 1), stride=(num_param, 1), bias=bias),nn.BatchNorm2d(outc),nn.SiLU())  # the conv adds the BN and SiLU to compare original Conv in YOLOv5.self.p_conv = nn.Conv2d(inc, 2 * num_param, kernel_size=3, padding=1, stride=stride)nn.init.constant_(self.p_conv.weight, 0)self.p_conv.register_full_backward_hook(self._set_lr)#https://blog.csdn.net/m0_63774211/category_12289773.html?spm=1001.2014.3001.5482@staticmethoddef _set_lr(module, grad_input, grad_output):grad_input = (grad_input[i] * 0.1 for i in range(len(grad_input)))grad_output = (grad_output[i] * 0.1 for i in range(len(grad_output)))def forward(self, x):# N is num_param.offset = self.p_conv(x)dtype = offset.data.type()N = offset.size(1) // 2# (b, 2N, h, w)p = self._get_p(offset, dtype)# (b, h, w, 2N)p = p.contiguous().permute(0, 2, 3, 1)q_lt = p.detach().floor()q_rb = q_lt + 1q_lt = torch.cat([torch.clamp(q_lt[..., :N], 0, x.size(2) - 1), torch.clamp(q_lt[..., N:], 0, x.size(3) - 1)],dim=-1).long()q_rb = torch.cat([torch.clamp(q_rb[..., :N], 0, x.size(2) - 1), torch.clamp(q_rb[..., N:], 0, x.size(3) - 1)],dim=-1).long()q_lb = torch.cat([q_lt[..., :N], q_rb[..., N:]], dim=-1)q_rt = torch.cat([q_rb[..., :N], q_lt[..., N:]], dim=-1)# clip pp = torch.cat([torch.clamp(p[..., :N], 0, x.size(2) - 1), torch.clamp(p[..., N:], 0, x.size(3) - 1)], dim=-1)# bilinear kernel (b, h, w, N)g_lt = (1 + (q_lt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_lt[..., N:].type_as(p) - p[..., N:]))g_rb = (1 - (q_rb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_rb[..., N:].type_as(p) - p[..., N:]))g_lb = (1 + (q_lb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_lb[..., N:].type_as(p) - p[..., N:]))g_rt = (1 - (q_rt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_rt[..., N:].type_as(p) - p[..., N:]))# resampling the features based on the modified coordinates.x_q_lt = self._get_x_q(x, q_lt, N)x_q_rb = self._get_x_q(x, q_rb, N)x_q_lb = self._get_x_q(x, q_lb, N)x_q_rt = self._get_x_q(x, q_rt, N)# bilinearx_offset = g_lt.unsqueeze(dim=1) * x_q_lt + \g_rb.unsqueeze(dim=1) * x_q_rb + \g_lb.unsqueeze(dim=1) * x_q_lb + \g_rt.unsqueeze(dim=1) * x_q_rtx_offset = self._reshape_x_offset(x_offset, self.num_param)out = self.conv(x_offset)return out#https://blog.csdn.net/m0_63774211/category_12289773.html?spm=1001.2014.3001.5482# generating the inital sampled shapes for the AKConv with different sizes.def _get_p_n(self, N, dtype):base_int = round(math.sqrt(self.num_param))row_number = self.num_param // base_intmod_number = self.num_param % base_intp_n_x,p_n_y = torch.meshgrid(torch.arange(0, row_number),torch.arange(0,base_int))p_n_x = torch.flatten(p_n_x)p_n_y = torch.flatten(p_n_y)if mod_number >  0:mod_p_n_x,mod_p_n_y = torch.meshgrid(torch.arange(row_number,row_number+1),torch.arange(0,mod_number))mod_p_n_x = torch.flatten(mod_p_n_x)mod_p_n_y = torch.flatten(mod_p_n_y)p_n_x,p_n_y  = torch.cat((p_n_x,mod_p_n_x)),torch.cat((p_n_y,mod_p_n_y))p_n = torch.cat([p_n_x,p_n_y], 0)p_n = p_n.view(1, 2 * N, 1, 1).type(dtype)return p_n# no zero-paddingdef _get_p_0(self, h, w, N, dtype):p_0_x, p_0_y = torch.meshgrid(torch.arange(0, h * self.stride, self.stride),torch.arange(0, w * self.stride, self.stride))p_0_x = torch.flatten(p_0_x).view(1, 1, h, w).repeat(1, N, 1, 1)p_0_y = torch.flatten(p_0_y).view(1, 1, h, w).repeat(1, N, 1, 1)p_0 = torch.cat([p_0_x, p_0_y], 1).type(dtype)return p_0def _get_p(self, offset, dtype):N, h, w = offset.size(1) // 2, offset.size(2), offset.size(3)# (1, 2N, 1, 1)p_n = self._get_p_n(N, dtype)# (1, 2N, h, w)p_0 = self._get_p_0(h, w, N, dtype)p = p_0 + p_n + offsetreturn pdef _get_x_q(self, x, q, N):b, h, w, _ = q.size()padded_w = x.size(3)c = x.size(1)# (b, c, h*w)x = x.contiguous().view(b, c, -1)# (b, h, w, N)index = q[..., :N] * padded_w + q[..., N:]  # offset_x*w + offset_y# (b, c, h*w*N)index = index.contiguous().unsqueeze(dim=1).expand(-1, c, -1, -1, -1).contiguous().view(b, c, -1)x_offset = x.gather(dim=-1, index=index).contiguous().view(b, c, h, w, N)return x_offset# https://blog.csdn.net/m0_63774211/category_12289773.html?spm=1001.2014.3001.5482#  Stacking resampled features in the row direction.@staticmethoddef _reshape_x_offset(x_offset, num_param):b, c, h, w, n = x_offset.size()# using Conv3d# x_offset = x_offset.permute(0,1,4,2,3), then Conv3d(c,c_out, kernel_size =(num_param,1,1),stride=(num_param,1,1),bias= False)# using 1 × 1 Conv# x_offset = x_offset.permute(0,1,4,2,3), then, x_offset.view(b,c×num_param,h,w)  finally, Conv2d(c×num_param,c_out, kernel_size =1,stride=1,bias= False)# using the column conv as follow, then, Conv2d(inc, outc, kernel_size=(num_param, 1), stride=(num_param, 1), bias=bias)x_offset = rearrange(x_offset, 'b c h w n -> b c (h n) w')return x_offset

3.2修改yolo.py

1)首先进行引用

from models.Conv.AKConv import AKConv

2)修改def parse_model(d, ch):  # model_dict, input_channels(3)

在源码基础上加入AKConv

        n = n_ = max(round(n * gd), 1) if n > 1 else n  # depth gainif m in {Conv, AConv, ConvTranspose, Bottleneck, SPP, SPPF, DWConv, BottleneckCSP, nn.ConvTranspose2d, DWConvTranspose2d, SPPCSPC, ADown,RepNCSPELAN4, SPPELAN,AKConv}:c1, c2 = ch[f], args[0]if c2 != no:  # if not outputc2 = make_divisible(c2 * gw, 8)args = [c1, c2, *args[1:]]

3.3 yolov9-c-AKConv.yaml

# YOLOv9# parameters
nc: 80  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
#activation: nn.LeakyReLU(0.1)
#activation: nn.ReLU()# anchors
anchors: 3# YOLOv9 backbone
backbone:[[-1, 1, Silence, []],  # conv down[-1, 1, Conv, [64, 3, 2]],  # 1-P1/2# conv down[-1, 1, Conv, [128, 3, 2]],  # 2-P2/4# elan-1 block[-1, 1, RepNCSPELAN4, [256, 128, 64, 1]],  # 3# avg-conv down[-1, 1, ADown, [256]],  # 4-P3/8# elan-2 block[-1, 1, RepNCSPELAN4, [512, 256, 128, 1]],  # 5# avg-conv down[-1, 1, ADown, [512]],  # 6-P4/16# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 7# avg-conv down[-1, 1, ADown, [512]],  # 8-P5/32# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 9]# YOLOv9 head
head:[# elan-spp block[-1, 1, SPPELAN, [512, 256]],  # 10# up-concat merge[-1, 1, nn.Upsample, [None, 2, 'nearest']],[[-1, 7], 1, Concat, [1]],  # cat backbone P4# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 13# up-concat merge[-1, 1, nn.Upsample, [None, 2, 'nearest']],[[-1, 5], 1, Concat, [1]],  # cat backbone P3# elan-2 block[-1, 1, RepNCSPELAN4, [256, 256, 128, 1]],  # 16 (P3/8-small)# avg-conv-down merge[-1, 1, ADown, [256]],[[-1, 13], 1, Concat, [1]],  # cat head P4# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 19 (P4/16-medium)# avg-conv-down merge[-1, 1, ADown, [512]],[[-1, 10], 1, Concat, [1]],  # cat head P5# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 22 (P5/32-large)# multi-level reversible auxiliary branch# routing[5, 1, CBLinear, [[256]]], # 23[7, 1, CBLinear, [[256, 512]]], # 24[9, 1, CBLinear, [[256, 512, 512]]], # 25# conv down[0, 1, AKConv, [64, 3, 2]],  # 26-P1/2# conv down[-1, 1, AKConv, [128, 3, 2]],  # 27-P2/4# elan-1 block[-1, 1, RepNCSPELAN4, [256, 128, 64, 1]],  # 28# avg-conv down fuse[-1, 1, ADown, [256]],  # 29-P3/8[[23, 24, 25, -1], 1, CBFuse, [[0, 0, 0]]], # 30  # elan-2 block[-1, 1, RepNCSPELAN4, [512, 256, 128, 1]],  # 31# avg-conv down fuse[-1, 1, ADown, [512]],  # 32-P4/16[[24, 25, -1], 1, CBFuse, [[1, 1]]], # 33 # elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 34# avg-conv down fuse[-1, 1, ADown, [512]],  # 35-P5/32[[25, -1], 1, CBFuse, [[2]]], # 36# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]],  # 37# detection head# detect[[31, 34, 37, 16, 19, 22], 1, DualDDetect, [nc]],  # DualDDetect(A3, A4, A5, P3, P4, P5)]

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

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

相关文章

【单点知识】基于实例讲解PyTorch中的ImageFolder类

文章目录 0. 前言1. ImageFolder功能2 基本使用方法及参数解析2.1 基本调用方式2.2 构造参数说明2.3 属性2.4 方法 3. PyTorch实例说明3.1 实例数据集3.2 实例说明 0. 前言 按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的…

word excel ppt转pdf

1.excel转pdf package pers.wwz.study;import com.aspose.cells.PdfSaveOptions; import com.aspose.cells.Workbook;public class ExcelToPdf {public static void main(String[] args) throws Exception {// 加载Excel文件Workbook workbook new Workbook("D:\\tmp\\…

Git tag总结

文章目录 Git 标签标签(Tag)是什么创建标签轻量标签(Lightweight Tag)附注标签(Annotated Tag) 查看标签切换到标签基于标签创建新分支直接切换到标签 切换到标签的注意事项 Git 标签 标签(Ta…

市场复盘总结 20240319

仅用于记录当天的市场情况,用于统计交易策略的适用情况,以便程序回测 短线核心:不参与任何级别的调整,采用龙空龙模式 一支股票 10%的时候可以操作, 90%的时间适合空仓等待 二进三: 进级率中 17% 最常用的…

小程序开发平台哪个比较好?

当我们谈论小程序时,我们往往会联想到微信、支付宝、百度等大厂的生态。但你是否想过,除了这些知名平台,还有哪些优秀的小程序开发平台呢?在这篇文章中,我们将一探究竟。 小程序开发平台的优劣,通常取决于…

Unity发布webgl设置占满浏览器运行

Unity发布webgl设置占满浏览器运行 Unity发布webgl的时候index.html的模板文件 模板文件路径,根据自己的需求修改。 C:\Program Files\Unity\Hub\Editor\2021.1.18f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\WebGLTemplates\Default再桌面新建一个t…

Transformer的前世今生 day04(ELMO

ELMO 前情回顾 NNLM模型:主要任务是在预测下一个词,副产品是词向量Word2Vec模型:主要任务是生成词向量 CBOW:训练目标是根据上下文预测目标词Skip-gram:训练目标是根据目标词预测上下文词 ELMO模型的流程 针对Wor…

AcWing 3498. 日期差值(每日一题)

题目链接:3498. 日期差值 - AcWing题库 有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天。 输入格式 输入包含多组测试数据。 每组数据占两行,分别表示两个日期,形式为 YYYYMM…

项目投标文件两大必备检索工具

项目投标文件两大必备检索工具: 1.anytxt 文件内容检索工具【AnyTXT Searcher】 这款软件也是一款搜索工具,但是它与Everything不同的是,Everything是基于文件名搜索相关文件,而AnyTXT是基于文件内容搜索相关文件,两…

项目七 完成学生信息的综合查询

项目七 完成学生信息的综合查询 1,探究综合查询理论的必备理论知识 笛卡尔积运算原理 笛卡尔乘积是指在数学中,两个集合 X 和 Y 的笛卡尓积(Cartesian product),又称直积,表示为 X Y。 对数据表做笛卡…

AI入门之旅:从基础知识到实战应用(六)

一、人工智能学习之路总结 人工智能学习的关键点与挑战可以总结如下: 关键点: 理论基础: 理解机器学习、深度学习等人工智能的基本原理和算法是学习的基础,包括线性代数、概率统计、微积分等数学知识,以及神经网络、…

想提升职场形象?收下这3种工作中常用的邮件问候语吧!

发给老板、同事或跨部门同事的第一行邮件就会为你的整封邮件定下基调。开场白揭示了你写电子邮件时的许多情绪状态:它们可能传达出自信,增强团队成员之间的信任度。或者起到反作用:语气上的不足可能会影响士气并造成混乱。 因此,…

Linux -- 常用命令积累

1、查找后台正在运行的命令,以shell 程序 为例 ps -ef | grep cv1.sh使用 ps 命令来获取更详细的信息,包括进程的完整命令行可以使用 kill 命令通过进程的 PID 来停止特定的进程 得到以下内容: rot 27772 5072 0 11:59 pts/8 0…

Vue3--计算属性和侦听器

计算属性 Computed 一般是对某个响应式数据进行加工处理获得新数据 侦听器 watch 监视某个响应式数据,如果它发生变化,就自动调用某个函数

MySQL的概述与安装

一、数据库的基本概念: 1.1 数据: 1) 描述事物的符号记录称为数据(Data)。数字、文字、图形、图像、声音、档案记录等 都是数据。 2)数据是以“记录”的形式按照统一的格式进行存储的,而不是…

个体户、个人独资企业和一人有限公司的区别

个体工商户 定义 根据《中华人民共和国民法通则》规定,公民在法律允许的范围内,依法经核准登记,从事工商业经营的,为个体工商户。个体工商户的债务,个人经营的,以个人财产承担;家庭经营的&…

权限维持小结

windows 1.自启动 1、自启动路径加载 C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ 2、自启动服务加载 (服务重启system权限) sc create ServiceTest binPath C:\xd.exe start auto sc delete ServiceTest 3、自启动注册表加载 -…

SQL语言之CREATE/DROP/ALTER 语句

CREATE/DROP/ALTER 语句,表的创建、删除、修改语句 文章目录 一、模式 1、定义模式 CREATE SCHEMA 2、删除模式 DROP SCHEMA 二、基本表 1、定义基本表 CREATE TABLE (1)数据类型 (2)列级完整性约束条件 (3)表级完整性约束条件 2、在模式中定义表 3、修改…

openEuler 欧拉系统nginx正向代理 http https —— 筑梦之路

正向代理 Nginx正向代理,通过服务器代理客户端去重定向请求访问到目标服务器的一种代理服务。对于目标服务器来说浏览器/客户端是隐藏的。Nginx 正向代理默认只支持http 协议,不支持 https 协议,需借助"ngx_http_proxy_connect_module&q…

面试算法-49-缺失的第一个正数

题目 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1: 输入:nums [1,2,0] 输出:3 解释:范围 [1,2] 中的数字都…