Bootstrap

昇思 25 天学习打卡营第 18 天 | 基于MobileNetv2的垃圾分类

今天是参加昇思学习打卡营的第18天,学习内容是基于MobileNetv2的垃圾分类。

以下是内容关键点概要:
    1.MobileNetv2模型原理介绍

    2.数据处理

   3.MobileNetV2模型搭建

   4.MobileNetV2模型的训练与测试

   5.模型推理

实验目的

  • 了解熟悉垃圾分类应用代码的编写(Python语言);
  • 了解Linux操作系统的基本使用;
  • 掌握atc命令进行模型转换的基本操作。

MobileNetv2模型原理介绍

MobileNet网络是由Google团队于2017年提出的专注于移动端、嵌入式或IoT设备的轻量级CNN网络,相比于传统的卷积神经网络,MobileNet网络使用深度可分离卷积(Depthwise Separable Convolution)的思想在准确率小幅度降低的前提下,大大减小了模型参数与运算量。并引入宽度系数 α和分辨率系数 β使模型满足不同应用场景的需求。

由于MobileNet网络中Relu激活函数处理低维特征信息时会存在大量的丢失,所以MobileNetV2网络提出使用倒残差结构(Inverted residual block)和Linear Bottlenecks来设计网络,以提高模型的准确率,且优化后的模型更小。 

 

图中Inverted residual block结构是先使用1x1卷积进行升维,然后使用3x3的DepthWise卷积,最后使用1x1的卷积进行降维,与Residual block结构相反。Residual block是先使用1x1的卷积进行降维,然后使用3x3的卷积,最后使用1x1的卷积进行升维。

数据处理

数据准备

MobileNetV2的代码默认使用ImageFolder格式管理数据集,每一类图片整理成单独的一个文件夹, 数据集结构如下:

└─ImageFolder

├─train
│   class1Folder
│   ......
└─eval
    class1Folder
    ......
    

 

 

from download import download  # 从download模块中导入download函数

# 下载data_en数据集
# 定义数据集的URL地址
url = "https://ascend-professional-construction-dataset.obs.cn-north-4.myhuaweicloud.com:443/MindStudio-pc/data_en.zip"
# 指定下载文件保存的路径,这里使用当前目录"./"作为保存路径
path = download(url, "./", kind="zip", replace=True)

from download import download

# 下载预训练权重文件
url = "https://ascend-professional-construction-dataset.obs.cn-north-4.myhuaweicloud.com:443/ComputerVision/mobilenetV2-200_1067.zip" 
path = download(url, "./", kind="zip", replace=True)

数据加载

import math
import numpy as np
import os
import random

from matplotlib import pyplot as plt
from easydict import EasyDict
from PIL import Image
import numpy as np
import mindspore.nn as nn
from mindspore import ops as P
from mindspore.ops import add
from mindspore import Tensor
import mindspore.common.dtype as mstype
import mindspore.dataset as de
import mindspore.dataset.vision as C
import mindspore.dataset.transforms as C2
import mindspore as ms
from mindspore import set_context, nn, Tensor, load_checkpoint, save_checkpoint, export
from mindspore.train import Model
from mindspore.train import Callback, LossMonitor, ModelCheckpoint, CheckpointConfig

os.environ['GLOG_v'] = '3' # Log level includes 3(ERROR), 2(WARNING), 1(INFO), 0(DEBUG).
os.environ['GLOG_logtostderr'] = '0' # 0:输出到文件,1:输出到屏幕
os.environ['GLOG_log_dir'] = '../../log' # 日志目录
os.environ['GLOG_stderrthreshold'] = '2' # 输出到目录也输出到屏幕:3(ERROR), 2(WARNING), 1(INFO), 0(DEBUG).
set_context(mode=ms.GRAPH_MODE, device_target="CPU", device_id=0) # 设置采用图模式执行,设备为Ascend#

配置后续训练、验证、推理用到的参数:

# 垃圾分类数据集标签,以及用于标签映射的字典。
garbage_classes = {
    '干垃圾': ['贝壳', '打火机', '旧镜子', '扫把', '陶瓷碗', '牙刷', '一次性筷子', '脏污衣服'],
    '可回收物': ['报纸', '玻璃制品', '篮球', '塑料瓶', '硬纸板', '玻璃瓶', '金属制品', '帽子', '易拉罐', '纸张'],
    '湿垃圾': ['菜叶', '橙皮', '蛋壳', '香蕉皮'],
    '有害垃圾': ['电池', '药片胶囊', '荧光灯', '油漆桶']
}

