[PYTHON] "Classer les déchets par image!" Journal de création d'application day2 ~ Mise au point avec VGG16 ~

introduction

"Classer les déchets par image!" Aujourd'hui, deuxième jour du journal de création d'application, j'aimerais enfin créer un modèle. Je voudrais affiner le modèle en utilisant VGG16. Faisons-le maintenant.


Liste d'articles

Synopsis jusqu'à la dernière fois

La dernière fois, j'ai pris diverses photos pour créer un jeu de données. La structure des dossiers est la suivante.

train ├ Déchets combustibles │ └ Images (même ci-dessous) ├ Recyclables ├ Déchets non combustibles ├ Emballage en plastique de conteneur └ Déchets nocifs val ├ Déchets combustibles │ └ Images (même ci-dessous) ├ Recyclables ├ Déchets non combustibles ├ Emballage en plastique de conteneur └ Déchets nocifs

Nous allons créer un modèle basé sur cela.

Importer la bibliothèque

Chargez les bibliothèques requises.


from keras.applications.vgg16 import VGG16
from keras.models import Sequential, Model
from keras.layers import Input, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
import numpy as np
import matplotlib.pyplot as plt
from glob import glob

Définissons également les paramètres. Tout d'abord, spécifiez la classe à classer. Vous pouvez spécifier chacun d'eux individuellement, mais si vous faites une erreur, ce sera ennuyeux, alors essayez de les obtenir tous en même temps.


#Classe à classer
classes = glob("train/*")
classes = [c.split("\\", 1)[-1] for c in classes]
nb_classes = len(classes)

Si vous placez le fichier exécutable dans le même répertoire que le dossier train, les classes seront obtenues sous la forme ['train \\ non-burnable garbage', 'train \\ packaging container plastics' ,,,] sur la première ligne. .. Par conséquent, si vous le divisez en le divisant en tous les éléments de la notation d'inclusion, vous ne pouvez retirer que les parties nécessaires telles que ['déchets non combustibles', 'emballages en plastique de conteneurs' ,,,]. (Selon le système d'exploitation, le délimiteur de répertoire est / ou \\ (\ est une séquence d'échappement, donc deux sont nécessaires), donc si vous n'êtes pas Windows, veuillez vérifier à nouveau celui obtenu.

Ensuite, spécifiez les paramètres liés à l'image.


#Définir la taille de l'image
img_width, img_height = 150, 150

#Spécifiez le dossier d'image
train_dir = 'train'
val_dir = 'val'

#Taille du lot
batch_size = 16

Créer des données

Cette fois, j'utiliserai ImageDataGenerator parce que je veux gonfler les données. Cela vous permet de spécifier comment gonfler.


#Traitement gonflant
train_datagen = ImageDataGenerator(
    rotation_range=90, #± Combien de fois tourner
    width_shift_range=0.1, #De combien se déplacer dans la direction horizontale
    height_shift_range=0.1, #De combien se déplacer dans le sens vertical
    rescale=1.0 / 255, #0~Normalisé à 1
    zoom_range=0.2, #Combien d'agrandissement
    horizontal_flip=True, #S'il faut retourner horizontalement
    vertical_flip=True #S'il faut retourner dans le sens vertical
)

val_datagen = ImageDataGenerator(rescale=1.0 / 255)

Le but est d'effectuer un traitement de gonflage pour les données de train et uniquement un traitement de mise à l'échelle pour les données de test. Les paramètres sont tels que décrits dans les commentaires, mais veuillez vous référer à ce qui suit pour des informations plus détaillées sur les paramètres.

Je voudrais appliquer le traitement ci-dessus à l'image réelle. En utilisant une fonction appelée flow_from_directory, les données seront créées joliment à partir du répertoire. * Pour exécuter ce processus, la structure des dossiers doit être créée correctement conformément aux spécifications.


#Générer un générateur
train_generator = train_datagen.flow_from_directory(
    train_dir, #Chemin d'accès au répertoire
    target_size=(img_width, img_height), #Taille de l'image après le redimensionnement
    color_mode='rgb', #Spécifier le canal de l'image
    classes=classes, #Liste des classes (les images doivent être dans le sous-répertoire spécifié ici)
    class_mode='categorical', #"categorical","binary","sparse"Tel
    batch_size=batch_size,
    shuffle=True)

val_generator = val_datagen.flow_from_directory(
    val_dir,
    target_size=(img_width, img_height),
    color_mode='rgb',
    classes=classes,
    class_mode='categorical',
    batch_size=batch_size,
    shuffle=True)

Construire un modèle

Je vais enfin faire un modèle. De plus, je me réfère aux articles suivants, y compris la structure et les paramètres.

En tant que structure à fabriquer, VGG16 est utilisé pour la couche de pliage, et la couche entièrement connectée est conçue par vous-même. De plus, nous n'apprendrons pas les poids jusqu'à la couche 15, mais nous avons envie d'apprendre la dernière couche de convolution et la couche entièrement connectée.


# VGG16
input_tensor = Input(shape=(img_width, img_height, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

Commencez par charger VGG16. Les paramètres sont:

--include_top: s'il faut inclure la couche entièrement connectée --weights: Il semble que vous ne puissiez sélectionner Aucun (initialisation aléatoire) '' ou imagenet '' pour le moment pour savoir quel type de poids utiliser.

Ensuite, définissez la couche entièrement connectée.


#Couche entièrement connectée
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(nb_classes, activation='softmax'))
top_model.summary()

La couche entièrement connectée est la suivante.

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
flatten_3 (Flatten)          (None, 8192)              0         
_________________________________________________________________
dense_6 (Dense)              (None, 256)               2097408   
_________________________________________________________________
dropout_3 (Dropout)          (None, 256)               0         
_________________________________________________________________
dense_7 (Dense)              (None, 5)                 1285      
=================================================================
Total params: 2,098,693
Trainable params: 2,098,693
Non-trainable params: 0

Maintenant que nous avons créé une sortie pour classer dans le nombre de classes souhaité, nous allons la combiner avec VGG16.


vgg_model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))

