当前位置:   article > 正文

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

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

目录

一、任务概述

二、算法研发

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岁的图片移动到另一个文件夹内。

  1. #!/usr/bin/env python
  2. # -*- encoding: utf-8 -*-
  3. '''
  4. @文件 :split_asian.py
  5. @说明 :拆分megaage_asian数据集,将小于13岁的移动到一个文件夹,大于等于13岁的移动到另一个文件夹
  6. @时间 :2024/07/16 09:11:16
  7. @作者 :Bin Qian
  8. @版本 :1.0
  9. '''
  10. import os
  11. import cv2
  12. thr = 13 # 年龄阈值
  13. # 读取年龄列表
  14. agefile = 'megaage_asian/list/test_age.txt'
  15. f=open(agefile)
  16. ageLst = f.read().splitlines()
  17. f.close()
  18. # 读取图像
  19. imgFolder = 'megaage_asian/val'
  20. imgnames = os.listdir(imgFolder)
  21. index = 50000
  22. for imgname in imgnames:
  23. imgPath = os.path.join(imgFolder,imgname)
  24. img = cv2.imread(imgPath)
  25. if img is None:
  26. continue
  27. print(imgPath)
  28. imgindex = int(imgname.split('.')[0])
  29. age = int(ageLst[imgindex-1])
  30. if age < thr:
  31. dstFolder = 'ageclas/child'
  32. else:
  33. dstFolder = 'ageclas/adult'
  34. savePath = os.path.join(dstFolder,str(index)+'_asian.jpg')
  35. cv2.imwrite(savePath,img)
  36. index += 1
  37. print('完成')

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

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

  1. import os
  2. import cv2
  3. # 读取图像
  4. imgFolder = 'adult'
  5. imgnames = os.listdir(imgFolder)
  6. index = 1
  7. for imgname in imgnames:
  8. imgPath = os.path.join(imgFolder,imgname)
  9. img = cv2.imread(imgPath)
  10. if img is None:
  11. continue
  12. print(imgPath)
  13. dstFolder = 'ageclas/adult'
  14. savePath = os.path.join(dstFolder,str(index)+'_data.jpg')
  15. cv2.imwrite(savePath,img)
  16. index += 1
  17. print('完成')

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

  1. # 导入系统库
  2. import os
  3. import random
  4. import cv2
  5. # 定义参数
  6. img_folder = 'ageclas'
  7. trainlst = 'train_list.txt'
  8. vallst = 'val_list.txt'
  9. ratio = 0.95 # 训练集占比
  10. labellst='label.txt'
  11. def writeLst(lstpath,namelst):
  12. '''
  13. 保存文件列表
  14. '''
  15. print('正在写入 '+lstpath)
  16. random.shuffle (namelst)
  17. # 写入训练样本文件
  18. f=open(lstpath, 'a', encoding='utf-8')
  19. for i in range(len(namelst)):
  20. text = namelst[i]+'\n'
  21. f.write(text)
  22. f.close()
  23. print(lstpath+ '已完成写入')
  24. def main():
  25. '''
  26. 主函数
  27. '''
  28. # 查找文件夹
  29. folderlst = os.listdir(img_folder)
  30. print('共找到 %d 个文件夹' % len(folderlst))
  31. # 循环处理
  32. trainnamelst = list()
  33. valnamelst = list()
  34. labelnamelst = list()
  35. for i in range(len(folderlst)):
  36. class_name = folderlst[i]
  37. class_label = i
  38. print('开始处理 '+class_name+' 文件夹')
  39. # 获取子文件夹文件列表
  40. filenamelst = os.listdir(os.path.join(img_folder,class_name))
  41. totalNum = len(filenamelst)
  42. print('当前文件夹图片数量为: ' + str(totalNum))
  43. trainNum = int(ratio*totalNum)
  44. text = str(class_label)+ ' ' + class_name
  45. labelnamelst.append(text)
  46. # 检查并校验图像
  47. for j in range(totalNum):
  48. imgpath = os.path.join(img_folder,class_name,filenamelst[j])
  49. img = cv2.imread(imgpath, cv2.IMREAD_COLOR)
  50. if img is None:
  51. continue
  52. text = imgpath + ' ' + str(class_label)
  53. if j <= trainNum:
  54. trainnamelst.append(text)
  55. else:
  56. valnamelst.append(text)
  57. writeLst(trainlst,trainnamelst)
  58. writeLst(vallst,valnamelst)
  59. writeLst(labellst,labelnamelst)
  60. print('全部完成')
  61. if __name__ == '__main__':
  62. '''程序入口'''
  63. main()

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

2.3 安装PaddleClas套件

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

