Как сопоставить строку с int (номером класса) как часть tf.data.Dataset .map() для предварительной обработки в Tensorflow?

#python #tensorflow #data-preprocessing

Вопрос:

Я пытаюсь создать функцию синтаксического анализа, которая считывает пару (изображение, метка) из TFRecord. Когда метка является int64, все работает хорошо, однако, когда я пытаюсь сохранить метку в виде строки и преобразовать ее в int в функции синтаксического анализатора, все ломается. Я все еще новичок в фреймворке, поэтому в моей реализации ниже используется eager .numpy(), который не работает, когда я применяю .map к tf.data .Dataset.

Некоторая справочная информация: у меня есть иерархии в моем наборе данных, очень похожие на этот пример, где я использовал «dog» и «labrador» для представления этого. Я хотел бы разрешить обучать данные, которые могут классифицировать категориальные изображения (кошка, собака), а также обучать модели, которые могут классифицировать более конкретные типы (бульдог, лабрадор) из тех же помеченных данных, которые обычно имеют метки наиболее конкретного типа для этого на изображении.

Это то, что я пытался сделать:

 #!/usr/bin/env python3
import tensorflow as tf
import functools

# example: this model classifies dogs and cats. this map translates a specific dog type to the "dog" label, 
# and the same for cats. all other labels would go to the negative class "neg".
example_label_map = {
    "bulldog": "dog",
    "labrador": "dog",
    "persian cat": "cat",
    "cow": "neg",
}

# map the N classes to an int of 0..N-1
labels_to_class_int = {
    "neg": 0,   # negative class
    "dog": 1,
    "cat": 2,
}

def parse_tfrec_function(example, labels_map):
    """
    labels is a map of labels to class numbers. e.g. "bulldog" -> 1, "labrador" -> 1, "persian cat" -> 2, ...
    """
    image_feature_description = {
        "image": tf.io.FixedLenFeature([], tf.string),
        "label": tf.io.FixedLenFeature([], tf.string)
    }

    features = tf.io.parse_single_example(example, image_feature_description)

    image = tf.io.parse_tensor(features["image"], tf.uint8)
    
    # *** the following fails. how can this be done efficiently (lazy)?
    label = features["label"].numpy().decode('utf-8')
    class_num = labels_map[label]

    return image, class_num


tfrec_file = ['/path/to/dataset.tfrec']
dataset = tf.data.TFRecordDataset(tfrec_file)

labels_map = {}
for k, v in example_label_map.items():
    labels_map[k] = labels_to_class_int[v]

print(labels_map)

parser_fn = functools.partial(parse_tfrec_function, labels_map=labels_map)
parset_dataset = dataset.map(parser_fn)

 

Причина сбоя, поскольку он пытается быстро запустить:

 AttributeError: 'Tensor' object has no attribute 'numpy'
 

Как правильно это сделать? Сохранение номеров классов в TFRecord потребовало бы создания разных TFRecords с практически одинаковыми данными, если бы я хотел обучить сеть, которая более специфична для различения определенного подмножества классов.

Как можно эффективно преобразовать строку метки в номер класса, учитывая сопоставление, которое выполняется как шаг предварительной обработки (.map для tf.data.Набор данных)?

Спасибо!

Комментарии:

1. Привет, ты смог это решить?

2. @VivekPayasi, Нет. Я обошел это, создав несколько наборов данных, каждый с нужным подмножеством. С тем объемом данных, который мне был нужен для обучения, мне это сошло с рук.

Ответ №1:

IIUC вопрос на самом деле заключается в переназначении строки на int в режиме графика. Я думаю, что должно быть возможно выполнить это переназначение string->int с использованием tf.case.

Пример:

 import tensorflow.compat.v1 as tf                                               
tf.disable_eager_execution()                                                    
                                                                             
                                                                                
def remap_string_to_int(tensor: tf.Tensor) -> tf.Tensor:                        
    pred_fn_pairs = [                                                           
        (tf.equal(tensor, 'neg'), lambda: 0),                                   
        (tf.equal(tensor, 'dog'), lambda: 1),                                   
        (tf.equal(tensor, 'cat'), lambda: 2),                                   
    ]                                                                           
    return tf.case(pred_fn_pairs)                           
                                                                                                                       
with tf.Session() as sess:                                                      
    label_tensor = tf.placeholder(tf.string, [])                                
    label_int_tensor = remap_string_to_int(label_tensor)                        
    print("int-value is: ",                                                     
          sess.run(label_int_tensor, feed_dict={label_tensor: 'dog'}))

 

С принтами:

 >>> int-value is:  1