翻译: 迁移学习和微调 Transfer learning fine-tuning

news2024/12/30 3:07:21

1. 介绍

迁移学习包括获取在一个问题上学到的特征,并将它们用于一个新的类似问题。例如,已经学会识别浣熊的模型的特征可能有助于启动旨在识别狸猫的模型。

迁移学习通常用于您的数据集数据太少而无法从头开始训练全尺寸模型的任务。

在深度学习的背景下,迁移学习最常见的体现是以下工作流程:

从先前训练的模型中提取层。
冻结它们,以避免在未来的训练回合中破坏它们包含的任何信息。
在冻结层之上添加一些新的可训练层。他们将学习将旧特征转化为对新数据集的预测。
在数据集上训练新层。
最后一个可选步骤是微调,它包括解冻您在上面获得的整个模型(或其中的一部分),并以非常低的学习率在新数据上对其进行重新训练。通过逐步使预训练特征适应新数据,这可能会实现有意义的改进。

首先,我们将详细介绍 Keras trainableAPI,它是大多数迁移学习和微调工作流程的基础。

然后,我们将通过采用在 ImageNet 数据集上预训练的模型,并在 Kaggle“猫与狗”分类数据集上对其进行重新训练来演示典型的工作流程。

2. 冻结层:了解trainable属性

Layers & models 具有三个权重属性:

  • weights是该层所有权重变量的列表。
  • trainable_weights是那些要更新(通过梯度下降)以最小化训练期间损失的列表。
  • non_trainable_weights是那些不打算接受培训的人的名单。通常,它们在正向传递期间由模型更新。

2.1 设置

import numpy as np
import tensorflow as tf
from tensorflow import keras

2.2 示例:该Dense层有 2 个可训练权重(内核和偏差)

layer = keras.layers.Dense(3)
layer.build((None, 4))  # Create the weights

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2
trainable_weights: 2
non_trainable_weights: 0

一般来说,所有的权重都是可训练的权重。唯一具有不可训练权重的内置层是BatchNormalization层。它使用不可训练的权重来跟踪训练期间输入的均值和方差。要了解如何在您自己的自定义层中使用不可训练的权重,请参阅 从头开始编写新层的指南。

2.3 示例:该BatchNormalization层有 2 个可训练权重和 2 个不可训练权重

layer = keras.layers.BatchNormalization()
layer.build((None, 4))  # Create the weights

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 4
trainable_weights: 2
non_trainable_weights: 2

图层和模型还具有布尔属性trainable。它的值可以改变。设置layer.trainable为False将所有层的权重从可训练移动到不可训练。这称为“冻结”层:冻结层的状态在训练期间不会更新(无论是在训练时还是在fit()训练依赖于trainable_weights应用梯度更新的任何自定义循环时)。

2.4 示例:设置trainable为False

layer = keras.layers.Dense(3)
layer.build((None, 4))  # Create the weights
layer.trainable = False  # Freeze the layer

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2
trainable_weights: 0
non_trainable_weights: 2

当可训练权重变为不可训练时,其值在训练期间不再更新。

# Make a model with 2 layers
layer1 = keras.layers.Dense(3, activation="relu")
layer2 = keras.layers.Dense(3, activation="sigmoid")
model = keras.Sequential([keras.Input(shape=(3,)), layer1, layer2])

# Freeze the first layer
layer1.trainable = False

# Keep a copy of the weights of layer1 for later reference
initial_layer1_weights_values = layer1.get_weights()

# Train the model
model.compile(optimizer="adam", loss="mse")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))

# Check that the weights of layer1 have not changed during training
final_layer1_weights_values = layer1.get_weights()
np.testing.assert_allclose(
    initial_layer1_weights_values[0], final_layer1_weights_values[0]
)
np.testing.assert_allclose(
    initial_layer1_weights_values[1], final_layer1_weights_values[1]
)
1/1 [==============================] - 0s 333ms/step - loss: 0.1007

不要将属性与参数混淆( layer.trainable它控制层是否应在推理模式或训练模式下运行其前向传递)。有关详细信息,请参阅 Keras 常见问题解答。traininglayer.call()

2.5 trainable属性的递归设置

