Bildklassifizierung mit PyTorch
Deep Learning-Lehrbücher sind voller professioneller und unverständlicher Terminologie. Ich versuche es auf ein Minimum zu beschränken und gebe immer ein Beispiel, das leicht erweitert werden kann, wenn Sie sich an die Arbeit mit PyTorch gewöhnen. Wir verwenden dieses Beispiel im gesamten Buch, um zu demonstrieren, wie ein Modell debuggt (Kapitel 7) oder für die Produktion bereitgestellt wird (Kapitel 8).
Von nun an bis zum Ende von Kapitel 4 werden wir den Bildklassifikator kompilieren. Neuronale Netze werden üblicherweise als Bildklassifizierer verwendet. Netzwerke bieten ein Bild und stellen eine einfache Frage: "Was ist das?"
Beginnen wir mit der Erstellung unserer Anwendung in PyTorch.
Klassifizierungsproblem
Hier erstellen wir einen einfachen Klassifikator, der einen Fisch von einer Katze unterscheiden kann. Wir werden den Entwurfs- und Entwicklungsprozess unseres Modells wiederholen, um es genauer zu machen.
In Abb. 2.1 und 2.2 zeigen einen Fisch und eine Katze in ihrer ganzen Pracht. Ich bin nicht sicher, ob der Fisch einen Namen hat, aber der Name der Katze ist Helvetica.
Beginnen wir mit der Erörterung einiger Standardklassifizierungsprobleme.
Standardschwierigkeiten
Wie schreibe ich ein Programm, das einen Fisch von einer Katze unterscheiden kann? Vielleicht würden Sie eine Reihe von Regeln schreiben, die beschreiben, ob eine Katze einen Schwanz hat oder ob ein Fisch Schuppen hat, und diese Regeln auf ein Bild anwenden, damit das Programm das Bild klassifizieren kann. Dies erfordert jedoch Zeit, Mühe und Geschick. Was ist, wenn Sie auf eine Manx-Katze stoßen? Obwohl es eindeutig eine Katze ist, hat sie keinen Schwanz.
Diese Regeln werden immer komplexer, wenn Sie versuchen, alle möglichen Szenarien mit ihnen zu beschreiben. Außerdem muss ich zugeben, dass visuelle Programmierung für mich schrecklich ist, daher ist der Gedanke, Code für all diese Regeln manuell schreiben zu müssen, erschreckend.
Sie benötigen eine Funktion, die eine Katze oder einen Fisch zurückgibt, wenn Sie ein Bild eingeben. Es ist schwierig, eine solche Funktion zu konstruieren, indem einfach alle Kriterien vollständig aufgelistet werden. Deep Learning zwingt den Computer jedoch im Wesentlichen dazu, die harte Arbeit zu leisten, all diese Regeln zu erstellen, über die wir gerade gesprochen haben, vorausgesetzt, wir erstellen die Struktur, versorgen das Netzwerk mit vielen Daten und lassen es wissen, ob es die richtige Antwort gibt. Das werden wir tun. Darüber hinaus lernen Sie einige grundlegende Techniken zur Verwendung von PyTorch.
Aber zuerst die Daten
Erstens brauchen wir Daten. Wie viele Daten? Hängt von verschiedenen Faktoren ab. Wie Sie in Kapitel 4 sehen werden, ist die Vorstellung, dass jede Deep-Learning-Technik große Datenmengen benötigt, um ein neuronales Netzwerk zu trainieren, nicht unbedingt wahr. Jetzt fangen wir jedoch bei Null an, was normalerweise den Zugriff auf viele Daten erfordert. Viele Bilder von Fischen und Katzen sind erforderlich.
Man könnte einige Zeit damit verbringen, eine Reihe von Bildern von einer Bildsuche bei Google herunterzuladen, aber es gibt einen einfacheren Weg: Die Standardsammlung von Bildern, die zum Trainieren neuronaler Netze verwendet wird, ist ImageNet. Es enthält über 14 Millionen Bilder und 20.000 Bildkategorien. Dies ist der Standard, nach dem alle Bildklassifizierer vergleichen. Daher mache ich Bilder von dort, obwohl Sie andere Optionen wählen können, wenn Sie möchten.
Neben Daten sollte PyTorch eine Möglichkeit haben, zu definieren, was eine Katze und was ein Fisch ist. Für uns ist es einfach genug, für einen Computer jedoch schwieriger (deshalb erstellen wir ein Programm!). Wir verwenden eine Kennzeichnung, die an die Daten angehängt ist, und dies wird als überwachtes Lernen bezeichnet. (Wenn Sie keinen Zugriff auf eines der Labels haben, haben Sie es erraten, es wird unbeaufsichtigtes maschinelles Lernen verwendet.)
Wenn wir ImageNet-Daten verwenden, sind deren Labels nicht nützlich, da sie zu viele Informationen enthalten. Das Beschriften einer Tabbykatze oder Forelle für einen Computer ist nicht dasselbe wie eine Katze oder ein Fisch.
Es ist erforderlich, sie neu zu kennzeichnen. Da ImageNet eine große Sammlung von Bildern ist, habe ich die Bild- und Tagging-URLs von Fischen und Katzen zusammengestellt (https://oreil.ly/NbtEU).
Sie können das Skript download.py in diesem Verzeichnis ausführen. Es lädt die Bilder von den URLs herunter und platziert sie an den entsprechenden Schulungsorten. Das Umetikettieren ist einfach; Das Skript speichert Bilder von Katzen im Zug- / Katzenverzeichnis und Bilder von Fischen im Zug- / Fischverzeichnis. Wenn Sie zum Herunterladen kein Skript verwenden möchten, erstellen Sie einfach diese Verzeichnisse und platzieren Sie die entsprechenden Bilder an den richtigen Stellen. Wir haben jetzt Daten, aber wir müssen sie in ein Format konvertieren, das PyTorch verstehen kann.
PyTorch und Datenlader
Das Laden und Umwandeln von Daten in trainingsbereite Formate ist häufig ein Bereich der Datenwissenschaft, der zu lange dauert. PyTorch hat etablierte Dateninteraktionsanforderungen entwickelt, die es ziemlich einfach machen, ob Sie mit Bildern, Text oder Audio arbeiten.
Die beiden Hauptbedingungen für die Arbeit mit Daten sind Datensätze und Datenlader. Ein Datensatz ist eine Python-Klasse, mit der wir die Daten empfangen können, die wir an das neuronale Netzwerk senden.
Ein Datenlader überträgt Daten von einem Datensatz in das Netzwerk. (Dies kann Informationen enthalten wie: Wie viele Arbeitsprozesse laden Daten in das Netzwerk hoch? Wie viele Bilder laden wir gleichzeitig hoch?)
Schauen wir uns zuerst den Datensatz an. Jeder Datensatz, unabhängig davon, ob er Bilder, Audio, Text, 3D-Landschaften, Börseninformationen oder etwas anderes enthält, kann mit PyTorch interagieren, sofern er die Anforderungen dieser abstrakten Python-Klasse erfüllt:
class Dataset(object):
def __getitem__(self, index):
raise NotImplementedError
def __len__(self):
raise NotImplementedError
Es ist ziemlich einfach: Wir müssen eine Methode verwenden, die die Größe unseres Datasets (len) zurückgibt, und eine, die ein Element in einem Paar (Label, Tensor) aus dem Dataset abrufen kann. Dies wird vom Datenlader aufgerufen, wenn er die Daten zum Training dem neuronalen Netzwerk zuführt. Wir müssen also einen Body für die getitem-Methode schreiben, der ein Bild aufnehmen, in einen Tensor konvertieren und zurücksetzen und markieren kann, damit PyTorch damit arbeiten kann. Alles ist klar, aber offensichtlich tritt dieses Szenario ziemlich oft auf. Vielleicht erleichtert PyTorch die Aufgabe?
Trainingsdatensatz erstellen
Das Torchvision-Paket enthält eine ImageFolder-Klasse, die so ziemlich alles macht, vorausgesetzt, unsere Bilder befinden sich in einer Struktur, in der jedes Verzeichnis eine Bezeichnung ist (zum Beispiel befinden sich alle Katzen in einem Verzeichnis namens cat). Folgendes benötigen Sie für das Katzen- und Fischbeispiel:
import torchvision
from torchvision import transforms
train_data_path = "./train/"
transforms = transforms.Compose([
transforms.Resize(64),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225] )
])
train_data = torchvision.datasets.ImageFolder
(root=train_data_path,transform=transforms)
Hier wird noch etwas hinzugefügt, da Sie mit Torchvision auch eine Liste von Transformationen angeben können, die auf das Bild angewendet werden sollen, bevor es in das neuronale Netzwerk eintritt. Die Standardtransformation besteht darin, die Bilddaten in einen Tensor umzuwandeln (die im vorherigen Code gezeigte Methode trans forms.ToTensor ()), aber es werden auch einige andere Dinge ausgeführt, die möglicherweise nicht so offensichtlich sind.
Erstens sind GPUs so konstruiert, dass sie schnelle Berechnungen in Standardgröße durchführen. Aber wir haben wahrscheinlich eine Auswahl an Bildern in vielen Auflösungen. Um die Verarbeitungsleistung zu verbessern, skalieren wir jedes Eingabebild mithilfe der Größenänderungstransformation (64) auf dieselbe Auflösung von 64 x 64. Dann konvertieren wir die Bilder in einen Tensor und normalisieren den Tensor schließlich um einen bestimmten Satz von Mittelwert- und Standardabweichungspunkten.
Die Normalisierung ist wichtig, da erwartet wird, dass eine große Anzahl von Multiplikationen durchgeführt wird, wenn die Eingabe die Schichten des neuronalen Netzwerks durchläuft. Das Halten der Eingabewerte zwischen 0 und 1 verhindert einen starken Anstieg der Werte während der Lernphase (bekannt als das explodierende Gradientenproblem). Diese magische Inkarnation ist nur der Mittelwert und die Standardabweichung des gesamten ImageNet-Datensatzes. Sie können es speziell für eine Untergruppe von Fischen und Katzen berechnen, aber diese Werte sind ziemlich zuverlässig. (Wenn Sie an einem völlig anderen Datensatz arbeiten würden, müssten dieser Mittelwert und diese Varianz berechnet werden, obwohl viele einfach ImageNet-Konstanten verwenden und akzeptable Ergebnisse melden.)
Zusammensetzbare Transformationen erleichtern auch das Ausführen von Aktionen wie Bilddrehung und Bildverschiebung zur Datenerweiterung, auf die wir in Kapitel 4 zurückkommen werden.
In diesem Beispiel ändern wir die Größe der Bilder auf 64 x 64. Ich habe diese zufällige Auswahl getroffen, um die Berechnung in unserem ersten Netzwerk zu beschleunigen. Die meisten vorhandenen Architekturen, die Sie in Kapitel 3 sehen werden, verwenden 224 x 224 oder 299 x 299 für ihre Eingabebilder. Je größer die Eingabedateigröße, desto mehr Daten kann das Netzwerk im Allgemeinen lernen. Die Kehrseite der Medaille ist dass Sie normalerweise einen kleineren Stapel von Bildern in den GPU-Speicher einpassen können.
Es gibt viele andere Informationen zu Datensätzen, und das ist noch nicht alles. Aber warum sollten wir mehr wissen als nötig, wenn wir bereits über den Trainingsdatensatz Bescheid wissen?
Validierungs- und Referenzdatensätze
Unser Trainingsdatensatz ist eingerichtet, aber jetzt müssen wir dieselben Schritte mit dem Validierungsdatensatz wiederholen. Was ist der Unterschied hier? Eine der Gefahren des tiefen Lernens (und tatsächlich des gesamten maschinellen Lernens) ist die Überanpassung: Das Modell kann wirklich gut erkennen, worauf es trainiert wurde, funktioniert jedoch nicht an Beispielen, die es nicht gesehen hat. Das Modell sieht ein Bild einer Katze, und wenn alle anderen Bilder von Katzen diesem nicht sehr ähnlich sind, entscheidet das Modell, dass es keine Katze ist, obwohl das Gegenteil offensichtlich ist. Um zu verhindern, dass sich das neuronale Netzwerk so verhält, laden wir das Kontrollbeispiel in download.py, dh in eine Reihe von Bildern von Katzen und Fischen, die nicht im Trainingsdatensatz enthalten sind. Am Ende jedes Trainingszyklus (auch als Epoche bezeichnet) vergleichen wir diesen Satz, um sicherzustellen, dass das Netzwerk nicht falsch ist. Seien Sie nicht beunruhigt, der Code für diese Prüfung ist unglaublich einfach:Dies ist derselbe Code mit mehreren geänderten Variablennamen:
val_data_path = "./val/"
val_data = torchvision.datasets.ImageFolder(root=val_data_path,
transform=transforms)
Wir haben nur die Transformationskette verwendet, anstatt sie erneut zu definieren.
Zusätzlich zum Validierungsdatensatz müssen wir auch einen Validierungsdatensatz erstellen. Es wird verwendet, um das Modell nach Abschluss aller Schulungen zu testen:
test_data_path = "./test/"
test_data = torchvision.datasets.ImageFolder(root=test_data_path,
transform=transforms)
Auf den ersten Blick können die verschiedenen Arten von Sätzen komplex und verwirrend sein. Deshalb habe ich eine Tabelle zusammengestellt, um anzugeben, welcher Teil des Trainings die einzelnen Sätze verwendet (Tabelle 2.1).
Wir können jetzt Datenlader mit ein paar weiteren Zeilen Python-Code erstellen:
batch_size=64
train_data_loader = data.DataLoader(train_data, batch_size=batch_size)
val_data_loader = data.DataLoader(val_data, batch_size=batch_size)
test_data_loader = data.DataLoader(test_data, batch_size=batch_size)
Neu und bemerkenswert in diesem Code ist der Befehl batch_size. Sie sagt, wie viele Bilder das Netzwerk durchlaufen werden, bevor wir es trainieren und aktualisieren. Theoretisch könnten wir einer Reihe von Bildern in den Test- und Trainingsdatensätzen batch_size zuweisen, sodass das Netzwerk jedes Bild vor dem Aktualisieren sieht. In der Praxis wird dies normalerweise nicht durchgeführt, da kleinere Pakete (in der Literatur häufiger als Minipakete bekannt) weniger Speicher benötigen und nicht alle Informationen zu jedem Bild im Datensatz gespeichert werden müssen und eine kleinere Paketgröße zu einem schnelleren Lernen als Netzwerk führt Updates viel schneller. Bei PyTorch-Datenladern ist die Größe von batch_size standardmäßig auf 1 festgelegt. Sie möchten sie höchstwahrscheinlich ändern. Obwohl ich 64 gewählt habe, können Sie experimentieren, um zu verstehenWie viele Mini-Pakete können verwendet werden, ohne dass der GPU-Speicher knapp wird? Experimentieren Sie mit einigen zusätzlichen Parametern: Sie können beispielsweise festlegen, wie die Datensätze abgerufen werden, ob der gesamte Datensatz bei jedem Start gemischt wird und wie viele Workflows zum Abrufen von Daten aus dem Datensatz erforderlich sind. All dies finden Sie inPyTorch-Dokumentation .
Hier geht es darum, Daten an PyTorch zu übergeben. Stellen wir uns nun ein einfaches neuronales Netzwerk vor, das mit der Klassifizierung unserer Bilder beginnt.
Endlich ein neuronales Netzwerk!
Wir beginnen mit dem einfachsten Deep-Learning-Netzwerk - einer Eingabeebene, die mit den Eingabetensoren (unseren Bildern) zusammenarbeitet. eine Ausgabeschicht von der Größe der Anzahl unserer Ausgabeklassen (2); und eine versteckte Schicht dazwischen. Im ersten Beispiel werden vollständig verknüpfte Ebenen verwendet. In Abb. 2.3 zeigt eine Eingabeschicht von drei Knoten, eine verborgene
Schicht von drei Knoten und eine Ausgabe von zwei Knoten.
In diesem Beispiel wirkt sich jeder Knoten in einer Schicht auf einen Knoten in der nächsten Schicht aus, und jede Verbindung hat eine Gewichtung, die die Stärke des Signals von diesem Knoten zur nächsten Schicht bestimmt. (Dies sind die Gewichte, die aktualisiert werden, wenn wir das Netzwerk trainieren, normalerweise durch zufällige Initialisierung.) Wenn die Eingabe das Netzwerk durchläuft, können wir (oder PyTorch) einfach die Gewichte und Verzerrungen dieser Schicht mit der Eingabe multiplizieren. Bevor sie an die nächste Funktion weitergegeben werden, wird dieses Ergebnis in die Aktivierungsfunktion eingegeben, die lediglich eine Möglichkeit darstellt, Nichtlinearität in unser System einzuführen.
Aktivierungsfunktionen
Die Aktivierungsfunktion klingt schwierig, aber die häufigste Aktivierungsfunktion, die Sie jetzt finden, ist ReLU oder eine gleichgerichtete Lineareinheit. Wieder klug! Dies ist jedoch nur eine Funktion, die max (0, x) implementiert. Das Ergebnis ist also 0, wenn der Eingang negativ ist, oder nur der Eingang (x), wenn x positiv ist. So einfach ist das!
Eine weitere Aktivierungsfunktion, auf die Sie am wahrscheinlichsten stoßen, ist die multivariate Logistikfunktion (Softmax), die im mathematischen Sinne etwas komplizierter ist. Grundsätzlich wird eine Reihe von Werten von 0 bis 1 generiert, die sich zu 1 addieren (Wahrscheinlichkeiten!). Die Werte werden so gewichtet, dass die Differenz erhöht wird. Das heißt, es wird ein Ergebnis in einem Vektor erzeugt, der größer als alle anderen ist. Sie werden häufig sehen, dass es am Ende eines Klassifizierungsnetzwerks verwendet wird, um sicherzustellen, dass das Netzwerk eine eindeutige Vorhersage darüber macht, welche Klasse es für die Eingabedaten hält.
Nachdem wir alle diese Bausteine haben, können wir mit dem Aufbau unseres ersten neuronalen Netzwerks beginnen.
Erstellung eines neuronalen Netzwerks
Das Erstellen eines neuronalen Netzwerks in PyTorch ähnelt dem Programmieren in Python. Wir erben von einer Klasse namens torch.nn.Network und füllen die Methoden __init__ und forward aus:
class SimpleNet(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(12288, 84)
self.fc2 = nn.Linear(84, 50)
self.fc3 = nn.Linear(50,2)
def forward(self):
x = x.view(-1, 12288)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.softmax(self.fc3(x))
return x
simplenet = SimpleNet()
Auch dies ist nicht schwierig. Wir nehmen die erforderlichen Einstellungen in init () vor. In diesem Fall rufen wir den Superklassenkonstruktor und drei vollständig verbundene Ebenen auf (in PyTorch als linear bezeichnet, in Keras als dicht bezeichnet). Die forward () -Methode beschreibt, wie Daten sowohl im Training als auch in der Vorhersage (Inferenz) über das Netzwerk übertragen werden. Zuerst müssen wir den 3D-Tensor (x und y plus 3-Kanal-Farbinformationen - rot, grün, blau) im Bild transformieren - Aufmerksamkeit! - in einen eindimensionalen Tensor, damit er an die erste lineare Ebene übergeben werden kann, und wir tun dies mit view (). Daher wenden wir die Ebenen und Aktivierungsfunktionen der Reihe nach an und geben die Softmax-Ausgabe zurück, um die Vorhersage für dieses Bild zu erhalten.
Die Zahlen in den ausgeblendeten Ebenen sind willkürlich, mit Ausnahme der Ausgabe der letzten Ebene, nämlich 2, die unseren beiden Klassen - Katze oder Fisch - entspricht. Erfordert, dass Daten in Ebenen verkleinert werden, wenn sie im Stapel verkleinert werden. Wenn die Schicht beispielsweise von 50 Eingängen auf 100 Ausgänge wechselt, kann das Netzwerk lernen, indem es einfach 50 Verbindungen an fünfzig von hundert Ausgängen übergibt und seine Arbeit als abgeschlossen betrachtet. Indem wir die Größe der Ausgabe im Verhältnis zur Eingabe reduzieren, zwingen wir diesen Teil des Netzwerks, die Repräsentativität der ursprünglichen Eingabe mit weniger Ressourcen zu lernen, was vermutlich bedeutet, dass das Netzwerk einige Unterscheidungsmerkmale der Bilder definiert: Zum Beispiel hat es gelernt, eine Flosse oder einen Schwanz zu erkennen.
Wir haben eine Prognose und können sie mit der tatsächlichen Beschriftung des Originalbilds vergleichen, um festzustellen, ob sie korrekt war. Es muss jedoch möglich sein, dass PyTorch nicht nur die Richtigkeit oder Unrichtigkeit einer Vorhersage quantifizieren kann, sondern auch, wie richtig oder falsch sie ist. Die Verlustfunktion macht das.
ÜBER DEN AUTOR
Ian Poynter (Ian Pointer) - Ingenieur für Datenwissenschaft, spezialisiert auf Lösungen für maschinelles Lernen (einschließlich vertiefender Lehrmethoden) für eine Reihe von Kunden im Fortune 100. Derzeit arbeitet Yang in Lucidworks, das sich mit der Entwicklung fortschrittlicher Anwendungen und NLP befasst.
»Weitere Details zum Buch finden Sie auf der Website des Verlags.
» Inhaltsverzeichnis
» Auszug
für Einwohner 25% Rabatt auf den Gutschein - PyTorch
Nach Zahlungseingang für die Papierversion des Buches wird ein E-Book per E-Mail verschickt.