Bootstrap

tensorflow案例7--数据增强与测试集, 训练集, 验证集的构建

前言

  • 这次主要是学习数据增强, 训练集 验证集 测试集的构建等等的基本方法, 数据集还是用的上一篇的猫狗识别;
  • 基础篇还剩下几个, 后面的难度会逐步提升;
  • 欢迎收藏 + 关注, 本人会持续更新.

1. 简介

数据增强

💙 有时候数据很好, **就可以通过在原有的基础上做一些操作, ** 从而增加数据的数量, 使训练模型更加有效.

📶 对于基础的的增强, 一般就是旋转, 在pytorch中一般是用transforms.Compose进行处理, 在tensorflow中,一般用的是tf.keras.layers.experimental.preprocessing.RandomFliptf.keras.layers.experimental.preprocessing.RandomRotation 进行数据增强, 👁 具体做法请看案例

当然还有其他的方法进行增强, 比如说添加噪音, 👓 详情请看第四节, 4. 其他方法数据增强

数据增强加入模型中

一般有两个方法:

  1. 加入数据集(本文用的方法)
  2. 加入到模型中, 让模型训练的时候, 开始进行数据增强, 这个本文不介绍

注意: 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.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)                      # 随机翻转
])

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"))


在这里插入图片描述

;