如果您trainable = False在模型或任何具有子层的层上进行设置,则所有子层也将变得不可训练。

例子:

inner_model = keras.Sequential(
    [
        keras.Input(shape=(3,)),
        keras.layers.Dense(3, activation="relu"),
        keras.layers.Dense(3, activation="relu"),
    ]
)

model = keras.Sequential(
    [keras.Input(shape=(3,)), inner_model, keras.layers.Dense(3, activation="sigmoid"),]
)

model.trainable = False  # Freeze the outer model

assert inner_model.trainable == False  # All layers in `model` are now frozen
assert inner_model.layers[0].trainable == False  # `trainable` is propagated recursively

3. 典型的迁移学习工作流程 The typical transfer-learning workflow

这引导我们了解如何在 Keras 中实现典型的迁移学习工作流程:

  1. 实例化一个基础模型并将预训练的权重加载到其中。
  2. 通过设置冻结基础模型中的所有层trainable = False。
  3. 在基础模型的一个(或多个)层的输出之上创建一个新模型。
  4. 在新数据集上训练新模型。

请注意,另一种更轻量级的工作流程也可以是:

  1. 实例化一个基础模型并将预训练的权重加载到其中。
  2. 通过它运行您的新数据集并记录基础模型中一个(或多个)层的输出。这称为特征提取。
  3. 使用该输出作为新的更小模型的输入数据。

第二个工作流程的一个关键优势是您只需对数据运行一次基本模型,而不是每个训练周期运行一次。所以它更快更便宜。

但是,第二个工作流程的一个问题是它不允许您在训练期间动态修改新模型的输入数据,例如,在进行数据扩充时需要这样做。当新数据集的数据太少而无法从头开始训练全尺寸模型时,迁移学习通常用于任务,在这种情况下,数据增强非常重要。所以接下来,我们将重点关注第一个工作流程。

这是 Keras 中的第一个工作流程:

首先,实例化一个具有预训练权重的基础模型。

base_model = keras.applications.Xception(
    weights='imagenet',  # Load weights pre-trained on ImageNet.
    input_shape=(150, 150, 3),
    include_top=False)  # Do not include the ImageNet classifier at the top.

然后,冻结基础模型。

base_model.trainable = False

在上面创建一个新模型。

inputs = keras.Input(shape=(150, 150, 3))
# We make sure that the base_model is running in inference mode here,
# by passing `training=False`. This is important for fine-tuning, as you will
# learn in a few paragraphs.
x = base_model(inputs, training=False)
# Convert features of shape `base_model.output_shape[1:]` to vectors
x = keras.layers.GlobalAveragePooling2D()(x)
# A Dense classifier with a single unit (binary classification)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

在新数据上训练模型。

model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=[keras.metrics.BinaryAccuracy()])
model.fit(new_dataset, epochs=20, callbacks=..., validation_data=...)

4. 微调 Fine-tuning

一旦您的模型收敛于新数据,您可以尝试解冻全部或部分基础模型,并以非常低的学习率端到端地重新训练整个模型。

这是一个可选的最后一步,可能会给您带来增量改进。它还可能导致快速过度拟合——请记住这一点。

至关重要的是,只有在具有冻结层的模型训练到收敛后才执行此步骤。如果将随机初始化的可训练层与包含预训练特征的可训练层混合使用,则随机初始化的层将在训练期间导致非常大的梯度更新,这将破坏您的预训练特征。

在此阶段使用非常低的学习率也很重要,因为您在通常非常小的数据集上训练比第一轮训练大得多的模型。因此,如果您应用较大的权重更新,您将面临很快过度拟合的风险。在这里,您只想以增量方式重新调整预训练的权重。

这是如何实现整个基础模型的微调:

# Unfreeze the base model
base_model.trainable = True

# It's important to recompile your model after you make any changes
# to the `trainable` attribute of any inner layer, so that your changes
# are take into account
model.compile(optimizer=keras.optimizers.Adam(1e-5),  # Very low learning rate
              loss=keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=[keras.metrics.BinaryAccuracy()])

# Train end-to-end. Be careful to stop before you overfit!
model.fit(new_dataset, epochs=10, callbacks=..., validation_data=...)

compile()重要说明trainable

