非算法工程师也能玩转深度学习--数据集处理篇

1,086 阅读6分钟

前言

当下,人工智能多次被提到国家战略,其发展势头可称得上是互联网行业之最,在各个大厂都疯狂招揽AI相关人才的同时,人工智能行业的入门门槛也越来越高。但是,人工智能真正要落地,离不开各行各业的工程师。传统意义上讲,将人工智能引入到工程中大概是这样的过程:收集数据集->处理数据集->定义模型->训练模型->模型调参->开发模型服务->模型部署->加入工程链路。

首先声明,本篇文章仅适用于图片分类数据集的处理,大家如果已经拥有了自己的图片数据集,例如传统的mnist,猫狗数据集这种,以文件夹名称为图片的标签,可以直接跳过“VOC”数据集处理这一步,直接进入数据增强和数据集划分,数据增强是本篇文章的重点所在,下面会重点介绍。

作为阿里巴巴-淘系技术部-频道与D2C智能团队的一员,我们的使命就是:前端智能让业务创新更⾼效。整个的过程有一定的上手难度,不过不用担心,小编会从实际项目入手,一步步为你揭开AI的神秘面纱。

VOC数据集处理(已经拥有图片数据集的小伙伴可以直接进入第二步)

拥有一个高质量的数据集是做好机器学习的关键,模型结构千千万,数据集可千万不能拖后腿。小编假设你已经拥有了一个图片分类数据集,小编是做前端UI组件识别的,所以使用的是一个VOC格式的数据集,格式如下

voc数据集格式

具体处理VOC数据集的代码小编也为亲准备好啦 不同的数据集更改一下cats里的标签即可

# 提取一些组件保存为图片 裁剪出特定分类保存
import os
import sys
import shutil
import random
import cv2
import xml.etree.ElementTree as ET

cats = [
    'cat', 'dog'
    ]
def do_start(input_dir, output_dir):
    # 输入文件路径
    input_xml_dir = os.path.join(input_dir, 'Annotations')
    input_img_dir = os.path.join(input_dir, 'JPEGImages')
    # 输出图片路径
    output_img_dir = os.path.join(output_dir, 'Components')
    # 如果路径不存在
    if not os.path.exists(output_img_dir):
        os.makedirs(output_img_dir)
    all_xmls = os.listdir(input_xml_dir)
    count = 0
    for index, xml_name in enumerate(all_xmls):
        count = count + 1 
        # print(xml_name)
        xml_path = os.path.join(input_xml_dir, xml_name)
        xml_data = open(xml_path,'r')
        tree = ET.parse(xml_data)
        root = tree.getroot()
        
        img_name = root.find('filename').text
        img = cv2.imread(os.path.join(input_img_dir, img_name))
        size = root.find('size')
        width = int(size.find('width').text)
        height = int(size.find('height').text)
        
        objs = root.findall('object')
        for index, obj in enumerate(objs):
            r_name = obj.find('name').text
            if r_name in cats:
                print(img_name, r_name)
                r_xmlbox = obj.find('bndbox')
                r_xmin = int(float(r_xmlbox.find('xmin').text))
                r_xmax = int(float(r_xmlbox.find('xmax').text))
                r_ymin = int(float(r_xmlbox.find('ymin').text))
                r_ymax = int(float(r_xmlbox.find('ymax').text))
                cropped_img = img[
                    int(r_ymin) : int(r_ymax), 
                    int(r_xmin) : int(r_xmax)
                ] 
                cut_img_name = '{}-{}'.format(r_name, img_name)
                print(cut_img_name)
                try:
                    cv2.imwrite(os.path.join(output_img_dir, cut_img_name), cropped_img)
                except:
                    print('error: ', img_name) 
if __name__ == '__main__':
    args = sys.argv
    input_dir = args[1]
    output_dir = args[2]
    do_start(input_dir, output_dir)

数据增强

数据增强可以说是极其关键的一步,一套更加丰富的数据集对模型能力的提升是非常大的,小编将传统的各种图片数据增强的方法都集成到一起并且随机化,包括图片的裁剪,翻转,局部遮挡,像素模糊等等。

无论是使用TensorFlow还是PyTorch,都有自带的一些数据增强方法,在这里小编为亲准备了一套更专业,随机化程度更高的数据增强方法,主要使用imgaug,有兴趣的小伙伴可以去官网浏览哦 imgaug官网 可见一张图片就能有许多种变换方式,通过数据增强可以使得数据集更加丰富。

在方法中小编定义了很多种数据增强的方法,对于每个数据文件夹,随机选取2-5种方法对数据进行增强,很大程度上保证了数据的不重复性。话不多说,直接上代码

import cv2
from imgaug import augmenters as iaa
import os

