关于人工智能:课程作业经验基于Mindspore实现MTCNN

3次阅读

共计 4105 个字符,预计需要花费 11 分钟才能阅读完成。

介绍
本我的项目的起源是我选修的北航与华为单干的《AI 开源计算零碎前沿技术》课程大作业。课程请到华为的各位专家介绍了华为目前的 AI 软硬件体系,并解说了许多人工智能畛域的常识。

我的大作业选题是用轻量级的网络模型 backbone,实现手机端人脸检测算法,并应用 MindSpore Lite 在端侧推理部署。比拟遗憾的是整体我的项目开发进度慢于预期,加上挪动端指标检测 APP 的源码中应用了 JNI 等我不相熟的接口,最初没有实现挪动端的部署,仅实现了人脸检测 + 关键点检测神经网络的搭建、训练和测试。

后期调研之后,我决定基于 MindSpore 框架实现论文《Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks》中的人脸识别和关键点检测网络 MTCNN。该网络的构造并不简单,包含 PNet、RNet、ONet 这三个构造类似的网络。因为我对 Python 和机器学习都是初学者,我的项目过程中遇到了不少问题。解决过程中有了一些教训心得,斗胆在此做些分享。

本我的项目中的 MTCNN 局部代码基于夜雨飘零 1 的 Github 我的项目和华为 MindSpore 官网文档撰写,包含数据集生成、网络结构定义、网络训练、模型测试的代码。

本我的项目的 Gitee 地址。

0 训练环境
笔记本训练环境:

CPU:i7-11800H
GPU:RTX 3070
Windows10:
CUDA11.6+cudnn8.x
MindSpore1.7.0-CPU
Pytorch1.11.0
Ubuntu22.04:
CUDA11.1+cudnn8.0.4
MindSpore1.7.0-GPU
服务器训练环境:

minsspore1.7.0-cuda10.1-py3.7-ubuntu18.04, GPU: 2*V100(64GB), CPU: 16 核 128GB
minsspore1.7.0-cuda10.1-py3.7-ubuntu18.04, GPU: 1*P100(16GB), CPU: 8 核 64GB
1 数据集下载
WIDER Face 下载训练数据压缩包 WIDER Face Training Images,解压的 WIDER_train 文件夹搁置到 dataset 文件夹下。并下载 Face annotations,解压把外面的 wider_face_train_bbx_gt.txt 文件放在 dataset 目录下,

在 Deep Convolutional Network Cascade for Facial Point Detection 下载 Training set 并解压,将外面的 lfw_5590 和 net_7876 文件夹搁置到 dataset 下

最终,dataset 目录下应该有文件夹 lfw_5590,net_7876,WIDER_train,有标注文件 testImageList.txt,trainImageList.txt,wider_face_train.txt,wider_face_train_bbx_gt.txt(这四个 txt 文件已放在 dataset 文件夹下)。

2 文件夹性能阐明
dataset:最开始只蕴含原始数据集,后续会保留用于训练每个网络的数据集。

infer_models:保留训练出的模型 PNet.ckpt,RNet.ckpt,ONet.ckpt,我训练出的模型文件曾经放在该文件夹下。

models:

Loss .py:定义损失函数。
PNet .py,RNet .py,ONet .py:定义网络。
load_models:包含 PNet .py、RNet .py、ONet.py 三个文件,实际上 RNet.py 和 ONet.py 与 models 文件夹中的文件截然不同,只有 PNet 做了改变。这是因为训练中 PNet 的输出是 12*12 的图片,通过卷积之后最初两维都是 1,进行了 squeeze 操作。但在推理过程中,输出 PNet 的是图像金字塔,再应用 squeeze 会产生谬误,故 load_models 文件夹中的 PNet 缩小了 squeeze 操作。

train_PNet:蕴含生成训练 PNet 的数据集的文件 generate_PNet_data.py 和训练 PNet 的文件 train_PNet.py。train_RNet 和 train_ONet 文件夹相似。

utils:蕴含加载图片、解决图片的函数。

infer_camera.py:调用电脑摄像头,对摄像头失去的图片进行推理,实时显示人脸回归框和五个关键点。

infer_path.py:解决指定门路的图片,辨认图片中的人脸,显示人脸回归框和五个关键点(可解决多人)。

modelToMNDIR .py:将.ckpt 格局的模型转换为 mindir 格局。

3 模型训练过程
MTCNN 是一个级联网络模型,蕴含 PNet,RNet,ONet 三个网络。这三个网络的计算结果一个比一个准确,后两个网络都会起到对其前一个网络的后果进行进一步筛选的作用。生成数据集时会调用之前网络进行推理,将之前网络的推理后果用于生成下一个网络的数据集,网络训练过程比拟繁琐。

训练步骤:

进入 train_PNet 文件夹,运行 generate_PNet_data.py 生成训练 PNet 的数据集;运行 train_PNet.py 训练 PNet。
进入 train_RNet 文件夹,运行 generate_RNet_data.py 生成训练 RNet 的数据集;运行 train_RNet.py 训练 RNet。
进入 train_ONet 文件夹,运行 generate_ONet_data.py 生成训练 ONet 的数据集;运行 train_ONet.py 训练 ONet。
训练完结后,如果想验证模型训练后果,能够运行 infer_camera.py,调用电脑的摄像头,模型参数正确则能够显示人脸回归框和关键点。也能够运行 infer_path.py,对指定图片进行推理。

4 我的项目心得
最开始网络训练时 loss 不降落,我推断出是梯度反向流传的问题,但迟迟没有解决。感激 ms 技术交换群的 wgx 老师,帮我批改了 loss 函数和训练 PNet 的代码(训练 RNet 和 ONet 的代码是相似的),并标准了我的模型定义。

4.1 模型训练
MindSpore 中数据集的加载形式和 Pytorch 不同,所以我自定义了数据库加载类 GetDatasetGenerator 来获取数据。

在 Pytorch 中,网络模型和损失函数是离开计算的,应用起来比拟自在。MindSpore 中模型的训练流程相比之下要固定很多,但也的确对代码做出了简化。因为该模型的损失函数对类别、回归框、关键点三局部的损失做加权求和,故须要自定义损失函数。在 ms 中自定义的损失函数要利用到网络中须要再定义一个类,我定义的这个类名为 NetWithLossCell。该类中调用网络 backbone 失去计算结果,再将后果送到 loss 函数中计算损失。训练过程中如果想查看损失函数,则应该在 model.train 中传入 callbacks,用 LossMonitor 来输入损失。

夜雨飘零老师的代码里训练过程中还会输入模型的精度,这在 Pytorch 中是间接计算的,但 ms 的 Model 类想返回两个值十分艰难,所以 wgx 老师帮忙我通过在 callbacks 中传入一个精度计算函数来实现了这个性能。该函数的实质是对数据集做一次 eval,因为传入的是整个训练集所以十分耗时,我在训练过程中没有应用。实践上来说如果能够提取训练集的一部分传入精度计算函数就能够节约大量工夫并取得精度。

4.2 API 映射问题
Pytorch 版代码中,MaxPool2d 的 ceil 参数设置为 True。因为 mindspore 中的 MaxPool2d 办法没有 ceil 这个参数,故间接应用会导致后果的 shape 与论文中的不同,于是我在进行 pool 之前先进行了 padding,能够保障每层的输入后果与论文中统一。

pytorch 版代码中的其余办法都能够在 mindspore 中找到性能甚至名称一样的办法,写法上留神一下即可。

4.3 内存溢出和推理速度过慢问题
生成训练 RNet 和 ONet 用的数据集时会采纳图像金字塔的思维,这会导致输出到 MindSpore 中的图像 shape 一直变动。如果是在 GRAPH_MODE 下训练模型,仿佛会让框架反复生成网络,导致程序的内存占用量一直回升,最终在 Linux 零碎中导致内存耗尽程序主动退出。如果是 PYNATIVE_MODE,程序的内存占用量会一直回升,但最终占用量不会像在 GRAPH_MODE 下那么夸大,生成 RNet 的数据集用了 25G 左右,我开了 32G 的虚拟内存之后就能够应酬。不过生成 ONet 用的数据集时,因为生成数据集的写法问题,导致内存占用量太大,我在 linux 零碎中新开的虚拟内存没方法挂载上根节点,故最初是在服务器上实现了 ONet 数据集的生成和 ONet 的训练。

对于内存溢出,还有一个我不分明起因的问题,在 minsspore1.7.0-cuda10.1-py3.7-ubuntu18.04, GPU: 1P100(16GB), CPU: 8 核 64GB 这一环境上运行 generate_ONet_data.py 的时候产生了内存溢出的状况,64GB 内存不够用,于是我开了 minsspore1.7.0-cuda10.1-py3.7-ubuntu18.04, GPU: 2V100(64GB), CPU: 16 核 128GB 这个环境,但在这个环境下内存最多用了 48G,代码是截然不同的。能够发现,ms 框架在不同的环境上运行成果有肯定的区别。

最开始的训练过程中,我发现 ms 代码的模型解决数据集中的一张图片用时比 pytorch 版本代码长很多。剖析后发现起因还是最开始 loss 不降落,意味着 PNet 等模型没有学到什么货色,后续生成训练 RNet 的数据集时就多了许多谬误的回归框等数据,因而增大了数据量,减少了解决工夫。起初调整模型使得模型能够正确推理,失去正确的数据集之后,ms 版本代码的推理速度就失常了,并且哪怕是 PYNATIVE_MODE 推理速度依然比 pytorch 版本代码快(因为怕推理过程中再呈现内存泄露,故没敢用 GRAPH_MODE 训练)。

正文完
 0