Suite de l'article précédent .
En utilisant ResNet, un type de réseau neuronal convolutif, Je voudrais classer les images.
ResNet est un type d'algorithme de réseau de neurones convolutif que nous avons fait dans l'article précédent. Annoncé par Microsoft en 2015, il a ensuite été utilisé dans une IA Go appelée AlphaGO, qui a battu le champion du monde de Go.
En utilisant un si bon algorithme, Je voudrais classer les images comme dans l'article précédent.
Plus le réseau de neurones convolutif est convolutif, plus il est facile de reconnaître des caractéristiques complexes. Cependant, le surapprentissage est également probable et son augmentation aggrave souvent les résultats. ResNet résout ce problème en ajoutant une structure de raccourcis appelée Blocs résiduels.
C'est une structure qui crée une route de détour appelée connexion de raccourci avant le processus de convolution et passe à la couche suivante sans effectuer le processus de convolution lorsque l'apprentissage n'est plus nécessaire.
Dans le cas de la prédiction de régression, j'ai créé un processus pour interrompre même si le nombre d'époques spécifié n'est pas atteint si aucune amélioration n'est observée pendant l'entraînement avec Early Stopping, mais c'est une image pour le faire dans la couche de convolution.
ResNet a deux architectures, Plain et Bottleneck.
Cette fois, nous utiliserons une architecture appelée Bottleneck pour faire des prédictions de classification.
Le bloc résiduel de l'architecture Bottleneck a la structure suivante.
Soit la première fois le nombre de noyaux x et la taille du noyau y
La deuxième fois, le nombre de noyaux x, taille du noyau 3y
La troisième fois, le nombre de noyaux est 4x, la taille du noyau est y
Puisque tensorflow utilise 1.13.1, veuillez l'installer avec la commande suivante.
!pip install tensorflow==1.13.1
Une fois installé, importez la bibliothèque.
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.callbacks import LearningRateScheduler
from tensorflow.keras.layers import Activation, Add, BatchNormalization, Conv2D, Dense, GlobalAveragePooling2D, Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.regularizers import l2
from tensorflow.keras.utils import to_categorical
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
Les données utilisent le même cifar10 que le CNN précédent.
(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()
Parce que ce modèle a des branches compliquées et de multiples sorties Construisez avec une structure de réseau appelée API fonctionnelle au lieu de Sequentinal.
FonctionnelAPI Plutôt que d'ajouter des couches au modèle lui-même avec model.add comme Sequentinal Nous allons le créer en passant le calque créé au calque suivant et en le passant finalement au modèle.
Plus précisément, écrivez comme suit. __ * Puisqu'il s'agit d'un exemple d'API fonctionnelle, le code suivant lui-même n'est pas utilisé pour ce programme. __
input = Input(Shape=(784,)) #Couche d'entrée
x = Conv2D(64, (3, 3), activation='relu', padding='same')(input) #Ajoutez le calque d'entrée à la fin et passez-le
x = MaxPool2D(pool_size=(2, 2))(x) #Couche précédente à la fin(x)Ajouter et passer
model = Model(inputs=input, outputs=x) #La modélisation
Le modèle Sequentinal était une méthode d'ajout de couches telles que Conv2D dans model.add, Dans FunctionalAPI, passez-le dans () à la fin du calque suivant au lieu de model.add.
Cette fois, nous utiliserons la couche de convolution à plusieurs reprises, nous en ferons donc une fonction. Créons une fonction appelée def conv.
def conv(filters, kernel_size, strides=1):
return Conv2D(filters, kernel_size, strides=strides, padding='same', use_bias=False,
kernel_initializer='he_normal', kernel_regularizer=l2(0.0001))
filtres: nombre de noyaux kernel_size: taille du noyau foulées: nombre de foulées (valeur numérique de la valeur à définir lors de la création d'une carte d'entités) foulées: méthode de remplissage use_bias: s'il faut ajouter un biais kernel_initializer: valeur initiale de la matrice de poids. S'il est normal, c'est une distribution normale. kernel_regularizer: méthode de régularisation des pénalités appliquées aux poids. Utilisez la régularisation L2.
Cette fois, je vais créer trois types de blocs résiduels avec différents nombres de noyaux. Je souhaite traiter chaque bloc résiduel 18 fois.
Lors d'un traitement 18 fois, le contenu du traitement est légèrement différent entre le premier bloc résiduel et le deuxième bloc résiduel et les suivants. Par souci de clarté, je voudrais séparer le premier bloc résiduel du deuxième bloc résiduel et des suivants.
#Bloc résiduel à exécuter la première fois
def first_residual_unit(filters, strides):
def f(x):
# →BN→ReLU
x = BatchNormalization()(x)
b = Activation('relu')(x)
#Couche pliante → BN → ReLU
x = conv(filters // 4, 1, strides)(b)
x = BatchNormalization()(x)
x = Activation('relu')(x)
#Couche pliante → BN → ReLU
x = conv(filters // 4, 3)(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
#Couche pliante →
x = conv(filters, 1)(x)
#Ajuster la taille de la forme du raccourci
sc = conv(filters, 1, strides)(b)
# Add
return Add()([x, sc])
return f
Je vais expliquer le code du bloc résiduel. `` x = BatchNormalization () (x) '' est un processus qui optimise l'apprentissage. Il existe deux modèles de synchronisation à utiliser.
Le motif à exécuter au tout début du bloc résiduel, Un motif qui prend en sandwich entre la couche convolutionnelle et la fonction d'activation. On dit qu'il vaut mieux ne pas l'utiliser avec Dropout, donc je n'utilise pas Dropout cette fois.
Ensuite, appliquez la fonction d'activation avec `` b = Activation ('relu') (x) ''. Ensuite, nous allons à la couche de convolution, mais si nous n'avons pas besoin d'apprendre à ce stade, nous allons passer à Ajouter. Il s'agit de la structure de raccourcis décrite ci-dessus.
De `` x = conv (filters // 4, 1, strides) (b) '', cela devient une couche de convolution. Le quotient correspond aux filtres (taille du noyau) divisés par 4. Cela est dû à l'utilisation de l'architecture Bottleneck. Vous pouvez voir que la troisième couche de pliage est traitée sans rupture. __ Si la première fois est le nombre de noyaux x et la taille du noyau y La deuxième fois, le nombre de noyaux x, taille du noyau 3y La troisième fois, le nombre de noyaux est 4x, la taille du noyau est y__
Dans `` sc = conv (filters, 1, strides) (b) '' Ajustement du raccourci à la même taille que x.
Enfin, retour avec retour, et le premier bloc résiduel est terminé.
Vient ensuite le bloc résiduel à exécuter à partir de la deuxième fois.
Les foulées ne sont pas nécessaires pour l'argument. L'ajustement du numéro de forme du raccourci n'est pas nécessaire car il a déjà été effectué dans le calque précédent. L'autre traitement est le même que la première fois.
#Bloc résiduel à exécuter à partir de la deuxième fois
def residual_unit(filters):
def f(x):
sc = x
# →BN→ReLU
x = BatchNormalization()(x)
x = Activation('relu')(x)
#Couche pliante → BN → ReLU
x = conv(filters // 4, 1)(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
#Couche pliante → BN → ReLU
x = conv(filters // 4, 3)(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
#Couche pliante →
x = conv(filters, 1)(x)
# Add
return Add()([x, sc])
return f
Maintenant que nous avons une fonction de création de bloc résiduel qui ne peut être exécutée que la première fois et la deuxième fois et les suivantes, Combinez ces deux pour créer le nombre spécifié de fonctions de création de bloc résiduel.
#Une fonction qui exécute un nombre spécifié de blocs résiduels
def residual_block(filters, strides, unit_size):
"""
filters:Nombre de noyaux
strides:Nombre de foulées
unit_size:Nombre d'exécutions de bloc résiduel
"""
def f(x):
#Bloc résiduel à exécuter la première fois
x = first_residual_unit(filters, strides)(x)
#Bloc résiduel après la deuxième fois(Je l'ai déjà exécuté une fois, alors unité_Soustraire 1 de la taille)
for i in range(unit_size-1):
x = residual_unit(filters)(x)
return x
return f
Maintenant que les préparatifs sont terminés, créons le modèle.
Créez selon le flux suivant.
Étant donné que la sortie de GlobalAveragePooling2D est unidimensionnelle, Il n'est pas nécessaire de convertir en une dimension avec Flatten avant la jointure complète.
#Forme des données d'entrée (couche d'entrée)
input = Input(shape=(32,32, 3))
#Couche pliante(passer l'entrée)
x = conv(16, 3)(input)
#Bloc résiduel 18 x 3
x = residual_block(64, 1, 18)(x)
x = residual_block(128, 2, 18)(x)
x = residual_block(256, 2, 18)(x)
# →BN→ReLU
x = BatchNormalization()(x)
x = Activation('relu')(x)
#Couche de regroupement
x = GlobalAveragePooling2D()(x)
#Couche entièrement connectée
output = Dense(10, activation='softmax', kernel_regularizer=l2(0.0001))(x)
#Créer un modèle
model = Model(inputs=input, outputs=output)
Si vous souhaitez convertir en modèle TPU, le code suivant (* Ceci est le code pour tensorflow 1.13.1.)
import tensorflow as tf
import os
tpu_model = tf.contrib.tpu.keras_to_tpu_model(
model,
strategy=tf.contrib.tpu.TPUDistributionStrategy(
tf.contrib.cluster_resolver.TPUClusterResolver(tpu='grpc://' + os.environ['COLAB_TPU_ADDR'])
)
)
tpu_model.compile(loss='categorical_crossentropy', optimizer=SGD(momentum=0.9), metrics=['acc'])
Normaliser les données d'image à l'aide d'ImageDataGenerator Gonflez l'image. (Les données d'entraînement sont gonflées uniquement)
Après la normalisation, utilisez fit pour calculer les statistiques.
Puisqu'il y a 50 000 images, dois-je les gonfler? Vous pourriez penser, mais comme il n'y en a que 5000 par type, Il vaut mieux le gonfler.
#Normalisation et remplissage des données d'entraînement
train_gen = ImageDataGenerator(
featurewise_center=True,
featurewise_std_normalization=True,
width_shift_range=0.125,
height_shift_range=0.125,
horizontal_flip=True)
#Processus de normalisation des données de test (le gonflage n'a pas de sens, donc pas)
test_gen = ImageDataGenerator(
featurewise_center=True,
featurewise_std_normalization=True)
#Pré-calculer les statistiques pour l'ensemble de données
for data in (train_gen, test_gen):
data.fit(train_images)
Maintenant que nous avons créé le modèle et prétraité les données, nous pouvons commencer la formation.
En apprenant cette fois, en utilisant LearningRateScheduler Je voudrais changer le taux d'apprentissage pour chaque nombre d'époques.
Taux d'apprentissage 0,1 de 1 à 79 80 ~ 119 a un taux d'apprentissage de 0,01, 120 ou plus peuvent être réduits à 0,001 pour chaque nombre de fois. Vous pouvez raccourcir le temps d'apprentissage.
#Préparation à LearningRateScheduler
def step_decay(epoch):
x = 0.1
if epoch >= 80: x = 0.01
if epoch >= 120: x = 0.001
return x
lr_decay = LearningRateScheduler(step_decay)
Effectuer l'apprentissage. Cette fois, nous gonflons l'image, alors Il y a quelques options.
#Apprentissage
batch_size = 128
history = tpu_model.fit_generator(
train_gen.flow(train_images, train_labels, batch_size=batch_size),
epochs=100,
steps_per_epoch=train_images.shape[0] // batch_size,
validation_data=test_gen.flow(test_images, test_labels, batch_size=batch_size),
validation_steps=test_images.shape[0] // batch_size,
callbacks=[lr_decay])
Cette étude prendra environ une heure. Lorsque la formation est terminée, le résultat de la formation sera affiché dans un graphique.
plt.plot(history.history['acc'], label='acc')
plt.plot(history.history['val_acc'], label='val_acc')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(loc='best')
plt.show()
Évaluons l'apprentissage avec test_images. Dans le CNN précédent, le taux de réponse correcte était de 71,2%. Et Resnet?
batch_size = 128
test_loss, test_acc = tpu_model.evaluate_generator(
test_gen.flow(test_images, test_labels, batch_size=batch_size),
steps=10)
print('loss: {:.3f}\nacc: {:.3f}'.format(test_loss, test_acc ))
Le taux de résultat est de 93,4%! Amélioré considérablement par rapport à CNN traditionnel.
Vérifions le résultat de la prédiction de chaque image au cas où.
#Affichage de l'image déduite
for i in range(16):
plt.subplot(2, 8, i+1)
plt.imshow(test_images[i])
plt.show()
#Affichage de l'étiquette déduite
test_predictions = tpu_model.predict_generator(
test_gen.flow(test_images[0:16], shuffle = False, batch_size=16),
steps=16)
test_predictions = np.argmax(test_predictions, axis=1)[0:16]
labels = ['airplane', 'automobile', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck']
print([labels[n] for n in test_predictions])
Le modèle sur Google colaboratory, Téléchargez-le dans votre environnement local avec le code ci-dessous.
#Enregistrer le modèle
tpu_model.save('resnet.h5')
#Téléchargez le modèle dans votre environnement local
from google.colab import files
files.download( "resnet.h5" )
Ça fait longtemps, mais c'est tout.