调用compile()模型意味着“冻结”该模型的行为。这意味着trainable 模型编译时的属性值应该在该模型的整个生命周期中保留,直到compile再次调用。因此,如果您更改任何trainable值,请确保compile()再次调用您的模型以使您的更改被考虑在内。

BatchNormalization关于图层的重要说明

许多图像模型包含BatchNormalization图层。该层是每个可以想象的特例。请记住以下几点。

  • BatchNormalization包含 2 个在训练期间更新的不可训练的权重。这些是跟踪输入的均值和方差的变量。
  • 当您设置 时bn_layer.trainable = False,该BatchNormalization层将以推理模式运行,并且不会更新其均值和方差统计信息。一般来说,其他层的情况并非如此,因为 权重可训练性和推理/训练模式是两个正交的概念。但是在图层的情况下两者是并列的BatchNormalization。
  • 当您解冻包含层的模型BatchNormalization以进行微调时,您应该在调用基础模型时BatchNormalization通过传递将层保持在推理模式。training=False否则,应用于不可训练权重的更新会突然破坏模型学到的东西。

您将在本指南末尾的端到端示例中看到这种模式的实际应用。

5. 使用自定义训练循环进行迁移学习和微调 Transfer learning & fine-tuning with a custom training loop

fit()如果您使用自己的低级训练循环而不是,则工作流程基本保持不变。在应用梯度更新时,你应该小心只考虑列表 model.trainable_weights:

# Create base model
base_model = keras.applications.Xception(
    weights='imagenet',
    input_shape=(150, 150, 3),
    include_top=False)
# Freeze base model
base_model.trainable = False

# Create new model on top.
inputs = keras.Input(shape=(150, 150, 3))
x = base_model(inputs, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)
optimizer = keras.optimizers.Adam()

# Iterate over the batches of a dataset.
for inputs, targets in new_dataset:
    # Open a GradientTape.
    with tf.GradientTape() as tape:
        # Forward pass.
        predictions = model(inputs)
        # Compute the loss value for this batch.
        loss_value = loss_fn(targets, predictions)

    # Get gradients of loss wrt the *trainable* weights.
    gradients = tape.gradient(loss_value, model.trainable_weights)
    # Update the weights of the model.
    optimizer.apply_gradients(zip(gradients, model.trainable_weights))

同样用于微调。

6. 端到端示例:微调猫与狗数据集上的图像分类模型

为了巩固这些概念,让我们带您完成一个具体的端到端迁移学习和微调示例。我们将加载在 ImageNet 上预训练的 Xception 模型,并将其用于 Kaggle“猫与狗”分类数据集。

6.1 获取数据

首先,让我们使用 TFDS 获取猫狗数据集。如果您有自己的数据集,您可能希望使用该实用程序 tf.keras.utils.image_dataset_from_directory从磁盘上的一组图像生成类似的标记数据集对象,这些图像被归档到特定于类的文件夹中。

迁移学习在处理非常小的数据集时最有用。为了保持我们的数据集较小,我们将使用 40% 的原始训练数据(25,000 张图像)进行训练,10% 用于验证,10% 用于测试。

import tensorflow_datasets as tfds

tfds.disable_progress_bar()

train_ds, validation_ds, test_ds = tfds.load(
    "cats_vs_dogs",
    # Reserve 10% for validation and 10% for test
    split=["train[:40%]", "train[40%:50%]", "train[50%:60%]"],
    as_supervised=True,  # Include labels
)

print("Number of training samples: %d" % tf.data.experimental.cardinality(train_ds))
print(
    "Number of validation samples: %d" % tf.data.experimental.cardinality(validation_ds)
)
print("Number of test samples: %d" % tf.data.experimental.cardinality(test_ds))
Number of training samples: 9305
Number of validation samples: 2326
Number of test samples: 2326

这些是训练数据集中的前 9 张图像——如您所见,它们的大小各不相同。

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
for i, (image, label) in enumerate(train_ds.take(9)):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(image)
    plt.title(int(label))
    plt.axis("off")

在这里插入图片描述
我们还可以看到标签 1 是“狗”,标签 0 是“猫”。

6.2 标准化数据

