TensorFlow|猫狗识别

要求:

  1. 了解model.train_on_batch()并运用
  2. 了解tqdm,并使用tqdm实现可视化进度条

🍻 拔高(可选):

  1. 本文代码中存在一个严重的BUG,请找出它并配以文字说明

🔎 探索(难度有点大)

  1. 修改代码,处理BUG

这篇文章中我放弃了以往的model.fit()训练方法,改用model.train_on_batch方法。两种方法的比较:

  • model.fit():用起来十分简单,对新手非常友好
  • model.train_on_batch():封装程度更低,可以玩更多花样。

此外我也引入了进度条的显示方式,更加方便我们及时查看模型训练过程中的情况,可以及时打印各项指标。

一、前期工作

1. 设置GPU

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

python 复制代码
import tensorflow as tf

gpus = tf.config.list_physical_devices("GPU")

if gpus:
    tf.config.experimental.set_memory_growth(gpus[0], True)  #设置GPU显存用量按需使用
    tf.config.set_visible_devices([gpus[0]],"GPU")

# 打印显卡信息,确认GPU可用
print(gpus)

2. 导入数据

python 复制代码
import matplotlib.pyplot as plt
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

import os,PIL,pathlib

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

data_dir = "C:/Users/76967/.keras/datasets/365-7-data"
data_dir = pathlib.Path(data_dir)

image_count = len(list(data_dir.glob('*/*')))

print("图片总数为:",image_count)

图片总数为: 3400

二、数据预处理

1. 加载数据

使用image_dataset_from_directory方法将磁盘中的数据加载到tf.data.Dataset

python 复制代码
batch_size = 8
img_height = 224
img_width = 224

TensorFlow版本是2.2.0的同学可能会遇到module 'tensorflow.keras.preprocessing' has no attribute 'image_dataset_from_directory'的报错,升级一下TensorFlow就OK了。

python 复制代码
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="training",
    seed=12,
    image_size=(img_height, img_width),
    batch_size=batch_size)

Found 3400 files belonging to 2 classes.

Using 2720 files for training.

python 复制代码
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="validation",
    seed=12,
    image_size=(img_height, img_width),
    batch_size=batch_size)

Found 3400 files belonging to 2 classes.

Using 680 files for validation.

我们可以通过class_names输出数据集的标签。标签将按字母顺序对应于目录名称。

python 复制代码
class_names = train_ds.class_names
print(class_names)

['cat', 'dog']

2. 再次检查数据

python 复制代码
for image_batch, labels_batch in train_ds:
    print(image_batch.shape)
    print(labels_batch.shape)
    break

(8, 224, 224, 3)

(8,)

  • Image_batch是形状的张量(8, 224, 224, 3)。这是一批形状224x224x3的8张图片(最后一维指的是彩色通道RGB)。
  • Label_batch是形状(8,)的张量,这些标签对应8张图片

3. 配置数据集

  • prefetch() :预取数据,加速运行,其详细介绍可以参考我前两篇文章,里面都有讲解。
  • cache() :将数据集缓存到内存当中,加速运行
python 复制代码
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)

train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds   = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

如果报 AttributeError: module 'tensorflow._api.v2.data' has no attribute 'AUTOTUNE' 错误,就将 AUTOTUNE = tf.data.AUTOTUNE 更换为 AUTOTUNE = tf.data.experimental.AUTOTUNE,这个错误是由于版本问题引起的。

4. 可视化数据

python 复制代码
plt.figure(figsize=(15, 10))  # 图形的宽为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")

三、构建VG-16网络

VGG优缺点分析:

  • VGG优点

VGG的结构非常简洁,整个网络都使用了同样大小的卷积核尺寸(3x3)和最大池化尺寸(2x2)。

  • VGG缺点

1)训练时间过长,调参难度大。2)需要的存储容量大,不利于部署。例如存储VGG-16权重值文件的大小为500多MB,不利于安装到嵌入式系统中。

结构说明:

  • 13个卷积层(Convolutional Layer),分别用blockX_convX表示
  • 3个全连接层(Fully connected Layer),分别用fcXpredictions表示
  • 5个池化层(Pool layer),分别用blockX_pool表示