#Poids fixe
for layer in vgg_model.layers[:15]:
    layer.trainable = False

vgg_model.compile(loss='categorical_crossentropy',
          optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
          metrics=['acc'])
vgg_model.summary()

La manière de combiner est la même que la manière d'écrire l'API fonctionnelle. Étant donné que la fonction d'optimisation est Fine-tuning, utilisez SGD avec un faible taux d'apprentissage. Le modèle ressemble à ceci:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_4 (InputLayer)         [(None, 150, 150, 3)]     0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 150, 150, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 150, 150, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 75, 75, 64)        0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 75, 75, 128)       73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 75, 75, 128)       147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 37, 37, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 37, 37, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 18, 18, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 18, 18, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 9, 9, 512)         0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 4, 4, 512)         0         
_________________________________________________________________
sequential_3 (Sequential)    (None, 5)                 2098693   
=================================================================
Total params: 16,813,381
Trainable params: 9,178,117
Non-trainable params: 7,635,264

Apprenons maintenant.


history = vgg_model.fit(
    train_generator, #Générateur de formation
    steps_per_epoch=len(train_generator), #Nombre de lots par époque
    epochs=30,
    validation_data=val_generator,
    validation_steps=len(val_generator))


#acc, val_tracé acc
plt.plot(history.history["acc"], label="acc", ls="-", marker="o")
plt.plot(history.history["val_acc"], label="val_acc", ls="-", marker="x")
plt.ylabel("acc")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.savefig("acc")
plt.close()

plt.plot(history.history["loss"], label="loss", ls="-", marker="o")
plt.plot(history.history["val_loss"], label="val_loss", ls="-", marker="x")
plt.ylabel("loss")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.savefig("loss")
plt.close()

loss.png

Je pense que j'ai pu apprendre d'une bonne manière.

Enfin, enregistrez et chargez ce modèle pour terminer la création du modèle.


#sauvegarder
open("model.json", 'w').write(vgg_model.to_json())
vgg_model.save_weights('param.hdf5')

Prévoir

Ensuite, j'aimerais faire des prédictions en utilisant le modèle de niveau supérieur.


import numpy as np
import matplotlib.pyplot as plt
from keras.preprocessing import image
from keras.models import model_from_json
model = model_from_json(open("model.json").read())
model.load_weights('param.hdf5')

img_width, img_height = 150, 150
classes = ['Déchets non combustibles', 'Emballage en plastique', 'Déchets combustibles', 'Déchets nocifs', 'Recyclables']

