【华为云-上云之路】【2020华为云AI实战营】【每天进步一点点】基于ModelArts 实现人脸识别(MXNET)

举报
Tianyi_Li 发表于 2020/05/30 14:42:34 2020/05/30
【摘要】 在ModelArts实现基于MXNET的人脸识别,这里我们用到的就是ModelArts中的NoteBook,也用到了OBS桶。正好在ModelArts上做了一个人脸识别的云端实验,在这里分享一下执行过程,下面开始吧。

什么是ModelArts? 来看看介绍


这里我们用到的就是ModelArts中的NoteBook,也用到了OBS桶。正好在ModelArts上做了一个人脸识别的云端实验,在这里分享一下执行过程,下面开始吧。


1. 创建NoteBook

选择左侧栏,点击“开发环境”-> “Notebook”->“创建”,如下图所示:



进入Notebook创建页面。参数:

① 计费方式:按需计费

② 名称:任意,如face_recognition

③ 自动停止:关闭

④ 工作环境:Python3

⑤ 资源池:公共资源池

⑥ 类型:GPU

⑦ 规格:GPU: 1*v100NV32 CPU: 8 核 64GiB

⑧ 存储配置:云硬盘⑧ 磁盘规格:默认




参数填写完毕后,点击“下一步”,查看Notebook实例预览信息,确认无误后点击“提交”。创建任务提交成功,点击页面的“返回Notebook列表”,返回Notebook列表页,等待Notebook创建成功,创建成功后状态会变成“运行中”,如下图所示:



2. 创建Notebook Python开发环境

 点击“打开”按钮进入Notebook。点击“New” ->”MXNet 1.2.1”创建 Notebook Python开发环境。如下图所示:



重命名刚刚创建的Notebook Python开发环境。点击“Untitled”,我们可以填写一个跟本实验相关的名称,然后点击“Rename”按钮,如下图所示:



我们打印一行字符串,按Shift+Enter(该组合键是Notebook中执行代码的快捷键)或者点击下图“Run”。

print("hello notebook!")

  查看代码执行结果。如下图所示:



3. 准备源代码和数据

相关资源已经保存在OBS中,通过ModelArts SDK将资源下载,并解压到当前目录下。解压后,当前目录包含src和data两个目录,分别存有源代码和数据集(可进入Notebook查看)。代码如下:

from modelarts.session import Session
session = Session()
if session.region_name=='cn-north-4':
    session.download_data(bucket_path='ai-course-common-20-bj4/face_recognition/face_recognition.tar',
    path='./face_recognition.tar')
else:
    session.download_data(bucket_path='ai-course-common-20/face_recognition/face_recognition.tar',
    path='./face_recognition.tar')
# 使用tar命令解压资源包
!tar xf ./face_recognition.tar

日志如下:

Successfully download file ai-course-common-20-bj4/face_recognition/face_recognition.tar from OBS to local ./face_recognition.tar

如下图所示:


执行成功,回到ModelArts“Notebook”列表即可看到资源,后面步骤生成的文件及文件夹同样可以这样查看,如下图所示:



4. 创建OBS桶

鼠标移动到云桌面浏览器页面中左侧菜单栏,点击“服务列表”-> 选择“存储”的“对象存储服务”,进入后右上角点击“创建桶”。

参数:

① 区域:华北-北京四

② 桶名称:自定义即可(需要记住此名称以备后续步骤使用)

③ 存储类别:标准存储

④ 桶策略:私有

⑤ 归档数据直读:关闭

⑥ 多AZ:关闭

点击右下角“立即创建”,如下图所示:



5. 配置信息

将运行结果上传至OBS中,设置相关的参数(使用自己真实的桶名替换掉*号):

• BUCKET_NAME:自己的OBS桶名。

切换至前面创建的Notebook Python开发环境界面,将下方代码拷贝粘贴并替换桶名称后,执行“run”,此段代码无输出。

BUCKET_NAME = '*'
UNIQUE_ID = 'face-data'
OBS_BASE_PATH = BUCKET_NAME + '/' + UNIQUE_ID

6. 导入基本信息

执行以下代码,导入使用的Python开发基本工具库,代码无输出。

MXNet框架是我们使用的AI编程框架;

argparse库用于解析外部输入参数;

src.face_resnet包含了人脸识别的卷积模型骨干。