VGG-16****包含了16个隐藏层(13个卷积层和3个全连接层),故称为 VGG-16

python 复制代码
from tensorflow.keras import layers, models, Input
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout

def VGG16(nb_classes, input_shape):
    input_tensor = Input(shape=input_shape)
    # 1st block
    x = Conv2D(64, (3,3), activation='relu', padding='same',name='block1_conv1')(input_tensor)
    x = Conv2D(64, (3,3), activation='relu', padding='same',name='block1_conv2')(x)
    x = MaxPooling2D((2,2), strides=(2,2), name = 'block1_pool')(x)
    # 2nd block
    x = Conv2D(128, (3,3), activation='relu', padding='same',name='block2_conv1')(x)
    x = Conv2D(128, (3,3), activation='relu', padding='same',name='block2_conv2')(x)
    x = MaxPooling2D((2,2), strides=(2,2), name = 'block2_pool')(x)
    # 3rd block
    x = Conv2D(256, (3,3), activation='relu', padding='same',name='block3_conv1')(x)
    x = Conv2D(256, (3,3), activation='relu', padding='same',name='block3_conv2')(x)
    x = Conv2D(256, (3,3), activation='relu', padding='same',name='block3_conv3')(x)
    x = MaxPooling2D((2,2), strides=(2,2), name = 'block3_pool')(x)
    # 4th block
    x = Conv2D(512, (3,3), activation='relu', padding='same',name='block4_conv1')(x)
    x = Conv2D(512, (3,3), activation='relu', padding='same',name='block4_conv2')(x)
    x = Conv2D(512, (3,3), activation='relu', padding='same',name='block4_conv3')(x)
    x = MaxPooling2D((2,2), strides=(2,2), name = 'block4_pool')(x)
    # 5th block
    x = Conv2D(512, (3,3), activation='relu', padding='same',name='block5_conv1')(x)
    x = Conv2D(512, (3,3), activation='relu', padding='same',name='block5_conv2')(x)
    x = Conv2D(512, (3,3), activation='relu', padding='same',name='block5_conv3')(x)
    x = MaxPooling2D((2,2), strides=(2,2), name = 'block5_pool')(x)
    # full connection
    x = Flatten()(x)
    x = Dense(4096, activation='relu',  name='fc1')(x)
    x = Dense(4096, activation='relu', name='fc2')(x)
    output_tensor = Dense(nb_classes, activation='softmax', name='predictions')(x)

    model = Model(input_tensor, output_tensor)
    return model

model=VGG16(1000, (img_width, img_height, 3))
model.summary()

这段代码定义了一个实现 VGG16 神经网络架构的函数,并使用 Keras 构建模型。VGG16 是一种常见的卷积神经网络(CNN)架构,通常用于图像分类任务。接下来是代码逐步的解释:

导入模块:

from tensorflow.keras import layers, models, Input

from tensorflow.keras.models import Model

from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout

  • tensorflow.keras 是 TensorFlow 的高级接口,用于构建深度学习模型。
  • Input 用于定义模型的输入层。
  • Conv2D 定义卷积层。
  • MaxPooling2D 定义最大池化层。
  • Dense 定义全连接层。
  • Flatten 用于将多维的输入展平为一维。
  • Dropout 是一种正则化方法,用于防止过拟合(在代码中没有使用)。

定义 VGG16 函数:

def VGG16(nb_classes, input_shape):

input_tensor = Input(shape=input_shape)

  • VGG16 函数接受两个参数:nb_classes 表示输出类别数(例如,如果进行 1000 类分类任务,nb_classes=1000),input_shape 是输入图像的形状(例如,(224, 224, 3) 表示图像为 224x224 像素,3 个颜色通道)。
  • input_tensor = Input(shape=input_shape) 定义了网络的输入层。

1st Block(第一个卷积块):

x = Conv2D(64, (3,3), activation='relu', padding='same', name='block1_conv1')(input_tensor)

