实际上可以使用 TensorFlow 而不是 TFRecords 直接读取 NPY 文件。关键部分是tf.data.FixedLengthRecordDataset https://www.tensorflow.org/api_docs/python/tf/data/FixedLengthRecordDataset and tf.io.decode_raw https://www.tensorflow.org/api_docs/python/tf/io/decode_raw,并查看文档NPY 格式 https://docs.scipy.org/doc/numpy/neps/npy-format.html。为了简单起见,我们假设一个 float32 NPY 文件包含一个形状为(N, K)
给出了,并且你知道特征的数量K
事先,以及它是一个 float32 数组的事实。 NPY 文件只是一个带有小标头的二进制文件,后面跟着原始数组数据(对象数组不同,但我们现在考虑的是数字)。简而言之,您可以使用如下函数找到该标头的大小:
def npy_header_offset(npy_path):
with open(str(npy_path), 'rb') as f:
if f.read(6) != b'\x93NUMPY':
raise ValueError('Invalid NPY file.')
version_major, version_minor = f.read(2)
if version_major == 1:
header_len_size = 2
elif version_major == 2:
header_len_size = 4
else:
raise ValueError('Unknown NPY file version {}.{}.'.format(version_major, version_minor))
header_len = sum(b << (8 * i) for i, b in enumerate(f.read(header_len_size)))
header = f.read(header_len)
if not header.endswith(b'\n'):
raise ValueError('Invalid NPY file.')
return f.tell()
这样你就可以创建一个像这样的数据集:
import tensorflow as tf
npy_file = 'my_file.npy'
num_features = ...
dtype = tf.float32
header_offset = npy_header_offset(npy_file)
dataset = tf.data.FixedLengthRecordDataset([npy_file], num_features * dtype.size, header_bytes=header_offset)
该数据集的每个元素都包含一长串表示单个示例的字节。您现在可以对其进行解码以获得实际的数组:
dataset = dataset.map(lambda s: tf.io.decode_raw(s, dtype))
不过,这些元素将具有不确定的形状,因为 TensorFlow 不会跟踪字符串的长度。因为您知道特征的数量,所以您可以强制执行形状:
dataset = dataset.map(lambda s: tf.reshape(tf.io.decode_raw(s, dtype), (num_features,)))
同样,您可以选择在批处理后执行此步骤,或以您喜欢的任何方式组合它。
限制是您必须提前知道功能的数量。不过,可以从 NumPy 标头中提取它,只是有点麻烦,而且在任何情况下都很难从 TensorFlow 中提取,因此需要提前知道文件名。另一个限制是,该解决方案要求您每个数据集仅使用一个文件或具有相同标头大小的文件,尽管如果您知道所有数组都具有相同的大小,那么实际上应该是这样。
诚然,如果有人考虑这种方法,最好拥有一个没有标题的纯二进制文件,并且要么对功能数量进行硬编码,要么从不同的源读取它们......