class_cn = ['贝壳', '打火机', '旧镜子', '扫把', '陶瓷碗', '牙刷', '一次性筷子', '脏污衣服',
            '报纸', '玻璃制品', '篮球', '塑料瓶', '硬纸板', '玻璃瓶', '金属制品', '帽子', '易拉罐', '纸张',
            '菜叶', '橙皮', '蛋壳', '香蕉皮',
            '电池', '药片胶囊', '荧光灯', '油漆桶']
class_en = ['Seashell', 'Lighter','Old Mirror', 'Broom','Ceramic Bowl', 'Toothbrush','Disposable Chopsticks','Dirty Cloth',
            'Newspaper', 'Glassware', 'Basketball', 'Plastic Bottle', 'Cardboard','Glass Bottle', 'Metalware', 'Hats', 'Cans', 'Paper',
            'Vegetable Leaf','Orange Peel', 'Eggshell','Banana Peel',
            'Battery', 'Tablet capsules','Fluorescent lamp', 'Paint bucket']

index_en = {'Seashell': 0, 'Lighter': 1, 'Old Mirror': 2, 'Broom': 3, 'Ceramic Bowl': 4, 'Toothbrush': 5, 'Disposable Chopsticks': 6, 'Dirty Cloth': 7,
            'Newspaper': 8, 'Glassware': 9, 'Basketball': 10, 'Plastic Bottle': 11, 'Cardboard': 12, 'Glass Bottle': 13, 'Metalware': 14, 'Hats': 15, 'Cans': 16, 'Paper': 17,
            'Vegetable Leaf': 18, 'Orange Peel': 19, 'Eggshell': 20, 'Banana Peel': 21,
            'Battery': 22, 'Tablet capsules': 23, 'Fluorescent lamp': 24, 'Paint bucket': 25}

# 训练超参
config = EasyDict({
    "num_classes": 26,
    "image_height": 224,
    "image_width": 224,
    #"data_split": [0.9, 0.1],
    "backbone_out_channels":1280,
    "batch_size": 16,
    "eval_batch_size": 8,
    "epochs": 10,
    "lr_max": 0.05,
    "momentum": 0.9,
    "weight_decay": 1e-4,
    "save_ckpt_epochs": 1,
    "dataset_path": "./data_en",
    "class_index": index_en,
    "pretrained_ckpt": "./mobilenetV2-200_1067.ckpt" # mobilenetV2-200_1067.ckpt 
})
数据预处理操作

利用ImageFolderDataset方法读取垃圾分类数据集,并整体对数据集进行处理。

读取数据集时指定训练集和测试集,首先对整个数据集进行归一化,修改图像频道等预处理操作。然后对训练集的数据依次进行RandomCropDecodeResize、RandomHorizontalFlip、RandomColorAdjust、shuffle操作,以增加训练数据的丰富度;对测试集进行Decode、Resize、CenterCrop等预处理操作;最后返回处理后的数据集。

import os
import mindspore.dataset as ds
import mindspore.dataset.vision.c_transforms as C
import mindspore.common.dtype as mstype
from mindspore import communication

