汽车指示盘警示灯数据集增强(one-shot)

本文探讨了过拟合的原因和解决方法,重点在于数据增强在汽车指示盘警示灯数据集中的应用。通过几何变换、色彩空间变换等多种数据增强手段,有效地防止了过拟合,同时介绍了正则化、Dropout等技术。在数据集划分和数量上,提出了具体建议,并分享了使用Augmentor和ImageDataGenerator进行数据增强的实践经验。
摘要由CSDN通过智能技术生成

数据数据数据当然是一个很重要的东西,如果数据很少该怎么办呢?既要马儿跑,又不给马儿草......本文中我将要做的工作的是一个关于汽车指示盘警示灯的数据集增强,因为网上的相关数据不好找,搜集和筛选数据的时间性价比较为低下,已经搜集到了少量的数据,如果要保证模型的准确性我们必须要进行数据增强,否则很容易陷入过拟合(增加数据不会导致过拟合,参数太多才会),之后可以考虑拿着这些数据进行迁移学习,这里打算进行的是离线数据增强(有数据扩充的那种增强)。

目录

一.过拟合导致的原因

二.解决过拟合的方法

1.数据增强

(1)几何变换

(2)色彩空间变换

(3)随机擦除

(4)对抗训练

(5)神经风格迁移

2.正则化

3.Dropout

4.训练提前停止

5.重新清洗数据

6.使用集成学习方法

7.增加更多数据

8.降低模型复杂度

三.数据增强警示灯数据集

1.数据集的数量(需要数据扩充到多少)

2.数据增强过程

(1)初步确定需要进行的增强

(2)根据实际情况使用的增强(多次调整)

3.数据集的划分


一.过拟合导致的原因

1.过拟合产生的原因通常是训练数据不够多。

2.是对数据集进行过度训练造成的,导致模型复杂度过高,参数过多。

3.训练集和测试集分布不一致:样本里面的噪声数据干扰过大,导致模型过分记住了噪声特征,反而忽略了真实的输入输出特征,训练集和测试集特征分布不一样(如果训练集和测试集使用了不同类型的数据集会出现这种情况)

二.解决过拟合的方法

1.数据增强

通过数据增强实现数据更复杂的表征,从而减小验证集和训练集以及最终测试集的差距,让网络更好地学习迁移数据集上的数据分布。这也说明网络不是真正地理解数据,而是记忆数据分布。

(1)几何变换

  • Geometric transformations:如果数据集潜在的表征能够被观察和分离,那么简单的几何变换就能取得很好的效果。对于我们复杂的数据集,数据小而且训练集和测试集的偏差大,几何变换就非常有必要在这里应用。而在汽车指示盘警示灯中我们应用几何变换还是比较安全的。
  • Flipping:翻转这种变换对于数字数据集不具有安全性,但是对于图片数据集还是具有安全性的,在这里我们的汽车指示盘警示灯有必要进行这种变换。
  • Cropping:通常在输入图片的尺寸不一时会进行按中心的裁剪操作。裁剪某种程度上和平移操作有相似性。根据裁剪幅度变换,这个操作还是具有一定的安全性威胁的(别裁剪的太厉害了),不过这个增强的效益还是可以的,在这里对于我们汽车指示灯还是有必要进行这种数据增强的。
  • Rotation:旋转的话一般还是安全的,但是也是存在一定的安全性威胁的(别大幅度的旋转)。
  • Traslation:平移的话我们需要合理设计,因为也是存在一定安全性威胁的(别大幅度的平移),另外在针对一定的特别需求当中我们平移增强需要更小幅度的增强(比如只需要中心检测的时候,边缘检测不允许,否则或许会产生图像攻击,比如在车站人脸检测的时候只露出半张脸......),对于空出的部分填0或者255,或用高斯分布噪声。在这里我们的汽车指示盘警示灯数据集安全性威胁性不高,虽然没有啥安全性的要求吧,但是也得注意别大幅度的平移。
  • Noise injection:在像素上叠加高斯分布的随机噪声,正则化的方法就是一种向模型注入噪声的手段这种可以较为有效的防止过拟合,因为过拟合通常发生在神经网络学习高频特征的时候(因为低频特征神经网络很容易就可以学到,而高频特征只有在最后的时候可以学到),而这些特征对于神经网络所做的任务可能没有帮助,而且会对低频特征产生影响,为了消除高频特征我们随机加入噪声数据来消除这些特征,迫使模型降低对参数的依赖,使得较弱的参数对模型判断不再产生影响,合适的噪声添加对于我们汽车指示盘警示灯的数据集防止过拟合也是很有必要的。(但是注意我们的噪声数据干扰别过大,否则容易导致模型过分记住了噪声特征,反而忽略了真实的输入输出特征)
  • A note on combining augmentations:组合增强方式往往是连续变化的,导致数据集的容量会迅速扩大,这对于小数据集领域来说很容易发生过拟合,所以我们需要设计合理的搜索方法设计恰当的训练数据集。

