[PYTHON] Lesen und implementieren Sie Deep Residual Learning für die Bilderkennung

In diesem Artikel stellen wir das vom berühmten ResNet vorgeschlagene Papier "Deep Residual Learning for Image Recognition" (CVPR 2016) \ [1] vor. Außerdem habe ich den Code der CIFAR-10-Klassifizierung implementiert, an dem in dem Artikel gearbeitet wird (GitHub) und tatsächlich ein Reproduktionsexperiment durchgeführt. Das in diesem Dokument vorgeschlagene ResNet hat nicht nur den ILSVRC-Wettbewerb für die Klassifizierungsgenauigkeit von ImageNet gewonnen, sondern wird aufgrund der Effektivität und Vielseitigkeit der Vorschlagsmethode für tiefe Netzwerke auch für eine Vielzahl von Aufgaben eingesetzt. (Die Anzahl der Zitate übersteigt ab August 2020 52.000).

Papierkommentar

Überblick

Es wurde traditionell gesagt, dass tiefe neuronale Netze schwer zu erlernen sind. Anstatt den wahren Wert direkt zu lernen, verwenden wir in diesem Artikel eine Struktur, in der viele Blöcke des neuronalen Netzwerks, die das "Residuum" (Differenz zum wahren Wert) lernen, verbunden sind, um das Lernen tiefer Netzwerke zu erleichtern. Deutlich verbesserte Leistung für verschiedene Bilderkennungsaufgaben.

Hintergrund

Bei der Bilderkennung ist bekannt, dass die semantischen Merkmale umso tiefer extrahiert werden können, je tiefer die Netzwerkhierarchie ist. Das einfache Stapeln der Netzwerkschichten verursachte jedoch Probleme wie das Verschwinden des Gradienten / die Explosion des Gradienten, und die Netzwerkparameter konvergierten nicht und das Lernen konnte nicht gut durchgeführt werden. Dieses Problem der Konvergenzschwierigkeiten wurde durch den Anfangswert des Netzwerks und die Normalisierungsmethode gelöst, aber selbst wenn es konvergiert, gab es ein Problem, dass die Genauigkeit abnimmt, wenn die Schicht vertieft wird (dies). Ist nicht übertrainiert und der Trainingsfehler ist schlimmer (siehe Grafik unten). image.png (Die Abbildung stammt aus dem Papier. Gleiches gilt unten)

Vorgeschlagene Methode

Die in diesem Artikel vorgeschlagene Methode besteht darin, ein tiefes Netzwerk durch Verbinden einer großen Anzahl kleiner "Restblöcke" zu realisieren. Jeder Restblock besteht aus mehreren Gewichtsschichten und einer Identitätszuordnung, wie in der folgenden Abbildung gezeigt. Unter der Annahme, dass die im gesamten Block auszudrückende Funktion $ H (x) $ ist, wird $ F (x) = H (x) - x $ in dem Teil gelernt, in dem die Gewichtsschichten kombiniert werden. Deshalb wird es "Residuum" genannt. image.png Diese Methode löst das am Ende des Hintergrunds erwähnte Problem "Je tiefer die Schicht, desto geringer die Genauigkeit". Tatsächlich ist es schwierig, die Identitätszuordnung ($ H (x) = x $) für mehrere nichtlineare Schichten zu lernen, und diese Zuordnung kann in Blöcken, in denen die Identitätszuordnung die optimale Lösung ist, wenn die Anzahl der Schichten erhöht wird, nicht gut gelernt werden. Es wird gesagt, dass die Genauigkeit sinken wird. Wenn Sie jedoch den vorgeschlagenen Restblock verwenden, können Sie die Identitätszuordnung leicht erlernen, indem Sie einfach das Gewicht der Gewichtsschicht auf 0 setzen. In der Praxis ist es selten eine wahre Identität, aber es erleichtert das Lernen in Fällen, in denen $ x $ und $ H (x) $ sehr klein sind.

Details des Restblocks

Wenn die Ausgabe des Restblocks $ y $ ist, kann der Block durch eine solche Operation ausgedrückt werden. $y = F(x) + x$ Dieses $ F (x) $ wird durch Kombinieren von zwei oder mehr Schichten realisiert. In der obigen Abbildung gibt es beispielsweise zwei Ebenen, sodass $ \ sigma $ als ReLU-Funktion verwendet wird. $F(x) = W_2\sigma(W_1x)$ Es kann ausgedrückt werden als (der Bias-Term wird hier weggelassen). Die nichtlineare Operation von ReLU wird tatsächlich nach der endgültigen Addition ausgeführt, also ist es tatsächlich so $y = \sigma(W_2\sigma(W_1x) + x)$ Es sieht aus wie. Wenn die Anzahl der Kanäle von $ x $ und $ F (x) $ nicht übereinstimmt, wird die Anzahl der Kanäle durch Auffüllen mit 0 oder Durchführen einer 1x1-Faltung angepasst.

