【Tensorflow 2.0 正式版教程】ImageNet(一)数据增强

前面的教程都只在小模型、小数据库上进行了演示,这次来真正实战一个大型数据库ImageNet。教程会分为三部分:数据增强、模型加载与训练、模型测试,最终在ResNet50上可以达到77.72%的top-1准确率,复现出了ResNet原文的结果。

完整的代码可以在我的github上找到。https://github.com/Apm5/ImageNet_Tensorflow2.0

提供ResNet-18和ResNet-50的预训练模型,以供大家做迁移使用。
链接:https://pan.baidu.com/s/1nwvkt3Ei5Hp5Pis35cBSmA
提取码:y4wo

还提供百度云链接的ImageNet原始数据,但是这份资源只能创建临时链接以供下载,有需要的还请私信联系。下面开始正文。

数据增强

本文着重于数据增强的代码实现,内容的学习可以参考CVPR2019上的一篇文章Bag of Tricks for Image Classification with Convolutional Neural Networks。文章中提到在训练时图像依次进行:

  1. 随机选取一张图片并转为32位浮点数
  2. 随机选取一个长宽比在3:4到4:3之间的区域,区域面积占完整图像的8%到100%,然后将选取的区域插值为224*224大小
  3. 50%的概率进行左右翻转
  4. 图像在hsv空间随机抖动,抖动幅度采样于(0.6,1.4)的均匀分布
  5. 为图像添加PCA噪声,噪声系数采样于(0,0.1)的正态分布
  6. 将图像归一化到(0,1)分布

在代码的实现中,实际的处理顺序与参数选取与论文稍有不同,整体流程为:

image = random_aspect(image)
image = random_size(image)
image = random_crop(image)
image = random_flip(image)
image = random_hsv(image)
image = random_pca(image)

下面一一解析每个增强函数。

随机长宽比

def random_aspect(image):
    height, width, _ = np.shape(image)
    aspect_ratio = np.random.uniform(*c.aspect_ratio_scale)
    if height < width:
        resize_shape = (int(width * aspect_ratio), height)
    else:
        resize_shape = (width, int(height * aspect_ratio))
    return cv2.resize(image, resize_shape)

裁剪随机长宽比的区域,等价于先对原始图像的长宽比进行调整,然后裁剪正方形的区域。长宽比设置为aspect_ratio_scale = (0.8, 1.25),即4:5到5:4之间。

随机尺度

def random_size(image, target_size=None):
    height, width, _ = np.shape(image)
    if target_size is None:
        # for test
        # target size is fixed
        target_size = np.random.randint(*c.short_side_scale)
    if height < width:
        size_ratio = target_size / height
    else:
        size_ratio = target_size / width
    resize_shape = (int(width * size_ratio), int(height * size_ratio))  # width and height in cv2 are opposite to np.shape()
    return cv2.resize(image, resize_shape)

裁剪随机面积占比的区域再插值到224224大小,等价于先随机放缩原始图像,再裁剪224224大小的区域。实现上保持长宽比,让图像的短边长度在256到384之间,即short_side_scale = (256, 384)

随机裁剪

def random_crop(image):
    height, width, _ = np.shape(image)
    input_height, input_width, _ = c.input_shape
    crop_x = np.random.randint(0, width - input_width)
    crop_y = np.random.randint(0, height - input_height)
    return image[crop_y: crop_y + input_height, crop_x: crop_x + input_width, :]

有了上述两个随机长宽比和随机尺度的函数,随机裁剪只需要随机选取224*224大小的区域位置即可。

随机翻转

def random_flip(image):
    if np.random.rand() < 0.5:
        image = cv2.flip(image, 1)
    return image

对于ImageNet这类日常图像来说,上下翻转是没有意义的,因此只以50%的概率进行左右翻转

hsv空间随机抖动

def random_hsv(image):
    random_h = np.random.uniform(*c.hue_delta)
    random_s = np.random.uniform(*c.saturation_scale)
    random_v = np.random.uniform(*c.brightness_scale)

    image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    image_hsv[:, :, 0] = image_hsv[:, :, 0] + random_h % 360.0  # hue
    image_hsv[:, :, 1] = np.minimum(image_hsv[:, :, 1] * random_s, 1.0)  # saturation
    image_hsv[:, :, 2] = np.minimum(image_hsv[:, :, 2] * random_v, 255.0)  # brightness

    return cv2.cvtColor(image_hsv, cv2.COLOR_HSV2BGR)

hsv空间即图像的色调(hue)、对比度(saturation)和明度(brightness),随机抖动时需要注意各维度取值上下界以避免转回RGB空间时发生溢出。

随机pca噪声

def random_pca(image):
    alpha = np.random.normal(0, c.pca_std, size=(3,))
    offset = np.dot(c.eigvec * alpha, c.eigval)
    image = image + offset
    return np.maximum(np.minimum(image, 255.0), 0.0)

需要事先对训练集数据进行主成分分析,然后再在训练时添加随机大小的主成分噪声。

至此,图像增强便已完成,最后再将图像归一化到标准正态分布,作为网络的输入。

def normalize(image):
    for i in range(3):
        image[..., i] = (image[..., i] - c.mean[i]) / c.std[i]
    return image

在实际训练中,数据增强能在不改变模型本身的情况下大幅提高网络性能,但过多的增强(如极端的长宽比变化、尺度放缩)也会使网络展现出欠拟合的结果。

上述代码位于项目中的utils/aug_utils.py

你可能感兴趣的:(tensorflow)