我正在尝试进行迁移学习,使用预训练的Xception模型并添加一个新的分类器。
这是我的模型:
base_model = keras.applications.Xception( weights="imagenet", input_shape=(224,224,3), include_top=False)
我使用的数据集是oxford_flowers102
,直接从tensorflow数据集中获取。这是数据集页面。
我在选择一些参数时遇到了问题 – 要么训练准确率显示出可疑的低值,要么出现错误。
我需要帮助来指定这些参数,适用于这个(oxford_flowers102)数据集:
- 为分类器添加新的全连接层。我尝试使用:
outputs = keras.layers.Dense(102, activation='softmax')(x)
,我不知道是否应该在这里选择激活函数。 - 模型的损失函数。
- 评估指标。
我尝试过:
model.compile( optimizer=keras.optimizers.Adam(), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=[keras.metrics.Accuracy()],)
我不确定应该选择SparseCategoricalCrossentropy
还是CategoricalCrossentropy
,以及from_logits
参数的用法?
我也不确定在评估指标中是应该选择keras.metrics.Accuracy()
还是keras.metrics.CategoricalAccuracy()
?
我确实缺乏一些理论知识,但现在我只需要让它工作。期待您的回答!
回答:
关于数据集:oxford_flowers102
该数据集被分为训练集、验证集和测试集。训练集和验证集各包含每个类别的10张图片(共1020张图片)。测试集包含剩余的6149张图片(每个类别至少20张)。
'test' 6,149'train' 1,020'validation' 1,020
如果我们检查,会看到
import tensorflow_datasets as tfdstfds.disable_progress_bar()data, ds_info = tfds.load('oxford_flowers102', with_info=True, as_supervised=True)train_ds, valid_ds, test_ds = data['train'], data['validation'], data['test']for i, data in enumerate(train_ds.take(3)): print(i+1, data[0].shape, data[1])1 (500, 667, 3) tf.Tensor(72, shape=(), dtype=int64)2 (500, 666, 3) tf.Tensor(84, shape=(), dtype=int64)3 (670, 500, 3) tf.Tensor(70, shape=(), dtype=int64)
ds_info.features["label"].num_classes102
所以,它有102个类别或分类,目标是以整数形式给出,输入形状各不相同。
澄清
首先,如果你保留这个整数目标或标签,你应该使用sparse_categorical_accuracy
来计算准确率,并使用sparse_categorical_crossentropy
作为损失函数。但如果你将整数标签转换为独热编码向量,那么你应该使用categorical_accuracy
来计算准确率,并使用categorical_crossentropy
作为损失函数。由于这些数据集有整数标签,你可以选择sparse_categorical
,或者你可以将标签转换为独热编码以使用categorical
。
其次,如果你将最后一层的输出设置为outputs = keras.layers.Dense(102, activation='softmax')(x)
,你将获得概率分数。但如果你设置为outputs = keras.layers.Dense(102)(x)
,那么你将获得logits。因此,如果你设置了activations='softmax'
,那么你不应该使用from_logit = True
。例如,在你上面的代码中,你应该按如下方式做(这里有一些理论供你参考):
...(a)# 使用softmax激活函数(无logits输出)outputs = keras.layers.Dense(102, activation='softmax')(x)...model.compile( optimizer=keras.optimizers.Adam(), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics=[keras.metrics.Accuracy()],)or,(b)# 无激活函数,输出将是logitsoutputs = keras.layers.Dense(102)(x)...model.compile( optimizer=keras.optimizers.Adam(), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=[keras.metrics.Accuracy()],)
第三,keras使用字符串标识符,例如metrics=['acc'] , optimizer='adam'
。但在你的情况下,你需要更具体,因为你提到了特定的损失函数。因此,代替keras.metrics.Accuracy()
,你应该选择keras.metrics.SparseCategoricalAccuracy()
如果你的目标是整数,或者选择keras.metrics.CategoricalAccuracy()
如果你的目标是独热编码向量。
代码示例
这是一个端到端的示例。注意,我将将整数标签转换为独热编码向量(目前,这对我来说只是偏好问题)。此外,我希望从最后一层获得概率(而不是logits),这意味着from_logits = False
。对于所有这些,我需要在训练中选择以下参数:
# 使用softmax获取概率 outputs = keras.layers.Dense(102, activation='softmax')(x)# 因此没有logits,设置为false(顺便说一下,默认情况下它已经是false)loss = keras.losses.CategoricalCrossentropy(from_logits=False),# 正确指定评估指标 metrics = keras.metrics.CategoricalAccuracy(),
让我们完成整个代码。
import tensorflow_datasets as tfdstfds.disable_progress_bar()data, ds_info = tfds.load('oxford_flowers102', with_info=True, as_supervised=True)train_ds, valid_ds, test_ds = data['train'], data['validation'], data['test']NUM_CLASSES = ds_info.features["label"].num_classestrain_size = len(data['train'])batch_size = 64img_size = 120
预处理和增强
import tensorflow as tf # 预处理函数 def normalize_resize(image, label): image = tf.cast(image, tf.float32) image = tf.divide(image, 255) image = tf.image.resize(image, (img_size, img_size)) label = tf.one_hot(label , depth=NUM_CLASSES) # 整数转换为独热编码 return image, label# 增强函数 def augment(image, label): image = tf.image.random_flip_left_right(image) return image, label train = train_ds.map(normalize_resize).cache().map(augment).shuffle(100).\ batch(batch_size).repeat()valid = valid_ds.map(normalize_resize).cache().batch(batch_size)test = test_ds.map(normalize_resize).cache().batch(batch_size)
模型
from tensorflow import keras base_model = keras.applications.Xception( weights='imagenet', input_shape=(img_size, img_size, 3), include_top=False) base_model.trainable = Falseinputs = keras.Input(shape=(img_size, img_size, 3))x = base_model(inputs, training=False)x = keras.layers.GlobalAveragePooling2D()(x)outputs = keras.layers.Dense(NUM_CLASSES, activation='softmax')(x)model = keras.Model(inputs, outputs)
好的,此外,我还想使用两个评估指标来计算top-1
和top-3
准确率。
model.compile(optimizer=keras.optimizers.Adam(), loss=keras.losses.CategoricalCrossentropy(), metrics=[ keras.metrics.TopKCategoricalAccuracy(k=3, name='acc_top3'), keras.metrics.TopKCategoricalAccuracy(k=1, name='acc_top1') ])model.fit(train, steps_per_epoch=train_size // batch_size, epochs=20, validation_data=valid, verbose=2)
...Epoch 19/2015/15 - 2s - loss: 0.2808 - acc_top3: 0.9979 - acc_top1: 0.9917 - val_loss: 1.5025 - val_acc_top3: 0.8147 - val_acc_top1: 0.6186Epoch 20/2015/15 - 2s - loss: 0.2743 - acc_top3: 0.9990 - acc_top1: 0.9885 - val_loss: 1.4948 - val_acc_top3: 0.8147 - val_acc_top1: 0.6255
评估
# 在测试集上评估 model.evaluate(test, verbose=2)97/97 - 18s - loss: 1.6482 - acc_top3: 0.7733 - acc_top1: 0.5994[1.648208498954773, 0.7732964754104614, 0.5994470715522766]