效果总体来评价的话,其实随机裁剪效果是最好的,但是我们在实际应用中,这些变换都是同时添加的。

(2)色彩空间变换

  • Color space:主要提及的识别RGB通道上的变换,将三通道图进行分离,以及直方图变换等等。这种变换我觉得也是对数据集存在一定的安全性威胁的,比如你做人脸识别,人脸只有黑白黄这三种颜色,我们做数据增强做出了蓝色的紫色的粉色的脸,这肯定会破坏数据集的安全性的,这样训练的话容易失去一些准确参数。在这里我们的汽车指示灯只有红色,我们做数据增强的时候必然要限制了颜色的变换区域。在红色周围色域是有效色域,其他则规定为无效色域。
  • Color space transformations:由于实际图像中一定存在着光线偏差,所以光线的增强十分有必要的,但是3D建模的灯光增强实在是很难学习到,所以对光线增强的效果不如几何也可能因为光线的复杂性度更高,数据样本远远不高,它的变换十分多样,比如像素限制、像素矩阵变换、像素值颠倒,除了计算大内存消耗和时间长等缺点,它的变换既然这么多,我们很有可能因为增强不够反而学不好,造成欠拟合现象,这种变换也是存在着不安全性的,但是对于汽车指示盘警示灯的光线增强我们还是有必要的。

(3)随机擦除

通过mask的遮挡使得网络能够提高遮挡情况的鲁棒性,需要手工设计的部分包括maskd的大小以及生成方式,这种手段是对于目标检测领域的一种比较行之有效的方法,但是也需要考量增强的安全性(你不能遮挡太多了......),在汽车警示灯中我们没有必要去应用这种数据增强手段(很少人故意遮个东西拍照我想要知道的东西吧),如果我们应用了的话反而容易造成对数据集的威胁(有的俩指示灯标志就差那么一点点区别)。

(4)对抗训练

对抗样本训练可以提高鲁棒性,但是实际应用中其实提高不一定明显,因为自然对抗样本的数目没有那么多,对于普适的检测性能提高意义不大,强调安全需求高的场合,在这里我们没有必要进行对抗训练。

(5)神经风格迁移

这里应用于警示灯的数据集数据增强的话不太合适。

但是我们要注意的是一开始就进行大量的增强的话容易导致网络不收敛,因此增强一定要适当的增强,也可以放在检测阶段进行同等的数据增强(但是这种手段时间成本太高,可以在追求精度高的关键领域使用)。注意我们的增强需要仔细设计,否则容易造成过拟合问题。

2.正则化

在损失函数后面加一个正则化项,常见的有L1正则化和L2正则化。L1惩罚项的目的是使权重绝对值最小化,L2惩罚项的目的是使权重的平法最小化。如果数据过于复杂以至于无法准确地进行建模,那么L2是更好的选择,因为它能够学习数据中呈现的内在模式,而当数据足够简单的时候,可以精确建模的话,L1更合适,正确的正则化对于我们来说很重要,L2正则化几乎总是可以给出更好的结果,然后L1正则化不容易受到离群值的影响。

3.Dropout

参考https://blog.csdn.net/wanghuiqiang1/article/details/113323586#2.dropout的意义

在这里对于警示灯的数据集我们是有必要进行Dropout的。

4.训练提前停止

在经过几次迭代之后看出来训练误差虽然在减少,但是测验误差已经开始增加了的时候赶紧提前停止。

5.重新清洗数据

把明显异常的数据剔除

6.使用集成学习方法

多个模型融合在一起,能够有效的降低单个模型的过拟合风险,这个其实还是很有效的。

7.增加更多数据

这个看情况了,看你的数据集是否容易搜集到以及搜集的成本,不过当然数据越多越好了。

8.降低模型复杂度

我们可以通过降低模型复杂度,即简单地移除层或者减少神经元的数量使得网络规模变小,与此同时,计算神经网络中不同层的输入和输出维度也十分重要,虽然移除层的数量或神经网络的规模没有一个通用的规定,但是如果我们的神经网络规模没有通用的规定,我们可以尝试一下缩小它的规模。

三.数据增强警示灯数据集

1.数据集的数量(需要数据扩充到多少)

训练我的分类器?需要多少图像?

