web-dev-qa-db-ja.com

Tensorflow 2.0データセットでバッチサイズを動的に変更する方法

TensorFlow 1.Xでは、プレースホルダーを使用してバッチサイズを動的に変更できます。例えば

dataset.batch(batch_size=tf.placeholder())
完全な例を参照

TensorFlow 2.0ではそれをどのように行いますか?

私は以下を試しましたが、うまくいきません。

import numpy as np
import tensorflow as tf


def new_gen_function():
    for i in range(100):
        yield np.ones(2).astype(np.float32)


batch_size = tf.Variable(5, trainable=False, dtype=tf.int64)
train_ds = tf.data.Dataset.from_generator(new_gen_function, output_types=(tf.float32)).batch(
    batch_size=batch_size)

for data in train_ds:
    print(data.shape[0])
    batch_size.assign(10)
    print(batch_size)

出力

5
<tf.Variable 'Variable:0' shape=() dtype=int64, numpy=10>
5
<tf.Variable 'Variable:0' shape=() dtype=int64, numpy=10>
5
<tf.Variable 'Variable:0' shape=() dtype=int64, numpy=10>
5
...
...

グラデーションテープを使用したカスタムトレーニングループを使用してモデルをトレーニングしています。どうすればこれを達成できますか?

5
Himaprasoon

明らかに.from_generatorを使用している場合は、そこに手動でバッチ処理できますが、それでは実際には問題に対処できません。

私が考えることができる最も簡単な2つの方法は、データセットのコンポーネントとしてバッチサイズを含めてから、要求されたサイズのバッチを作成することです。

import tensorflow as tf

batch_sizes = tf.data.Dataset.range(4)
ds = batch_sizes.map(lambda n: tf.random.normal(shape=[n,3]))

for item in ds:
  print(item.shape)
  print()
(0,3)
(1,3)
(2,3)
(3,3)

または、@ PG-Nのソリューションに基づいて構築し、tf.function内で完全に実行されるバージョンが必要な場合は、tf.TensorArrayを使用してそれらをパックできます。

import tensorflow as tf 

 class Batcher(tf.Module):
   def __init__(self, ds, batch_size=0):   
     self.it = iter(ds) 
     self._batch_size = tf.Variable(batch_size) 

   @property 
   def batch_size(self): 
     return self._batch_size

   @batch_size.setter  
   def batch_size(self, new_size): 
     self._batch_size.assign(new_size) 

   @tf.function 
   def __call__(self): 
     examples =tf.TensorArray(dtype=tf.int64, size=self.batch_size) 
     for i in tf.range(self.batch_size): 
       examples = examples.write(i, next(self.it)) 

     return examples.stack() 
ds = tf.data.Dataset.range(100)
B = Batcher(ds)
B.batch_size = 5
B().numpy()
array([0, 1, 2, 3, 4])
B.batch_size = 10
B().numpy()
array([ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
B.batch_size = 3
B().numpy()
array([ 15,  16,  17])

おそらく、tf.nestを使用して途中で何かを実行し、これを単一のテンソルコンポーネントだけではないデータセットに一般化することができます。

また、ユースケースによっては、 group_by_windowbucket_by_sequence_length などの方法が役立つ場合があります。これらはいくつかのマルチサイズのバッチを実行します。それらはあなたが探しているものかもしれませんし、実装が問題の手掛かりになるかもしれません。

1
mdaoust

私が知っていることから、変更を有効にするには、新しいデータセットイテレータをインスタンス化する必要があります。これは、すでに見たサンプルをスキップするために少し微調整する必要があります。

これが私の最も簡単な解決策です:

import numpy as np
import tensorflow as tf

def get_dataset(batch_size, num_samples_seen):
    return tf.data.Dataset.range(
        100
    ).skip(
        num_samples_seen
    ).batch(
        batch_size=batch_size
    )

def main():
    batch_size = 1
    num_samples_seen = 0

    train_ds = get_dataset(batch_size, num_samples_seen)

    ds_iterator = iter(train_ds)
    while True:
        try:
            data = next(ds_iterator)
        except StopIteration:
            print("End of iteration")
            break

        print(data)
        batch_size *= 2
        num_samples_seen += data.shape[0]
        ds_iterator = iter(get_dataset(batch_size, num_samples_seen))
        print("New batch size:", batch_size)

if __name__ == "__main__":
    main()

ここを見るとわかるように、(get_datasetを呼び出して)新しいデータセットをインスタンス化し、イテレータを更新する必要があります。

このようなソリューションのパフォーマンスへの影響はわかりません。データセット全体ではなくbatchステップを「ちょうど」インスタンス化する必要がある別のソリューションがあるかもしれません。

1
AlexisBRENON