【项目实践】EfficientDet原理讲解与目标检测项目实践(一)

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: 【项目实践】EfficientDet原理讲解与目标检测项目实践(一)

1、EfficientDet简介


   在简介部分,作者提出了 “鱼与熊掌俺能兼得乎?”,要知道在此之前,实际目标检测算法家族已经提出了很多很多经典的算法,有two-stage方法的,主要是早期的一些算法,如Fast R-CNN、Faster R-CNN,一般检测精度较高但速度慢,为了加快速度,后来逐步发展为one-stage,从RoI的提取到识别检测全部融合在一个框架下,实现 end to end,加快检测速度,但一般是以牺牲精度换速度的。


   因此在FPN及EfficientNet 的影响下,作者分别基于此在 FPN 基础上进行优化提出BiFPN以及全方位的模型缩放探索。


作者提出两个方法:

BiFPN: 这个毋庸置疑,肯定是从 FPN 发展过来的,至于 Bi 就是双向,原始的FPN实现的自顶向下(top-down)融合,所谓的BiFPN就是两条路线既有top-down也有down-top。

在融合过程中,之前的一些模型方法没有考虑到各级特征对融合后特征的g共享度问题,即之前模型认为各级特征的贡献度相同,而本文作者认为它们的分辨率不同,其对融合后特征的贡献度不同,因此在特征融合阶段引入了weight。


复合缩放方法(compound scaling method):这个主要灵感来自于 EfficientNet,即在基线网络上同时对多个维度进行缩放(一般都是放大),这里的维度体现在主干网络、特征网络、以及分类/回归网络全流程的整体架构上整体网络由主干网络、特征网络以及分类/回归网络组成,可以缩放的维度比 EfficientNet 多得多,所以用网络搜索方式不合适了,作者提出一些启发式方法,可以参照论文的 Table 1。


   该方法可以统一地对所有主干网、特征网络和预测网络的分辨率、深度和宽度进行缩放。基于这些优化,开发了一个新的对象检测器家族,称为EfficientDet。


2、EfficientDet原理与PyTorch实现


2.1、EfficientNet模型

2.1.1、EfficientNet简介

image.png

   模型的基础网络架构是通过使用神经网络架构搜索(neural architecture search)设计得到。为了研究系统的模型缩放,谷歌大脑的研究人员针对EfficientNets的基础网络模型提出了一种全新的模型缩放方法,该方法使用简单而高效的复合系数来权衡网络深度、宽度和输入图片分辨率。


   通过放大EfficientNets基础模型,获得了一系列EfficientNets模型。该系列模型在效率和准确性上战胜了之前所有的卷积神经网络模型。尤其是EfficientNet-B7在ImageNet数据集上得到了top-1准确率84.4%和top-5准确率97.1%的结果。且它和当时准确率最高的其它模型对比,大小缩小了8.4倍,效率提高了6.1倍。且通过迁移学习,EfficientNets在多个知名数据集上均达到了当时最先进的水平。


2.1.2、EfficientNet网络模型结构


1)移动翻转瓶颈卷积

   移动翻转瓶颈卷积也是通过神经网络架构搜索得到的,该模块结构与深度分离卷积(depthwise separable convolution)相似,该移动翻转瓶颈卷积首先对输入进行1x1的逐点卷积并根据扩展比例(expand ratio)改变输出通道维度(如扩展比例为3时,会将通道维度提升3倍。但如果扩展比例为1,则直接省略该1x1的逐点卷积和其之后批归一化和激活函数)。


   接着进行kxk的深度卷积(depthwise convolution)。如果要引入压缩与激发操作,该操作会在深度卷积后进行。再以1x1的逐点卷积结尾恢复原通道维度。


   最后进行连接失活(drop connect)和输入的跳越连接(skip connection),这一做法源于论文《Deep networks with stochastic depth》,它让模型具有了随机的深度,剪短了模型训练所需的时间,提升了模型性能(注意,在EfficientNets中,只有当相同的移动翻转瓶颈卷积重复出现时,才会进行连接失活和输入的跳越连接,且还会将其中的深度卷积步长变为1),连接失活是一种类似于随机失活(dropout)的操作,并且在模块的开始和结束加入了恒等跳越。注意该模块中的每一个卷积操作后都会进行批归一化,激活函数使用的是Swish激活函数。总流程如图1所示,是扩展比例为6,深度卷积大小为5x5,步长为2x2(MBConv6,k5x5,stride2x2)的移动翻转瓶颈卷积模块。