x = Conv2D(64, (3,3), activation='relu', padding='same', name='block1_conv2')(x) x = MaxPooling2D((2,2), strides=(2,2), name='block1_pool')(x)

  • 该块包含 2 个卷积层,每个卷积层使用 64 个 3x3 的滤波器(Conv2D(64, (3, 3))),激活函数为 ReLU。
  • padding='same' 表示输入和输出的大小相同,卷积会自动填充输入的边缘。
  • 使用最大池化(MaxPooling2D),池化窗口大小为 2x2,步幅为 2。

2nd Block(第二个卷积块):

x = Conv2D(128, (3,3), activation='relu', padding='same', name='block2_conv1')(x) x = Conv2D(128, (3,3), activation='relu', padding='same', name='block2_conv2')(x) x = MaxPooling2D((2,2), strides=(2,2), name='block2_pool')(x)

  • 该块包含 2 个卷积层,每个卷积层使用 128 个 3x3 的滤波器。
  • 同样使用最大池化,池化窗口大小为 2x2。

3rd Block(第三个卷积块):

x = Conv2D(256, (3,3), activation='relu', padding='same', name='block3_conv1')(x) x = Conv2D(256, (3,3), activation='relu', padding='same', name='block3_conv2')(x) x = Conv2D(256, (3,3), activation='relu', padding='same', name='block3_conv3')(x) x = MaxPooling2D((2,2), strides=(2,2), name='block3_pool')(x)

  • 该块包含 3 个卷积层,每个卷积层使用 256 个 3x3 的滤波器。
  • 最大池化操作与前面相同。

4th Block(第四个卷积块):

x = Conv2D(512, (3,3), activation='relu', padding='same', name='block4_conv1')(x) x = Conv2D(512, (3,3), activation='relu', padding='same', name='block4_conv2')(x) x = Conv2D(512, (3,3), activation='relu', padding='same', name='block4_conv3')(x) x = MaxPooling2D((2,2), strides=(2,2), name='block4_pool')(x)

  • 该块包含 3 个卷积层,每个卷积层使用 512 个 3x3 的滤波器。
  • 使用最大池化层。

5th Block(第五个卷积块):

x = Conv2D(512, (3,3), activation='relu', padding='same', name='block5_conv1')(x) x = Conv2D(512, (3,3), activation='relu', padding='same', name='block5_conv2')(x) x = Conv2D(512, (3,3), activation='relu', padding='same', name='block5_conv3')(x) x = MaxPooling2D((2,2), strides=(2,2), name='block5_pool')(x)

  • 该块也是 3 个卷积层,每个卷积层使用 512 个 3x3 的滤波器,并且使用最大池化。

全连接层(Fully Connected Layers):

x = Flatten()(x) x = Dense(4096, activation='relu', name='fc1')(x)

x = Dense(4096, activation='relu', name='fc2')(x)

output_tensor = Dense(nb_classes, activation='softmax', name='predictions')(x)

  • Flatten() 将卷积层输出的多维张量展平成一维向量,准备传入全连接层。
  • Dense(4096, activation='relu') 创建 2 个全连接层,每个层有 4096 个神经元,使用 ReLU 激活函数。
  • Dense(nb_classes, activation='softmax') 输出层使用 softmax 激活函数,输出每个类别的概率,类别数为 nb_classes(即网络的输出类别数)。

创建并返回模型:

model = Model(input_tensor, output_tensor) return model

  • 使用 Model(input_tensor, output_tensor) 创建一个 Keras 模型,并返回这个模型对象。

创建 VGG16 模型:

model = VGG16(1000, (img_width, img_height, 3)) model.summary()

  • VGG16(1000, (img_width, img_height, 3)) 创建一个具有 1000 个类别的 VGG16 模型,输入图像的形状为 (img_width, img_height, 3),假设 img_widthimg_height 是预定义的图像宽度和高度。
  • model.summary() 打印出模型的概况,包括每层的输出形状和参数数量。

总结:这段代码实现了 VGG16 网络架构,包含了 5 个卷积块,每个块由多个卷积层和池化层组成。最后,网络经过两个全连接层输出类别概率。

四、编译