我们的原始图像有多种尺寸。此外,每个像素由 0 到 255(RGB 级别值)之间的 3 个整数值组成。这不太适合为神经网络提供数据。我们需要做两件事:

  • 标准化为固定的图像大小。我们选择 150x150。
  • 标准化介于 -1 和 1 之间的像素值。我们将使用Normalization图层作为模型本身的一部分来执行此操作。

一般来说,开发将原始数据作为输入的模型是一种很好的做法,而不是采用已经预处理过的数据的模型。原因是,如果您的模型需要预处理数据,则任何时候您导出模型以在其他地方使用它(在网络浏览器中,在移动应用程序中),您都需要重新实现完全相同的预处理管道。这很快就会变得非常棘手。所以我们应该在打模型之前做尽可能少的预处理。

在这里,我们将在数据管道中调整图像大小(因为深度神经网络只能处理连续批次的数据),并且我们将在创建模型时将输入值缩放作为模型的一部分。

让我们将图像调整为 150x150:

size = (150, 150)

train_ds = train_ds.map(lambda x, y: (tf.image.resize(x, size), y))
validation_ds = validation_ds.map(lambda x, y: (tf.image.resize(x, size), y))
test_ds = test_ds.map(lambda x, y: (tf.image.resize(x, size), y))

此外,让我们对数据进行批处理并使用缓存和预取来优化加载速度。

batch_size = 32

train_ds = train_ds.cache().batch(batch_size).prefetch(buffer_size=10)
validation_ds = validation_ds.cache().batch(batch_size).prefetch(buffer_size=10)
test_ds = test_ds.cache().batch(batch_size).prefetch(buffer_size=10)

6.3 使用随机数据增强

当您没有大型图像数据集时,通过对训练图像应用随机但逼真的变换(例如随机水平翻转或小的随机旋转)来人为引入样本多样性是一种很好的做法。这有助于将模型暴露于训练数据的不同方面,同时减缓过度拟合。

from tensorflow import keras
from tensorflow.keras import layers

data_augmentation = keras.Sequential(
    [layers.RandomFlip("horizontal"), layers.RandomRotation(0.1),]
)

让我们想象一下第一批的第一张图像在经过各种随机变换后的样子:

import numpy as np

for images, labels in train_ds.take(1):
    plt.figure(figsize=(10, 10))
    first_image = images[0]
    for i in range(9):
        ax = plt.subplot(3, 3, i + 1)
        augmented_image = data_augmentation(
            tf.expand_dims(first_image, 0), training=True
        )
        plt.imshow(augmented_image[0].numpy().astype("int32"))
        plt.title(int(labels[0]))
        plt.axis("off")

在这里插入图片描述

7. 建立一个模型

现在让我们按照我们之前解释的蓝图构建一个模型。

注意:

  • 我们添加一个Rescaling层以将输入值(最初在[0, 255] 范围内)缩放到[-1, 1]范围。
  • 我们Dropout在分类层之前添加一层,用于正则化。
  • 我们确保training=False在调用基础模型时通过,以便它以推理模式运行,这样即使我们解冻基础模型进行微调,batchnorm 统计数据也不会更新。
base_model = keras.applications.Xception(
    weights="imagenet",  # Load weights pre-trained on ImageNet.
    input_shape=(150, 150, 3),
    include_top=False,
)  # Do not include the ImageNet classifier at the top.

# Freeze the base_model
base_model.trainable = False

# Create new model on top
inputs = keras.Input(shape=(150, 150, 3))
x = data_augmentation(inputs)  # Apply random data augmentation

# Pre-trained Xception weights requires that input be scaled
# from (0, 255) to a range of (-1., +1.), the rescaling layer
# outputs: `(inputs * scale) + offset`
scale_layer = keras.layers.Rescaling(scale=1 / 127.5, offset=-1)
x = scale_layer(x)

