[PYTHON] [TensorFlow] J'ai essayé d'introduire l'IA dans le repassage des véhicules

introduction

La technologie d'apprentissage en profondeur est devenue familière et lorsque j'ai cherché sur Google, j'ai trouvé de nombreux échantillons qui me permettaient de reconnaître diverses choses comme des images. Les résultats sont faciles à comprendre et amusants à regarder, donc je voulais me rendre compte de quelque chose en brassant le nième, mais comme tout le monde le fait pour reconnaître les animaux et les actrices préférées, une autre histoire Je veux le faire.

Donc, ** j'ai essayé de deviner le modèle du véhicule montré dans l'image d'entrée par la technologie d'apprentissage en profondeur en utilisant l'image du véhicule ferroviaire comme données d'apprentissage. ** Cependant, je pense que le sentiment de niveau est à peu près le même que celui d'un petit enfant qui a commencé à s'intéresser aux trains.

↓ Nous visons à saisir une image d'un tel véhicule et à lui faire deviner "E231 series 500 series". E231系500番台(山手線)

Environnement de développement

Problème de réglage

Apprenons un modèle de réseau neuronal qui devine l'une des cinq options pour une ** photo extérieure de véhicule ferroviaire ** donnée! Cette fois, nous ciblerons 5 types de véhicules circulant dans la banlieue de Tokyo parmi les types de véhicules de JR East. Si vous le regardez, vous pouvez facilement le dire par la couleur du bracelet, mais il peut être difficile pour l'ordinateur de savoir quelle partie est le véhicule.

Exemple d'image de véhicule

L'image ici est la mienne.

(1) Série E231 série 500 (gamme Yamate)

E231系500番台(山手線)

(2) Série E233 série 0 (Chuo Line Rapid / Oume Line / Gokaichi Line)

E233系0番台(中央線快速・青梅線・五日市線)

(3) Série E233 série 1000 (gamme Keihin Tohoku / Negishi)

E233系1000番台(京浜東北・根岸線)

(4) Série E233 Série 8000 (gamme Nanbu)

E233系8000番台(南武線)

(5) E235 série 0 série (nouveau modèle de la ligne Yamate)

E235系0番台(山手線)

Collecte de données

J'ai fait référence à cet article. Création d'une reconnaissance d'image "○○ discriminateur" avec TensorFlow --Qiita

Téléchargez automatiquement les images qui apparaissent dans la recherche d'images Google.

pip install google_images_download
googleimagesdownload -k Ligne Yamate
googleimagesdownload -k Chuo Line Rapid
googleimagesdownload -k E235
:
:

Parmi les images collectées, n'utilisez que les photos montrant l'extérieur du véhicule. Les images suivantes ne sont pas utilisées.

Essayez différents mots-clés et collectez enfin plus de 100 images par format. Cette fois, il existe 5 types, pour un total de 540 feuilles. Il semble que le nombre ne soit pas du tout suffisant, mais même cela est assez difficile ... Même si vous modifiez le mot-clé, seule la même image sera affichée.

Ensuite, placez les images collectées dans des dossiers pour chaque classe.

Images réellement collectées

(1) Série E231 série 500 (gamme Yamate)

image.png

(2) Série E233 série 0 (Chuo Line Rapid / Oume Line / Gokaichi Line)

image.png

(3) Série E233 série 1000 (gamme Keihin Tohoku / Negishi)

image.png

(4) Série E233 Série 8000 (gamme Nanbu)

image.png

(5) Série E235 série 0 (gamme Yamate)

image.png

Code de formation du modèle

Maintenant, lorsque l'image est prête, il est temps d'apprendre. En gros, j'ai évoqué le contenu des articles suivants. Réglage fin de VGG16 à l'aide de GPU pour faire de la reconnaissance faciale AI --Qiita

