Das Thema von Kapitel 7 ist Faltungsneurales Netzwerk: ** CNN **
CNN ist das gleiche wie das neuronale Netzwerk, das wir bisher gesehen haben, und es ist möglich, eine Kombination von Schichten wie einen Legoblock zu erstellen. Weiter ist neu ・ "Faltungsschicht" ・ "Pooling Layer"
Allgemeine CNN-Funktionen ・ Fluss von "Convolution --ReLU- (Pooling)" ・ Die Pooling-Ebene kann weggelassen werden ・ Die Kombination "Affine --ReLU" wird in der Ebene nahe der Ausgabe verwendet. ・ Die letzte Ausgabeebene ist eine Kombination aus "Affine --Softmax"
Die folgenden Begriffe wurden angezeigt ・ Putten ·schreiten
Zusätzlich sind dreidimensionale Daten erschienen
Das Problem bei vollständigen Verknüpfungen ist, dass ** die Struktur der Daten "ignoriert" wird **
Beispielsweise handelt es sich bei einem Bild normalerweise um eine dreidimensionale Form in vertikaler, horizontaler und Kanalrichtung. Diese Form enthält wichtige räumliche Informationen Zum Beispiel -Spatial Close Pixel können ähnliche Werte haben ・ Zwischen jedem RBG-Kanal besteht eine enge Beziehung ・ Pixel, die weit voneinander entfernt sind, sind nicht so verwandt Es gibt ein wesentliches Muster, das in der dreidimensionalen Form aufgenommen werden muss.
Die vollständig verbundene Schicht ignoriert die obige Form und behandelt sie alle als äquivalente Neuronen (Neuronen derselben Dimension). Andererseits behält die Faltungsschicht ihre Form bei
In CNN sind die Eingabe- / Ausgabedaten der Faltungsschicht ** Merkmalskarte ** (Merkmalskarte). Eingabedaten ** Feature-Map eingeben ** (Feature-Map) Ausgabedaten ** Feature-Map ausgeben ** (Feature-Map) Manchmal sage ich.
"Klappvorgang" Entspricht "Filterverarbeitung" in der Bildverarbeitung In einigen Literaturstellen wird der Begriff "Filter" manchmal als "Kernel" bezeichnet.
Die für diesen Filter verwendeten Parameter entsprechen den "Gewichten" im vollständig gekoppelten neuronalen Netzwerk.
Berechnungsbeispiel
Voreingenommener Betrieb
Putting: Füllen fester Daten (zB 0) um die Eingabedaten
Die folgende Abbildung ist mit 1 Pixel 0 in der Breite gefüllt
Schritt: Der Abstand zwischen den Positionen, an denen der Filter angewendet wird.
Eingabegröße (H, B) Filtergröße (FH, FW) Ausgabegröße (OH, OW) Polsterung P. Schritt S. Die Ausgabegröße ist wie folgt
OH = \frac{H + 2P - FH}{S} + 1\\
OW = \frac{W + 2P - FW}{S} + 1
In Anbetracht der dreidimensionalen Faltungsoperation in einem leicht verständlichen rechteckigen Block ist dies wie folgt.
Das Obige ist eine Feature-Map mit einer Ausgabe. Mit anderen Worten, es handelt sich um eine Feature-Map mit einem Kanal.
Das Folgende ist ein Diagramm bei Mehrkanalmethoden.
Das Hinzufügen des Bias-Terms ist wie folgt.
Bei der Stapelverarbeitung von N Datenelementen ist die Form der Daten dieselbe
Pooling: Berechnung zur Reduzierung der vertikalen und horizontalen Sicht auf den Himmel
In der folgenden Abbildung wird die Speicherplatzgröße reduziert, indem eine Verarbeitung durchgeführt wird, bei der der 2x2-Bereich zu einem Element zusammengefasst wird.
In diesem Beispiel wird auf Pool 2 2x2 Max-Pooling ausgeführt.
Max Pooling: Berechnung, um den Maximalwert in der Fläche zu ermitteln Außerdem werden im Allgemeinen die Größe des Poolfensters und die Folie auf denselben Wert festgelegt.
Zusätzlich zum maximalen Pooling gibt es ein durchschnittliches Pooling, das den Durchschnittswert in der Region annimmt.
・ Es sind keine Parameter zu lernen
Pooling ist ein Prozess, bei dem nur der Maximalwert (oder Durchschnittswert) vom Ziel übernommen wird, sodass keine Parameter zu lernen sind.
・ Die Anzahl der Kanäle ändert sich nicht
Die Anzahl der Kanäle für Eingangsdaten und Ausgangsdaten ändert sich aufgrund der Pooling-Berechnung nicht. (OH und OW ändern sich, FN jedoch nicht)
・ Robust gegen winzige Positionsänderungen
Das Pooling liefert ähnliche Ergebnisse für kleine Abweichungen in den Eingabedaten. Daher ist es robust gegen geringfügige Abweichungen in den Eingabedaten.
#Generieren Sie zufällig Daten
x = np.random.rand(10,1,28,28)
x.shape
# (10, 1, 28, 28)
x[0].shape
# (1, 28, 28)
x[1].shape
# (1, 28, 28)
x[0, 0].shape # x[0][0]Aber ok
# (28, 28)
Wenn Sie die Faltung wie in der vorherigen Abbildung gezeigt implementieren, müssen Sie mehrere for-Anweisungen kombinieren. Außerdem ist NumPy bei Verwendung der for-Anweisung langsam.
Implementieren Sie daher eine Funktion namens im2col anstelle einer for-Anweisung. im2col ist eine Funktion, die die Eingabedaten entsprechend dem Filter erweitert.
In dieser Abbildung haben wir die Verständlichkeit hervorgehoben und ein Beispiel angegeben, bei dem sich die Filterbereiche nicht überlappen.
・ Im2col Vorteile Nachteile Vorteil: Es ist möglich, die Bibliothek der linearen Algebra effektiv zu nutzen, da sie auf die Matrixberechnung reduziert werden kann. Nachteile: Verbrauchen Sie mehr Speicher als üblich
#----------------------------------------------------
# Parameters
# input_data : (Die Anzahl der Daten,Kanal,Höhe,Breite)Eingabedaten bestehend aus einem 4-dimensionalen Array von
# filter_h :Filterhöhe
# filter_w :Filterbreite
# stride :schreiten
# pad :Polsterung
# Returns
# col :Ein zweidimensionales Array
#----------------------------------------------------
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
N, C, H, W = input_data.shape
out_h = (H + 2*pad - filter_h)//stride + 1
out_w = (W + 2*pad - filter_w)//stride + 1
img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))
for y in range(filter_h):
y_max = y + stride*out_h
for x in range(filter_w):
x_max = x + stride*out_w
col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
return col
Ansicht mit im2col
import sys, os
sys.path.append(os.pardir)
from common.util import im2col
x1 = np.random.rand(1, 3, 7, 7)
col1 = im2col(x1, 5, 5, stride=1, pad=0)
print(col1.shape)
x2 = np.random.rand(10, 3, 7, 7)
col2 = im2col(x2, 5, 5, stride=1, pad=0)
print(col2.shape)
Ergebnis (9, 75) (90, 75)
x1 sind 7x7-Daten mit Kanälen der Stapelgröße 1 und 3 x2 sind 7x7-Daten mit einer Stapelgröße von 10 und 3 Kanälen
In beiden Fällen beträgt die Anzahl der Elemente in der zweiten Dimension 75, was die Summe der Anzahl der Aspekte des Filters ist. (Kanal 3, Größe 5 x 5)
Nachdem Sie die Daten mit im2col erweitert haben, müssen Sie lediglich den Filter (das Gewicht) der Faltungsschicht in eine Spalte erweitern und das innere Produkt der beiden Matrizen berechnen. Dies ist fast das gleiche wie in der affinen Schicht der vollständig verbundenen Schicht.
class Convolution:
def __init__(self, W, b, stride=1, pad=0):
self.W = W
self.b = b
self.stride = stride
self.pad = pad
#Zwischendaten (werden rückwärts verwendet)
self.x = None
self.col = None
self.col_W = None
#Gradient des Gewichts / Bias-Parameters
self.dW = None
self.db = None
def forward(self, x):
FN, C, FH, FW = self.W.shape
N, C, H, W = x.shape
out_h = 1 + int((H + 2*self.pad - FH) / self.stride)
out_w = 1 + int((W + 2*self.pad - FW) / self.stride)
col = im2col(x, FH, FW, self.stride, self.pad)
#zur Umformfunktion-Wenn 1 angegeben ist, wird die Anzahl der Elemente zusammengefasst, sodass das Tsuji des mehrdimensionalen Arrays übereinstimmt.
col_W = self.W.reshape(FN, -1).T
out = np.dot(col, col_W) + self.b
#Zum Schluss formen Sie die Ausgabegröße in die entsprechende Form
#Durch Umformen wird die angegebene Ausgabegröße neu konfiguriert
#transponieren ändert die Reihenfolge der Achsen
out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
self.x = x
self.col = col
self.col_W = col_W
return out
def backward(self, dout):
FN, C, FH, FW = self.W.shape
dout = dout.transpose(0,2,3,1).reshape(-1, FN)
#Die Berechnung der inversen Matrix selbst erfolgt in den folgenden zwei Zeilen, die mit Affine identisch sind. Der einzige Unterschied besteht in der Anpassung der Abmessungen der Matrix.
self.db = np.sum(dout, axis=0)
self.dW = np.dot(self.col.T, dout)
self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)
dcol = np.dot(dout, self.col_W.T)
#Umgekehrte Verarbeitung von im2col
dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)
return dx
Verwenden Sie wie bei der Convolution-Ebene im2col, um die Eingabedaten zu erweitern und zu implementieren Beim Pooling besteht der Unterschied jedoch darin, dass sie in Kanalrichtung unabhängig sind.
class Pooling:
def __init__(self, pool_h, pool_w, stride=1, pad=0):
self.pool_h = pool_h
self.pool_w = pool_w
self.stride = stride
self.pad = pad
self.x = None
self.arg_max = None
def forward(self, x):
N, C, H, W = x.shape
out_h = int(1 + (H - self.pool_h) / self.stride)
out_w = int(1 + (W - self.pool_w) / self.stride)
col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
col = col.reshape(-1, self.pool_h*self.pool_w)
arg_max = np.argmax(col, axis=1)
out = np.max(col, axis=1)
out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)
self.x = x
self.arg_max = arg_max
return out
def backward(self, dout):
dout = dout.transpose(0, 2, 3, 1)
pool_size = self.pool_h * self.pool_w
dmax = np.zeros((dout.size, pool_size))
#Durch Abflachen wird die Struktur wieder in ein eindimensionales Array eingefügt
dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
dmax = dmax.reshape(dout.shape + (pool_size,))
dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)
return dx
# coding: utf-8
import sys, os
sys.path.append(os.pardir) #Einstellungen zum Importieren von Dateien in das übergeordnete Verzeichnis
import pickle
import numpy as np
from collections import OrderedDict
from common.layers import *
from common.gradient import numerical_gradient
#Einfaches ConvNet
# conv - relu - pool - affine - relu - affine - softmax
class SimpleConvNet:
#----------------------------------------------------
# Parameters
# input_size :Eingabegröße (784 für MNIST)
# hidden_size_list :Liste der Anzahl der Neuronen in der verborgenen Schicht (e.g. [100, 100, 100])
# output_size :Ausgabegröße (10 für MNIST)
# activation : 'relu' or 'sigmoid'
# weight_init_std :Geben Sie die Standardabweichung des Gewichts an (e.g. 0.01)
# 'relu'Oder'he'Wenn angegeben, wird "Anfangswert von He" gesetzt.
# 'sigmoid'Oder'xavier'Wenn angegeben, wird "Anfangswert von Xavier" gesetzt.
#----------------------------------------------------
def __init__(self, input_dim=(1, 28, 28),
conv_param={'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1},
hidden_size=100, output_size=10, weight_init_std=0.01):
#Initialisierung von Gewichten, Berechnung der Ausgabegröße der Faltungsschicht
filter_num = conv_param['filter_num']
filter_size = conv_param['filter_size']
filter_pad = conv_param['pad']
filter_stride = conv_param['stride']
input_size = input_dim[1]
conv_output_size = (input_size - filter_size + 2*filter_pad) / filter_stride + 1
pool_output_size = int(filter_num * (conv_output_size/2) * (conv_output_size/2))
#Gewichtsinitialisierung
self.params = {}
self.params['W1'] = weight_init_std * \
np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
self.params['b1'] = np.zeros(filter_num)
self.params['W2'] = weight_init_std * \
np.random.randn(pool_output_size, hidden_size)
self.params['b2'] = np.zeros(hidden_size)
self.params['W3'] = weight_init_std * \
np.random.randn(hidden_size, output_size)
self.params['b3'] = np.zeros(output_size)
#Schichterzeugung
self.layers = OrderedDict()
self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
conv_param['stride'], conv_param['pad'])
self.layers['Relu1'] = Relu()
self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
self.layers['Relu2'] = Relu()
self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])
self.last_layer = SoftmaxWithLoss()
#Machen Sie einen Abzug
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
#Finden Sie die Verlustfunktion
def loss(self, x, t):
"""Finden Sie die Verlustfunktion
Argument x sind Eingabedaten, t ist Lehrerbezeichnung
"""
y = self.predict(x)
return self.last_layer.forward(y, t)
def accuracy(self, x, t, batch_size=100):
if t.ndim != 1 : t = np.argmax(t, axis=1)
acc = 0.0
for i in range(int(x.shape[0] / batch_size)):
tx = x[i*batch_size:(i+1)*batch_size]
tt = t[i*batch_size:(i+1)*batch_size]
y = self.predict(tx)
y = np.argmax(y, axis=1)
acc += np.sum(y == tt)
return acc / x.shape[0]
def numerical_gradient(self, x, t):
"""Finden Sie den Gradienten (numerische Differenzierung)
Parameters
----------
x :Eingabedaten
t :Lehreretikett
Returns
-------
Wörterbuchvariable mit Farbverlauf jeder Ebene
grads['W1']、grads['W2']、...Ist das Gewicht jeder Schicht
grads['b1']、grads['b2']、...Ist die Vorspannung jeder Schicht
"""
loss_w = lambda w: self.loss(x, t)
grads = {}
for idx in (1, 2, 3):
grads['W' + str(idx)] = numerical_gradient(loss_w, self.params['W' + str(idx)])
grads['b' + str(idx)] = numerical_gradient(loss_w, self.params['b' + str(idx)])
return grads
def gradient(self, x, t):
"""Finden Sie den Gradienten (Fehlerrückausbreitungsmethode)
Parameters
----------
x :Eingabedaten
t :Lehreretikett
Returns
-------
Wörterbuchvariable mit Farbverlauf jeder Ebene
grads['W1']、grads['W2']、...Ist das Gewicht jeder Schicht
grads['b1']、grads['b2']、...Ist die Vorspannung jeder Schicht
"""
# forward
self.loss(x, t)
# backward
dout = 1
dout = self.last_layer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
#Aufbau
grads = {}
grads['W1'], grads['b1'] = self.layers['Conv1'].dW, self.layers['Conv1'].db
grads['W2'], grads['b2'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
grads['W3'], grads['b3'] = self.layers['Affine2'].dW, self.layers['Affine2'].db
return grads
def save_params(self, file_name="params.pkl"):
params = {}
for key, val in self.params.items():
params[key] = val
with open(file_name, 'wb') as f:
pickle.dump(params, f)
def load_params(self, file_name="params.pkl"):
with open(file_name, 'rb') as f:
params = pickle.load(f)
for key, val in params.items():
self.params[key] = val
for i, key in enumerate(['Conv1', 'Affine1', 'Affine2']):
self.layers[key].W = self.params['W' + str(i+1)]
self.layers[key].b = self.params['b' + str(i+1)]
Der Punkt ist, dass es einfach implementiert werden kann, indem die Anzahl der Ebenen und der Wert des in der verborgenen Ebene verwendeten Hyperparameters erhöht werden.
Führen Sie das Lernen durch Außerdem hatte mein Macbook Air eine hohe CPU-Auslastung, sodass ich die Datenreduktion auskommentierte und ausführte.
# coding: utf-8
import sys, os
sys.path.append(os.pardir) #Einstellungen zum Importieren von Dateien in das übergeordnete Verzeichnis
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from simple_convnet import SimpleConvNet
from common.trainer import Trainer
#Daten lesen
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)
#Daten reduzieren, wenn die Verarbeitung Zeit braucht
#x_train, t_train = x_train[:5000], t_train[:5000]
#x_test, t_test = x_test[:1000], t_test[:1000]
max_epochs = 20
network = SimpleConvNet(input_dim=(1,28,28),
conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
hidden_size=100, output_size=10, weight_init_std=0.01)
trainer = Trainer(network, x_train, t_train, x_test, t_test,
epochs=max_epochs, mini_batch_size=100,
optimizer='Adam', optimizer_param={'lr': 0.001},
evaluate_sample_num_per_epoch=1000)
trainer.train()
#Parameter speichern
network.save_params("params.pkl")
print("Saved Network Parameters!")
#Zeichnen eines Diagramms
markers = {'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, trainer.train_acc_list, marker='o', label='train', markevery=2)
plt.plot(x, trainer.test_acc_list, marker='s', label='test', markevery=2)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
…… train loss:0.0145554384445 train loss:0.0275851756417 train loss:0.00785021651885 train loss:0.00986611950473 =============== Final Test Accuracy =============== test acc:0.956 Saved Network Parameters!
Vor dem Lernen: Es gibt keine Regelmäßigkeit in Schwarz- und Weißtönen, da der Filter zufällig initialisiert wird.
Nach dem Lernen: regelmäßig
Was ist ein Filter mit einer solchen Regelmäßigkeit "betrachten"? ・ ** Kante **: Rand, an dem sich die Farbe ändert ・ ** Blob **: Lokal klumpiger Bereich
Faltung der ersten Schicht: Extrahiert Informationen auf niedriger Ebene wie Kanten und Blobs Mehrere Faltungsebenen stapeln: Komplexere und abstraktere Informationen extrahieren
Die folgende DEMO 1 wurde mit http://vision03.csail.mit.edu/cnn_art/index.html#v_single zitiert
In der Demo war es wie folgt. Cov1: Kante, Blob (Kante + Blob) Cov3: Textur Cov5: Objektteile Fc8: Objektklassen wie Hunde und Katzen
Wenn die Schicht tiefer wird, ändern sich die Neuronen von einfachen Formen zu "erweiterten" Informationen **. Mit anderen Worten, es ist der Tag, an dem sich das Objekt, das reagiert, ändert, damit Sie die "Bedeutung" der Dinge verstehen können.
Dieses Buch erklärt Folgendes ・ CNN, das erstmals 1998 vorgeschlagen wurde, ist auch das ursprüngliche LeNet ・ AlexNet im Jahr 2012, als tiefes Lernen Aufmerksamkeit erregte
LeNet
Die folgenden Punkte unterscheiden sich im Vergleich zu "Current CNN" ・ Verwenden Sie die Sigmoid-Funktion für die Aktivierungsfunktion (Derzeit ReLU-Funktion) ・ Die Größe der Zwischendaten wird durch Unterabtastung reduziert. (Derzeit Max Pooling) http://dx.doi.org/10.1109/5.726791
AlexNet
AlexNet stapelt die Faltungsschicht und die Poolschicht und gibt das Ergebnis schließlich über die vollständig verbundene Schicht aus. Die folgenden Punkte unterscheiden sich von LeNet
Es gibt keinen großen Unterschied zwischen LeNet und AlexNe in der Netzwerkkonfiguration, aber es wurden große Fortschritte in der Computertechnologie erzielt. Speziell ・ Eine große Datenmenge steht jetzt jedem zur Verfügung ・ GPUs, die sich auf große Mengen paralleler Berechnungen spezialisiert haben, sind weit verbreitet und ermöglichen es, große Mengen von Operationen mit hoher Geschwindigkeit auszuführen.
Recommended Posts