我想在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的新手,任何帮助都很感激!!
1条答案
按热度按时间z0qdvdin1#
你也必须改变训练标签,将它们Map到0-5范围而不是
[1, 4, 5, 6, 8, 9]
,因为sparse_categorical_crossentropy
期望它们在0-5范围内,因为你删除了其中的一些。您可以在规格化图像时执行此操作,例如:
实际上,您也可以在
ds_valid
和ds_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
函数中,image
和label
都是符号化的,这意味着它们就像占位符一样,没有numpy值与之关联:这样的值在实际消费数据集对象时将是可用的。所以我们必须使用能够处理符号Tensor的tf原语。在这里,我使用tf.case来实现一个switch-case语句在符号Tensor上:要做到这一点,您需要定义一个函数对列表(即
label_cases
):每个标签一对。make_fn
创建一个closure,当调用时,例如fn(label)
,返回布尔条件tf.equal(z, y)
的值-其中y
是known_classes
的一个值,z
是提供的标签。make_case
产生另一个闭包,它返回一个常数Tensor,对应于switch情况下的返回值,即新Map的标签。现在,在
normalize_img_and_label
内部,我们必须应用tf.case
,因此我们循环函数对,产生一个新的对(布尔,Tensor),因为我们必须将label
Tensor传递给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
,因为条件为真。