Ich wollte ein tiefes neuronales Netzwerk wie Residual Network verwenden. ImageNet verwendete das CIFAR-10-Bilddatensatz zur Klassifizierung von Bildern, da MNIST nicht ausreicht und ImageNet anscheinend schwierig zu erfassen ist und Zeit zum Lernen benötigt.
CIFAR-10-Bilddatensatz ist ein kleiner Farbbilddatensatz https://www.cs.toronto.edu/~kriz/cifar.html
Ich habe es in der folgenden Umgebung ausgeführt
Der verwendete Quellcode ist unten. Bei den unten beschriebenen Befehlen wird davon ausgegangen, dass Sie diesen Code geklont haben und sich im Stammverzeichnis des Quellbaums befinden. https://github.com/dsanno/chainer-cifar
Es hat die folgenden Funktionen
Bei der Klassifizierung wurde die Fehlerrate wie folgt gemessen.
Sie können es über den Link "CIFAR-10 Python-Version" unter https://www.cs.toronto.edu/~kriz/cifar.html herunterladen. Oder laden Sie den Datensatz mit dem folgenden Befehl herunter. Die Dataset-Datei ist 166 MB groß und es dauert einige Zeit, wenn die Linie dünn ist.
$ python src/download.py
Entpacken Sie den heruntergeladenen Datensatz und Sie sehen das folgende Bild.
Der Inhalt von data_batch_1 usw. ist diktiert, und die Rohdaten des Bildes werden in Daten gespeichert, und die Etiketteninformationen werden in Etiketten gespeichert. Hier ist ein Beispiel, das die ersten 100 als Bild speichert, während data_batch_1 untersucht wird.
$ python
>>> import cPickle as pickle
>>> f = open('dataset/cifar-10-batches-py/data_batch_1', 'rb')
>>> train_data = pickle.load(f)
>>> f.close()
>>> type(train_data['data'])
<type 'numpy.ndarray'>
>>> train_data['data'].shape #Holen Sie sich die Form der Rohdaten
(10000L, 3072L)
>>> train_data['data'][:5] #Holen Sie sich die ersten 5 Rohdaten
array([[ 59, 43, 50, ..., 140, 84, 72],
[154, 126, 105, ..., 139, 142, 144],
[255, 253, 253, ..., 83, 83, 84],
[ 28, 37, 38, ..., 28, 37, 46],
[170, 168, 177, ..., 82, 78, 80]], dtype=uint8)
>>> type(train_data['labels'])
<type 'list'>
>>> train_data['labels'][:10] #Holen Sie sich die ersten 10 Etikettendaten
[6, 9, 9, 4, 1, 1, 2, 7, 8, 3]
>>> from PIL import Image
>>> sample_image = train_data['data'][:100].reshape((10, 10, 3, 32, 32)).transpose((0, 3, 1, 4, 2)).reshape((320, 320, 3)) #Sortieren Sie die ersten 100 Teile in Kacheln
>>> Image.fromarray(sample_image).save('sample.png')
Sie können die folgenden Bilder erhalten
Führen Sie den folgenden Befehl aus, um ein Dataset mit drei Arten der Vorverarbeitung zu generieren.
$ python src/dataset.py
Für "Durchschnittswert des Bildes" haben wir den Durchschnittswert der RGB-Werte der gesamten Trainingsdaten unabhängig vom RGB verwendet. Bei der Kontrastnormalisierung wurde nach Subtraktion des Durchschnittswerts jedes Bildes vom RGB-Wert der Kontrast durch Multiplizieren mit einer Konstanten gleichmäßig gemacht, so dass die Standardabweichung 1 wird. Ich verstehe ZCA Whitening nicht wirklich, aber Toki no Mori Wiki Sagt, dass "Transformation, so dass die Kovarianzmatrix von Daten eine Einheitsmatrix wird" durchgeführt wird. Die spezifische Berechnung des Aufhellens ist ausführlich in Manbou-Tagebuch "CIFAR-10 und ZCA-Aufhellen" beschrieben.
Das Bild nach Kontrastnormalisierung + ZCA-Aufhellung ist wie folgt. Wenn es so bleibt, wie es ist, ist die Verteilung der RGB-Werte eng, so dass es normalisiert wird, so dass sich die Verteilung der RGB-Werte für jedes Bild im Bereich von 0 bis 255 ausbreitet.
Die folgenden Erweiterungen werden bei allen Lernvorgängen durchgeführt. Zum Zeitpunkt des Tests wird keine Erweiterung durchgeführt, und die vorverarbeiteten Testdaten werden unverändert verwendet.
Der Code für den Erweiterungsteil lautet wie folgt.
import numpy as np
(Unterlassung)
def __trans_image(self, x):
size = 32
n = x.shape[0]
images = np.zeros((n, 3, size, size), dtype=np.float32)
offset = np.random.randint(-4, 5, size=(n, 2))
mirror = np.random.randint(2, size=n)
for i in six.moves.range(n):
image = x[i]
top, left = offset[i]
left = max(0, left)
top = max(0, top)
right = min(size, left + size)
bottom = min(size, left + size)
if mirror[i] > 0:
images[i,:,size-bottom:size-top,size-right:size-left] = image[:,top:bottom, left:right][:,:,::-1]
else:
images[i,:,size-bottom:size-top,size-right:size-left] = image[:,top:bottom,left:right]
return images
Lernen wir mit einem relativ flachen Netzwerk wie dem folgenden. Verwenden Sie eine netzwerkähnliche Struktur, die im Tensorflor-Lernprogramm verwendet wird (https://www.tensorflow.org/versions/r0.9/tutorials/deep_cnn/index.html). (Nicht genau gleich, es gibt Unterschiede in der Schichtzusammensetzung und den anfänglichen Parameterwerten) Nach dem Stapeln von 3 Schichten Convolution Neural Network (CNN) + ReLU + MaxPooling gibt es 2 Schichten der vollständig verbundenen Schicht. Nach der vollständig verbundenen Ebene wird ein Dropout bereitgestellt, um das Überlernen zu unterdrücken.
class CNN(chainer.Chain):
def __init__(self):
super(CNN, self).__init__(
conv1=L.Convolution2D(3, 64, 5, stride=1, pad=2),
conv2=L.Convolution2D(64, 64, 5, stride=1, pad=2),
conv3=L.Convolution2D(64, 128, 5, stride=1,
pad=2),
l1=L.Linear(4 * 4 * 128, 1000),
l2=L.Linear(1000, 10),
)
def __call__(self, x, train=True):
h1 = F.max_pooling_2d(F.relu(self.conv1(x)), 3, 2)
h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), 3, 2)
h3 = F.max_pooling_2d(F.relu(self.conv3(h2)), 3, 2)
h4 = F.relu(self.l1(F.dropout(h3, train=train)))
return self.l2(F.dropout(h4, train=train))
Führen Sie das Lernen mit dem folgenden Befehl aus. Der Lauf dauerte ungefähr 40 Minuten.
$ python src/train.py -g 0 -m cnn -b 128 -p cnn --optimizer adam --iter 300 --lr_decay_iter 100
Die Optionen haben folgende Bedeutung
Die Fehlerkurve sieht mit einer Testfehlerrate von 18,94% so aus. Wenn Sie in der Fehlerkurve die Lernrate nach einer Weile des Lernens senken, wird das Lernen wieder schnell voranschreiten. Es ist eine Technik, die verwendet wird, um die Lernrate so zu planen, dass sie nach einer bestimmten Anzahl von Malen auf diese Weise abnimmt.
Dieses Mal werden wir mit einem Datensatz trainieren, der in der Vorverarbeitung einer Kontrastnormalisierung + ZCA-Aufhellung unterzogen wurde. Führen Sie das Lernen mit dem folgenden Befehl aus. Der Lauf dauerte ungefähr 40 Minuten.
$ python src/train.py -g 0 -m cnn -b 128 -p cnn_zca --optimizer adam --iter 300 --lr_decay_iter 100 -d dataset/image_norm_zca.pkl
"-d Dataset / image_norm_zca.pkl" gibt das Dataset an, das einer Kontrastnormalisierung + ZCA-Aufhellung unterzogen wurde.
Die Fehlerkurve sieht mit einer Testfehlerrate von 18,76% so aus. Das Ergebnis ist, dass es zwar besser ist als nur den Durchschnittswert zu subtrahieren, aber nahezu unverändert bleibt.
Die Chargennormalisierung ist eine Methode, um die Ausgabe einer bestimmten Ebene für jeden Mini-Batch auf durchschnittlich 0 und bei Verteilung auf 1 zu normalisieren.
Ziel ist es, das Erlernen der nächsten Ebene durch Normalisieren zu erleichtern.
Der Algorithmus wird ausführlich in [diesem Artikel] beschrieben (http://qiita.com/supersaiakujin/items/8a465ecb1dcbc7df8b02).
Sie können chainer.links.BatchNormalization
verwenden, um die Chargennormalisierung mit Chainer durchzuführen.
Der diesmal verwendete Netzwerkcode wird unten angezeigt. Die Netzwerkkonfiguration ist fast dieselbe wie die zuvor verwendete. Der einzige Unterschied besteht im Vorhandensein oder Fehlen einer Stapelnormalisierung.
class BatchConv2D(chainer.Chain):
def __init__(self, ch_in, ch_out, ksize, stride=1, pad=0, activation=F.relu):
super(BatchConv2D, self).__init__(
conv=L.Convolution2D(ch_in, ch_out, ksize, stride, pad),
bn=L.BatchNormalization(ch_out),
)
self.activation=activation
def __call__(self, x, train):
h = self.bn(self.conv(x), test=not train)
if self.activation is None:
return h
return F.relu(h)
class CNNBN(chainer.Chain):
def __init__(self):
super(CNNBN, self).__init__(
bconv1=BatchConv2D(3, 64, 5, stride=1, pad=2),
bconv2=BatchConv2D(64, 64, 5, stride=1, pad=2),
bconv3=BatchConv2D(64, 128, 5, stride=1, pad=2),
l1=L.Linear(4 * 4 * 128, 1000),
l2=L.Linear(1000, 10),
)
def __call__(self, x, train=True):
h1 = F.max_pooling_2d(self.bconv1(x, train), 3, 2)
h2 = F.max_pooling_2d(self.bconv2(h1, train), 3, 2)
h3 = F.max_pooling_2d(self.bconv3(h2, train), 3, 2)
h4 = F.relu(self.l1(F.dropout(h3, train=train)))
return self.l2(F.dropout(h4, train=train))
Geben Sie den folgenden Befehl ein, um die Stapelnormalisierung zu erlernen: Der Lauf dauerte ungefähr 50 Minuten.
$ python src/train.py -g 0 -m cnnbn -b 128 -p cnnbn --optimizer adam --iter 300 --lr_decay_iter 100
Verwenden Sie das Modell mit Batch-Normalisierung mit "-m cnnbn".
Die Fehlerkurve sieht mit einer Fehlerrate von 12,40% so aus. Sie können sehen, dass die Fehlerrate erheblich niedriger ist als ohne Chargennormalisierung.
Ich habe auch versucht, die Trainingsdaten von Contrast Normalization + ZCA Whitening zu verwenden. Der Befehl lautet wie folgt.
$ python src/train.py -g 0 -m cnnbn -b 128 -p cnnbn --optimizer adam --iter 300 --lr_decay_iter 100 -d dataset/image_norm_zca.pkl
Die Fehlerrate betrug 12,27%, was etwas besser war als nur den Durchschnitt abzuziehen.
Verwenden Sie ein Modell ähnlich der Ebene VGG 16 oder VGG 19. Das VGG-Modell verfügt über eine vollständig verbundene Schicht, nachdem mehrere CNNs mit Kernelgröße 3 + Max Pooling mehrmals wiederholt wurden. Ich habe an Tag "Die Geschichte von Kaggle CIFAR-10" ein VGG-basiertes Netzwerk verwendet, also habe ich ein ähnliches Netzwerk verwendet. Lernen. Dieser Blog hat eine hohe Punktzahl von 94,15% Erkennungsrate von Testdaten erreicht.
Die Unterschiede zu "Die Geschichte von Kaggle CIFAR-10" sind wie folgt.
Diese Implementierung | Kaggle CIFAR-10 Geschichten | |
---|---|---|
Eingabedaten | 32 x 32px | 24 x 24 px |
Augmentation(Während des Lernens) | Parallelbewegung, Links-Rechts-Umkehrung | Parallelbewegung, Links-Rechts-Umkehrung、拡大 |
Augmentation(Zum Zeitpunkt des Tests) | Keiner | Parallelbewegung, Links-Rechts-Umkehrung, Vergrößerung |
Anzahl der Modelle | 1 Stück | 6(Verwenden Sie die durchschnittliche Leistung jedes Modells) |
Batch Normaliztion | Ja | Keiner |
Es ist wie folgt, wenn es mit Chainer beschrieben wird.
class VGG(chainer.Chain):
def __init__(self):
super(VGG, self).__init__(
bconv1_1=BatchConv2D(3, 64, 3, stride=1, pad=1),
bconv1_2=BatchConv2D(64, 64, 3, stride=1, pad=1),
bconv2_1=BatchConv2D(64, 128, 3, stride=1, pad=1),
bconv2_2=BatchConv2D(128, 128, 3, stride=1, pad=1),
bconv3_1=BatchConv2D(128, 256, 3, stride=1, pad=1),
bconv3_2=BatchConv2D(256, 256, 3, stride=1, pad=1),
bconv3_3=BatchConv2D(256, 256, 3, stride=1, pad=1),
bconv3_4=BatchConv2D(256, 256, 3, stride=1, pad=1),
fc4=L.Linear(4 * 4 * 256, 1024),
fc5=L.Linear(1024, 1024),
fc6=L.Linear(1024, 10),
)
def __call__(self, x, train=True):
h = self.bconv1_1(x, train)
h = self.bconv1_2(h, train)
h = F.dropout(F.max_pooling_2d(h, 2), 0.25, train=train)
h = self.bconv2_1(h, train)
h = self.bconv2_2(h, train)
h = F.dropout(F.max_pooling_2d(h, 2), 0.25, train=train)
h = self.bconv3_1(h, train)
h = self.bconv3_2(h, train)
h = self.bconv3_3(h, train)
h = self.bconv3_4(h, train)
h = F.dropout(F.max_pooling_2d(h, 2), 0.25, train=train)
h = F.relu(self.fc4(F.dropout(h, train=train)))
h = F.relu(self.fc5(F.dropout(h, train=train)))
h = self.fc6(h)
return h
Führen Sie es mit dem folgenden Befehl aus. Ein VGG-ähnliches Modell wird mit "-m vgg" angegeben. Der Lauf dauerte ungefähr fünfeinhalb Stunden.
$ python src/train.py -g 0 -m vgg -b 128 -p vgg_adam --optimizer adam --iter 300 --lr_decay_iter 100
Die Fehlerrate beträgt 7,65%, wodurch die Erkennungsgenauigkeit verbessert wird.
Residual Network ist ein Netzwerk, das eine einheitliche Transformation und mehrere CNN-Schichten kombiniert, sodass das Lernen auch dann gut durchgeführt werden kann, wenn die Hierarchie vertieft wird. Es wird ausführlich in [diesem Artikel] beschrieben (http://qiita.com/supersaiakujin/items/935bbc9610d0f87607e8).
Das diesmal implementierte Netzwerk hat insgesamt 74 Schichten und die folgende Konfiguration. In Bezug auf das Zählen der Schichten wird ein Restblock als 2 Schichten gezählt, da der Restblock zwei Schichten CNN verwendet.
Ich wollte es unbedingt auf 110 Ebenen machen, konnte es aber aufgrund fehlenden GPU-Speichers nicht ausführen. Wir haben bestätigt, dass das Eingabebild, wenn es auf 24 x 24 Pixel verkleinert wird, auf 110 Ebenen ausgeführt werden kann.
Die Netzwerkimplementierung in Chainer sieht folgendermaßen aus:
class ResidualBlock(chainer.Chain):
def __init__(self, ch_in, ch_out, stride=1, swapout=False, skip_ratio=0, activation1=F.relu, activation2=F.relu):
w = math.sqrt(2)
super(ResidualBlock, self).__init__(
conv1=L.Convolution2D(ch_in, ch_out, 3, stride, 1, w),
bn1=L.BatchNormalization(ch_out),
conv2=L.Convolution2D(ch_out, ch_out, 3, 1, 1, w),
bn2=L.BatchNormalization(ch_out),
)
self.activation1 = activation1
self.activation2 = activation2
self.skip_ratio = skip_ratio
self.swapout = swapout
def __call__(self, x, train):
skip = False
if train and self.skip_ratio > 0 and np.random.rand() < self.skip_ratio:
skip = True
sh, sw = self.conv1.stride
c_out, c_in, kh, kw = self.conv1.W.data.shape
b, c, hh, ww = x.data.shape
if sh == 1 and sw == 1:
shape_out = (b, c_out, hh, ww)
else:
hh = (hh + 2 - kh) // sh + 1
ww = (ww + 2 - kw) // sw + 1
shape_out = (b, c_out, hh, ww)
h = x
if x.data.shape != shape_out:
xp = chainer.cuda.get_array_module(x.data)
n, c, hh, ww = x.data.shape
pad_c = shape_out[1] - c
p = xp.zeros((n, pad_c, hh, ww), dtype=xp.float32)
p = chainer.Variable(p, volatile=not train)
x = F.concat((p, x))
if x.data.shape[2:] != shape_out[2:]:
x = F.average_pooling_2d(x, 1, 2)
if skip:
return x
h = self.bn1(self.conv1(h), test=not train)
if self.activation1 is not None:
h = self.activation1(h)
h = self.bn2(self.conv2(h), test=not train)
if not train:
h = h * (1 - self.skip_ratio)
if self.swapout:
h = F.dropout(h, train=train) + F.dropout(x, train=train)
else:
h = h + x
if self.activation2 is not None:
return self.activation2(h)
else:
return h
class ResidualNet(chainer.Chain):
def __init__(self, depth=18, swapout=False, skip=True):
super(ResidualNet, self).__init__()
links = [('bconv1', BatchConv2D(3, 16, 3, 1, 1), True)]
skip_size = depth * 3 - 3
for i in six.moves.range(depth):
if skip:
skip_ratio = float(i) / skip_size * 0.5
else:
skip_ratio = 0
links.append(('res{}'.format(len(links)), ResidualBlock(16, 16, swapout=swapout, skip_ratio=skip_ratio, ), True))
links.append(('res{}'.format(len(links)), ResidualBlock(16, 32, stride=2, swapout=swapout), True))
for i in six.moves.range(depth - 1):
if skip:
skip_ratio = float(i + depth) / skip_size * 0.5
else:
skip_ratio = 0
links.append(('res{}'.format(len(links)), ResidualBlock(32, 32, swapout=swapout, skip_ratio=skip_ratio), True))
links.append(('res{}'.format(len(links)), ResidualBlock(32, 64, stride=2, swapout=swapout), True))
for i in six.moves.range(depth - 1):
if skip:
skip_ratio = float(i + depth * 2 - 1) / skip_size * 0.5
else:
skip_ratio = 0
links.append(('res{}'.format(len(links)), ResidualBlock(64, 64, swapout=swapout, skip_ratio=skip_ratio), True))
links.append(('_apool{}'.format(len(links)), F.AveragePooling2D(8, 1, 0, False, True), False))
links.append(('fc{}'.format(len(links)), L.Linear(64, 10), False))
for name, f, _with_train in links:
if not name.startswith('_'):
self.add_link(*(name, f))
self.layers = links
def __call__(self, x, train=True):
h = x
for name, f, with_train in self.layers:
if with_train:
h = f(h, train=train)
else:
h = f(h)
return h
Es gibt einen Parameter namens Swapout, aber es handelt sich um eine experimentelle Implementierung. Ignorieren Sie ihn daher vorerst. Wenn im Restblock die Eingangs- und Ausgangsgrößen unterschiedlich sind (die Breite und Höhe am Ausgang sind kleiner und die Anzahl der Kanäle ist gleich oder der Ausgang ist größer), ist der Teil der Gleichheitskonvertierung wie folgt.
Führen Sie es mit dem folgenden Befehl aus.
python src/train.py -g 0 -m residual -b 128 -p residual --res_depth 12 --optimizer sgd --lr 0.1 --iter 300 --lr_decay_iter 100
-m Residuum
ist die Restnetzwerkspezifikation.
--optimizer sgd
ist eine Spezifikation, die Momentum SGD verwendet und bessere Ergebnisse liefert als Adam.
Der Anfangswert der Lernrate scheint 0,1 zu sein.
Bei der Implementierung der CIFAR-10-Bildklassifizierung unter Verwendung des unten aufgeführten Restnetzwerks betrug die anfängliche Lernrate 0,1.
Der Lauf dauerte ungefähr 10 Stunden. Die Testfehlerrate betrug 8,06%, was schlechter war als beim VGG-ähnlichen Modell.
Die stochastische Tiefe ist eine Methode zum wahrscheinlichen Überspringen des Restblocks während des Lernens. Die Erklärung finden Sie in diesem Artikel.
Der Netzwerkcode wird unter Verwenden des Restnetzwerks (unter Verwendung von # Restnetzwerk) veröffentlicht.
Geben Sie "ReisualBlock" eine Eigenschaft namens "skip_ratio" und verhindern Sie, dass der CNN-Teil des Restblocks während des Lernens mit der in "skip_ratio" angegebenen Wahrscheinlichkeit ausgeführt wird.
Verwenden Sie beim Testen den Wert, der durch Multiplizieren des CNN-Teils mit "(1 --skip_ratio)" erhalten wird.
Das "skip_ratio" ist geneigt, so dass das "skip_ratio" umso größer ist, je tiefer der Restblock ist.
Dieses Mal ist skip_ratio
0 für den ersten Restblock, 0,5 für das tiefste skip_ratio
und die dazwischen liegenden Blöcke werden linear geändert.
Führen Sie es mit dem folgenden Befehl aus.
python src/train.py -g 0 -m residual -b 128 -p residual_skip --skip_depth --res_depth 12 --optimizer sgd --lr 0.1 --iter 300 --lr_decay_iter 100
--skip_depth
ist eine Option zur Verwendung von Stochastic Depth.
Das Lernen dauerte ungefähr 9 Stunden. Die Testfehlerrate beträgt jetzt 7,42%, was die Genauigkeit verbessert.
Wir haben den CIFAR-10-Bilddatensatz anhand verschiedener Modelle klassifiziert. Wir konnten den Unterschied in der Erkennungsrate aufgrund des Unterschieds in der Vorverarbeitung, des Vorhandenseins oder Nichtvorhandenseins einer Chargennormalisierung und des Unterschieds in den Modellen bestätigen.
Dieses Mal habe ich es mit Chainer implementiert, aber zum Beispiel ist Implementierung der stochastischen Tiefe durch Torch7 schneller und spart Speicher als die diesmal verwendete Implementierung und dieselbe Umgebung (https://github.com/yueatsprograms/Stochastic_Depth) Ich konnte 56layer Residual Network unter Ubuntu trainieren. Wenn Sie ein tieferes Modell ausführen möchten, können Sie ein anderes Framework in Betracht ziehen.
Recommended Posts