web-dev-qa-db-ja.com

.npy(numpyファイル)をtensorflowデータパイプラインにフィードする

Tensorflowには、「。npy」ファイルのリーダーがないようです。データファイルを新しいtensorflow.data.Datasetピップラインに読み込むにはどうすればよいですか?データがメモリに収まりません。

各オブジェクトは個別の「.npy」ファイルに保存されます。各ファイルには、特徴として2つの異なるndarray、ラベルとしてスカラーが含まれています。

12
Sluggish Crow

データはメモリに収まりますか?もしそうなら、あなたはドキュメントの 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))

ファイルがメモリに収まらない場合、推奨される唯一のアプローチは、最初にnpyデータをTFRecord形式に変換し、次にTFRecordデータセット形式。メモリに完全に読み込まなくてもストリーミングできます。

ここにいくつかの指示のある投稿があります。

FWIW、TFRecordをnpyファイルのディレクトリ名またはファイル名で直接インスタンス化できないのは不思議に思えますが、それは単純なTensorflowの制限のようです。

1つの大きなnpyファイルを小さなファイルに分割して、それぞれがトレーニングの1つのバッチを大まかに表すことができる場合、現在のバッチに必要なデータのみを生成するカスタムデータジェネレーターをKerasで作成できます。

一般に、データセットがメモリに収まらない場合、1つの大きなnpyファイルとして保存すると操作が非常に難しくなります。できれば、最初にデータをTFRecordまたは複数のnpyファイルとして再フォーマットしてから、他の方法を使用する必要があります。

3
ely

これはtf.py_funcで実行できます。例 here を参照してください。解析関数はファイル名をバイトから文字列にデコードし、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,])))
5
George

実際には、TFRecordの代わりにTensorFlowを使用してNPYファイルを直接読み取ることができます。重要な部分は tf.data.FixedLengthRecordDataset および tf.decode_raw であり、 NPY形式 のドキュメントを参照してください。簡単にするために、形状(N, K)の配列を含むfloat32 NPYファイルが指定されており、特徴の数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)

このデータセットの各要素には、1つの例を表す長いバイト文字列が含まれています。これをデコードして、実際の配列を取得できます。

dataset = dataset.map(lambda s: tf.decode_raw(s, dtype))

ただし、TensorFlowは文字列の長さを追跡しないため、要素の形状は不確定になります。機能の数がわかっているので、形状を強制できます。

dataset = dataset.map(lambda s: tf.reshape(tf.decode_raw(s, dtype), (num_features,)))

同様に、バッチ処理の後にこの手順を実行するか、好きなように組み合わせることができます。

制限は、事前に機能の数を知っておく必要があることです。ただし、NumPyヘッダーから抽出することは可能ですが、少し面倒であり、いずれにしてもTensorFlow内からはほとんど実行できないため、ファイル名を事前に知っておく必要があります。別の制限として、すべての配列のサイズが同じであることがわかっている場合でも、ソリューションでは、データセットごとにファイルを1つだけ使用するか、同じヘッダーサイズのファイルを使用する必要があります。

確かに、この種のアプローチを検討する場合は、ヘッダーのない純粋なバイナリファイルを用意し、機能の数をハードコーディングするか、別のソースから読み取る方がよい場合があります。

2
jdehesa