import os
import mxnet as mx
import argparse
from src.face_resnet import get_symbol
import warnings
warnings.filterwarnings('ignore')
import logginglogger = logging.getLogger()
logger.setLevel(logging.INFO)

训练数据量和训练的关系说明:

per_batch_size:指在每个GPU上一次训练一批数据的大小,调整这个值越大,表示一次训练的数据量也就越大,这个公式代表了梯度更新的方法大小,

m为batch_size大小,

Var是指方差,显然当batch_size越大,方差也就越大,而方差越大,也就表示数据越稳定,在深度学习中也就表示训练得越稳定。

结合上面讲的学习率大小的调整,在显存可承受范围内,越大的batch_size使得学习更稳定情况下,学习率也可以随着调整得更大。

def parse_args():
    parser = argparse.ArgumentParser(description='Train face network')
    parser.add_argument('--data_dir', default='data',
                        help='training set directory')# 训练数据路径
    parser.add_argument('--train_url', type=str, default='ckpt',
                        help='train path')# 训练模型输出路径
    parser.add_argument('--num_epoch', type=int, default=20,
                        help='training epoch size.')# 训练epoch数量
    parser.add_argument('--num_layers', type=int, default=18,
                        help='specify network layer, 18 50 100')# 使用的ResNet卷积层层数
    parser.add_argument('--image_size', default='112,112',
                        help='specify input image height and width')# 输入图像的大小,高、宽
    parser.add_argument('--lr', type=float, default=0.005,
                        help='start learning rate')# 训练开始的学习率大小
    parser.add_argument('--wd', type=float, default=0.0005,
                        help='weight decay')# 权值衰减系数
    parser.add_argument('--mom', type=float, default=0.9,
                        help='momentum')# 动量
    parser.add_argument('--per_batch_size', type=int, default=8,
                        help='batch size in each context')# 每一个GPU的batch size大小
    parser.add_argument('--epoch_image_num', type=int, default=200,
                        help='image size in one epoch')# 训练样本大小
    parser.add_argument('--train_file', type=str, default='celeb_train.rec',
                        help='train')# 训练数据文件
    parser.add_argument('--val_file', type=str, default='celeb_val.rec',
                        help='val')# 验证集文件
    parser.add_argument('--num_classes', type=int, default=5,
                        help='classes number')# 分类数
    parser.add_argument('--num_embed', type=int, default=8,
                        help='embedding number')# 嵌入层神经元数量
    parser.add_argument('--num_gpus', type=int, default=1,
                        help='GPUs number')# GPU数量
    parser.add_argument('--save_frequency', type=int, default=1,
                        help='save frequency')# 保存模型的频率
    parser.add_argument('--export_model', type=bool, default=False,
                        help='change train url to model,metric.json')#是否改变train url结构
    
    args, _ = parser.parse_known_args()
    return args

args = parse_args()
# 模型所在的环境,指cpu或者哪块gpu
ctx = [mx.gpu(x) for x in range(args.num_gpus)] if args.num_gpus>0 else [mx.cpu()]
print(ctx)

设置超参,就是在训练前需要人工指定的参数。设置训练需要的超参:

  1. 设置模型训练的设备环境;

  2.  设置训练时的batch size,固定图像大小的信息,和梯度归一化等参数;

  3.  设置优化器参数;

  4.  设置训练时的回调函数;

  5.  设置模型参数初始化方法

# 一次训练输入的batch size大小的数据
batch_size = int(args.per_batch_size * (args.num_gpus if args.num_gpus>0 else 1))
print('batch size: ', batch_size)
# 输入图像的高和宽
image_h, image_w = [int(x) for x in args.image_size.split(',')]
print('image height: ', image_h, ', image width: ', image_w)
# 梯度归一化参数,大小为1.0 / batch_size
_rescale = 1.0 / batch_size
print('rescale grad: ', _rescale)
# 优化器参数,包含学习率调度器、权值衰减系数等
optimizer_params = {'learning_rate': args.lr,
                    'wd' : args.wd,
                    'lr_scheduler': None,
                    'clip_gradient': 5,
                    'momentum' : args.mom,
                    'multi_precision' : True,
                    'rescale_grad': _rescale,
                    }
