为 Tensorflow 模型选择损失和指标

Nic*_*e11 7 machine-learning deep-learning keras tensorflow tensorflow2.0

我正在尝试使用预训练的Xception模型和新添加的分类器进行迁移学习。

这是模型:

base_model = keras.applications.Xception(
    weights="imagenet",
    input_shape=(224,224,3),
    include_top=False
)
Run Code Online (Sandbox Code Playgroud)

我使用的数据集oxford_flowers102直接取自张量流数据集。 是一个数据集页面。

我在选择某些参数时遇到问题- 要么训练准确性显示可疑的低值,要么存在错误。

我需要帮助指定此参数,对于这个(oxford_flowers102)数据集:

  1. 为分类器新添加了密集层。我正在尝试: outputs = keras.layers.Dense(102, activation='softmax')(x)并且我不确定是否应该在此处选择激活功能。
  2. 模型的损失函数。
  3. 指标。

我试过:

model.compile(
    optimizer=keras.optimizers.Adam(),
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[keras.metrics.Accuracy()],
)
Run Code Online (Sandbox Code Playgroud)

我不确定它是否应该是SparseCategoricalCrossentropyCategoricalCrossentropy,参数呢from_logits

我也不确定是否应该选择指标keras.metrics.Accuracy()keras.metrics.CategoricalAccuracy()

我确实缺乏一些理论知识,但现在我只需要这个就能工作。期待您的答复!

M.I*_*nat 12

关于数据集:oxford_flowers102

数据集分为训练集验证集测试集。训练集和验证集每类包含10 个图像(每个类总共1020 个图像)。测试集由剩余的6149 张图像组成(每类至少20 张)。

'test'        6,149
'train'       1,020
'validation'  1,020
Run Code Online (Sandbox Code Playgroud)

如果我们检查一下,我们会看到

import tensorflow_datasets as tfds
tfds.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)
Run Code Online (Sandbox Code Playgroud)
ds_info.features["label"].num_classes
102
Run Code Online (Sandbox Code Playgroud)

因此,它有102 个类别或类,目标带有一个具有不同形状输入的整数。

澄清

首先,如果保留这个整数目标或标签,则应该用于sparse_categorical_accuracy准确性和sparse_categorical_crossentropy损失函数。但是,如果您将整数标签转换为one-hot 编码向量,那么您应该用于categorical_accuracy准确性和categorical_crossentropy损失函数。由于这些数据集具有整数标签,您可以选择sparse_categorical或将标签转换为 one-hot 以便使用categorical

其次,如果设置outputs = keras.layers.Dense(102, activation='softmax')(x)为最后一层,您将获得概率分数。但如果你设置了outputs = keras.layers.Dense(102)(x),那么你将得到logits。因此,如果您设置了activations='softmax',那么您就不应该使用from_logit = True. 例如,在上面的代码中,您应该执行以下操作(这里有一些理论):

...
(a)
# Use softmax activation (no logits output)
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)
# no activation, output will be logits
outputs = keras.layers.Dense(102)(x)
...
model.compile(
    optimizer=keras.optimizers.Adam(),
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[keras.metrics.Accuracy()],
)
Run Code Online (Sandbox Code Playgroud)

第三使用字符串标识符,例如metrics=['acc'] , optimizer='adam'. 但就您而言,当您提到特定于损失函数时,您需要更具体一些。因此,keras.metrics.Accuracy()您应该选择keras.metrics.SparseCategoricalAccuracy() 目标是否为整数,或者keras.metrics.CategoricalAccuracy() 目标是否为 one-hot 编码向量,而不是选择

代码示例

这是一个端到端的示例。请注意,我会将整数标签转换为 one-hot 编码向量(现在,这对我来说是一个偏好问题)。另外,我想要最后一层的概率from_logits = False(而不是逻辑),这意味着. 对于所有这些,我需要在训练中选择以下参数:

# use softmax to get probabilities 
outputs = keras.layers.Dense(102, 
                   activation='softmax')(x)

# so no logits, set it false (FYI, by default it already false)
loss = keras.losses.CategoricalCrossentropy(from_logits=False),

# specify the metrics properly 
metrics = keras.metrics.CategoricalAccuracy(),
Run Code Online (Sandbox Code Playgroud)

让我们完成整个代码。

import tensorflow_datasets as tfds
tfds.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_classes
train_size =  len(data['train'])

batch_size = 64
img_size = 120 
Run Code Online (Sandbox Code Playgroud)

预处理和增强

import tensorflow as tf 

# pre-process functions 
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) # int to one-hot
    return image, label

# augmentation 
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)
Run Code Online (Sandbox Code Playgroud)

模型

from tensorflow import keras 

base_model = keras.applications.Xception(
    weights='imagenet',  
    input_shape=(img_size, img_size, 3),
    include_top=False)  

base_model.trainable = False
inputs = 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)
Run Code Online (Sandbox Code Playgroud)

好吧,另外,这里我喜欢使用两个指标来计算top-1top-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)
Run Code Online (Sandbox Code Playgroud)
...
Epoch 19/20
15/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.6186

Epoch 20/20
15/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
Run Code Online (Sandbox Code Playgroud)

评价

# evaluate on test set 
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]
Run Code Online (Sandbox Code Playgroud)