Je lis un chef-d'œuvre, ** "Deep Learning from Zero" **. Cette fois, c'est le mémo du chapitre 5. Pour exécuter le code, téléchargez le code complet depuis Github et utilisez le notebook jupyter dans ch05.
À la fin du chapitre 5, il y a un code (twoLayerNet.py & train_neuralnet.py) qui génère une couche et effectue la propagation des erreurs, mais avant cela, le code qui génère une couche et effectue la propagation des erreurs se trouve déjà dans le chapitre 4. Il y a (aussi twoLayerNet.py & train_neuralnet.py), donc je vais l'essayer à partir de là.
Comme nous l'avons fait au chapitre 4, pour améliorer la visibilité, si nous le rassemblons dans un code, c'est presque le même que la dernière fois, seul le --- calcul du gradient --- est différent. Pour le moment, je vais le proposer.
import sys, os
sys.path.append(os.pardir) #Paramètres d'importation des fichiers dans le répertoire parent
from common.functions import * #fonction dans le dossier commun.Configuré pour utiliser toutes les fonctions de py
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
class TwoLayerNet:
#Initialisation des paramètres
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
#Propagation vers l'avant
def predict(self, x):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
return y
#Calcul des pertes
def loss(self, x, t):
y = self.predict(x)
return cross_entropy_error(y, t)
#Calcul de la précision
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
# -----------------Calcul du gradient-------------------
def gradient(self, x, t):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
grads = {}
batch_num = x.shape[0]
# forward
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
# backward
dy = (y - t) / batch_num
grads['W2'] = np.dot(z1.T, dy)
grads['b2'] = np.sum(dy, axis=0)
dz1 = np.dot(dy, W2.T)
da1 = sigmoid_grad(a1) * dz1
grads['W1'] = np.dot(x.T, da1)
grads['b1'] = np.sum(da1, axis=0)
return grads
# ------------------------------------------------
#Lire les données
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
#Instancier TwoLayerNet
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
#Réglage initial
iters_num = 10000 #Nombre d'exécutions
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
train_loss_list, train_acc_list, test_acc_list = [], [], []
iter_per_epoch = max(train_size / batch_size, 1)
for i in range(iters_num):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
#Calcul du gradient
grad = network.gradient(x_batch, t_batch)
#Mise à jour des paramètres
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
#Affichage de la précision
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))
#Dessiner un graphique
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
Génial! Par rapport à la différenciation numérique, la vitesse d'exécution est aussi différente que le ciel et la terre. C'est super rapide! (En fait, la différenciation numérique est extrêmement lente)
Le contenu du code a déjà été expliqué dans le chapitre 4 Memo sauf pour --- Calcul du gradient ---, donc si vous voulez le voir, [Chapter 4 Memo](https://qiita.com/jun40vn/ Veuillez vous référer aux articles / be171ff7626d370072d1). Dans ce chapitre, seule la partie calcul du gradient est expliquée.
Si vous expliquez une partie du code de calcul de gradient avec un graphique de calcul, cela ressemble à ceci.
La somme de grades ['b2'] = np.sum (dy, axis = 0)
est la correspondance avec le traitement par lots. Même ainsi, je suis impressionné par le fait que la rétropropagation des erreurs apparemment compliquée est remplacée par des opérations matricielles.
Tout d'abord, dérivez $ \ frac {\ partial L} {\ partial W2} = z1 ^ {T} * dy $. En fonction du taux de chaîne
Ensuite, nous dérivons $ \ frac {\ partial y} {\ partial Z1} = \ frac {\ partial y} {\ partial a2} W2 ^ T $.
Le passage du monde de la différenciation numérique au monde de la rétropropagation des erreurs par opération matricielle est vraiment innovant, n'est-ce pas?
Comme mentionné précédemment, vous pouvez obtenir une vitesse d'exécution pratique en écrivant la formule d'opération de la matrice directement dans le code pour la propagation directe et la propagation arrière d'erreur, mais l'écriture du code est un peu gênante, n'est-ce pas? Il existe donc un moyen de générer une couche et d'écrire le code plus simplement.
Pour générer une couche, importez d'abord le OrderedDict avec from collections import OrderedDict
.
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size,
# ................
#Génération de couches
self.layers = OrderedDict()
self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
self.layers['Relu1'] = Relu()
self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
self.lastLayer = SoftmaxWithLoss()
Instanciez OrderedDict avec self.layers = OrderedDict ()
lors de la configuration initiale de la classe TwoLayerNet.
OrderedDict est mémorisé, y compris l'ordre, donc si vous enregistrez ʻAffine1,
Relu1, ʻAffine2
et les noms et processus de couches dans le dictionnaire self.layers
dans l'ordre, l'ordre sera également mémorisé.
Seule la couche finale a une formule de rétropropagation d'erreur différente, alors traitez-la séparément comme «self.lastLayer».
#Propagation vers l'avant
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
#Calcul de la propagation / perte vers l'avant
def loss(self, x, t):
y = self.predict(x)
return self.lastLayer.forward(y, t)
Ensuite, la propagation vers l'avant predire
lit simplement les noms de couches un par un dans le dictionnaire self.layers
et exécute le processus
forward` à plusieurs reprises, donc peu importe le nombre de couches Tout ce dont vous avez besoin est du code.
Le calcul de propagation / perte vers l'avant perte '' se propage également vers l'avant et n'effectue que le traitement
avant '' de la couche finale avec self.lastLayer.forward (y, t)
, donc même si le contenu de traitement de la couche finale change, cela Cela reste le code.
#Calcul du gradient
def gradient(self, x, t):
#Calcul de la propagation / perte vers l'avant
self.loss(x, t)
#Erreur de propagation de retour
dout = 1
dout = self.lastLayer.backward(dout) #Remplacez le résultat arrière de la dernière couche par dout
layers = list(self.layers.values()) #Dictionnaire self.Lire le nom du calque à partir des calques
layers.reverse() #Inverser l'ordre des noms de calques
for layer in layers: #Lire le nom du calque inversé
dout = layer.backward(dout) #Exécuter en arrière du nom du calque
# ..................
return grads
Et le calcul du gradient est le même même si la combinaison des couches change. C'est pratique, n'est-ce pas?
Maintenant, exécutons le code avec la génération de couches.
Comme précédemment, nous allons combiner twoLayerNet.py
et train_neuralnet.py
pour une meilleure visibilité du code. De plus, la partie dessin du graphique est ajoutée.
import sys, os
sys.path.append(os.pardir) #Paramètres d'importation des fichiers dans le répertoire parent
from common.layers import * #calques dans le dossier commun.Configuré pour utiliser toutes les fonctions de py
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from collections import OrderedDict #Importer OrderedDict
class TwoLayerNet:
#Initialisation
def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
#Initialisation des paramètres
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
#Génération de couches
self.layers = OrderedDict()
self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
self.layers['Relu1'] = Relu()
self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
self.lastLayer = SoftmaxWithLoss()
#Propagation vers l'avant
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
#Calcul de la propagation / perte vers l'avant
def loss(self, x, t):
y = self.predict(x)
return self.lastLayer.forward(y, t)
#Calcul de la précision
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
if t.ndim != 1 : t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
#Calcul du gradient
def gradient(self, x, t):
# forward
self.loss(x, t)
# backward
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
#Réglage
grads = {}
grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db
return grads
#Lire les données
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
#Instancier TwoLayerNet
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
#Réglage initial
iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
train_loss_list, train_acc_list, test_acc_list = [], [], []
iter_per_epoch = max(train_size / batch_size, 1)
for i in range(iters_num):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
#Calcul du gradient
grad = network.gradient(x_batch, t_batch)
#Mise à jour des paramètres
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
#Affichage de la précision
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print(train_acc, test_acc)
#Dessiner un graphique
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epoch")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
La précision est améliorée d'environ +3 points par rapport au cas sans génération de couche. La raison en est, bien entendu, pas la génération de couches. C'est parce que la fonction d'activation est passée de «sigmoïde» à «ReLU».
Recommended Posts