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
...
...
グラデーションテープを使用したカスタムトレーニングループを使用してモデルをトレーニングしています。どうすればこれを達成できますか?
明らかに.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_window
や bucket_by_sequence_length
などの方法が役立つ場合があります。これらはいくつかのマルチサイズのバッチを実行します。それらはあなたが探しているものかもしれませんし、実装が問題の手掛かりになるかもしれません。
私が知っていることから、変更を有効にするには、新しいデータセットイテレータをインスタンス化する必要があります。これは、すでに見たサンプルをスキップするために少し微調整する必要があります。
これが私の最も簡単な解決策です:
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
ステップを「ちょうど」インスタンス化する必要がある別のソリューションがあるかもしれません。