【华为云-上云之路】【2020华为云AI实战营】【每天进步一点点】基于ModelArts 实现人脸识别(MXNET)
什么是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)
设置超参,就是在训练前需要人工指定的参数。设置训练需要的超参:
设置模型训练的设备环境;
设置训练时的batch size,固定图像大小的信息,和梯度归一化等参数;
设置优化器参数;
设置训练时的回调函数;
设置模型参数初始化方法
# 一次训练输入的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. 执行推理
模型推理共分为以下几个步骤:
导入读取图片所需的库;
输出测试的原图;
测试原图转换成模型需要的数据;
加载推理使用的模型结构;
推理计算并输出结果。
代码如下:
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中,逐步运行即可。
- 点赞
- 收藏
- 关注作者
评论(0)