def create_dataset(dataset_path, config, training=True, buffer_size=1000):
    """
    创建训练或评估数据集。

    参数:
        dataset_path (string): 数据集的路径。
        config (struct): 不同平台上训练和评估的配置。
        training (bool): 是否创建训练数据集,默认为True。
        buffer_size (int): 用于洗牌操作的缓冲区大小,默认为1000。

    返回:
        train_dataset, val_dataset: 返回训练数据集和验证数据集(如果需要)。
    """
    # 根据训练标志选择数据集路径
    data_path = os.path.join(dataset_path, 'train' if training else 'test')
    # 使用ImageFolderDataset加载数据集
    ds = ds.ImageFolderDataset(data_path, num_parallel_workers=4, class_indexing=config.class_index)
    
    # 获取配置中的图像尺寸
    resize_height = config.image_height
    resize_width = config.image_width
    
    # 定义数据增强和预处理操作
    normalize_op = C.Normalize(mean=[0.485*255, 0.456*255, 0.406*255], std=[0.229*255, 0.224*255, 0.225*255])
    change_swap_op = C.HWC2CHW()
    type_cast_op = C2.TypeCast(mstype.int32)  # 确保C2是已定义的,并且TypeCast是可用的

    # 根据是否训练数据集,选择不同的数据增强操作
    if training:
        # 定义训练时的数据增强操作
        crop_decode_resize = C.RandomCropDecodeResize(resize_height, scale=(0.08, 1.0), ratio=(0.75, 1.333))
        horizontal_flip_op = C.RandomHorizontalFlip(prob=0.5)
        color_adjust = C.RandomColorAdjust(brightness=0.4, contrast=0.4, saturation=0.4)
        
        train_trans = [crop_decode_resize, horizontal_flip_op, color_adjust, normalize_op, change_swap_op]
        # 应用数据增强操作
        train_ds = ds.map(input_columns="image", operations=train_trans, num_parallel_workers=4)
        # 应用类型转换操作
        train_ds = train_ds.map(input_columns="label", operations=type_cast_op, num_parallel_workers=4)
        
        # 打乱数据集
        train_ds = train_ds.shuffle(buffer_size=buffer_size)
        # 批处理
        ds = train_ds.batch(config.batch_size, drop_remainder=True)
    else:
        # 定义评估时的数据预处理操作
        decode_op = C.Decode()
        resize_op = C.Resize((int(resize_width/0.875), int(resize_height/0.875)))  # 注意这里可能需要调整为resize_height
        center_crop = C.CenterCrop(resize_width)
        
        eval_trans = [decode_op, resize_op, center_crop, normalize_op, change_swap_op]
        # 应用数据预处理操作
        eval_ds = ds.map(input_columns="image", operations=eval_trans, num_parallel_workers=4)
        # 应用类型转换操作
        eval_ds = eval_ds.map(input_columns="label", operations=type_cast_op, num_parallel_workers=4)
        # 批处理
        ds = eval_ds.batch(config.eval_batch_size, drop_remainder=True)

    # 返回创建的数据集
    return ds

展示部分处理后的数据:

# 导入必要的库
import matplotlib.pyplot as plt
import numpy as np

# 调用create_dataset函数创建评估数据集,config是一个包含数据集路径和配置的变量
ds = create_dataset(dataset_path=config.dataset_path, config=config, training=False)

# 获取并打印数据集的大小(样本数量)
print(ds.get_dataset_size())

# 创建数据集的迭代器,并指定输出为NumPy数组格式
data = ds.create_dict_iterator(output_numpy=True)._get_next()

# 从迭代器中获取第一个批次的图像和标签
images = data['image']
labels = data['label']

# 使用matplotlib创建2x2的子图布局,并迭代展示前4张图像
for i in range(1, 5):
    plt.subplot(2, 2, i)  # 创建子图
    plt.imshow(np.transpose(images[i], (1, 2, 0)))  # 显示图像,需要从CHW转换到HWC格式
    plt.title('label: %s' % class_en[labels[i]])  # 设置图像标题为对应的标签
    plt.xticks([])  # 不显示x轴的刻度
    plt.yticks([])  # 不显示y轴的刻度

# 显示所有子图
plt.show()

MobileNetV2模型搭建

使用MindSpore定义MobileNetV2网络的各模块时需要继承mindspore.nn.Cell。Cell是所有神经网络(Conv2d等)的基类。

神经网络的各层需要预先在__init__方法中定义,然后通过定义construct方法来完成神经网络的前向构造。原始模型激活函数为ReLU6,池化模块采用是全局平均池化层。

# 定义模块导出的公开接口
__all__ = ['MobileNetV2', 'MobileNetV2Backbone', 'MobileNetV2Head', 'mobilenet_v2']

# _make_divisible函数确保指定的值v可以被divisor整除,同时考虑最小值min_value
def _make_divisible(v, divisor, min_value=None):
    # ...函数实现...

# GlobalAvgPooling类实现了全局平均池化层
class GlobalAvgPooling(nn.Cell):
    # ...类实现...

# ConvBNReLU类实现了带有批量归一化和ReLU激活函数的卷积/深度可分离卷积块
class ConvBNReLU(nn.Cell):
    # ...类实现...

# InvertedResidual类实现了MobileNetV2中的倒置残差块
class InvertedResidual(nn.Cell):
    # ...类实现...

# MobileNetV2Backbone类实现了MobileNetV2的骨干网络
class MobileNetV2Backbone(nn.Cell):
    # ...类实现...

