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).
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.
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). (Die Abbildung stammt aus dem Papier. Gleiches gilt unten)
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. 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.
Wenn die Ausgabe des Restblocks $ y $ ist, kann der Block durch eine solche Operation ausgedrückt werden.
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. 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.
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.
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
Alles in der Zeitung folgt ihm.
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.
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 | 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.)
Recommended Posts