Während ich "Deep Learning from Grund" (gelesen von Yasuki Saito, veröffentlicht von O'Reilly Japan) lese, werde ich die Websites notieren, auf die ich mich bezog. Teil 10 ←
Wenn es um Faltungsnetzwerke in Kapitel 7 geht, sieht es ganz anders aus als in Kapitel 6. Es sieht so aus, als würden Sie viele verschiedene Dinge tun, aber am Ende werden Sie die Gewichts- und Vorspannungsverläufe finden und speichern. Mit anderen Worten, das Grundprinzip hat sich überhaupt nicht geändert, es sind die Eingabedaten, die sich geändert haben
P207 Wenn die Eingabedaten ein Bild sind, hat das Bild normalerweise eine dreidimensionale Form in vertikaler, horizontaler und Kanalrichtung. Beim Eintritt in die vollständig verbundene Schicht müssen die dreidimensionalen Daten jedoch flache eindimensionale Daten sein. In früheren Beispielen unter Verwendung des MNIST-Datensatzes hatte das Eingabebild die Form (1, 28, 28) - 1 Kanal, 28 Pixel hoch, 28 Pixel breit - aber in einer Reihe angeordnet. Sie haben 784 Daten in die erste affine Ebene eingegeben. ・ ・ ・ Die Faltungsschicht hingegen behält ihre Form. Im Fall eines Bildes werden die Eingabedaten als 3D-Daten empfangen und die Daten werden auch als 3D-Daten an die nächste Ebene ausgegeben. Daher kann CNN Daten mit Formen wie Bildern (möglicherweise) korrekt verstehen.
Tatsächlich habe ich selbst dieses Selbststudien-Memo Nr. 6-2 verwendet, um Kaggles Katzen- und Hundedatensätze um 1 zu verarbeiten Ich konvertiere es in eine Dimension und benutze es. Wenn dies in drei Dimensionen verarbeitet werden kann, kann sich die Erkennungsrate verbessern.
Diese Erklärungen sind überhaupt nicht schwierig, und ich kann sie als solche verstehen, aber da diese Formel plötzlich auf P212 erscheint, was ist das? Ist das wirklich der Fall? Also habe ich darüber nachgedacht.
Denken wir vorerst darüber nach, dass es kein S (Schritt) gibt. Lassen Sie uns einige Eingabegrößen und Filtergrößen überprüfen
Wenn die Eingangsgröße (n, n) und die Filtergröße (m, m) Die Ausgabegröße scheint zu sein (n-m + 1, n-m + 1). Wenn Sie den Filter auf die obere linke Ecke anwenden, können Sie ihn nach rechts (nm) drehen. Es kann sich nach unten drehen (nm). Wenn Sie also 1 Minute in der oberen linken Ecke hinzufügen, ist es nm + 1?
Was passiert also mit Schritten? Wenn der Schritt 2 ist, wird die Anzahl der Umdrehungen nach rechts (nm) halbiert. (Nm) / 2 Wenn es 3 ist, wird es 1/3.
Mit anderen Worten, die Häufigkeit, mit der Sie sich bewegen können, beträgt (nm) / s Die Ausgabegröße beträgt (nm) / s + 1.
Angenommen, die Eingangsdatengröße ist (H, W), die Auffüllung ist P und die Filtergröße ist (FH, FW) n = H + 2 × P In ähnlicher Weise ist n = W + 2 × P. m=FH n=FW Damit Die Ausgabegröße ist OH=(H+2×P-FH)/s + 1 OW=(W+2×P-FW)/s + 1
Ab P230 gibt es eine Beschreibung der Klasse SimpleConvNet als Beispiel für das Training von MNIST-Daten. Lassen Sie mich mit dieser Klasse lernen
import sys, os
sys.path.append(os.pardir) #Einstellungen zum Importieren von Dateien in das übergeordnete Verzeichnis
import numpy as np
from dataset.mnist import load_mnist
from common.simple_convnet import SimpleConvNet
from common.trainer import Trainer
#Daten lesen
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)
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, verbose=False)
trainer.train()
Ich habe versucht, den Urteilsinhalt der Testdaten zu überprüfen.
import numpy as np
from common.simple_convnet import SimpleConvNet
from dataset.mnist import load_mnist
import pickle
import matplotlib.pyplot as plt
def showImg(x):
example = x.reshape((28, 28))
plt.figure()
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(example, cmap=plt.cm.binary)
plt.show()
return
#Mit Testdaten auswerten
x = x_test
t = t_test
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)
network.load_params("params.pkl")
y = network.predict(x)
accuracy_cnt = 0
for i in range(len(x)):
p= np.argmax(y[i])
#print(str(x[i]) + " : " + str(p))
if p == t[i]:
accuracy_cnt += 1
else:
print("Richtige Antwort:"+str(t[i])+"Inferenzergebnis:"+str(p))
showImg(x[i])
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
Infolgedessen ist die richtige Antwortrate
Accuracy:0.988
Was falsch war, ist so
Die Verarbeitung von 60.000 Daten dauerte jedoch Stunden. Als ich nach dem Lernen versuchte, die Testdaten zu verarbeiten, war ich außerdem von dem Problem des unzureichenden Speichers betroffen und konnte nicht einfach fortfahren. Ist Deep Learning in Memory 4G nicht zumutbar?
Vorerst konnte ich bestätigen, dass ich mit CNN mit hoher Genauigkeit lernen konnte.
damit
Wie immer möchte ich den Inhalt des Programms verfolgen.
# 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
class SimpleConvNet:
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):
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()
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
def loss(self, x, t):
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 gradient(self, x, t):
# 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 einzige Unterschied besteht darin, dass die Ebenen gestapelt sind und die anderen sich nicht wesentlich von der MultiLayerNet-Klasse unterscheiden.
self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
conv_param['stride'], conv_param['pad'])
Die Convolution-Klasse ist auch in layer.py definiert
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)
col_W = self.W.reshape(FN, -1).T
out = np.dot(col, col_W) + self.b
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)
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)
dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)
return dx
im2col Das Herzstück ist die im2col-Funktion. In util.py definiert
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
Dies scheint also die Ursache dafür zu sein, dass der Speicher knapp wird. Wenn die Anzahl der zu verarbeitenden Datenzeilen zunimmt, tritt hier ein Speicherfehler auf.
In den ersten drei Zeilen überprüfen wir die Größe der Eingabedaten und berechnen die Ausgabegröße aus der Eingabegröße und der Filtergröße. Der Grund, warum // für die Schrittteilung verwendet wird, scheint darin zu liegen, nach dem Dezimalpunkt abzuschneiden, wenn es nicht teilbar ist.
len(x_test) #Die Anzahl der Daten
10000
len(x_test[0]) #Kanal
1
len(x_test[0][0]) #Höhe
28
len(x_test[0][0][0]) #Breite
28
len(network.params['W1']) #Anzahl der Filter
30
len(network.params['W1'][0]) #Anzahl der Kanäle
1
len(network.params['W1'][0][0]) #Filterhöhe
5
len(network.params['W1'][0][0][0]) #Filterbreite
5
conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
Das Auffüllen von 0 und Schritt 1 wird beim Erstellen des Netzwerkobjekts angegeben.
len(network.layers['Conv1'].forward(x_test)) #Die Anzahl der Daten
10000
len(network.layers['Conv1'].forward(x_test)[0]) #Anzahl der Filter
30
len(network.layers['Conv1'].forward(x_test)[0][0]) #Ausgangshöhe
24
len(network.layers['Conv1'].forward(x_test)[0][0][0]) #Ausgabebreite
24
self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
conv_param['stride'], conv_param['pad'])
class Convolution:
(Weggelassen)
def forward(self, x):
FN, C, FH, FW = self.W.shape # 30, 1, 5, 5
N, C, H, W = x.shape # 10000, 1, 28, 28
out_h = 1 + int((H + 2*self.pad - FH) / self.stride) # 24
out_w = 1 + int((W + 2*self.pad - FW) / self.stride) # 24
col = im2col(x, FH, FW, self.stride, self.pad)
(Weggelassen)
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
N, C, H, W = input_data.shape # 10000, 1, 28, 28
out_h = (H + 2*pad - filter_h)//stride + 1 # 24
out_w = (W + 2*pad - filter_w)//stride + 1 # 24
img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
input_data ist 4 Dimensionen (10000 Datenzeilen, 1 Kanal, 28 Höhe, 28 Breite) Wenn pad = 0 ist, wird [(0,0), (0,0), (0, 0), (0, 0)] nicht aufgefüllt. Wenn Pad = 1, [(0,0), (0,0), (1, 1), (1, 1)] Pad nacheinander oben, unten, links und rechts von Höhe und Breite. Wenn pad = 2, [(0,0), (0,0), (2, 2), (2, 2)] Pads jeweils zwei oben, unten, links und rechts von Höhe und Breite. In diesem Programmbeispiel ist pad = 0. Das gleiche wie input_data wird in img gesetzt.
col = np.zeros((N, C, filter_h, filter_w, out_h, out_w)) #10000, 1, 5, 5, 24, 24
Die Eingabedaten (Bild Bild) werden in die Array-Spalte erweitert. Erstellen Sie jedoch als Container zum Erweitern der Daten ein Array mit der Größe (Anzahl der Daten, Kanal, Filterhöhe, Filterbreite, Ausgabehöhe, Ausgabebreite). ..
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
Ich kann das Bild hier überhaupt nicht bekommen, deshalb habe ich es mit der folgenden vereinfachten Sequenz getestet.
import numpy as np
N=1
C=1
H=8
W=8
filter_h=4
filter_w=4
stride=2
out_h=3
out_w=3
img= np.arange(64).reshape(N, C, 8, 8)
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)
col
array([[ 0., 1., 2., 3., 8., 9., 10., 11., 16., 17., 18., 19., 24., 25., 26., 27.], [ 2., 3., 4., 5., 10., 11., 12., 13., 18., 19., 20., 21., 26., 27., 28., 29.], [ 4., 5., 6., 7., 12., 13., 14., 15., 20., 21., 22., 23., 28., 29., 30., 31.], [16., 17., 18., 19., 24., 25., 26., 27., 32., 33., 34., 35., 40., 41., 42., 43.], [18., 19., 20., 21., 26., 27., 28., 29., 34., 35., 36., 37., 42., 43., 44., 45.], [20., 21., 22., 23., 28., 29., 30., 31., 36., 37., 38., 39., 44., 45., 46., 47.], [32., 33., 34., 35., 40., 41., 42., 43., 48., 49., 50., 51., 56., 57., 58., 59.], [34., 35., 36., 37., 42., 43., 44., 45., 50., 51., 52., 53., 58., 59., 60., 61.], [36., 37., 38., 39., 44., 45., 46., 47., 52., 53., 54., 55., 60., 61., 62., 63.]])
In Spalte [0] wird der Teil, auf den der Filter zuerst angewendet wird, aus den Eingabedaten extrahiert. col [1] ist der Teil, in dem der Filter angewendet wird, indem der Schritt 2 um zwei nach rechts verschoben wird. Unten wird der Teil extrahiert und angeordnet, auf den der Filter 9 Mal angewendet wird.
Ich bin nicht sicher, was ich tue, aber ich kann das Ergebnis verstehen.
Wenn Sie den 4x4-Filter in eine Spalte umformen und Spalten- und Punktoperationen ausführen, können Sie das Ergebnis der neunmaligen Anwendung des Filters in einer Operation erhalten.
#Convolution.forward
col = im2col(x, FH, FW, self.stride, self.pad)
col_W = self.W.reshape(FN, -1).T
out = np.dot(col, col_W) + self.b
out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
Vollständiges Verständnis der Funktion numpy.pad Manipulieren Sie das zweidimensionale Array frei. [Initialisierung / Referenz / Extraktion / Berechnung / Verlagerung]
Teil 10 ←
Recommended Posts