keras 使用TensorFlow在MNIST数据集的特定标签上训练分类器

1wnzp6jl  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(149)

我想在MNIST数据集上训练一个分类器,但标签有限。例如,我想只在所有标签[0-9]中的标签[1,4,5,6,8,9]上训练分类器。我得到以下错误:
res = tf.nn.sparse_softmax_cross_entropy_with_logits( Node: 'sparse_categorical_crossentropy/SparseSoftmaxCrossEntropyWithLogits/SparseSoftmaxCrossEntropyWithLogits' Received a label value of 9 which is outside the valid range of [0, 6). Label values: 9 5 9 9 6 1 4 4 6 6 9 4 9 1 8 5 9 5 4 8 9 9 1 8 6 4 4 9 9 4 4 8 8 6 6 5 9 4 1 5 5 6 4 1 1 8 9 6 8 5 6 1 6 6 4 6 1 4 4 4 1 1 1 6 9 8 8 8 5 1 8 8 6 6 5 1 1 5 1 6 9 8 1 8 4 6 4 9 8 1 6 5 5 9 1 6 8 1 5 5 6 9 1 9 9 6 4 6 6 4 8 6 6 4 5 4 4 5 8 1 8 6 1 5 4 5 8 1
以下是我使用的方法:

import tensorflow_datasets as tfds
import tensorflow as tf

val_split = 20  # percent of training data

(ds_test, ds_valid, ds_train), ds_info = tfds.load(
    'mnist,
    split=['test', f'train[0%:{val_split}%]', f'train[{val_split}%:]'],
    as_supervised=True,
    with_info=True
)

ds_train数据集对象的每个标签具有以下样本
{0:4705,1:5433,2:4772,3:4936,4:4681,5:4333,6:4728,7:4966,8:4703,9:4743}。
在此之后,我使用filter()过滤数据集,如下所示:

known_classes = [1, 4, 5, 6, 8, 9]
kc = tf.constant(known_classes, dtype=tf.int64)

def predicate(image, label):
    isallowed = tf.equal(kc, label)
    reduced = tf.reduce_sum(tf.cast(isallowed, tf.int64))
    return tf.greater(reduced, tf.constant(0, dtype=tf.int64))

ds_test = ds_test.filter(predicate)
ds_valid = ds_valid.filter(predicate)
ds_train = ds_train.filter(predicate)

ds_train的每个标签后过滤器的更新样本为
{0:0,1:5433,2:0,3:0,4:四六八一,五:四三三三,六:4728,7:0,8:4703,9:4743}。
接下来的步骤是归一化图像并准备数据集对象进行训练。

def normalize_img(image, label):
    """Normalizes images: `uint8` -> `float32`."""
    return tf.cast(image, tf.float32) / 255., label

ds_train = ds_train.map(normalize_img, num_parallel_calls=tf.data.AUTOTUNE)
ds_train = ds_train.shuffle(ds_info.splits['train'].num_examples)
ds_train = ds_train.batch(128, drop_remainder=True)
ds_train = ds_train.prefetch(tf.data.AUTOTUNE)

ds_test = ds_test.map(normalize_img, num_parallel_calls=tf.data.AUTOTUNE)
ds_test = ds_test.batch(128, drop_remainder=True)
ds_test = ds_test.prefetch(tf.data.AUTOTUNE)

ds_valid = ds_valid.map(normalize_img, num_parallel_calls=tf.data.AUTOTUNE)
ds_valid = ds_valid.batch(128, drop_remainder=True)
ds_valid = ds_valid.prefetch(tf.data.AUTOTUNE)

此后,我创建了一个简单的模型,如下所示,然后继续训练

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(len(known_classes))  # known_classes from above
])

model.compile(
    optimizer=tf.keras.optimizers.Adam(0.001),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

history = model.fit(
    ds_train,
    epochs=5,
    validation_data=ds_valid,
)

我是Tensorflow的新手,任何帮助都很感激!!

z0qdvdin

z0qdvdin1#

你也必须改变训练标签,将它们Map到0-5范围而不是[1, 4, 5, 6, 8, 9],因为sparse_categorical_crossentropy期望它们在0-5范围内,因为你删除了其中的一些。
您可以在规格化图像时执行此操作,例如:

def make_fn(y):
    def fn(z):
        return tf.equal(z, tf.constant(y, dtype=tf.int64))
    return fn

def make_case(i):
    def case():
        return tf.constant(i, dtype=tf.int64)
    return case

label_cases = [(make_fn(y), make_case(i)) for i, y in enumerate(known_classes)]

def normalize_img_and_label(image, label):
    """Normalizes images and labels: `uint8` -> `float32`."""
    label = tf.case([(fn(label), case) for fn, case in label_cases], 
                    exclusive=False, default=lambda: label)

    return tf.cast(image, tf.float32) / 255., label

ds_train = ds_train.map(normalize_img_and_label, num_parallel_calls=tf.data.AUTOTUNE)
...

实际上,您也可以在ds_validds_test上执行此操作

**说明:**我们要实现以下剩余类标签的Map,即[1 -> 0, 4 -> 1, 5 -> 2, 6 -> 3, 8 -> 4, 9 -> 5],因为损失函数期望它们在0-5范围内。由于在tf.data.Dataset对象上操作的函数(例如normalize_img())在 * 符号Tensor上工作 * 我们不能直接使用带有整数键值对的dict来实现Map(如我以前的解决方案)。

normalize_img_and_label函数中,imagelabel都是符号化的,这意味着它们就像占位符一样,没有numpy值与之关联:这样的值在实际消费数据集对象时将是可用的。所以我们必须使用能够处理符号Tensor的tf原语。在这里,我使用tf.case来实现一个switch-case语句在符号Tensor上:

if label == 1:
  return 0
elif label == 4:
  return 1:
...
elif label == 9:
  return 5

要做到这一点,您需要定义一个函数对列表(即label_cases):每个标签一对。

  • make_fn创建一个closure,当调用时,例如fn(label),返回布尔条件tf.equal(z, y)的值-其中yknown_classes的一个值,z是提供的标签。
  • make_case产生另一个闭包,它返回一个常数Tensor,对应于switch情况下的返回值,即新Map的标签。

现在,在normalize_img_and_label内部,我们必须应用tf.case,因此我们循环函数对,产生一个新的对(布尔,Tensor),因为我们必须将labelTensor传递给tf.equal闭包(即fn。)例如,假设label = 4,列表[(fn(label), case) for fn, case in label_cases]实际上是:[(False, 0), (True, 1), (False, 2), ..., (False, 5)] .因此,tf.case将产生1,因为条件为真。

相关问题