基于PaddleClas的人物年龄分类项目

avatar
作者
猴君
阅读量:0

目录

一、任务概述

二、算法研发

2.1 下载数据集

2.2 数据集预处理

2.3 安装PaddleClas套件

2.4 算法训练

2.5 静态图导出

2.6 静态图推理

三、小结


一、任务概述

    最近遇到个需求,需要将图像中的人物区分为成人和小孩,这是一个典型的二分类问题,打算采用飞桨的图像分类套件PaddleClas来完成算法研发。本文记录相关流程。

二、算法研发

2.1 下载数据集

    本文采用MaGaAge_Asian数据集,该数据集主要由亚洲人图片组成,训练集包含40000张图像,验证集包含3495张图像,每张图像都有对应的年龄真值,所有图像均处理成了统一的大小,宽178像素,高218像素。

数据集地址下载链接。数据集部分示例如下图所示:

    该数据集本意是用来做年龄预测的,属于一个数值回归任务,本文将其变成二分类任务,以13岁年龄为界限,小于该年龄的属于小孩,大于该年龄的属于成人。这里之所以选择13岁,因为这个任务是需要筛选出长得很“像”小孩的小孩,13岁以上的青少年很多本身已经长的像成人了,因此,选择13岁作为分界线。

    下面首先对该数据集进行处理。

2.2 数据集预处理

    MaGaAge_Asian数据集每张图片对应的人物年龄存放在list文件夹的两个文件中,其中train_age.txt存放训练集对应的年龄真值,test_age.txt存放验证集对应的年龄真值。下面要写一个脚本,将所有小于13岁的图片移动到一个文件夹内,所有大于等于13岁的图片移动到另一个文件夹内。

#!/usr/bin/env python # -*- encoding: utf-8 -*- ''' @文件        :split_asian.py @说明        :拆分megaage_asian数据集,将小于13岁的移动到一个文件夹,大于等于13岁的移动到另一个文件夹 @时间        :2024/07/16 09:11:16 @作者        :Bin Qian @版本        :1.0 '''   import os import cv2  thr = 13 # 年龄阈值  # 读取年龄列表 agefile = 'megaage_asian/list/test_age.txt' f=open(agefile)  ageLst = f.read().splitlines() f.close()   # 读取图像 imgFolder = 'megaage_asian/val' imgnames = os.listdir(imgFolder) index = 50000 for imgname in imgnames:     imgPath = os.path.join(imgFolder,imgname)     img = cv2.imread(imgPath)     if img is None:         continue     print(imgPath)     imgindex = int(imgname.split('.')[0])     age = int(ageLst[imgindex-1])     if age < thr:         dstFolder = 'ageclas/child'     else:         dstFolder = 'ageclas/adult'          savePath = os.path.join(dstFolder,str(index)+'_asian.jpg')     cv2.imwrite(savePath,img)     index += 1 print('完成')

值得注意的是MaGaAge_Asian数据集中有很多质量较差的图像,这些“脏”图像会影响学习效果,最好手工检查这些数据并将其剔除。

另外,为了能够取得更好的效果,本文从互联网和FFHQ数据集里面再挑选出一些小孩和成人的照片进行补充。部分代码如下:

import os import cv2  # 读取图像 imgFolder = 'adult' imgnames = os.listdir(imgFolder) index = 1 for imgname in imgnames:     imgPath = os.path.join(imgFolder,imgname)     img = cv2.imread(imgPath)     if img is None:         continue     print(imgPath)     dstFolder = 'ageclas/adult'     savePath = os.path.join(dstFolder,str(index)+'_data.jpg')     cv2.imwrite(savePath,img)     index += 1 print('完成')

补充完整后,最后对整理好的数据集进行拆分,并且获得对应的文件列表:

# 导入系统库 import os import random import cv2   # 定义参数 img_folder = 'ageclas' trainlst = 'train_list.txt' vallst = 'val_list.txt' ratio = 0.95 # 训练集占比 labellst='label.txt'    def writeLst(lstpath,namelst):     '''     保存文件列表     '''     print('正在写入 '+lstpath)     random.shuffle (namelst)     # 写入训练样本文件     f=open(lstpath, 'a', encoding='utf-8')     for i in range(len(namelst)):         text = namelst[i]+'\n'         f.write(text)     f.close()     print(lstpath+ '已完成写入')         def main():     '''     主函数     '''     # 查找文件夹     folderlst = os.listdir(img_folder)     print('共找到 %d 个文件夹' % len(folderlst))          # 循环处理     trainnamelst = list()     valnamelst = list()     labelnamelst = list()     for i in range(len(folderlst)):         class_name = folderlst[i]         class_label = i         print('开始处理 '+class_name+' 文件夹')                  # 获取子文件夹文件列表         filenamelst = os.listdir(os.path.join(img_folder,class_name))         totalNum = len(filenamelst)         print('当前文件夹图片数量为: ' + str(totalNum))          trainNum = int(ratio*totalNum)         text =  str(class_label)+ ' ' + class_name         labelnamelst.append(text)                  # 检查并校验图像         for j in range(totalNum):             imgpath = os.path.join(img_folder,class_name,filenamelst[j])             img = cv2.imread(imgpath, cv2.IMREAD_COLOR)             if img is None:                 continue             text = imgpath + ' ' + str(class_label)             if j <= trainNum:                  trainnamelst.append(text)             else:                 valnamelst.append(text)                      writeLst(trainlst,trainnamelst)     writeLst(vallst,valnamelst)        writeLst(labellst,labelnamelst)          print('全部完成')   if __name__ == '__main__':     '''程序入口'''     main() 

运行后会生成train_lst.txt、val_lst.txt以及label.txt三个文件,有了这三个文件就可以使用PaddleClas套件进行算法研发了。

2.3 安装PaddleClas套件

git clone https://gitee.com/paddlepaddle/PaddleClas.git cd PaddleClas sudo python setup.py install

2.4 算法训练

在PaddleClas目录下新建一个配置文件config_lcnet.yaml,采用PPLCNet_x0_5模型来训练,配置文件代码如下:

# global configs Global:   checkpoints: null   pretrained_model: null   output_dir: ./output/   device: gpu   save_interval: 5   eval_during_train: True   eval_interval: 5   epochs: 200   print_batch_step: 10   use_visualdl: True   # used for static mode and model export   image_shape: [3, 224, 224]   save_inference_dir: ./output/inference # model architecture Arch:   name: PPLCNet_x0_5   class_num: 2   # loss function config for traing/eval process Loss:   Train:     - CELoss:         weight: 1.0         epsilon: 0.1   Eval:     - CELoss:         weight: 1.0   Optimizer:   name: Momentum   momentum: 0.9   lr:     name: Cosine     learning_rate: 0.8     warmup_epoch: 5   regularizer:     name: 'L2'     coeff: 0.00003   # data loader for train and eval DataLoader:   Train:     dataset:       name: ImageNetDataset       image_root: ../process_data/       cls_label_path: ../process_data/train_list.txt       transform_ops:         - DecodeImage:             to_rgb: True             channel_first: False         - ResizeImage:             size: [224,224]         - RandFlipImage:             flip_code: 1         - NormalizeImage:             scale: 1.0/255.0             mean: [0.485, 0.456, 0.406]             std: [0.229, 0.224, 0.225]             order: ''      sampler:       name: DistributedBatchSampler       batch_size: 64       drop_last: False       shuffle: True     loader:       num_workers: 4       use_shared_memory: True    Eval:     dataset:        name: ImageNetDataset       image_root: ../process_data/       cls_label_path: ../process_data/val_list.txt       transform_ops:         - DecodeImage:             to_rgb: True             channel_first: False         - ResizeImage:             size: [224,224]         - NormalizeImage:             scale: 1.0/255.0             mean: [0.485, 0.456, 0.406]             std: [0.229, 0.224, 0.225]             order: ''     sampler:       name: DistributedBatchSampler       batch_size: 64       drop_last: False       shuffle: False     loader:       num_workers: 4       use_shared_memory: True  Infer:   infer_imgs: "../testimgs/10.jpg"   batch_size: 1   transforms:     - DecodeImage:         to_rgb: True         channel_first: False     - ResizeImage:         size: [224,224]     - NormalizeImage:         scale: 1.0/255.0         mean: [0.485, 0.456, 0.406]         std: [0.229, 0.224, 0.225]         order: ''     - ToCHWImage:   PostProcess:     name: Topk     topk: 1     class_id_map_file: "../process_data/label.txt"  Metric:   Train:     - TopkAcc:         topk: [1]   Eval:     - TopkAcc:         topk: [1] 