# The base model contains batchnorm layers. We want to keep them in inference mode
# when we unfreeze the base model for fine-tuning, so we make sure that the
# base_model is running in inference mode here.
x = base_model(x, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dropout(0.2)(x)  # Regularize with dropout
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

model.summary()
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_5 (InputLayer)         [(None, 150, 150, 3)]     0         
_________________________________________________________________
sequential_3 (Sequential)    (None, 150, 150, 3)       0         
_________________________________________________________________
rescaling (Rescaling)        (None, 150, 150, 3)       0         
_________________________________________________________________
xception (Functional)        (None, 5, 5, 2048)        20861480  
_________________________________________________________________
global_average_pooling2d (Gl (None, 2048)              0         
_________________________________________________________________
dropout (Dropout)            (None, 2048)              0         
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 2049      
=================================================================
Total params: 20,863,529
Trainable params: 2,049
Non-trainable params: 20,861,480
_________________________________________________________________

8. 训练顶层

model.compile(
    optimizer=keras.optimizers.Adam(),
    loss=keras.losses.BinaryCrossentropy(from_logits=True),
    metrics=[keras.metrics.BinaryAccuracy()],
)

epochs = 20
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)

Epoch 1/20
291/291 [==============================] - 133s 451ms/step - loss: 0.1670 - binary_accuracy: 0.9267 - val_loss: 0.0830 - val_binary_accuracy: 0.9716
Epoch 2/20
291/291 [==============================] - 135s 465ms/step - loss: 0.1208 - binary_accuracy: 0.9502 - val_loss: 0.0768 - val_binary_accuracy: 0.9716
Epoch 3/20
291/291 [==============================] - 135s 463ms/step - loss: 0.1062 - binary_accuracy: 0.9572 - val_loss: 0.0757 - val_binary_accuracy: 0.9716
Epoch 4/20
291/291 [==============================] - 137s 469ms/step - loss: 0.1024 - binary_accuracy: 0.9554 - val_loss: 0.0733 - val_binary_accuracy: 0.9725
Epoch 5/20
291/291 [==============================] - 137s 470ms/step - loss: 0.1004 - binary_accuracy: 0.9587 - val_loss: 0.0735 - val_binary_accuracy: 0.9729
Epoch 6/20
291/291 [==============================] - 136s 467ms/step - loss: 0.0979 - binary_accuracy: 0.9577 - val_loss: 0.0747 - val_binary_accuracy: 0.9708
Epoch 7/20
291/291 [==============================] - 134s 462ms/step - loss: 0.0998 - binary_accuracy: 0.9596 - val_loss: 0.0706 - val_binary_accuracy: 0.9725
Epoch 8/20
291/291 [==============================] - 133s 457ms/step - loss: 0.1029 - binary_accuracy: 0.9592 - val_loss: 0.0720 - val_binary_accuracy: 0.9733
Epoch 9/20
291/291 [==============================] - 135s 466ms/step - loss: 0.0937 - binary_accuracy: 0.9625 - val_loss: 0.0707 - val_binary_accuracy: 0.9721
Epoch 10/20
291/291 [==============================] - 137s 472ms/step - loss: 0.0967 - binary_accuracy: 0.9580 - val_loss: 0.0720 - val_binary_accuracy: 0.9712
Epoch 11/20
291/291 [==============================] - 135s 463ms/step - loss: 0.0961 - binary_accuracy: 0.9612 - val_loss: 0.0802 - val_binary_accuracy: 0.9699
Epoch 12/20
291/291 [==============================] - 134s 460ms/step - loss: 0.0963 - binary_accuracy: 0.9638 - val_loss: 0.0721 - val_binary_accuracy: 0.9716
Epoch 13/20
291/291 [==============================] - 136s 468ms/step - loss: 0.0925 - binary_accuracy: 0.9635 - val_loss: 0.0736 - val_binary_accuracy: 0.9686
Epoch 14/20
291/291 [==============================] - 138s 476ms/step - loss: 0.0909 - binary_accuracy: 0.9624 - val_loss: 0.0766 - val_binary_accuracy: 0.9703
Epoch 15/20
291/291 [==============================] - 136s 467ms/step - loss: 0.0949 - binary_accuracy: 0.9598 - val_loss: 0.0704 - val_binary_accuracy: 0.9725
Epoch 16/20
291/291 [==============================] - 133s 456ms/step - loss: 0.0969 - binary_accuracy: 0.9586 - val_loss: 0.0722 - val_binary_accuracy: 0.9708
Epoch 17/20
291/291 [==============================] - 135s 464ms/step - loss: 0.0913 - binary_accuracy: 0.9635 - val_loss: 0.0718 - val_binary_accuracy: 0.9716
Epoch 18/20
291/291 [==============================] - 137s 472ms/step - loss: 0.0915 - binary_accuracy: 0.9639 - val_loss: 0.0727 - val_binary_accuracy: 0.9725
Epoch 19/20
291/291 [==============================] - 134s 460ms/step - loss: 0.0938 - binary_accuracy: 0.9631 - val_loss: 0.0707 - val_binary_accuracy: 0.9733
Epoch 20/20
291/291 [==============================] - 134s 460ms/step - loss: 0.0971 - binary_accuracy: 0.9609 - val_loss: 0.0714 - val_binary_accuracy: 0.9716

<keras.callbacks.History at 0x7f4494e38f70>

9. 对整个模型做一轮微调

最后,让我们解冻基础模型并以低学习率端到端地训练整个模型。

重要的是,虽然基础模型变得可训练,但它仍然以推理模式运行,因为我们training=False在构建模型时调用它时通过了。这意味着内部的批量归一化层不会更新它们的批量统计信息。如果他们这样做了,他们将破坏模型到目前为止所学习的表征。

# Unfreeze the base_model. Note that it keeps running in inference mode
# since we passed `training=False` when calling it. This means that
# the batchnorm layers will not update their batch statistics.
# This prevents the batchnorm layers from undoing all the training
# we've done so far.
base_model.trainable = True
model.summary()

model.compile(
    optimizer=keras.optimizers.Adam(1e-5),  # Low learning rate
    loss=keras.losses.BinaryCrossentropy(from_logits=True),
    metrics=[keras.metrics.BinaryAccuracy()],
)

epochs = 10
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_5 (InputLayer)         [(None, 150, 150, 3)]     0         
_________________________________________________________________
sequential_3 (Sequential)    (None, 150, 150, 3)       0         
_________________________________________________________________
rescaling (Rescaling)        (None, 150, 150, 3)       0         
_________________________________________________________________
xception (Functional)        (None, 5, 5, 2048)        20861480  
_________________________________________________________________
global_average_pooling2d (Gl (None, 2048)              0         
_________________________________________________________________
dropout (Dropout)            (None, 2048)              0         
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 2049      
=================================================================
Total params: 20,863,529
Trainable params: 20,809,001
Non-trainable params: 54,528
_________________________________________________________________
Epoch 1/10
291/291 [==============================] - 567s 2s/step - loss: 0.0749 - binary_accuracy: 0.9689 - val_loss: 0.0605 - val_binary_accuracy: 0.9776
Epoch 2/10
291/291 [==============================] - 551s 2s/step - loss: 0.0559 - binary_accuracy: 0.9770 - val_loss: 0.0507 - val_binary_accuracy: 0.9798
Epoch 3/10
291/291 [==============================] - 545s 2s/step - loss: 0.0444 - binary_accuracy: 0.9832 - val_loss: 0.0502 - val_binary_accuracy: 0.9807
Epoch 4/10
291/291 [==============================] - 558s 2s/step - loss: 0.0365 - binary_accuracy: 0.9874 - val_loss: 0.0506 - val_binary_accuracy: 0.9807
Epoch 5/10
291/291 [==============================] - 550s 2s/step - loss: 0.0276 - binary_accuracy: 0.9890 - val_loss: 0.0477 - val_binary_accuracy: 0.9802
Epoch 6/10
291/291 [==============================] - 588s 2s/step - loss: 0.0206 - binary_accuracy: 0.9916 - val_loss: 0.0444 - val_binary_accuracy: 0.9832
Epoch 7/10
291/291 [==============================] - 542s 2s/step - loss: 0.0206 - binary_accuracy: 0.9923 - val_loss: 0.0502 - val_binary_accuracy: 0.9828
Epoch 8/10
291/291 [==============================] - 544s 2s/step - loss: 0.0153 - binary_accuracy: 0.9939 - val_loss: 0.0509 - val_binary_accuracy: 0.9819
Epoch 9/10
291/291 [==============================] - 548s 2s/step - loss: 0.0156 - binary_accuracy: 0.9934 - val_loss: 0.0610 - val_binary_accuracy: 0.9807
Epoch 10/10
291/291 [==============================] - 546s 2s/step - loss: 0.0176 - binary_accuracy: 0.9936 - val_loss: 0.0561 - val_binary_accuracy: 0.9789

<keras.callbacks.History at 0x7f4495056040>

在 10 个 epoch 之后,微调让我们在这里有了很好的改进。

参考

https://keras.io/guides/transfer_learning/

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

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

相关文章

AI模型推理(1)——入门篇

前言 本文主要介绍AI模型推理的相关基础概念&#xff0c;为后续云原生模型推理服务的学习做准备。 初识模型部署 对于深度学习模型来说&#xff0c;模型部署指让训练好的模型在特定环境中运行的过程。相比于常规的软件部署&#xff0c;模型部署会面临更多的难题&#xff1a; …

SPI机制详细讲解

文章目录 SPI机制案例分析建立DriverManager建立MysqlDriver来实现扩展建立OracleDriver来实现扩展测试spitest 源码分析ServiceLoader类的结构reload加载类LazyIterator类parse解析URL对象方法parseLine方法 SPI机制 SPI &#xff0c;全称为 Service Provider Interface&…

Bridge模式如何配置

Bridge模式案例&#xff08;一&#xff09; 基于Docker引擎启动Nginx WEB容器&#xff0c;默认以Bridge方式启动Docker容器&#xff0c;会动态DHCP给Docker容器分配IP、网关等信息&#xff0c;操作指令如下&#xff1a; 查看镜像列表 docker images#运行新的Nginx容器 dock…

ChatGPT实现代码解释

代码解释 新手程序员在入门之初&#xff0c;最好的学习路径就是直接阅读其他人的代码&#xff0c;从中学会别人是怎么写的&#xff0c;为什么这么写。过去&#xff0c;这个学习过程可能需要广泛阅读官方文档&#xff0c;在 GitHub issue 上提问&#xff0c;上 Stack Overflow …

内网渗透(六十)之AS-REP Roasting攻击

AS-REP Roasting攻击 AS-REP Roasting是一种针对用户账户进行离线爆破的攻击方式。但是该攻击方式使用上比较受限,因为其需要用户账户设置“不要求Kerberos预身份验证”选项。而该选项默认是没有勾选的。Kerberos域身份验证发生在Kerberos身份验证的第一阶段(AS_REQ&AS_…

手记系列之四 ----- 关于使用MySql的一些经验

前言 本篇文章主要介绍的关于本人在使用MySql记录笔记的一些使用方法和经验&#xff0c;温馨提示&#xff0c;本文有点长&#xff0c;约1.5w字&#xff0c;几十张图片&#xff0c;建议收藏查看。 一、MySql安装 下载地址:https://dev.mysql.com/downloads/ 在安装MySql之前&a…

我在VScode学Java(Java一维数组)

我的个人博客主页&#xff1a;如果\真能转义1️⃣说1️⃣的博客主页 关于Java基本语法学习---->可以参考我的这篇博客&#xff1a;(我在Vscode学Java) 我在VScode学Java(Java一维数组&#xff09; Java 一维数组 声明数组&#xff1a;先声明&#xff0c;后使用 动态分配内…

最长连续子序列---双指针

一、最长连续不重复子序列 核心思路&#xff1a; 遍历数组a中的每一个元素a[i], 对于每一个i&#xff0c;找到j使得双指针[j, i]维护的是以a[i]结尾的最长连续不重复子序列&#xff0c;长度为i - j 1, 将这一长度与r的较大者更新给r。 对于每一个i&#xff0c;如何确定…

XR技术在手术中的应用调研

虚拟现实、增强现实、混合现实等概念和技术是最近几年发展起来的&#xff0c;相信你对去年大火的元宇宙深有感触&#xff0c;元宇宙属于虚拟现实的技术范畴&#xff0c;头号玩家电影也让虚拟现实走进大众的视野中。早在2015年&#xff0c;笔者参加一次展会时就有接触&#xff0…

【数据库复习】第四章数据库恢复技术

一、事务 定义 一个数据库操作序列 一个不可分割的工作单位&#xff08;要么全做&#xff0c;要么不做&#xff09; 恢复和并发控制的基本单位 事务和程序比较 在关系数据库中&#xff0c;一个事务可以是一条或多条SQL语句,也可以包含一个或多个程序。 一个程序通常包含…

java倒序输出数字的方法

1.在输入框中输入一个整数&#xff0c;比如要输入“5”&#xff0c;需要输出倒序&#xff0c;可以使用数字键盘进行输入&#xff0c;也可以使用文本编辑器进行输入。 2.在命令行中输入“6”&#xff0c;如图所示。 3.选择一个字符串作为例子。使用字符串编辑器中的 reverse命令…

这个 Chrome 插件,让你的 ChatGPT 不再报错

ChatGPT的官网最近几天报错越来越频繁了&#xff0c;相信大家都发现了。 一旦你离开页面时间比较久&#xff0c;再度返回跟它进行对话&#xff0c;就会出现如下报错&#xff1a; 虽然这个报错信息以前也出现过&#xff0c;但现在的频率确实过高&#xff0c;对于每天需要使用 C…

“火灾不分昼夜,安全在我心中”——五一前厂房消防检查纪实

检查人员: Scott, Jason, Willson, Hanson 检查时间: 2023年4月28日 检查地点: 1厂房、2厂房室内外 检查内容: 一、室内外消火栓: 室内栓外观正常&#xff1b; 室外栓: 栓体防冻防尘套破损、遗失&#xff0c;消防栓缺少防撞保护&#xff1b; 按规定距离厂房外墙不宜小于5…

gitlab部署及整合Jenkins持续构建(四)sonarqube9.9安装和使用(一步一坑)

文章目录 postgresql13.0安装1、配置postgresql数据库2、进入postgresql创建数据库 代码质量管理平台--sonarqube安装1、前置依赖下载2、安装unzip并解压sonarqube并移动到/usr/local&#xff1a;3、修改sonarqube相应的配置4、新增用户&#xff0c;并将目录所属权赋予该用户&a…

回村准备结婚了~

小伙伴们大家好&#xff0c;我是阿秀。 这几天是一年一度的五一假期&#xff0c;前几年因为疫情的存在&#xff0c;很多人的五一假期都只能憋在家里&#xff0c;不知道今年各位有没有出去游玩的计划和打算&#xff1f; 昨晚刷朋友圈看到很多人都在晒行程的&#xff0c;有打算去…

【MATLAB数据处理实用案例详解(19)】——利用神经模糊控制实现对洗衣机的控制

目录 一、问题描述二、洗衣机模糊控制三、运行结果四、完整代码 一、问题描述 20世纪90年代初期&#xff0c;日本松下公司推出了神经模糊控制全自动洗衣机这种洗衣机能够自动判断衣物质地的软硬程度、衣量多少、脏污程度和性质等&#xff0c;应用神经模糊控制技术&#xff0c;…

JavaScript实现输入成绩,输出成绩等级的代码

以下为实现输入成绩&#xff0c;输出成绩等级&#xff08;switch语句&#xff09;的代码和运行截图 目录 前言 一、实现输入成绩&#xff0c;输出成绩等级 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录…

C#底层库--自定义进制转换器(可去除特殊字符,非Convert.ToString方式)

系列文章 C#底层库–程序日志记录类 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/124187709 C#底层库–MySQLBuilder脚本构建类&#xff08;select、insert、update、in、带条件的SQL自动生成&#xff09; 本文链接&#xff1a;https://blog.csd…

从南极到你家,易开得,一支“中国芯”的奇幻漂流

2023年的AWE&#xff0c;精彩程度比我想象中还要夸张&#xff01; 一方面是热度空前&#xff0c;现场人头攒动&#xff0c;有一家老中幼三代一起来观展的&#xff0c;有经销商、客户来问价采购的&#xff0c;还有行业媒体举着单反、手机、摄像机激情拍照的&#xff0c;让我们正…

4月30日第壹简报,星期日,农历三月十一

4月30日第壹简报&#xff0c;星期日&#xff0c;农历三月十一坚持阅读&#xff0c;静待花开1. 五一小长假首日全国铁路迎客流高峰&#xff0c;创铁路单日客流历史新高&#xff0c;旅游订单量较春节假期首日增长668%。2. 六大国有银行一季报业绩披露&#xff1a;共赚近3600亿元&…