Используя конвейер набора данных tensorflow, как мне * назвать * результаты операции «карта»?

У меня есть функция карты ниже (исполняемый пример), которая вводит string и выводит string и integer.

в tf.data.Dataset.from_tensor_slices я назвал исходный ввод 'filenames'. Но когда я возвращаю значения из функции карты map_element_counts, я могу вернуть только кортеж (возврат словаря генерирует исключение).

Есть ли способ назвать 2 элемента, возвращаемые моей функцией map_element_counts?

import tensorflow as tf

filelist = ['fileA_6', 'fileB_10', 'fileC_7']

def map_element_counts(fname):
  # perform operations outside of tensorflow
  return 'test', 10

ds = tf.data.Dataset.from_tensor_slices({'filenames': filelist})
ds = ds.map(map_func=lambda x: tf.py_func(
  func=map_element_counts, inp=[x['filenames']], Tout=[tf.string, tf.int64]
))
element = ds.make_one_shot_iterator().get_next()

with tf.Session() as sess:
  print(sess.run(element))

Результат:

(b'test', 10)

Желаемый результат:

{'elementA': b'test', 'elementB': 10)

Добавлено:

Когда я делаю return {'elementA': 'test', 'elementB': 10}, я получаю это исключение:

tensorflow.python.framework.errors_impl.UnimplementedError: Unsupported object type dict

person David Parks    schedule 27.01.2018    source источник
comment
Что является исключением, когда вы возвращаете словарь?   -  person Alexandre Passos    schedule 29.01.2018
comment
Я добавил это в конец вопроса.   -  person David Parks    schedule 29.01.2018
comment
Можете выложить полную трассировку стека?   -  person Alexandre Passos    schedule 01.02.2018


Ответы (3)


Применение tf.py_func внутри ds.map работает.

В качестве примера я создал очень простой файл. Где я просто пишу 10 внутри.

фиктивный_файл.txt:

10

Вот для скрипта:

import tensorflow as tf

filelist = ['dummy_file.txt', 'dummy_file.txt', 'dummy_file.txt']


def py_func(input):
    # perform operations outside of tensorflow
    parsed_txt_file = int(input)
    return 'test', parsed_txt_file


def map_element_counts(fname):
    # let tensorflow read the text file
    file_string = tf.read_file(fname['filenames'])
    # then use python function on the extracted string
    a, b = tf.py_func(
                    func=py_func, inp=[file_string], Tout=[tf.string, tf.int64]
                    )
    return {'elementA': a, 'elementB': b, 'file': fname['filenames']}

ds = tf.data.Dataset.from_tensor_slices({'filenames': filelist})
ds = ds.map(map_element_counts)
element = ds.make_one_shot_iterator().get_next()

with tf.Session() as sess:
    print(sess.run(element))
    print(sess.run(element))
    print(sess.run(element))

Выход:

{'file': b'dummy_file.txt', 'elementA': b'test', 'elementB': 10}
{'file': b'dummy_file.txt', 'elementA': b'test', 'elementB': 10}
{'file': b'dummy_file.txt', 'elementA': b'test', 'elementB': 10}
person rAyyy    schedule 01.02.2018

Я предлагаю окончательное решение этого вопроса для потомков. Приведенный ниже код представляет собой пример копирования/вставки, который работает в самых сложных условиях, к которым относится этот вопрос (обратите внимание, что два других ответа не являются образцами кода для копирования/вставки):

Цель кода:

  • Возьмите список (больших) файлов и разбейте его на куски (пары имя файла/индекс)
  • Обработайте каждый фрагмент с помощью операции сопоставления (здесь генераторы не подходят, см.: https://github.com/tensorflow/tensorflow/issues/16343)
  • Выведите несколько выборок из операции карты, которая принимает только 1 файл/фрагмент в качестве входных данных.
  • Сохраняйте имена элементов на протяжении всего процесса

Скопируйте/вставьте рабочий пример для Tensorflow 1.5/Python 3.x

import tensorflow as tf
import numpy as np

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

def mymap1(x):
  result_tensors = tf.py_func(func=mymap2, inp=[x], Tout=[tf.string, tf.int64])
  return {'filename': result_tensors[0], 'value': result_tensors[1]}

def mymap2(x):
  return np.array([x, x, x]), np.array([10, 20, 30])

def myflatmap(named_elements):
  return tf.data.Dataset.zip({
    'filename': tf.data.Dataset.from_tensor_slices(named_elements['filename']),
    'value': tf.data.Dataset.from_tensor_slices(named_elements['value'])
  })

ds = tf.data.Dataset.from_tensor_slices(files)
ds = ds.map(map_func=mymap1)
ds = ds.flat_map(map_func=myflatmap)

element = ds.make_one_shot_iterator().get_next()

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

Выход:

{'filename': b'testA', 'value': 10}
{'filename': b'testA', 'value': 20}
{'filename': b'testA', 'value': 30}
{'filename': b'testB', 'value': 10}
{'filename': b'testB', 'value': 20}
{'filename': b'testB', 'value': 30}
{'filename': b'testC', 'value': 10}
{'filename': b'testC', 'value': 20}
{'filename': b'testC', 'value': 30}
person David Parks    schedule 02.02.2018

В этом случае нет необходимости в tf.py_func, потому что map_func из Dataset#map работает со словарями и другими структурами:

map_func: функция, отображающая вложенную структуру тензоров (с формами и типами, определенными self.output_shapes и self.output_types) в другую вложенную структуру тензоров.

Вот пример:

import tensorflow as tf

filelist = ['fileA_6', 'fileB_10', 'fileC_7']

def map_element_counts(fnames):
  return {'elementA': b'test', 'elementB': 10, 'file': fnames['filenames']}

ds = tf.data.Dataset.from_tensor_slices({'filenames': filelist})
ds = ds.map(map_func=map_element_counts)
element = ds.make_one_shot_iterator().get_next()

with tf.Session() as sess:
  print(sess.run(element))
  print(sess.run(element))
  print(sess.run(element))

Выход:

{'elementA': 'test', 'elementB': 10, 'file': 'fileA_6'}
{'elementA': 'test', 'elementB': 10, 'file': 'fileB_10'}
{'elementA': 'test', 'elementB': 10, 'file': 'fileC_7'}
person Maxim    schedule 30.01.2018
comment
Предполагая, что map_element_counts необходимо выполнять функции, которые невозможны в тензорном потоке (например, чтение из пользовательского формата файла), должен ли я затем обернуть tf.py_func с map_element_counts, чтобы я мог вернуть словарь из map_element_counts после того, как tf.py_func вернет значения тензора в виде безымянного кортежа ? Проблема в том, что fnames приходит как тензор, но мне нужно преобразовать его в строку для обработки. - person David Parks; 30.01.2018
comment
Можете ли вы применить функцию внутри map? fnames['filenames'] является тензором - person Maxim; 30.01.2018
comment
Да, это работает, как правильно указывает rAyyy на примере. - person David Parks; 02.02.2018