print(optimizer_params)
# 每一个step训练完后的操作
batch_end_callbacks = [mx.callback.Speedometer(batch_size, 10, auto_reset=True)]
# 每一个epoch训练完后的操作
# 这里的模型保存路径为模型输出文件目录下的model目录,模型文件以face开头
epoch_end_callbacks = mx.callback.do_checkpoint(
    os.path.join(args.train_url,'model/face'), args.save_frequency)
# 模型评估方法,准确度和交叉熵
eval_metrics = [mx.metric.Accuracy(), mx.metric.CrossEntropy()]
# 模型参数的初始化方法
initializer = mx.init.Xavier(rnd_type='gaussian', factor_type="out", magnitude=2)


7. 读取数据集,准备训练

人脸识别数据集使用的是明星人脸图像集CelebFace,经过人脸3D对齐等操作制作成了适合分类模型训练的数据,每张图像的大小为112*112,样本图像如下:


数据集格式为MXNet专用的rec格式,rec格式的数据集相比原图读取速度更快。使用MXNet自带的mx.io.ImageRecordIter方法读取rec格式数据集,mx.io.ImageRecordIter可以在读取数据时就实现数据增强的方法,这里做了随机翻转、数据集重新洗牌等增强。拷贝执行如下代码,这里定义数据加载器的对象,加载成功会打印输出 “data loaded!”。

# 训练集和验证集的rec文件路径
path_imgrec = os.path.join(args.data_dir, args.train_file)
path_val_imgrec = os.path.join(args.data_dir, args.val_file)
# 使用mxnet自带的mx.io.ImageRecordIter加载数据
val_dataiter = mx.io.ImageRecordIter(
        path_imgrec=path_val_imgrec,
        data_shape=(3, image_h, image_w),
        batch_size=batch_size,
        resize=image_w,                 # 图像按照其最短边resize到定义好的宽长
        shuffle=False,
        preprocess_threads=2)
train_dataiter = mx.io.ImageRecordIter(
        path_imgrec=path_imgrec,
        data_shape=(3, image_h, image_w),
        num_parts=1,                    # 训练的节点数
        part_index=0,                   # 本机所在的节点id
        batch_size=batch_size,
        resize=image_w,
        rand_crop=False,
        rand_mirror=True,              # 图像随机翻转
        shuffle=True,                  # 数据集随机洗牌
        preprocess_threads=2)
print('data loaded!')
import matplotlib.pyplot as plt
next_data_batch = next(iter(train_dataiter))
img = next_data_batch.data[0][0].transpose(axes=(2,1,0))
img = img.asnumpy().astype('uint8')
# 画图,数据增强后的图片
%matplotlib inline
fig = plt.figure()
plt.imshow(img)
plt.show()

搭建网络,人脸识别算法使用的模型骨干(backbone)为ResNet-50,基于ResNet-50模型构建人脸识别神经网络。src目录(打开的Notebook下可以查看)下的face_resnet.py源代码文件加载模型骨干,并将其分类层作为嵌入层,分类数设置为嵌入层神经元数。在嵌入层后面接一个分类数为num_classes的分类层,就可以得到本实验的人脸识别神经网络了。拷贝执行如下代码,这里会输出一个 mx.mod.Module 对象。

# 初始化模型参数为None,这里可以加载预训练的模型参数
arg_params = None
aux_params = None
# 得到嵌入层模型的Symbol
embedding = get_symbol(num_classes=args.num_embed, num_layers=args.num_layers)
# 设置分类层的权值
_weight = mx.symbol.Variable("fc7_weight",
                             shape=(args.num_classes, args.num_embed),
                             lr_mult=1.0, wd_mult=1.0)
_bias = mx.symbol.Variable('fc7_bias', lr_mult=2.0, wd_mult=0.0)
# 全连接层
fc7 = mx.sym.FullyConnected(data=embedding, weight = _weight,
                            bias = _bias, num_hidden=args.num_classes, name='fc7')
all_label = mx.symbol.Variable('softmax_label')
softmax = mx.symbol.SoftmaxOutput(data=fc7, label = all_label, name='softmax')
# 输入模型环境和Symbol
model = mx.mod.Module(context=ctx, symbol=softmax)
print(model)

开始训练

model_output_dir = args.train_url + '/model/'

if os.path.exists(model_output_dir) == False:
    os.makedirs(model_output_dir)

