目录
1. 实验目的与内容
1.1 实验目的
本实验使用MindSpore框架进行AI模型的训练与部署,通过训练一个基于MobileNet V2架构的深度学习模型,实现垃圾分类。具体目标包括:
1. 下载并预处理垃圾分类数据集。
2. 利用预训练的MobileNet V2模型进行微调,训练出一个高精度的垃圾分类模型。
3. 实现实时垃圾分类的功能,并对模型的性能进行评估和优化。
1.2 实验内容
1. 数据准备与加载:
下载并解压垃圾分类数据集。
对数据集进行图像增强和预处理操作,创建训练集和验证集。
2. 模型训练:
下载预训练的MobileNet V2模型。
利用预训练模型进行微调,调整模型参数以适应垃圾分类任务。
训练模型,并使用验证集评估模型性能,保存验证精度最高的模型。
3. 模型可视化:
使用训练好的模型对输入图像进行预测,并将预测结果可视化展示。
4. 性能评估与优化:
评估模型在验证集上的性能,包括准确率和损失。
对模型进行调参和优化,提高模型的精度和推理速度。
1.3 研究背景与意义
垃圾分类是当前社会面临的一个重要环境保护问题。通过对垃圾进行分类处理,可以有效提高资源的回收利用率,减少环境污染。然而,人工进行垃圾分类不仅效率低下,而且容易出错。为了解决这一问题,利用深度学习技术开发自动化的垃圾分类系统具有重要的现实意义。
近年来,随着深度学习技术的发展和硬件计算能力的提升,基于图像分类的AI模型在实际应用中表现出色。MobileNet V2作为一种轻量级卷积神经网络,因其高效的计算能力和较低的资源消耗,特别适用于移动端和嵌入式设备。通过MobileNet V2的垃圾分类模型,可以实现高效、便捷的实时垃圾分类,助力环保事业的发展。
2. 算法概述、流程、功能模块
2.1 准备数据
下载数据集: 垃圾分类数据集包含6个类别:纸板、玻璃、金属、纸、塑料、垃圾,我们下载并解压数据集到指定路径下。
datasets
└── Garbage_Data
├── infer
│ ├── cardboard1.jpg
│ ├── glass1.jpg
│ ├── metal1.jpg
│ ├── paper1.jpg
│ ├── plastic1.jpg
│ └── trash1.jpg
├── train
│ ├── cardboard
│ ├── glass
│ ├── metal
│ ├── paper
│ ├── plastic
│ └── trash
└── val
├── cardboard
├── glass
├── metal
├── paper
├── plastic
└── trash
加载数据集: 该create_dataset函数从指定路径加载数据集,如果train设置为,则应用数据增强True,它规范化图像并将其转换为所需的格式。
def create_dataset(path, batch_size=10, train=True, image_size=224):
dataset = ds.ImageFolderDataset(path, num_parallel_workers=8, class_indexing={"cardboard": 0, "glass": 1, "metal": 2, "paper": 3, "plastic": 4, "trash": 5})
# 图像增强操作
mean = [0.485 * 255, 0.456 * 255, 0.406 * 255]
std = [0.229 * 255, 0.224 * 255, 0.225 * 255]
if train:
trans = [
vision.RandomCropDecodeResize(image_size, scale=(0.08, 1.0), ratio=(0.75, 1.333)),
vision.RandomHorizontalFlip(prob=0.5),
vision.Normalize(mean=mean, std=std),
vision.HWC2CHW()
]
else:
trans = [
vision.Decode(),
vision.Resize(256),
vision.CenterCrop(image_size),
vision.Normalize(mean=mean, std=std),
vision.HWC2CHW()
]
dataset = dataset.map(operations=trans, input_columns="image", num_parallel_workers=8)
# 设置batch_size的大小,若最后一次抓取的样本数小于batch_size,则丢弃
dataset = dataset.batch(batch_size, drop_remainder=True)
return dataset
# 加载训练数据集和验证数据集
train_path = "Garbage_Data/train"
dataset_train = create_dataset(train_path, train=True)
val_path = "Garbage_Data/val"
dataset_val = create_dataset(val_path, train=False)
2.2 配置网络
下载预训练的MobileNet V2模型,并进行微调。设置网络超参数,包括学习率、批次大小、训练轮数等。
from mindspore import nn, load_checkpoint, load_param_into_net
from mindspore.train import Model
from mindspore import context
# 设置执行环境
context.set_context(mode=context.GRAPH_MODE, device_target="CPU")
# 加载预训练的MobileNetV2模型
from mindvision.classification.models import mobilenet_v2
network = mobilenet_v2(num_classes=6, pretrained=True)
# 修改最后的全连接层
network.classifier = nn.Dense(in_channels=1280, out_channels=6)
# 加载预训练参数
param_dict = load_checkpoint('mobilenet_v2.ckpt')
load_param_into_net(network, param_dict)
2.3 定义网络
网络定义包括前向传播、后向传播以及网络结构的设计。本实验中,基于MobileNet V2模型进行微调,MobileNet V2是Google团队提出的轻量级卷积神经网络,适用于移动端和嵌入式设备。其核心是使用深度可分离卷积和倒残差结构,提高模型的效率和准确率。
# 创建模型,其中目标分类数为6,图像输入大小为(224,224)
network = mobilenet_v2(num_classes=6, resize=224)
# 模型参数存入到param_dict
param_dict = ms.load_checkpoint("./mobilenet_v2_1.0_224.ckpt")
# 获取mobilenet_v2网络最后一个卷积层的参数名
filter_list = [x.name for x in network.head.classifier.get_parameters()]
# 删除预训练模型的最后一个卷积层
def filter_ckpt_parameter(origin_dict, param_filter):
for key in list(origin_dict.keys()):
for name in param_filter:
if name in key:
print("Delete parameter from checkpoint: ", key)
del origin_dict[key]
break
filter_ckpt_parameter(param_dict, filter_list)
# 加载预训练模型参数作为网络初始化权重
ms.load_param_into_net(network, param_dict)
2.4 定义损失函数
# 定义损失函数
network_loss = CrossEntropySmooth(sparse=True, reduction="mean", smooth_factor=0.1, classes_num=2)
2.5 定义优化算法
# 定义优化器
network_opt = nn.Momentum(params=network.trainable_params(), learning_rate=0.01, momentum=0.9)
2.6 训练网络
加载预训练模型: 加载 MobileNetV2 模型,并删除其最后一层,以便针对新的分类任务(垃圾类型)进行微调。
优化和损失函数: 定义了动量优化器和带标签平滑的交叉熵损失函数。
训练模型: 对模型进行指定数量的训练,并通过回调监控验证准确率,保存最佳模型。
2.7 模型评估
visualize_model函数加载训练模型并对新图像进行预测,显示预测的类别。生成窗口,显示预测结果。训练并评估模型,并保存验证精度最高的文件。
from mindvision.engine.callback import ValAccMonitor
import mindspore as ms
num_epochs = 10
# 模型训练与验证,训练完成后保存验证精度最高的ckpt文件(best.ckpt)到当前目录下
model.train(num_epochs,
dataset_train,
callbacks=[ValAccMonitor(model,dataset_val, num_epochs), ms.TimeMonitor()])
2.8 模型预测
使用验证精度最高的模型对输入图像进行预测,并将预测结果可视化。
def visualize_model(path):
image = Image.open(path).convert("RGB")
image = image.resize((224, 224))
plt.imshow(image)
# 归一化处理
mean = np.array([0.485 * 255, 0.456 * 255, 0.406 * 255])
std = np.array([0.229 * 255, 0.224 * 255, 0.225 * 255])
image = np.array(image)
image = (image - mean) / std
image = image.astype(np.float32)
# 图像通道由(h, w, c)转换为(c, h, w)
image = np.transpose(image, (2, 0, 1))
# 扩展数据维数为(1, c, h, w)
image = np.expand_dims(image, axis=0)
# 定义并加载网络
net = mobilenet_v2(num_classes=6, resize=224)
param_dict = ms.load_checkpoint("./best.ckpt")
ms.load_param_into_net(net, param_dict)
model = ms.Model(net)
# 模型预测
pre = model.predict(ms.Tensor(image))
result = np.argmax(pre)
class_name = {0:"cardboard", 1:"glass", 2:"metal", 3:"paper", 4: "plastic", 5: "trash"}
plt.title(f"Predict: {class_name[result]}")
return result
image_paths=["Garbage_Data/infer/cardboard1.jpg","Garbage_Data/infer/glass1.jpg","Garbage_Data/infer/metal1.jpg","Garbage_Data/infer/paper1.jpg","Garbage_Data/infer/plastic1.jpg", "Garbage_Data/infer/trash1.jpg"]
plt.figure(figsize=(15, 7))
for i, image_path in enumerate(image_paths):
plt.subplot(2, 3, i+1)
visualize_model(image_path)
plt.show()
3. 结果及性能分析
3.1 结果
在训练过程中,使用验证集对模型进行评估。训练过程中的损失值和评估精度如下:
训练损失值:随着训练进行逐渐下降,表明模型在不断学习并优化其参数。
验证精度:通过每个epoch的验证,验证精度逐渐提高,最终达到一个较高的精度。
使用训练好的模型对几张测试图片进行预测,并可视化预测结果。以下为部分测试图片的预测结果:
3.2 调参及性能优化
在训练过程中,通过调整以下参数来优化模型性能:
- 学习率:对学习率进行微调,选择一个合适的学习率以平衡收敛速度和稳定性。
- batch size:调整batch size,平衡训练速度和显存占用。
- 数据增强:增加数据增强操作,提高模型的泛化能力。
- 训练轮数:通过适当增加训练轮数,确保模型充分学习数据中的模式和特征。
4. 总结
4.1 实验中遇到的问题
1. 数据集不平衡:在实验开始时,发现不同类别的垃圾数据数量不均衡,可能导致模型偏向于预测样本较多的类别。
2. 训练时间较长:由于数据集较大,训练过程耗时较长,特别是在进行多轮训练和微调时。
3. 过拟合问题:在训练过程中,模型在训练集上表现良好,但在验证集上的表现不尽如人意,表明可能存在过拟合。
4. 模型导出与转换:在将训练好的模型导出为MindIR格式并转换为ms格式时,遇到了一些格式兼容性的问题。
5. 手机侧推理性能:在手机侧进行推理时,部分设备的性能不够强大,导致推理时间较长,体验不够流畅。
4.2 解决问题的过程及体会
1. 数据集不平衡:通过数据增强和采样技术来平衡数据集。例如,使用随机水平翻转、随机裁剪等数据增强方法增加少数类别样本的数量,同时对多数类别进行欠采样。
2. 训练时间较长:采用多GPU并行训练和分布式训练技术,加速训练过程。同时,通过调整学习率和batch size,提高训练效率。
3. 过拟合问题:引入正则化技术,如Dropout和权重衰减,减少模型过拟合的风险。同时,通过增加验证集和交叉验证的方法,增强模型的泛化能力。
4. 模型导出与转换:详细阅读MindSpore的官方文档,了解模型导出与转换的正确步骤和注意事项。通过实验和调试,解决了模型格式兼容性的问题。
4.3 实验收获
1. 数据预处理与增强:掌握了如何使用MindSpore Dataset API进行数据预处理和增强,提升了数据的质量和多样性。
2. 模型训练与优化:熟悉了使用MindSpore框架进行模型训练和优化的流程,包括数据加载、模型定义、训练、评估和保存等步骤。
3. 模型微调:学习了如何在预训练模型的基础上进行微调,提高了模型在特定任务上的表现。
4. 模型导出与部署:了解了如何将训练好的模型导出为MindIR格式,并部署到移动设备上进行推理。
5. 调参与性能优化:通过调整学习率、batch size等超参数,优化了模型的训练和推理性能。
4.4 实验结论
通过本次实验,我们成功地使用MindSpore框架训练了一个用于垃圾分类的MobileNetV2模型,并将模型部署到了移动设备上进行实时推理。实验结果表明,模型在验证集上的精度达到了95%,具有较好的分类性能。同时,通过优化训练流程和模型结构,解决了训练时间长和过拟合等问题。
本次实验不仅提升了我们在深度学习和模型训练方面的技能,还增强了我们在实际应用中解决问题的能力。通过实验,我们深刻认识到数据质量、模型结构和优化策略对模型性能的重要性,本次实验为我们提供了一个完整的AI项目开发流程,从数据准备、模型训练、优化到最终部署,涵盖了深度学习应用的各个环节,具有重要的实践意义和应用价值。
5. 参考文献
环境搭建:
保姆式教程教你如何一步步在window10系统下安装mindspore框架+pycharm导入python遇到的问题解决_python怎么导入mindspore-CSDN博客
垃圾分类模型:
https://www.mindspore.cn/tutorials/zh-CN/r1.8/beginner/train.html
mindspore官网:
6. 源代码
path= "minspore/Garbage_Data"//自己设置data的路径
import mindspore.dataset as ds
import mindspore.dataset.vision as vision
def create_dataset(path, batch_size=50, train=True, image_size=224):
#dataset = ds.ImageFolderDataset(path, num_parallel_workers=8, class_indexing={"croissants": 0, "dog": 1})
dataset = ds.ImageFolderDataset(path, num_parallel_workers=8, class_indexing={"cardboard": 0, "glass": 1, "metal": 2, "paper": 3, "plastic": 4, "trash": 5})
# 图像增强操作
mean = [0.485 * 255, 0.456 * 255, 0.406 * 255]
std = [0.229 * 255, 0.224 * 255, 0.225 * 255]
if train:
trans = [
vision.RandomCropDecodeResize(image_size, scale=(0.08, 1.0), ratio=(0.75, 1.333)),
vision.RandomHorizontalFlip(prob=0.5),
vision.Normalize(mean=mean, std=std),
vision.HWC2CHW()
]
else:
trans = [
vision.Decode(),
vision.Resize(256),
vision.CenterCrop(image_size),
vision.Normalize(mean=mean, std=std),
vision.HWC2CHW()
]
dataset = dataset.map(operations=trans, input_columns="image", num_parallel_workers=8)
# 设置batch_size的大小,若最后一次抓取的样本数小于batch_size,则丢弃
dataset = dataset.batch(batch_size, drop_remainder=True)
return dataset
# 加载训练数据集和验证数据集用于后续的模型训练和验证。
# 加载训练数据集
train_path = "Garbage_Data/train"
dataset_train = create_dataset(train_path, train=True)
# 加载验证数据集
val_path = "Garbage_Data/val"
dataset_val = create_dataset(val_path, train=False)
# ## 模型训练
#
# 本案例使用预训练模型在狗与牛角包的分类数据集上对模型进行微调,并将训练好的CKPT模型文件转换为MINDIR格式,用于后续手机侧的部署。
#
# ### MobileNet V2模型原理
#
# MobileNet网络是由Google团队于2017年提出的专注于移动端、嵌入式或IoT设备的轻量级CNN网络,相比于传统的卷积神经网络,MobileNet网络使用深度可分离卷积(Depthwise Separable Convolution)的思想在准确率小幅度降低的前提下,大大减小了模型参数与运算量。并引入宽度系数 $\alpha$ 和分辨率系数 $\beta$ 使模型满足不同应用场景的需求。
#
# 由于MobileNet网络中Relu激活函数处理低维特征信息时会存在大量的丢失,所以MobileNetV2网络提出使用倒残差结构(Inverted residual block)和Linear Bottlenecks来设计网络,以提高模型的准确率,且优化后的模型更小。
#
# ![mobilenet](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.8/tutorials/source_zh_cn/beginner/images/mobilenet.png)
#
# 图中Inverted residual block结构是先使用1x1卷积进行升维,然后使用3x3的DepthWise卷积,最后使用1x1的卷积进行降维,与Residual block结构相反。Residual block是先使用1x1的卷积进行降维,然后使用3x3的卷积,最后使用1x1的卷积进行升维。
#
# > [详细内容可参见MobileNet V2论文](https://arxiv.org/pdf/1801.04381.pdf)
# ### 下载预训练模型
#
# 下载案例所需的[MobileNetV2预训练模型的ckpt文件](https://download.mindspore.cn/vision/classification/mobilenet_v2_1.0_224.ckpt),预训练模型的宽度系数$\alpha= 1.0$,输入图像大小为(224, 224), 将下载的预训练模型保存在当前目录下。使用MindSpore Vision中的`DownLoad`下载预训练模型文件到当前目录下,示例代码如下所示:
# In[4]:
from mindvision.dataset import DownLoad
models_url = "https://download.mindspore.cn/vision/classification/mobilenet_v2_1.0_224.ckpt"
dl = DownLoad()
# 下载预训练模型文件
dl.download_url(models_url)
# ### MobileNet V2模型微调
#
# 本章使用MobileNet V2的预训练模型进行微调,通过删除MobileNet V2预训练模型中最后一个用于分类的1x1的卷积层的参数,使用狗与牛角包数据集对模型进行重新训练以更新模型参数。
# In[5]:
import mindspore.nn as nn
import mindspore as ms
from mindvision.classification.models import mobilenet_v2
from mindvision.engine.loss import CrossEntropySmooth
# 创建模型,其中目标分类数为6,图像输入大小为(224,224)
network = mobilenet_v2(num_classes=6, resize=224)
# 模型参数存入到param_dict
param_dict = ms.load_checkpoint("./mobilenet_v2_1.0_224.ckpt")
# 获取mobilenet_v2网络最后一个卷积层的参数名
filter_list = [x.name for x in network.head.classifier.get_parameters()]
# 删除预训练模型的最后一个卷积层
def filter_ckpt_parameter(origin_dict, param_filter):
for key in list(origin_dict.keys()):
for name in param_filter:
if name in key:
print("Delete parameter from checkpoint: ", key)
del origin_dict[key]
break
filter_ckpt_parameter(param_dict, filter_list)
# 加载预训练模型参数作为网络初始化权重
ms.load_param_into_net(network, param_dict)
# 定义优化器
network_opt = nn.Momentum(params=network.trainable_params(), learning_rate=0.01, momentum=0.9)
# 定义损失函数
network_loss = CrossEntropySmooth(sparse=True, reduction="mean", smooth_factor=0.1, classes_num=2)
# 定义评价指标
metrics = {"Accuracy": nn.Accuracy()}
# 初始化模型
model = ms.Model(network, loss_fn=network_loss, optimizer=network_opt, metrics=metrics)
# > 上述WARNING是由于模型微调需要删除预训练模型的最后一个卷积层的参数,所以加载预训练模型会显示`head.classifier`参数未加载,`head.classifier`参数会使用创建模型时的初始化值。
#
# ### 模型训练与评估
#
# 训练并评估网络,使用MindSpore Vision中的`mindvision.engine.callback.ValAccMonitor`接口打印训练的损失值和评估精度,且在训练完成后,保存评估精度最高的CKPT文件`best.ckpt`在当前目录下。
# In[6]:
from mindvision.engine.callback import ValAccMonitor
import mindspore as ms
num_epochs = 1
# 模型训练与验证,训练完成后保存验证精度最高的ckpt文件(best.ckpt)到当前目录下
model.train(num_epochs,
dataset_train,
callbacks=[ValAccMonitor(model, dataset_val, num_epochs), ms.TimeMonitor()])
# ### 可视化模型预测
#
# 定义 `visualize_model` 函数,使用上述验证精度最高的模型对输入图像进行预测,并将预测结果可视化。
# In[8]:
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import mindspore as ms
def visualize_model(path):
image = Image.open(path).convert("RGB")
image = image.resize((224, 224))
plt.imshow(image)
# 归一化处理
mean = np.array([0.485 * 255, 0.456 * 255, 0.406 * 255])
std = np.array([0.229 * 255, 0.224 * 255, 0.225 * 255])
image = np.array(image)
image = (image - mean) / std
image = image.astype(np.float32)
# 图像通道由(h, w, c)转换为(c, h, w)
image = np.transpose(image, (2, 0, 1))
# 扩展数据维数为(1, c, h, w)
image = np.expand_dims(image, axis=0)
# 定义并加载网络
net = mobilenet_v2(num_classes=6, resize=224)
param_dict = ms.load_checkpoint("./best.ckpt")
ms.load_param_into_net(net, param_dict)
model = ms.Model(net)
# 模型预测
pre = model.predict(ms.Tensor(image))
result = np.argmax(pre)
#class_name = {0: "Croissants", 1: "Dog"}
class_name = {0:"cardboard", 1:"glass", 2:"metal", 3:"paper", 4: "plastic", 5: "trash"}
plt.title(f"Predict: {class_name[result]}")
return result
image1 = "Garbage_Data/infer/cardboard1.jpg"
plt.figure(figsize=(15, 7))
plt.subplot(1, 2, 1)
visualize_model(image1)
image2 = "Garbage_Data/infer/glass1.jpg"
plt.figure(figsize=(15, 7))
plt.subplot(1, 2, 2)
visualize_model(image2)
image3 = "Garbage_Data/infer/metal1.jpg"
plt.figure(figsize=(15, 7))
plt.subplot(1, 2, 2)
visualize_model(image3)
image4 = "Garbage_Data/infer/paper1.jpg"
plt.figure(figsize=(15, 7))
plt.subplot(1, 2, 2)
visualize_model(image4)
image5 = "Garbage_Data/infer/plastic1.jpg"
plt.figure(figsize=(15, 7))
plt.subplot(1, 2, 2)
visualize_model(image5)
image6 = "Garbage_Data/infer/trash1.jpg"
plt.figure(figsize=(15, 7))
plt.subplot(1, 2, 2)
visualize_model(image6)
plt.show()
# ### 模型导出
#
# 在模型训练完后,训练完成后的网络模型(即CKPT文件)转换为MindIR格式,用于后续手机侧的推理。通过`export`接口会在当前目录下会生成`mobilenet_v2_1.0_224.mindir`文件。
# In[9]:
import mindspore as ms
# 定义并加载网络参数
net = mobilenet_v2(num_classes=6, resize=224)
param_dict = ms.load_checkpoint("best.ckpt")
ms.load_param_into_net(net, param_dict)
# 将模型由ckpt格式导出为MINDIR格式
input_np = np.random.uniform(0.0, 1.0, size=[1, 3, 224, 224]).astype(np.float32)
ms.export(net, ms.Tensor(input_np), file_name="mobilenet_v2_1.0_224", file_format="MINDIR")
附数据集:
我用夸克网盘分享了「Garbage_Data.zip」,点击链接即可保存。
链接:https://pan.quark.cn/s/45bed34c5004
提取码:reSf