Сопоставление каждого образца в tf.dataset с идентификатором - PullRequest
1 голос
/ 23 февраля 2020

В целях тестирования я хочу прикрепить идентификатор к каждому образцу в моем tf.dataset. Для этого достаточно просто считать вверх.

Мой набор данных имеет тип FlatMapDataset fwiw.

for entry in img_ds:
        print(entry.shape)

(128, 128, 3)
(128, 128, 3)
(128, 128, 3)
(128, 128, 3)
...

Я попытался создать функцию отображения, которая определяет счетчик внутри него и считает вверх:

@staticmethod
    def map_to_id(img):
        try:
            ExperimentalPipeline.map_to_id.id_counter += 1
        except AttributeError:
            ExperimentalPipeline.map_to_id.id_counter = 0
        return img, ExperimentalPipeline.map_to_id.id_counter

, а затем использует Dataset.map из tf.data для прикрепления идентификатора к каждому образцу:

img_ds = img_ds.map(ExperimentalPipeline.map_to_id)

К сожалению, это не работает, каждый образец получает нулевой идентификатор:

for i, id in img_ds:
        print(f"{i.shape}, {id}")

(128, 128 , 3), 0
(128, 128, 3), 0
(128, 128, 3), 0
(128, 128, 3), 0
...

Я также заметил, что моя функция map_to_id вызывается только один раз.

@staticmethod
def map_to_id(img):
    print("enter map_to_id")
    try:
        ExperimentalPipeline.map_to_id.id_counter += 1
    except AttributeError:
        print("caught exception")
        ExperimentalPipeline.map_to_id.id_counter = np.random.randint(1000)
    return img, ExperimentalPipeline.map_to_id.id_counter

enter map_to_id
пойманное исключение
(128, 128, 3 ), 889
(128, 128, 3), 889
(128, 128, 3), 889
(128, 128, 3), 889

Я думаю, Я не понимаю, как Dataset.map должен работать. Я думал, что он возьмет каждый образец в наборе данных, к которому он вызывается, и вызовет поставляемую функцию с образцом в качестве аргумента.
Может кто-нибудь помочь мне разобраться в этом?

Ответы [ 2 ]

1 голос
/ 24 февраля 2020

TensorFlow запустит функцию map один раз, чтобы скомпилировать функцию в операции TensorFlow. Тогда эти операции, а не исходная функция python, будут применены к каждому элементу набора данных. Если вы хотите запустить исходную функцию python для каждого элемента, вы можете использовать py_function .

В этом конкретном c случае, когда вы хотите присоединить идентификаторы элементов, вы можете используйте Dataset.enumerate для достижения вашей цели:

img_ds = img_ds.enumerate()
0 голосов
/ 24 февраля 2020

Хорошо, так что после прочтения документации по тензорному потоку я обнаружил, что это так:

Обратите внимание, что независимо от контекста, в котором определяется map_fun c (стремление к графику), трассировка tf.data функция и выполняет его в виде графика. Чтобы использовать код Python внутри функции, у вас есть две опции:

1) Положитесь на AutoGraph, чтобы преобразовать код Python в эквивалентное вычисление графа. Недостатком этого подхода является то, что AutoGraph может преобразовывать некоторый, но не весь код Python.

2) Использовать функцию tf.py_function, которая позволяет писать произвольный код Python, но, как правило, приводит к ухудшению производительности, чем 1)

Таким образом, функция map_to_id действительно отслеживается только один раз.
Поскольку вариант 1) не работает, я просто использую вариант 2). Мне просто нужен идентификатор для некоторых модульных тестов, поэтому производительность не должна быть проблемой.

Решение выглядит следующим образом:

img_ds = img_ds.map(
    lambda img: tf.py_function(
        func=ExperimentalPipeline.map_to_id, inp=[img], Tout=(tf.float32, tf.int32)
    )
)
...