Python----神经网络(《Inverted Residuals and Linear Bottlenecks》论文概括和MobileNetV2网络)
一、论文
MobileNetV2 论文提出了一种新的移动架构,该架构提高了移动模型在多个任务和基准测试中的性能,以及在各种不同模型大小范围内的性能. 该架构基于倒残差结构,其中 shortcut 连接在 thin bottleneck 层之间. 中间的 expansion 层使用轻量级 depthwise 卷积来过滤特征,作为非线性的来源. 此外,作者发现,为了保持表示能力,重要的是要移除 narrow 层中的非线性. 论文展示了这种架构在 ImageNet 分类、COCO 目标检测和 VOC 图像分割任务中的有效性. 论文评估了准确性、计算成本(以 multiply-adds 衡量)、延迟和参数数量之间的权衡. 该论文的关键贡献是具有线性 bottleneck 的倒残差模块,它可以实现高效的推理并减少内存占用.
1.1、基本信息
-
标题: MobileNetV2: Inverted Residuals and Linear Bottlenecks
-
作者: Mark Sandler, Andrew Howard, Menglong Zhu, Andrey Zhmoginov, Liang-Chieh Chen
-
单位: Google Inc.
-
主要贡献: 提出了一种新的移动架构,MobileNetV2,它提高了移动模型在多个任务和基准测试中的性能,以及不同模型大小范围内的性能。
1.2、主要内容
1.2.1、倒残差块和线性瓶颈
-
MobileNetV2 的核心创新在于“倒残差块”。传统的残差块(如 ResNet 中的)连接的是较宽的层,而倒残差块连接的是较窄的 bottleneck 层。中间的 expansion 层负责扩展通道维度。
-
这些块使用深度可分离卷积以提高效率。
-
一个关键要素是“线性 bottleneck”。论文认为,ReLU 非线性激活在低维空间中可能会破坏信息。因此,每个块的最后一层是线性的(没有 ReLU),以保留信息。
1.2.2、架构设计
-
MobileNetV2 在 MobileNetV1 的深度可分离卷积的基础上构建。
-
该架构由一个初始卷积层、一系列倒残差块和一个最终卷积层组成。
-
平均池化层和全连接层用于分类。
-
使用宽度乘数和分辨率乘数来创建更小、计算量更少的模型。
1.2.3、效率
-
倒残差结构旨在实现高内存效率,这对于移动设备至关重要。它可以减少内存占用和对昂贵的内存访问的需求。
-
与标准卷积相比,深度可分离卷积显着减少了参数数量和计算量。
1.2.4、性能
-
论文表明,MobileNetV2 在 ImageNet 分类任务上比 MobileNetV1 实现了更高的准确率和效率。
-
它还在目标检测(使用 SSDLite)和语义分割(使用 Mobile DeepLabv3)方面取得了出色的效果。
1.3、作用
MobileNetV2 旨在为移动和资源受限环境提供高效的卷积神经网络。 它在准确性和计算成本之间实现了有效的权衡。
1.4、影响
MobileNetV2 改进了 MobileNetV1 的架构,并在多个任务和基准测试中实现了最先进的性能。它为在资源受限的设备上部署深度学习模型提供了一种有效的方法。
1.5、优点
-
效率: MobileNetV2 通过使用 depthwise separable 卷积和 inverted residuals,显著减少了计算需求和内存占用。
-
性能: MobileNetV2 在图像分类、目标检测和语义分割等任务中表现出色。
-
可调性: 宽度乘数和分辨率乘数允许轻松调整模型大小,以适应不同的资源约束。
-
内存效率: 倒残差 bottleneck 层允许特别节省内存的实现,这对于移动应用程序非常重要。
1.6、缺点
-
复杂性: 与 MobileNetV1 相比,MobileNetV2 引入了 inverted residual 块,这可能会使模型架构更复杂。
-
权衡: 虽然宽度乘数和分辨率乘数提供了灵活性,但它们需要在准确性和效率之间进行仔细的权衡。
论文地址:
[1801.04381] MobileNetV2: Inverted Residuals and Linear Bottlenecks
二、MobileNetV2
2.1、网络的背景
MobileNetV1网络的depthwise部分的卷积核容易参数为0,导致浪费掉。 MobileNetV2网络是由谷歌团队在2018年提出的,它对于MobileNetV1而言,有着更高的准确率和更小的网络模型。
2.2、Inverted Residuals(倒残差结构)
在MobileNetV2 中,是先升维,再降维的操作,所以该结构叫倒残差结构,网络结构表格中的 bottleneck就是倒残差结构。
残差结构的过程是:
1、1x1卷积降维
2、3x3卷积
3、1x1卷积升维
即对输入特征矩阵进行利用1x1卷积进行降维,减少输入特征矩阵的channel,然后 通过3x3的卷积核进行处理提取特征,最后通过1x1的卷积核进行升维,那么它的结 构就是两边深,中间浅的结构。
在MobileNetV2网络结构中,采用了倒残差结构,从名字可以联想到,它的结构应该 是中间深,两边浅,它的结构如下图所示:
倒残差结构的过程是:
1、 首先会通过一个1x1卷积层来进行升维处理,在卷积后会跟有BN和Relu6激活函 数
2、 紧接着是一个3x3大小DW卷积,卷积后面依旧会跟有BN和Relu6激活函数
3、 最后一个卷积层是1x1卷积,起到降维作用,注意卷积后只跟了BN结构,并没有 使用Relu6激活函数
在MobileNetV1中,DW卷积的个数局限于上一层的输出通道数,无法自由改变,但 是加入PW卷积之后,也就是升维卷积之后,DW卷积的个数取决于PW卷积的输出通 道数,而这个通道数是可以任意指定的,因此解除了3x3卷积核个数的限制。
2.3、Relu6
因为在低维空间使用非线性函数(如Relu)会损失一些信息,高维空间中会损失得相 对少一些,因此引入Inverted Residuals,升维之后再卷积,能够更好地保留特征。
在移动端设备float16的低精度的时 候,也能有很好的数值分辨率,如果对Relu的激活范围不加限制,输出范围为0到正 无穷,如果激活值非常大,分布在一个很大的范围内,则低精度的float16无法很好 地精确描述如此大范围的数值,带来精度损失,所以在量化过程中,Relu6能够有更 好的量化表现和更小的精度下降。
2.4、Linear Bottlenecks
2.5、Shortcut
注意:Shortcut并不是该网络提出的,而是残差结构提出的。
这里由于具有倒残差结构,所以也会有shortcut操作,如下图所示,左侧为有 shortcut连接的倒残差结构,右侧是无shortcut连接的倒残差结构:
shortcut将输入与输出直接进行相加,可以使得网络在较深的时候依旧可以进行训 练。
注意:这里只有stride=1且输入特征矩阵与输出特征矩阵shape相同时才有 shortcut连接。
2.6、拓展因子
举例:
假如输入特征矩阵是128维的,隐藏层个数是256,条件1是输出必须有128个 为正,并且这128个输出为正的神经元的应该是非线性相关的。
~~~~当隐藏层m远大于输入n,那么 更容易让ReLU保持可逆。
在训练前和训练后,各个层是否发生了不可逆的情况, 如下图所示:
在训练前,由于都是初始化的,所以激活为正的特征个数都比较集中,图中虚线是信 息会丢失的阈值,纵坐标低于该虚线,会导致信息丢失。在训练后,平均值变化较 小,但是两级分化较为严重,有一些最小值已经低于了阈值,就会造成信息丢失,但 是绝大多数层还是可逆的。
2.7、网络的结构
Input是每一层结构的输入矩阵尺寸和channel;
Operator是操作;
t是拓展因子;
c是输出特征矩阵channel;
n是bottleneck的重复次数;
s是步距,如果bottleneck重复,但只针对于第一次bottleneck的DW卷积,其他为 1。
由网络结构的图可以看到最后一层是卷积层,但是其实就是一个全连接层的作用,k 是输出的类别,如果是ImageNet数据集,那么k就是1000。
注意:
在每个DW卷积之后都有batchNorm操作,这里组件中为了减少学习者工 作量并没有体现该结构,但是学习者需要知道。
注意:
在第一个bottleneck结构中,由于t=1,所以并没有进行升维操作,即没有 第一个Conv2D层。
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchsummary import summary# 定义一个辅助函数,用于确保通道数可以被 divisor 整除,这对于硬件优化很重要
def _make_divisible(v, divisor, min_value=None):"""确保通道数 v 可以被 divisor 整除,如果需要可以指定最小值 min_value。这个技巧在移动端神经网络设计中常用,有助于硬件加速。"""if min_value is None:min_value = divisornew_v = max(min_value, int(v + divisor / 2) // divisor * divisor)# 为了避免向下取整过多导致信息损失,进行一个小的调整if new_v < 0.9 * v:new_v += divisorreturn new_v# 定义一个卷积 + BatchNorm + ReLU6 的基本块
class ConvBNReLU(nn.Sequential):def __init__(self, in_planes, out_planes, kernel_size, stride, groups=1):"""卷积、批归一化和 ReLU6 激活函数的组合。groups 参数用于实现分组卷积(当 groups > 1 时)或深度可分离卷积(当 groups 等于输入通道数时)。"""padding = (kernel_size - 1) // 2super(ConvBNReLU, self).__init__(nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False),nn.BatchNorm2d(out_planes),nn.ReLU6(inplace=True) # ReLU6 激活函数,限制输出范围在 [0, 6])# 定义 MobileNetV2 的核心模块:倒置残差块 (Inverted Residual Block)
class InvertedResidual(nn.Module):def __init__(self, inp, oup, stride, expand_ratio):"""倒置残差块是 MobileNetV2 的基本构建单元。它首先通过一个扩展层增加通道数,然后进行深度可分离卷积,最后通过一个投影层减少通道数。如果输入和输出的形状相同且步长为 1,则使用残差连接。Args:inp (int): 输入通道数oup (int): 输出通道数stride (int): 卷积步长 (1 或 2)expand_ratio (int): 扩展率,中间层的通道数是输入通道数的 expand_ratio 倍"""super(InvertedResidual, self).__init__()self.stride = strideassert stride in [1, 2]hidden_dim = int(round(inp * expand_ratio)) # 中间扩展层的通道数self.use_res_connect = self.stride == 1 and inp == oup # 判断是否使用残差连接layers = []if expand_ratio != 1:# 扩展层:1x1 卷积增加通道数layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1, stride=1))layers.extend([# 深度可分离卷积:一个深度卷积和一个逐点卷积的组合# 深度卷积 (Depthwise Convolution): 每个输入通道应用一个独立的卷积核ConvBNReLU(hidden_dim, hidden_dim, kernel_size=3, stride=stride, groups=hidden_dim),# 逐点卷积 (Pointwise Convolution): 1x1 卷积用于线性地组合深度卷积的输出nn.Conv2d(hidden_dim, oup, kernel_size=1, stride=1, bias=False),nn.BatchNorm2d(oup),])self.conv = nn.Sequential(*layers)def forward(self, x):if self.use_res_connect:return x + self.conv(x) # 如果满足条件,使用残差连接else:return self.conv(x)# 定义 MobileNetV2 网络结构
class MobileNetV2(nn.Module):def __init__(self, num_classes=1000, width_mult=1.0, inverted_residual_setting=None, round_nearest=8):"""MobileNet V2 的主体网络结构。Args:num_classes (int): 分类的类别数width_mult (float): 宽度乘数,用于调整网络中所有层的通道数,控制模型大小inverted_residual_setting (list of list): 倒置残差块的配置列表,每个子列表包含 [t, c, n, s],分别表示扩展率 (t),输出通道数 (c),重复次数 (n),步长 (s)。round_nearest (int): 将通道数调整为最接近的 round_nearest 的倍数,有助于硬件优化。"""super(MobileNetV2, self).__init__()if inverted_residual_setting is None:# 默认的 MobileNetV2 结构参数input_channel = 32last_channel = 1280inverted_residual_setting = [# t, c, n, s[1, 16, 1, 1],[6, 24, 2, 2],[6, 32, 3, 2],[6, 64, 4, 2],[6, 96, 3, 1],[6, 160, 3, 2],[6, 320, 1, 1],]else:# 如果提供了自定义的 inverted_residual_setting,则使用它if len(inverted_residual_setting) != 7:raise ValueError("inverted_residual_setting 应该包含 7 个层级的配置")# 构建网络的第一个卷积层input_channel = _make_divisible(input_channel * width_mult, round_nearest)self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest)features = [ConvBNReLU(3, input_channel, kernel_size=3, stride=2)] # 输入通道数为 3 (RGB),输出通道数为调整后的 input_channel,步长为 2# 构建倒置残差块for t, c, n, s in inverted_residual_setting:output_channel = _make_divisible(c * width_mult, round_nearest)for i in range(n):stride = s if i == 0 else 1 # 只有每个模块的第一个残差块使用指定的步长,其余步长都为 1features.append(InvertedResidual(input_channel, output_channel, stride, expand_ratio=t))input_channel = output_channel # 更新下一个残差块的输入通道数# 构建最后一个卷积层features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1, stride=1))# 将特征提取层封装成 Sequential 容器self.features = nn.Sequential(*features)# 构建分类器self.classifier = nn.Sequential(nn.Dropout(0.2), # Dropout 正则化,防止过拟合nn.Linear(self.last_channel, num_classes), # 全连接层,将特征映射到类别数)# 初始化权重for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode='fan_out') # 使用 Kaiming 正态分布初始化卷积层权重if m.bias is not None:nn.init.zeros_(m.bias) # 如果有偏置,初始化为 0elif isinstance(m, nn.BatchNorm2d):nn.init.ones_(m.weight) # BatchNorm 的权重初始化为 1nn.init.zeros_(m.bias) # BatchNorm 的偏置初始化为 0elif isinstance(m, nn.Linear):nn.init.normal_(m.weight, 0, 0.01) # 全连接层的权重使用均值为 0,标准差为 0.01 的正态分布初始化nn.init.zeros_(m.bias) # 全连接层的偏置初始化为 0def _forward_impl(self, x):# 前向传播的实现x = self.features(x) # 通过特征提取层x = F.adaptive_avg_pool2d(x, 1) # 自适应平均池化,将特征图尺寸变为 1x1x = torch.flatten(x, 1) # 将特征图展平成一维向量x = self.classifier(x) # 通过分类器return xdef forward(self, x):# 定义前向传播函数return self._forward_impl(x)if __name__ == '__main__':model = MobileNetV2()print(summary(model,(3,224,224)))
----------------------------------------------------------------Layer (type) Output Shape Param #
================================================================Conv2d-1 [-1, 32, 112, 112] 864BatchNorm2d-2 [-1, 32, 112, 112] 64ReLU6-3 [-1, 32, 112, 112] 0Conv2d-4 [-1, 32, 112, 112] 288BatchNorm2d-5 [-1, 32, 112, 112] 64ReLU6-6 [-1, 32, 112, 112] 0Conv2d-7 [-1, 16, 112, 112] 512BatchNorm2d-8 [-1, 16, 112, 112] 32InvertedResidual-9 [-1, 16, 112, 112] 0Conv2d-10 [-1, 96, 112, 112] 1,536BatchNorm2d-11 [-1, 96, 112, 112] 192ReLU6-12 [-1, 96, 112, 112] 0Conv2d-13 [-1, 96, 56, 56] 864BatchNorm2d-14 [-1, 96, 56, 56] 192ReLU6-15 [-1, 96, 56, 56] 0Conv2d-16 [-1, 24, 56, 56] 2,304BatchNorm2d-17 [-1, 24, 56, 56] 48InvertedResidual-18 [-1, 24, 56, 56] 0Conv2d-19 [-1, 144, 56, 56] 3,456BatchNorm2d-20 [-1, 144, 56, 56] 288ReLU6-21 [-1, 144, 56, 56] 0Conv2d-22 [-1, 144, 56, 56] 1,296BatchNorm2d-23 [-1, 144, 56, 56] 288ReLU6-24 [-1, 144, 56, 56] 0Conv2d-25 [-1, 24, 56, 56] 3,456BatchNorm2d-26 [-1, 24, 56, 56] 48InvertedResidual-27 [-1, 24, 56, 56] 0Conv2d-28 [-1, 144, 56, 56] 3,456BatchNorm2d-29 [-1, 144, 56, 56] 288ReLU6-30 [-1, 144, 56, 56] 0Conv2d-31 [-1, 144, 28, 28] 1,296BatchNorm2d-32 [-1, 144, 28, 28] 288ReLU6-33 [-1, 144, 28, 28] 0Conv2d-34 [-1, 32, 28, 28] 4,608BatchNorm2d-35 [-1, 32, 28, 28] 64InvertedResidual-36 [-1, 32, 28, 28] 0Conv2d-37 [-1, 192, 28, 28] 6,144BatchNorm2d-38 [-1, 192, 28, 28] 384ReLU6-39 [-1, 192, 28, 28] 0Conv2d-40 [-1, 192, 28, 28] 1,728BatchNorm2d-41 [-1, 192, 28, 28] 384ReLU6-42 [-1, 192, 28, 28] 0Conv2d-43 [-1, 32, 28, 28] 6,144BatchNorm2d-44 [-1, 32, 28, 28] 64InvertedResidual-45 [-1, 32, 28, 28] 0Conv2d-46 [-1, 192, 28, 28] 6,144BatchNorm2d-47 [-1, 192, 28, 28] 384ReLU6-48 [-1, 192, 28, 28] 0Conv2d-49 [-1, 192, 28, 28] 1,728BatchNorm2d-50 [-1, 192, 28, 28] 384ReLU6-51 [-1, 192, 28, 28] 0Conv2d-52 [-1, 32, 28, 28] 6,144BatchNorm2d-53 [-1, 32, 28, 28] 64InvertedResidual-54 [-1, 32, 28, 28] 0Conv2d-55 [-1, 192, 28, 28] 6,144BatchNorm2d-56 [-1, 192, 28, 28] 384ReLU6-57 [-1, 192, 28, 28] 0Conv2d-58 [-1, 192, 14, 14] 1,728BatchNorm2d-59 [-1, 192, 14, 14] 384ReLU6-60 [-1, 192, 14, 14] 0Conv2d-61 [-1, 64, 14, 14] 12,288BatchNorm2d-62 [-1, 64, 14, 14] 128InvertedResidual-63 [-1, 64, 14, 14] 0Conv2d-64 [-1, 384, 14, 14] 24,576BatchNorm2d-65 [-1, 384, 14, 14] 768ReLU6-66 [-1, 384, 14, 14] 0Conv2d-67 [-1, 384, 14, 14] 3,456BatchNorm2d-68 [-1, 384, 14, 14] 768ReLU6-69 [-1, 384, 14, 14] 0Conv2d-70 [-1, 64, 14, 14] 24,576BatchNorm2d-71 [-1, 64, 14, 14] 128InvertedResidual-72 [-1, 64, 14, 14] 0Conv2d-73 [-1, 384, 14, 14] 24,576BatchNorm2d-74 [-1, 384, 14, 14] 768ReLU6-75 [-1, 384, 14, 14] 0Conv2d-76 [-1, 384, 14, 14] 3,456BatchNorm2d-77 [-1, 384, 14, 14] 768ReLU6-78 [-1, 384, 14, 14] 0Conv2d-79 [-1, 64, 14, 14] 24,576BatchNorm2d-80 [-1, 64, 14, 14] 128InvertedResidual-81 [-1, 64, 14, 14] 0Conv2d-82 [-1, 384, 14, 14] 24,576BatchNorm2d-83 [-1, 384, 14, 14] 768ReLU6-84 [-1, 384, 14, 14] 0Conv2d-85 [-1, 384, 14, 14] 3,456BatchNorm2d-86 [-1, 384, 14, 14] 768ReLU6-87 [-1, 384, 14, 14] 0Conv2d-88 [-1, 64, 14, 14] 24,576BatchNorm2d-89 [-1, 64, 14, 14] 128InvertedResidual-90 [-1, 64, 14, 14] 0Conv2d-91 [-1, 384, 14, 14] 24,576BatchNorm2d-92 [-1, 384, 14, 14] 768ReLU6-93 [-1, 384, 14, 14] 0Conv2d-94 [-1, 384, 14, 14] 3,456BatchNorm2d-95 [-1, 384, 14, 14] 768ReLU6-96 [-1, 384, 14, 14] 0Conv2d-97 [-1, 96, 14, 14] 36,864BatchNorm2d-98 [-1, 96, 14, 14] 192InvertedResidual-99 [-1, 96, 14, 14] 0Conv2d-100 [-1, 576, 14, 14] 55,296BatchNorm2d-101 [-1, 576, 14, 14] 1,152ReLU6-102 [-1, 576, 14, 14] 0Conv2d-103 [-1, 576, 14, 14] 5,184BatchNorm2d-104 [-1, 576, 14, 14] 1,152ReLU6-105 [-1, 576, 14, 14] 0Conv2d-106 [-1, 96, 14, 14] 55,296BatchNorm2d-107 [-1, 96, 14, 14] 192
InvertedResidual-108 [-1, 96, 14, 14] 0Conv2d-109 [-1, 576, 14, 14] 55,296BatchNorm2d-110 [-1, 576, 14, 14] 1,152ReLU6-111 [-1, 576, 14, 14] 0Conv2d-112 [-1, 576, 14, 14] 5,184BatchNorm2d-113 [-1, 576, 14, 14] 1,152ReLU6-114 [-1, 576, 14, 14] 0Conv2d-115 [-1, 96, 14, 14] 55,296BatchNorm2d-116 [-1, 96, 14, 14] 192
InvertedResidual-117 [-1, 96, 14, 14] 0Conv2d-118 [-1, 576, 14, 14] 55,296BatchNorm2d-119 [-1, 576, 14, 14] 1,152ReLU6-120 [-1, 576, 14, 14] 0Conv2d-121 [-1, 576, 7, 7] 5,184BatchNorm2d-122 [-1, 576, 7, 7] 1,152ReLU6-123 [-1, 576, 7, 7] 0Conv2d-124 [-1, 160, 7, 7] 92,160BatchNorm2d-125 [-1, 160, 7, 7] 320
InvertedResidual-126 [-1, 160, 7, 7] 0Conv2d-127 [-1, 960, 7, 7] 153,600BatchNorm2d-128 [-1, 960, 7, 7] 1,920ReLU6-129 [-1, 960, 7, 7] 0Conv2d-130 [-1, 960, 7, 7] 8,640BatchNorm2d-131 [-1, 960, 7, 7] 1,920ReLU6-132 [-1, 960, 7, 7] 0Conv2d-133 [-1, 160, 7, 7] 153,600BatchNorm2d-134 [-1, 160, 7, 7] 320
InvertedResidual-135 [-1, 160, 7, 7] 0Conv2d-136 [-1, 960, 7, 7] 153,600BatchNorm2d-137 [-1, 960, 7, 7] 1,920ReLU6-138 [-1, 960, 7, 7] 0Conv2d-139 [-1, 960, 7, 7] 8,640BatchNorm2d-140 [-1, 960, 7, 7] 1,920ReLU6-141 [-1, 960, 7, 7] 0Conv2d-142 [-1, 160, 7, 7] 153,600BatchNorm2d-143 [-1, 160, 7, 7] 320
InvertedResidual-144 [-1, 160, 7, 7] 0Conv2d-145 [-1, 960, 7, 7] 153,600BatchNorm2d-146 [-1, 960, 7, 7] 1,920ReLU6-147 [-1, 960, 7, 7] 0Conv2d-148 [-1, 960, 7, 7] 8,640BatchNorm2d-149 [-1, 960, 7, 7] 1,920ReLU6-150 [-1, 960, 7, 7] 0Conv2d-151 [-1, 320, 7, 7] 307,200BatchNorm2d-152 [-1, 320, 7, 7] 640
InvertedResidual-153 [-1, 320, 7, 7] 0Conv2d-154 [-1, 1280, 7, 7] 409,600BatchNorm2d-155 [-1, 1280, 7, 7] 2,560ReLU6-156 [-1, 1280, 7, 7] 0Dropout-157 [-1, 1280] 0Linear-158 [-1, 1000] 1,281,000
================================================================
Total params: 3,504,872
Trainable params: 3,504,872
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 152.87
Params size (MB): 13.37
Estimated Total Size (MB): 166.81
----------------------------------------------------------------
相关文章:

Python----神经网络(《Inverted Residuals and Linear Bottlenecks》论文概括和MobileNetV2网络)
一、论文 MobileNetV2 论文提出了一种新的移动架构,该架构提高了移动模型在多个任务和基准测试中的性能,以及在各种不同模型大小范围内的性能. 该架构基于倒残差结构,其中 shortcut 连接在 thin bottleneck 层之间. 中间的 expansion 层使用轻…...

React Flow 简介:构建交互式流程图的最佳工具
本文为《React Agent:从零开始构建 AI 智能体》专栏系列文章。 专栏地址:https://blog.csdn.net/suiyingy/category_12933485.html。项目地址:https://gitee.com/fgai/react-agent(含完整代码示例与实战源)。完整介绍…...

Jupyter-AI Pandas-AI本地使用功能优化
引言 Jupyter-ai 和 Pandas-ai 的优化主要是个人工作遇到的需求,个人觉得是一个不错的体验优化,所以进行分享仅供参考,不喜勿喷,共同进步!Jupyter-AI优化主要包含以下方向(当前已实现): Jupyter-AI中 Chat 扩展和 NoteBook 的 Cell 工作去部分,使用的Language Model 和 …...
安全版4.5.8开启审计后,hac+读写分离主备切换异常
文章目录 环境BUG/漏洞编码症状触发条件解决方案 环境 系统平台:UOS (飞腾) 版本:4.5.8 BUG/漏洞编码 3043 症状 BUG安装包: hgdb-see-4.5.8-db43858.aarch64.rpm 异常:hac集群一主两备环境ÿ…...

WEB安全--Java安全--shiro550反序列化漏洞
一、前言 什么是shiro? shiro是一个Apache的Java安全框架 它的作用是什么? Apache Shiro 是一个强大且灵活的 Java 安全框架,用于处理身份验证、授权、密码管理以及会话管理等功能 二、shiro550反序列化原理 1、用户首次登录并勾选记住密码…...

【 Redis | 实战篇 秒杀实现 】
目录 前言: 1.全局ID生成器 2.秒杀优惠券 2.1.秒杀优惠券的基本实现 2.2.超卖问题 2.3.解决超卖问题的方案 2.4.基于乐观锁来解决超卖问题 3.秒杀一人一单 3.1.秒杀一人一单的基本实现 3.2.单机模式下的线程安全问题 3.3.集群模式下的线程安全问题 前言&…...

数据通信原理 光纤通信 期末速成
一、图表题 1. 双极性不归零、单极性不归零、曼彻斯特码、抑制载频2ASK,2PSK、2DPSK信号的波形 双极性不归零 和 单极性不归零:不归零意思是 0 低 1 高 非归零编码(NRZ):用不同电平表示二进制数字,常以…...

华为云kubernetes容器相关组件及作用
Kubernetes组件按功能分为控制平面组件、工作节点组件及扩展插件,协同实现容器化应用的编排与管理。 一、控制平面组件(Control Plane) 1、kube-apiserver 作用:提供集群API入口,处理所有REST请…...

安全与智能的双向奔赴,安恒信息先行一步
人类文明发展的长河中,每一次技术变革都重新书写了安全的定义。 从蒸汽机的轰鸣到电力的普及,从互联网的诞生到人工智能的崛起,技术创新与变革从未停止对于安全的挑战。今天,我们又站在一个关键的历史节点:AI大模型的…...
Node.js中的URL模块
一、URL 模块基础 1. 模块导入方式 模块导入方式还是根据代码规范使用require或者import来引入模块。 // Node.js 方式 const url require(url);// ES 模块方式 (Node.js 14 或启用 ESM) import * as url from url; 2. 核心功能 解析 URL 字符串 格式化 URL 对象 URL 处…...
2025 Java 微信小程序根据code获取openid,二次code获取手机号【工具类】拿来就用
一、controller调用 /*** 登录** author jiaketao* since 2024-04-10*/ RestController RequestMapping("/login") public class LoginController {/*** 【小程序】登录获取session_key和openid** param code 前端传code* return*/GetMapping("/getWXSessionKe…...

window 显示驱动开发-分页视频内存资源
与 Microsoft Windows 2000 显示驱动程序模型不同,Windows Vista 显示驱动程序模型允许创建比可用物理视频内存总量更多的视频内存资源,然后根据需要分页进出视频内存。 换句话说,并非所有视频内存资源都同时位于视频内存中。 GPU 的管道中可…...

【笔记】记一次PyCharm的问题反馈
#工作记录 最近更新至 PyCharm 社区版的最新版本后,我遇到了多个影响使用体验的问题。令人感到不便的是,一些在旧版本中非常便捷的功能,在新版本中却变得操作复杂、不够直观。过去,我一直通过 PyCharm 内置的故障报告与反馈机制反…...

uniapp中vue3和pinia安装依赖npm install失败
目录 一、问题描述 二、问题原因 三、问题解析及解决方案 一、问题描述 用uni-app开发小程序的时候,使用了vue3pinia,安装依赖的时候发现vue和pinia的版本问题,安装失败, npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve np…...
MySQL 8.0 OCP 1Z0-908 131-140题
Q131.You have upgraded the MySQL binaries from 5.7.28 to 8.0.18 by using an in-place upgrade. Examine the message sequence generated during the first start of MySQL 8.0.18: 。。。[System]。。。/usx/sbin/mysqld (mysqld 8.0.18-commercial) starting as process…...
Spring-messaging-Message接口/环境依赖
参考文档1:https://docs.spring.io/spring-integration/reference/index.html 参考文档2:https://www.jackssybin.cn/articles/2021/03/16/1615897840354.html#b3_solo_h4_44 环境配置 由于我使用的是spring boot,所以只有一个依赖…...
WPF自定义控件开发全指南:多内容切换与动画集成
WPF自定义控件开发全指南:多内容切换与动画集成 一、控件基础架构设计1.1 选择控件基类1.2 定义关键属性 二、动画系统集成2.1 淡入淡出动画实现2.2 滑动动画实现 三、视觉状态管理四、完整使用示例4.1 XAML声明4.2 动画触发逻辑 五、扩展与优化5.1 性能优化建议5.2…...
ECMAScript标准:JavaScript的核心
什么是ECMAScript? ECMAScript(简称ES)是一个由ECMA国际(欧洲计算机制造商协会)制定的脚本语言标准,它为JavaScript、JScript和ActionScript等脚本语言提供了基础规范。JavaScript 可以视为 ECMAScript 的…...
qtc++ qdebug日志生成
本文介绍了将qdebug注册到日志系统,这样qdebug打印的信息将记录在日志文本文件,方便观看程序运行中的历史信息,但是需要注意的是,注册后qdebug的信息将不会打印在qtcreator的输出中,所以作者建议,在开发的时…...
【分布式锁通关指南 10】源码剖析redisson之MultiLock的实现
引言 本期我们将把目光聚焦在 Redisson 中另一个颇具代表性的分布式锁实现——MultiLock。它的核心思想是:一次性对多个独立的 RLock 进行加锁或解锁操作,只有当多个锁都成功加锁时才算真正完成锁的获取,一旦有任何一个失败,整体操…...

DBF Converter:高效转换DBF文件,满足多样化数据处理需求
DBF Converter 是一款功能强大的数据转换工具,专为需要将DBF文件转换为其他格式的用户设计。它支持将DBF文件转换为CSV、Excel、HTML、SQL等多种常见格式,满足用户在不同场景下的数据处理需求。无论是数据迁移、报表生成还是日常数据处理,DBF…...
Java—— 方法引用 : :
方法引用是什么 把已经存在的方法拿过来用,当做函数式接口中抽象方法的方法体 方法引用符 :: 方法引用的条件 1.需要有函数式接口 2.被引用方法必须已经存在 3.被引用方法的形参和返回值需要跟抽象方法保持一致 4.被引用方法的功能要满足当前…...

Jmeter 安装包与界面汉化
Jmeter 安装包: 通过网盘分享的文件:CSDN-apache-jmeter-5.5 链接: https://pan.baidu.com/s/17gK98NxS19oKmkdRhGepBA?pwd1234 提取码: 1234 Jmeter界面汉化:...
6 任务路由与负载均衡
一、任务路由核心机制 1.1 静态路由配置 # celeryconfig.pytask_routes {# 精确匹配任务路径payment.process_order: {queue: priority_payment},# 通配符匹配任务类型report.*: {queue: low_priority_reports},# 正则表达式匹配re.compile(r^video\.(encode|compress)): {q…...

【C++】 —— 笔试刷题day_29
一、排序子序列 题目解析 一个数组的连续子序列,如果这个子序列是非递增或者非递减的;这个连续的子序列就是排序子序列。 现在给定一个数组,然后然我们判断这个子序列可以划分成多少个排序子序列。 例如:1 2 3 2 2 1 可以划分成 …...
Ruby 循环与迭代器
Ruby 循环与迭代器 循环迭代器timesuptostep 循环 。。。。 迭代器 迭代器本质上可以理解为是循环的一种类型 times 3.times do print "Ho! " end begin Ho! Ho! Ho! end上述代码表示我们对当前 block 部分中的内容循环三次。最终,我们打印出了三个…...
力扣-39.组合总和
题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一个 数字可以 无限制重复被…...
优化 Element UI 表格样式,隐藏滚动条但保持滚动功能
优化 Element UI 表格样式,隐藏滚动条但保持滚动功能 前言 在基于 Element UI 的项目中,el-table 是非常常用的表格组件。默认情况下,表格的滚动条可能影响页面的美观,特别是在视觉设计上希望更简洁时。本文分享一段优化的 CSS …...
线程池(ThreadPoolExecutor)实现原理和源码细节是Java高并发面试和实战开发的重点
一、线程池核心流程图 ----------------- | 提交任务 | submit/execute -----------------|v ----------------- | 判断核心线程数 | < corePoolSize? -----------------|Yes |Nov v [创建新线程] -----------------| 队列是否满&a…...

MongoTemplate 基础使用帮助手册
前言 MongoDB 是一种流行的 NoSQL 数据库,适合存储大量的非结构化数据。MongoTemplate 是 Spring Data MongoDB 中的一个核心组件,它提供了一组丰富的 API 来与 MongoDB 进行交互。它封装了许多常见的数据库操作,使开发者能够轻松执行 CRUD 操…...