image.png

图 MBConv6,k5x5,stride2x2结构示意图

PyTorch实现MBConv模块:

class MBConvBlock(nn.Module):
    """
    Mobile Inverted Residual Bottleneck 模块
    Args:
        block_args (namedtuple): 模型模块参数
        global_params (namedtuple): 全局参数
    Attributes:
        has_se (bool): 是否存在SENet
    """
    def __init__(self, block_args, global_params):
        super().__init__()
        self._block_args = block_args    # EfficientNet全局参数
        self._bn_mom = 1 - global_params.batch_norm_momentum    # 训练时的动量参数
        self._bn_eps = global_params.batch_norm_epsilon    # BN的参数
        self.has_se = (self._block_args.se_ratio is not None) and (0 < self._block_args.se_ratio <= 1)
        self.id_skip = block_args.id_skip  # 连接失活和跳跃连接
        # 恒等宽高卷积操作
        Conv2d = get_same_padding_conv2d(image_size=global_params.image_size)
        # 通道拓展操作
        inp = self._block_args.input_filters  # number of input channels
        oup = self._block_args.input_filters * self._block_args.expand_ratio  # number of output channels
        if self._block_args.expand_ratio != 1:
            self._expand_conv = Conv2d(in_channels=inp, out_channels=oup, kernel_size=1, bias=False)
            self._bn0 = nn.BatchNorm2d(num_features=oup, momentum=self._bn_mom, eps=self._bn_eps)
        # 深度卷积操作
        k = self._block_args.kernel_size
        s = self._block_args.stride
        # groups用于设置depthwise卷积(一个卷积核负责一个通道)
        self._depthwise_conv = Conv2d(in_channels=oup, out_channels=oup, groups=oup,  kernel_size=k, stride=s, bias=False)
        self._bn1 = nn.BatchNorm2d(num_features=oup, momentum=self._bn_mom, eps=self._bn_eps)
        # SENet模块
        if self.has_se:
            num_squeezed_channels = max(1, int(self._block_args.input_filters * self._block_args.se_ratio))
            self._se_reduce = Conv2d(in_channels=oup, out_channels=num_squeezed_channels, kernel_size=1)
            self._se_expand = Conv2d(in_channels=num_squeezed_channels, out_channels=oup, kernel_size=1)
        # 最终输出模块
        final_oup = self._block_args.output_filters
        self._project_conv = Conv2d(in_channels=oup, out_channels=final_oup, kernel_size=1, bias=False)
        self._bn2 = nn.BatchNorm2d(num_features=final_oup, momentum=self._bn_mom, eps=self._bn_eps)
        self._swish = MemoryEfficientSwish()
    def forward(self, inputs, drop_connect_rate=None):
        """
        MBConv模块的流程:
            0、输入
            1、深度卷积操作
            2、批归一化操作
            3、Swish激活函数操作
            4、深度卷积操作
            5、批归一化操作
            6、Swish激活函数操作
            7、SENet操作
            8、Depthwise Convolution操作
            9、批归一化操作
            10、连接失活和跳越连接操作
            11、输出
        """
        # 0、输入
        x = inputs
        # 1+2+3、深度卷积+批归一化+Swish操作
        if self._block_args.expand_ratio != 1:
            x = self._swish(self._bn0(self._expand_conv(inputs)))
        # 4+5+6、Depthwise Convolution+批归一化+Swish操作
        x = self._swish(self._bn1(self._depthwise_conv(x)))
        # 7、SENet操作
        if self.has_se:
            x_squeezed = F.adaptive_avg_pool2d(x, 1)
            x_squeezed = self._se_expand(self._swish(self._se_reduce(x_squeezed)))
            x = torch.sigmoid(x_squeezed) * x
        # 8+9、批归一化+Depthwise Convolution
        x = self._bn2(self._project_conv(x))
        # 10、连接失活和跳越连接操作
        input_filters, output_filters = self._block_args.input_filters, self._block_args.output_filters
        if self.id_skip and self._block_args.stride == 1 and input_filters == output_filters:
            if drop_connect_rate:
                x = drop_connect(x, p=drop_connect_rate, training=self.training)    # 连接失活
            x = x + inputs  # 跳越连接操作
        # 11、输出
        return x

