Es ist eine der Regularisierung. Durch pseudo-erhöhtes Erhöhen der Trainingsdaten besteht der Vorteil, dass Sie lange Zeit langsam lernen können.
Ist es als Datenerweiterung wirksam, wenn die Anzahl der Daten gering ist? Vorerst möchte ich es diesmal mit CIFAR10 versuchen.
Eine kurze Beschreibung der Shake-Shake-Regularisierung ist oben dargestellt. Erstellen Sie in Resnet zwei parallele Redisualblöcke und fügen Sie der Ausgabe der Restblöcke die folgenden Operationen hinzu.
Einzelheiten finden Sie in den Artikeln anderer Personen. Ich habe diesen Artikel gelesen und verstanden. https://qiita.com/masataka46/items/fc7f31073c89b02f8a04
Das Papier wurde auf verschiedene Arten entworfen.
Es gibt zwei Arten von Resnet, die einfache Architektur und die Engpassarchitektur, aber dieses Mal möchte ich die einfache Architektur verwenden, die dem Papier folgt.
test.py
class ResidualPlainBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride, padding=0):
super(ResidualPlainBlock, self).__init__()
self.in_channels = in_channels
self.out_channels = out_channels
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)
self.bn1 = nn.BatchNorm2d(out_channels)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(out_channels)
self.conv1_2 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)
self.bn1_2 = nn.BatchNorm2d(out_channels)
self.conv2_2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
self.bn2_2 = nn.BatchNorm2d(out_channels)
self.identity = nn.Identity()
if in_channels != out_channels:
self.down_avg1 = nn.AvgPool2d(kernel_size=1, stride=1)
self.down_conv1 = nn.Conv2d(in_channels, in_channels, kernel_size=1, stride=stride, padding=0)
self.down_pad1 = nn.ZeroPad2d((1,0,1,0))
self.down_avg2 = nn.AvgPool2d(kernel_size=1, stride=1)
self.down_conv2 = nn.Conv2d(in_channels, in_channels, kernel_size=1, stride=stride, padding=0)
#Spezielle Verarbeitung während der Probenahme
def shortcut(self,x):
x = F.relu(x)
h1 = self.down_avg1(x)
h1 = self.down_conv1(h1)
h2 = self.down_pad1(x[:,:,1:,1:])
h2 = self.down_avg1(h2)
h2 = self.down_conv2(h2)
return torch.cat((h1,h2),axis=1)
def forward(self, x):
if self.training:
#1. Restblock
out = self.bn1(self.conv1(F.relu(x)))
out = self.bn2(self.conv2(F.relu(out)))
#Zweiter Restblock
out2 = self.bn1_2(self.conv1_2(F.relu(x)))
out2 = self.bn2_2(self.conv2_2(F.relu(out2)))
if self.in_channels != self.out_channels:
output = self.shortcut(x) + ShakeShake.apply(out,out2)
else:
output = self.identity(x) + ShakeShake.apply(out,out2)
return output
else:
out = self.bn1(self.conv1(F.relu(x)))
out = self.bn2(self.conv2(F.relu(out)))
out2 = self.bn1_2(self.conv1_2(F.relu(x)))
out2 = self.bn2_2(self.conv2_2(F.relu(out2)))
if self.in_channels != self.out_channels:
output = self.shortcut(x) + (out+out2)*0.5
else:
output = self.identity(x) + (out+out2)*0.5
return output
Der Konstruktor ist chaotisch, aber ich denke, Sie können es verstehen, wenn Sie sich die Vorwärtsfunktion ansehen.
Der Inhalt der Vorwärtsfunktion ist 1: Geben Sie das empfangene x zwei Blöcken 2: Ausgabe aus und aus2 3: Lassen Sie out and out2 von ** ShakeShake.apply () ** verarbeiten 4: Fügen Sie die Verknüpfung und 3 hinzu und geben Sie sie zusammen aus
Sie können eine Klasse namens ShakeShake-Klasse definieren, um die Vorwärts- und Rückwärtsverarbeitung zu definieren.
test.py
class ShakeShake(torch.autograd.Function):
@staticmethod
def forward(ctx, i1, i2):
alpha = random.random()
result = i1 * alpha + i2 * (1-alpha)
return result
@staticmethod
def backward(ctx, grad_output):
beta = random.random()
return grad_output * beta, grad_output * (1-beta)
In Forward wird eine Zufallszahl Alpha generiert und mit out und out2 multipliziert.
Rückwärts wird eine neue Zufallszahl Beta generiert und auf grad_output angewendet (Wert, der durch Fehlerrückübertragung übertragen wird).
Mit PyTorch können Sie Lernraten planen.
Implementieren Sie wie folgt.
test.py
learning_rate = 0.02
optimizer = optim.SGD(net.parameters(),lr=learning_rate,momentum=0.9,weight_decay=0.0001)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50, eta_min=0.001)
for i in range(200):
#for 1epoch
#Lerne hier für 1 Epoche...
scheduler.step()
Auf diese Weise schwankt die Lernrate für jede Epoche entlang der Kosinuskurve.
Rufen Sie nach dem Definieren des Optimierers den Namen ** CosineAnnealingLR ** auf.
Das erste Argument ist Optimierer, das zweite Argument (T_max) ist die Anzahl der Schritte (Epochennummer) bis zum halben Kosinuszyklus, und das dritte Argument ist die minimale Lernrate.
Im obigen Fall sinkt die Lernrate nach 50 Epochen von 0,02 auf 0,001, kehrt dann nach 50 Epochen zurück, sinkt dann nach 50 Epochen und so weiter.
** Die Genauigkeit betrug 89,43% **.
Es ist subtil, weil es zu über 95% im Papier ist.
Die Genauigkeit von normalem ResNet, das nicht entwickelt wurde, liegt jedoch bei etwa 80%, sodass die Genauigkeit anscheinend besser ist.
Das blaue ist train_acc und das orange ist test_acc.
Tatsächlich gibt es einige Stellen, die laut dem Papier nicht implementiert sind.
Shake-Shake-Regularisierung macht als leistungsstarke Regularisierungsmethode auf sich aufmerksam.
Vor kurzem wurde anscheinend eine neue Methode namens Shake Drop entwickelt, die ich auch implementieren werde.
Recommended Posts