【方法】数据增强(Data Augmentation)

在训练过程中,网络优化是一方面,数据集的优化又是另一方面。数据集会存在各类样本不均匀的情况,也就是各类样本的数量不一样,有的甚至差别很大。为了让模型具有更强的鲁棒性,采用Data Augmentation是一个不错的选择。

常用的方法

  • Color Jittering:对颜色的数据增强:图像亮度、饱和度、对比度变化(此处对色彩抖动的理解不知是否得当)
  • PCA Jittering:首先按照RGB三个颜色通道计算均值和标准差,再在整个训练集上计算协方差矩阵,进行特征分解,得到特征向量和特征值。参见论文
  • Random Scale:尺度变换
  • Random Crop:采用随机图像差值方式,对图像进行裁剪、缩放;包括Scale Jittering方法(VGG及ResNet模型使用)或者尺度和长宽比增强变换
  • Horizontal/Vertical Flip:水平/垂直翻转
  • Shift:平移变换
  • Rotation/Reflection:旋转/仿射变换
  • Noise:高斯噪声、模糊处理
  • Label shuffle:类别不平衡数据的增广,参见海康威视ILSVRC2016的report

ImageDataGenerator()函数

这是Keras提供的一个自动增强的函数

ImageDataGenerator(
    featurewise_center=False,
    samplewise_center=False,
    featurewise_std_normalization=False,
    samplewise_std_normalization=False,
    zca_whitening=False,
    rotation_range=0.,
    width_shift_range=0.,
    height_shift_range=0.,
    shear_range=0.,
    zoom_range=0.,
    channel_shift_range=0.,
    fill_mode='nearest',
    cval=0.,
    horizontal_flip=False,
    vertical_flip=False,
    rescale=None,
    dim_ordering=K.image_dim_ordering())

参数解释,所有参数不一定都需要包括,可以不写。

featurewise_center:布尔值,使输入数据集去中心化(均值为0)
samplewise_center:布尔值,使输入数据的每个样本均值为0
featurewise_std_normalization:布尔值,将输入除以数据集的标准差以完成标准化
samplewise_std_normalization:布尔值,将输入的每个样本除以其自身的标准差
zca_whitening:布尔值,对输入数据施加ZCA白化
rotation_range:整数,数据提升时图片随机转动的角度
width_shift_range:浮点数,图片宽度的某个比例,数据提升时图片水平偏移的幅度
height_shift_range:浮点数,图片高度的某个比例,数据提升时图片竖直偏移的幅度
shear_range:浮点数,剪切强度(逆时针方向的剪切变换角度)
zoom_range:浮点数或形如[lower,upper]的列表,随机缩放的幅度,若为浮点数,则相当于[lower,upper] = [1 - zoom_range, 1+zoom_range]
channel_shift_range:浮点数,随机通道偏移的幅度
fill_mode:;‘constant’,‘nearest’,‘reflect’或‘wrap’之一,当进行变换时超出边界的点将根据本参数给定的方法进行处理
cval:浮点数或整数,当fill_mode=constant时,指定要向超出边界的点填充的值
horizontal_flip:布尔值,进行随机水平翻转
vertical_flip:布尔值,进行随机竖直翻转
rescale: 重放缩因子,默认为None. 如果为None或0则不进行放缩,否则会将该数值乘到数据上(在应用其他变换之前)
dim_ordering:‘tf’和‘th’之一,规定数据的维度顺序。‘tf’模式下数据的形状为samples, width, height, channels,‘th’下形状为(samples, channels, width, height).该参数的默认值是Keras配置文件~/.keras/keras.json的image_dim_ordering值,如果你从未设置过的话,就是'th'

有了这个函数,那怎么具体对一张图片或者批量化处理