2)EfficientNet-B0结构说明

image.png

EfficientNet-B0结构由16个移动翻转瓶颈卷积模块,2个卷积层,1个全局平均池化层和1个分类层构成。其结构如图所示,图中不同的颜色代表了不同的阶段。

image.png

图 EfficientNet-B0结构图


第一阶段对输入的224x224x3的图像按顺序进行以下操作得到第一阶段的结果:


   1) 卷积(卷积核为32核3×3×3,步长为2×2,填充为“same”即输出的宽和高缩小一半),该卷积运算的输出是一个维度为(112×112×32)的特征图。因该层不含偏置项,故该层需要训练学习的参数共计864(32x3x3x3)个。


   2) 批归一化层(Batch Normalization,BN),该层输入为(112×112×32)的特征图,故该层含参数总数为128个(32x4),其中需要训练学习的参数为64个。


   3) Swish激活函数


第一阶段,总计参数128+864=992个,需要训练学习的参数928个。


第二阶段对前一阶段输出的112x112x32的特征图进行移动翻转瓶颈卷积

   一次移动翻转瓶颈卷积(扩张比例为1,深度卷积核大小为3x3,核步长为1x1,包含压缩与激发操作,无连接失活和连接跳越),并输出第二阶段的结果:


   1) 由于扩张比例为1,故跳过一开始的逐点卷积,直接进行深度卷积(卷积核为32核3×3×3,步长为1×1,填充为“same”即输出的宽和高不变)。深度卷积输出是一个维度为(112×112×32)的特征图。因该层不含偏置项,故该层需要训练学习的参数共计288(32x3x3x1)个。


   2) 批归一化层(Batch Normalization,BN),该层输入为(112×112×32)的特征图,故该层含参数总数为128个(32x4),其中需要训练学习的参数为64个。


   3) Swish激活函数。


   4) 全局平均池化层(global average pooling),该层在通道维度方向上进行全局平均池化,输出为(1x1x32)的特征图。


   5) 卷积(压缩与激发模块中的第一个卷积,卷积核为8核1x1x32,步长为1×1,填充为“same”即输出的宽和高不变),该卷积运算的输出是一个维度为(1×1×8)的特征图。因该层包含偏置项,故该层需要训练学习的参数共计264(8x1x1x32+8)个。


   6) Swish激活函数。


   7) 卷积(压缩与激发模块中的第二个卷积,卷积核为32核1x1x8,步长为1×1,填充为“same”即输出的宽和高不变),该卷积运算的的输出是一个维度为(1×1×32)的特征图。因该层包含偏置项,故该层需要训练学习的参数共计288(32x1x1x8+32)个。


   8) Sigmoid激活函数


   9) 与步骤3)的结果相乘,得到112x112x32的特征图。


   10) 逐点卷积(卷积核为16核1×1×32,步长为1×1,填充为“same”即输出的宽和高不变)该卷积运算的输出是一个维度为(112×112×16)的特征图。因该层不含偏置项,故该层需要训练学习的参数共计512(16x1x1x32)个。


   11) 批归一化层(Batch Normalization,BN),该层输入为(112×112×16)的特征图,故该层含参数总数为64个(16x4),其中需要训练学习的参数为32个。


