第T10周:数据增强

>- ** 本文为[365天深度学习训练营]中的学习记录博客**
>- ** 原作者:[K同学啊]**

第10周:数据增强

  • 难度:夯实基础⭐⭐
  • 语言:Python3、TensorFlow2

要求:

  1. 学会在代码中使用数据增强手段来提高acc
  2. 请探索更多的数据增强手段并记录

在本教程中,你将学会如何进行数据增强,并通过数据增强用少量数据达到非常非常棒的识别准确率。

我将展示两种数据增强方式,以及如何自定义数据增强方式并将其放到我们代码当中,两种数据增强方式如下:

  • 将数据增强模块嵌入model中
  • 在Dataset数据集中进行数据增强

我的环境:

  • 语言环境:Python3.11.7
  • 编译器:jupyter notebook
  • 深度学习框架:TensorFlow2.13.0

一、前期工作

1. 设置GPU

如果使用的是CPU可以注释掉这部分的代码。

#隐藏警告
import warnings
warnings.filterwarnings("ignore")

import tensorflow as tf
gpus=tf.config.list_physical_devices("GPU")

if gpus:
    tf.config.experimental.set_memory_growth(gpus[0],True)
    tf.config.set_visible_devices([gpus[0]],"GPU")
    
#打印显卡信息,确认GPU可用
print(gpus)

2. 加载数据

加载训练集:

data_dir="D:\THE MNIST DATABASE\T8"

train_ds=tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.3,
    subset="training",
    seed=12,
    image_size=(224,224),
    batch_size=32
)

 运行结果:

Found 3400 files belonging to 2 classes.
Using 2380 files for training.

加载验证集:

val_ds=tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.3,
    subset="validation",
    seed=12,
    image_size=(224,224),
    batch_size=32
)

运行结果:

Found 3400 files belonging to 2 classes.
Using 1020 files for validation.

由于原始数据集不包含测试集,因此需要创建一个。使用 tf.data.experimental.cardinality 确定验证集中有多少批次的数据,然后将其中的 20% 移至测试集。

