Как хранить массивы 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
аргумент. В зависимости от ваших данных, вы можете попробовать один из следующих подходов:
Сгладьте данные в вашем массиве перед их передачей
tf.train.Feature
:def _floats_feature(value): return tf.train.Feature(float_list=tf.train.FloatList(value=value.reshape(-1)))
Обратите внимание, что вам может понадобиться добавить еще одну функцию, чтобы указать, как эти данные должны быть изменены при повторном анализе (и вы можете использовать
int64_list
особенность для этой цели).Разделите многомерный объект на несколько одномерных объектов. Например, если
c2d
содержитN * 2
массив координат х и у, вы можете разбить эту функцию на отдельныеtrain/coord2d/x
а такжеtrain/coord2d/y
объекты, каждая из которых содержит координаты x и y соответственно.
Документация по Tfrecord рекомендует использовать serialize_tensor
Примечание. Для простоты в этом примере используются только скалярные входные данные. Самый простой способ обработки нескалярных функций — использовать tf.io.serialize_tensor для преобразования тензоров в двоичные строки. Строки — это скаляры в тензорном потоке. Используйте tf.io.parse_tensor, чтобы преобразовать двоичную строку обратно в тензор.
2 строки кода помогают мне:
tensor = tf.convert_to_tensor(array)
result = tf.io.serialize_tensor(tensor)