第二阶段,总计参数288+128+264+288+512+64=1544个,需要训练学习的参数1448个。


第三阶段对前一阶段输出的112x112x16的特征图进行两次移动翻转瓶颈卷积

   第一个(扩张比例为6,深度卷积核大小为3x3,核步长为2x2,包含压缩与激发操作,无连接失活核连接跳越);


   第二个(扩张比例为6,深度卷积核大小为3x3,核步长为1x1,包含压缩与激发操作,有连接失活和连接跳越),并输出第二阶段的结果;


   第三阶段,总计参数17770个,需要训练学习的参数16705个。


第四阶段对前一阶段输出的56x56x24的特征图进行两次移动翻转瓶颈卷积

   第一个(扩张比例为6,深度卷积核大小为5x5,核步长为2x2,包含压缩与激发操作,无连接失活核连接跳越);


   第二个(扩张比例为6,深度卷积核大小为5x5,核步长为1x1,包含压缩与激发操作,有连接失活和连接跳越);


   输出是一个28x28x40的特征图。总计参数48336个,需要训练学习的参数46640个。


第五阶段对前一阶段输出的28x28x40的特征图进行三次移动翻转瓶颈卷积

   第一个(扩张比例为6,深度卷积核大小为3x3,核步长为2x2,包含压缩与激发操作,无连接失活核连接跳越);


   第二个(扩张比例为6,深度卷积核大小为3x3,核步长为1x1,包含压缩与激发操作,有连接失活核连接跳越);


   第三个(扩张比例为6,深度卷积核大小为3x3,核步长为1x1,包含压缩与激发操作,有连接失活核连接跳越);


   输出是一个14x14x80的特征图。总计参数248210个,需要训练学习的参数242930个。


第六阶段对前一阶段输出的14x14x80的特征图进行三次移动翻转瓶颈卷积

   第一个(扩张比例为6,深度卷积核大小为5x5,核步长为1x1,包含压缩与激发操作,无连接失活核连接跳越);


   第二个(扩张比例为6,深度卷积核大小为5x5,核步长为1x1,包含压缩与激发操作,有连接失活核连接跳越);


   第三个(扩张比例为6,深度卷积核大小为5x5,核步长为1x1,包含压缩与激发操作,有连接失活核连接跳越);


   输出是一个14x14x112的特征图。总计参数551116个,需要训练学习的参数543148个。


第七阶段:对前一阶段输出的14x14x112的特征图进行四次移动翻转瓶颈卷积

第一个(扩张比例为6,深度卷积核大小为5x5,核步长为2x2,包含压缩与激发操作,无连接失活核连接跳越);


第二个(扩张比例为6,深度卷积核大小为5x5,核步长为1x1,包含压缩与激发操作,有连接失活核连接跳越);


第三个(扩张比例为6,深度卷积核大小为5x5,核步长为1x1,包含压缩与激发操作,有连接失活核连接跳越);


第四个(扩张比例为6,深度卷积核大小为5x5,核步长为1x1,包含压缩与激发操作,有连接失活核连接跳越);


输出是一个7x7x192的特征图。总计参数2044396个,需要训练学习的参数2026348个。


第八阶段,对前一阶段输出的7x7x192的特征图进行一次移动翻转瓶颈卷积

   (扩张比例为6,深度卷积核大小为3x3,核步长为1x1,包含压缩与激发操作,无连接失活核连接跳越);

   输出是一个7x7x320的特征图。总计参数722480个,需要训练学习的参数717232个。


