Cet article est l'article précédent "L'histoire selon laquelle la fonction de jeu de données qui peut être utilisée avec TensorFlow était forte" "[[application TF2.0] tf.data. C'est l'histoire d'une autre version améliorée de Data Augmentation qui a été un peu soulevée dans "Rendre l'augmentation des données plus rapide avec Dataset" (https://qiita.com/Suguru_Toyohara/items/49c2914b21615b554afa).
Lors de l'utilisation du système tf.data.Dataset
pour l'amélioration de la vitesse et du système keras.preprocessing.image
** Réussite à réaliser du code qui peut être traité en parallèle. ** **
Je vais mettre le mécanisme réel et l'arrière-plan à ce point à côté du code.
Je mettrai le code ci-dessous
Tout d'abord, préparons l'environnement expérimental.
init
import tensorflow as tf
import tensorflow.keras as keras
import matplotlib.pyplot as plt
import sklearn
import numpy as np
from tqdm import tqdm
(tr_x,tr_y),(te_x,te_y)=keras.datasets.cifar10.load_data()
tr_x, te_x = tr_x/255.0, te_x/255.0
tr_y, te_y = tr_y.reshape(-1,1), te_y.reshape(-1,1)
model = keras.models.Sequential()
model.add(keras.layers.Convolution2D(32,3,padding="same",activation="relu",input_shape=(32,32,3)))
model.add(keras.layers.Convolution2D(32,3,padding="same",activation="relu"))
model.add(keras.layers.Convolution2D(32,3,padding="same",activation="relu"))
model.add(keras.layers.MaxPooling2D())
model.add(keras.layers.Convolution2D(128,3,padding="same",activation="relu"))
model.add(keras.layers.Convolution2D(128,3,padding="same",activation="relu"))
model.add(keras.layers.Convolution2D(128,3,padding="same",activation="relu"))
model.add(keras.layers.Convolution2D(128,3,padding="same",activation="relu"))
model.add(keras.layers.Convolution2D(128,3,padding="same",activation="relu"))
model.add(keras.layers.MaxPooling2D())
model.add(keras.layers.Convolution2D(256,3,padding="same",activation="relu"))
model.add(keras.layers.Convolution2D(256,3,padding="same",activation="relu"))
model.add(keras.layers.Convolution2D(256,3,padding="same",activation="relu"))
model.add(keras.layers.GlobalAveragePooling2D())
model.add(keras.layers.Dense(1000,activation="relu"))
model.add(keras.layers.Dense(128,activation="relu"))
model.add(keras.layers.Dense(10,activation="softmax"))
model.compile(loss="sparse_categorical_crossentropy",metrics=["accuracy"])
Tout d'abord, exprimons keras.preprocessing.image.random_rotate
pour que cela puisse être fait avec .map
.
random_rotate
from tensorflow.keras.preprocessing.image import random_rotation
from joblib import Parallel, delayed
def r_rotate(imgs, degree):
pics=imgs.numpy()
degree = degree.numpy()
if tf.rank(imgs)==4:
X=Parallel(n_jobs=-1)( [delayed(random_rotation)(pic, degree, 0, 1, 2) for pic in pics] )
X=np.asarray(X)
elif tf.rank(imgs)==3:
X=random_rotation(pics, degree, 0, 1, 2)
return X
@tf.function
def random_rotate(imgs, label):
x = tf.py_function(r_rotate,[imgs,30],[tf.float32])
X = x[0]
X.set_shape(imgs.shape)
return X, label
Maintenant, cela fonctionne réellement. Déplaçons-le et voyons les données.
Afficher les données
labels = np.array([
'airplane',
'automobile',
'bird',
'cat',
'deer',
'dog',
'frog',
'horse',
'ship',
'truck'])
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000).batch(128).map(random_rotate)
plt.figure(figsize=(10,10),facecolor="white")
for b_img,b_label in tr_ds:
for i, img,label in zip(range(25),b_img,b_label):
plt.subplot(5,5,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(img)
plt.xlabel(labels[label])
break
plt.show()
Vérifions à quelle vitesse ce sera réellement. Tout d'abord, la vitesse dans "[application TF2.0] tf.data.Dataset pour accélérer l'augmentation des données" était la suivante.
résultat
Train on 50000 samples
50000/50000 [==============================] - 9s 175us/sample - loss: 2.3420 - accuracy: 0.1197
Train on 50000 samples
50000/50000 [==============================] - 7s 131us/sample - loss: 2.0576 - accuracy: 0.2349
Train on 50000 samples
50000/50000 [==============================] - 7s 132us/sample - loss: 1.7687 - accuracy: 0.3435
Train on 50000 samples
50000/50000 [==============================] - 7s 132us/sample - loss: 1.5947 - accuracy: 0.4103
Train on 50000 samples
50000/50000 [==============================] - 7s 132us/sample - loss: 1.4540 - accuracy: 0.4705
CPU times: user 1min 33s, sys: 8.03 s, total: 1min 41s
Wall time: 1min 14s
Ensuite, je publierai le code et les résultats de l'implémentation précédente.
dataset
%%time
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000)
tr_ds = tr_ds.batch(tr_x.shape[0]).map(random_rotate).repeat(5)
tr_ds = tr_ds.prefetch(tf.data.experimental.AUTOTUNE)
for img,label in tr_ds:
model.fit(x=img,y=label,batch_size=128)
résultat
Train on 50000 samples
50000/50000 [==============================] - 9s 176us/sample - loss: 1.3960 - accuracy: 0.5021
Train on 50000 samples
50000/50000 [==============================] - 9s 173us/sample - loss: 1.2899 - accuracy: 0.5430
Train on 50000 samples
50000/50000 [==============================] - 9s 175us/sample - loss: 1.2082 - accuracy: 0.5750
Train on 50000 samples
50000/50000 [==============================] - 9s 171us/sample - loss: 1.1050 - accuracy: 0.6133
Train on 50000 samples
50000/50000 [==============================] - 7s 132us/sample - loss: 1.0326 - accuracy: 0.6405
CPU times: user 52 s, sys: 15.4 s, total: 1min 7s
Wall time: 48.7 s
Est-ce que vous avez le sentiment que la carte de prétraitement fonctionne sur le CPU alors que le GPU fonctionne à 90%?
Comme il est de 48,7 secondes au total, il peut être raccourci d'environ 25 secondes.
En outre, le temps sans carte était de 35,1 secondes, vous pouvez donc voir que l'augmentation des données peut être effectuée assez rapidement.
Et si vous le faites de la même manière, vous pouvez ** tout le système keras.preprocessing.image
. ** **
show_data
def show_data(tf_dataset):
for b_img,b_label in tf_dataset:
for i, img,label in zip(range(25),b_img,b_label):
plt.subplot(5,5,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(img)
plt.xlabel(labels[label])
break
plt.show()
random_shift
Vous pouvez spécifier jusqu'à quel pourcentage du quart de travail au hasard.
random_shift
from tensorflow.keras.preprocessing.image import random_shift
from joblib import Parallel, delayed
def r_shift(imgs,wrg,hrg):
pics=imgs.numpy()
w = wrg.numpy()
h = wrg.numpy()
if tf.rank(imgs)==4:
X=Parallel(n_jobs=-1)( [delayed(random_shift)(pic,w,h,0,1,2) for pic in pics] )
X=np.asarray(X)
elif tf.rank(imgs)==3:
X=random_shift(pics, w,h, 0, 1, 2)
return X
@tf.function
def tf_random_shift(imgs, label):
x = tf.py_function(r_shift,[imgs,0.3,0.3],[tf.float32])
X = x[0]
X.set_shape(imgs.shape)
return X, label
Visualisation de données
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000).batch(128).map(tf_random_shift)
plt.figure(figsize=(10,10),facecolor="white")
show_data(tr_ds)
random_shear
Il peut être déformé. (Je ne connais pas les détails)
random_shear
from tensorflow.keras.preprocessing.image import random_shear
def r_shear(imgs,degree):
pics=imgs.numpy()
degree = degree.numpy()
if tf.rank(imgs)==4:
X=Parallel(n_jobs=-1)( [delayed(random_shear)(pic,degree,0,1,2) for pic in pics] )
X=np.asarray(X)
elif tf.rank(imgs)==3:
X=random_shear(pics,degree,0,1,2)
return X
@tf.function
def tf_random_shear(imgs, label):
x = tf.py_function(r_shear,[imgs,30],[tf.float32])
X = x[0]
X.set_shape(imgs.shape)
return X, label
Confirmation des données
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000).batch(128).map(tf_random_shear)
plt.figure(figsize=(10,10),facecolor="white")
show_data(tr_ds)
random_zoom
Effectue un zoom aléatoire.
random_zoom
from tensorflow.keras.preprocessing.image import random_zoom
def r_zoom(imgs,range_w,range_h):
pics=imgs.numpy()
zoom_range = (range_w.numpy(),range_h.numpy())
if tf.rank(imgs)==4:
X=Parallel(n_jobs=-1)( [delayed(random_zoom)(pic,zoom_range,0,1,2) for pic in pics] )
X=np.asarray(X)
elif tf.rank(imgs)==3:
X=random_zoom(pics,zoom_range,0,1,2)
return X
@tf.function
def tf_random_zoom(imgs, label):
x = tf.py_function(r_zoom,[imgs,0.5,0.5],[tf.float32])
X = x[0]
X.set_shape(imgs.shape)
return X, label
Résultat de sortie
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000).batch(128).map(tf_random_zoom)
plt.figure(figsize=(10,10),facecolor="white")
show_data(tr_ds)
Il ressemble à la même taille ... Améliorons-le.
enhanced
from tensorflow.keras.preprocessing.image import random_zoom
import random
def zoom_range_gen(random_state):
while True:
x=random.uniform(random_state[0],random_state[1])
yield (x,x)
def r_zoom(imgs):
pics=imgs.numpy()
random_state = [0.5,1.5]
if tf.rank(imgs)==4:
X=Parallel(n_jobs=-1)( [delayed(random_zoom)(pic,(x,y),0,1,2) for pic,(x,y) in zip(pics,zoom_range_gen(random_state))])
X=np.asarray(X)
elif tf.rank(imgs)==3:
zoom_range=next(zoom_range_gen)
X=random_zoom(pics,zoom_range,0,1,2)
return X
@tf.function
def tf_random_zoom_enhanced(imgs, label):
x = tf.py_function(r_zoom,[imgs],[tf.float32])
X = x[0]
X.set_shape(imgs.shape)
return X, label
Vérifions les données
Confirmation des données
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000).batch(128).map(tf_random_zoom_enhanced)
plt.figure(figsize=(10,10),facecolor="white")
show_data(tr_ds)
Ça fait du bien! !!
Ensuite, implémentez l'augmentation dans ce blog "Résumé de l'augmentation des données des images dans NumPy" L'augmentation dans Keras est basée sur Numpy, vous pouvez donc maintenant implémenter l'augmentation basée sur Numpy.
Je citerai l'image de Neko sur le blog "Résumé de l'augmentation des données des images dans NumPy". Je citerai également le contenu de cette implémentation. J'écrirai également la source dans le code.
random-flip
Implémentons une inversion aléatoire gauche-droite. Cela a déjà été implémenté dans le système TF, nous allons donc l'utiliser.
random-flip
@tf.function
def flip_left_right(image,label):
return tf.image.random_flip_left_right(image),label
@tf.function
def flip_up_down(image,label):
return tf.image.random_flip_up_down(image),label
Confirmation des données
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000).batch(128)
tr_ds = tr_ds.map(flip_left_right).map(flip_up_down)
plt.figure(figsize=(10,10),facecolor="white")
show_data(tr_ds)
random-clip
Ici, nous utiliserons l'augmentation d'échelle dans Blog.
Pour la mise en œuvre, je me suis référé au blog.
random-clip
from PIL import Image
def random_crop(pic, crop_size=(28, 28)):
try:
h, w, c = pic.shape
except ValueError:
raise ValueError("4Ds image can't decode")
#Déterminez le point supérieur gauche de l'image dans la section spécifiée
top = np.random.randint(0, h - crop_size[0])
left = np.random.randint(0, w - crop_size[1])
#Déterminez le point en bas à droite pour s'adapter à la taille
bottom = top + crop_size[0]
right = left + crop_size[1]
#Découpez uniquement l'intersection du point supérieur gauche et du point inférieur droit
pic = pic[top:bottom, left:right, :]
return pic
def scale_augmentation(pic, scale_range=(38, 80), crop_size=32):
scale_size = np.random.randint(*scale_range)
Ppic = Image.fromarray(pic)
Ppic = Ppic.resize((scale_size,scale_size),resample=1)
pic = np.asarray(Ppic)
return random_crop(pic, (crop_size, crop_size))
def r_crop(imgs):
pics=imgs.numpy()
pics=np.asarray(pics * 255.0,dtype=np.uint8)
random_state = (38,60)
crop_size=32
if tf.rank(imgs)==4:
X=Parallel(n_jobs=-1)([delayed(scale_augmentation)(pic,random_state,crop_size) for pic in pics ])
X=np.asarray(X)
elif tf.rank(imgs)==3:
X=scale_augmentation(pics,random_state,crop_size)
X=X/255.0
return X
@tf.function
def tf_random_crop(imgs, label):
x = tf.py_function(r_crop,[imgs],[tf.float32])
X = x[0]
X.set_shape(imgs.shape)
return X, label
Confirmation des données
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000).batch(128)
tr_ds = tr_ds.map(tf_random_crop)
plt.figure(figsize=(10,10),facecolor="white")
show_data(tr_ds)
random-erasing
Mettez cela en œuvre. Pour la mise en œuvre, j'ai fait référence à Blog.
random_erasing
def random_erasing(pic, p=0.5, s=(0.02, 0.4), r=(0.3, 3)):
#Masquer ou non
if np.random.rand() > p:
return pic
#Déterminez aléatoirement la valeur de pixel à masquer
mask_value = np.random.random()
try:
h, w, c = pic.shape
except ValueError:
raise ValueError("4Ds image can't decode")
#Taille du masque s de l'image originale(0.02~0.4)Décidez au hasard dans la double gamme
mask_area = np.random.randint(h * w * s[0], h * w * s[1])
#Ratio d'aspect du masque r(0.3~3)Décidé au hasard dans la gamme de
mask_aspect_ratio = np.random.rand() * r[1] + r[0]
#Déterminez la hauteur et la largeur du masque à partir de la taille et du rapport hauteur / largeur du masque
#Hauteur et largeur calculées(Soit)Peut être plus grande que l'image d'origine, donc corrigez
mask_height = int(np.sqrt(mask_area / mask_aspect_ratio))
if mask_height > h - 1:
mask_height = h - 1
mask_width = int(mask_aspect_ratio * mask_height)
if mask_width > w - 1:
mask_width = w - 1
top = np.random.randint(0, h - mask_height)
left = np.random.randint(0, w - mask_width)
bottom = top + mask_height
right = left + mask_width
pic[top:bottom, left:right, :].fill(mask_value)
return pic
def r_erase(imgs):
pics=imgs.numpy()
if tf.rank(imgs)==4:
X=Parallel(n_jobs=-1)([delayed(random_erasing)(pic) for pic in pics ])
X=np.asarray(X)
elif tf.rank(imgs)==3:
X=random_erasing(pics)
return X
@tf.function
def tf_random_erase(imgs, label):
x = tf.py_function(r_erase,[imgs],[tf.float32])
X = x[0]
X.set_shape(imgs.shape)
return X, label
Confirmation des données
tr_ds = tf.data.Dataset.from_tensor_slices((tr_x,tr_y)).shuffle(40000).batch(128)
tr_ds = tr_ds.map(tf_random_erase)
plt.figure(figsize=(10,10),facecolor="white")
show_data(tr_ds)
Avec CIFAR10, je suis un peu débordé, et il y a des choses que je ne comprends pas ...
Voici quelques points à garder à l'esprit lors de l'écriture de code. C'est une chose basique, donc je suis sûr que certains d'entre vous peuvent penser que c'est quelque chose. Étonnamment, il n'y avait que des écueils, je vais donc les noter ici.
tf.data.Dataset.map
Il y a quelques pièges ici, mais le comportement lors du mappage est du type Tensor
.
... ce que je veux dire, c'est que ** Eager Execution ne fonctionne pas sur Tensor géré par .map
. ** **
En d'autres termes, la multiplication normale, etc. peut être parfaitement convertie en opération de type TF avec @ tf.function
,
Sinon, les opérations qui ne peuvent pas être effectuées sans nombres réels, telles que .numpy ()
, ne peuvent pas être utilisées **. ** **
Je pense que c'est facile à comprendre si vous pensez qu'il est simplement décrit comme une expression comme x + y = z.
Ce qu'il faut utiliser, c'est activer le mode Eager.
Ce qu'il faut faire est d'utiliser ** tf.py_function
. ** **
Le mode graphique est comme une formule. C'est ce que j'ai fait avec Sess.run dans la série TF1.x. En concevant quelque chose comme la formule x + y = z, puis en attribuant des valeurs aux variables (c'est-à-dire en exécutant Session) 2 + 3 = 5 Z Tensor a une valeur de 5 pour la première fois. C'est là que le système TF1.x était difficile à comprendre.
Le mode Eager est comme la saisie d'une expression et la sortie de la valeur immédiatement exécutée. Sess.run est exécuté automatiquement et Graph est conservé, il semble donc facile à comprendre. (Je ne connais pas du tout ce domaine, j'espère donc que vous pourrez vous référer au guide officiel.) (S'il vous plaît dites-moi si vous faites une erreur)
tf.py_function
tf.py_function
est une fonction qui peut être partiellement exécutée en mode Eager comme décrit dans Guide.
Ici, en d'autres termes, il suffit de définir la fonction de boîte noire f (x) et de spécifier uniquement ce qui sort, et elle sera exécutée en mode graphique.
Ce sera comme ça.
En tant qu'expression, le côté TF veut une expression telle que x + f (a, b) = y, et ce qui est nécessaire ici, ce sont les types de données d'entrée et de sortie.
py_Pseudo code pour la fonction
def function(Entrée 1,Entrée 2):
#Ici, il fonctionne en mode Eager
#Traiter quelque chose
retour sortie 1,Sortie 2
[Sortie 1,Sortie 2] = tf.py_function(function,[Entrée 1,Entrée 2],[Sortie 1の型,Sortie 2の型])
Plus précisément, ce sera comme ça.
py_function
def function(data1,data2):
return data1+data2,data1*data2
@tf.function
def process(tensor1,tensor2):
[data1,data2]=tf.py_function(function,[tensor1,tensor2],[tf.float32,tf.float32])
return data1, data2
En d'autres termes, la fonction de fonction ici est exécutée en mode Eager au moment de l'exécution, donc elle devient tf.Tensor
avec une valeur dans Tensor
.
tf.Tensor
et Tensor
sont différents en mode Eager et en mode Graph, alors soyez prudent **
Ceci est fait en mode Eager et tf.Tensor
est introduit, il est donc normal de faire le premier .numpy ()
et de renvoyer le résultat comme numpy.
C'est là que surviennent de nombreux malentendus.
tf.data.Dataset.map
fonctionne uniquement en mode graphique au début. En plus de cela, certains doivent fonctionner en mode Eager.
Cela semble être le comportement de tf.data.Dataset.from_tensor_slices
. (Je suis désolé car ce ne sont pas des informations exactes)
Et lorsque les données sont déchargées, ce sera comme suit.
Si vous codez dans cet esprit, vous pourrez coder en douceur sans être dérouté par de mystérieux bugs.
Avec cela, je pense avoir été en mesure de vous dire comment développer une augmentation de données à usage général. Je veux faire autre chose comme ça! J'espère que ceux qui disent cela réussiront de la même manière. Je suis vraiment soulagé de résoudre le mystère de «py_function». Veuillez utiliser tous les moyens.
Ce blog "Résumé de l'augmentation des données des images dans NumPy" a été très utile pour l'implémenter. Je profite de cette occasion pour vous remercier.
Recommended Posts