Personnalisation du décodage des fonctionnalités

L'API tfds.decode vous permet de remplacer le décodage des fonctionnalités par défaut. Le cas d'utilisation principal consiste à ignorer le décodage de l'image pour de meilleures performances.

Exemples d'utilisation

Ignorer le décodage de l'image

Pour garder un contrôle total sur le pipeline de décodage ou pour appliquer un filtre avant que les images ne soient décodées (pour de meilleures performances), vous pouvez ignorer complètement le décodage de l'image. Cela fonctionne à la fois avec tfds.features.Image et tfds.features.Video .

ds = tfds.load('imagenet2012', split='train', decoders={
    'image': tfds.decode.SkipDecoding(),
})

for example in ds.take(1):
  assert example['image'].dtype == tf.string  # Images are not decoded

Filtrer/mélanger l'ensemble de données avant que les images ne soient décodées

De la même manière que dans l'exemple précédent, vous pouvez utiliser tfds.decode.SkipDecoding() pour insérer une personnalisation supplémentaire du pipeline tf.data avant de décoder l'image. De cette façon, les images filtrées ne seront pas décodées et vous pourrez utiliser un tampon de lecture aléatoire plus grand.

# Load the base dataset without decoding
ds, ds_info = tfds.load(
    'imagenet2012',
    split='train',
    decoders={
        'image': tfds.decode.SkipDecoding(),  # Image won't be decoded here
    },
    as_supervised=True,
    with_info=True,
)
# Apply filter and shuffle
ds = ds.filter(lambda image, label: label != 10)
ds = ds.shuffle(10000)
# Then decode with ds_info.features['image']
ds = ds.map(
    lambda image, label: ds_info.features['image'].decode_example(image), label)

Recadrage et décodage en même temps

Pour remplacer l'opération tf.io.decode_image par défaut, vous pouvez créer un nouvel objet tfds.decode.Decoder à l'aide du décorateur tfds.decode.make_decoder() .

@tfds.decode.make_decoder()
def decode_example(serialized_image, feature):
  crop_y, crop_x, crop_height, crop_width = 10, 10, 64, 64
  return tf.image.decode_and_crop_jpeg(
      serialized_image,
      [crop_y, crop_x, crop_height, crop_width],
      channels=feature.feature.shape[-1],
  )

ds = tfds.load('imagenet2012', split='train', decoders={
    # With video, decoders are applied to individual frames
    'image': decode_example(),
})

Ce qui équivaut à :

def decode_example(serialized_image, feature):
  crop_y, crop_x, crop_height, crop_width = 10, 10, 64, 64
  return tf.image.decode_and_crop_jpeg(
      serialized_image,
      [crop_y, crop_x, crop_height, crop_width],
      channels=feature.shape[-1],
  )

ds, ds_info = tfds.load(
    'imagenet2012',
    split='train',
    with_info=True,
    decoders={
        'image': tfds.decode.SkipDecoding(),  # Skip frame decoding
    },
)
ds = ds.map(functools.partial(decode_example, feature=ds_info.features['image']))

Personnalisation du décodage vidéo

Les vidéos sont Sequence(Image()) . Lors de l'application de décodeurs personnalisés, ils seront appliqués à des images individuelles. Cela signifie que les décodeurs d'images sont automatiquement compatibles avec la vidéo.

@tfds.decode.make_decoder()
def decode_example(serialized_image, feature):
  crop_y, crop_x, crop_height, crop_width = 10, 10, 64, 64
  return tf.image.decode_and_crop_jpeg(
      serialized_image,
      [crop_y, crop_x, crop_height, crop_width],
      channels=feature.feature.shape[-1],
  )

ds = tfds.load('ucf101', split='train', decoders={
    # With video, decoders are applied to individual frames
    'video': decode_example(),
})

Ce qui équivaut à :

def decode_frame(serialized_image):
  """Decodes a single frame."""
  crop_y, crop_x, crop_height, crop_width = 10, 10, 64, 64
  return tf.image.decode_and_crop_jpeg(
      serialized_image,
      [crop_y, crop_x, crop_height, crop_width],
      channels=ds_info.features['video'].shape[-1],
  )


def decode_video(example):
  """Decodes all individual frames of the video."""
  video = example['video']
  video = tf.map_fn(
      decode_frame,
      video,
      dtype=ds_info.features['video'].dtype,
      parallel_iterations=10,
  )
  example['video'] = video
  return example


ds, ds_info = tfds.load('ucf101', split='train', with_info=True, decoders={
    'video': tfds.decode.SkipDecoding(),  # Skip frame decoding
})
ds = ds.map(decode_video)  # Decode the video

Décodez uniquement un sous-ensemble des fonctionnalités.

Il est également possible d'ignorer complètement certaines fonctionnalités en spécifiant uniquement celles dont vous avez besoin. Toutes les autres fonctionnalités seront ignorées/ignorées.

builder = tfds.builder('my_dataset')
builder.as_dataset(split='train', decoders=tfds.decode.PartialDecoding({
    'image': True,
    'metadata': {'num_objects', 'scene_name'},
    'objects': {'label'},
})

TFDS sélectionnera le sous-ensemble de builder.info.features correspondant à la structure tfds.decode.PartialDecoding donnée.

Dans le code ci-dessus, les fonctionnalités sont implicitement extraites pour correspondre à builder.info.features . Il est également possible de définir explicitement les fonctionnalités. Le code ci-dessus est équivalent à :

builder = tfds.builder('my_dataset')
builder.as_dataset(split='train', decoders=tfds.decode.PartialDecoding({
    'image': tfds.features.Image(),
    'metadata': {
        'num_objects': tf.int64,
        'scene_name': tfds.features.Text(),
    },
    'objects': tfds.features.Sequence({
        'label': tfds.features.ClassLabel(names=[]),
    }),
})

Les métadonnées originales (noms des étiquettes, forme de l'image,...) sont automatiquement réutilisées il n'est donc pas nécessaire de les fournir.

tfds.decode.SkipDecoding peut être transmis à tfds.decode.PartialDecoding , via les kwargs PartialDecoding(..., decoders={}) .