深度学习 Day8——T8猫狗识别


前言

本文将采用CNN实现猫狗识别。本文放弃了以往的model.fit()训练方法,改用model.train_on_batch方法,引入了进度条的显示方式,更加方便我们及时查看模型训练过程中的情况,可以及时打印各项指标。简单讲述实现代码与执行结果,并浅谈涉及知识点。
关键字:数据归一化详解,batchsize对网络的影响,model.train_on_batch详解,Python tqdm 进度条库的基本使用,tf.keras.backend.set_value函数。

一、我的环境

  • 电脑系统:Windows 11
  • 语言环境:python 3.8.6
  • 编译器:pycharm2020.2.3
  • 深度学习环境:TensorFlow 2.10.1
  • 显卡:NVIDIA GeForce RTX 4070

二、代码实现与执行结果

1.引入库

from PIL import Image
import numpy as np
from pathlib import Path
import tensorflow as tf
from tensorflow.keras import datasets, layers, models, regularizers
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras import layers, models, Input
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout, BatchNormalization
from tqdm import tqdm
import tensorflow.keras.backend as K
import matplotlib.pyplot as plt
import warnings

warnings.filterwarnings('ignore')  # 忽略一些warning内容,无需打印

2.设置GPU(如果使用的是CPU可以忽略这步)

'''前期工作-设置GPU(如果使用的是CPU可以忽略这步)'''
# 检查GPU是否可用
print(tf.test.is_built_with_cuda())
gpus = tf.config.list_physical_devices("GPU")
print(gpus)
if gpus:
    gpu0 = gpus[0]  # 如果有多个GPU,仅使用第0个GPU
    tf.config.experimental.set_memory_growth(gpu0, True)  # 设置GPU显存用量按需使用
    tf.config.set_visible_devices([gpu0], "GPU")

执行结果

True
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]

3.导入数据

'''前期工作-导入数据'''
data_dir = r"D:\DeepLearning\data\CatDog"
data_dir = Path(data_dir)

4.查看数据

'''前期工作-查看数据'''
image_count = len(list(data_dir.glob('*/*.png')))
print("图片总数为:", image_count)
image_list = list(data_dir.glob('cat/*.png'))
image = Image.open(str(image_list[1]))
# 查看图像实例的属性
print(image.format, image.size, image.mode)
plt.imshow(image)
plt.show()

执行结果:

图片总数为: 3400
JPEG (512, 512) RGB

在这里插入图片描述

5.加载数据

 '''数据预处理-加载数据'''
