这是第二节正课,按照流程来说,主要讲解是项目中最开始的数据准备工作。首先介绍一下完整的数据准备流程(这里只给出CV版本):
· 随机旋转
· 随机水平或者重直翻转
· 缩放
· 剪裁
· 平移
· 调整亮度、对比度、饱和度、色差等等
· 注入噪声
· 基于生成对抗网络GAN做数搪增强AutoAugment等
概括一下,本节的主要内容为四点:
- 数据的获取途径
- 数据处理与标注
- 数据预处理方法
- 模型训练评估
一般来说,我们可以从开源平台上获取,比如kaggle、天池以及AI studio,Github上也有丰富的资源。
资源很丰富,但是是全英的网页。
天池我没有仔细了解过,但是也用它搜索过数据集,资源还是很丰富的。
这节课被安利的新的网站,后续调研一下。
科大讯飞官网
COCO数据集
COCO格式,文件夹路径样式:
COCO_2017/
├── val2017 # 总的验证集
├── train2017 # 总的训练集
├── annotations # COCO标注
│ ├── instances_train2017.json # object instances(目标实例) ---目标实例的训练集标注
│ ├── instances_val2017.json # object instances(目标实例) ---目标实例的验证集标注
│ ├── person_keypoints_train2017.json # object keypoints(目标上的关键点) ---关键点检测的训练集标注
│ ├── person_keypoints_val2017.json # object keypoints(目标上的关键点) ---关键点检测的验证集标注
│ ├── captions_train2017.json # image captions(看图说话) ---看图说话的训练集标注
│ ├── captions_val2017.json # image captions(看图说话) ---看图说话的验证集标注
VOC格式,文件夹路径样式:
VOC_2017/
├── Annotations # 每张图片相关的标注信息,xml格式
├── ImageSets
│ ├── Main # 各个类别所在图片的文件名
├── JPEGImages # 包括训练验证测试用到的所有图片
├── label_list.txt # 标签的类别数
├── train_val.txt #训练集
├── val.txt # 验证集
{
"info": info,
"licenses": [license],
"images": [image],
"annotations": [annotation],
"categories": [category]
}
其中,info、licenses、images这三个结构体/类型,在不同的JSON文件中这三个类型是一样的,定义是共享的(object instances(目标实例), object keypoints(目标上的关键点), image captions(看图说话))。
不共享的是annotation和category这两种结构体,他们在不同类型的JSON文件中是不一样的。 新增的keypoints是一个长度为3 X k的数组,其中k是category中keypoints的总数量。每一个keypoint是一个长度为3的数组,第一和第二个元素分别是x和y坐标值,第三个元素是个标志位v,v为0时表示这个关键点没有标注(这种情况下x=y=v=0),v为1时表示这个关键点标注了但是不可见(被遮挡了),v为2时表示这个关键点标注了同时也可见。 um_keypoints表示这个目标上被标注的关键点的数量(v>0),比较小的目标上可能就无法标注关键点。
annotation{
"keypoints": [x1,y1,v1,...],
"num_keypoints": int,
"id": int,
"image_id": int,
"category_id": int,
"segmentation": RLE or [polygon],
"area": float,
"bbox": [x,y,width,height],
"iscrowd": 0 or 1,
}
示例:
{
"segmentation": [[125.12,539.69,140.94,522.43,100.67,496.54,84.85,469.21,73.35,450.52,104.99,342.65,168.27,290.88,179.78,288,189.84,286.56,191.28,260.67,202.79,240.54,221.48,237.66,248.81,243.42,257.44,256.36,253.12,262.11,253.12,275.06,299.15,233.35,329.35,207.46,355.24,206.02,363.87,206.02,365.3,210.34,373.93,221.84,363.87,226.16,363.87,237.66,350.92,237.66,332.22,234.79,314.97,249.17,271.82,313.89,253.12,326.83,227.24,352.72,214.29,357.03,212.85,372.85,208.54,395.87,228.67,414.56,245.93,421.75,266.07,424.63,276.13,437.57,266.07,450.52,284.76,464.9,286.2,479.28,291.96,489.35,310.65,512.36,284.76,549.75,244.49,522.43,215.73,546.88,199.91,558.38,204.22,565.57,189.84,568.45,184.09,575.64,172.58,578.52,145.26,567.01,117.93,551.19,133.75,532.49]],
"num_keypoints": 10,
"area": 47803.27955,
"iscrowd": 0,
"keypoints": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,142,309,1,177,320,2,191,398,2,237,317,2,233,426,2,306,233,2,92,452,2,123,468,2,0,0,0,251,469,2,0,0,0,162,551,2],
"image_id": 425226,"bbox": [73.35,206.02,300.58,372.5],"category_id": 1,
"id": 183126},
最后,对于每一个category结构体,相比Object Instance中的category新增了2个额外的字段,keypoints是一个长度为k的数组,包含了每个关键点的名字;skeleton定义了各个关键点之间的连接性(比如人的左手腕和左肘就是连接的,但是左手腕和右手腕就不是)。 目前,COCO的keypoints只标注了person category (分类为人)。
{
"id": int,
"name": str,
"supercategory": str,
"keypoints": [str],
"skeleton": [edge]
}
示例:
{
"supercategory": "person",
"id": 1,
"name": "person",
"keypoints": ["nose","left_eye","right_eye","left_ear","right_ear","left_shoulder","right_shoulder","left_elbow","right_elbow","left_wrist","right_wrist","left_hip","right_hip","left_knee","right_knee","left_ankle","right_ankle"],
"skeleton": [[16,14],[14,12],[17,15],[15,13],[12,13],[6,12],[7,13],[6,7],[6,8],[7,9],[8,10],[9,11],[2,3],[1,2],[1,3],[2,4],[3,5],[4,6],[5,7]]
}
人体关键点标注,每个人体关键点个数的分布情况,其中11-15这个范围的人体是最多的,有接近70000人,6-10其次,超过40000人,后面依次为16-17,2-5,...
#拉取PaddleDetection
!git clone https://github.com.cnpmjs.org/PaddlePaddle/PaddleDetection
#存入持久层中
!mv PaddleDetection/ work/
#导入所需要的依赖
!pip install -r work/PaddleDetection/requirements.txt
#导入转格式所需要的包
!pip install pycocotools
!pip install scikit-image
#创建解析好的图片与xml文件的目录
!mkdir -p VOCData/images/
!mkdir -p VOCData/Annotations/
!mkdir COCOData/
#处理目标检测的数据集
!python ProcessData.py
#创建解析好的图片与xml文件的目录
!mkdir -p VOCData/images/
!mkdir -p VOCData/Annotations/
!mkdir COCOData/
#移动到dataset文件夹
!mv VOCData work/PaddleDetection/dataset/
%cd work/PaddleDetection/
"""
按VOC格式划分数据集,train : val = 0.85 : 0.15
生成标签label_list.txt
"""
import os
import shutil
import skimage.io as io
from tqdm import tqdm
from random import shuffle
dataset = 'dataset/VOCData/'
train_txt = os.path.join(dataset, 'train_val.txt')
val_txt = os.path.join(dataset, 'val.txt')
lbl_txt = os.path.join(dataset, 'label_list.txt')
classes = [
"person"
]
with open(lbl_txt, 'w') as f:
for l in classes:
f.write(l+'\n')
xml_base = 'Annotations'
img_base = 'images'
xmls = [v for v in os.listdir(os.path.join(dataset, xml_base)) if v.endswith('.xml')]
shuffle(xmls)
split = int(0.85 * len(xmls)) #划分训练集与验证集
with open(train_txt, 'w') as f:
for x in tqdm(xmls[:split]):
m = x[:-4]+'.jpg'
xml_path = os.path.join(xml_base, x)
img_path = os.path.join(img_base, m)
f.write('{} {}\n'.format(img_path, xml_path))
with open(val_txt, 'w') as f:
for x in tqdm(xmls[split:]):
m = x[:-4]+'.jpg'
xml_path = os.path.join(xml_base, x)
img_path = os.path.join(img_base, m)
f.write('{} {}\n'.format(img_path, xml_path))
%cd /home/aistudio/
!mv val2017/ COCOData/
!mv train2017/ COCOData/
!mv annotations/ COCOData/
!mv -f COCOData/ data/
!mv -f work/PaddleDetection/dataset/VOCData data/
对于图像分类任务,我们只要将对应的图片是哪个类别划分好即可。对于检测任务和分割任务,目前比较流行的数据标注工具是labelimg、labelme,分别用于检测任务与分割任务的标注。
标注工具Github地址:
labelimg
labelme
PPOCRLabel
!mkdir work/PaddleDetection/dataset/MaskVOCData
#解压自制数据集
!unzip -oq /home/aistudio/data/data101583/facemask.zip -d work/PaddleDetection/dataset/MaskVOCData
#导入paddlex
!pip install paddlex
#划分VOC数据集
!paddlex --split_dataset --format VOC --dataset_dir work/PaddleDetection/dataset/MaskVOCData/ --val_value 0.15 --test_value 0.05
%cd work/PaddleDetection/
#制作COCO数据集
#提取文件下img目录所有照片名不要后缀
import pandas as pd
import os
filelist = os.listdir("dataset/MaskVOCData/JPEGImages")
train_name = []
for file_name in filelist:
name, point ,end =file_name.partition('.')
train_name.append(name)
df = pd.DataFrame(train_name)
df.head(8)
df.to_csv('./train_all.txt', sep='\t', index=None,header=None)
!mkdir -p dataset/MaskVOCData/ImageSets/Main
!mv train_all.txt dataset/MaskVOCData/ImageSets
!mv dataset/MaskVOCData/labels.txt dataset/MaskVOCData/label_list.txt
!cp dataset/MaskVOCData/label_list.txt dataset/MaskVOCData/ImageSets/
#备份VOC
!cp -r dataset/MaskVOCData /home/aistudio/
!python tools/x2coco.py \
--dataset_type voc \
--voc_anno_dir dataset/MaskVOCData/Annotations \
--voc_anno_list dataset/MaskVOCData/ImageSets/train_all.txt \
--voc_label_list dataset/MaskVOCData/ImageSets/label_list.txt \
--voc_out_name ./dataset/annotations.json
提示:AssertionError:label is not in label2id. 假如遇见这个问题,说明你的label对应不上标注文件里面的label,在打标签的时候不要有空格
!mv dataset/MaskVOCData dataset/MaskCOCOData
!mv ../../MaskVOCData dataset
!mkdir dataset/MaskCOCOData/annotations
!mv dataset/annotations.json dataset/MaskCOCOData/annotations
!rm dataset/MaskCOCOData/train_list.txt
!rm dataset/MaskCOCOData/val_list.txt
!rm dataset/MaskCOCOData/label_list.txt
!rm dataset/MaskCOCOData/test_list.txt
!rm -r dataset/MaskCOCOData/Annotations
!rm -r dataset/MaskCOCOData/ImageSets
#划分COCO数据集
!paddlex --split_dataset --format COCO --dataset_dir dataset/MaskCOCOData/annotations --val_value 0.15 --test_value 0.05
我们常见的图片其实分为两种,一种叫位图,另一种叫做矢量图。如下图所示:
位图的特点:
由像素点定义一放大会糊
文件体积较大
色彩表现丰富逼真
矢量图的特点:
超矢量定义
放太不模糊
文件体积较小
表现力差
%cd /home/aistudio/work/PaddleDetection
import paddle
import paddlex as pdx
import numpy as np
import paddle.nn as nn
import paddle.nn.functional as F
import PIL.Image as Image
import cv2
import os
from random import shuffle
from paddlex.det import transforms as T
from PIL import Image, ImageFilter, ImageEnhance
import matplotlib.pyplot as plt # plt 用于显示图片
path='dataset/MaskCOCOData/JPEGImages/maksssksksss195.png'
img = Image.open(path)
plt.imshow(img) #根据数组绘制图像
plt.show() #显示图像
# 灰度图
img = np.array(Image.open(path).convert('L'), 'f')
plt.imshow(img,cmap="gray") #根据数组绘制图像
plt.show() #显示图像
#小Tips:jupyter notebook中plt显示灰度图异常,需要使用plt.imshow(gray,cmap="gray")方法正常显示灰度图。
#原图
img = cv2.imread(path)
plt.subplot(221)
plt.imshow(img,cmap="gray")
# matplotlib 按照RGB顺序展示原图
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.subplot(222)
# cv2默认的GBR显示图
plt.imshow(img)
plt.subplot(223)
# 32*32的缩略图
plt.imshow(cv2.resize(img, (32, 32)))
#图像处理示例 目标视野里比较多重叠,或者有点模糊的适用
path='dataset/MaskCOCOData/JPEGImages/maksssksksss443.png'
img = Image.open(path)
plt.imshow(img)
plt.show()
#锐化
img = img.filter(ImageFilter.SHARPEN)
img = img.filter(ImageFilter.SHARPEN)
plt.imshow(img)
plt.show()
#亮度变换
bright_enhancer = ImageEnhance.Brightness(img) # 传入调整系数亮度
img = bright_enhancer.enhance(1.6)
plt.imshow(img)
plt.show()
#提高对比度
contrast_enhancer = ImageEnhance.Contrast(img) # 传入调整系数对比度
img = contrast_enhancer.enhance(1.9)
plt.imshow(img)
plt.show()
是因为很多深度学习的模型复杂度太高了,且在数据量少的情况下,比较容易造成过拟合。
def preprocess(dataType="train"):
if dataType == "train":
transform = T.Compose([
T.MixupImage(mixup_epoch=10), #对图像进行mixup操作,模型训练时的数据增强操作,目前仅YOLOv3模型支持该transform
# T.RandomExpand(), #随机扩张图像
# T.RandomDistort(brightness_range=1.2, brightness_prob=0.3), #以一定的概率对图像进行随机像素内容变换
# T.RandomCrop(), #随机裁剪图像
# T.ResizeByShort(), #根据图像的短边调整图像大小
T.Resize(target_size=608, interp='RANDOM'), #调整图像大小,[’NEAREST’, ‘LINEAR’, ‘CUBIC’, ‘AREA’, ‘LANCZOS4’, ‘RANDOM’]
# T.RandomHorizontalFlip(), #以一定的概率对图像进行随机水平翻转
T.Normalize() #对图像进行标准化
])
return transform
else:
transform = T.Compose([
T.Resize(target_size=608, interp='CUBIC'),
T.Normalize()
])
return transform
train_transforms = preprocess(dataType="train")
eval_transforms = preprocess(dataType="eval")
# 定义训练和验证所用的数据集
# API地址:https://paddlex.readthedocs.io/zh_CN/develop/data/format/detection.html?highlight=paddlex.det
train_dataset = pdx.datasets.VOCDetection(
data_dir='./dataset/MaskVOCData',
file_list='./dataset/MaskVOCData/train_list.txt',
label_list='./dataset/MaskVOCData/label_list.txt',
transforms=train_transforms,
shuffle=True)
eval_dataset = pdx.datasets.VOCDetection(
data_dir='./dataset/MaskVOCData',
file_list='./dataset/MaskVOCData/val_list.txt',
label_list='./dataset/MaskVOCData/label_list.txt',
transforms=eval_transforms)
import matplotlib
matplotlib.use('Agg')
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
%matplotlib inline
import warnings
warnings.filterwarnings("ignore")
#num_classes有些模型需要加1 比如faster_rcnn
num_classes = len(train_dataset.labels)
model = pdx.det.PPYOLO(num_classes=num_classes, )
model.train(
num_epochs=70,
train_dataset=train_dataset,
train_batch_size=16,
eval_dataset=eval_dataset,
learning_rate=3e-5,
warmup_steps=90,
warmup_start_lr=0.0,
save_interval_epochs=7,
lr_decay_epochs=[42, 70],
save_dir='output/PPYOLO',
use_vdl=True)
!mkdir ./output
!unzip -oq /home/aistudio/data/data101583/PPYOLO_YES.zip -d ./output
!unzip -oq /home/aistudio/data/data101583/PPYOLO_ALL.zip -d ./output
!unzip -oq /home/aistudio/data/data101583/PPYOLO_NO.zip -d ./output
!mv -f output/home/aistudio/PPYOLO_ALL output
!mv -f output/home/aistudio/PPYOLO_YES output
!rm -r output/home/
注意:训练点中断后的必须重启环境,清除中断之前缓存的环境,点重启。
在其他参数都相同的情况下,在没有加任何的数据增强时,mAP为38.06:
在其他参数都相同的情况下,在随机扩张,随机像素变换数据增强时,mAP为41.9:
在其他参数都相同的情况下,在加随机裁剪,随机水平翻转,短边调整,与Mixup的数据增强时,mAP为35.4:
以上对比实验说明,正确的增加数据增强时,可以小幅度提升mAP值。
在机器学习领域中,用于评价一个模型的性能有多种指标,其中几项就是FP、FN、TP、TN、精确率(Precision)、召回率(Recall)、准确率(Accuracy)。
mean Average Precision, 即各类别AP的平均值,是AP:PR 曲线下面积。
此前先了解一下IOU评判标准:
常见的评判方式,第一位的T,F代表正确或者错误。第二位的P和N代表判断的正确或者错误
True Positive (TP): IoU>IOUthreshold \mathrm{IoU}>I O U_{\text {threshold }}IoU>IOUthreshold (IOU的阈值一般取0.5)的所有检测框数量(同一Ground Truth只计算一次),可以理解为真实框,或者标准答案
False Positive (FP): IoU
False Negative (FN): 没有检测到的 GT 的数量
True Negative (TN): mAP中无用到
查准率(Precision): Precision =TPTP+FP=TP all detections =\frac{T P}{T P+F P}=\frac{T P}{\text { all detections }}=TP+FPTP= all detections TP
查全率(Recall): Recall =TPTP+FN=TP all ground truths =\frac{T P}{T P+F N}=\frac{T P}{\text { all ground truths }}=TP+FNTP= all ground truths TP
二者绘制的曲线称为 P-R 曲线: 查准率:P 为纵轴y 查全率:R 为横轴x轴,如下图
mAP值即为,PR曲线下的面积。
使用模型进行预测,同时使用pdx.det.visualize将结果可视化,可视化结果将保存到work/PaddleDetection/output/PPYOLO/vdl_log下,载入模型推理保存图片至work/PaddleDetection/output/PPYOLO/img下。
#maksssksksss152.png maksssksksss105.png
model = pdx.load_model('output/PPYOLO_YES/best_model')
image_dir = '../../Test/'
images = os.listdir(image_dir)
for img in images:
image_name = image_dir + img
result = model.predict(image_name)
pdx.det.visualize(image_name, result, threshold=0.3, save_dir='./output/PPYOLO_YES/img')
#展示模型推理结果
path = "../../Test/maksssksksss152.png"
img = Image.open(path)
plt.imshow(img) #根据数组绘制图像
plt.show() #显示图像
path = 'output/PPYOLO_YES/img/visualize_maksssksksss152.png'
img = Image.open(path)
plt.imshow(img) #根据数组绘制图像
plt.show() #显示图像
本次课程主要为大家介绍了数据集获取,以及数据标注、数据划分、数据增强处理方法和简单的口罩检测实现,及其用数据增强和不用数据增强的对比实验,体现了数据增强在AI学习中的重要性。