Как хранить массивы NumPy как Tfrecord?

Я пытаюсь создать набор данных в формате tfrecord из numy массивов. Я пытаюсь сохранить 2D и 3D координаты.

2d координаты являются массивом формы (2,10) типа float64 3d координаты являются массивом формы (3,10) типа float64

это мой код:

def _floats_feature(value):
    return tf.train.Feature(float_list=tf.train.FloatList(value=value))


train_filename = 'train.tfrecords'  # address to save the TFRecords file
writer = tf.python_io.TFRecordWriter(train_filename)


for c in range(0,1000):

    #get 2d and 3d coordinates and save in c2d and c3d

    feature = {'train/coord2d': _floats_feature(c2d),
                   'train/coord3d': _floats_feature(c3d)}
    sample = tf.train.Example(features=tf.train.Features(feature=feature))
    writer.write(sample.SerializeToString())

writer.close()

когда я запускаю это, я получаю ошибку:

  feature = {'train/coord2d': _floats_feature(c2d),
  File "genData.py", line 19, in _floats_feature
return tf.train.Feature(float_list=tf.train.FloatList(value=value))
  File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\site-packages\google\protobuf\internal\python_message.py", line 510, in init
copy.extend(field_value)
  File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\site-packages\google\protobuf\internal\containers.py", line 275, in extend
new_values = [self._type_checker.CheckValue(elem) for elem in elem_seq_iter]
  File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\site-packages\google\protobuf\internal\containers.py", line 275, in <listcomp>
new_values = [self._type_checker.CheckValue(elem) for elem in elem_seq_iter]
  File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\site-packages\google\protobuf\internal\type_checkers.py", line 109, in CheckValue
raise TypeError(message)
TypeError: array([-163.685,  240.818, -114.05 , -518.554,  107.968,  427.184,
    157.418, -161.798,   87.102,  406.318]) has type <class 'numpy.ndarray'>, but expected one of: ((<class 'numbers.Real'>,),)

Я не знаю, как это исправить. я должен хранить функции как int64 или байты? Я понятия не имею, как это сделать, так как я совершенно новичок в tenorflow. любая помощь будет отличной! Спасибо

3 ответа

Функция _floats_featureописанный в Tensorflow-Guide, ожидает скаляр (float32 или float64) в качестве входных данных.

def _float_feature(value):
  """Returns a float_list from a float / double."""
  return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))

Как видите, введенный скаляр записывается в список (value=[value]), который впоследствии передается tf.train.FloatList как вход. tf.train.FloatList ожидает итератор, который выводит число с плавающей запятой на каждой итерации (как это делает список).

Если ваша функция не скаляр, а вектор, _float_feature можно переписать, чтобы передать итератор непосредственно в tf.train.FloatList (вместо того, чтобы сначала помещать его в список).

def _float_array_feature(value):
  return tf.train.Feature(float_list=tf.train.FloatList(value=value))

Однако, если ваша функция имеет два или более измерения, это решение больше не работает. Как и @mmry, описанный в его ответе, в этом случае решением будет сглаживание вашей функции или ее разделение на несколько одномерных функций. Недостатком этих двух подходов является то, что информация о фактической форме элемента теряется, если не прилагать дополнительных усилий.

Другая возможность написать пример сообщения для многомерного массива - это преобразовать массив в байтовую строку, а затем использовать _bytes_featureфункция, описанная в Tensorflow-Guide, чтобы написать для нее пример сообщения. Затем пример сообщения сериализуется и записывается в файл TFRecord.

import tensorflow as tf
import numpy as np

def _bytes_feature(value):
    """Returns a bytes_list from a string / byte."""
    if isinstance(value, type(tf.constant(0))): # if value ist tensor
        value = value.numpy() # get value of tensor
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))


def serialize_array(array):
  array = tf.io.serialize_tensor(array)
  return array


#----------------------------------------------------------------------------------
# Create example data
array_blueprint = np.arange(4, dtype='float64').reshape(2,2)
arrays = [array_blueprint+1, array_blueprint+2, array_blueprint+3]

#----------------------------------------------------------------------------------
# Write TFrecord file
file_path = 'data.tfrecords'
with tf.io.TFRecordWriter(file_path) as writer:
  for array in arrays:
    serialized_array = serialize_array(array)
    feature = {'b_feature': _bytes_feature(serialized_array)}
    example_message = tf.train.Example(features=tf.train.Features(feature=feature))
    writer.write(example_message.SerializeToString())

Доступ к сериализованным примерам сообщений, хранящимся в файле TFRecord, можно получить через tf.data.TFRecordDataset. После того, как примеры сообщений были проанализированы, необходимо восстановить исходный массив из байтовой строки, в которую он был преобразован. Это возможно черезtf.io.parse_tensor.

# Read TFRecord file
def _parse_tfr_element(element):
  parse_dic = {
    'b_feature': tf.io.FixedLenFeature([], tf.string), # Note that it is tf.string, not tf.float32
    }
  example_message = tf.io.parse_single_example(element, parse_dic)

  b_feature = example_message['b_feature'] # get byte string
  feature = tf.io.parse_tensor(b_feature, out_type=tf.float64) # restore 2D array from byte string
  return feature


tfr_dataset = tf.data.TFRecordDataset('data.tfrecords') 
for serialized_instance in tfr_dataset:
  print(serialized_instance) # print serialized example messages

dataset = tfr_dataset.map(_parse_tfr_element)
for instance in dataset:
  print()
  print(instance) # print parsed example messages with restored arrays

tf.train.Feature класс поддерживает только списки (или одномерные массивы) при использовании float_list аргумент. В зависимости от ваших данных, вы можете попробовать один из следующих подходов:

  1. Сгладьте данные в вашем массиве перед их передачей tf.train.Feature:

    def _floats_feature(value):
      return tf.train.Feature(float_list=tf.train.FloatList(value=value.reshape(-1)))
    

    Обратите внимание, что вам может понадобиться добавить еще одну функцию, чтобы указать, как эти данные должны быть изменены при повторном анализе (и вы можете использовать int64_list особенность для этой цели).

  2. Разделите многомерный объект на несколько одномерных объектов. Например, если c2d содержит N * 2 массив координат х и у, вы можете разбить эту функцию на отдельные train/coord2d/x а также train/coord2d/y объекты, каждая из которых содержит координаты x и y соответственно.

Документация по Tfrecord рекомендует использовать serialize_tensor

TFRecord и tf.train.Example

Примечание. Для простоты в этом примере используются только скалярные входные данные. Самый простой способ обработки нескалярных функций — использовать tf.io.serialize_tensor для преобразования тензоров в двоичные строки. Строки — это скаляры в тензорном потоке. Используйте tf.io.parse_tensor, чтобы преобразовать двоичную строку обратно в тензор.

2 строки кода помогают мне:

      tensor = tf.convert_to_tensor(array)
result = tf.io.serialize_tensor(tensor)
Другие вопросы по тегам