# MobileNetV2Head类实现了MobileNetV2的头部网络,包括全局平均池化和全连接层
class MobileNetV2Head(nn.Cell):
    # ...类实现...

# MobileNetV2类通过组合backbone和head来构建完整的MobileNetV2模型
class MobileNetV2(nn.Cell):
    # ...类实现...

# MobileNetV2Combine类提供了一种将backbone和head组合成完整模型的方式
class MobileNetV2Combine(nn.Cell):
    # ...类实现...

# mobilenet_v2函数是一个工厂函数,用于创建MobileNetV2模型实例
def mobilenet_v2(backbone, head):
    # ...函数实现...

MobileNetV2模型的训练与测试

训练策略

一般情况下,模型训练时采用静态学习率,如0.01。随着训练步数的增加,模型逐渐趋于收敛,对权重参数的更新幅度应该逐渐降低,以减小模型训练后期的抖动。所以,模型训练时可以采用动态下降的学习率,常见的学习率下降策略有:

  • polynomial decay/square decay;
  • cosine decay;
  • exponential decay;
  • stage decay.

这里使用cosine decay下降策略:

在模型训练过程中,可以添加检查点(Checkpoint)用于保存模型的参数,以便进行推理及中断后再训练使用。使用场景如下:

  • 训练后推理场景
  1. 模型训练完毕后保存模型的参数,用于推理或预测操作。
  2. 训练过程中,通过实时验证精度,把精度最高的模型参数保存下来,用于预测操作。
  • 再训练场景
  1. 进行长时间训练任务时,保存训练过程中的Checkpoint文件,防止任务异常退出后从初始状态开始训练。
  2. Fine-tuning(微调)场景,即训练一个模型并保存参数,基于该模型,面向第二个类似任务进行模型训练。

这里加载ImageNet数据上预训练的MobileNetv2进行Fine-tuning,只训练最后修改的FC层,并在训练过程中保存Checkpoint。

import math

def cosine_decay(total_steps, lr_init=0.0, lr_end=0.0, lr_max=0.1, warmup_steps=0):
    """
    应用余弦衰减策略生成学习率数组。
    
    参数:
       total_steps(int): 训练中的总步数。
       lr_init(float): 初始学习率。
       lr_end(float): 结束学习率。
       lr_max(float): 最大学习率。
       warmup_steps(int): 预热时期的步数。

    返回:
       list, 学习率数组。
    """
    # 确保输入的学习率参数是浮点数
    lr_init, lr_end, lr_max = float(lr_init), float(lr_end), float(lr_max)
    
    # 计算衰减步骤数
    decay_steps = total_steps - warmup_steps
    
    # 初始化学习率数组
    lr_all_steps = []
    
    # 计算预热期间每步的增量
    inc_per_step = (lr_max - lr_init) / warmup_steps if warmup_steps else 0
    
    # 遍历所有步数
    for i in range(total_steps):
        if i < warmup_steps:
            # 在预热期间,线性增加学习率
            lr = lr_init + inc_per_step * (i + 1)
        else:
            # 在衰减期间,应用余弦衰减公式
            cosine_decay = 0.5 * (1 + math.cos(math.pi * (i - warmup_steps) / decay_steps))
            lr = (lr_max - lr_end) * cosine_decay + lr_end
        # 将计算出的学习率添加到数组中
        lr_all_steps.append(lr)

    # 返回学习率数组
    return lr_all_steps
import mindspore as ms
from mindspore import nn

def switch_precision(net, data_type):
    """
    更改MindSpore网络中的张量数据类型。
    
    参数:
        net (Cell): 要更改数据类型的MindSpore网络。
        data_type (str): 目标数据类型名称,如"float16"或"float32"。
    
    返回:
        无,原网络net的数据类型被更改。
    """
    # 检查当前设备目标是否为Ascend
    if ms.get_context('device_target') == "Ascend":
        # 如果是Ascend,调用to_float方法更改网络的数据类型
        net.to_float(data_type)
        
        # 遍历网络中的所有单元(cells)及其名称
        for _, cell in net.cells_and_names():
            # 如果单元是nn.Dense类型,也将其数据类型更改为float32
            # 这可能是为了确保某些操作仍然以float32执行,即使网络的其余部分使用不同的精度
            if isinstance(cell, nn.Dense):
                cell.to_float(ms.float32)
模型训练与测试

在进行正式的训练之前,定义训练函数,读取数据并对模型进行实例化,定义优化器和损失函数。

