[PYTHON] "Müll nach Bild klassifizieren!" App-Erstellungstagebuch Tag2 ~ Feinabstimmung mit VGG16 ~

Einführung

Heute, am zweiten Tag des Tagebuchs zur App-Erstellung, "Müll nach Bild klassifizieren!", Möchte ich endlich ein Modell erstellen. Ich möchte das Modell mit VGG16 optimieren. Lass es uns jetzt machen.


Artikelliste

Synopse bis zum letzten Mal

Beim letzten Mal habe ich verschiedene Bilder aufgenommen, um einen Datensatz zu erstellen. Die Ordnerstruktur ist wie folgt.

train ├ Brennbare Abfälle │ └ Bilder (wie unten) ├ Wertstoffe ├ Nicht brennbarer Abfall ├ Verpackungsbehälter Kunststoffe └ Schädlicher Müll val ├ Brennbare Abfälle │ └ Bilder (wie unten) ├ Wertstoffe ├ Nicht brennbarer Abfall ├ Verpackungsbehälter Kunststoffe └ Schädlicher Müll

Darauf aufbauend werden wir ein Modell erstellen.

Bibliothek importieren

Laden Sie die erforderlichen Bibliotheken.


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

Lassen Sie uns auch die Parameter einstellen. Geben Sie zunächst die zu klassifizierende Klasse an. Sie können jedes einzeln angeben. Wenn Sie jedoch einen Fehler machen, ist dies ärgerlich. Versuchen Sie daher, alle Fehler gleichzeitig zu ermitteln.


#Klasse zum Klassifizieren
classes = glob("train/*")
classes = [c.split("\\", 1)[-1] for c in classes]
nb_classes = len(classes)

