>- ** 本文为[365天深度学习训练营]中的学习记录博客**
>- ** 原作者:[K同学啊]**
第10周:数据增强
要求:
在本教程中,你将学会如何进行数据增强,并通过数据增强用少量数据达到非常非常棒的识别准确率。
我将展示两种数据增强方式,以及如何自定义数据增强方式并将其放到我们代码当中,两种数据增强方式如下:
我的环境:
如果使用的是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)
加载训练集:
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']
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.RandomFlip
与 tf.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")
运行结果:
更多的数据增强方式可以参考: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()
])
这样做的好处是:
注意:只有在模型训练时(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())
作用:
images[3]*255
:将images
列表中的第4个元素(索引为3)乘以255,可能是为了将图像数据从归一化的范围(例如0到1)转换为8位无符号整数范围(0到255)。tf.expand_dims(..., 0)
:在张量的第0维上增加一个维度,使得原本的形状为(height, width, channels)
的图像变为形状为(1, height, width, channels)
的张量。这通常用于将单个图像转换为批次的形式,以便在神经网络中进行批量处理。image.numpy()
:将TensorFlow张量转换为NumPy数组,以便使用Python的标准库函数进行操作。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")
运行结果:
在本项目中,对数据集进行了数据增强,可以看出,在搭建的很小的一个模型下,模型结果在极短的时间内迅速提升。故数据增强可以有效提升模型的准确率和泛化能力,使模型鲁棒性更强,避免过度拟合。