- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
前言
- 这次主要是学习数据增强, 训练集 验证集 测试集的构建等等的基本方法, 数据集还是用的上一篇的猫狗识别;
- 基础篇还剩下几个, 后面的难度会逐步提升;
- 欢迎收藏 + 关注, 本人会持续更新.
文章目录
1. 简介
数据增强
💙 有时候数据很好, **就可以通过在原有的基础上做一些操作, ** 从而增加数据的数量, 使训练模型更加有效.
📶 对于基础的的增强, 一般就是旋转, 在pytorch
中一般是用transforms.Compose
进行处理, 在tensorflow
中,一般用的是tf.keras.layers.experimental.preprocessing.RandomFlip
与 tf.keras.layers.experimental.preprocessing.RandomRotation
进行数据增强, 👁 具体做法请看案例
当然还有其他的方法进行增强, 比如说添加噪音, 👓 详情请看第四节, 4. 其他方法数据增强
数据增强加入模型中
一般有两个方法:
- 加入数据集(本文用的方法)
- 加入到模型中, 让模型训练的时候, 开始进行数据增强, 这个本文不介绍
注意: tensorflow和numpy版本问题不同, 可能会出现比较多数据方面的错误, 本人这个案例最后也是在云平台上跑通的.
训练集划分
简单说一下训练集, 测试集, 验证集的区别:
- 训练集: 用来训练模型的, 确定神经网络的各种参数, 相当于我们学习一样
- 验证集: 在训练集中, 通过验证模型效果, 来调整模型参数, 这个就相当于我们月考一样
- 测试集: 这个就是验证模型是都具有效果, 适用于其他数据, 这个就相当于我们大考
👀 在tensorflow
中, 我们可以通过tf.keras.preprocessing.image_dataset_from_directory
创建训练集和验证集, 但是不能创建测试集, 创建测试集的方法, 需要我们后面对数据进行分类, 如下:
val_batches = tf.data.experimental.cardinality(val_ds)
# 创建测试集, 方法: 将验证集合拆成 5 分, 测试集占一份, 验证集占 4 份
test_ds = val_ds.take(val_batches // 2) # 取前 * 批次
val_ds = val_ds.skip(val_batches // 2) # 除了前 * 批次
解释:
tf.data.experimental.cardinality
获取数据批次大小.take
: 取前n批数据.skip
: 取除了前n批次数据
2. 案例测试
本次案例是对猫狗图像进行分类, 和上一期很像, 但是这个模型使用比较简单.
注意: 不同池化层, 效果有时候天差地别, 比如说: 这个案例用的是最大池化, 但是用平均池化的话, 效果极差
1. 数据处理
1. 导入库
import tensorflow as tf
from tensorflow.keras import layers, models, datasets
import numpy as np
gpus = tf.config.list_physical_devices("GPU")
if gpus:
gpu0 = gpus[0]
tf.config.experimental.set_memory_growth(gpu0, True) # 输出存储在GPU
tf.config.set_visible_devices([gpu0], "GPU") # 选择第一块GPU
gpus
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
2. 导入数据(训练集 测试集 验证集)
# 查看数据目录
import os, pathlib
data_dir = "./data/"
data_dir = pathlib.Path(data_dir)
classnames = [str(path) for path in os.listdir(data_dir)]
classnames
['cat', 'dog']
# 创建训练集和验证集
batch_size = 32
image_width, image_height = 224, 224
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
'./data/',
subset='training',
validation_split=0.3,
batch_size=batch_size,
image_size=(image_width, image_height),
shuffle=True,
seed=42
)
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
'./data',
subset='validation',
validation_split=0.3,
batch_size=batch_size,
image_size=(image_width, image_height),
shuffle=True,
seed=42
)
Found 600 files belonging to 2 classes.
Using 420 files for training.
Found 600 files belonging to 2 classes.
Using 180 files for validation.
在tensorflow没有提供直接分割测试集的函数,但是可以通过分割验证集的方法进行创建测试集
val_batches = tf.data.experimental.cardinality(val_ds)
# 创建测试集, 方法: 将验证集合拆成 5 分, 测试集占一份, 验证集占 4 份
test_ds = val_ds.take(val_batches // 2) # 取前 * 批次
val_ds = val_ds.skip(val_batches // 2) # 取除了前 * 批次
print("test batches: %d"%tf.data.experimental.cardinality(test_ds))
print("val batches: %d"%tf.data.experimental.cardinality(val_ds))
test batches: 3
val batches: 3
训练集: 验证集: 测试集 = 0.7 : 0.15 : 0.15
3. 数据部分展示
# 数据规格展示
for images, labels in train_ds.take(1):
print("image: [N, W, H, C] ", images.shape)
print("labels: ", labels)
break
image: [N, W, H, C] (32, 224, 224, 3)
labels: tf.Tensor([0 1 1 0 0 0 1 0 0 0 0 1 0 1 0 0 0 1 1 1 1 0 1 1 0 1 1 0 1 0 1 0], shape=(32,), dtype=int32)
# 部分图片数据展示
import matplotlib.pyplot as plt
train_one_batch = next(iter(train_ds))
plt.figure(figsize=(20, 10))
images, labels = train_one_batch
for i in range(20):
plt.subplot(5, 10, i + 1)
plt.title(classnames[labels[i]])
plt.imshow(images[i].numpy().astype('uint8'))
plt.axis('off')
plt.show()
4. 数据归一化与内存加速
from tensorflow.data.experimental import AUTOTUNE
# 像素归一化, ---> [0, 1]
normalization_layer = layers.experimental.preprocessing.Rescaling(1.0 / 255)
# 训练集、测试集像素归一化
train_ds = train_ds.map(lambda x, y : (normalization_layer(x), y))
val_ds = val_ds.map(lambda x, y : (normalization_layer(x), y))
test_ds = test_ds.map(lambda x, y : (normalization_layer(x), y))
# 设置内存加速
AUTOTUNE = tf.data.experimental.AUTOTUNE
# 打乱顺序加速, 测试集就不必了哈
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
5. 数据增强
我们可以使用 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) # 随机翻转
])
test_datas = next(iter(train_ds))
test_images, test_labels = test_datas
# 随机选取一个
test_image = tf.expand_dims(test_images[i], 0)
plt.figure(figsize=(8, 8))
for i in range(9):
augmented_image = data_augmentation(test_image) # 旋转
ax = plt.subplot(3, 3, i + 1)
plt.imshow(augmented_image[0])
plt.axis("off")
6. 将增强数据融合到原始数据中
batch_size = 32
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)
2. 模型创建
model = models.Sequential([
# 第一层要输入维度
layers.Conv2D(16, (3, 3), activation='relu', input_shape=(image_width, image_height, 3)),
layers.MaxPooling2D((2,2)),
layers.Conv2D(32, (3, 3), activation='relu'),
layers.MaxPooling2D((2,2)),
layers.Dropout(0.3),
layers.Conv2D(32, (3, 3), activation='relu'),
layers.MaxPooling2D((2,2)),
layers.Dropout(0.3),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(len(classnames))
])
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 222, 222, 16) 448
max_pooling2d (MaxPooling2D (None, 111, 111, 16) 0
)
conv2d_1 (Conv2D) (None, 109, 109, 32) 4640
max_pooling2d_1 (MaxPooling (None, 54, 54, 32) 0
2D)
dropout (Dropout) (None, 54, 54, 32) 0
conv2d_2 (Conv2D) (None, 52, 52, 32) 9248
max_pooling2d_2 (MaxPooling (None, 26, 26, 32) 0
2D)
dropout_1 (Dropout) (None, 26, 26, 32) 0
flatten (Flatten) (None, 21632) 0
dense (Dense) (None, 128) 2769024
dense_1 (Dense) (None, 2) 258
=================================================================
Total params: 2,783,618
Trainable params: 2,783,618
Non-trainable params: 0
_________________________________________________________________
3. 模型训练
1. 超参数设置
opt = tf.keras.optimizers.Adam(learning_rate=0.001) # 学习率:0.001
model.compile(
optimizer = opt,
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics = ['accuracy']
)
2. 模型训练
epochs=20
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs,
verbose=1
)
Epoch 1/20
2024-11-22 18:03:21.866630: I tensorflow/stream_executor/cuda/cuda_dnn.cc:384] Loaded cuDNN version 8101
2024-11-22 18:03:23.553540: I tensorflow/stream_executor/cuda/cuda_blas.cc:1786] TensorFloat-32 will be used for the matrix multiplication. This will only be logged once.
14/14 [==============================] - 4s 36ms/step - loss: 0.7059 - accuracy: 0.5643 - val_loss: 0.6646 - val_accuracy: 0.6667
Epoch 2/20
14/14 [==============================] - 0s 27ms/step - loss: 0.6125 - accuracy: 0.6381 - val_loss: 0.6096 - val_accuracy: 0.7143
Epoch 3/20
14/14 [==============================] - 0s 15ms/step - loss: 0.5027 - accuracy: 0.7714 - val_loss: 0.5646 - val_accuracy: 0.7500
Epoch 4/20
14/14 [==============================] - 0s 14ms/step - loss: 0.4723 - accuracy: 0.7952 - val_loss: 0.5496 - val_accuracy: 0.7500
Epoch 5/20
14/14 [==============================] - 0s 14ms/step - loss: 0.4395 - accuracy: 0.7857 - val_loss: 0.6267 - val_accuracy: 0.7024
Epoch 6/20
14/14 [==============================] - 0s 13ms/step - loss: 0.3721 - accuracy: 0.8262 - val_loss: 0.5001 - val_accuracy: 0.7619
Epoch 7/20
14/14 [==============================] - 0s 14ms/step - loss: 0.4041 - accuracy: 0.8238 - val_loss: 0.4595 - val_accuracy: 0.7857
Epoch 8/20
14/14 [==============================] - 0s 13ms/step - loss: 0.3195 - accuracy: 0.8643 - val_loss: 0.4247 - val_accuracy: 0.8095
Epoch 9/20
14/14 [==============================] - 0s 13ms/step - loss: 0.3010 - accuracy: 0.8738 - val_loss: 0.3674 - val_accuracy: 0.8452
Epoch 10/20
14/14 [==============================] - 0s 14ms/step - loss: 0.3190 - accuracy: 0.8762 - val_loss: 0.3660 - val_accuracy: 0.8452
Epoch 11/20
14/14 [==============================] - 0s 15ms/step - loss: 0.2864 - accuracy: 0.8690 - val_loss: 0.3529 - val_accuracy: 0.8333
Epoch 12/20
14/14 [==============================] - 0s 13ms/step - loss: 0.2532 - accuracy: 0.8762 - val_loss: 0.2737 - val_accuracy: 0.8929
Epoch 13/20
14/14 [==============================] - 0s 13ms/step - loss: 0.2374 - accuracy: 0.9000 - val_loss: 0.2939 - val_accuracy: 0.8810
Epoch 14/20
14/14 [==============================] - 0s 15ms/step - loss: 0.2216 - accuracy: 0.8976 - val_loss: 0.2952 - val_accuracy: 0.8810
Epoch 15/20
14/14 [==============================] - 0s 13ms/step - loss: 0.2365 - accuracy: 0.9095 - val_loss: 0.2559 - val_accuracy: 0.9167
Epoch 16/20
14/14 [==============================] - 0s 13ms/step - loss: 0.2114 - accuracy: 0.9071 - val_loss: 0.2702 - val_accuracy: 0.8929
Epoch 17/20
14/14 [==============================] - 0s 15ms/step - loss: 0.2075 - accuracy: 0.9024 - val_loss: 0.2353 - val_accuracy: 0.9286
Epoch 18/20
14/14 [==============================] - 0s 13ms/step - loss: 0.1850 - accuracy: 0.9262 - val_loss: 0.1927 - val_accuracy: 0.9524
Epoch 19/20
14/14 [==============================] - 0s 13ms/step - loss: 0.1318 - accuracy: 0.9524 - val_loss: 0.1837 - val_accuracy: 0.9286
Epoch 20/20
14/14 [==============================] - 0s 15ms/step - loss: 0.1561 - accuracy: 0.9476 - val_loss: 0.1951 - val_accuracy: 0.9643
3. 模型测试
loss, acc = model.evaluate(test_ds)
print("Loss: ", loss)
print("Accuracy: ", acc)
3/3 [==============================] - 0s 8ms/step - loss: 0.2495 - accuracy: 0.9062
Loss: 0.24952644109725952
Accuracy: 0.90625
测试集准确率高, 模型效果良好
4. 其他方法增强数据
这里是使数据变得模糊
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(test_images[i] * 255, 0) # 注意: 不乘255, 会出现黑色, 因为 像素在0 - 1中
plt.figure(figsize=(8,8))
for i in range(9):
image_show = aug_img(image)
plt.subplot(3, 3, i + 1)
plt.imshow(image_show[0].numpy().astype("uint8"))