在准备对模型进行训练之前,还需要再对其进行一些设置。以下内容是在模型的编译步骤中添加的:

  • 损失函数(loss):用于衡量模型在训练期间的准确率。
  • 优化器(optimizer):决定模型如何根据其看到的数据和自身的损失函数进行更新。
  • 评价函数(metrics):用于监控训练和测试步骤。以下示例使用了准确率,即被正确分类的图像的比率。
python 复制代码
model.compile(optimizer="adam",
              loss     ='sparse_categorical_crossentropy',
              metrics  =['accuracy'])

五、训练模型

python 复制代码
from tqdm import tqdm
import tensorflow.keras.backend as K

epochs = 10
lr     = 1e-4

# 记录训练数据,方便后面的分析
history_train_loss     = []
history_train_accuracy = []
history_val_loss       = []
history_val_accuracy   = []

for epoch in range(epochs):
    train_total = len(train_ds)
    val_total   = len(val_ds)
    
    """
    total:预期的迭代数目
    ncols:控制进度条宽度
    mininterval:进度更新最小间隔,以秒为单位(默认值:0.1)
    """
    with tqdm(total=train_total, desc=f'Epoch {epoch + 1}/{epochs}',mininterval=1,ncols=100) as pbar:
        
        lr = lr*0.92
        K.set_value(model.optimizer.lr, lr)

        for image,label in train_ds:   
            """
            训练模型,简单理解train_on_batch就是:它是比model.fit()更高级的一个用法

            想详细了解 train_on_batch 的同学,
            可以看看我的这篇文章:https://www.yuque.com/mingtian-fkmxf/hv4lcq/ztt4gy
            """
            history = model.train_on_batch(image,label)

            train_loss     = history[0]
            train_accuracy = history[1]
            
            pbar.set_postfix({"loss": "%.4f"%train_loss,
                              "accuracy":"%.4f"%train_accuracy,
                              "lr": K.get_value(model.optimizer.lr)})
            pbar.update(1)
        history_train_loss.append(train_loss)
        history_train_accuracy.append(train_accuracy)
            
    print('开始验证!')
    
    with tqdm(total=val_total, desc=f'Epoch {epoch + 1}/{epochs}',mininterval=0.3,ncols=100) as pbar:

        for image,label in val_ds:      
            
            history = model.test_on_batch(image,label)
            
            val_loss     = history[0]
            val_accuracy = history[1]
            
            pbar.set_postfix({"loss": "%.4f"%val_loss,
                              "accuracy":"%.4f"%val_accuracy})
            pbar.update(1)
        history_val_loss.append(val_loss)
        history_val_accuracy.append(val_accuracy)
            
    print('结束验证!')
    print("验证loss为:%.4f"%val_loss)
    print("验证准确率为:%.4f"%val_accuracy)

这段代码是用来训练一个深度学习模型的,并且在每个训练周期(epoch)中进行训练和验证。具体解释如下:

1. 导入模块

from tqdm import tqdm

import tensorflow.keras.backend as K

  • tqdm: 用于显示进度条,便于监控训练和验证过程的进度。
  • tensorflow.keras.backend: 提供了与Keras后端进行交互的功能,这里用来动态调整学习率。

2. 初始化训练参数和记录列表

epochs = 10

lr = 1e-4

history_train_loss = []

history_train_accuracy = []

history_val_loss = []

history_val_accuracy = []

  • epochs = 10: 设置训练周期(epoch)的数量为10。
  • lr = 1e-4: 设置初始学习率为 0.0001
  • history_train_loss, history_train_accuracy, history_val_loss, history_val_accuracy: 用于记录每个epoch中的训练和验证损失及准确度。

3. 训练循环

for epoch in range(epochs):

train_total = len(train_ds)

val_total = len(val_ds)

  • train_totalval_total 分别获取训练集和验证集的大小。

4. 训练过程中的进度条

with tqdm(total=train_total, desc=f'Epoch {epoch + 1}/{epochs}', mininterval=1, ncols=100) as pbar:

  • 使用 tqdm 创建一个进度条显示训练进度。
    • total=train_total:进度条的总长度等于训练数据集的大小。
    • desc:显示当前epoch的描述信息。
    • mininterval=1:进度更新的最小间隔为1秒。
    • ncols=100:进度条的宽度设置为100个字符。

