Wenn Sie eine große Datenmenge mit TensorFlow trainieren, ist es zweckmäßig, die Dataset-API zum Laden der in TFRecord gespeicherten Funktionen zu verwenden. [\ TensorFlow 2.x-kompatible Version ] So trainieren Sie eine große Datenmenge mit TFRecord & DataSet mit TensorFlow (Keras) - Qiita
Sie können eine Menge Beispielcode finden, indem Sie suchen, aber tatsächlich habe ich festgestellt, dass es durch die Entwicklung einer Methode zum Lesen möglich sein kann, ihn viel schneller zu lesen als die Methode, die Sie häufig sehen.
Zusätzlich zu dem obigen Artikel können Sie "tf.io.parse_single_example ()" als Lesemethode verwenden, die häufig in offiziellen Dokumenten und anderen Websites eingeführt wird. [Verwendung von TFRecords und tf.Example | TensorFlow Core](https://www.tensorflow.org/tutorials/load_data/tfrecord?hl=ja#tfrecord_%E3%83%95%E3%82%A1%E3% 82% A4% E3% 83% AB% E3% 81% AE% E8% AA% AD% E3% 81% BF% E8% BE% BC% E3% 81% BF)
import tensorflow as tf
import numpy as np
feature_dim = 784
def parse_example(example):
features = tf.io.parse_single_example(
example,
features={
"x": tf.io.FixedLenFeature([feature_dim], dtype=tf.float32),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
})
x = features["x"]
y = features["y"]
return x, y
ds1 = tf.data.TFRecordDataset(["test.tfrecords"]).map(parse_example).batch(512)
print(ds1)
print(next(iter(ds1)))
Mit dieser Art von Gefühl wird der Prozess des Konvertierens jedes Datensatzes in einen Feature-Betrag mit map ()
in den Datensatz gestellt.
Wahrscheinlich die größte Verwendung.
Ich habe jedoch das Gefühl, dass die Verarbeitung langsam ist ... Selbst wenn ich mit GPU lerne, bleibt die GPU-Auslastung nicht bei nahezu 100%, aber die CPU-Auslastung steigt nicht an. Ich habe das Gefühl, dass I / O der Engpass ist.
Betrachten Sie die offizielle Dokumentation als allgemeine Theorie bei der Konvertierung von Datensätzen
Invoking a user-defined function passed into the map transformation has overhead related to scheduling and executing the user-defined function. We recommend vectorizing the user-defined function (that is, have it operate over a batch of inputs at once) and apply the batch transformation before the map transformation.
ist was es liest. Better performance with the tf.data API | TensorFlow Core
Kurz gesagt, es wird empfohlen, map () mit benutzerdefinierten Funktionen stapelweise auszuführen. Wenn ja, würde sich die Leistung verbessern, wenn Daten in Batch-Einheiten gelesen und dekodiert werden könnten?
Ich konnte überhaupt kein japanisches Material finden, aber es scheint, dass Features mithilfe von tf.data.experimental.parse_example_dataset ()
in Batch-Einheiten dekodiert werden können. [^ 1]
Der Dekodierungsprozess beginnt nach dem Stapeln wie unten gezeigt.
[^ 1]: Es gibt auch tf.io.parse_example ()
und [Beispielcode](https://stackoverflow.com/questions/37151895/tensorflow-read-all-examples-from-a- Ich habe auch tfrecords-auf einmal gefunden), aber ich konnte es nicht gut verwenden, da es ein Rest der 1.x-Serie (0.x-Serie?) Zu sein scheint. (Als ich versuchte, "TFRecordReader" zu verwenden, war ich wütend, dass es nicht mit Eager Execution verwendet werden konnte.)
feature_dim = 784
ds2 = tf.data.TFRecordDataset(["test.tfrecords"]) \
.batch(512) \
.apply(tf.data.experimental.parse_example_dataset({
"x": tf.io.FixedLenFeature([feature_dim], dtype=tf.float32),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
}))
print(ds2)
print(next(iter(ds2)))
Jeder Datensatz wird im dict
-Format zurückgegeben, sodass Sie ihn beim Training mit keras.Model.fit ()
in ein separates Tupel konvertieren müssen. Bei einer Datensatzeinheit können Sie die Konvertierung in taple sofort in parse_example ()
schreiben, aber hier müssen Sie den Konvertierungsprozess hinzufügen, um separat mit map ()
zu taple.
Ich habe es tatsächlich versucht. Schreiben Sie 10000 MNIST-Testdaten und messen Sie die Verarbeitungszeit des zu lesenden Teils. Ich werde es erst versuchen, wenn ich diesmal gelernt habe, aber da davon ausgegangen wird, dass es danach zum Lernen verwendet wird, ist im Fall von Batch-Einheiten auch der Prozess des Konvertierens von Datensätzen in Taples enthalten.
Schreiben Sie zuerst die Daten in die TFRecord-Datei.
data2tfrecord.py
import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets import mnist
def feature_float_list(l):
return tf.train.Feature(float_list=tf.train.FloatList(value=l))
def record2example(r_x, r_y):
return tf.train.Example(features=tf.train.Features(feature={
"x": feature_float_list(r_x),
"y": feature_float_list(r_y)
}))
filename_test = "test.tfrecords"
#Schreiben Sie 10000 Bewertungsdaten von MNIST
_, (x_test, y_test) = mnist.load_data()
print("x_test : ", x_test.shape) # x_test : (10000, 28, 28)
print("y_test : ", y_test.shape) # y_test : (10000,)
x_test = x_test.reshape((-1, 28*28)).astype("float32") / 255.0
y_test = y_test.reshape((-1, 1)).astype("float32")
with tf.io.TFRecordWriter(filename_test) as writer:
for r_x, r_y in zip(x_test, y_test):
ex = record2example(r_x, r_y)
writer.write(ex.SerializeToString())
Laden Sie es dann auf zwei Arten.
read_tfrecord.py
import tensorflow as tf
import numpy as np
feature_dim = 784
def parse_example(example):
features = tf.io.parse_single_example(example, features={
"x": tf.io.FixedLenFeature([feature_dim], dtype=tf.float32),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
})
x = features["x"]
y = features["y"]
return x, y
ds1 = tf.data.TFRecordDataset(["test.tfrecords"]).map(parse_example).batch(512)
print(ds1) # <BatchDataset shapes: ((None, 784), (None,)), types: (tf.float32, tf.float32)>
def dict2tuple(feat):
return feat["x"], feat["y"]
ds2 = tf.data.TFRecordDataset(["test.tfrecords"]) \
.batch(512) \
.apply(tf.data.experimental.parse_example_dataset({
"x": tf.io.FixedLenFeature([feature_dim], dtype=tf.float32),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
})) \
.map(dict2tuple)
print(ds2) # <MapDataset shapes: ((None, 784), (None,)), types: (tf.float32, tf.float32)>
Beachten Sie, dass ds1
und ds2
unterschiedlich erstellt werden, aber am Ende sind es genau die gleichen Daten. Die Stapelgröße und die zurückgegebenen Daten sind gleich.
Starten Sie die interaktive Shell mit "ipython -i read_tfrecord.py" und messen Sie die Verarbeitungszeit, die zum Dekodieren aller 10000 Datensätze erforderlich ist.
ipython
In [1]: %timeit [1 for _ in iter(ds1)]
1 loop, best of 3: 1.4 s per loop
In [2]: %timeit [1 for _ in iter(ds2)]
10 loops, best of 3: 56.3 ms per loop
Es ist ein überwältigender Sieg in der Methode des Lesens in Batch-Einheiten ...!
Im vorherigen Beispiel hatte x
eine feste Länge (784 Dimensionen), aber es ist etwas ärgerlich, wenn es um variable Länge geht (abhängig vom Datensatz).
Im Allgemeinen scheint die Hauptmethode darin zu bestehen, Daten variabler Länge zu serialisieren und als "tf.string" zu behandeln.
data2tfrecord_var.py
import numpy as np
import tensorflow as tf
def feature_bytes_list(l):
return tf.train.Feature(bytes_list=tf.train.BytesList(value=l))
def feature_float_list(l):
return tf.train.Feature(float_list=tf.train.FloatList(value=l))
def record2example(r_x, r_y):
return tf.train.Example(features=tf.train.Features(feature={
"x": feature_bytes_list(r_x),
"y": feature_float_list(r_y)
}))
filename = "random.tfrecords"
#Schreiben Sie 1000 Daten variabler Länge
with tf.io.TFRecordWriter(filename) as writer:
for i in range(1000):
r_x = np.random.random(i+1).astype("float32")
r_y = np.random.random(1)
ex = record2example([r_x.tostring()], r_y)
writer.write(ex.SerializeToString())
Lesen Sie beim Decodieren in Datensatzeinheiten wie folgt.
read_tfrecord_var.py
import tensorflow as tf
import numpy as np
def parse_example(example):
features = tf.io.parse_single_example(
example,
features={
"x": tf.io.FixedLenFeature([], dtype=tf.string),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
})
x = tf.io.decode_raw(features["x"], tf.float32)
y = [features["y"]]
return x, y
ds1 = tf.data.TFRecordDataset(["random.tfrecords"]).map(parse_example).padded_batch(512, ([None], [1]))
print(ds1) # <PaddedBatchDataset shapes: ((None, None), (None, 1)), types: (tf.float32, tf.float32)>
In Batch-Einheiten wird die Anzahl der Spalten von "x" an die längste Merkmalsmenge angepasst und der kurze Teil mit 0 gefüllt.
ipython
In [1]: %timeit [1 for _ in iter(ds1)]
10 loops, best of 3: 153 ms per loop
Was ist, wenn Sie es in Chargen tun? Da die Anzahl der Dimensionen von "x" für jeden Datensatz unterschiedlich ist, schlägt das Stapeln des Datensatzes und das anschließende Ausführen von "decode_raw" mit "map ()" fehl.
def dict2tuple(feature):
return tf.io.decode_raw(feature["x"], tf.float32), [feature["y"]]
ds2 = tf.data.TFRecordDataset(["random.tfrecords"]) \
.batch(512) \
.apply(tf.data.experimental.parse_example_dataset({
"x": tf.io.FixedLenFeature([], dtype=tf.string),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
})) \
.map(dict2tuple)
print(next(iter(ds2)))
# InvalidArgumentError: DecodeRaw requires input strings to all be the same size, but element 1 has size 4 != 8
Wenn Sie jedoch unbatch ()
und dann decode_raw
ausführen, verlieren Sie den Vorteil der Beschleunigung.
ds2 = tf.data.TFRecordDataset(["random.tfrecords"]) \
.batch(512) \
.apply(tf.data.experimental.parse_example_dataset({
"x": tf.io.FixedLenFeature([], dtype=tf.string),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
})).unbatch().map(dict2tuple).padded_batch(512, ([None], [1]))
ipython
In [2]: %timeit [1 for _ in iter(ds2)]
10 loops, best of 3: 136 ms per loop
RaggedFeature
Hier kommt der Retter ins Spiel. Nur in TensorFlow 2.1 und höher verfügbar, können Sie jetzt beim Laden von Daten einen neuen Funktionstyp namens "RaggedFeature" angeben. tf.io.RaggedFeature | TensorFlow Core v2.1.0
Damit sind die dekodierten Funktionen "RaggedTensor". Gewöhnlicher Tensor
sollte die gleiche Anzahl von Spalten pro Zeile haben, RaggedTensor
jedoch nicht. Sie können einen Tensor mit einer unterschiedlichen Anzahl von Spalten für jede Zeile darstellen.
tf.RaggedTensor | TensorFlow Core v2.1.0
Erstellen Sie beim Schreiben von Daten zunächst "Features", indem Sie die Features variabler Länge als Liste von "float32" verwenden.
def feature_float_list(l):
return tf.train.Feature(float_list=tf.train.FloatList(value=l))
def record2example(r_x, r_y):
return tf.train.Example(features=tf.train.Features(feature={
"x": feature_float_list(r_x),
"y": feature_float_list(r_y)
}))
filename = "random2.tfrecords" #Ich habe den Namen geändert
with tf.io.TFRecordWriter(filename) as writer:
for i in range(1000):
r_x = np.random.random(i+1).astype("float32")
r_y = np.random.random(1)
ex = record2example(r_x, r_y)
writer.write(ex.SerializeToString())
Geben Sie beim Laden "RaggedFeature" als Feature-Menge an.
ds2 = tf.data.TFRecordDataset(["random2.tfrecords"]) \
.batch(512) \
.apply(tf.data.experimental.parse_example_dataset({
"x": tf.io.RaggedFeature(tf.float32),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
}))
Hier wird jeder Datensatz von ds2
wie im Fall einer festen Länge zu dict
, außer dass x
zu RaggedTensor
wird. Wenn Sie jede Zeile von "RaggedTensor" in Scheiben schneiden, sehen Sie "Tensor" in verschiedenen Größen, wie unten gezeigt.
ipython
In [1]: next(iter(ds2))["x"][0]
Out[1]: <tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.8635351], dtype=float32)>
In [2]: next(iter(ds2))["x"][1]
Out[2]: <tf.Tensor: shape=(2,), dtype=float32, numpy=array([0.66411597, 0.8526721 ], dtype=float32)>
In [3]: next(iter(ds2))["x"][2]
Out[3]: <tf.Tensor: shape=(3,), dtype=float32, numpy=array([0.7902446 , 0.13108689, 0.05331135], dtype=float32)>
Sie können das Ende eines kurzen Features mit Nullen auffüllen, um es stapelweise zu einem regulären "Tensor" zu machen. Auf diese Weise erhalten Sie den gleichen Stapel, als würden Sie Datensatz für Datensatz dekodieren.
def dict2tuple(feature):
return feature["x"].to_tensor(), [feature["y"]]
ds2 = tf.data.TFRecordDataset(["random2.tfrecords"]) \
.batch(512) \
.apply(tf.data.experimental.parse_example_dataset({
"x": tf.io.RaggedFeature(tf.float32),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
})).map(dict2tuple)
ipython
In [4]: %timeit [1 for _ in iter(ds2)]
100 loops, best of 3: 18.6 ms per loop
Es wurde auf fast ein Zehntel der rekordverdächtigen Verarbeitung reduziert. Toll!
VarLenFeature
Tatsächlich bietet TensorFlow 1.x / 2.0 auch die Möglichkeit, Features mit variabler Länge zu lesen. Wenn der Feature-Typ "VarLenFeature" ist, können Sie das Feature als "SparseTensor" lesen. Das Erstellen eines TFRecord ist dasselbe wie "RaggedFeature".
def dict2tuple(feature):
return tf.sparse.to_dense(feature["x"]), [feature["y"]]
ds3 = tf.data.TFRecordDataset(["random2.tfrecords"]) \
.batch(512) \
.apply(tf.data.experimental.parse_example_dataset({
"x": tf.io.VarLenFeature(tf.float32),
"y": tf.io.FixedLenFeature([], dtype=tf.float32)
})) \
.map(dict2tuple)
ipython
In [5]: %timeit [1 for _ in iter(ds3)]
10 loops, best of 3: 39.9 ms per loop
Sicher, es ist viel schneller als auf Rekordbasis, aber langsamer als das "RaggedFeature".
Wenn möglich, möchte ich RaggedFeature
in TensorFlow 2.1 oder höher verwenden.
parse_example_dataset ()
zum Konvertieren. Geben Sie den Rückgabewert dieser Funktion im Argument apply ()
von Dataset an.
--Für TensorFlow 2.1 oder höher geben Sie "RaggedFeature" an, um Features mit variabler Länge zu laden.Recommended Posts