Lors de l'entraînement d'une grande quantité de données avec TensorFlow, il est pratique d'utiliser l'API Dataset pour lire les fonctionnalités stockées dans TFRecord. [\ Version compatible TensorFlow 2.x ] Comment entraîner une grande quantité de données à l'aide de TFRecord & DataSet avec TensorFlow (Keras) --Qiita
Vous pouvez trouver beaucoup d'exemples de code en effectuant une recherche, mais en fait, j'ai trouvé qu'en imaginant un moyen de le lire, il serait peut-être possible de le lire beaucoup plus rapidement que la méthode que vous voyez souvent.
En plus de l'article ci-dessus, vous pouvez utiliser `` tf.io.parse_single_example () '' comme méthode de lecture souvent introduite dans les documents officiels et autres sites. [Utilisation de TFRecords et 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)))
Avec ce genre de sentiment, le processus de conversion de chaque enregistrement en une quantité de caractéristiques est placé dans Dataset avec `` map () ''. Probablement l'utilisation la plus importante.
Cependant, j'ai l'impression que le traitement est lent ... Même si j'apprends avec le GPU, l'utilisation du GPU ne s'en tient pas à près de 100%, mais l'utilisation du processeur n'augmente pas. Je pense que les E / S sont le goulot d'étranglement.
Consulter la documentation officielle, en tant que théorie générale lors de la conversion d'un ensemble de données
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.
est ce qu'il lit. Better performance with the tf.data API | TensorFlow Core
En bref, "Il est recommandé de faire
map () '' en utilisant des fonctions définies par l'utilisateur sur une base batch."
Si tel est le cas, les performances s'amélioreraient-elles si les données pouvaient être lues et décodées par lots?
Je n'ai pas trouvé de matériel japonais du tout, mais il semble que la quantité de fonctionnalités puisse être décodée en unités de lot en utilisant `` tf.data.experimental.parse_example_dataset () ''. [^ 1] Le processus de décodage démarre après le traitement par lots comme indiqué ci-dessous.
[^ 1]: Il y a aussi tf.io.parse_example () '', et [exemple de code](https://stackoverflow.com/questions/37151895/tensorflow-read-all-examples-from-a- J'ai aussi trouvé tfrecords-at-once), mais je n'ai pas pu l'utiliser correctement car il semble être un vestige de la série 1.x (série 0.x?). (Quand j'ai essayé d'utiliser
TFRecordReader``, j'étais en colère qu'il ne puisse pas être utilisé avec Eager Execution)
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)))
Chaque enregistrement est retourné au format dict '', vous devrez donc le convertir en un tuple séparé lors de l'entraînement avec
keras.Model.fit () ''. Dans le cas de l'unité d'enregistrement, vous pouvez écrire la conversion en taple à la fois dans parse_example () '', mais ici vous devez ajouter le processus de conversion pour tapoter séparément avec
map () ''.
Je l'ai essayé. Ecrivez 10000 données de test MNIST et mesurez le temps de traitement de la pièce pour la lire. Je ne l'essayerai pas avant d'apprendre cette fois, mais comme on suppose qu'il sera utilisé pour l'apprentissage par la suite, dans le cas d'unités par lots, le processus de conversion des enregistrements en taples est également inclus.
Tout d'abord, écrivez les données dans le fichier TFRecord.
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"
#Écrire 10000 données d'évaluation de 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())
Ensuite, chargez-le de deux manières.
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)>
Notez que ds1 '' et
ds2 '' sont créés différemment, mais à la fin ce sont exactement les mêmes données. La taille du lot et les données renvoyées seront les mêmes.
Démarrez le shell interactif avec `ʻi python -i read_tfrecord.py`` et mesurez le temps de traitement nécessaire pour décoder les 10000 enregistrements.
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
C'est une victoire écrasante dans la méthode de lecture par lots ...!
Dans l'exemple précédent, x '' avait une longueur fixe (784 dimensions), mais c'est un peu gênant quand il s'agit de longueur variable (en fonction de l'enregistrement). En général, il semble que la méthode principale soit de sérialiser des données de longueur variable et de les traiter comme
tf.string ''.
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"
#Ecrire 1000 données de longueur variable
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())
Lors du décodage en unités d'enregistrement, lisez ce qui suit.
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)>
Dans les unités de lot, le nombre de colonnes de `` x '' est ajusté à la quantité de fonction la plus longue et la partie courte est remplie de 0.
ipython
In [1]: %timeit [1 for _ in iter(ds1)]
10 loops, best of 3: 153 ms per loop
Et si vous le faisiez par lots?
Étant donné que le nombre de dimensions de x '' est différent pour chaque enregistrement, le traitement par lots de l'ensemble de données puis l'exécution de
decode_raw '' avec `` map () '' échouera.
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
Cependant, si vous faites ```unbatch () puis
decode_raw``, vous perdrez l'avantage d'accélérer.
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
C'est là que le sauveur entre en jeu. Uniquement disponible dans TensorFlow 2.1 et versions ultérieures, vous pouvez désormais spécifier un nouveau type de fonctionnalité appelé `` RaggedFeature '' lors du chargement des données. tf.io.RaggedFeature | TensorFlow Core v2.1.0
Avec cela, les fonctionnalités décodées seront RaggedTensor ''. Un
Tensor '' ordinaire doit avoir le même nombre de colonnes par ligne, mais pas RaggedTensor ''. Vous pouvez représenter un
Tensor '' avec différents nombres de colonnes pour chaque ligne.
tf.RaggedTensor | TensorFlow Core v2.1.0
Tout d'abord, lors de l'écriture de données, créez des Fonctionnalités '' en utilisant les fonctionnalités de longueur variable comme liste de
float32 ''.
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" #J'ai changé le nom
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())
Lors du chargement, spécifiez `` RaggedFeature '' comme quantité de fonctionnalité.
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)
}))
Ici, chaque enregistrement de ds2 '' devient
dictcomme dans le cas d'une longueur fixe, sauf que
x '' devient RaggedTensor
. Si vous coupez chaque ligne de RaggedTensor '', vous verrez
Tensor '' de différentes tailles, comme indiqué ci-dessous.
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)>
Vous pouvez remplir la fin d'une fonction courte avec des zéros pour en faire un `` Tensor '' régulier en unités de lots. Cela vous donnera le même lot que si vous décodiez enregistrement par enregistrement.
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
Il a été réduit à près d'un dixième de celui du traitement enregistrement par enregistrement. Génial!
VarLenFeature
En fait, TensorFlow 1.x / 2.0 a également un moyen de lire les caractéristiques de longueur variable.
Si le type de fonctionnalité est VarLenFeature '', vous pouvez lire la fonctionnalité comme
SparseTensor ''.
Comment faire TFRecord est le même que `` 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
Bien sûr, c'est beaucoup plus rapide que sur une base d'enregistrement par enregistrement, mais plus lent que le RaggedFeature ''. Si possible, j'aimerais utiliser
RaggedFeature '' dans TensorFlow 2.1 ou version ultérieure.
ʻapply ()
ʻof Dataset.Recommended Posts