Chaque format cette fois-ci a des couleurs complètement différentes (il y a deux lignes Yamate, mais elles ont l'air assez différentes), donc je pense que c'est une tâche plus facile que d'identifier le visage de l'actrice, mais ce n'est encore qu'environ 500 images. Ensuite, ce sera difficile. Pour gérer cette petite quantité de données, nous effectuerons un réglage fin ** en utilisant le modèle entraîné de VGG16. Le modèle VGG16 est disponible auprès de TensorFlow (Keras) sans nécessiter une installation de package distincte. Keras: Que sont VGG16 et VGG19? ?? --Qiita

VGG16 est un modèle qui effectue 1000 classes de classification d'images qui n'a rien à voir avec les véhicules ferroviaires, mais étant donné que les poids appris expriment les caractéristiques efficaces pour l'identification d'image, cette tâche est uniquement pour la couche proche de la sortie. Il sera remplacé et appris selon. Il semble qu'il sera possible de résoudre le problème d'identification qui n'a rien à voir avec les données d'apprentissage du premier modèle. Oh mystérieux. L'entrée est une image couleur de 128 x 128 [^ 1], et après avoir traversé le modèle VGG16, 256 unités de couche entièrement connectée, Dropout et 5 unités de couche entièrement connectée (couche de sortie) sont attachées. Seuls les poids de la couche entièrement connectée ajoutée cette fois et la partie de Conv2D-Conv2D-Conv2D la plus proche de la couche de sortie de VGG16 sont entraînés, et les couches Conv2D restantes ne sont pas déplacées des paramètres appris.

[^ 1]: Comme dans l'article original, 150x150 était bien, mais lorsque VGG16 est passé, la taille de l'image devient 1/32 dans les directions verticale et horizontale, donc j'ai en quelque sorte pensé à en faire un multiple de 32. Le 224x224 utilisé pour l'apprentissage original VGG16 manquait de mémoire (probablement parce que TensorFlow ne fonctionnait pas bien sous Windows 10 et fonctionnait sous Linux sur une machine virtuelle).

L'image d'entrée est utilisée pour l'apprentissage en donnant des fluctuations telles que l'agrandissement / réduction et l'inversion gauche / droite avec `ʻImageDataGenerator`` comme introduit à divers endroits. L'image originale est d'environ 500, mais ** chaque époque donne une fluctuation différente, il semble donc que les données soient gonflées **. Python - À propos du générateur de données d'image Keras | teratail

train.py


import tensorflow as tf
from tensorflow.keras.layers import Dense, Input, Flatten, Dropout
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau
from tensorflow.keras.models import Model
from tensorflow.keras.applications import VGG16
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt

#Paramètres d'apprentissage
batch_size = 32
epochs = 30
#Paramètre de fonctionnalité
#fait correspondre les classes au nom du sous-dossier
classes = ["E231-yamanote", "E233-chuo", "E233-keihintohoku", "E233-nanbu", "E235-yamanote"]
num_classes = len(classes)
img_width, img_height = 128, 128
feature_dim = (img_width, img_height, 3)
#Chemin du fichier
data_dir = "./images"

# ===Préparation de l'image===
datagen = ImageDataGenerator(
    rescale=1.0 / 255, #Chaque valeur de pixel est[0, 1]Convertir en et gérer
    zoom_range=0.2,
    horizontal_flip=True,
    validation_split=0.1
)
train_generator = datagen.flow_from_directory(
    data_dir,
    subset="training",
    target_size=(img_width, img_height),
    color_mode="rgb",
    classes=classes,
    class_mode="categorical",
    batch_size=batch_size,
    shuffle=True)
validation_generator = datagen.flow_from_directory(
    data_dir,
    subset="validation",
    target_size=(img_width, img_height),
    color_mode="rgb",
    classes=classes,
    class_mode="categorical",
    batch_size=batch_size)

#Obtenez le nombre d'images et calculez le nombre de mini-lot de 1 époque
num_train_samples = train_generator.n
num_validation_samples = validation_generator.n
steps_per_epoch_train = (num_train_samples-1) // batch_size + 1
steps_per_epoch_validation  = (num_validation_samples-1) // batch_size + 1

# ===Définition du modèle===
#Basé sur le modèle VGG16 entraîné, entraînez-vous en modifiant uniquement la couche de sortie
# block4_N'entraînez pas les paramètres jusqu'au pool
vgg16 = VGG16(include_top=False, weights="imagenet", input_shape=feature_dim)
for layer in vgg16.layers[:15]:
    layer.trainable = False

#Construisez ce modèle
layer_input = Input(shape=feature_dim)
layer_vgg16 = vgg16(layer_input)
layer_flat = Flatten()(layer_vgg16)
layer_fc = Dense(256, activation="relu")(layer_flat)
layer_dropout = Dropout(0.5)(layer_fc)
layer_output = Dense(num_classes, activation="softmax")(layer_dropout)
model = Model(layer_input, layer_output)
model.summary()
model.compile(loss="categorical_crossentropy",
              optimizer=SGD(lr=1e-3, momentum=0.9),
              metrics=["accuracy"])

# ===Apprentissage===
cp_cb = ModelCheckpoint(
    filepath="weights.{epoch:02d}-{loss:.4f}-{val_loss:.4f}.hdf5",
    monitor="val_loss",
    verbose=1,
    mode="auto")
reduce_lr_cb = ReduceLROnPlateau(
    monitor="val_loss",
    factor=0.5,
    patience=1,
    verbose=1)
history = model.fit(
    train_generator,
    steps_per_epoch=steps_per_epoch_train,
    epochs=epochs,
    validation_data=validation_generator,
    validation_steps=steps_per_epoch_validation,
    callbacks=[cp_cb, reduce_lr_cb])

# ===Sortie de transition du taux de réponse correct===
plt.plot(range(1, len(history.history["accuracy"]) + 1),
         history.history["accuracy"],
         label="acc", ls="-", marker="o")
plt.plot(range(1, len(history.history["val_accuracy"]) + 1),
         history.history["val_accuracy"],
         label="val_acc", ls="-", marker="x")
plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.savefig("accuracy.png ")
plt.show()

Transition d'apprentissage

Après avoir passé 30 époques, la transition du taux de réponse correct dans les données d'entraînement et les données de vérification ressemble à ceci. Je me suis entraîné en faisant tourner le processeur sur un ordinateur portable sans GPU (à un fonctionnement complet de 4 cœurs), mais comme 1 époque était d'environ 1 minute, cela a pris environ 30 minutes au total. accuracy.png Le taux de réponse correcte des données de vérification s'est arrêté vers 10 époques, mais le taux de réponse correcte a atteint 94% pour les questions à 5 choix. Vous avez fait de votre mieux malgré le peu de données!

La fonction ModelCheckpoint '' enregistre automatiquement le modèle à la fin de chaque époque. Cette fois, le modèle de la 17ème époque poids.17-0.1049-0.1158.hdf5 '' a eu la plus petite perte de données de validation, nous allons donc l'utiliser pour l'identification.

import numpy as np
print(np.argmin(history.history["val_loss"]) + 1)
#17 (peut changer à chaque fois)

point important

J'ai réglé l'Optimizer sur SGD, mais si je l'ai réglé sur ```Adam`` etc., il ne converge pas bien. Cela est probablement dû à un réglage fin. Pour plus de détails, consultez l'article suivant. [\ TensorFlow ] Optimizer a également Weight --Qiita

Identifiez le véhicule

Identifions en fait chacune des images listées au début.

predict.py


import sys

def usage():
    print("Usage: {0} <input_filename>".format(sys.argv[0]), file=sys.stderr)
    exit(1)

# ===Récupère le nom de fichier de l'image d'entrée à partir de l'argument===
if len(sys.argv) != 2:
    usage()
input_filename = sys.argv[1]

import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing import image
 
#Paramètre de fonctionnalité
classes = ["E231-yamanote", "E233-chuo", "E233-keihintohoku", "E233-nanbu", "E235-yamanote"]
num_classes = len(classes)
img_width, img_height = 128, 128
feature_dim = (img_width, img_height, 3)

# ===Chargement du modèle===
model = tf.keras.models.load_model("weights.17-0.1049-0.1158.hdf5")

# ===Chargement de l'image d'entrée===
img = image.load_img(input_filename, target_size=(img_height, img_width))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
#La plage de valeurs est la même que lors de l'apprentissage[0, 1]Convertir en
x = x / 255.0
#Prédire le type de véhicule
pred = model.predict(x)[0]
#Voir les résultats
for cls, prob in zip(classes, pred):
    print("{0:18}{1:8.4f}%".format(cls, prob * 100.0))

Exemple d'entrée / sortie

Si vous donnez le nom du fichier image à l'argument de ligne de commande de `` predict.py '', le résultat de l'identification sera affiché.

python3 predict.py filename.jpg

Tous les exemples d'entrée présentés ici sont les vôtres. De plus, bien qu'une partie de l'image soit traitée pour être affichée ici, elle est saisie telle qu'elle est au moment de l'apprentissage et de l'identification réels.

(1) Série E231 série 500 (gamme Yamate)

E231系500番台(山手線)
E231-yamanote      99.9974%
E233-chuo           0.0000%
E233-keihintohoku   0.0000%
E233-nanbu          0.0004%
E235-yamanote       0.0021%

C'est la bonne réponse sans aucune plainte.

(2) Série E233 série 0 (Chuo Line Rapid / Oume Line / Gokaichi Line)

E233系0番台(中央線快速・青梅線・五日市線)
E231-yamanote       0.0023%
E233-chuo          97.3950%
E233-keihintohoku   0.0101%
E233-nanbu          2.5918%
E235-yamanote       0.0009%

Ce n'est pas du tout un problème.

(3) Série E233 série 1000 (gamme Keihin Tohoku / Negishi)

E233系1000番台(京浜東北・根岸線) C'est une image qui semble difficile à identifier pour la machine car elle montre le nom de la station et les personnes.
E231-yamanote       2.0006%
E233-chuo           0.9536%
E233-keihintohoku  34.9607%
E233-nanbu          6.5641%
E235-yamanote      55.5209%

La probabilité de la série Yamate Line E235 a augmenté. Les conditions sont mauvaises et l'image est de côté, est-ce donc inévitable?

À propos, la raison pour laquelle cette image est de côté est que je n'avais pas d'image du train de la ligne Keihin Tohoku prise par moi-même de l'avant ... (sueur)

(4) Série E233 Série 8000 (gamme Nanbu)

E233系8000番台(南武線)
E231-yamanote       0.1619%
E233-chuo           7.9535%
E233-keihintohoku   0.0309%
E233-nanbu         91.7263%
E235-yamanote       0.1273%

J'ai donné une probabilité élevée à la ligne Nanbu correcte, mais il semble que j'étais un peu perdu avec la ligne Chuo rapide. Cependant, la forme est presque la même et seulement des couleurs différentes, mais dans ce cas, cela peut prêter à confusion avec la ligne Keihin Tohoku.

(5) Série E235 série 0 (gamme Yamate)

E235系0番台(山手線)
E231-yamanote       0.0204%
E233-chuo           0.0000%
E233-keihintohoku   0.0027%
E233-nanbu          0.0002%
E235-yamanote      99.9767%

Ce n'est pas un problème.

Autre

E233系1000番台(京浜東北・根岸線)をE235系0番台(山手線)に間違えた
E231-yamanote       0.2417%
E233-chuo           0.0204%
E233-keihintohoku   2.1286%
E233-nanbu          0.0338%
E235-yamanote      97.5755%

En fait, la troisième est la bonne réponse, mais il semble que je pensais que c'était un nouveau modèle sur la ligne Yamate. Pourquoi. .. ..

E233系0番台(中央線快速)をE235系0番台(山手線)に間違えた
E231-yamanote      47.2513%
E233-chuo           0.0898%
E233-keihintohoku   0.4680%
E233-nanbu          6.5922%
E235-yamanote      45.5986%

La deuxième est la bonne réponse, mais pour une raison quelconque, je recommanderai la ligne Yamate. Existe-t-il une théorie selon laquelle E235 est simplement recommandé pour les images de côté? La raison pour laquelle la probabilité de la ligne Nanbu est un peu plus élevée est qu'elle répond au signe jaune à l'extrême droite (je ne sais pas si c'est réellement le cas).

Résumé

J'ai essayé d'apprendre un modèle qui identifie 5 types de types de véhicules en utilisant environ 500 images de véhicules ferroviaires collectées par recherche d'images Google. En détournant une partie du modèle entraîné (VGG16) et en l'apprenant, il semble qu'un modèle identifiable raisonnablement en 30 minutes environ même sur un PC sans GPU ait été créé. Il y a certains modèles qui font des erreurs, mais je pense qu'il était un bon combattant pour la quantité de ressources informatiques et de données. C'était étonnamment facile à faire et c'était amusant.

Si vous le faites sérieusement, vous devez collecter des données d'image dans différentes directions, et je pense qu'il est nécessaire de découper la partie du véhicule. Si vous souhaitez identifier le visage, vous pouvez le découper immédiatement avec OpenCV etc., mais dans le cas d'un véhicule, c'est probablement à partir de l'annotation de détection d'objet.

Recommended Posts

[TensorFlow] J'ai essayé d'introduire l'IA dans le repassage des véhicules
J'ai essayé de présenter Pylint
J'ai essayé d'implémenter Autoencoder avec TensorFlow
J'ai essayé de visualiser AutoEncoder avec TensorFlow
J'ai essayé de classer le texte en utilisant TensorFlow
J'ai essayé de faire de l'IA pour Smash Bra
J'ai créé un jeu ○ ✕ avec TensorFlow
J'ai essayé d'exécuter TensorFlow
Quand j'ai essayé d'introduire python3 dans atom, je suis resté coincé
J'ai essayé d'implémenter Grad-CAM avec keras et tensorflow
J'ai essayé de trouver la classe alternative avec tensorflow
J'ai essayé d'apprendre PredNet
J'ai essayé d'organiser SVM.
J'ai essayé d'implémenter PCANet
J'ai essayé de réintroduire Linux
J'ai essayé de résumer SparseMatrix
J'ai essayé d'utiliser magenta / TensorFlow
jupyter je l'ai touché
J'ai essayé d'implémenter StarGAN (1)
J'ai essayé d'introduire l'outil de génération de diagramme blockdiag
J'ai essayé de porter le code écrit pour TensorFlow sur Theano
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
J'ai essayé d'implémenter Deep VQE
J'ai essayé le tutoriel TensorFlow 1er
J'ai essayé de créer l'API Quip
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Introduction ~
J'ai essayé le tutoriel TensorFlow 2ème
J'ai essayé de mettre en place une validation contradictoire
J'ai essayé d'expliquer l'ensemble de données de Pytorch
J'ai essayé l'authentification vocale Watson (Speech to Text)
Tutoriel TensorFlow J'ai essayé CNN 4th
J'ai touché l'API de Tesla
Continuation: j'ai essayé d'introduire l'outil de génération de diagramme blockdiag
J'ai essayé de m'organiser à propos de MCMC.
J'ai essayé d'implémenter Realness GAN
J'ai essayé de déplacer le ballon
J'ai essayé de prédire les hauts et les bas du cours de clôture du cours de l'action de Guru Navi en utilisant TensorFlow (progression)
J'ai essayé d'estimer la section.
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Implémentation ~
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Battle Edition ~
J'ai essayé de transformer l'image du visage en utilisant sparse_image_warp de TensorFlow Addons
J'ai essayé de faire un diagnostic de visage AI pour les golfeuses professionnelles ②
J'ai refactoré "J'ai essayé de faire d'Othello AI lorsque les débutants en programmation ont étudié python"
J'ai essayé de créer un linebot (implémentation)
Tutoriel TensorFlow J'ai essayé MNIST 3rd
J'ai essayé de résumer la gestion des exceptions Python
J'ai essayé d'implémenter PLSA en Python
J'ai essayé d'utiliser Azure Speech to Text.
J'ai essayé de résumer la commande umask
J'ai essayé d'implémenter la permutation en Python
J'ai essayé de créer un linebot (préparation)
J'ai essayé de reconnaître le mot de réveil
J'ai essayé de commencer avec Hy
J'ai essayé de jouer au jeu ○ ✕ en utilisant TensorFlow
J'ai essayé d'implémenter PLSA dans Python 2
Entrée standard Python3 que j'ai essayé de résumer
J'ai essayé de résumer la modélisation graphique.