5. 每个epoch的训练步骤

lr = lr*0.92

K.set_value(model.optimizer.lr, lr)

  • 每经过一个epoch,学习率会乘以 0.92 来实现学习率衰减,从而逐渐降低学习率,帮助模型收敛。
  • 使用 K.set_value 来更新模型优化器的学习率。

6. 训练每个batch

for image, label in train_ds:

history = model.train_on_batch(image, label)

  • 这里使用了 train_on_batch(),它是一种更细粒度的训练方式,相较于 model.fit()train_on_batch() 在每个批次上训练,而 model.fit() 是在整个数据集上进行训练。
  • history = model.train_on_batch(image, label):对当前的训练批次进行训练,返回训练的损失值和准确率。

7. 更新进度条信息

pbar.set_postfix({"loss": "%.4f"%train_loss, "accuracy":"%.4f"%train_accuracy, "lr": K.get_value(model.optimizer.lr)})

  • 使用 set_postfix 方法动态更新进度条显示的信息,包括当前的训练损失、训练准确率和当前的学习率。

8. 记录训练结果

history_train_loss.append(train_loss) history_train_accuracy.append(train_accuracy)

  • 将当前epoch的训练损失和准确率添加到历史记录列表中,方便后续分析。

9. 验证过程

with tqdm(total=val_total, desc=f'Epoch {epoch + 1}/{epochs}', mininterval=0.3, ncols=100) as pbar:

  • 使用 tqdm 创建一个进度条显示验证过程的进度,参数与训练过程类似。

10. 每个batch的验证

for image, label in val_ds: history = model.test_on_batch(image, label)

  • 使用 test_on_batch() 进行验证,与 train_on_batch() 类似,只是它用于验证阶段。

11. 更新验证进度条信息

pbar.set_postfix({"loss": "%.4f"%val_loss, "accuracy":"%.4f"%val_accuracy})

  • 同样使用 set_postfix 更新验证过程中的损失和准确率信息。

12. 记录验证结果

history_val_loss.append(val_loss)

history_val_accuracy.append(val_accuracy)

  • 将当前epoch的验证损失和验证准确率记录到历史列表中。

13. 打印每个epoch结束时的结果

print("验证loss为:%.4f"%val_loss) print("验证准确率为:%.4f"%val_accuracy)

  • 在每个epoch结束后,打印出当前epoch的验证损失和准确率。

总结:

该代码是一个包含训练和验证过程的深度学习模型训练脚本。它使用 train_on_batch()test_on_batch() 方法逐步处理每个批次,且在训练过程中动态调整学习率,使用 tqdm 显示训练和验证的进度条,并记录每个epoch的损失和准确率。

相关推荐
sp_fyf_202426 分钟前
【大语言模型】ACL2024论文-19 SportsMetrics: 融合文本和数值数据以理解大型语言模型中的信息融合
人工智能·深度学习·神经网络·机器学习·语言模型·自然语言处理
CoderIsArt29 分钟前
基于 BP 神经网络整定的 PID 控制
人工智能·深度学习·神经网络
编程修仙38 分钟前
Collections工具类
linux·windows·python
开源社43 分钟前
一场开源视角的AI会议即将在南京举办
人工智能·开源
FreeIPCC43 分钟前
谈一下开源生态对 AI人工智能大模型的促进作用
大数据·人工智能·机器人·开源
芝麻团坚果1 小时前
对subprocess启动的子进程使用VSCode python debugger
linux·ide·python·subprocess·vscode debugger
机器之心1 小时前
全球十亿级轨迹点驱动,首个轨迹基础大模型来了
人工智能·后端
z千鑫1 小时前
【人工智能】PyTorch、TensorFlow 和 Keras 全面解析与对比:深度学习框架的终极指南
人工智能·pytorch·深度学习·aigc·tensorflow·keras·codemoss
EterNity_TiMe_1 小时前
【论文复现】神经网络的公式推导与代码实现
人工智能·python·深度学习·神经网络·数据分析·特征分析
Stara05111 小时前
Git推送+拉去+uwsgi+Nginx服务器部署项目
git·python·mysql·nginx·gitee·github·uwsgi