2.4 算法训练

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

  1. # global configs
  2. Global:
  3. checkpoints: null
  4. pretrained_model: null
  5. output_dir: ./output/
  6. device: gpu
  7. save_interval: 5
  8. eval_during_train: True
  9. eval_interval: 5
  10. epochs: 200
  11. print_batch_step: 10
  12. use_visualdl: True
  13. # used for static mode and model export
  14. image_shape: [3, 224, 224]
  15. save_inference_dir: ./output/inference
  16. # model architecture
  17. Arch:
  18. name: PPLCNet_x0_5
  19. class_num: 2
  20. # loss function config for traing/eval process
  21. Loss:
  22. Train:
  23. - CELoss:
  24. weight: 1.0
  25. epsilon: 0.1
  26. Eval:
  27. - CELoss:
  28. weight: 1.0
  29. Optimizer:
  30. name: Momentum
  31. momentum: 0.9
  32. lr:
  33. name: Cosine
  34. learning_rate: 0.8
  35. warmup_epoch: 5
  36. regularizer:
  37. name: 'L2'
  38. coeff: 0.00003
  39. # data loader for train and eval
  40. DataLoader:
  41. Train:
  42. dataset:
  43. name: ImageNetDataset
  44. image_root: ../process_data/
  45. cls_label_path: ../process_data/train_list.txt
  46. transform_ops:
  47. - DecodeImage:
  48. to_rgb: True
  49. channel_first: False
  50. - ResizeImage:
  51. size: [224,224]
  52. - RandFlipImage:
  53. flip_code: 1
  54. - NormalizeImage:
  55. scale: 1.0/255.0
  56. mean: [0.485, 0.456, 0.406]
  57. std: [0.229, 0.224, 0.225]
  58. order: ''
  59. sampler:
  60. name: DistributedBatchSampler
  61. batch_size: 64
  62. drop_last: False
  63. shuffle: True
  64. loader:
  65. num_workers: 4
  66. use_shared_memory: True
  67. Eval:
  68. dataset:
  69. name: ImageNetDataset
  70. image_root: ../process_data/
  71. cls_label_path: ../process_data/val_list.txt
  72. transform_ops:
  73. - DecodeImage:
  74. to_rgb: True
  75. channel_first: False
  76. - ResizeImage:
  77. size: [224,224]
  78. - NormalizeImage:
  79. scale: 1.0/255.0
  80. mean: [0.485, 0.456, 0.406]
  81. std: [0.229, 0.224, 0.225]
  82. order: ''
  83. sampler:
  84. name: DistributedBatchSampler
  85. batch_size: 64
  86. drop_last: False
  87. shuffle: False
  88. loader:
  89. num_workers: 4
  90. use_shared_memory: True
  91. Infer:
  92. infer_imgs: "../testimgs/10.jpg"
  93. batch_size: 1
  94. transforms:
  95. - DecodeImage:
  96. to_rgb: True
  97. channel_first: False
  98. - ResizeImage:
  99. size: [224,224]
  100. - NormalizeImage:
  101. scale: 1.0/255.0
  102. mean: [0.485, 0.456, 0.406]
  103. std: [0.229, 0.224, 0.225]
  104. order: ''
  105. - ToCHWImage:
  106. PostProcess:
  107. name: Topk
  108. topk: 1
  109. class_id_map_file: "../process_data/label.txt"
  110. Metric:
  111. Train:
  112. - TopkAcc:
  113. topk: [1]
  114. Eval:
  115. - TopkAcc:
  116. topk: [1]

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

  1. export CUDA_VISIBLE_DEVICES=0,1
  2. python3 -m paddle.distributed.launch \
  3. --gpus="0,1" \
  4. tools/train.py \
  5. -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 静态图导出

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

  1. python3 tools/export_model.py \
  2. -c config_lcnet.yaml \
  3. -o Global.pretrained_model=output/PPLCNet_x0_5/best_model \
  4. -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:

  1. import cv2
  2. import numpy as np
  3. from paddle.inference import create_predictor
  4. from paddle.inference import Config as PredictConfig
  5. # 加载静态图模型
  6. model_path = "./output/inference/inference.pdmodel"
  7. params_path = "./output/inference/inference.pdiparams"
  8. pred_cfg = PredictConfig(model_path, params_path)
  9. pred_cfg.enable_memory_optim() # 启用内存优化
  10. pred_cfg.switch_ir_optim(True)
  11. pred_cfg.enable_use_gpu(500, 0) # 启用GPU推理
  12. predictor = create_predictor(pred_cfg) # 创建PaddleInference推理器
  13. # 解析模型输入输出
  14. input_names = predictor.get_input_names()
  15. input_handle = {}
  16. for i in range(len(input_names)):
  17. input_handle[input_names[i]] = predictor.get_input_handle(input_names[i])
  18. output_names = predictor.get_output_names()
  19. output_handle = predictor.get_output_handle(output_names[0])
  20. # 图像预处理
  21. img = cv2.imread("../testimgs/10.jpg", flags=cv2.IMREAD_COLOR)
  22. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  23. img = cv2.resize(img, (224, 224), interpolation=cv2.INTER_AREA)
  24. img = img.astype(np.float32)
  25. PIXEL_MEANS =(0.485, 0.456, 0.406) # RGB格式的均值和方差
  26. PIXEL_STDS = (0.229, 0.224, 0.225)
  27. img/=255.0
  28. img-=np.array(PIXEL_MEANS)
  29. img/=np.array(PIXEL_STDS)
  30. img = np.transpose(img[np.newaxis, :, :, :], (0, 3, 1, 2))
  31. # 预测
  32. input_handle["x"].copy_from_cpu(img)
  33. predictor.run()
  34. results = output_handle.copy_to_cpu()
  35. # 后处理
  36. results = results.squeeze(0)
  37. if results[0]>results[1]:
  38. print('小孩'+" "+str(results[0]))
  39. else:
  40. print('大人'+" "+str(results[1]))

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

输出结果:

小孩  0.7256172

输出结果:

大人  0.9533998

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

三、小结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/907151
推荐阅读
相关标签
  

闽ICP备14008679号