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
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.
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
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)
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()
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')
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.
'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
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.
Recommended Posts