Je lis un chef-d'œuvre, ** "Deep Learning from Zero" **. Cette fois, c'est un mémo du chapitre 7. Pour exécuter le code, téléchargez le code complet depuis Github et utilisez le notebook jupyter dans ch07.
Je vais implémenter un exemple pour déplacer réellement ** Convolution, Pooling ** que j'ai étudié dans le manuel. Les données utilisées sont MNIST, et les poids du filtre de convolution sont appris (params.pkl enregistré dans le dossier ch07). Le code de convolution et de regroupement est importé de common / layer.py et utilisé.
Tout d'abord, exécutons-le.
import sys, os
sys.path.append(os.pardir) #Paramètres d'importation des fichiers dans le répertoire parent
import numpy as np
import matplotlib.pyplot as plt
from simple_convnet import SimpleConvNet
from common.layers import * #Importation de couches
from dataset.mnist import load_mnist
#Fonction d'affichage(FH, FW)
def show(filters):
FH, FW = filters.shape
fig = plt.figure(figsize=(FH*0.1, FW*0.1)) #Spécification de la taille de l'écran
plt.imshow(((filters)), cmap='gray')
plt.tick_params(left=False, labelleft=False, bottom=False, labelbottom=False) #Effacer l'échelle / l'étiquette de l'axe
plt.show()
#Lire les données MNIST
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)
x_train = x_train[5:6] #Sélectionnez la 5ème donnée depuis le début
#Chargement des paramètres appris
network = SimpleConvNet() #Instancier SimpleConvNet
network.load_params("params.pkl") #Lire l'intégralité du paramètre
W1, b1 = network.params['W1'][:1], network.params['b1'][:1] #Seulement le premier
#Génération de couches
conv = Convolution(W1, b1, stride=1, pad=0)
pool = Pooling(pool_h=2, pool_w=2, stride=2, pad=0)
#Propagation vers l'avant
out1 = conv.forward(x_train) #Plier
out2 = pool.forward(out1) #mise en commun
#afficher
print('input.shape = ',x_train.shape)
show(x_train.reshape(28, 28))
print('filter.shape = ', W1.shape)
show(W1.reshape(5, 5))
print('convolution.shape = ', out1.shape)
show(out1.reshape(24, 24))
print('pooling.shape = ', out2.shape)
show(out2.reshape(12, 12))
L'image MNIST (1, 1, 28, 28) est contournée avec filter5 * 5, padding = 0, stride = 1, et les données de (1, 1, 24, 24) sont en outre filtrées2 * 2, padding = 0, La mise en commun se fait avec stride = 2 (1, 1, 12, 12) et les données sont obtenues.
Regardons maintenant les points clés du code.
3.Convolution
# ------------- from common_layers.py -------------
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)
#① Convertissez les données d'image en données matricielles avec im2col
col = im2col(x, FH, FW, self.stride, self.pad)
#② Remodelez le filtre et développez-le dans un tableau à deux dimensions
col_W = self.W.reshape(FN, -1).T
#③ Calculez la sortie par calcul matriciel
out = np.dot(col, col_W) + self.b
#④ Ajustez la forme de la sortie
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
Un diagramme montrant comment une image en 4 dimensions (taille de lot, nombre de canaux, hauteur d'image, largeur d'image) est traitée par l'opération de convolution ressemble à ceci. Jetons un œil à la fonction ** ① im2col ** la plus importante.
4.im2col
# ------------- from common_layers.py -------------
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))
# 24*24 filtres 5*Trancher 5 fois(stride=Quand 1)
for y in range(filter_h): #5 boucles
y_max = y + stride*out_h # y_max = y + 24
for x in range(filter_w): #5 boucles
x_max = x + stride*out_w # x_max = x + 24
#y à y+Jusqu'à 24,x à x+Jusqu'à 24、スライシング
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
y: y_max: stride, x: x_max: stride
signifie que la plage de y à y_max est spécifiée pour chaque foulée, et la plage de x à x_max est spécifiée pour chaque foulée.
Avec y_max = y + 24, x_max = x + 24, stride = 1, chaque boucle for est une double boucle 5 fois, vous finissez donc par trancher 5 * 5 fois avec un ** filtre 24 * 24 **.
D'un autre côté, compte tenu de ce que vous avez appris dans le manuel, vous devriez être capable de trancher 24 * 24 fois avec un ** filtre 5 * 5 **. Si vous le transformez en code, il ressemblera à ceci.
# 5*24 avec 5 filtres*Trancher 24 fois(stride=Quand 1)
for y in range(0, out_h, stride):
for x in range(0, out_w, stride):
col[:, :, y, x, :, :] = img[:, :, y:y+filter_h, x:x+filter_w]
En effet, le nombre total d'éléments traités par le ** filtre 24 * 24 tranchant 5 * 5 fois ** et le filtre ** 5 * 5 découpant 24 * 24 fois ** est le même. Si les résultats sont les mêmes, quel est le meilleur? Bien sûr, ** l'ancien **. La raison est que le nombre de boucles for, qui prend beaucoup de temps de traitement, est extrêmement faible.
Si vous montrez les deux méthodes dans la figure, cela ressemble à ceci
Vérifions si les résultats des deux sont vraiment les mêmes. Exécutez le code suivant pour visualiser le ʻim2cold'origine et la fonction
my_im2col` qui tranche ** 24 * 24 fois avec un filtre ** 5 * 5, y compris les données en cours de calcul.
import sys, os
sys.path.append(os.pardir) #Paramètres d'importation des fichiers dans le répertoire parent
import numpy as np
from dataset.mnist import load_mnist
import matplotlib.pyplot as plt
#Fonction d'affichage des données( x =Largeur d'affichage, y=Hauteur d'affichage, nx =Le nombre de colonnes)
def show(filters, x, y, nx, margin=1, scale=10):
FN, C, FH, FW = filters.shape
ny = int(np.ceil(FN / nx))
fig = plt.figure(figsize=(x, y))
fig.subplots_adjust(left=0, right=1.3, bottom=0, top=1.3, hspace=0.05, wspace=0.05)
for i in range(FN):
ax = fig.add_subplot(ny, nx, i+1, xticks=[], yticks=[])
ax.imshow(filters[i, 0], cmap='gray', interpolation='nearest')
plt.show()
def my_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 #Hauteur de sortie
out_w = (W + 2*pad - filter_w)//stride + 1 #Largeur de sortie
img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant') #Remplissage d'image
col = np.zeros((N, C, out_h, out_w, filter_h, filter_w)) #préparation de la matrice de calcul col
# 5*24 avec 5 filtres*Trancher 24 fois(stride=Quand 1)
for y in range(0, out_h, stride):
for x in range(0, out_w, stride):
col[:, :, y, x, :, :] = img[:, :, y:y+filter_h, x:x+filter_w]
# check1
print('col.shape after slicing = ', col.shape)
show(col.reshape(576, 1, 5, 5), x = 3.5, y = 3.5, nx = 24)
#Transposer et remodeler
col = col.transpose(0, 2, 3, 1, 4, 5).reshape(N*out_h*out_w, -1)
# check2
print('col.shape after transpose & reshape = ', col.shape)
show(col.reshape(1, 1, 25, 576), x = 18, y =3, nx=1)
return col
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 #Hauteur de sortie
out_w = (W + 2*pad - filter_w)//stride + 1 #Largeur de sortie
img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant') #Remplissage d'image
col = np.zeros((N, C, filter_h, filter_w, out_h, out_w)) #préparation de la matrice de calcul col
# 24*24 filtres 5*Trancher 5 fois(stride=Quand 1)
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]
# check1
print('col.shape after slicing = ', col.shape)
show(col.reshape(25, 1, 24, 24), x = 3.5, y = 3.5, nx = 5)
#Transposer et remodeler
col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
# check2
print('col.shape after transpose & reshape = ', col.shape)
show(col.reshape(1, 1, 25, 576), x = 18, y =3, nx=1)
return col
#Lire les données MNIST
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)
x_train = x_train[5:6] #Sélectionnez la 5ème donnée depuis le début
out1 = my_im2col(x_train, 5, 5)
out2 = im2col(x_train, 5, 5)
print('all elements are same = ', (out1 == out2).all()) #Si tous les éléments sont égaux
La première moitié est le résultat de my_im2col
et la seconde moitié est le résultat de ʻim2col`. Dans les deux cas, col.shape après transpose & reshape est en fait de 576 lignes x 25 colonnes, ce qui est verticalement long, mais cela n'a pas l'air bien, donc l'affichage est horizontalement long avec 25 lignes x 576 colonnes en permutant les lignes et les colonnes. Je vais. Et si vous regardez les deux images, elles ont certainement le même motif.
Dans la dernière ligne de la sortie, ** True ** lors de la comparaison de tous les éléments du tableau multidimensionnel **, nous pouvons donc voir que my_im2col
et ʻim2col` donnent exactement le même résultat. En général, si stride = 1, il vous suffit de ** filter_h * filter_w slice **. C'est une excellente technique, n'est-ce pas?
Voici un exemple simple et intuitif des raisons pour lesquelles vous pouvez faire cela.
6.Pooling
# ------------- from common_layers.py -------------
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
Cela ressemble à ceci lorsque l'image 4D (taille du lot, nombre de canaux, hauteur de l'image, largeur de l'image) est traitée par Pooling.
Semblable à la convolution, la fonction im2col est utilisée pour obtenir la matrice et la traiter avec pool_h, pool_w, stride et pad comme arguments.
Recommended Posts