一般性的来说对于每个类,我们需要1000张具有代表性的训练图像,当然这条规则不是放之四海而皆准,有的时候得看情况,我觉得1000张的话足以训练像AlexNet这样的早期图像分类模型了。当然图像的质量很重要,而不仅仅是数量,重要的是,训练图像应当和模型看到的输入尽可能的接近(我们猜想要进行识别的图片会是什么样的),使用相同环境中拍摄的较少量的图像训练模型,比使用大量不太具有代表性的图像会得到更好的结果(一些没有意义的图像反而会让你丢失参数),因此我们再数据增强的时候,要观察增强提升后的图片质量是否高?在整体精确度方面,失真副本的价值不如新原始图像那么多,但如果我们只有少量图像,那么这是优化结果的好方法,并且会减少你需要的图像总数(我们在迫不得已的情况下进行一些不为"夸张"的数据增强效果还是比较好的,虽然比不上大量的原始图像)。

不过到底需要多少训练图像,最好的方法还是尝试一下,具体情况具体分析。但是一般来说我们的数据集可以有这几点要求:

  • 训练集中每个类应有1000张图像(问题越复杂,需要越多样本)
  • 所用图像应当是具有代表性的高质量图像
  • 如果图像数量不够,可采用数据增强方法(数据增强一定要合适)

这里我们的汽车指示牌警示灯问题并不是很复杂,我们有31个分类,采用早期AlexNet这种图像分类模型,按理说应该需要310000张图像,这是非常庞大的一个图像数量,但是我们的问题并不复杂,因此我们可以先尝试数据增强到3100张图像。如果效果不理想的话,我们逐渐增加通过数据增强扩充的数量。

2.数据增强过程

(1)初步确定需要进行的增强

(2)根据实际情况使用的增强(多次调整)

这里我们打算使用Augmentor数据增强工具。

使用pip install Augmentor安装Augmentor数据增强利器,使用方法详见官方文档Augmentor — Augmentor 0.2.9 documentation

这里我的数据增强代码是

import Augmentor
p=Augmentor.Pipeline("F:/警示灯项目文件/数据/训练集数据")
p.zoom(probability=1,min_factor=0.3,max_factor=0.5)#缩放/放大图像(注意图像大小不变),这里缩小那么多是因为我们原来图像数据集的原因
p.rotate(probability=0.5,max_left_rotation=20,max_right_rotation=20)
p.skew(probability=0.3,magnitude=0.2)#沿任意方向倾斜图像因为我们拍摄的照片有可能发生一点点形变,以便我们看起来是从不同角度看图像
# p.skew_corner(probability=0.3,magnitude=0.1)#透视形变
p.random_distortion(probability=0.1,grid_height=5,grid_width=5,magnitude=1)#弹性扭曲,我们可以使用这个方法创建逼真的新样本
p.shear(probability=0.3,max_shear_left=5,max_shear_right=5)#错切变换
#p.crop_centre(probability=0.6,percentage_area=0.1,randomise_percentage_area=True)#裁剪的话会导致图像大小改变,我们留在后面在线数据增强的时候进行裁剪
p.flip_left_right(probability=0.5)#从左到右翻转图像
p.random_brightness(probability=0.5,min_factor=0.5,max_factor=1.5)#图片亮度调节,以适应拍摄时光照强度
p.random_color(probability=0.5,min_factor=0.5,max_factor=1)#图片饱和度调节
p.random_contrast(probability=0.5,min_factor=0.5,max_factor=1)#图片对比度调节

p.sample(3100)

数据增强之后的结果是

我这里衡量质量的标准是

  • 这些图片我是否能认出来这是啥?(避免过于失真)
  • 我如果用手机或者相机拍摄这些标志的话会不会有可能拍成这个样?(贴合实际环境)
  • 是否覆盖比较全面了?(避免过拟合,学习过度)

用上面三个标准来评定的话我们离线数据增强的效果还能够接受(我感觉自己过去拿手机实际拍好多张也就这个样......总不能变着花样拍叭)。要注意的是裁剪的话我们可以放到后面在线数据增强的时候进行裁剪,因为Augmentor裁剪的话直接改变图像大小,回头不好处理,而keras中的ImageDataGenerator比较好处理(它是在线数据增强)。

keras中的ImageDataGenerator代码

#归一化数据以及数据增强
TRAINING_DIR = "F:/警示灯项目文件/数据/训练集数据"
training_datagen = ImageDataGenerator(
    rescale=1. / 255,
    # rotation_range=40,
    # width_shift_range=0.2,
    # height_shift_range=0.2,
    shear_range=0.2,
    # zoom_range=0.2,
    # horizontal_flip=True,
    fill_mode='nearest')

#

就这样我们数据已经扩充完毕了。

3.数据集的划分

这个划分其实还是蛮重要的,训练集是用来训练模型的,通过尝试不同的方法和思路使用训练集来训练不同的模型,再通过验证集使用交叉验证来挑选最优的模型,通过不断的迭代来改善模型在验证集上的性能,最后再通过测试集来评估模型的性能,如果数据集划分的好,可以提高模型的应用速度,如果我们划分的不好的话则会大大影响模型的应用部署,甚至可能使得我们之后所做的工作功亏一篑,所以我们数据集的划分一定要慎重,主要需要通过数据集大小来划分(注意一定要先打乱数据集,保证数据处在同一分布)。

