将.npy(numpy文件)送入tensorflow数据管道

Slu*_*row 10 numpy dataset tensorflow data-pipeline

Tensorflow似乎缺少".npy"文件的读者.如何将我的数据文件读入新的tensorflow.data.Dataset pipline?我的数据不适合内存.

每个对象都保存在单独的".npy"文件中.每个文件包含2个不同的ndarray作为特征,标量作为标签.

jde*_*esa 12

实际上,可以使用TensorFlow而不是TFRecords直接读取NPY文件。关键件是tf.data.FixedLengthRecordDatasettf.decode_raw,用看的文件一起NPY格式。为简单起见,我们假设给出了一个包含具有形状的数组的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()
Run Code Online (Sandbox Code Playgroud)

这样,您可以创建一个像这样的数据集:

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)
Run Code Online (Sandbox Code Playgroud)

该数据集的每个元素都包含一长串字节,代表一个示例。现在,您可以对其进行解码以获得实际的数组:

dataset = dataset.map(lambda s: tf.decode_raw(s, dtype))
Run Code Online (Sandbox Code Playgroud)

但是,这些元素将具有不确定的形状,因为TensorFlow不会跟踪字符串的长度。因为您知道特征的数量,所以您可以只执行形状:

dataset = dataset.map(lambda s: tf.reshape(tf.decode_raw(s, dtype), (num_features,)))
Run Code Online (Sandbox Code Playgroud)

同样,您可以选择在批处理之后执行此步骤,或以您喜欢的任何方式进行组合。

局限性在于您必须事先知道功能数量。不过,可以从NumPy标头中提取它,这有点麻烦,而且无论如何很难从TensorFlow内部提取它,因此文件名需要事先知道。另一个局限性在于,该解决方案要求您每个数​​据集仅使用一个文件或具有相同标头大小的文件,尽管如果您知道所有数组的大小应与实际情况相同。

诚然,如果考虑使用这种方法,最好有一个没有头的纯二进制文件,然后硬编码功能的数量或从不同的来源读取它们……


Geo*_*rge 10

您可以使用tf.py_func执行此操作,请参阅此处的示例.解析函数只是将文件名从字节解码为字符串并调用np.load.

更新:这样的事情:

def read_npy_file(item):
    data = np.load(item.decode())
    return data.astype(np.float32)

file_list = ['/foo/bar.npy', '/foo/baz.npy']

dataset = tf.data.Dataset.from_tensor_slices(file_list)

dataset = dataset.map(
        lambda item: tuple(tf.py_func(read_npy_file, [item], [tf.float32,])))
Run Code Online (Sandbox Code Playgroud)


ely*_*ely 6

您的数据是否适合内存?如果是这样,您可以按照文档的“ 消耗NumPy数组”部分的说明进行操作:

消耗NumPy数组

如果所有输入数据都适合内存,从它们创建数据集的最简单方法是将它们转换为tf.Tensor对象并使用Dataset.from_tensor_slices()。

# Load the training data into two NumPy arrays, for example using `np.load()`.
with np.load("/var/data/training_data.npy") as data:
  features = data["features"]
  labels = data["labels"]

# Assume that each row of `features` corresponds to the same row as `labels`.
assert features.shape[0] == labels.shape[0]

dataset = tf.data.Dataset.from_tensor_slices((features, labels))
Run Code Online (Sandbox Code Playgroud)

如果文件不适合内存,似乎唯一推荐的方法是先将npy数据转换为TFRecord格式,然后再使用TFRecord数据集格式,而无需完全加载到内存中就可以流式传输数据。

这是带有一些说明的帖子。

FWIW,对我来说似乎很疯狂,TFRecord无法直接使用npy文件的目录名称或文件名实例化,但这似乎是普通Tensorflow的局限性。

如果您可以将单个大的npy文件拆分为较小的文件,每个文件大致代表一个批次进行训练,那么您可以在Keras中编写一个自定义数据生成器,该数据生成器将仅生成当前批次所需的数据。

通常,如果您的数据集无法容纳在内存中,则将其存储为一个大的npy文件非常困难,因此最好您首先将数据重新格式化为TFRecord或多个npy文件,然后再使用其他方法。

  • 非常感谢,但是将我的Numpy文件转换为TFRecord是我要做的最后一件事,因为我大约有5,000,000个文件,而且这样做需要很长时间。我想我会采用keras生成器的想法。再次感谢! (2认同)
  • 您的5,000,000个文件中的每个文件都不适合存储? (2认同)