web-dev-qa-db-fra.com

Dans l'API de jeu de données de Tensorflow, comment mapper un élément en plusieurs éléments?

Dans le pipeline tensorflow Dataset, j'aimerais définir une fonction de carte personnalisée qui prend un seul élément d'entrée (échantillon de données) et renvoie plusieurs éléments (échantillons de données).

Le code ci-dessous est ma tentative, avec les résultats souhaités.

Je ne pouvais pas suivre suffisamment la documentation sur tf.data.Dataset().flat_map() pour savoir si elle était applicable ici ou non.

import tensorflow as tf

input = [10, 20, 30]

def my_map_func(i):
  return [[i, i+1, i+2]]       # Fyi [[i], [i+1], [i+2]] throws an exception

ds = tf.data.Dataset.from_tensor_slices(input)
ds = ds.map(map_func=lambda input: tf.py_func(
  func=my_map_func, inp=[input], Tout=[tf.int64]
))
element = ds.make_one_shot_iterator().get_next()

with tf.Session() as sess:
  for _ in range(9):
    print(sess.run(element))

Résultats:

(array([10, 11, 12]),)
(array([20, 21, 22]),)
(array([30, 31, 32]),)

Les résultats souhaités:

(10)
(11)
(12)
(20)
(21)
(22)
(30)
(31)
(32)
10
David Parks

Deux étapes supplémentaires étaient nécessaires pour y parvenir. Premièrement, la fonction map doit renvoyer un tableau numpy, pas une liste.

Ensuite, vous pouvez utiliser flat_map combiné avec Dataset().from_tensor_slices() pour les aplatir. Le code ci-dessous produit maintenant le résultat souhaité:

Testé dans Tensorflow 1.5 (exemple exécutable copier/coller)

import tensorflow as tf
import numpy as np

input = [10, 20, 30]

def my_map_func(i):
  return np.array([i, i + 1, i + 2])

ds = tf.data.Dataset.from_tensor_slices(input)
ds = ds.map(map_func=lambda input: tf.py_func(
  func=my_map_func, inp=[input], Tout=[tf.int64]
))
ds = ds.flat_map(lambda x: tf.data.Dataset().from_tensor_slices(x))

element = ds.make_one_shot_iterator().get_next()

with tf.Session() as sess:
  for _ in range(9):
    print(sess.run(element))

Voici une méthode pour ce faire si vous avez plusieurs variables à renvoyer. Dans cet exemple, je saisis une chaîne (telle qu'un nom de fichier) et les multiples en sortie de chaînes et d'entiers. Dans ce cas, je répète la chaîne pour chacun des entiers de [10, 20, 30]. 

Copier/coller un exemple exécutable:

import tensorflow as tf
import numpy as np

input = [b'testA', b'testB', b'testC']

def my_map_func(input):
  return np.array([input, input, input]), np.array([10, 20, 30])

ds = tf.data.Dataset.from_tensor_slices(input)
ds = ds.map(map_func=lambda input: tf.py_func(
    func=my_map_func, inp=[input], Tout=[tf.string, tf.int64]))
ds = ds.flat_map(lambda mystr, myint: tf.data.Dataset().Zip((
  tf.data.Dataset().from_tensor_slices(mystr),
  tf.data.Dataset().from_tensor_slices(myint))
))

element = ds.make_one_shot_iterator().get_next()

with tf.Session() as sess:
  for _ in range(9):
    print(sess.run(element))
4
David Parks

une solution propre utilisant flat_map et from_tensor_slices

import tensorflow as tf

input = [10, 20, 30]

ds = tf.data.Dataset.from_tensor_slices(input)
ds = ds.flat_map(lambda x: tf.data.Dataset.from_tensor_slices([x, x+1, x+2]))
element = ds.make_one_shot_iterator().get_next()

with tf.Session() as sess:
    for _ in range(9):
        print(sess.run(element))

# 10
# 11
# 12
# 20
# 21
# 22
# 30
# 31
# 32
1
BugKiller