第九阶段:对输入的7x7x320的图像按顺序进行以下操作得到模型最终的结果:


   1) 卷积(卷积核为1280核1×1×320,步长为1×1,填充为“same”即输出的宽和高不变),该卷积运算的输出是一个维度为(7×7×1280)的特征图。因该层不含偏置项,故该层需要训练学习的参数共计409600(1280x1x1x320)个。


   2) 批归一化层(Batch Normalization,BN),该层输入为(7×7×1280)的特征图,故该层含参数总数为5120个(1280x4),其中需要训练学习的参数为2560个。


   3) Swish激活函数


   4) 全局平均池化层(global average pooling),该层在通道维度方向上进行全局平均池化,输出为(1x1x1280)的特征图。


   5) 随机失活dropout


   6) 全连接层,该层有1000个神经元。因该层包含偏置项,总参数个数为1281000(1000x1280+1000)


   7) Softmax激活函数,输出分类结果。


   第九阶段,总计参数1695720个,需要训练学习的参数1693160个。


   除了EfficientNet-B0外,EfficientNet系列还有其它7个网络B0-B7,主要涉及三个参数深度参数、广度参数和输入分辨率参数,通过这三个参数来控制模型的缩放。


其中:

   深度参数通过与EfficientNet-B0中各阶段的模块重复次数相乘,得到更深层的网络架构;

  广度系数通过与EfficientNet-B0中各卷积操作输入的核个数相乘,得到表现能力更强的网络模型;

   输入分辨率参数控制的则是网络的输入图片的长宽大小。


PyTorch实现EfficientNet-B0模型:

class EfficientNet(nn.Module):
    """
    EfficientNet model框架流程(B0为例):
    输入 ——>
         ——> 第一阶段:卷积层 ——> 批归一化 ——> Swish激活函数
         ——> 第二阶段:1个MBConvBlock
         ——> 第三阶段:2个MBConvBlock
         ——> 第四阶段:2个MBConvBlock
         ——> 第五阶段:3个MBConvBlock
         ——> 第六阶段:3个MBConvBlock
         ——> 第七阶段:4个MBConvBlock
         ——> 第八阶段:1个MBConvBlock
         ——> 第九阶段:卷积层 ——> 批归一化 ——> Swish激活函数 ——> 全局平均池化 ——> 随机失活 ——> 全连接层 ——> Softmax 层
    ——> 输出
    """
    def __init__(self, blocks_args=None, global_params=None):
        super().__init__()
        assert isinstance(blocks_args, list), 'blocks_args should be a list'
        assert len(blocks_args) > 0, 'block args must be greater than 0'
        self._global_params = global_params
        self._blocks_args = blocks_args
        # 恒等宽高卷积操作
        Conv2d = get_same_padding_conv2d(image_size=global_params.image_size)
        # BN参数
        bn_mom = 1 - self._global_params.batch_norm_momentum
        bn_eps = self._global_params.batch_norm_epsilon
        in_channels = 3  # rgb
        # 输出通道数目
        out_channels = round_filters(32, self._global_params)
        self._conv_stem = Conv2d(in_channels, out_channels, kernel_size=3, stride=2, bias=False)
        self._bn0 = nn.BatchNorm2d(num_features=out_channels, momentum=bn_mom, eps=bn_eps)
        # 建立ModuleList of MBConvBlock 模块,方便多次使用和调用
        self._blocks = nn.ModuleList([])
        for i in range(len(self._blocks_args)):
            # Update block input and output filters based on depth multiplier.
            self._blocks_args[i] = self._blocks_args[i]._replace(
                input_filters=round_filters(self._blocks_args[i].input_filters, self._global_params),
                output_filters=round_filters(self._blocks_args[i].output_filters, self._global_params),
                num_repeat=round_repeats(self._blocks_args[i].num_repeat, self._global_params))
            self._blocks.append(MBConvBlock(self._blocks_args[i], self._global_params))
            if self._blocks_args[i].num_repeat > 1:
                self._blocks_args[i] = self._blocks_args[i]._replace(input_filters=self._blocks_args[i].output_filters, stride=1)
            for _ in range(self._blocks_args[i].num_repeat - 1):
                self._blocks.append(MBConvBlock(self._blocks_args[i], self._global_params))
        # 输出模块
        in_channels = self._blocks_args[len(self._blocks_args)-1].output_filters
        out_channels = round_filters(1280, self._global_params)
        self._conv_head = Conv2d(in_channels, out_channels, kernel_size=1, bias=False)
        self._bn1 = nn.BatchNorm2d(num_features=out_channels, momentum=bn_mom, eps=bn_eps)
        # 最后的全连接层以及输出阶段
        self._avg_pooling = nn.AdaptiveAvgPool2d(1)
        self._dropout = nn.Dropout(self._global_params.dropout_rate)
        self._fc = nn.Linear(out_channels, self._global_params.num_classes)
        self._swish = MemoryEfficientSwish()
    def set_swish(self, memory_efficient=True):
        """Sets swish function as memory efficient (for training) or standard (for export)"""
        self._swish = MemoryEfficientSwish() if memory_efficient else Swish()
        for block in self._blocks:
            block.set_swish(memory_efficient)
    def extract_features(self, inputs):
        """ Returns output of the final convolution layer """
        # 第一阶段
        x = self._swish(self._bn0(self._conv_stem(inputs)))
        P = []
        index = 0
        num_repeat = 0
        # 第二、三、四、五、六、七、八阶段
        for idx, block in enumerate(self._blocks):
            drop_connect_rate = self._global_params.drop_connect_rate
            if drop_connect_rate:
                drop_connect_rate *= float(idx) / len(self._blocks)
            x = block(x, drop_connect_rate=drop_connect_rate)
            # x = self._swish(self._bn1(self._conv_head(x)))
            num_repeat = num_repeat + 1
            if num_repeat == self._blocks_args[index].num_repeat:
                num_repeat = 0
                index = index + 1
                P.append(x)
        return P
    def forward(self, inputs):
        """ Calls extract_features to extract features, applies final linear layer, and returns logits. """
        bs = inputs.size(0)
        # Convolution layers
        x = self.extract_features(inputs)
        # Pooling and final linear layer
        # x = self._avg_pooling(x)
        # x = x.view(bs, -1)
        # x = self._dropout(x)
        # x = self._fc(x)
        return x
    @classmethod
    # 自身的预训练模型加载
    def from_name(cls, model_name, override_params=None):
        cls._check_model_name_is_valid(model_name)
        blocks_args, global_params = get_model_params(model_name, override_params)
        return cls(blocks_args, global_params)
    @classmethod
    # 下载的预训练模型加载
    def from_pretrained(cls, model_name, num_classes=1000, in_channels=3):
        model = cls.from_name(model_name, override_params={'num_classes': num_classes})
        load_pretrained_weights(
            model, model_name, load_fc=(num_classes == 1000))
        if in_channels != 3:
            Conv2d = get_same_padding_conv2d(image_size=model._global_params.image_size)
            out_channels = round_filters(32, model._global_params)
            model._conv_stem = Conv2d(in_channels, out_channels, kernel_size=3, stride=2, bias=False)
        return model
    @classmethod
    # load预训练权重
    def from_pretrained(cls, model_name, num_classes=1000):
        model = cls.from_name(model_name, override_params={'num_classes': num_classes})
        load_pretrained_weights(model, model_name, load_fc=(num_classes == 1000))
        return model
    @classmethod
    # 获取图像的shape
    def get_image_size(cls, model_name):
        cls._check_model_name_is_valid(model_name)
        _, _, res, _ = efficientnet_params(model_name)
        return res
    @classmethod
    # 检验预训练模型的名称是否正确
    def _check_model_name_is_valid(cls, model_name, also_need_pretrained_weights=False):
        """ Validates model name. None that pretrained weights are only available for
        the first four models (efficientnet-b{i} for i in 0,1,2,3) at the moment. """
        num_models = 4 if also_need_pretrained_weights else 8
        valid_models = ['efficientnet-b'+str(i) for i in range(num_models)]
        if model_name not in valid_models:
            raise ValueError('model_name should be one of: ' + ', '.join(valid_models))
    # 获取卷积后的特征图
    def get_list_features(self):
        list_feature = []
        for idx in range(len(self._blocks_args)):
            list_feature.append(self._blocks_args[idx].output_filters)
        return list_feature