Gesamtnetzwerkstruktur

image.png Dieses Netzwerkdiagramm ist ein Netzwerk zur Lösung von ImageNet-Klassifizierungsproblemen. Die Abbildung ganz rechts zeigt die vorgeschlagene Methode. Die Netzwerkstruktur basiert auf dem Prinzip, dass bei gleicher Größe der Feature-Map die Anzahl der Kanäle gleich ist und bei halbierter Größe der Feature-Map die Anzahl der Kanäle verdoppelt wird. Die Operation zum Reduzieren der Größe der Feature-Map besteht im Wesentlichen darin, den Schritt der Faltung der ersten Schicht auf 2 zu setzen, und es wird kein Pooling durchgeführt (außer dem ersten und dem letzten). (Das Pooling vor dem Eintritt in die letzte FC-Schicht ist übrigens Global Average Pooling.) Die Chargennormalisierung wird unmittelbar nach jeder Faltung ausgeführt.

Implementierungsunterschiede für jeden Datensatz

CIFAR-10 Die Bildklassifizierung von CIFAR-10 weist eine geringfügig andere Netzwerkstruktur auf, da die Eingabebildgröße viel kleiner als die von ImageNet ist. Die erste Schicht ist eine einzelne 3x3-Faltung, gefolgt von Restblöcken. Die Faltungsschicht verwendet $ 6n + 1 $ Schichten, und die Aufteilung ist wie in der folgenden Tabelle gezeigt. Das Ergebnis sind $ 3n $ Restblöcke. image.png Das globale durchschnittliche Pooling wird nach dieser $ 6n + 1 $ -Schicht durchgeführt, und die Klassifizierung wird unter Verwendung von 10 FC-Schichten durchgeführt. Experimente werden bei $ n = \ {3, 5, 7, 9 \} $ durchgeführt, was zu einem Netzwerk mit 20, 32, 44, 56 $ Schichten führt. Wenn die Anzahl der Kanäle unterschiedlich ist, erfolgt die Hinzufügung der Identitätszuordnung, indem der fehlende Teil mit 0 gefüllt wird.

Reproduktionsimplementierung

Von den in diesem Dokument vorgestellten Datensätzen haben wir den gesamten Code implementiert, der das CIFAR-10-Klassifizierungsproblem mithilfe von PyTorch löst. Der gesamte Quellcode wurde auf GitHub veröffentlicht.

Code

Hier werden auch nur die wichtigsten Teile der Modelldefinition veröffentlicht. Der Restblock wird als eine Klasse "ResNetCifarBlock" implementiert, und die allgemeine Funktion "make_resblock_group", die Gruppen mit der gleichen Anzahl von Kanälen erstellt, wird implementiert, um den Code einfach und erweiterbar zu machen. Wenn sich die Größe der Feature-Map und die Anzahl der Kanäle ändern, werden zuerst die Pixel ausgedünnt und dann Nullen eingegeben.

nets.py


import torch
import torch.nn as nn
import torch.nn.functional as F


class ResNetCifarBlock(nn.Module):
    def __init__(self, input_nc, output_nc):
        super().__init__()
        stride = 1
        self.expand = False
        if input_nc != output_nc:
            assert input_nc * 2 == output_nc, 'output_nc must be input_nc * 2'
            stride = 2
            self.expand = True

        self.conv1 = nn.Conv2d(input_nc, output_nc, kernel_size=3, stride=stride, padding=1)
        self.bn1 = nn.BatchNorm2d(output_nc)
        self.conv2 = nn.Conv2d(output_nc, output_nc, kernel_size=3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(output_nc)

    def forward(self, x):
        xx = F.relu(self.bn1(self.conv1(x)), inplace=True)
        y = self.bn2(self.conv2(xx))
        if self.expand:
            x = F.interpolate(x, scale_factor=0.5, mode='nearest')  # subsampling
            zero = torch.zeros_like(x)
            x = torch.cat([x, zero], dim=1)  # option A in the original paper
        h = F.relu(y + x, inplace=True)
        return h


def make_resblock_group(cls, input_nc, output_nc, n):
    blocks = []
    blocks.append(cls(input_nc, output_nc))
    for _ in range(1, n):
        blocks.append(cls(output_nc, output_nc))
    return nn.Sequential(*blocks)


class ResNetCifar(nn.Module):
    def __init__(self, n):
        super().__init__()

        self.conv = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1)
        self.bn = nn.BatchNorm2d(16)
        self.block1 = make_resblock_group(ResNetCifarBlock, 16, 16, n)
        self.block2 = make_resblock_group(ResNetCifarBlock, 16, 32, n)
        self.block3 = make_resblock_group(ResNetCifarBlock, 32, 64, n)
        self.pool = nn.AdaptiveAvgPool2d(output_size=(1, 1))  # global average pooling
        self.fc = nn.Linear(64, 10)

    def forward(self, x):
        x = F.relu(self.bn(self.conv(x)), inplace=True)
        x = self.block1(x)
        x = self.block2(x)
        x = self.block3(x)
        x = self.pool(x)
        x = x.view(x.shape[0], -1)
        x = self.fc(x)
        return x

