T8:猫狗识别

news2024/12/24 9:01:34

T8周:猫狗识别

      • **一、前期工作**
        • 1.设置GPU,导入库
        • 2.导入数据
        • 3.查看数据
      • **二、数据预处理**
        • 1.加载数据
        • 2.可视化数据
        • 3.配置数据集
      • **三、构建CNN网络模型**
      • **四、编译模型**
      • **五、训练模型**
      • **六、模型评估**
      • **七、预测**
      • 八、总结
        • 1、[`train_on_batch` 和 `test_on_batch`](https://keras.io/2.16/api/models/model_training_apis/)
        • 2、数据集配置
        • 3、tqdm
          • **tqdm的参数**
          • tqdm相关参数与方法
          • 使用示例:
        • 4、`keras.backend.set_value()`和`backend.get_value()`
        • 5、代码问题
          • **本期代码存在的bug**
          • **个人遇到的报错**

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊
  • 难度:夯实基础⭐⭐

🍺 要求:

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

🍻 拔高(可选):

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

🔎 探索(难度有点大)

  1. 修改代码,处理BUG

⛽我的环境:

  • Python3.10.12
  • Tensorflow 2.17.0
  • Jupyter notebook

⛽本文参考学习的链接:

  • 深度学习-第T8周——猫狗识别
  • 深度学习 Day8——T8猫狗识别
  • keras train_on_batch详解
  • Tensorflow中的数据对象Dataset.shuffle()、repeat()、batch() 等用法
  • 第T9周:猫狗识别2
  • 本文遇到的tf.function retracing引发的报警可能的修改方法

一、前期工作

1.设置GPU,导入库
#os提供了一些与操作系统交互的功能,比如文件和目录操作
import os
#提供图像处理的功能,包括打开和显示、保存、裁剪等
import PIL
from PIL import Image
#pathlib提供了一个面向对象的接口来处理文件系统路径。路径被表示为Path对象,可以调用方法来进行各种文件和目录操作。
import pathlib

#用于绘制图形和可视化数据
import tensorflow as tf
import matplotlib.pyplot as plt
#用于数值计算的库,提供支持多维数组和矩阵运算
import numpy as np
#keras作为高层神经网络API,已被集成进tensorflow,使得训练更方便简单
from tensorflow import keras
#layers提供了神经网络的基本构建块,比如全连接层、卷积层、池化层等
#提供了构建和训练神经网络模型的功能,包括顺序模型(Sequential)和函数式模型(Functional API)
from tensorflow.keras import layers, models
#导入两个重要的回调函数:前者用于训练期间保存模型最佳版本;后者监测到模型性能不再提升时提前停止训练,避免过拟合
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
# # 获取所有可用的GPU设备列表,储存在变量gpus中
# gpus = tf.config.list_physical_devices("GPU")

# # 如果有GPU,即列表不为空
# if gpus:
#   # 获取第一个 GPU 设备
#   gpu0 = gpus[0]
#   # 设置 GPU 内存增长策略。开启这个选项可以让tf按需分配gpu内存,而不是一次性分配所有可用内存。
#   tf.config.experimental.set_memory_growth(gpu0, True)
#   #设置tf只使用指定的gpu(gpu[0])
#   tf.config.set_visible_devices([gpu0],"GPU")

# gpus
2.导入数据
data_dir = "../data/8"
data_dir = pathlib.Path(data_dir)
3.查看数据
import matplotlib.pyplot as plt

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

print("图片总数为:",image_count)
图片总数为: 3400
ex = list(data_dir.glob("dog/*.jpg"))
image=PIL.Image.open(str(ex[3]))
#查看图像属性
print(image.format, image.size,image.mode)
plt.axis("off")
plt.imshow(image)
plt.show()
JPEG (512, 512) RGB

在这里插入图片描述

二、数据预处理

1.加载数据
#设置批量大小,即每次训练模型时输入图像数量
#每次训练迭代时,模型需处理8张图像
batch_size = 8
#图像的高度,加载图像数据时,将所有的图像调整为相同的高度
img_height = 224
#图像的宽度,加载图像数据时,将所有的图像调整为相同的宽度
img_width = 224
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.
"""
关于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 = train_ds.class_names
# 可以通过class_names输出数据集的标签。标签将按字母顺序对应于目录名称
class_names
['cat', 'dog']
# #数据增强---参考博客:https://blog.csdn.net/afive54/article/details/135004174

# def augment_images(image, label):
#     image = tf.image.random_flip_up_down(image)  # 随机水平翻转
#     image = tf.image.random_flip_left_right(image)
#     image = tf.image.random_contrast(image, lower=0.1, upper=1.2)  # 随机对比度
#     image = tf.image.random_brightness(image, max_delta=0.2)  # 随机亮度
#     image = tf.image.random_saturation(image, lower=0.1, upper=1.2)  # 随机饱和度
#     #noise = tf.random.normal(tf.shape(image), mean=0.0, stddev=0.1)
#     #image = tf.clip_by_value(image, 0.0, 0.5)  # 添加高斯噪声并将像素值限制在0到1之间
#     return image, label
# # 对训练集数据进行增强
# augmented_tr_ds = tr_ds.map(augment_images)

for image_batch, labels_batch in train_ds:
    print(image_batch.shape)
    print(labels_batch.shape)
    break
(8, 224, 224, 3)
(8,)
2.可视化数据
plt.figure(figsize=(10, 4))  # 图形的宽为10高为4

for images, labels in train_ds.take(1):
  for i in range(8):
    ax = plt.subplot(2, 4, i + 1)
    plt.imshow(images[i].numpy().astype("uint8"))
    plt.title(class_names[labels[i]])
    plt.axis("off")

在这里插入图片描述

3.配置数据集
#自动调整数据管道性能
AUTOTUNE = tf.data.AUTOTUNE
# 使用 tf.data.AUTOTUNE 具体的好处包括:
#自动调整并行度:自动决定并行处理数据的最佳线程数,以最大化数据吞吐量。
#减少等待时间:通过优化数据加载和预处理,减少模型训练时等待数据的时间。
#提升性能:自动优化数据管道的各个环节,使整个训练过程更高效。
#简化代码:不需要手动调整参数,代码更简洁且易于维护。

#使用cache()方法将训练集缓存到内存中,这样加快数据加载速度
#当多次迭代训练数据时,可以重复使用已经加载到内存的数据而不必重新从磁盘加载
#使用shuffle()对训练数据集进行洗牌操作,打乱数据集中的样本顺序
#参数1000指缓冲区大小,即每次从数据集中随机选择的样本数量
#prefetch()预取数据,节约在训练过程中数据加载时间
def preprocess_image(image,label):
    return (image/255.0,label) #图像统一处理归一化

train_ds = train_ds.map(preprocess_image, num_parallel_calls=AUTOTUNE)# 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)

三、构建CNN网络模型

VGGNet (Visual Geometry Group Network):

创新:VGGNet的创新在于采用了相对简单的卷积层堆叠的结构,其中使用了多个小卷积核(3*3)来替代较大的卷积核。这种结构使网络更深,同时参数共享更多,有助于提取丰富的特征。

  • 优点:
  • 相对简单而易于理解的网络结构。
  • 良好的性能在图像分类任务中得到了验证。
  • 网络结构可提取更丰富的特征信息
  • 缺点:
  • 参数量较大,网络结构比较深,需要消耗大量计算资源和时间来训练。
  • 网络结构比较复杂,容易出现梯度消失或爆炸等问题
  • 相对于一些后续的模型,不够高效。

以下为轻量化并引入dropout层和BN层后的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

drop_rate = 0.2
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 = Dropout(drop_rate)(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 = Dropout(drop_rate)(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 = Dropout(drop_rate)(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 = Dropout(drop_rate)(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 = Dropout(drop_rate)(x)
    x = MaxPooling2D((2,2), strides=(2,2), name = 'block5_pool')(x)
    # full connection
    x = Flatten()(x)
    x = Dense(2048, activation='relu',  name='fc1')(x) #修改为2048
    x = Dropout(drop_rate)(x)
    x = BatchNormalization()(x)
    x = Dense(256, activation='relu', name='fc2')(x)  #修改为256
    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))
#bug应该是这里的1000,这里nb_classes代表的是模型最后一层所输出的分类数量,这里只分成2类
model=VGG16(len(class_names), (img_width, img_height, 3))
model.summary()
Model: "functional_1"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃ Layer (type)                         ┃ Output Shape                ┃         Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer (InputLayer)             │ (None, 224, 224, 3)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block1_conv1 (Conv2D)                │ (None, 224, 224, 64)        │           1,792 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block1_conv2 (Conv2D)                │ (None, 224, 224, 64)        │          36,928 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout (Dropout)                    │ (None, 224, 224, 64)        │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block1_pool (MaxPooling2D)           │ (None, 112, 112, 64)        │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block2_conv1 (Conv2D)                │ (None, 112, 112, 128)       │          73,856 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block2_conv2 (Conv2D)                │ (None, 112, 112, 128)       │         147,584 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_1 (Dropout)                  │ (None, 112, 112, 128)       │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block2_pool (MaxPooling2D)           │ (None, 56, 56, 128)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_conv1 (Conv2D)                │ (None, 56, 56, 256)         │         295,168 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_conv2 (Conv2D)                │ (None, 56, 56, 256)         │         590,080 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_conv3 (Conv2D)                │ (None, 56, 56, 256)         │         590,080 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_2 (Dropout)                  │ (None, 56, 56, 256)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_pool (MaxPooling2D)           │ (None, 28, 28, 256)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_conv1 (Conv2D)                │ (None, 28, 28, 512)         │       1,180,160 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_conv2 (Conv2D)                │ (None, 28, 28, 512)         │       2,359,808 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_conv3 (Conv2D)                │ (None, 28, 28, 512)         │       2,359,808 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_3 (Dropout)                  │ (None, 28, 28, 512)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_pool (MaxPooling2D)           │ (None, 14, 14, 512)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_conv1 (Conv2D)                │ (None, 14, 14, 512)         │       2,359,808 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_conv2 (Conv2D)                │ (None, 14, 14, 512)         │       2,359,808 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_conv3 (Conv2D)                │ (None, 14, 14, 512)         │       2,359,808 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_4 (Dropout)                  │ (None, 14, 14, 512)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_pool (MaxPooling2D)           │ (None, 7, 7, 512)           │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten (Flatten)                    │ (None, 25088)               │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ fc1 (Dense)                          │ (None, 2048)                │      51,382,272 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_5 (Dropout)                  │ (None, 2048)                │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization                  │ (None, 2048)                │           8,192 │
│ (BatchNormalization)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ fc2 (Dense)                          │ (None, 256)                 │         524,544 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ predictions (Dense)                  │ (None, 2)                   │             514 │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
 Total params: 66,630,210 (254.17 MB)
 Trainable params: 66,626,114 (254.16 MB)
 Non-trainable params: 4,096 (16.00 KB)

四、编译模型

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

  • 损失函数(loss):用于衡量模型在训练期间的准确率。
  • 优化器(optimizer):决定模型如何根据其看到的数据和自身的损失函数进行更新。
  • 指标(metrics):用于监控训练和测试步骤。以下示例使用了准确率,即被正确分类的图像的比率。
# 编译模型时使用优化器
lr = 1e-4
optimizer = keras.optimizers.Adam(learning_rate=lr)
model.compile(optimizer=optimizer,
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

五、训练模型

from tqdm import tqdm
epochs = 5

# 记录训练数据,方便后面的分析
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)

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

        model.optimizer.learning_rate.assign(lr)

        train_loss = []
        train_accuracy = []
        for image, label in train_ds:
            history = model.train_on_batch(image, label)
            train_loss.append(history[0])
            train_accuracy.append(history[1])

            pbar.set_postfix({"train_loss": "%.4f" % history[0],
                              "train_acc": "%.4f" % history[1],
                              "lr": lr})
            pbar.update(1)
        # Update learning rate manually
        lr = lr * 0.92

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

    print('开始验证!')

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

        val_loss = []
        val_accuracy = []
        for image, label in val_ds:
            history = model.test_on_batch(image, label)
            val_loss.append(history[0])
            val_accuracy.append(history[1])

            pbar.set_postfix({"val_loss": "%.4f" % history[0],
                              "val_acc": "%.4f" % history[1]})
            pbar.update(1)
        history_val_loss.append(np.mean(val_loss))
        history_val_accuracy.append(np.mean(val_accuracy))

    print('结束验证!')
    print("验证loss为:%.4f" % np.mean(val_loss))
    print("验证准确率为:%.4f" % np.mean(val_accuracy))


Epoch 1/5: 100%|█| 340/340 [1:13:36<00:00, 12.99s/it, train_loss=0.6399, train_acc=0.6607, lr=0.0001


开始验证!


Epoch 1/5:   5%|▉                   | 4/85 [00:14<04:35,  3.40s/it, val_loss=0.6399, val_acc=0.6592]

WARNING:tensorflow:5 out of the last 5 calls to <function TensorFlowTrainer.make_test_function.<locals>.one_step_on_iterator at 0x000001E57B88B2E0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.


Epoch 1/5:   6%|█▏                  | 5/85 [00:17<04:26,  3.33s/it, val_loss=0.6397, val_acc=0.6594]

WARNING:tensorflow:6 out of the last 6 calls to <function TensorFlowTrainer.make_test_function.<locals>.one_step_on_iterator at 0x000001E57B88B2E0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.


Epoch 1/5: 100%|███████████████████| 85/85 [04:38<00:00,  3.27s/it, val_loss=0.6429, val_acc=0.6403]


结束验证!
验证loss为:0.6411
验证准确率为:0.6499


Epoch 2/5: 100%|█| 340/340 [1:06:05<00:00, 11.66s/it, train_loss=0.5159, train_acc=0.7297, lr=9.2e-5


开始验证!


Epoch 2/5: 100%|███████████████████| 85/85 [04:25<00:00,  3.13s/it, val_loss=0.5242, val_acc=0.7222]


结束验证!
验证loss为:0.5197
验证准确率为:0.7256


Epoch 3/5: 100%|█| 340/340 [1:05:11<00:00, 11.50s/it, train_loss=0.4391, train_acc=0.7765, lr=8.46e-


开始验证!


Epoch 3/5: 100%|███████████████████| 85/85 [04:23<00:00,  3.10s/it, val_loss=0.4274, val_acc=0.7836]


结束验证!
验证loss为:0.4328
验证准确率为:0.7803


Epoch 4/5: 100%|█| 340/340 [1:05:15<00:00, 11.52s/it, train_loss=0.3708, train_acc=0.8169, lr=7.79e-


开始验证!


Epoch 4/5: 100%|███████████████████| 85/85 [04:27<00:00,  3.15s/it, val_loss=0.3686, val_acc=0.8182]


结束验证!
验证loss为:0.3698
验证准确率为:0.8175


Epoch 5/5: 100%|█| 340/340 [1:08:18<00:00, 12.06s/it, train_loss=0.3254, train_acc=0.8410, lr=7.16e-


开始验证!


Epoch 5/5: 100%|███████████████████| 85/85 [04:28<00:00,  3.16s/it, val_loss=0.3278, val_acc=0.8386]

结束验证!
验证loss为:0.3266
验证准确率为:0.8399

六、模型评估

epochs_range = range(epochs)

plt.figure(figsize=(14, 4))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, history_train_accuracy, label='Training Accuracy')
plt.plot(epochs_range, history_val_accuracy, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, history_train_loss, label='Training Loss')
plt.plot(epochs_range, history_val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

在这里插入图片描述

七、预测

import numpy as np

# 采用加载的模型(new_model)来看预测结果
plt.figure(figsize=(18, 3))  # 图形的宽为18高为5
plt.suptitle("Prediction Results")

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

        plt.axis("off")
[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 1s/step
[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 758ms/step
[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 776ms/step
[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 769ms/step
[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 765ms/step
[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 727ms/step
[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 744ms/step
[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 750ms/step

在这里插入图片描述

八、总结

1、train_on_batchtest_on_batch

train_on_batch

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

对单批数据进行单次梯度更新。当你想用自己的自定义规则训练模型并想要完全控制模型训练时,你可以使用Keras.train_on_batch()函数。在使用 train_on_batch() 方法前,需要先编译模型,并指定优化器(如 Adam、SGD 等)和损失函数(如交叉熵、均方误差等)

这种方法的优点如下:

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

参数

  • x:输入数据
  • 可以是一个numpy数组(类似数组)或数组列表(如果模型有多个输入);
  • tensorflow张量或者张量列表;
  • 如果模型具有命名输入,则为将输入名称映射到相应的数组/张量的字典
  • y:预测目标数据;和x的格式类似;
  • sample_weight:与 x 长度相同的可选数组,包含应用于每个样本的模型损失的权重。对于时间数据,您可以传递形状为 (samples,sequence_length) 的二维数组,以对每个样本的每个时间步应用不同的权重。
  • class_weight:可选字典,将类别指数(整数)映射为权重(浮点数),用于计算模型在训练期间对该类样本的损失。这对告诉模型 “更关注 ”来自代表性不足的类别的样本非常有用。如果指定了 class_weight,且目标的秩为 2 或更大,则必须对 y 进行独热编码,或者为稀疏的类标签加入明确的最终维度 1。
  • reset_metrics:如果为 True,返回的指标将只针对本批次。如果为 False,指标将在各批次中进行状态累积。
  • return_dict:如果为True,则损失和指标结果将以字典形式返回,每个键都是指标的名称。如果为False,则它们将以列表形式返回。

test_on_batch

Model.test_on_batch(x, y=None, sample_weight=None, reset_metrics=True, return_dict=False)

test_on_batch(x, y) 方法则是用于在测试集上评估模型的性能,与 train_on_batch() 方法类似,该方法将输入数据 x 和对应的真实标签数据 y 喂入模型,但不会更新模型参数。而是计算模型在该批次数据上的预测输出以及相应的损失函数值,并返回本批次数据的平均损失值。通常在整个测试集上执行多次 test_on_batch() 方法,以获得模型在测试集上的总体性能指标。

示例代码和输出

  • 单输出模型,且只有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

2、数据集配置

map()方法:

用于将给定的函数应用于数据集中的每个元素。它通常用于对数据进行预处理,例如图像归一化、数据增强等。

map(map_func, num_parallel_calls=None, deterministic=None, name=None)
  • map_func: 要应用于数据集每个元素的函数。该函数接受数据集中的一个元素,并返回处理后的元素。
dataset = Dataset.range(1, 6)  # ==> [ 1, 2, 3, 4, 5 ]
dataset = dataset.map(lambda x: x + 1)
list(dataset.as_numpy_iterator())
# ==>[2,3,4,5,6]
  • num_parallel_calls: 该参数表示要异步并行处理数据的数量,可以为tf.int64tf.Tensor类型数据。如果不指定,数据将按顺序处理。如果指定为tf.data.AUTOTUNE,则会根据可用的CPU动态设置并行处理数据的数量。
  • deterministic: 布尔型。当num_parallel_calls确定时,deterministic将会控制数据转换过程中处理数据的顺序。如果设置为 “False”,则允许转换不按顺序产生元素,从而以确定性换取性能。如果未指定,则由 tf.data.Options.deterministic 选项(默认为 True)控制行为。

shuffle()方法:

shuffle(buffer_size,seed=None,reshuffle_each_iteration=None)

该函数可以随机洗牌此数据集的元素。此数据集使用buffer_size的元素填充缓冲区,然后从该缓冲区中随机采样元素,用新元素替换所选元素。为了实现完美的洗牌,需要缓冲区大小大于或等于数据集的完整大小。

参数buffer_size:表示新数据集将从中采样的数据集中的元素数。

  • buffer_size=1:不打乱顺序,既保持原序;
  • buffer_size越大,打乱程度越大.
  • seed:(可选)表示将用于创建分布的随机种子。
  • reshuffle_each_iteration:(可选)一个布尔值,如果为true,则表示每次迭代时都应对数据集进行伪随机重组。(默认为True)

数据集归一化的不同方法:

方法一:

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)
# 原文链接:https://blog.csdn.net/LittleRuby/article/details/134687822

方法二:

normalization_layer = layers.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))
###隐函数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]
3、tqdm

参考:

  • https://blog.csdn.net/LittleRuby/article/details/134687822)
  • https://blog.csdn.net/weixin_38346042/article/details/135903042

tqdm 是 Python 进度条库,可以在 Python 长循环中添加一个进度提示信息。用户只需要封装任意的迭代器,是一个快速、扩展性强的进度条工具库。

tqdm的参数
tqdm(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: 接收一个可迭代对象。如果iterable为空的话,则手动通过update来更新迭代
  • total: 总的迭代次数,用于计算进度百分比, 默认等于len(iterable)
  • desc: 进度条的显示信息,显示在进度条前面(前缀)
  • postfix: 字典形式信息,例如:loss=0.56, 显示在进度条的末尾(后缀)
  • mininterval:设置进度条最小的更新间隔,单位秒,默认:0.1s
  • maxinterval: 设置进度条最大更新间隔,单位秒,默认:10s
  • bar_format: 设置进度条显示格式,默认为 {desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt}
  • ncols: 设置进度条的宽度
  • nrows: 设置进度条的高度
  • 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相关参数与方法
#一些常用方法:
tqdm.write(s, file=None, end="\n",nolock=False)
#s-要写入的字符串;file要写入的文件,默认sys.stderr;nolock-是否使用锁来保护写入操作,默认False
tqdm.set_verbosity(verbosity)
#verbosity-要设置的详细程度,可以是0、1或2
tqdm.set_mininterval(mininterval)
#要设置最小时间间隔,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
使用示例:

1、使用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]

2、对tqdm进行手动更新

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

with tqdm(total=100) as pbar:
#使用 with 语句可以确保在代码块结束后自动关闭进度条,避免忘记关闭进度条导致资源泄漏
    for i in range(10):
        sleep(0.1)
        pbar.update(10)
#不使用with的情况:
pbar = tqdm(total=100)
for i in range(10):
    sleep(0.1)
    #使用 time.sleep(0.1) 函数暂停程序执行 0.1 秒。
    pbar.update(10)
 pbar.close()
4、keras.backend.set_value()backend.get_value()
#
tf.keras.backend.set_value(
    x, value)
tf.keras.backend.get_value(
    x)
  • tf.keras.backend.set_value()

    • 将 TensorFlow 张量 x 的值设置为 value。
    • x 可以是 TensorFlow 张量或变量。
    • value 可以是 Python 标量、列表、元组或 NumPy 数组。
  • tf.keras.backend.get_value()

    • 获取 TensorFlow 张量 x 的值,x 可以是 TensorFlow 张量或变量。
5、代码问题

以下包括个人遇到的一些报错警告和参考了T9训练当中,本期BUG答案总结在这里:

本期代码存在的bug

1、模型训练中关于acc等的更新计算有问题,修改部分如下:

from tqdm import tqdm
import tensorflow.keras.backend as K
import numpy as np

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)
        
        train_loss     = []
        train_accuracy = []
        for image,label in train_ds:   
            """
            训练模型,简单理解train_on_batch就是:它是比model.fit()更高级的一个用法

            想详细了解 train_on_batch 的同学,
            可以看看我的这篇文章:https://www.yuque.com/mingtian-fkmxf/hv4lcq/ztt4gy
            """
             # 这里生成的是每一个batch的acc与loss
            history = model.train_on_batch(image,label)
            
            train_loss.append(history[0])
            train_accuracy.append(history[1])
            
            pbar.set_postfix({"train_loss": "%.4f"%history[0],
                              "train_acc":"%.4f"%history[1],
                              "lr": K.get_value(model.optimizer.lr)})
            pbar.update(1)
            
        history_train_loss.append(np.mean(train_loss))
        history_train_accuracy.append(np.mean(train_accuracy))
            
    print('开始验证!')
    
    with tqdm(total=val_total, desc=f'Epoch {epoch + 1}/{epochs}',mininterval=0.3,ncols=100) as pbar:

        val_loss     = []
        val_accuracy = []
        for image,label in val_ds:      
            # 这里生成的是每一个batch的acc与loss
            history = model.test_on_batch(image,label)
            
            val_loss.append(history[0])
            val_accuracy.append(history[1])
            
            pbar.set_postfix({"val_loss": "%.4f"%history[0],
                              "val_acc":"%.4f"%history[1]})
            pbar.update(1)
        history_val_loss.append(np.mean(val_loss))
        history_val_accuracy.append(np.mean(val_accuracy))
            
    print('结束验证!')
    print("验证loss为:%.4f"%np.mean(val_loss))
    print("验证准确率为:%.4f"%np.mean(val_accuracy))
  • 修改前 - 将每训练1个batch之后的损失和准确率直接记录进history_train/val_losshistory_train/val_accuracy当中,最后记录的只是整个epoch中最后1个batch所得的损失和准确率而不是整个epoch中训练数据的平均值,这对于衡量实际训练效果是有失偏颇的;

  • 修改后 - 每次处理一个 batch后,将该 batch 的损失和准确率保存在lossaccuracy列表中。计算1个epoch中所有batch的训练损失和准确率的平均值,并将均值记录到history_train/val_losshistory_train/val_accuracy中。能够更准确地反映整个训练集和验证集上的表现。

2、最后全连接层输出应为分类的数量
在functional模型定义时我认为应该修改定义时的输出model=VGG16(1000, (img_width, img_height, 3))model=VGG16(len(class_names), (img_width, img_height, 3)),会更加规范。

个人遇到的报错

1、我认为可能是tensorflow/keras版本引起的Attribute Error
通过降低版本至tf==2.15.0验证报错消除,但是2.16及以上的版本应该都会报错
https://github.com/keras-team/keras/issues/19148
在这里插入图片描述

在这里插入图片描述
直接使用keras.backend.set_value()将显示该属性不存在,无法通过lr直接访问optimizer中的学习率;要调整学习率,可以使用 tf.keras.optimizers.schedules 来创建一个学习率调度器,或者直接修改优化器的学习率属性。如果仍然希望在每个 epoch 开始时手动调整学习率,可以通过更新优化器的 learning_rate 属性来实现:

本次代码使用model.optimizer.learning_rate.assign(lr)来更新学习率。

参考:https://blog.csdn.net/weixin_51520249/article/details/142174201

2、如下警告(具体原理也不甚理解)

WARNING:tensorflow:5 out of the last 5 calls to <function TensorFlowTrainer.make_train_function.<locals>.one_step_on_iterator at 0x00000201655FF060> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.

主要是因为在训练循环中重复创建了@tf.function。每次循环迭代调用 model.train_on_batch 时,都会创建一个新的 TensorFlow 图,导致重复追踪。

gpt所给的解决方法: model.train_on_batchmodel.test_on_batch 移到循环外,用 @tf.function 装饰。这样就能避免重复创建 TensorFlow 图,从而提高训练速度。

建议是使用train_step()进行训练但是本人未继续深入探究代码应该怎么改。。。

  • 搜索得到一个暂时的解决办法↓,验证有效:
    在这里插入图片描述
    综上为本人遇到的一些问题,如果有更了解具体原理和更好解决方法的朋友们,望不吝赐教

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2171686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

得物App荣获国家级大奖,以沉浸式体验重塑消费新标杆

近日&#xff0c;备受瞩目的中国国际服务贸易交易会&#xff08;简称“服贸会”&#xff09;在北京举行。得物App作为上海科技企业代表参展&#xff0c;向全球展现中国企业的数字化创新服务。在展台现场&#xff0c;得物App搭建了首个3D创新应用“球鞋博物馆”&#xff0c;对球…

探探Java与python中的闭包

说在前面&#xff1a;在计算机科学中&#xff0c;闭包是指一个函数以及其引用的周围环境&#xff08;变量&#xff09;所组成的整体。简单来说&#xff0c;闭包允许一个函数访问并操作其外部函数作用域中的变量&#xff0c;即使外部函数已经执行完毕。 Java函数式编程—闭包&am…

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下搭建MaxKB开源知识库问答系统

828华为云征文&#xff5c;华为云Flexus云服务器X实例之openEuler系统下搭建MaxKB开源知识库问答系统 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、MaxKB 介绍2.1 MaxKB简介2.2 MaxKB整体架构…

Springboot中yml文件不生效原因分析及解决

在Spring Boot项目中&#xff0c;YML文件是常用的配置文件格式。然而&#xff0c;有时你可能会遇到YML文件中的配置不生效的问题。本文将分析可能的原因并提供解决方案。 1. 格式错误 YML文件非常敏感&#xff0c;任何缩进或语法错误都可能导致配置不被解析。确保使用两个空格…

Vxe UI vue vxe-table vxe-text-ellipsis 如何实现单元格多行文本超出、多行文本溢出省略

Vxe UI vue vxe-table 如何实现单元格多行文本超出、多行文本溢出省略 代码 配合 vxe-text-ellipsis 组件实现多行文本溢出省略 <template><div><vxe-grid v-bind"gridOptions"><template #defaultAddress"{ row }"><vxe-te…

2025届 深圳 嵌入式岗 秋招上岸记录

文章目录 1 背景2 准备阶段2.1 前期2.1.1 掌握的技术栈2.1.2 项目经历2.1.3 比赛&奖学金经历 2.2 中期2.2.1 简历准备2.2.2 个人信息准备2.2.3 企业以及岗位信息的收集2.2.4 个人资料的准备 2.3 简历投递2.3.1 网申2.3.2 招聘会现场投递 3. 简历投递后3.1 测评3.2 笔试3.3 …

镜像电流源与其应用(防倒灌电路)

目录&#xff1a; 1、镜像电流源 2、防倒灌电路 1&#xff09;输入防反接保护 2&#xff09;输出防倒灌功能 1、镜像电流源 如下图1.1所示&#xff0c;三极管Q1的发射极经过b极&#xff0c;再通过R1电阻&#xff0c;形成回路&#xff0c;此时有回路电流形成。设Vbe -0.7…

C++杂项

作业&#xff1a; 将之前实现的顺序表、栈、队列都更改成模板类 顺序表 #include <iostream>using namespace std;template<typename T>class SeqList { private:T *ptr;int size; //总长度int len 0; //当前顺序表实际长度public://初始…

【笔记】X射线物理基础

一、X射线衍射分析简史 1895年X射线发现 1896 年 2 月对骨折的观察&#xff1a;G.和 E. Frost是第一个使用 X 射线进行医疗用途 1897 年法国海关官员的行李扫描。 X射线衍射理论1 X射线衍射理论2 元素的特征X射线 X射线光电子的应用 电磁波的粒子属性 X射线层析成像法 X-ray…

《CARAT: Contrastive Feature Reconstruction and Aggregation...》中文校对版

系列论文研读目录 例如&#xff1a; 文章目录 系列论文研读目录摘要引言相关作品方法论问题定义单模态标签特征提取基于对比重建的融合 基于Shuffle的特征聚合实验实验设置实验结果 分析结论 摘要 多模态多标签情感识别&#xff08;Multi-modal multi-label emotion recognit…

windows10使用bat脚本安装前后端环境之redis注册服务

首先需要搞清楚redis在本地是怎么安装配置、然后在根据如下步骤编写bat脚本&#xff1a; 思路 1.下载zip格式redis 2.查看windows server服务是否已安装redis 3.启动查看服务是否正常 bat脚本 echo off echo windows10 x64 server redis init REM 请求管理员权限并隐藏窗口 …

Total Blocking Time指标

Total Blocking Time 总阻塞时间 (TBT) 是一项用于衡量加载响应能力的重要实验指标&#xff0c;因为它有助于量化网页在变为可靠交互之前处于非交互状态的严重程度。 TBT&#xff08;Total Blocking Time&#xff09;&#xff0c;阻塞总时间&#xff0c;记录在 FCP 到 TTI 之间…

大厂太卷了!又一款一站式AI短剧创作神器,这套AI工作流厉害了:自动生成脚本、角色、分镜、视频、音乐、字幕...(附保姆级教程)

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 行业在卷 AI 应用&#xff0c;而美图已经在卷 AI 工作流了………

每日一题|2516. 每种字符至少取 K 个|双指针、最长子串、字典

本题需要转化求解目标。 对于一个序列&#xff0c;两头收集的最少数量的时候&#xff0c;剩下的部分&#xff08;我们称之为子串&#xff09;就会对应的越长。也就是说&#xff0c;我们只要求解一个满足要求的最长子串&#xff0c;使得两边剩余的字符数量刚好满足要求。 由于…

学习threejs,实现几何体阴影效果

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言二、&#x1f340;绘制任意字体模型…

量化交易backtrader实践(三)_指标与策略篇(1)_指标简介与手工双均线策略

01_指标 指标是一个汉语词语&#xff0c;读音是zhǐ biāo&#xff0c;意思是衡量目标的参数&#xff1b;预期中打算达到的指数、规格、标准&#xff0c;一般用数据表示。 统计学术语 指标是说明总体数量特征的概念及其数值的综合&#xff0c;故又称为综合指标。在实际的统计…

使用ESPnet的 setup_anaconda.sh安装脚本一步到位,配置conda虚拟环境

使用ESPnet的 setup_anaconda.sh安装脚本一步到位&#xff0c;配置conda虚拟环境 前言 ESPnet&#xff08;End-to-End Speech Processing Toolkit&#xff09;是一款用于语音识别、语音合成等任务的开源端到端语音处理工具包。为了在不同系统上快速配置ESPnet开发环境&#x…

Serverless and Go

本篇内容是根据2019年8月份Serverless and Go音频录制内容的整理与翻译, Johnny、Mat、Jaana 和特邀嘉宾 Stevenson Jean-Pierre 讨论了 Go 世界中的Serverless。什么是Serverless&#xff0c;Serverless适用于哪些用例&#xff0c;有哪些权衡&#xff0c;以及如何在Serverles…

AI芯片WT2605C赋能厨房家电,在线对话操控,引领智能烹饪新体验:尽享高效便捷生活

在智能家居的蓬勃发展中&#xff0c;智能厨电作为连接科技与生活的桥梁&#xff0c;正逐步渗透到每一个现代家庭的厨房中。蒸烤箱作为智能厨电的代表&#xff0c;以其丰富的功能和高效的性能&#xff0c;满足了人们对美食的多样化追求。然而&#xff0c;面对众多复杂的操作功能…

每日OJ_牛客_OR59字符串中找出连续最长的数字串_双指针_C++_Java

目录 牛客_OR59字符串中找出连续最长的数字串 题目解析 C代码1 C代码2 C代码3 Java代码 牛客_OR59字符串中找出连续最长的数字串 字符串中找出连续最长的数字串_牛客题霸_牛客网 题目解析 双指针&#xff1a; 遍历整个字符串&#xff0c;遇到数字的时候&#xff0c;用双…