#计算TensorFlow数据集(val_ds)的批次数量
val_batches=tf.data.experimental.cardinality(val_ds)
#将原始验证集(val_ds)的前1/5的数据作为测试集
test_ds=val_ds.take(val_batches//5)
#将原始验证集(val_ds)的后4/5的数据作为新的验证集
val_ds=val_ds.skip(val_batches//5)

print('Number of validation batches:%d'%tf.data.experimental.cardinality(val_ds))
print('Number of test batches:%d'%tf.data.experimental.cardinality(test_ds))

运行结果: 

Number of validation batches:26
Number of test batches:6

查看训练集的类别:

class_names=train_ds.class_names
print(class_names)

运行结果:

['cat', 'dog']

3. 数据预处理

AUTOTUNE=tf.data.AUTOTUNE

def preprocess_image(image,label):
    return (image/255.0,label)

#归一化处理
train_ds=train_ds.map(preprocess_image,num_parallel_calls=AUTOTUNE)
val_ds=val_ds.map(preprocess_image,num_parallel_calls=AUTOTUNE)
test_ds=test_ds.map(preprocess_image,num_parallel_calls=AUTOTUNE)

#使用cache()方法缓存数据集,以便在多次迭代时提高性能,
#并使用prefetch(buffer_size=AUTOTUNE)预取数据,以便在模型训练时能够更快地获取数据
train_ds=train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds=val_ds.cache().prefetch(buffer_size=AUTOTUNE)

在 TensorFlow 中,map 是一种对数据集中的每个元素应用一个函数的方法,常用于数据预处理和数据增强等任务。其使用方式为:

dataset = dataset.map(map_func, num_parallel_calls=None)
其中,dataset 表示待处理的数据集对象,map_func 表示要应用的函数,num_parallel_calls 表示并行执行 map_func 的线程数。

具体来说,map_func 函数会被应用到数据集中的每个元素上,函数接受一个或多个张量作为输入,输出也可以是一个或多个张量。map_func 的定义方式应当符合 TensorFlow 的计算图模型,即是一组 TensorFlow 的计算操作(ops)。

使用 map 方法可以方便地对数据集进行预处理,例如图像数据的归一化、尺寸调整、数据增强等。同时,由于 map 方法本身支持并行处理,因此可以大大加速数据处理的速度。

在使用 map 方法时,应尽可能指定 num_parallel_calls 参数以充分利用计算资源,提高处理效率。 


查看数据集中的图片:

import matplotlib.pyplot as plt

plt.figure(figsize=(15,10))

for images,labels in train_ds.take(1):
    for i in range(8):
        
        ax=plt.subplot(5,8,i+1)
        plt.imshow(images[i])
        plt.title(class_names[labels[i]])
        
        plt.axis("off")

 train_ds.take(1) 是一个方法调用,它返回一个数据集对象 train_ds 中的子集,其中包含了 take() 方法参数指定的数量的样本。
在这个例子中,take(1) 意味着我们从 train_ds 数据集中获取一批包含一个样本的数据块。

因此,for images, labels in train_ds.take(1): 的作用是遍历这个包含一个样本的数据块,并将其中的图像张量和标签张量依次赋值给变量 images 和 labels。具体来说,
它的执行过程如下:

从 train_ds 数据集中获取一批大小为 1 的数据块。
遍历这个数据块,每次获取一个图像张量和一个标签张量。
将当前图像张量赋值给变量 images,将当前标签张量赋值给变量 labels。
执行 for 循环中的代码块,即对当前图像张量和标签张量进行处理。

运行结果:

二、数据增强

我们可以使用 tf.keras.layers.experimental.preprocessing.RandomFliptf.keras.layers.experimental.preprocessing.RandomRotation 进行数据增强

  • tf.keras.layers.experimental.preprocessing.RandomFlip:水平和垂直随机翻转每个图像。
  • tf.keras.layers.experimental.preprocessing.RandomRotation:随机旋转每个图像
data_augmentation=tf.keras.Sequential([
    tf.keras.layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical"),
    tf.keras.layers.experimental.preprocessing.RandomRotation(0.2)
])

第一个层表示进行随机的水平和垂直翻转,而第二个层表示按照 0.2 的弧度值进行随机旋转。


显示增强处理后的图片:

image=tf.expand_dims(images[i],0)

plt.figure(figsize=(8,8))
for i in range(9):
    augmented_image=data_augmentation(image)
    ax=plt.subplot(3,3,i+1)
    plt.imshow(augmented_image[0])
    plt.axis("off")

运行结果:

第T10周:数据增强_第1张图片

更多的数据增强方式可以参考:https://www.tensorflow.org/api_docs/python/tf/keras/layers/RandomRotation 

三、增强方式

方法一:将其嵌入model中

from tensorflow.keras import layers

model=tf.keras.Sequential([
    data_augmentation,
    layers.Conv2D(16,3,padding='same',activation='relu'),
    layers.MaxPooling2D()
])

 这样做的好处是:

  • 数据增强这块的工作可以得到GPU的加速(如果你使用了GPU训练的话)

注意:只有在模型训练时(Model.fit)才会进行增强,在模型评估(Model.evaluate)以及预测(Model.predict)时并不会进行增强操作。

方法二:在Dataset数据集中进行数据增强

AUTOTUNE=tf.data.AUTOTUNE

def prepare(ds):
    ds=ds.map(lambda x,y:(data_augmentation(x,training=True),y),num_parallel_calls=AUTOTUNE)
    return ds
train_ds=prepare(train_ds)

四、训练模型

搭建模型:

model=tf.keras.Sequential([
    layers.Conv2D(16,3,padding='same',activation='relu'),
    layers.MaxPooling2D(),
    layers.Conv2D(32,3,padding='same',activation='relu'),
    layers.MaxPooling2D(),
    layers.Conv2D(64,3,padding='same',activation='relu'),
    layers.MaxPooling2D(),
    layers.Flatten(),
    layers.Dense(128,activation='relu'),
    layers.Dense(len(class_names))
])

在准备对模型进行训练之前,还需要再对其进行一些设置。以下内容是在模型的编译步骤中添加的:
●损失函数(loss):用于衡量模型在训练期间的准确率。
●优化器(optimizer):决定模型如何根据其看到的数据和自身的损失函数进行更新。
●评价函数(metrics):用于监控训练和测试步骤。以下示例使用了准确率,即被正确分类的图像的比率。

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

开始训练:

epochs=20
history=model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=epochs
)

运行结果:

Epoch 1/20
75/75 [==============================] - 57s 734ms/step - loss: 0.7199 - accuracy: 0.5866 - val_loss: 0.7071 - val_accuracy: 0.6316
Epoch 2/20
75/75 [==============================] - 48s 641ms/step - loss: 0.4772 - accuracy: 0.7731 - val_loss: 0.3238 - val_accuracy: 0.8551
Epoch 3/20
75/75 [==============================] - 47s 620ms/step - loss: 0.2988 - accuracy: 0.8702 - val_loss: 0.2515 - val_accuracy: 0.8973
Epoch 4/20
75/75 [==============================] - 47s 622ms/step - loss: 0.2416 - accuracy: 0.9013 - val_loss: 0.2010 - val_accuracy: 0.9143
Epoch 5/20
75/75 [==============================] - 46s 614ms/step - loss: 0.2027 - accuracy: 0.9214 - val_loss: 0.2278 - val_accuracy: 0.8961
Epoch 6/20
75/75 [==============================] - 47s 619ms/step - loss: 0.1832 - accuracy: 0.9315 - val_loss: 0.1490 - val_accuracy: 0.9384
Epoch 7/20
75/75 [==============================] - 46s 610ms/step - loss: 0.1522 - accuracy: 0.9395 - val_loss: 0.1552 - val_accuracy: 0.9372
Epoch 8/20
75/75 [==============================] - 46s 605ms/step - loss: 0.1712 - accuracy: 0.9340 - val_loss: 0.1601 - val_accuracy: 0.9324
Epoch 9/20
75/75 [==============================] - 45s 602ms/step - loss: 0.1249 - accuracy: 0.9542 - val_loss: 0.1386 - val_accuracy: 0.9396
Epoch 10/20
75/75 [==============================] - 47s 617ms/step - loss: 0.1398 - accuracy: 0.9458 - val_loss: 0.1095 - val_accuracy: 0.9626
Epoch 11/20
75/75 [==============================] - 45s 595ms/step - loss: 0.1066 - accuracy: 0.9592 - val_loss: 0.1141 - val_accuracy: 0.9541
Epoch 12/20
75/75 [==============================] - 45s 593ms/step - loss: 0.1073 - accuracy: 0.9559 - val_loss: 0.1460 - val_accuracy: 0.9396
Epoch 13/20
75/75 [==============================] - 45s 594ms/step - loss: 0.1140 - accuracy: 0.9597 - val_loss: 0.1089 - val_accuracy: 0.9529
Epoch 14/20
75/75 [==============================] - 45s 594ms/step - loss: 0.0974 - accuracy: 0.9639 - val_loss: 0.1399 - val_accuracy: 0.9481
Epoch 15/20
75/75 [==============================] - 45s 593ms/step - loss: 0.1001 - accuracy: 0.9626 - val_loss: 0.1177 - val_accuracy: 0.9577
Epoch 16/20
75/75 [==============================] - 45s 598ms/step - loss: 0.0878 - accuracy: 0.9651 - val_loss: 0.1584 - val_accuracy: 0.9457
Epoch 17/20
75/75 [==============================] - 45s 598ms/step - loss: 0.0915 - accuracy: 0.9647 - val_loss: 0.1313 - val_accuracy: 0.9529
Epoch 18/20
75/75 [==============================] - 45s 594ms/step - loss: 0.1057 - accuracy: 0.9605 - val_loss: 0.1183 - val_accuracy: 0.9601
Epoch 19/20
75/75 [==============================] - 45s 595ms/step - loss: 0.0772 - accuracy: 0.9697 - val_loss: 0.1319 - val_accuracy: 0.9529
Epoch 20/20
75/75 [==============================] - 45s 597ms/step - loss: 0.0840 - accuracy: 0.9664 - val_loss: 0.1093 - val_accuracy: 0.9674

五、自定义增强函数

tf.image.stateless_random_contrast 是TensorFlow中的一个函数,用于对图像进行随机对比度调整。它接受一个图像张量、一个范围(最小和最大对比度因子)和一个种子作为输入参数,并返回一个新的图像张量,其对比度已经根据指定的范围进行了随机调整。

import random

def aug_img(image):
    seed=(random.randint(0,9),0)
    #随机改变图像对比度
    stateless_random_brightness=tf.image.stateless_random_contrast(image,lower=0.1,upper=1.0,seed=seed)
    return stateless_random_brightness

image=tf.expand_dims(images[3]*255,0)
print("Min and max pixel values:",image.numpy().min(),image.numpy().max())

 作用:

  1. images[3]*255:将images列表中的第4个元素(索引为3)乘以255,可能是为了将图像数据从归一化的范围(例如0到1)转换为8位无符号整数范围(0到255)。
  2. tf.expand_dims(..., 0):在张量的第0维上增加一个维度,使得原本的形状为(height, width, channels)的图像变为形状为(1, height, width, channels)的张量。这通常用于将单个图像转换为批次的形式,以便在神经网络中进行批量处理。
  3. image.numpy():将TensorFlow张量转换为NumPy数组,以便使用Python的标准库函数进行操作。
  4. image.numpy().min()image.numpy().max():分别计算转换后的NumPy数组中的最小值和最大值。

运行结果:

Min and max pixel values: 2.4591687 241.47968

plt.figure(figsize=(8,8))
for i in range(9):
    augmented_image=aug_img(image)
    ax=plt.subplot(3,3,i+1)
    plt.imshow(augmented_image[0].numpy().astype("uint8"))
    
    plt.axis("off")

 运行结果:

第T10周:数据增强_第2张图片


六、心得体会

在本项目中,对数据集进行了数据增强,可以看出,在搭建的很小的一个模型下,模型结果在极短的时间内迅速提升。故数据增强可以有效提升模型的准确率和泛化能力,使模型鲁棒性更强,避免过度拟合。
 

你可能感兴趣的:(深度学习,人工智能,tensorflow2)