Verwendeter Datensatz und verschiedene Parameter

Alles in der Zeitung folgt ihm.

Datensatz

CIFAR-10 ist ein Datensatz, der 60.000 Bilder von 10 Klassen enthält, von denen 50.000 für das Training und 10.000 für die Auswertung verwendet wurden. Die Bildgröße beträgt 32x32.

Parameter

Ergebnis

Die Ergebnisse des Lernens und der Bewertung mit den obigen Einstellungen sind wie folgt. Die Top-1-Fehlerrate wird als Bewertungsindex verwendet. Es repräsentiert den Durchschnitt ± Standardabweichung in 5 Läufen.

Methode n Top-1 error rate (%) Reported error rate (%)
ResNet-20 3 8.586 ± 0.120 8.75
ResNet-32 5 7.728 ± 0.318 7.51
ResNet-44 7 7.540 ± 0.475 7.17
ResNet-56 9 7.884 ± 0.523 6.97

Je tiefer die Schicht ist, desto größer ist die Variation der Fehlerrate, und obwohl sich der Durchschnittswert von dem im Papier angegebenen Wert unterscheidet, kann gesagt werden, dass der Wert im Allgemeinen nahe am Papierwert liegt. (Obwohl es nicht geschrieben wurde, denke ich, dass es ein vernünftiger Wert ist, wenn der Papierwert mehrmals ausgeführt wird und am besten verwendet wird.)

Verweise

Recommended Posts

Lesen und implementieren Sie Deep Residual Learning für die Bilderkennung
Implementierung eines Deep Learning-Modells zur Bilderkennung
Deep Learning Bilderkennung 2 Modellimplementierung
[AI] Deep Learning für das Entrauschen von Bildern
Bilderkennungsmodell mit Deep Learning im Jahr 2016
Deep Learning Bilderkennung 3 nach der Modellerstellung
Deep Learning für die Bildung von Verbindungen?
Implementieren Sie Deep Learning / VAE (Variational Autoencoder)
Deep Learning 2 durch Implementierung gelernt (Bildklassifizierung)
Machen Sie Ihren eigenen PC für tiefes Lernen
Bildausrichtung: von SIFT bis Deep Learning
[Deep Learning] Nogisaka Gesichtserkennung ~ Für Anfänger ~
Bilderkennung
Tiefes Lernen
Informationen zur Datenerweiterungsverarbeitung für tiefes Lernen
Empfohlene Studienreihenfolge für Anfänger des maschinellen Lernens / Deep Learning
Grundprinzipien der Bilderkennungstechnologie (für Anfänger)
[Implementierung zum Lernen] Implementieren Sie Stratified Sampling in Python (1)
Ich habe den Deep Learning Framework Chainer installiert
Deep Learning Memorandum
Starten Sie Deep Learning
Aufgeblasenes Lernbild
Python Deep Learning
Deep Learning × Python
Bildersammlung Python-Skript zum Erstellen von Datensätzen für maschinelles Lernen
Deep Learning Bildanalyse beginnend mit Kaggle und Keras
[Erkennung von Abnormalitäten] Erkennen Sie Bildverzerrungen durch Fernunterricht
Techniken zum Verständnis der Grundlagen von Deep-Learning-Entscheidungen
Tiefes Lernen mit Python Kapitel 2 (Material für runde Vorlesung)
Künstliche Intelligenz, maschinelles Lernen, tiefes Lernen zu implementieren und zu verstehen
Eine Szene, in der GPU für tiefes Lernen nützlich ist?
Einführung in Deep Learning zum ersten Mal (Chainer) Erkennung japanischer Zeichen Kapitel 1 [Umgebungskonstruktion]