Introduction à la création d'IA avec Python! Partie 4 J'ai fait une prédiction de classification d'image avec un taux de réponse correct de 93,4% avec ResNet

À propos de cet article

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.

En quoi est-il différent du réseau de neurones convolutif conventionnel?

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.

Qu'est-ce qu'un bloc résiduel?

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. image.png 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

Importation de package

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

base de données

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()

création de modèle

Création de modèle avec l'API fonctionnelle

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.

Créer une couche convolutive

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.

Créer un bloc résiduel à exécuter la première fois

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é.

Bloc résiduel à exécuter à partir de la deuxième fois

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

Créer une fonction qui exécute le nombre spécifié de blocs résiduels

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

La modélisation

Maintenant que les préparatifs sont terminés, créons le modèle. Créez selon le flux suivant. image.png

É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)

Conversion en modèle TPU

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'])
    )
)

Compiler le modèle

tpu_model.compile(loss='categorical_crossentropy', optimizer=SGD(momentum=0.9), metrics=['acc'])

Normalisation et remplissage d'image

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)

Apprentissage

Maintenant que nous avons créé le modèle et prétraité les données, nous pouvons commencer la formation.

Changement de taux d'apprentissage

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)

Exécution de l'apprentissage

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()

image.png

Évaluation de l'apprentissage

É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 ))

image.png

Le taux de résultat est de 93,4%! Amélioré considérablement par rapport à CNN traditionnel.

inférence

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])

image.png

Enregistrer le modèle

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.

Recommended Posts

Introduction à la création d'IA avec Python! Partie 4 J'ai fait une prédiction de classification d'image avec un taux de réponse correct de 93,4% avec ResNet
Introduction à la création d'IA avec Python! Partie 3 J'ai essayé de classer et de prédire les images avec un réseau de neurones convolutifs (CNN)
[Python] J'ai créé une visionneuse d'images avec une fonction de tri simple.
Je veux colorer une partie de la chaîne Excel avec Python
Django super introduction par les débutants Python! Partie 5 J'ai créé une application de journal super simple avec une vue générale basée sur la classe
J'ai essayé de trouver l'entropie de l'image avec python
J'ai créé un package pour filtrer les séries chronologiques avec python
J'ai créé une application de livre simple avec python + Flask ~ Introduction ~
J'ai fait une image ponctuelle de l'image d'Irasutoya. (partie 1)
J'ai fait une image ponctuelle de l'image d'Irasutoya. (partie 2)
J'ai créé une fonction pour découper l'image de python openCV, alors veuillez l'utiliser.
Introduction à la création d'IA avec Python! Partie 1 J'ai essayé de classer et de prédire le nombre à partir de l'image du numéro manuscrit
J'ai créé une bibliothèque qui lit facilement les fichiers de configuration avec Python
Système de notation IPynb réalisé avec TA d'introduction à la programmation (Python)
[Introduction à StyleGAN] J'ai joué avec "The Life of a Man" ♬
Essayez d'extraire une chaîne de caractères d'une image avec Python3
J'ai fait une loterie avec Python.
J'ai essayé de créer une liste de nombres premiers avec python
[Introduction à Python] Comment obtenir la taille d'une liste avec la taille de la liste
J'ai créé un démon avec Python
J'ai essayé de créer une fonction de similitude d'image avec Python + OpenCV
Introduction de "scikit-mobility", une bibliothèque qui vous permet d'analyser facilement les données de flux humain avec Python (Partie 1)
Django super introduction par les débutants Python! Partie 2 J'ai essayé d'utiliser les fonctions pratiques du modèle
J'ai créé un outil pour parcourir automatiquement plusieurs sites avec Selenium (Python)
Comment couper la partie inférieure droite de l'image avec Python OpenCV
[Introduction à Python] Comment trier efficacement le contenu d'une liste avec le tri par liste
J'ai fait un programme pour vérifier la taille d'un fichier avec Python
J'ai fait un compteur de caractères avec Python
Introduction au remplissage d'image Python Remplissage d'image à l'aide d'ImageDataGenerator
J'ai fait une carte hexadécimale avec Python
J'ai fait un jeu rogue-like avec Python
J'ai fait un simulateur de neurones avec Python
Django super introduction par les débutants Python! Partie 4 J'ai créé une application de journal super simple (créée uniquement avec des fonctions sans utiliser de vues à usage général basées sur les classes)
Partie 1 J'ai écrit un exemple de la réponse au problème de référence de l'écriture hors ligne en temps réel en Python
Je souhaite envoyer Gmail avec Python, mais je ne peux pas en raison d'une erreur
J'ai créé une commande appdo pour exécuter des commandes dans le contexte de l'application
J'ai fait un modèle de classification d'images et essayé de le déplacer sur mobile
J'ai essayé d'extraire le dessin au trait de l'image avec Deep Learning
J'ai fait un script pour enregistrer la fenêtre active en utilisant win32gui de Python
Une histoire à laquelle j'étais accro après la communication SFTP avec python
[Introduction à Python] Comment obtenir l'index des données avec l'instruction for
J'ai fait une prévision météo de type bot avec Python.
J'ai créé une application graphique avec Python + PyQt5
J'ai essayé de créer un bloqueur de filles pourries sur Twitter avec Python ①
Je veux faire un jeu avec Python
Comment recadrer une image avec Python + OpenCV
[Python] J'ai créé un téléchargeur Youtube avec Tkinter.
J'ai essayé de corriger la forme trapézoïdale de l'image
Je veux écrire dans un fichier avec Python
J'ai fait un jeu de cueillette avec Python
Made Mattermost Bot avec Python (+ Flask)
Une introduction au logiciel d'interface graphique de la plate-forme de classe fait avec Python / Tkinter! (Et de nombreux Try and Error)! (Au milieu de l'écriture)
[Python] J'ai créé une application pour pratiquer la délicate distinction vocale des mots anglais.
J'ai créé un programme pour convertir des images en art ASCII avec Python et OpenCV
[Introduction au trading système] J'ai dessiné un oscillateur stochastique avec python et joué avec ♬
[Python] J'ai essayé de créer automatiquement un rapport quotidien de YWT avec la messagerie Outlook
Je ne peux pas dormir tant que je n'ai pas construit un serveur !! (Introduction au serveur Python faite en un jour)
Traitement d'image avec Python (j'ai essayé de le binariser en art mosaïque 0 et 1)
J'ai créé un programme en Python qui change les données de 1 minute de FX en une heure arbitraire (1 heure, etc.)
Django super introduction par les débutants Python! Partie 1 J'ai essayé d'afficher une page HTML qui ne dit que "Hello World"
Je veux convertir une image en WebP avec sucette