然后使用下面的命令进行训练:

export CUDA_VISIBLE_DEVICES=0,1 python3 -m paddle.distributed.launch \     --gpus="0,1" \     tools/train.py \         -c config_lcnet.yaml 

训练完成后可以使用下面的命令可视化查看训练结果:

visualdl --logdir results/vdl

运行效果如下:

可以看到,基本在epoch=100以后就收敛了,最高top1准确率达到97.5%,准确率还是比较高的。

下面可以使用动态图对单张图像进行测试,命令如下:

python3 tools/infer.py -c config_lcnet.yaml -o Global.pretrained_model=output/PPLCNet_x0_5/best_model

输出如下:

[{'class_ids': [1], 'scores': [0.93522], 'file_name': '../testimgs/10.jpg', 'label_names': ['adult']}]

2.5 静态图导出

为了方便后面进行模型部署,将训练好的最佳模型进行静态图导出。具体命令如下:

python3 tools/export_model.py \     -c config_lcnet.yaml \     -o Global.pretrained_model=output/PPLCNet_x0_5/best_model \     -o Global.save_inference_dir=output/inference

导出的静态图模型存放在output/inference文件夹下面,整个模型参数加起来不超过3M,因此可以看出这个训练好的PPLCNet_x0_5模型是一个非常轻量级的模型。

2.6 静态图推理

下面使用静态图来进行推理。在推理前先使用visualdl工具查看下静态图模型的输入和输出,这将为编写推理脚本奠定基础。

可以看到,输入是[batch,3,224,224]的float型图像数据,输出是[batch,2]的float型数据。尤其是输出的两个值,代表的是两个类别的概率。

有了上面的分析,下面可以用PaddleInference写一个推理脚本infer.py:

import cv2 import numpy as np from paddle.inference import create_predictor from paddle.inference import Config as PredictConfig  # 加载静态图模型 model_path = "./output/inference/inference.pdmodel" params_path = "./output/inference/inference.pdiparams" pred_cfg = PredictConfig(model_path, params_path) pred_cfg.enable_memory_optim()  # 启用内存优化 pred_cfg.switch_ir_optim(True) pred_cfg.enable_use_gpu(500, 0)  # 启用GPU推理 predictor = create_predictor(pred_cfg)  # 创建PaddleInference推理器  # 解析模型输入输出 input_names = predictor.get_input_names() input_handle = {} for i in range(len(input_names)):     input_handle[input_names[i]] = predictor.get_input_handle(input_names[i]) output_names = predictor.get_output_names() output_handle = predictor.get_output_handle(output_names[0])  # 图像预处理 img = cv2.imread("../testimgs/10.jpg", flags=cv2.IMREAD_COLOR) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (224, 224), interpolation=cv2.INTER_AREA) img = img.astype(np.float32) PIXEL_MEANS =(0.485, 0.456, 0.406)    # RGB格式的均值和方差 PIXEL_STDS = (0.229, 0.224, 0.225) img/=255.0 img-=np.array(PIXEL_MEANS) img/=np.array(PIXEL_STDS) img = np.transpose(img[np.newaxis, :, :, :], (0, 3, 1, 2))  # 预测 input_handle["x"].copy_from_cpu(img) predictor.run() results = output_handle.copy_to_cpu()  # 后处理 results = results.squeeze(0) if results[0]>results[1]:     print('小孩'+"  "+str(results[0])) else:     print('大人'+"  "+str(results[1])) 

从网上随便找两张照片,运行效果如下:

输出结果:

小孩  0.7256172

输出结果:

大人  0.9533998

可以看到,推理效果还是比较满意的。

三、小结

本文以项目为主线,使用了PaddleClas算法套件解决了年龄分类问题。后续读者如果想要深入学习PaddlePaddle(飞桨)及相关算法套件,可以关注我的书籍(链接)。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!