对于小规模数据集中的划分,如果我们不加入验证集的话,划分标准是7:3,如果我们加入验证集的话划分标准是6:2:2,对于大规模数据集(上百万,上千万,上亿)我们的划分就得改变了,传统的数据划分原则已经不适用了,对于百万级别的数据集,我们可以采用98%/1%/1%的规则来划分数据。

建议加入验证集,因为我们利用训练集来训练模型然后通过测试模型在测试集上的表现来调整超参和采用不同的策略来提高模型在测试集上的表现,而没有验证集来评估模型的性能,这样有可能导致我们训练的模型过拟合。使用这张方式在测试集上所获取的模型评估并不是可靠的,因此我们最好利用验证集来调整模型,利用测试集来评估模型的指标,如果模型的性能指标要求比较高的时候,我们可以适当的加大验证集的数量以此来获取更高精度的评估指标,最好不要超过30%。

根据这样的原则,我们的数据集已经划分OK了。

附上我的切分代码

import os
import zipfile
import shutil
import random
import tensorflow as tf
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from shutil import copyfile



'''
切分数据集数据函数
输入参数(依次示意):需要打乱并分配的类文件夹路径,需要移动到的训练集类文件夹路径,需要移动到的测试集类文件夹路径,需要移动到的验证集类文件夹路径,训练集占比,测试集占比
'''
def split_data(SOURCE, TRAINING, TESTING,VALIDATION, TRAIN_SIZE, TEST_SIZE):
    files = []
    for filename in os.listdir(SOURCE): #筛选数据,把有效数据并加入到files数组中
        file = SOURCE + filename
        if os.path.getsize(file) > 0:
            files.append(filename)
        else:
            print(filename + " is zero length, so ignoring.")

    training_length = int(len(files) * TRAIN_SIZE)  #训练集长度
    testing_length = int(len(files) * TEST_SIZE) #测试集长度
    validation_length = int(len(files)-training_length-testing_length)
    shuffled_set = random.sample(files, len(files)) #打乱原数组
    training_set = shuffled_set[0:training_length] #分配给训练集数据
    testing_set = shuffled_set[-testing_length-validation_length:training_length+testing_length]  #分配给测试集数据
    validation_set = shuffled_set[-validation_length:] #分配给验证集数据

    for filename in training_set: #copy到训练集相应类的文件夹
        this_file = SOURCE + filename
        destination = TRAINING + filename
        copyfile(this_file, destination)

    for filename in testing_set:#copy到测试集相应类的文件夹
        this_file = SOURCE + filename
        destination = TESTING + filename
        copyfile(this_file, destination)

    for filename in validation_set:#copy到验证集相应类的文件夹
        this_file = SOURCE + filename
        destination = VALIDATION + filename
        copyfile(this_file, destination)


SOURCE_DIR = "F:/警示灯项目文件/数据/数据/源数据"
TRAINING_DIR = "F:/警示灯项目文件/数据/数据/训练集"
TESTING_DIR = "F:/警示灯项目文件/数据/数据/测试集"
VALIDATION_DIR = "F:/警示灯项目文件/数据/数据/验证集"

#分配函数,
def myAllocation(file_dir,TRAINING_DIR, TESTING_DIR,VALIDATION_DIR, TRAIN_SIZE, TEST_SIZE):
    for folderName in os.listdir(file_dir):
        #file_pre_dir =file_dir.decode('utf8')[0:-3].encode('utf8')#注意是中文截取
        newTrainDir=TRAINING_DIR+'/'+folderName
        newTestDir=TESTING_DIR+'/'+folderName
        newValiDir=VALIDATION_DIR+'/'+folderName

        os.makedirs(newTrainDir)
        os.makedirs(newTestDir)
        os.makedirs(newValiDir)

        split_data(file_dir+'/'+folderName+'/',newTrainDir + '/',newTestDir+'/',newValiDir+'/', TRAIN_SIZE, TEST_SIZE)


train_size = .6
test_size = .2

myAllocation(SOURCE_DIR,TRAINING_DIR,TESTING_DIR,VALIDATION_DIR,train_size,test_size)


print(len(os.listdir(SOURCE_DIR)))
print(len(os.listdir(TRAINING_DIR)))
print(len(os.listdir(TESTING_DIR)))
print(len(os.listdir(VALIDATION_DIR)))

切分结果验证:

源数据集(200张照片):

训练数据集(120张照片):

测试数据集(40张照片): 

验证数据集(40张照片):

经过检验,切分可行。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值