Actuellement, je suis une personne M2 spécialisée en CS. J'écris généralement principalement du code d'apprentissage automatique dans PyTorch. C'est facile à écrire pour le moment. On dit que PyTorch est ** "Define by Run" **, et ce qui est bien, c'est que vous pouvez vérifier les résultats du calcul au milieu en même temps que le graphique de calcul et le moment de l'envoi des données, et il est facile à déboguer. Je suis un adepte de PyTorch, mais cette fois, j'avais besoin d'écrire du code dans TensorFlow, donc je vais le laisser comme un mémo pour ne pas oublier le savoir-faire à ce moment-là.
Avant de toucher PyTorch, j'étais capable de le comprendre car je touchais TensorFlow et keras, mais je ne pouvais pas comprendre le contenu après être devenu TensorFlow2, donc c'était une bonne étude cette fois. À propos, TensorFlow1 (TF1) a adopté la méthode de transfert des données à l'endroit où le graphique statique a été créé par la méthode appelée ** "Define and Run" , mais dans TensorFlow2 (TF2) ** " Il utilise la même méthode que Define by Run " et PyTorch.
Cet article est destiné à ceux qui souhaitent démarrer l'apprentissage automatique avec TensorFlow2. En regardant les articles d'autres personnes, il y a beaucoup d'articles sur TensorFlow1 et je n'ai pas trouvé beaucoup d'articles sur TensorFlow2 (TF2), donc j'espère que cela sera utile pour ceux qui envisagent de toucher TF2 à partir de maintenant. De plus, plutôt que la méthode d'apprentissage de type sklearn avec model.fit
etc., j'ai principalement écrit la méthode experte dans le tutoriel TensorFlow, donc j'espère que ceux qui sont intéressés pourront la lire.
** 1. Principes de base de TensorFlow ** ** 2. Création et apprentissage faciles de modèles avec keras (version débutante) ** ** 3. Apprentissage par transfert (+ réglage fin) ** ** 4. Créez votre propre modèle ** ** 5. Création et apprentissage de modèles avec TensorFLow2 (version Expert) **
** [1]. Lorsque vous utilisez votre propre ensemble de données ** ** [2]. À propos d'Augmentaion ** [3]. TensorBoard [4]. TFRecord
Tout d'abord, sur les bases.
Vous pouvez définir une constante avec tf.constant (valeur, dtype = None, shape = None, name = 'Const', verify_shape = False)
.
>>> import tensorflow as tf
>>>
>>> tf.constant([1, 2, 3])
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 2, 3], dtype=int32)>
>>>
>>> b = tf.constant('Hello') #Chaîne ok
<tf.Tensor: shape=(), dtype=string, numpy=b'Hello'>
>>>
Si vous spécifiez shape
, tous les éléments ont la même valeur.
>>> tf.constant(3, shape=[1, 3])
<tf.Tensor: shape=(1, 3), dtype=int32, numpy=array([[3, 3, 3]], dtype=int32)>
>>>
>>>
--Addition tf.add ()
, soustraction tf.subtract ()
, multiplication tf.mul ()
, division tf.divide ()
>>> tf.add(2,3)
<tf.Tensor: shape=(), dtype=int32, numpy=5>
>>>
>>> tf.subtract(5,3)
<tf.Tensor: shape=(), dtype=int32, numpy=2>
>>>
>>> tf.multiply(3,4)
<tf.Tensor: shape=(), dtype=int32, numpy=12>
>>>
>>> tf.divide(2,3)
0.6666666666666666
Utilisez tf.convert_to_tensor
.
Utilisez .numpy ()
pour un tableau Numpy.
>>> a = np.asarray([1,2,3])
>>> a
array([1, 2, 3])
>>> a.shape
(3,)
>>> tf.convert_to_tensor(a)
<tf.Tensor: shape=(3,), dtype=int64, numpy=array([1, 2, 3])>
>>> a
array([1, 2, 3])
>>> c = tf.convert_to_tensor(a)
>>> c
<tf.Tensor: shape=(3,), dtype=int64, numpy=array([1, 2, 3])>
>>> c.numpy()
array([1, 2, 3])
Ajouter le numéro de dimension tf.expand_dims (entrée, axe, nom = Aucun)
Utilisé lors de l'ajout de la taille du lot à la taille de l'image
>>> a = tf.constant([2,3])
>>> a.shape
TensorShape([2])
>>> b = tf.expand_dims(a,0)
>>> b.shape
TensorShape([1, 2])
>>>
tf.stack(values, axis=0, name='stack')
>>> x = tf.constant([1, 4])
>>> y = tf.constant([2, 5])
>>> z = tf.constant([3, 6])
>>> tf.stack([x, y, z], axis=0)
<tf.Tensor: shape=(3, 2), dtype=int32, numpy=
array([[1, 4],
[2, 5],
[3, 6]], dtype=int32)>
>>> tf.stack([x, y, z], axis=1)
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[1, 2, 3],
[4, 5, 6]], dtype=int32)>
>>>
tf.concat(values, axis, name='concat')
>>> t1 = [[1, 2, 3], [4, 5, 6]]
>>> t2 = [[7, 8, 9], [10, 11, 12]]
>>> tf.concat([t1,t2],0)
<tf.Tensor: shape=(4, 3), dtype=int32, numpy=
array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]], dtype=int32)>
>>> tf.concat([t1,t2],1)
<tf.Tensor: shape=(2, 6), dtype=int32, numpy=
array([[ 1, 2, 3, 7, 8, 9],
[ 4, 5, 6, 10, 11, 12]], dtype=int32)>
>>>
Ensuite, je voudrais faire un problème de classification avec des données mnist.
sample1.py
import tensorflow as tf
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data() #Lire les données
x_train, x_test = x_train / 255.0, x_test / 255.0 #Normalisation des données
#Construire un modèle
# Sequential API
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation='softmax')
])
# optimizer, loss,paramètres de métrique (paramètres d'apprentissage)
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
#Apprentissage
model.fit(x_train, y_train, epochs=5)
#Évaluation
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print('\nTest accuracy:', test_acc)
Je pense que c'est un style d'écriture que vous voyez souvent. Ce modèle modifie une image avec une entrée 28x28 dans un tableau unidimensionnel 784 en utilisant Flatten ()
. Ensuite, connectez les couches entièrement connectées, et la dernière couche entièrement connectée «10» est le nombre de classes (le nombre que vous souhaitez classer). Surtout à la fin, vous pouvez obtenir la probabilité de chaque classe en spécifiant softmax
comme activation. Pour l'apprentissage et l'évaluation, vous pouvez utiliser model.fit
, model.evaluate
, model.predict
, etc.
Le flux de base est (1) la lecture des données, (2) le prétraitement des données, (3) la construction d'un modèle, (4) les paramètres d'apprentissage détaillés, (5) l'apprentissage et (6) l'évaluation.
Après avoir construit un modèle simple avec mnist et l'avoir appris, essayons ensuite de transférer l'apprentissage. L'apprentissage par transfert est l'utilisation de poids appris à l'avance sur un grand nombre d'images telles que imagenet. Il présente les avantages de raccourcir le temps d'apprentissage et d'obtenir une certaine précision même avec une petite quantité de données. Il n'y a peut-être pas beaucoup de distinction entre l'apprentissage par transfert et le réglage fin, mais l'apprentissage par transfert maintient les poids de la première couche fixes et n'apprend que les couches que vous remplacez ou ajoutez vous-même. Le réglage fin ne fixe pas le poids de la première couche et reconditionne tous les paramètres.
L'apprentissage de transfert TensorFlow utilise tf.keras.applicacctions
pour charger le modèle.
import tensorflow as tf
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
#Entrée lors du chargement du modèle_Veuillez spécifier la forme
# include_top =Si la valeur est False, la couche de sortie ne sera pas lue.
IMG_SIZE = (224,224,3)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SIZE,
include_top = False,
weights='imagenet')
#Lors de la fixation du poids du modèle de base
base_model.trainable = False
#Vous pouvez réduire la quantité de calcul en utilisant Global Average Pooling sans couches entièrement connectées.
GAP_layer = GlobalAveragePooling2D()
#Puisqu'il est classé en 10 classes, il est de 10. Remplacez-le par votre propre tâche.
pred_layer = Dense(10, activation='softmax')
model = tf.keras.Sequential([
base_model,
GAP_layer,
pred_layer
])
# model.summary()Veuillez vérifier le modèle sur
Il existe d'autres modèles faciles à utiliser, voir Module: tf.keras.applications.
+α
Bien qu'il s'agisse de MobileNetV2, ce modèle est un modèle très "léger" qui fonctionne même sur les terminaux de périphérie. Une technique appelée `` Convolution séparable en profondeur '' qui calcule la convolution séparément dans la direction spatiale et la direction du canal est utilisée, ou une activation qui maximise la valeur de sortie après avoir passé ReLU
à travers la fonction d'activation appelée ReLU6
. C'est un modèle très intéressant qui utilise des fonctions. Si vous êtes intéressé, veuillez le vérifier.
À partir de TF2, vous pouvez utiliser l'API de sous-classification lors de la création de modèles. Maintenant que vous pouvez créer un modèle comme PyTorch, je vais le présenter.
import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten, Conv2D
from tensorflow.keras import Model
class Net(Model):
def __init__(self):
super(Net, self).__init__()
self.conv1 = Conv2D(32, 3, activation='relu') #Conv2D(filters, kernel_size, activation)
self.flatten = Flatten()
self.d1 = Dense(128, activation='relu') # Dense(units, activation)
self.d2 = Dense(10, activation='softmax') # Dense(units, activation)
def call(self, x):
x = self.conv1(x)
x = self.flatten(x)
x = self.d1(x)
return self.d2(x)
#Créer une instance du modèle
model = Net()
--Définir la couche utilisée par __init __
--Conv2D ()
est Conv2D (nombre de filtres, taille du noyau, fonction d'activation)
--Flatten ()
convertira la carte des caractéristiques en une dimension. Exemple 28x28-> 784
--Dense ()
est une couche entièrement connectée. Spécifie le nombre de dimensions dans l'espace de sortie
def call (self, x):
Pour la construction du modèle, reportez-vous à 4. Créez votre propre modèle.
#Définition de la fonction de perte
loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
#paramètres de l'optimiseur
optimizer = tf.keras.optimizers.Adam()
###Utilisé pour calculer la perte et l'accident
train_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')
test_loss = tf.keras.metrics.Mean(name='test_loss')
test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')
def train_step(images, labels):
with tf.GradientTape() as tape: #Gardez un historique des calculs à effectuer à partir de maintenant
predictions = model(images) #Mettez une image dans le modèle et obtenez une prédiction
loss = loss_object(labels, predictions) #Calculer la perte à partir d'une étiquette et d'une prédiction correctes
gradients = tape.gradient(loss, model.trainable_variables) #Différencier la fonction de perte avec des paramètres apprenables
optimizer.apply_gradients(zip(gradients, model.trainable_variables)) #Mis à jour avec des informations sur le dégradé
train_loss(loss)
train_accuracy(labels, predictions)
――La différenciation est absolument nécessaire pour l'apprentissage automatique
--Utilisez tf.GradientTape
où vous souhaitez conserver l'historique des calculs
tf.GradientTape
est une classe pour trouver le dégradé.import tensorflow as tf
x = tf.constant(3.0)
with tf.GradientTape() as tape:
tape.watch(x)
y = 3x+2
gradient = tape.gradient(y,x)
print(f'y = {y}')
print(f'x = {x}')
print(f'grad = {gradient}')
Output
y = 11.0
x = 3.0
grad = 3.0
Enfin, j'ai posté le code de mon train et val. Il est plein d'endroits Tsukkomi, mais veuillez l'utiliser comme référence.
def train(model, train_dataset, loss_object, optimizer, train_loss, train_acc, CONFIG, train_count):
cnt = 0
max_value = train_count + CONFIG.batch_size
with progressbar.ProgressBar(max_value=max_value) as bar:
for imgs, labels in train_dataset:
with tf.GradientTape() as tape:
preds = model(imgs, training=True)
loss = loss_object(labels, preds)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
train_loss.update_state(values=loss)
train_acc.update_state(labels, preds)
cnt = cnt + CONFIG.batch_size
bar.update(cnt)
loss_t = train_loss.result().numpy()
acc_t = train_acc.result().numpy()
train_loss.reset_states()
train_acc.reset_states()
return loss_t, acc_t
def val(model, val_dataset, loss_object,optimizer, val_loss, val_acc,CONFIG, val_count):
cnt = 0
max_value = val_count + CONFIG.batch_size
with progressbar.ProgressBar(max_value=max_value) as bar:
for imgs, labels in val_dataset:
preds = model(imgs, training=False)
loss = loss_object(labels, preds)
val_loss.update_state(values=loss)
val_acc.update_state(labels, preds)
cnt = cnt + CONFIG.batch_size
bar.update(cnt)
loss_v = val_loss.result().numpy()
acc_v = val_acc.result().numpy()
val_loss.reset_states()
val_acc.reset_states()
return loss_v, acc_v
-- tqdm
ne fonctionne pas bien dans mon environnement et j'utilise progressbar
--acc et loss sont mis à jour avec .update_state ()
--acc et les métriques de perte doivent être réinitialisées à chaque époque, alors réinitialisez avec .reset_states ()
training
like (imgs, training = False)
, qui applique Dropout
pendant l'entraînement, mais n'applique plus Dropout
pendant les tests. EstLorsque vous apprenez avec votre propre ensemble de données, je pense qu'il existe deux types principaux.
--Utilisez flow_from_directory ()
train_aug = ImageDataGenerator(
rescale=(1./255),
horizontal_flip=True,
vertical_flip=True,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
rotation_range=30,
)
test_aug = ImageDataGenerator(
rescale=(1./255),
)
train_generator = train_aug.flow_from_directory(
'data/train',
target_size=(224,224),
batch_size=batch_size,
class_mode='categorical'
)
test_generator = test_aug.flow_from_directory(
'data/val',
target_size=(224,224),
batch_size=batch_size,
class_mode='categorical'
)
Si vous souhaitez exporter le chemin et l'étiquette de l'image vers csv et le charger, je pense que cette méthode est souvent utilisée. Je pense que n'importe quelle bibliothèque fera l'affaire lors du chargement d'images. Dans ce qui suit, j'écrirai jusqu'à ce que j'obtienne le chemin du fichier de l'image et que j'en crée un ensemble de données.
import os
import pathlib
import tensorflow as tf
#Spécification du chemin de données
data_root_dir = 'data/train'
data_root = pathlib.Path(data_root_dir)
#Obtenir le chemin de l'image
all_image_paths = [str(path) for path in list(data_root.glob('*/*'))]
#Trier
all_image_paths = sorted(all_image_paths)
#Vérification
print(all_image_paths)
print(len(all_image_paths))
#Obtenir l'étiquette:Obtenir à partir du nom du répertoire
label_names = sorted(item.name for item in data_root.glob('*/'))
print(f' label : {label_names}')
#Dict de création de dictionnaire d'étiquettes{label:index}
label_to_index = dict((label, index) for index, label in enumerate(label_names))
print(label_to_index)
#Obtenez des étiquettes pour toutes les images
all_image_labels = [label_to_index[pathlib.Path(image_path).parent.name] for image_path in all_image_paths]
print(all_image_labels)
def load_data(all_image_paths, all_image_labels):
img_list = []
for filename in all_image_paths:
#Chargement des images
img = tf.io.read_file(filename)
#Décoder
img = tf.io.decode_image(img,channels = 3)
#Si vous ne redimensionnez pas ou ne redimensionnez pas, une erreur se produira lors de la création d'un ensemble de données.
img = tf.image.resize(img, [224,224])
img_list.append(img)
images = tf.stack(img_list, axis=0)
labels = tf.stack(all_image_labels, axis=0)
return tf.cast(images, tf.float32), tf.cast(labels, tf.int32)
#Obtenir une image et une étiquette
imgs, labels = load_data(all_image_paths, all_image_labels)
#Mélanger les données pour créer des lots
dataset = tf.data.Dataset.from_tensor_slices((img_list, label_list)).shuffle(len(all_image_labels)).batch(8)
#Vérification
for data1, data2 in dataset.take(10):
print(data1, data2)
tf.data.Dataset.from_tensor_slices ()
--Utilisez shuffle ()
si vous voulez mélanger votre jeu de données
--Utilisez batch ()
si vous voulez grouper par batchshuffle
et batch
est inversé.[2]. Augmentaion
--Il existe une image tf.image qui peut être utilisée pour Augmentaion, veuillez donc la vérifier sur le site officiel.
image = tf.io.decode_image(contents=image,
channels=CONFIG.channels,
dtype=tf.dtypes.float32)
#Normalisation
image = (image / 0.5) -1
# data_aug =Si vrai
if data_aug:
image = tf.image.random_flip_left_right(image=image)
image = tf.image.resize_with_crop_or_pad(image=image,
target_height=int(CONFIG.img_height*1.2),
target_width=int(CONFIG.img_width*1.2))
image = tf.image.random_crop(value=image, size=[CONFIG.img_height,CONFIG.img_width, CONFIG.channels])
else:
image = tf.image.resize(image_tensor, [CONFIG.img_height, CONFIG.img_width])
--Pour l'augmentation, albumentaions est recommandé. «J'ai un peu l'habitude d'écrire, mais c'est très pratique. (La découpe, etc. est incluse en standard)
[3]. TensorBoard
import tensorflow as tf
#Spécifiez l'emplacement de la broche du journal
writer = tf.summary.create_file_writer('tmp/mylogs')
with writer.as_default():
for step in range(100):
tf.summary.scalar("acc/train", 0.7, step=step)
tf.summary.scalar("acc/val", 0.5, step=step)
tf.summary.scalar("loss/train", 0.7, step=step)
tf.summary.scalar("loss/val", 0.5, step=step)
writer.flush()
tf.summary.scalar (tag, valeur, étape)
――Dans cet exemple, il est spécifié par une constante telle que 0,5 ou 0,7, mais vous pouvez transmettre la perte réelle ou l'acc.
――En plus des valeurs, vous pouvez également créer des images, veuillez donc essayer différentes choses.Lors de la vérification, veuillez spécifier le répertoire du journal des crachats comme suit et accédez à http: // localhost: 6006 /
$ tensorboard --logdir='./tmp/mylogs'
Serving TensorBoard on localhost; to expose to the network, use a proxy or pass --bind_all
TensorBoard 2.1.1 at http://localhost:6006/ (Press CTRL+C to quit)
[4]. TFRecord
--TFRecord est une version binaire des données au format recommandé par TensorFlow.
def _bytes_feature(value):
if isinstance(value, type(tf.constant(0.))):
value = value.numpy()
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
def _float_feature(value):
return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))
def _int64_feature(value):
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
def get_info(data_root_dir):
data_root = pathlib.Path(data_root_dir)
all_image_paths = [str(path) for path in list(data_root.glob('*/*'))]
# Get label
label_names = sorted(item.name for item in data_root.glob('*/'))
# dict {label:index}
label_to_index = dict((label, index) for index, label in enumerate(label_names))
print(label_to_index)
# Get all images label
all_image_labels = [label_to_index[pathlib.Path(image_path).parent.name] for image_path in all_image_paths]
return all_image_paths, all_image_labels
def dataset_to_tfrecord(dataset_dir, tfrecord_name):
#Obtenez des images et des étiquettes pour chaque répertoire
image_paths, image_labels = get_info(dataset_dir)
image_paths_and_labels_dict = {}
#Convertir en type de dictionnaire
for i in range(len(image_paths)):
image_paths_and_labels_dict[image_paths[i]] = image_labels[i]
with tf.io.TFRecordWriter(path=tfrecord_name) as writer:
for image_path, label in image_paths_and_labels_dict.items():
image_string = open(image_path, 'rb').read()
feature = {
'label' : _int64_feature(label),
'image' : _bytes_feature(image_string)
}
tf_example = tf.train.Example(features=tf.train.Features(feature=feature))
writer.write(tf_example.SerializeToString())
Pour plus d'informations, consultez TensorFLow TFRecord et tf.Example.
Nous avons vu comment classer des images avec TensorFLow. La «Session» et le «espace réservé» qui étaient dans TF1 ont disparu, le «Mode Eager» a été mis par défaut, et keras est devenu une API de haut niveau du standard TensorFLow, ce qui facilite son utilisation personnelle. Était là. C'est difficile de s'y habituer, mais une fois que je m'y suis habitué, TensorFLow était facile à écrire. À l'avenir, j'aimerais pouvoir maîtriser à la fois Pytorch et TensorFlow. Il existe de nombreuses bibliothèques utiles telles que ** pathlib ** et ** albumentaions ** introduites au milieu de l'article, donc si vous ne l'utilisez pas, veuillez l'utiliser.