相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
相关文章
|
3月前
|
机器学习/深度学习 人工智能 算法
【专家系统】系统地掌握专家系统的基本概念、技术原理、实现方法以及应用实践。
专家系统是一种人工智能程序,它利用专家知识和推理能力来解决特定领域中的复杂问题,系统地掌握专家系统的基本概念、技术原理、实现方法以及应用实践。
383 1
|
5月前
|
机器学习/深度学习 API 索引
机器学习项目实践-基础知识部分
创建Python隔离环境使用`python -m venv`命令,如`python -m venv ml`来创建名为`ml`的虚拟环境。激活环境通过`.\&lt;Scripts&gt;\activate`(Windows)。然后可以使用`pip`安装库,如`numpy`、`pandas`、`matplotlib`和`jupyter notebook`。在虚拟环境中,`numpy`是用于数组计算的库,支持数学操作和绘图。`pip install`命令后面可添加`-i Simple Index`指定索引源。完成安装后,激活环境并启动`jupyter notebook`进行开发。
|
6月前
|
缓存 负载均衡 测试技术
探究职业发展的关键:能力模型解读
能力模型是指导个人职业发展的蓝图,它定义了行业和职位所需的具体技能和能力。业务测试工程师的能力模型包括需求理解、架构理解、测试设计、测试工具应用/脚本开发和测试总结五个维度,而测试开发工程师的能力模型则涵盖架构理解、开发语言应用、测试工具/平台开发和专项测试四个维度。通过理解这些模型,个人可以明确提升方向,例如业务测试工程师可参考《测试开发体系介绍》、《测试用例设计》等课程进行学习,而测试开发工程师则可关注《编程语言》、《测试框架》等相关课程。知行合一,按照能力模型进行学习和实践,有助于在职业生涯中取得成功。
|
机器学习/深度学习 数据采集 人工智能
AI大模型知识点大梳理1
AI大模型是什么 AI大模型发展历程
494 0
|
6月前
|
存储 人工智能 数据库
【AI大模型应用开发】MemGPT原理与快速上手:这可能是目前管理大模型记忆的最专业的框架和思路
【AI大模型应用开发】MemGPT原理与快速上手:这可能是目前管理大模型记忆的最专业的框架和思路
421 0
|
6月前
|
机器学习/深度学习 Python
有没有一些开源的深度学习项目可以帮助我实践所学的知识?
【2月更文挑战第14天】【2月更文挑战第40篇】有没有一些开源的深度学习项目可以帮助我实践所学的知识?
40 1
|
机器学习/深度学习 人工智能 自然语言处理
AI大模型知识点大梳理2
AI大模型的底层原理 AI大模型解决的问题
265 0
|
人工智能 自然语言处理 安全
AI大模型知识点大梳理3
大模型的优点和不足 影响 个人观点
126 0
|
缓存 算法 大数据
架构、框架侃侃而谈算法望而却步?吃透这份笔记轻松掌握算法技能
腾讯、百度阿里等国内的一线名企,在招聘工程师的过程中,对算法和数据结构都会重点考察。但算法易学难精,让很多程序员都望而却步,面试时总败在算法这一关,拿不到好 Offer。 面试时很多候选人,聊起架构、框架侃侃而谈,但一写代码,就暴露真实水平。说白了,还是基本功不够扎实。 其实,不管你是什么语言,基本功一定要扎实,最核心的一定是数据结构与算法。也因此,所有大厂面试,都必考算法题。
|
编解码 PyTorch 算法框架/工具
【项目实践】EfficientDet原理讲解与目标检测项目实践(二)
【项目实践】EfficientDet原理讲解与目标检测项目实践(二)
238 0
下一篇
无影云桌面