model.fit(train_dataiter,
          begin_epoch        = 0,
          num_epoch          = args.num_epoch,
          eval_data          = val_dataiter,
          eval_metric        = eval_metrics,
          kvstore            = 'local',
          optimizer          = 'nag',
          optimizer_params   = optimizer_params,
          initializer        = initializer,
          arg_params         = arg_params,
          aux_params         = aux_params,
          allow_missing      = True,
          batch_end_callback = batch_end_callbacks,
          epoch_end_callback = epoch_end_callbacks )

部分日志如下:


8. 执行推理

模型推理共分为以下几个步骤:

  1. 导入读取图片所需的库;

  2.  输出测试的原图;

  3.  测试原图转换成模型需要的数据;

  4.  加载推理使用的模型结构;

  5.  推理计算并输出结果。

代码如下:

import moxing as mox

# The obs_dst_path pattern: {BUCKET_NAME}/{UNIQUE_ID}/face_recognition/results/{TRAIN_URL}
obs_dst_path = "s3://" + OBS_BASE_PATH + "/face_recognition/results/" + args.train_url + '/'
mox.file.copy_parallel(args.train_url+'/model',
                      obs_dst_path)
import mxnet.image as image
import moxing as mox
import mxnet as mx
import matplotlib.pyplot as plt
import os
# 设置类别名,由于不能知道每张人脸的真实名字,用数字来替代
class_names = range(5)
# 人脸图像路径,这里可以任意选择一张人脸图像,并上传到自己的OBS路径
# 如s3://ai-course-001/face_recognition/data/下的test.png
file_name = args.data_dir + '/test.png'
# 以二进制形式,将图片读入内存
img = mox.file.read(file_name, binary=True)
# 查看图片
orig_img = mx.img.imdecode(img, to_rgb=1).asnumpy().astype('uint8')
plt.imshow(orig_img)
plt.show()
# 图像处理
# 将图像处理成(3,112,112)大小
[h, w] = [112, 112]
img_arr = mx.img.imdecode(img, to_rgb=1)
# 首先转换成NDArray形式
img_arr = mx.img.resize_short(img_arr, w, 0)
# 翻转,经过翻转的图像不能通过plt显示
img_arr = mx.nd.transpose(img_arr, (2, 0, 1))
img_arr = mx.nd.expand_dims(img_arr, axis=0)
# 将图像绑定到cpu上
d = [img_arr.as_in_context(mx.cpu())]
print(d)
# 模型文件位置,比如由上面训练得到的位置train_url+'model',
# 这个路径位置下面必须有.json和.params的模型文件
ckpt = os.path.join(args.train_url, 'model/face')
# 加载第几个epoch
load_epoch = 1
# 加载模型文件,sym为推理模型结构,arg_params, aux_params为模型参数
sym, arg_params, aux_params = mx.model.load_checkpoint(ckpt, load_epoch)
# 选择使用cpu进行推理,数据名默认为data,推理无需输入label
mx_model = mx.mod.Module(symbol=sym, context=mx.cpu(),
                                      data_names=['data'], label_names=None)
# 绑定模型计算模块到底层计算引擎,绑定的数据大小默认为(1,3,112,112)
mx_model.bind(for_training=False, data_shapes=[('data',(1,3,112,112))])
# 设置模型的参数
mx_model.set_params(arg_params, aux_params, allow_missing=True)
# 模型结果后处理方法
def _postprocess(data):
    dim = len(data[0].shape)
    if dim > 2:
        data = mx.nd.array(np.squeeze(data.asnumpy(), axis=tuple(range(dim)[2:])))
    # 将得到的结果排序,选择可能性最大的类别
    sorted_prob = mx.nd.argsort(data[0], is_ascend=False)
    top_prob = list(map(lambda x: int(x.asscalar()), sorted_prob[0:2]))
    # 输出推理得到的类别和对应可能性
    return {"predicted_label": class_names[top_prob[0]],
            "scores": [[class_names[i], float(data[0, i].asscalar())] for i in top_prob]}

# 进行前向推理
mx_model.forward(mx.io.DataBatch(d))
# 输出
print(_postprocess(mx_model.get_outputs()[0]))

最后得到输出


至此,基于ModelArts的MXNET框架的人脸识别完成了,包括训练和推理,快来试试吧。最后附上完成代码,在附件,下载解压缩后,上传face_recognition.ipynb到NoteBook中,逐步运行即可。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。