sometimes = lambda aug: iaa.Sometimes(0.5, aug)
# 定义一组变换方法.
seq = iaa.Sequential([

    # 选择2到5种方法做变换
    iaa.SomeOf((2, 5),
        [
                iaa.Fliplr(0.5), # 对50%的图片进行水平镜像翻转
                iaa.Flipud(0.5), # 对50%的图片进行垂直镜像翻转	

                sometimes(
                    iaa.Superpixels(
                        p_replace=(0, 1.0),
                        n_segments=(20, 200)
                    )
                ),
                iaa.OneOf([
                    iaa.GaussianBlur((0, 3.0)),
                    iaa.AverageBlur(k=(2, 7)),
                    iaa.MedianBlur(k=(3, 11)),
                ]),
                iaa.Sharpen(alpha=(0, 1.0), lightness=(0.75, 1.5)),
                iaa.Emboss(alpha=(0, 1.0), strength=(0, 2.0)),
                iaa.AdditiveGaussianNoise(
                    loc=0, scale=(0.0, 0.05*255)
                ),
                iaa.Invert(0.05, per_channel=True), 
                iaa.Add((-10, 10), per_channel=0.5),
                iaa.AddElementwise((-40, 40)),
                iaa.Multiply((0.5, 1.5)),
                iaa.MultiplyElementwise((0.5, 1.5)),
                iaa.ContrastNormalization((0.5, 2.0)),
        ],
        random_order=True
    )
 
],random_order=True) #apply augmenters in random order
 
# 图片文件相关路径
path = 'cat'
savedpath = 'cat-aug'
 
imglist=[]
filelist = os.listdir(path)
 
# 遍历要增强的文件夹,把所有的图片保存在imglist中
for item in filelist:
    if(item == '.DS_Store'):
        continue
    img = cv2.imread(path +'/'+ item)
    imglist.append(img)
print('all the picture have been appent to imglist')
print(len(imglist))
 
#对文件夹中的图片进行增强操作,循环n次,n为数据集扩充的倍数,可以自己设置哦
for count in range(4):
	images_aug = seq.augment_images(imglist)
	for index in range(len(images_aug)-1):
		filename = str(count) + str(index) +'.jpg'
		#保存图片
		cv2.imwrite(savedpath +'/'+ filename,images_aug[index])
		print('image of count%s index%s has been writen'%(count,index))

亲们可以定义自己的数据增强方法,建议尽量增大样本扩充的随机性,以上方法即使在普通的电脑上速度也非常快,但是考虑到深度学习的图片数据集比较大,所以脚本的输入是一个图片文件夹,亲们可以先准备少量的数据实验一下~

数据集划分

在进行模型训练之前,最好能够将数据集切分为训练集,测试集和验证集,训练集用于模型拟合的数据样本;验证集是模型训练过程中单独留出的样本集,它可以用于调整模型的超参数和用于对模型的能力进行初步评估;测试集用来评估模最终模型的泛化能力,但不能作为调参、选择特征等算法相关的选择的依据。

数据集作用比喻
训练集用于模型的训练学生的课本
验证集用于调超参数,监控模型是否发生过拟合(以决定是否停止训练)课后作业
测试集为了评估最终模型泛化能力(仅最后一次使用)期末考试
当然啦,小编也为亲们准备了划分数据集的脚本,直接上代码
# 将一个文件夹下图片按比例分在三个文件夹下
import os
import random
import shutil
from shutil import copy2
datadir_normal = "./cat-aug"

all_data = os.listdir(datadir_normal)#(图片文件夹)
num_all_data = len(all_data)
print( "num_all_data: " + str(num_all_data) )
index_list = list(range(num_all_data))
random.shuffle(index_list)
num = 0

trainDir = "./train/cat"#(将训练集放在这个文件夹下)
if not os.path.exists(trainDir):
    os.mkdir(trainDir)
        
validDir = './validation/cat'#(将验证集放在这个文件夹下)
if not os.path.exists(validDir):
    os.mkdir(validDir)
        
testDir = './test/cat'#(将测试集放在这个文件夹下)        
if not os.path.exists(testDir):
    os.mkdir(testDir)
        
for i in index_list:
    fileName = os.path.join(datadir_normal, all_data[i])
    if num < num_all_data*0.8:
        copy2(fileName, trainDir)
    elif num>num_all_data*0.8 and num < num_all_data*0.9:
        copy2(fileName, validDir)
    else:
        copy2(fileName, testDir)
    num += 1
num_train = len(os.listdir(trainDir))
print('num_train:' + str(num_train))

num_test = len(os.listdir(testDir))
print('num_test:' + str(num_test))

num_val = len(os.listdir(validDir))
print('num_val:' + str(num_val))

以上代码是按照3:1:1来划分数据集的,比较经典,大家也可以自己去调整划分的比例。

写在最后

以上三步就是处理数据集的整个流程,也是做深度学习的第一步,之后小编会出一系列的文章,和大家一起了解最新的AI技术。希望大家点个关注,点个赞呀~