batch_size = 32
img_height = 224
img_width = 224
"""
关于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)
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)
# 我们可以通过class_names输出数据集的标签。标签将按字母顺序对应于目录名称。
class_names = train_ds.class_names
print(class_names)

运行结果:

Found 3400 files belonging to 2 classes.
Using 2720 files for training.
Found 3400 files belonging to 2 classes.
Using 680 files for validation.
['cat', 'dog']

6.再次检查数据

'''数据预处理-再次检查数据'''
# Image_batch是形状的张量(32,180,180,3)。这是一批形状180x180x3的32张图片(最后一维指的是彩色通道RGB)。
# Label_batch是形状(32,)的张量,这些标签对应32张图片
for image_batch, labels_batch in train_ds:
    print(image_batch.shape)
    print(labels_batch.shape)
    break

运行结果

(32, 224, 224, 3)
(32,)

7.配置数据集

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,这个错误是由于版本问题引起的

8.可视化数据

'''数据预处理-可视化数据'''
plt.figure(figsize=(18, 3))
for images, labels in train_ds.take(2):
    for i in range(8):
        ax = plt.subplot(1, 8, i + 1)
        plt.imshow(images[i].numpy().astype("uint8"))
        plt.title(class_names[labels[i]], fontsize=40)
        plt.axis("off")
# 显示图片
plt.show()

在这里插入图片描述

9.构建CNN网络模型

'''构建CNN网络'''
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()

网络结构结果如下:

Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_1 (InputLayer)        [(None, 224, 224, 3)]     0         
                                                                 
 block1_conv1 (Conv2D)       (None, 224, 224, 64)      1792      
                                                                 
 block1_conv2 (Conv2D)       (None, 224, 224, 64)      36928     
                                                                 
 block1_pool (MaxPooling2D)  (None, 112, 112, 64)      0         
                                                                 
 block2_conv1 (Conv2D)       (None, 112, 112, 128)     73856     
                                                                 
 block2_conv2 (Conv2D)       (None, 112, 112, 128)     147584    
                                                                 
 block2_pool (MaxPooling2D)  (None, 56, 56, 128)       0         
                                                                 
 block3_conv1 (Conv2D)       (None, 56, 56, 256)       295168    
                                                                 
 block3_conv2 (Conv2D)       (None, 56, 56, 256)       590080    
                                                                 
 block3_conv3 (Conv2D)       (None, 56, 56, 256)       590080    
                                                                 
 block3_pool (MaxPooling2D)  (None, 28, 28, 256)       0         
                                                                 
 block4_conv1 (Conv2D)       (None, 28, 28, 512)       1180160   
                                                                 
 block4_conv2 (Conv2D)       (None, 28, 28, 512)       2359808   
                                                                 
 block4_conv3 (Conv2D)       (None, 28, 28, 512)       2359808   
                                                                 
 block4_pool (MaxPooling2D)  (None, 14, 14, 512)       0         
                                                                 
 block5_conv1 (Conv2D)       (None, 14, 14, 512)       2359808   
                                                                 
 block5_conv2 (Conv2D)       (None, 14, 14, 512)       2359808   
                                                                 
 block5_conv3 (Conv2D)       (None, 14, 14, 512)       2359808   
                                                                 
 block5_pool (MaxPooling2D)  (None, 7, 7, 512)         0         
                                                                 
 flatten (Flatten)           (None, 25088)             0         
                                                                 
 fc1 (Dense)                 (None, 4096)              102764544 
                                                                 
 fc2 (Dense)                 (None, 4096)              16781312  
                                                                 
 predictions (Dense)         (None, 1000)              4097000   
                                                                 
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0

10.编译模型

'''编译模型'''
model.compile(optimizer="adam",
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

11.训练模型

'''训练模型'''
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=120) as pbar:
        # lr = lr * 0.92
        K.set_value(model.optimizer.lr, lr)
        for image, label in train_ds:
            """
            训练模型,简单理解train_on_batch就是:它是比model.fit()更高级的一个用法
            """
            history = model.train_on_batch(image, label)
            train_loss = history[0]
            train_accuracy = history[1]
            pbar.set_postfix({"train_loss": "%.4f" % train_loss,
                              "train_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)

    with tqdm(total=val_total, desc=f'Epoch {epoch + 1}/{epochs}', mininterval=0.3, ncols=120) 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({"val_loss": "%.4f" % val_loss,
                              "val_accuracy": "%.4f" % val_accuracy})
            pbar.update(1)
        history_val_loss.append(val_loss)
        history_val_accuracy.append(val_accuracy)

训练记录如下:

Epoch 1/10: 100%|███████████████████| 85/85 [00:27<00:00,  3.08it/s, train_loss=0.5849, train_accuracy=0.7500, lr=1e-04]
Epoch 1/10: 100%|█████████████████████████████████| 22/22 [00:02<00:00,  8.63it/s, val_loss=0.6252, val_accuracy=0.7500]
Epoch 2/10: 100%|███████████████████| 85/85 [00:17<00:00,  4.93it/s, train_loss=0.1174, train_accuracy=1.0000, lr=1e-04]
Epoch 2/10: 100%|█████████████████████████████████| 22/22 [00:01<00:00, 14.04it/s, val_loss=0.4043, val_accuracy=0.7500]
Epoch 3/10: 100%|███████████████████| 85/85 [00:17<00:00,  4.91it/s, train_loss=0.3907, train_accuracy=0.8438, lr=1e-04]
Epoch 3/10: 100%|█████████████████████████████████| 22/22 [00:01<00:00, 13.96it/s, val_loss=0.0917, val_accuracy=1.0000]
Epoch 4/10: 100%|███████████████████| 85/85 [00:17<00:00,  4.92it/s, train_loss=0.1055, train_accuracy=0.9062, lr=1e-04]
Epoch 4/10: 100%|█████████████████████████████████| 22/22 [00:01<00:00, 14.00it/s, val_loss=0.0760, val_accuracy=1.0000]
Epoch 5/10: 100%|███████████████████| 85/85 [00:17<00:00,  4.89it/s, train_loss=0.0137, train_accuracy=1.0000, lr=1e-04]
Epoch 5/10: 100%|█████████████████████████████████| 22/22 [00:01<00:00, 13.65it/s, val_loss=0.1191, val_accuracy=1.0000]
Epoch 6/10: 100%|███████████████████| 85/85 [00:17<00:00,  4.88it/s, train_loss=0.0203, train_accuracy=1.0000, lr=1e-04]
Epoch 6/10: 100%|█████████████████████████████████| 22/22 [00:01<00:00, 13.68it/s, val_loss=0.1208, val_accuracy=1.0000]
Epoch 7/10: 100%|███████████████████| 85/85 [00:17<00:00,  4.82it/s, train_loss=0.0926, train_accuracy=0.9688, lr=1e-04]
Epoch 7/10: 100%|█████████████████████████████████| 22/22 [00:01<00:00, 13.83it/s, val_loss=0.0197, val_accuracy=1.0000]
Epoch 8/10: 100%|███████████████████| 85/85 [00:17<00:00,  4.88it/s, train_loss=0.0251, train_accuracy=1.0000, lr=1e-04]
Epoch 8/10: 100%|█████████████████████████████████| 22/22 [00:01<00:00, 13.80it/s, val_loss=0.0077, val_accuracy=1.0000]
Epoch 9/10: 100%|███████████████████| 85/85 [00:17<00:00,  4.88it/s, train_loss=0.0076, train_accuracy=1.0000, lr=1e-04]
Epoch 9/10: 100%|█████████████████████████████████| 22/22 [00:01<00:00, 13.90it/s, val_loss=0.0320, val_accuracy=1.0000]
Epoch 10/10: 100%|██████████████████| 85/85 [00:17<00:00,  4.89it/s, train_loss=0.0158, train_accuracy=1.0000, lr=1e-04]
Epoch 10/10: 100%|████████████████████████████████| 22/22 [00:01<00:00, 13.84it/s, val_loss=0.1894, val_accuracy=0.8750]


12.模型评估

'''模型评估'''
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs_range = range(epochs)
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

执行结果
在这里插入图片描述

13.指定图片进行预测

'''指定图片进行预测'''
plt.figure(figsize=(18, 3))  # 图形的宽为18高为3
plt.suptitle("预测结果展示")
for images, labels in val_ds.take(1):
    for i in range(8):
        ax = plt.subplot(1, 8, i + 1)
        # 显示图片
        plt.imshow(images[i].numpy())
        # 需要给图片增加一个维度
        img_array = tf.expand_dims(images[i], 0)
        # 使用模型预测图片中的人物
        predictions = model.predict(img_array)
        plt.title(class_names[np.argmax(predictions)], fontsize=40)
        plt.axis("off")
# 显示图片
plt.show()

执行结果

1/1 [==============================] - 0s 412ms/step
1/1 [==============================] - 0s 13ms/step
1/1 [==============================] - 0s 13ms/step
1/1 [==============================] - 0s 13ms/step
1/1 [==============================] - 0s 14ms/step
1/1 [==============================] - 0s 13ms/step
1/1 [==============================] - 0s 14ms/step
1/1 [==============================] - 0s 13ms/step

在这里插入图片描述

三、知识点详解

1.数据归一化讲解

1.1归一化方式一

# 归一化处理
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_dataset.map(map_func, num_parallel_calls=None, deterministic=None)
作用
对数据集进行变换(transformations)。该函数会对数据集中每一个元素执行map_func处理,并返回处理后的数据集。
参数
num_parallel_calls:该参数表示要异步并行处理数据的数量,可以为tf.int64和tf.Tensor类型数据。如果不指定,数据将按顺序处理。如果指定为tf.data.AUTOTUNE,则会根据可用的CPU动态设置并行处理数据的数量。
deterministic:布尔型。当num_parallel_calls确定时,deterministic将会控制数据转换过程中处理数据的顺序。具体参考。一般不指定(默认参数)。
参考链接:tf.data处理数据全过程——代码详解

1.2归一化方式二

normalization_layer = layers.experimental.preprocessing.Rescaling(1./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))

layers.experimental.preprocessing.Rescaling(1./255)表示缩放层将像素值标准化为[0,1]
隐函数lanbda及map函数讲解

###隐函数lanbda及map函数的应用实例
'''
lambda 参数1,参数2,参数3,...:表达式
'''
f = lambda x,y :x+y
print(f(1,2))    ###输出3
'''
map()接收一个函数f 和 一个Iterator(可迭代对象),并通过把函数f依次作用在Iterator的每一个元素上,并把结果做为新的Iterator返回
'''
a = [1,2,3]
b = map(lambda x:x*x ,a)
print(list(b))    ###输出[1,4,9]

lambda的两个参数分别代表(image_batch, labels_batch ),像素值进行归一化,标签值保持不变。

2.batchsize对网络的影响

2.1.定义

简单点说,就是我们一次要将多少个数据扔进模型去训练,这个值介于1和训练样本总个数之间。

2.2.若batchsize太小的缺点

①耗时长,训练效率低。
假设batchsize=1,每次用一个数据进行训练,如果数据总量很多时(假设有十万条数据),就需要向模型投十万次数据,完整训练完一遍数据需要很长的时问,训练效率很低;
②训练数据就会非常难收敛,从而导致欠拟合。
假设batchsize=1,每次用一个数据进行训练,则由于个体的差异性或者异常值的影响,模型的参数变化也会很大,每一层的梯度都具有很高的随机性,而且需要耗费了大量的时间,从而导致模型非常难收敛。

2.3.随着batchsize逐渐增大的优缺点

1.大的batchsize减少训练时间的同时所需内存容量增加
①大的batchsize减少训练时间
这是肯定的,同样的epoch数目,大的batchsize需要的batch数目减少了,所以处理速度变快,可以减少训练时间;
②大的batchsize所需内存容量增加
但是如果该值太大,假设batchsize=100000,一次将十万条数据扔进模型,很可能会造成内存溢出,而无法正常进行训练。

2.大的batchsize在提高稳定性的同时可能导致模型泛化能力下降
①大的batch size梯度的计算更加稳定
因为模型训练曲线会更加平滑。在微调的时候,大的batchsize可能会取得更好的结果。因为在一定范围内,一般来说batchsize越大,其确定的下降方向越准,引起训练震荡越小。batchsize增大到一定程度,其确定的下降方向已经基本不再变化。
②大的batchsize可能导致模型泛化能力下降
在一定范围内,增加batchsize有助于收敛的稳定性,但是随着batchsize的增加,模型的泛化性能会下降。若batchsize设为最大(样本总个数),则每次更新参数都是相同的样本,下降方向基本确定,这会导致模型的泛化性能下降。

2.4.如何平衡batchsize的大小

batchsize太大或者太小都不好。所以 batch size 的值越大,梯度也就越稳定,而 batch size 越小,梯度具有越高的随机性,但如果 batch size 太大,对于内存的需求就更高,同时也不利于网络跳出局部极小点。所以,我们需要设置一个合适的batchsize值,在训练速度和内存容量之间寻找到最佳的平衡点。
①一般在Batchsize增加的同时,我们需要对所有样本的训练次数(也就是后面要讲的epoch)增加(以增加训练次数达到更好的效果)这同样会导致耗时增加,因此需要寻找一个合适的batchsize值,在模型总体效率和内存容量之间做到最好的平衡。
②由于上述两种因素的矛盾,batchsize增大到某个时候,达到时间上的最优。由于最终收敛精度会陷入不同的局部极值,因此batchsize增大到某些时候,达到最终收敛精度上的最优。
参考链接:batchsize太小的缺点&随着batchsize逐渐增大的优缺点&如何平衡batchsize的大小

3.model.train_on_batch()详解

大部分使用 keras 的同学使用 fit() 或者 fit_generator() 进行模型训练, 这两个 api 对于刚接触深度学习的同学非常友好和方便,但是由于其是非常深度的封装,对于希望自定义训练过程的同学就显得不是那么方便(从 torch 转 keras 的同学可能更喜欢自定义训练过程),而且,对于 GAN 这种需要分步进行训练的模型,也无法直接使用 fit 或者 fit_generator 直接训练的。因此,keras 提供了 train_on_batch 这个 api,对一个 mini-batch 的数据进行梯度更新。

总结优点如下:

  • 更精细自定义训练过程,更精准的收集 loss 和 metrics
  • 分步训练模型-GAN的实现
  • 多GPU训练保存模型更加方便
  • 更多样的数据加载方式,结合 torch dataloader 的使用

函数原型

y_pred = Model.train_on_batch(
    x,
    y=None,
    sample_weight=None,
    class_weight=None,
    reset_metrics=True,
    return_dict=False,
)

输入参数:

  • x:模型输入,单输入就是一个 numpy 数组, 多输入就是 numpy 数组的列表 y:标签,单输出模型就是一个 numpy 数组, 多输出模型就是 numpy 数组列表 sample_weight:mini-batch 中每个样本对应的权重,形状为
  • (batch_size) class_weight:类别权重,作用于损失函数,为各个类别的损失添加权重,主要用于类别不平衡的情况, 形状为
  • (num_classes) reset_metrics:默认True,返回的metrics只针对这个mini-batch, 如果False,metrics 会跨批次累积
  • return_dict:默认 False, y_pred 为一个列表,如果 True 则 y_pred 是一个字典

输出:

  • 单输出模型,且只有loss,没有metrics, 此时 y_pred 为一个标量,代表这个 mini-batch的 loss, 例如下面的例子
model = keras.models.Model(inputs=inputs,
outputs=outputs) model.compile(Adam, loss=['binary_crossentropy'])
y_pred = model.train_on_batch(x=image,y=label) # y_pred is a scalar
# 输出y_pred 为标量 
  • 单输出模型,既有loss,也有metrics, 此时 y_pred 为一个列表,代表这个 mini-batch 的 loss 和 metrics, 列表长度为 1+len(metrics), 例如下面的例子
model = keras.models.Model(inputs=inputs, outputs=outputs) 
model.compile(Adam, loss=['binary_crossentropy'], metrics=['accuracy']) 
y_pred = model.train_on_batch(x=image,y=label) # len(y_pred) == 2
# y_pred 为长度为2的列表, y_pred[0]为loss, y_pred[1]为accuracy 
  • 多输出模型,既有loss,也有metrics, 此时 y_pred 为一个列表,列表长度为 1+len(loss)+len(metrics), 例如下面的例子
model = keras.models.Model(inputs=inputs, outputs=[output1, output2]) model.compile(Adam, loss=['binary_crossentropy', 'binary_crossentropy'], 
 metrics=['accuracy', 'accuracy']) y_pred = model.train_on_batch(x=image,y=label) # len(y_pred) == 5  
 #y_pred[0]为总loss(按照loss_weights加权), y_pred[1]为第一个输出的loss,y_pred[2]为第二个输出的loss
 # y_pred[3]为第一个accuracy,  y_pred[4]为第二个accuracy

参考链接:keras train_on_batch详解

4.Python tqdm 进度条库的基本使用

tqdm模块是python进度条库,tqdm加载一个可迭代对象,并以进度条的形式实时显示该可迭代对象的加载进度。

4.1 tqdm的基本使用

from tqdm import tqdm
import time

# range(100)是加载的迭代器,desc是进度条的描述文字。
for i in tqdm(range(100), desc='Processing'):
    time.sleep(0.05)

# output:
Processing: 100%|██████████| 100/100 [00:06<00:00, 15.81it/s]

注意:
必须使用 from tqdm import tqdm,而不能使用 import tqdm

4.2 进度条的迭代和进度条名字的设置

from tqdm import tqdm
import time

data=list(range(1000))
data_bar=tqdm(data)   # 将data设置为进度条
for i in data_bar:    # 进度条可以迭代
    data_bar.set_description("Processing "+str(i))   # 在不同时刻设置进度条的名字
    time.sleep(0.05)


# output:
Processing 202:  20%|██        | 202/1000 [00:12<00:50, 15.93it/s]

上面的程序中,我们设置在0-1000内不断将进度条名字设置为正在加载的数字。可以看到进度条到20%的时候,加载到了202

4.3 tqdm() 的参数与方法

tqdm() 的源码为:

def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None,
                 ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None,
                 ascii=None, disable=False, unit='it', unit_scale=False,
                 dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0,
                 position=None, postfix=None, unit_divisor=1000, write_bytes=None,
                 lock_args=None, nrows=None, colour=None, delay=0, gui=False,
                 **kwargs):

其中主要参数的含义为:

  • iterable:可迭代对象,即要迭代的对象,在手动更新时不需要进行设置。
  • desc:进度条的描述文字,默认为 None。
  • total:总的迭代次数,用于计算进度百分比,默认为 None。
  • leave:控制进度条完成后是否保留,默认为 False。
  • file:指定输出进度条的文件,默认为 sys.stderr。
  • ncols:进度条的总宽度,即显示的字符数,默认是根据环境自动调节长度, 如果设置为0, 就没有进度条, 只有输出的信息默认为 None(自动调整宽度)。
  • mininterval:最小更新间隔时间(秒),用于控制进度条的更新频率,默认为 0.1。
  • maxinterval:最大更新间隔时间(秒),用于控制进度条的更新频率,默认为 10。
  • miniters:最小更新次数,用于控制进度条的更新频率,默认为 1。
    – ascii:控制是否使用 ASCII 字符显示进度条,默认为 False。
  • disable:控制是否禁用进度条,默认为 False。
  • unit:进度条的单位名称,默认为 “it” ,例如: 100 it/s, 处理照片的话设置为’img’ ,则为 100 img/s。
  • unit_divisor:进度条的单位除数,默认为 1000。
  • unit_scale:控制是否自动缩放单位,例如 100000 it/s >> 100k it/s,默认为 False。
  • dynamic_ncols:控制是否动态调整进度条宽度以适应终端大小,默认为 False。
  • smoothing:控制平滑进度条的更新,默认为 False。
  • bar_format:自定义进度条的格式,默认为 “{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt}”。
  • initial:进度条的初始值,默认为0。
  • position:进度条的位置,默认为None。
  • disable:是否禁用进度条,默认为False。
  • gui:是否在GUI环境中运行,默认为False。
  • colour(str): 进度条的颜色
    方法
tqdm.write(s, file=None, end='\n', nolock=False)
  • s:要写入的字符串。
  • file:要写入的文件,默认为sys.stderr。
  • end:字符串的结尾,默认为’\n’。
  • nolock:是否使用锁来保护写入操作,默认为False。
tqdm.set_lock(lock)
  • lock:要使用的锁对象。
tqdm.set_verbosity(verbosity)
  • verbosity:要设置的详细程度,可以是0、1或2。
tqdm.set_mininterval(mininterval)
  • mininterval:要设置的最小时间间隔。
tqdm.set_maxinterval(maxinterval)
  • maxinterval:要设置的最大时间间隔。
tqdm.set_postfix(*args, **kwargs)
  • args:要添加到进度条后缀的参数。
  • kwargs:要添加到进度条后缀的关键字参数。
tqdm.set_description(desc=None, refresh=True)
  • desc:要设置的进度条描述。
  • refresh:是否刷新进度条,默认为True。
tqdm.set_total(total=None)
  • total:要设置的迭代器的总长度。
tqdm.update(n=1)
  • n:要更新的进度条的值,默认为1。
tqdm.close()
  • 关闭进度条。

4.4 深度学习网络训练中如何有效使用tqdm

比如数据集很大时,我们想实时知道已经训练了多少数据,就可以把data_loader设置为进度条对象,如下面的代码所示:

from tqdm import tqdm
from torch.utils.data import DataLoader

train_loader=DataLoader(dataset,shuffle=True,batch_size=16)   # 假设已知dataset
epochs=10
for epoch in range(epochs):
    # 此处省略若干步骤
    train_bar = tqdm(train_loader)   # 实时显示加载了多少数据
    for step, data in enumerate(train_bar):
        # 此处省略若干步骤
        train_bar.desc = f"train epoch [{epoch+1}/{epochs}]   loss= {loss:.3f}"


# output:
train epoch [1/10]   loss= 1.462:  11%|| 23/207 [00:37<05:00,  1.63s/it]

4.5 tqdm中手工操作(Manual)

使用with语句手动控制tqdm的更新,可以根据具体任务来更新进度条的进度。

with tqdm(total=100) as pbar:
    for i in range(10):
        sleep(0.1)
        pbar.update(10)

当然with这个语句想必大家都知道(想想使用with打开文件就知道了),也可以不使用with进行,则有如下操作:

pbar = tqdm(total=100)
for i in range(10):
    sleep(0.1)
    pbar.update(10)
pbar.close()

参考链接:Python tqdm 进度条库的基本使用

5.tf.keras.backend.set_value函数

tf.keras.backend.set_value(
    x,
    value
)

定义在:tensorflow/python/keras/backend.py。
从Numpy数组设置变量的值。
参数:

总结

通过本次的学习,了解了tqdm进度条的使用,也了解了通过Model.train_on_batch实现自定义训练模型方法。针对文章设定的bug任务进行查找,发现原文batchsize设置的过小,导致出现模型不收敛的情况,并分析了batchsize在整个网络中的作用。

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值