from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
datagen = ImageDataGenerator(
    rotation_range=0.2,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest')

img = load_img('lena.jpg')
x = img_to_array(img)
x = x.reshape((1,) + x.shape)
i = 0
for batch in datagen.flow(x,
    batch_size=1,
    save_to_dir='data/preview',   #保存在这个文件夹下
    save_prefix='lena',
    save_format='jpg'):
    i += 1
    if i > 20:  #生成20张图
        break

其中flow函数的参数解释

X:样本数据,秩应为4.在黑白图像的情况下channel轴的值为1,在彩色图像情况下值为3
batch_size:整数,默认32
shuffle:布尔值,是否随机打乱数据,默认为True
save_to_dir:None或字符串,该参数能让你将提升后的图片保存起来,用以可视化
save_prefix:字符串,保存提升后图片时使用的前缀, 仅当设置了save_to_dir时生效
save_format:”png”或”jpeg”之一,指定保存图片的数据格式,默认”jpeg”
yields:形如(x,y)的tuple,x是代表图像数据的numpy数组.y是代表标签的numpy数组.该迭代器无限循环.
seed: 整数,随机数种子

以上所有详情可以参考keras官方文档
如果你对每一步的效果不够清楚,请参考参数演示


PCA Jittering方法

PCA是主成分分析,它是为了减少数据集的维数,同时保持数据集中的对方差贡献最大的特征,保留低阶主成分,忽略高阶主成分。实施过程是对协方差矩阵进行特征分解,得到特征值和特征向量。 关于PCA,可以去查看链接。

根据AlexNet论文,内容描述为:

添加多个找到的主成分,其幅度与相应的特征值成正比,并要乘以一个零均值,标准差为0.1的高斯变量,对每一个RGB值 Ixy=[IxyR , IxyG , IxyB ] 要添加[p1 , p2 , p3] [α1λα1 , α2λα2 , α3λα3] ,p和λ是协方差矩阵的特征向量和特征值,α是高斯变量,该高斯变量的标准差参数也是唯一引入的外部变量

复现代码如下(使用方法在后面):

import numpy as np
import os
from PIL import Image, ImageOps
import random
from scipy import misc
import imageio

def PCA_Jittering(path):
    img_list = os.listdir(path)
    img_num = len(img_list)

    for i in range(img_num):
        img_path = os.path.join(path, img_list[i])
        img = Image.open(img_path)    

        img = np.asanyarray(img, dtype = 'float32')

        img = img / 255.0
        img_size = img.size // 3    #转换为单通道
        img1 = img.reshape(img_size, 3)

        img1 = np.transpose(img1)   #转置
        img_cov = np.cov([img1[0], img1[1], img1[2]])    #协方差矩阵
        lamda, p = np.linalg.eig(img_cov)     #得到上述协方差矩阵的特征向量和特征值

        #p是协方差矩阵的特征向量
        p = np.transpose(p)    #转置回去

        #生成高斯随机数********可以修改
        alpha1 = random.gauss(0,3)
        alpha2 = random.gauss(0,3)
        alpha3 = random.gauss(0,3)

        #lamda是协方差矩阵的特征值
        v = np.transpose((alpha1*lamda[0], alpha2*lamda[1], alpha3*lamda[2]))     #转置

        #得到主成分
        add_num = np.dot(p,v)

        #在原图像的基础上加上主成分
        img2 = np.array([img[:,:,0]+add_num[0], img[:,:,1]+add_num[1], img[:,:,2]+add_num[2]])

        #现在是BGR,要转成RBG再进行保存
        img2 = np.swapaxes(img2,0,2)
        img2 = np.swapaxes(img2,0,1)
        save_name = 'pre'+str(i)+'.png'
        save_path = os.path.join(path, save_name)
        misc.imsave(save_path,img2)

        #plt.imshow(img2)
        #plt.show()

PCA_Jittering('testpic')

使用方法是:主函数PCA_Jitterring括号中的参数是当前路径下的一个文件夹,它会自动加载文件夹中的图片。调整该方法的唯一参数(高斯函数的标准差)是在标记了生成高斯随机数那里,改变参数会得到很不同的效果。


Label Shuffling

由于场景数据集不均匀的类别分布,给模型训练带来了困难。海康威视提出了Label Shuffling的类别平衡策略。在Class-Aware Sampling方法中,定义了2种列表,一是类别列表,一是每个类别的图像列表,对于80类的分类问题来说,就需要事先定义80个列表,很不方便。对此进行了改进,只需要原始的图像列表就可以完成同样的均匀采样任务。

步骤如下:

首先对原始的图像列表,按照标签顺序进行排序;然后计算每个类别的样本数量,并得到样本最多的那个类别的样本数。根据这个最多的样本数,对每类随机都产生一个随机排列的列表;然后用每个类别的列表中的数对各自类别的样本数求余,得到一个索引值,从该类的图像中提取图像,生成该类的图像随机列表;然后把所有类别的随机列表连在一起,做个Random Shuffling,得到最后的图像列表,用这个列表进行训练。每个列表,到达最后一张图像的时候,然后再重新做一遍这些步骤,得到一个新的列表,接着训练。Label Shuffling方法的优点在于,只需要原始图像列表,所有操作都是在内存中在线完成,非常易于实现。

用一幅图片可以生动地展示这个过程

实现的代码如下:

import random

category=80

f=open('scene_train_20170904.txt')  #按照label从小到大排序
#f=open('scene_validation_20170908.txt')
dicts={}
for line in f:
    line=line.strip('\n')
    image=line.split()[0]
    label=int(line.split()[-1])
    dicts[image]=label
dicts=sorted(dicts.items(),key=lambda item:item[1])
f.close()

counts={}   #统计每一类label的数目
new_dicts=[]
for i in range(category):
    counts[i]=0
for line in dicts:
    line=list(line)
    line.append(counts[line[1]])
    #print line
    counts[line[1]]+=1
    new_dicts.append(line)
#print counts

#for line in new_dicts:
#    print line

tab=[]  #把原列表按照每一类分成各个block并形成新列表
origin_index=0
for i in range(category):
    block = []
    for j in range(counts[i]):
        block.append(new_dicts[origin_index])
        origin_index+=1
    #print block
    tab.append(block)
#print tab

nums=[] #找到数目最多的label类别
for key in counts:
    nums.append(counts[key])
nums.sort(reverse=True)
#print nums

lists=[]    #形成随机label序列
for i in range(nums[0]):
    lists.append(i)
#print lists
all_index=[]
for i in range(category):
    random.shuffle(lists)
    #print lists
    lists_res=[j%counts[i] for j in lists]
    all_index.append(lists_res)
    #print lists_res
#print all_index

#f=open('train_shuffle_labels.txt','w') #按照随机序列提取图像生成最后的标签
#f=open('val_shuffle_labels.txt','w')
f=open('train_shuffle_labels.lst','w')
#f=open('val_shuffle_labels.lst','w')
shuffle_labels=[]
index=0
for line in all_index:
    for i in line:
        shuffle_labels.append(tab[index][i])
    index+=1
#print shuffle_labels
random.shuffle(shuffle_labels)
id=0
for line in shuffle_labels:
    #print line
    #f.write(line[0]+' '+str(line[1]))
    f.write(str(id)+'\t'+str(line[1])+'\t'+line[0])
    f.write('\n')
    id+=1
f.close()

另外我觉得单纯地复制粘贴会不会有用呢

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页