J'étudiais l'apprentissage profond et j'ai essayé de faire une sortie comprenant l'établissement de connaissances, alors je l'ai écrit sous forme d'article. Le code complet est répertorié sur GitHub. Cette fois, nous avons implémenté VGG16, qui est célèbre pour les modèles CNN, en utilisant Keras, un framework d'apprentissage en profondeur qui facilite la création de modèles et identifie des images de CIFAR10.
Google Colaboratory
import
import numpy as np
import sys
%matplotlib inline
import matplotlib.pyplot as plt
import keras
from keras.datasets import cifar10
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten, BatchNormalization
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
Tout d'abord, importez les bibliothèques requises. Keras (Official Document) est un framework d'apprentissage profond de haut niveau avec TensorFlow etc. comme back-end, ce qui facilite la conception et l'extension de modèles complexes. ..
De plus, CIFAR10 est un ensemble de données d'images en couleur fourni par l'Université de Toronto, qui comprend des avions, des voitures, des oiseaux, des chats, des cerfs, etc. 10 types d'images de chiens, grenouilles, chevaux, navires et camions sont stockés en 32 x 32 pixels. CIFAR10 est fourni par défaut dans le package keras.data, similaire à MNIST pour les données numériques manuscrites.
datasets
'''Charger le jeu de données'''
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
'''Définition de la taille du lot, du nombre de classes, du nombre d'époques'''
batch_size=64
num_classes=10
epochs=20
'''one-vectorisation à chaud'''
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
'''affichage de forme'''
print("x_train : ", x_train.shape)
print("y_train : ", y_train.shape)
print("x_test : ", x_test.shape)
print("y_test : ", y_test.shape)
Ensuite, chargez les données d'entraînement et les données de test avec load_data (). La taille du lot et le nombre d'époques sont définis ci-dessus. De plus, les données d'étiquette sont converties en un vecteur one-hot (un vecteur dans lequel un seul composant est 1 et les autres sont tous 0) afin qu'il puisse être manipulé par softmax. Ces formes ressemblent à ceci:
Résultat de sortie
x_train : (50000, 32, 32, 3)
y_train : (50000, 10)
x_test : (10000, 32, 32, 3)
y_test : (10000, 10)
Le nombre de données d'entraînement est de 50 000 et le nombre de données de test est de 10 000.
Faisons maintenant le modèle VGG16. La série VGG est expliquée en détail dans cet article. En gros, VGG16 est un modèle CNN créé par l'équipe VGG, qui est en compétition pour la détection d'objets et la classification d'images ILSVRC (IMAGENET Large Scale Visulal Recognition) Est-ce comme un modèle qui s'est classé haut dans Challenge)? En raison de sa conception relativement simple et de ses performances élevées, il est souvent mentionné dans l'introduction de l'apprentissage en profondeur. L'origine de 16 semble être qu'il se compose de 16 couches au total. La structure de VGG16 est illustrée dans la figure ci-dessous. (Extrait de Article original. VGG16 est le modèle D.)
Il y a 13 couches convolutives avec une taille de filtre de 3x3 et 3 couches entièrement connectées. J'ai essayé d'implémenter VGG16 en référence à la figure ci-dessus.
VGG16
'''VGG16'''
input_shape=x_train.shape[1:]
model = Sequential()
model.add(Conv2D(filters=64, kernel_size=(3,3), strides=(1,1), padding='same', input_shape=input_shape, name='block1_conv1'))
model.add(BatchNormalization(name='bn1'))
model.add(Activation('relu'))
model.add(Conv2D(filters=64, kernel_size=(3,3), strides=(1,1), padding='same', name='block1_conv2'))
model.add(BatchNormalization(name='bn2'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same', name='block1_pool'))
model.add(Conv2D(filters=128, kernel_size=(3,3), strides=(1,1), padding='same', name='block2_conv1'))
model.add(BatchNormalization(name='bn3'))
model.add(Activation('relu'))
model.add(Conv2D(filters=128, kernel_size=(3,3), strides=(1,1), padding='same', name='block2_conv2'))
model.add(BatchNormalization(name='bn4'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same', name='block2_pool'))
model.add(Conv2D(filters=256, kernel_size=(3,3), strides=(1,1), padding='same', name='block3_conv1'))
model.add(BatchNormalization(name='bn5'))
model.add(Activation('relu'))
model.add(Conv2D(filters=256, kernel_size=(3,3), strides=(1,1), padding='same', name='block3_conv2'))
model.add(BatchNormalization(name='bn6'))
model.add(Activation('relu'))
model.add(Conv2D(filters=256, kernel_size=(3,3), strides=(1,1), padding='same', name='block3_conv3'))
model.add(BatchNormalization(name='bn7'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same', name='block3_pool'))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides=(1,1), padding='same', name='block4_conv1'))
model.add(BatchNormalization(name='bn8'))
model.add(Activation('relu'))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides=(1,1), padding='same', name='block4_conv2'))
model.add(BatchNormalization(name='bn9'))
model.add(Activation('relu'))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides=(1,1), padding='same', name='block4_conv3'))
model.add(BatchNormalization(name='bn10'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same', name='block4_pool'))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides=(1,1), padding='same', name='block5_conv1'))
model.add(BatchNormalization(name='bn11'))
model.add(Activation('relu'))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides=(1,1), padding='same', name='block5_conv2'))
model.add(BatchNormalization(name='bn12'))
model.add(Activation('relu'))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides=(1,1), padding='same', name='block5_conv3'))
model.add(BatchNormalization(name='bn13'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same', name='block5_pool'))
model.add(Flatten(name='flatten'))
model.add(Dense(units=4096, activation='relu', name='fc1'))
model.add(Dense(units=4096, activation='relu', name='fc2'))
model.add(Dense(units=num_classes, activation='softmax', name='predictions'))
model.summary()
Il existe deux types de méthodes de construction de modèles Keras, le modèle séquentiel et le modèle d'API fonctionnelle, mais cette fois j'ai utilisé le modèle séquentiel plus simple. Les modèles sont construits en série en les ajoutant au modèle comme décrit ci-dessus. Veuillez noter que le modèle VGG est à l'origine destiné à ILSVRC, donc la taille d'entrée et la taille de sortie ne correspondent pas à ces données. Par conséquent, la taille d'entrée / sortie est modifiée comme suit. Cette fois, j'utilise le CIFAR10 beaucoup plus simple, vous n'aurez donc peut-être pas besoin d'utiliser un modèle aussi complexe.
Changer avant | Après le changement | |
---|---|---|
Taille d'entrée | 224×224 | 32×32 |
Taille de sortie | 1000 | 10 |
De plus, la normalisation par lots est actuellement utilisée comme méthode pour éviter le surapprentissage des données d'entraînement, mais elle n'est pas utilisée car cette méthode n'a pas été établie lorsque VGG a été annoncé. Cette fois, j'ai également adopté cela. Le résultat de sortie du modèle est le suivant.
Résultat de sortie
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
block1_conv1 (Conv2D) (None, 32, 32, 64) 1792
_________________________________________________________________
bn1 (BatchNormalization) (None, 32, 32, 64) 256
_________________________________________________________________
activation_1 (Activation) (None, 32, 32, 64) 0
_________________________________________________________________
block1_conv2 (Conv2D) (None, 32, 32, 64) 36928
_________________________________________________________________
bn2 (BatchNormalization) (None, 32, 32, 64) 256
_________________________________________________________________
activation_2 (Activation) (None, 32, 32, 64) 0
_________________________________________________________________
block1_pool (MaxPooling2D) (None, 16, 16, 64) 0
_________________________________________________________________
block2_conv1 (Conv2D) (None, 16, 16, 128) 73856
_________________________________________________________________
bn3 (BatchNormalization) (None, 16, 16, 128) 512
_________________________________________________________________
activation_3 (Activation) (None, 16, 16, 128) 0
_________________________________________________________________
block2_conv2 (Conv2D) (None, 16, 16, 128) 147584
_________________________________________________________________
bn4 (BatchNormalization) (None, 16, 16, 128) 512
_________________________________________________________________
activation_4 (Activation) (None, 16, 16, 128) 0
_________________________________________________________________
block2_pool (MaxPooling2D) (None, 8, 8, 128) 0
_________________________________________________________________
block3_conv1 (Conv2D) (None, 8, 8, 256) 295168
_________________________________________________________________
bn5 (BatchNormalization) (None, 8, 8, 256) 1024
_________________________________________________________________
activation_5 (Activation) (None, 8, 8, 256) 0
_________________________________________________________________
block3_conv2 (Conv2D) (None, 8, 8, 256) 590080
_________________________________________________________________
bn6 (BatchNormalization) (None, 8, 8, 256) 1024
_________________________________________________________________
activation_6 (Activation) (None, 8, 8, 256) 0
_________________________________________________________________
block3_conv3 (Conv2D) (None, 8, 8, 256) 590080
_________________________________________________________________
bn7 (BatchNormalization) (None, 8, 8, 256) 1024
_________________________________________________________________
activation_7 (Activation) (None, 8, 8, 256) 0
_________________________________________________________________
block3_pool (MaxPooling2D) (None, 4, 4, 256) 0
_________________________________________________________________
block4_conv1 (Conv2D) (None, 4, 4, 512) 1180160
_________________________________________________________________
bn8 (BatchNormalization) (None, 4, 4, 512) 2048
_________________________________________________________________
activation_8 (Activation) (None, 4, 4, 512) 0
_________________________________________________________________
block4_conv2 (Conv2D) (None, 4, 4, 512) 2359808
_________________________________________________________________
bn9 (BatchNormalization) (None, 4, 4, 512) 2048
_________________________________________________________________
activation_9 (Activation) (None, 4, 4, 512) 0
_________________________________________________________________
block4_conv3 (Conv2D) (None, 4, 4, 512) 2359808
_________________________________________________________________
bn10 (BatchNormalization) (None, 4, 4, 512) 2048
_________________________________________________________________
activation_10 (Activation) (None, 4, 4, 512) 0
_________________________________________________________________
block4_pool (MaxPooling2D) (None, 2, 2, 512) 0
_________________________________________________________________
block5_conv1 (Conv2D) (None, 2, 2, 512) 2359808
_________________________________________________________________
bn11 (BatchNormalization) (None, 2, 2, 512) 2048
_________________________________________________________________
activation_11 (Activation) (None, 2, 2, 512) 0
_________________________________________________________________
block5_conv2 (Conv2D) (None, 2, 2, 512) 2359808
_________________________________________________________________
bn12 (BatchNormalization) (None, 2, 2, 512) 2048
_________________________________________________________________
activation_12 (Activation) (None, 2, 2, 512) 0
_________________________________________________________________
block5_conv3 (Conv2D) (None, 2, 2, 512) 2359808
_________________________________________________________________
bn13 (BatchNormalization) (None, 2, 2, 512) 2048
_________________________________________________________________
activation_13 (Activation) (None, 2, 2, 512) 0
_________________________________________________________________
block5_pool (MaxPooling2D) (None, 1, 1, 512) 0
_________________________________________________________________
flatten (Flatten) (None, 512) 0
_________________________________________________________________
fc1 (Dense) (None, 4096) 2101248
_________________________________________________________________
fc2 (Dense) (None, 4096) 16781312
_________________________________________________________________
predictions (Dense) (None, 10) 40970
=================================================================
Total params: 33,655,114
Trainable params: 33,646,666
Non-trainable params: 8,448
_________________________________________________________________
Nous allons apprendre le modèle créé.
Apprentissage
'''définition de l'optimiseur'''
optimizer=keras.optimizers.adam()
model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])
'''Normalisation des données'''
x_train=x_train.astype('float32')
x_train/=255
x_test=x_test.astype('float32')
x_test/=255
'''fit'''
history=model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(x_test, y_test))
La méthode d'optimisation utilisée était la méthode [Adam] couramment utilisée (https://arxiv.org/pdf/1412.6980v8.pdf). Étant donné que l'hyper paramètre n'est pas réglé cette fois, le paramètre est défini sur la valeur par défaut. La fonction de perte est une entropie croisée catégorielle utilisée dans le problème de classification multiclasse exprimé par l'équation (1).
La métrique à optimiser est le taux de réponse correct. (Spécifié par les métriques) Définissez-les par model.compile. Enfin, vous pouvez normaliser les données d'image et les entraîner avec model.fit. Enregistrez l'apprentissage dans l'histoire. Avec les paramètres ci-dessus, les résultats d'apprentissage sont les suivants.
Résultat d'exécution
Train on 50000 samples, validate on 10000 samples
Epoch 1/20
50000/50000 [==============================] - 38s 755us/step - loss: 2.0505 - acc: 0.1912 - val_loss: 2.1730 - val_acc: 0.2345
Epoch 2/20
50000/50000 [==============================] - 33s 667us/step - loss: 1.5810 - acc: 0.3763 - val_loss: 1.8167 - val_acc: 0.3522
Epoch 3/20
50000/50000 [==============================] - 33s 663us/step - loss: 1.2352 - acc: 0.5354 - val_loss: 1.4491 - val_acc: 0.5108
Epoch 4/20
50000/50000 [==============================] - 34s 674us/step - loss: 0.9415 - acc: 0.6714 - val_loss: 1.1408 - val_acc: 0.6202
Epoch 5/20
50000/50000 [==============================] - 34s 670us/step - loss: 0.7780 - acc: 0.7347 - val_loss: 0.8930 - val_acc: 0.6974
Epoch 6/20
50000/50000 [==============================] - 34s 675us/step - loss: 0.6525 - acc: 0.7803 - val_loss: 0.9603 - val_acc: 0.6942
Epoch 7/20
50000/50000 [==============================] - 34s 673us/step - loss: 0.5637 - acc: 0.8129 - val_loss: 0.9188 - val_acc: 0.7184
Epoch 8/20
50000/50000 [==============================] - 34s 679us/step - loss: 0.4869 - acc: 0.8405 - val_loss: 1.0963 - val_acc: 0.7069
Epoch 9/20
50000/50000 [==============================] - 34s 677us/step - loss: 0.4268 - acc: 0.8594 - val_loss: 0.6283 - val_acc: 0.8064
Epoch 10/20
50000/50000 [==============================] - 33s 668us/step - loss: 0.3710 - acc: 0.8785 - val_loss: 0.6944 - val_acc: 0.7826
Epoch 11/20
50000/50000 [==============================] - 34s 670us/step - loss: 0.3498 - acc: 0.8871 - val_loss: 0.6534 - val_acc: 0.8024
Epoch 12/20
50000/50000 [==============================] - 33s 663us/step - loss: 0.2751 - acc: 0.9113 - val_loss: 0.6253 - val_acc: 0.8163
Epoch 13/20
50000/50000 [==============================] - 34s 670us/step - loss: 0.2388 - acc: 0.9225 - val_loss: 1.1404 - val_acc: 0.7384
Epoch 14/20
50000/50000 [==============================] - 33s 667us/step - loss: 0.2127 - acc: 0.9323 - val_loss: 0.9577 - val_acc: 0.7503
Epoch 15/20
50000/50000 [==============================] - 33s 667us/step - loss: 0.1790 - acc: 0.9421 - val_loss: 0.7820 - val_acc: 0.7915
Epoch 16/20
50000/50000 [==============================] - 33s 666us/step - loss: 0.1559 - acc: 0.9509 - val_loss: 0.7138 - val_acc: 0.8223
Epoch 17/20
50000/50000 [==============================] - 34s 671us/step - loss: 0.1361 - acc: 0.9570 - val_loss: 0.8909 - val_acc: 0.7814
Epoch 18/20
50000/50000 [==============================] - 33s 669us/step - loss: 0.1272 - acc: 0.9606 - val_loss: 0.7006 - val_acc: 0.8246
Epoch 19/20
50000/50000 [==============================] - 33s 666us/step - loss: 0.1130 - acc: 0.9647 - val_loss: 0.7523 - val_acc: 0.8177
Epoch 20/20
50000/50000 [==============================] - 34s 671us/step - loss: 0.0986 - acc: 0.9689 - val_loss: 0.7233 - val_acc: 0.8350
Après avoir terminé 20 époques, le taux de réponse correcte était d'environ 97% pour les données d'entraînement et d'environ 84% pour les données de test. Tracons la perte et le taux de réponse correct pour chaque époque.
Graphique graphique
'''Visualisation des résultats'''
plt.figure(figsize=(10,7))
plt.plot(history.history['acc'], color='b', linewidth=3)
plt.plot(history.history['val_acc'], color='r', linewidth=3)
plt.tick_params(labelsize=18)
plt.ylabel('acuuracy', fontsize=20)
plt.xlabel('epoch', fontsize=20)
plt.legend(['training', 'test'], loc='best', fontsize=20)
plt.figure(figsize=(10,7))
plt.plot(history.history['loss'], color='b', linewidth=3)
plt.plot(history.history['val_loss'], color='r', linewidth=3)
plt.tick_params(labelsize=18)
plt.ylabel('loss', fontsize=20)
plt.xlabel('epoch', fontsize=20)
plt.legend(['training', 'test'], loc='best', fontsize=20)
plt.show()
La transition du taux de réponse correct est illustrée dans la figure ci-dessous.
La transition de la fonction de perte est illustrée dans la figure ci-dessous.
Hum ... La perte de données de test est devenue instable à partir de la 4e époque environ. J'ai fait la normalisation par lots, mais cela ressemble à Over tarining.
Cette formation ne prend pas beaucoup de temps, mais vous pouvez réutiliser le modèle que vous avez formé pendant une longue période en l'enregistrant. Enregistrez le modèle et les poids comme indiqué ci-dessous.
Enregistrer le modèle
'''Stockage de données'''
model.save('cifar10-CNN.h5')
model.save_weights('cifar10-CNN-weights.h5')
Cette fois, en guise de tutoriel, j'ai utilisé Keras pour identifier l'image de CIFAR10 avec le célèbre modèle VGG16. Comme VGG16 était à l'origine un modèle utilisé pour la classification de 1000 classes, j'ai changé la taille d'entrée / sortie et utilisé la normalisation par lots, mais je l'ai surentraînée. La taille d'entrée / sortie est peut-être trop petite. En outre, en tant que méthodes d'amélioration, la mise en œuvre de la régularisation Dropout et L2 et le réglage des méthodes d'optimisation peuvent être envisagés.
Pour la mise en œuvre de ce code, je me suis référé aux livres suivants.
Recommended Posts