首先简单介绍损失函数及优化器的概念:

  • 损失函数:又叫目标函数,用于衡量预测值与实际值差异的程度。深度学习通过不停地迭代来缩小损失函数的值。定义一个好的损失函数,可以有效提高模型的性能。

  • 优化器:用于最小化损失函数,从而在训练过程中改进模型。

定义了损失函数后,可以得到损失函数关于权重的梯度。梯度用于指示优化器优化权重的方向,以提高模型性能。

在训练MobileNetV2之前对MobileNetV2Backbone层的参数进行了固定,使其在训练过程中对该模块的权重参数不进行更新;只对MobileNetV2Head模块的参数进行更新。

MindSpore支持的损失函数有SoftmaxCrossEntropyWithLogits、L1Loss、MSELoss等。这里使用SoftmaxCrossEntropyWithLogits损失函数。

训练测试过程中会打印loss值,loss值会波动,但总体来说loss值会逐步减小,精度逐步提高。每个人运行的loss值有一定随机性,不一定完全相同。

每打印一个epoch后模型都会在测试集上的计算测试精度,从打印的精度值分析MobileNetV2模型的预测能力在不断提升。

from mindspore.amp import FixedLossScaleManager
import time
from mindspore import load_checkpoint, save_checkpoint

# 设置损失尺度为1024
LOSS_SCALE = 1024

# 创建训练和评估数据集
train_dataset = create_dataset(dataset_path=config.dataset_path, config=config, training=True)
eval_dataset = create_dataset(dataset_path=config.dataset_path, config=config, training=False)
# 获取训练数据集的大小(批数)
step_size = train_dataset.get_dataset_size()

# 初始化MobileNetV2的骨干网络backbone
backbone = MobileNetV2Backbone()
# 冻结backbone的参数
for param in backbone.get_parameters():
    param.requires_grad = False
# 加载预训练模型的参数
load_checkpoint(config.pretrained_ckpt, backbone)

# 初始化MobileNetV2的分类头部head
head = MobileNetV2Head(input_channel=backbone.out_channels, num_classes=config.num_classes)
# 创建MobileNetV2模型
network = mobilenet_v2(backbone, head)

# 定义损失函数
loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
# 创建固定损失尺度管理器
loss_scale = FixedLossScaleManager(LOSS_SCALE, drop_overflow_update=False)
# 使用余弦衰减策略生成学习率数组
lrs = cosine_decay(config.epochs * step_size, lr_max=config.lr_max)
# 定义优化器
opt = nn.Momentum(network.trainable_params(), lrs, config.momentum, config.weight_decay, loss_scale=LOSS_SCALE)

# 定义训练循环函数
def train_loop(model, dataset, loss_fn, optimizer):
    # ...函数实现...

# 定义测试循环函数
def test_loop(model, dataset, loss_fn):
    # ...函数实现...

# 打印开始训练的信息
print("============== Starting Training ==============")
# 记录训练开始时间
epoch_begin_time = time.time()
# 设置训练的epoch数
epochs = 2
for t in range(epochs):
    # 记录每个epoch的开始时间
    begin_time = time.time()
    print(f"Epoch {t+1}\n-------------------------------")
    # 执行训练循环
    train_loop(network, train_dataset, loss, opt)
    # 保存模型检查点
    save_checkpoint(network, "save_mobilenetV2_model.ckpt")
    # 记录每个epoch的结束时间,并计算epoch用时
    end_time = time.time()
    times = end_time - begin_time
    print(f"per epoch time: {times}s")
    # 执行测试循环
    test_loop(network, eval_dataset, loss)
# 记录训练结束时间,并计算总用时
epoch_end_time = time.time()
times = epoch_end_time - epoch_begin_time
print(f"total time:  {times}s")
# 打印训练成功的消息
print("============== Training Success ==============")

模型推理

加载模型Checkpoint进行推理,使用load_checkpoint接口加载数据时,需要把数据传入给原始网络,而不能传递给带有优化器和损失函数的训练网络。

import numpy as np
import mindspore as ms
from PIL import Image
from mindspore import Tensor, load_checkpoint

# 定义模型检查点的路径
CKPT = "save_mobilenetV2_model.ckpt"