les classes sont spécifiées directement par leur nom en tenant compte du moment où elles ont été déployées.

L'image n'a pas besoin d'être un générateur, elle sera donc chargée directement.


filename = "val/Recyclables/IMG_20201108_105804.jpg "
img = image.load_img(filename, target_size=(img_height, img_width))
x = image.img_to_array(img)
x = x / 255.0 #Normalisation
x = np.expand_dims(x, axis=0)

#Prédire la personne sur l'image
pred = model.predict(x)[0]
#Voir les résultats
result = {c:s for (c, s) in zip(classes, pred*100)}
result = sorted(result.items(), key=lambda x:x[1], reverse=True)
print(result)

Le résultat ressemble à ceci.

IMG_20201108_110533.jpg IMG_20201108_114503.jpg
'Recyclables', 99.783165 'Déchets non combustibles', 99.97801
'Déchets non combustibles', 0.1700096 'Recyclables', 0.014258962
'Emballage en plastique de conteneur', 0.04342786 'Emballage en plastique de conteneur', 0.007412854
'Déchets combustibles', 0.00205229 'Déchets combustibles', 0.0002818475
'Déchets nocifs', 0.0013515248 'Déchets nocifs', 3.024669e-05

Il est possible qu'il contienne quelque chose de similaire à l'ensemble de données au moment de la formation car nous n'avons pas pu préparer de nombreux types de déchets, mais il semble que nous pouvons les distinguer correctement.

La prochaine fois, j'aimerais intégrer ce modèle dans Django. impatient de!


Liste d'articles

Question

J'ai personnellement une question. Lorsque vous faites vgg_model.fit, le nombre de lots par époque est spécifié dans steps_per_epoch = len (train_generator) (= nombre d'images / nombre de lots), mais ce sont les données utilisées par époque. Le nombre est le même que le nombre original d'images, et je pense qu'il n'a pas été gonflé. Bien sûr, il est possible de s'entraîner avec diverses images en empilant des époques, mais je pense que c'est une méthode générale pour gonfler et s'entraîner en une seule époque, donc si la méthode est différente, dites-moi s'il vous plaît S'il vous plaît.

Au fait, si vous définissez steps_per_epoch sur une valeur supérieure à len (train_generator), vous obtiendrez l'erreur suivante.

WARNING:tensorflow:Your input ran out of data; interrupting training. Make sure that your dataset or generator can generate at least `steps_per_epoch * epochs` batches (in this case, 900 batches). You may need to use the repeat() function when building your dataset.

Quand j'utilise train_generator.repeat (), j'obtiens une erreur disant `` L'objet 'DirectoryIterator' n'a pas d'attribut repeat ''. Où dois-je le spécifier? Si quelqu'un peut le comprendre, je vous serais reconnaissant si vous pouviez m'apprendre.

Références

Recommended Posts

"Classer les déchets par image!" Journal de création d'application day2 ~ Mise au point avec VGG16 ~
"Classer les déchets par image!" Journal de création d'application jour1 ~ Création de l'ensemble de données ~
"Classification des déchets par image!" Journal de création d'application jour3 ~ Application Web avec Django ~
"Classer les déchets par image!" Journal de création d'application day5 ~ Préparez le frontal avec Bootstrap 2 ~
"Classer les déchets par image!" Journal de création d'application day8 ~ déploiement heroku ~
"Classer les déchets par image!" Journal de création d'application day4 ~ Préparez le frontal avec Bootstrap ~
"Classer les déchets par image!" Journal de création d'application jour6 ~ Correction de la structure des répertoires ~
Classification d'images avec un réseau de neurones auto-fabriqué par Keras et PyTorch
Défi la classification des images par TensorFlow2 + Keras 4 ~ Prédisons avec un modèle entraîné ~
Classification d'images avec un jeu de données d'images de fond d'oeil grand angle
Créer une application de fractionnement d'image avec Tkinter
Deep learning 2 appris par l'implémentation (classification d'images)
Classification d'image avec Keras - Du prétraitement au test de classification -
Classification multi-étiquettes par forêt aléatoire avec scikit-learn
Traitement d'image avec Lambda + OpenCV (création d'image grise)
Détection d'objets de cuisson par classification d'images Yolo +