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".
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.
L'image ici est la mienne.
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.
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()
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. 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)
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
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))
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.
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.
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.
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)
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.
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.
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. .. ..
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).
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