def image_process(image):
    """
    对单个图像进行预处理。
    
    参数:
        image: 形状为(H, W, C)的图像
    """
    # 定义均值和标准差,用于图像归一化
    mean = [0.485*255, 0.456*255, 0.406*255]
    std = [0.229*255, 0.224*255, 0.225*255]
    
    # 将图像转换为NumPy数组,并进行归一化处理
    image = (np.array(image) - mean) / std
    # 将图像从HWC格式转换为CHW格式
    image = image.transpose((2, 0, 1))
    # 将图像转换为MindSpore Tensor,并增加一个批次维度
    img_tensor = Tensor(np.array([image], np.float32))
    return img_tensor

def infer_one(network, image_path):
    # 对一张图像进行推理
    image = Image.open(image_path).resize((config.image_height, config.image_width))
    # 对图像进行预处理
    image = image_process(image)
    # 使用网络进行推理,获取预测结果
    logits = network(image)
    # 找到预测最大概率的索引
    pred = np.argmax(logits.asnumpy(), axis=1)[0]
    # 打印图像路径和对应的预测类别
    print(image_path, class_en[pred])

def infer():
    # 初始化MobileNetV2的骨干网络backbone
    backbone = MobileNetV2Backbone(last_channel=config.backbone_out_channels)
    # 初始化MobileNetV2的分类头部head
    head = MobileNetV2Head(input_channel=backbone.out_channels, num_classes=config.num_classes)
    # 创建MobileNetV2模型
    network = mobilenet_v2(backbone, head)
    
    # 加载模型检查点
    load_checkpoint(CKPT, network)
    
    # 对指定范围内的图像进行推理
    for i in range(91, 100):
        infer_one(network, f'data_en/test/Cardboard/000{i}.jpg')

# 调用infer函数执行推理
infer()

导出AIR/GEIR/ONNX模型文件

导出AIR模型文件,用于后续Atlas 200 DK上的模型转换与推理。当前仅支持MindSpore+Ascend环境。

# 导入必要的库
import mindspore as ms
from mindspore import Tensor
from your_model_file import MobileNetV2Backbone, MobileNetV2Head, mobilenet_v2, load_checkpoint  # 假设模型定义在这个文件中

# 定义模型检查点的路径
CKPT = "save_mobilenetV2_model.ckpt"

# 根据配置文件创建MobileNetV2的骨干网络backbone
backbone = MobileNetV2Backbone(last_channel=config.backbone_out_channels)
# 根据backbone的输出通道数创建MobileNetV2的分类头部head
head = MobileNetV2Head(input_channel=backbone.out_channels, num_classes=config.num_classes)
# 组合backbone和head创建完整的MobileNetV2模型
network = mobilenet_v2(backbone, head)

# 加载预训练的权重到模型中
load_checkpoint(CKPT, network)

# 创建一个随机初始化的输入张量,这里假设输入图像的维度是1x3x224x224
input = np.random.uniform(0.0, 1.0, size=[1, 3, 224, 224]).astype(np.float32)
input_tensor = Tensor(input)

# 导出模型到ONNX格式
# 注意:导出模型需要使用MindSpore的export函数,这里假设export函数是可用的
# export(network, input_tensor, file_name='mobilenetv2.air', file_format='AIR')  # 导出为华为昇思AIR模型
# export(network, input_tensor, file_name='mobilenetv2.pb', file_format='GEIR')  # 导出为GEIR格式
export(network, input_tensor, file_name='mobilenetv2.onnx', file_format='ONNX')  # 导出为ONNX格式

学习心得:

  1. 将MobileNetV2应用于垃圾分类问题,实践了从数据预处理、模型构建、训练到推理的整个流程,加深了对深度学习工作流程的认识。

  2. 学习了如何利用预训练模型进行迁移学习,这大大减少了训练时间,同时提高了模型在特定任务上的表现。

  3. 理解了数据预处理在模型性能提升中的关键作用,包括图像的归一化、尺寸调整以及数据增强等。

  4. 学习了在混合精度训练中使用损失尺度管理器来避免数值溢出,这对于提高模型训练的稳定性非常有帮助。

  5. 掌握了如何将训练好的模型导出为不同格式,这对于模型的部署和在不同平台上的应用至关重要。

  6. 通过实验验证了理论知识,这种实践让我更好地理解了理论背后的原理和实际效果。

  7. 在解决垃圾分类问题的过程中,我学会了如何针对具体问题调整模型参数和训练策略。

  8. 随着技术的不断发展,我意识到了持续学习新模型、新方法和新技术的重要性。

加油!!!

;