Wenn Sie die ausführbare Datei in dasselbe Verzeichnis wie den Zugordner legen, werden die Klassen in der ersten Zeile als "[" Zug \ nicht brennbarer Müll "," Zug \ Verpackungsbehälterplastik ",,,]" abgerufen. .. Wenn Sie es also durch Aufteilen in alle Elemente in der Einschlussnotation teilen, können Sie nur die erforderlichen Teile wie "[" nicht brennbarer Abfall "," Verpackungsbehälterkunststoffe ",,,]" herausnehmen. (Je nach Betriebssystem lautet das Verzeichnistrennzeichen "/" oder "\" (\ ist eine Escape-Sequenz, daher sind zwei erforderlich). Wenn Sie also kein Windows sind, überprüfen Sie das erhaltene erneut.

Geben Sie als Nächstes die bildbezogenen Parameter an.


#Stellen Sie die Bildgröße ein
img_width, img_height = 150, 150

#Geben Sie den Bildordner an
train_dir = 'train'
val_dir = 'val'

#Chargengröße
batch_size = 16

Daten erstellen

Dieses Mal werde ich "ImageDataGenerator" verwenden, weil ich die Daten aufblasen möchte. Auf diese Weise können Sie festlegen, wie aufgeblasen werden soll.


#Aufblasbehandlung
train_datagen = ImageDataGenerator(
    rotation_range=90, #± Wie oft muss gedreht werden?
    width_shift_range=0.1, #Wie viel in horizontaler Richtung zu bewegen
    height_shift_range=0.1, #Wie viel in vertikaler Richtung zu bewegen
    rescale=1.0 / 255, #0~Normalisiert auf 1
    zoom_range=0.2, #Wie viel zu erweitern
    horizontal_flip=True, #Ob horizontal gedreht werden soll
    vertical_flip=True #Gibt an, ob in vertikaler Richtung gedreht werden soll
)

val_datagen = ImageDataGenerator(rescale=1.0 / 255)

Es geht darum, eine Aufblasverarbeitung für Zugdaten und nur eine Skalierungsverarbeitung für Testdaten durchzuführen. Die Parameter sind wie in den Kommentaren beschrieben, aber im Detail finden Sie detailliertere Parameterinformationen.

Ich möchte die obige Verarbeitung auf das eigentliche Bild anwenden. Mit einer Funktion namens flow_from_directory werden Daten gut aus dem Verzeichnis erstellt. * Um diesen Vorgang ausführen zu können, muss die Ordnerstruktur gemäß den Spezifikationen ordnungsgemäß erstellt werden.


#Generator generieren
train_generator = train_datagen.flow_from_directory(
    train_dir, #Pfad zum Verzeichnis
    target_size=(img_width, img_height), #Bildgröße nach Größenänderung
    color_mode='rgb', #Angeben des Bildkanals
    classes=classes, #Liste der Klassen (Bilder müssen sich in dem hier angegebenen Unterverzeichnis befinden)
    class_mode='categorical', #"categorical","binary","sparse"Eine solche
    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)

Ein Modell bauen

Ich werde endlich ein Modell machen. Darüber hinaus verweise ich auf die folgenden Artikel einschließlich der Struktur und Parameter.

Als Struktur wird VGG16 für die Faltschicht verwendet, und die vollständig verbundene Schicht wird von Ihnen selbst entworfen. Außerdem lernen wir nicht die Gewichte bis zur Schicht 15 kennen, sondern die letzte Faltungsschicht und die vollständig verbundene Schicht.


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

Laden Sie zuerst VGG16. Die Parameter sind:

--include_top: Gibt an, ob die vollständig verbundene Ebene eingeschlossen werden soll --weights: Es scheint, dass Sie im Moment nur "None (zufällige Initialisierung)" oder "imagenet" auswählen können, welche Art von Gewicht verwendet werden soll.

Definieren Sie als Nächstes die vollständig verbundene Ebene.


#Vollständig verbundene Schicht
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()

Die vollständig verbundene Schicht ist wie folgt.

_________________________________________________________________
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

Nachdem wir einen Exit erstellt haben, um ihn in die gewünschte Anzahl von Klassen zu klassifizieren, werden wir ihn mit VGG16 kombinieren.


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

#Festes Gewicht
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()

Die Art der Kombination ist dieselbe wie die Art und Weise, wie die funktionale API geschrieben wird. Verwenden Sie SGD mit einer geringen Lernrate, da die Optimierungsfunktion Feinabstimmung ist. Das Modell sieht folgendermaßen aus:

_________________________________________________________________
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

Lass uns jetzt lernen.


history = vgg_model.fit(
    train_generator, #Trainingsgenerator
    steps_per_epoch=len(train_generator), #Anzahl der Chargen pro Epoche
    epochs=30,
    validation_data=val_generator,
    validation_steps=len(val_generator))


#acc, val_gem. Grundstück
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

Ich glaube, ich konnte gut lernen.

Speichern und laden Sie dieses Modell abschließend, um die Modellerstellung abzuschließen.


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

Prognose

Als nächstes möchte ich Vorhersagen unter Verwendung des übergeordneten Modells treffen.


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 = ['Nicht brennbarer Abfall', 'Verpackungsbehälter Kunststoffe', 'Brennbarer Abfall', 'Schädlicher Abfall', 'Wertstoffe']

Klassen werden direkt namentlich angegeben, je nachdem, wann sie bereitgestellt wurden.

Das Bild muss kein Generator sein, daher wird es direkt geladen.


filename = "val/Wertstoffe/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 #Normalisierung
x = np.expand_dims(x, axis=0)

#Sagen Sie die Person auf dem Bild voraus
pred = model.predict(x)[0]
#Ergebnisse anzeigen
result = {c:s for (c, s) in zip(classes, pred*100)}
result = sorted(result.items(), key=lambda x:x[1], reverse=True)
print(result)

Das Ergebnis sieht so aus.

IMG_20201108_110533.jpg IMG_20201108_114503.jpg
'Wertstoffe', 99.783165 'Nicht brennbarer Abfall', 99.97801
'Nicht brennbarer Abfall', 0.1700096 'Wertstoffe', 0.014258962
'Verpackungsbehälter Kunststoffe', 0.04342786 'Verpackungsbehälter Kunststoffe', 0.007412854
'Brennbarer Abfall', 0.00205229 'Brennbarer Abfall', 0.0002818475
'Schädlicher Abfall', 0.0013515248 'Schädlicher Abfall', 3.024669e-05

Es ist möglich, dass es etwas Ähnliches wie den Datensatz zum Zeitpunkt des Trainings enthielt, da wir nicht viele Arten von Müll vorbereiten konnten, aber es scheint, dass wir sie richtig unterscheiden können.

Nächstes Mal möchte ich dieses Modell in Django integrieren. freue mich auf!


Artikelliste

Frage

Ich persönlich habe eine Frage. Wenn Sie "vgg_model.fit" ausführen, wird die Anzahl der Stapel pro Epoche in "step_per_epoch = len (train_generator) (= Anzahl der Bilder / Anzahl der Stapel)" angegeben. Dies sind jedoch die pro Epoche verwendeten Daten. Die Anzahl entspricht der ursprünglichen Anzahl der Bilder, und ich denke, dass sie nicht aufgeblasen wurde. Natürlich ist es möglich, mit verschiedenen Bildern zu trainieren, indem Epochen gestapelt werden, aber ich denke, dass es eine allgemeine Methode ist, innerhalb einer Epoche aufzublasen und zu trainieren. Wenn die Methode also anders ist, sagen Sie es mir bitte Bitte.

Übrigens, wenn Sie "step_per_epoch" auf einen Wert setzen, der größer als "len (train_generator)" ist, wird der folgende Fehler angezeigt.

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.

Wenn ich "train_generator.repeat ()" verwende, wird eine Fehlermeldung angezeigt, dass das "DirectoryIterator" -Objekt kein Attribut "toreat" hat. Wo soll ich es angeben? Wenn jemand es verstehen kann, wäre ich dankbar, wenn Sie mich unterrichten könnten.

Verweise

Recommended Posts

"Müll nach Bild klassifizieren!" App-Erstellungstagebuch Tag2 ~ Feinabstimmung mit VGG16 ~
"Müll nach Bild klassifizieren!" App-Erstellungstagebuch Tag1 ~ Datensatzerstellung ~
"Klassifizierung von Müll nach Bild!" App-Erstellungstagebuch Tag3 ~ Webanwendung mit Django ~
"Müll nach Bild klassifizieren!" App-Erstellungstagebuch Tag5 ~ Bereiten Sie das Frontend mit Bootstrap 2 ~ vor
"Klassifizieren Sie Müll nach Bild!" App-Erstellungstagebuch Tag8 ~ Heroku-Bereitstellung ~
"Müll nach Bild klassifizieren!" App-Erstellungstagebuch Tag4 ~ Bereiten Sie das Frontend mit Bootstrap ~ vor
"Müll nach Bild klassifizieren!" App-Erstellungstagebuch Tag6 ~ Korrektur der Verzeichnisstruktur ~
Bildklassifizierung mit selbst erstelltem neuronalen Netzwerk von Keras und PyTorch
Fordern Sie die Bildklassifizierung mit TensorFlow2 + Keras 4 heraus. ~ Lassen Sie uns mit dem trainierten Modell ~ vorhersagen
Bildklassifizierung mit Weitwinkel-Fundusbilddatensatz
Erstellen einer Bildaufteilungs-App mit Tkinter
Deep Learning 2 durch Implementierung gelernt (Bildklassifizierung)
Bildklassifizierung mit Keras-Von der Vorverarbeitung zum Klassifizierungstest-
Multi-Label-Klassifizierung nach Random Forest mit Scikit-Learn
Bildverarbeitung mit Lambda + OpenCV (graue Bilderzeugung)
Kochobjekterkennung durch Yolo + Bildklassifizierung