Cette fois, afin de comprendre la propagation vers l'avant et la propagation des erreurs, je vais essayer d'implémenter un réseau de neurones réellement en mouvement avec scratch. L'ensemble de données utilise MNIST.
Un réseau neuronal avec 28 x 28 = 784 couches d'entrée, 10 couches intermédiaires et 2 couches de sortie, la fonction d'activation / fonction d'erreur est sigmoïde et la méthode d'optimisation est la méthode de descente de gradient.
Pour l'ensemble de données, "1" et "7" sont extraits de MNIST et une classification binaire est effectuée.
Premièrement, lorsque vous calculez $ a ^ 0_0 $, il y a 784 entrées de $ x ^ 0_0 $ à $ x ^ 0_ {783} $, chacune avec des poids $ w ^ 0_ {00} $ à $ w ^ 0_ {783,0} $ est bloqué, donc
Exprimé sous forme de matrice, tous les calculs de $ a ^ 0_0 $ à $ a ^ 0_9 $ peuvent être facilement représentés.
Puisque $ x ^ 1_0 $ à $ x ^ 1_9 $ est le résultat du passage de $ a ^ 0_0 $ à $ a ^ 0_9 $ via la fonction d'activation sigmoïde,
Ensuite, lorsque vous calculez $ a ^ 1_0 $, il y a 10 entrées de $ x ^ 1_0 $ à $ x ^ 1_9 $, chacune avec des poids $ w ^ 1_ {00} $ à $ w ^ 1_ {90. } $ Est bloqué, donc
Comme précédemment, si $ a ^ 1_0 $ et $ a ^ 1_1 $ sont représentés par une matrice,
Enfin, $ y ^ 0 $ et $ y ^ 1 $ sont
De cette manière, la propagation vers l'avant peut être facilement réalisée par le produit interne de la matrice ou de l'addition.
Tout d'abord, mettez à jour les pondérations et les biais de la couche intermédiaire à la couche de sortie.
L'expression de mise à jour pour le poids w peut être représentée par $ w = w- \ eta * \ frac {\ partial E} {\ partial w} $. Où $ \ eta $ est le taux d'apprentissage et $ \ frac {\ partial E} {\ partial w} $ est l'erreur E différenciée par le poids w.
Donnons un exemple concret de $ \ frac {\ partial E} {\ partial w} $ et exprimons-le avec une formule générale pour l'implémenter. Tout d'abord, les pondérations de la couche intermédiaire à la couche de sortie.
Trouvez $ \ frac {\ partial E ^ 0} {\ partial w ^ 1_ {00}} $ pour mettre à jour le poids $ w ^ 1_ {00} $. Du rapport en chaîne de la différenciation Exprimé sous forme de formule générale, k = 0 à 9, j = 0 à 1, Cela permet de mettre à jour le poids $ w ^ 1_ {kj} $. En ce qui concerne le biais, $ b ^ 1 $ vaut 1, donc $ x ^ 1_k $ dans la formule ci-dessus remplace juste 1. Cela permet de mettre à jour le biais $ b ^ 1_j $.
Vient ensuite la mise à jour des poids et biais de la couche d'entrée à la couche intermédiaire.
Pour mettre à jour le poids $ w ^ 0_ {00} $, $ \ frac {\ partial E ^ 0} {\ partial w ^ 0_ {00}} $ et $ \ frac {\ partial E ^ 1} {\ Vous devez trouver un w ^ 0_ {00}} $ partiel. Du rapport en chaîne de la différenciation Exprimé sous forme de formule générale, k = 0 à 783, j = 0 à 9, Cela permet de mettre à jour le poids $ w ^ 0_ {kj} $. En ce qui concerne le biais, $ b ^ 0 $ vaut 1, donc $ x ^ 0_k $ dans la formule ci-dessus remplace simplement 1. Cela permet de mettre à jour le biais $ b ^ 0_j $.
En vous basant sur la formule générale obtenue précédemment, implémentez les parties propagation directe et propagation arrière d'erreur.
#Fonction Sigmaid
def sigmoid(a):
return 1 / (1 + np.exp(-a))
#Différenciation de la fonction sigmoïde
def sigmoid_d(a):
return (1 - sigmoid(a)) * sigmoid(a)
#Erreur de propagation de retour
def back(l, j):
if l == max_layer - 1:
return (y[j] - t[j]) * sigmoid_d(A[l][j])
else:
output = 0
m = A[l+1].shape[0]
for i in range(m):
output += back(l+1, i) * W[l+1][i,j] * sigmoid_d(A[l][j])
return output
Le mouvement spécifique de def back (l, j):
est
Lorsque «l = 1»,
(y [j] -t [j]) * sigmoid_d (A [1] [j])
est retourné.
Lorsque l = 0
,
(y[0]-t[0])*sigmoid_d(A[1][0])*W[1][0,j]*sigmoid_d(A[0][j]) +(y[1]-t[1])*sigmoid_d(A[1][1])*W[1][1,j]*sigmoid_d(A[0][j])
Est retourné.
#Réglage poids W
np.random.seed(seed=7)
w0 = np.random.normal(0.0, 1.0, (10, 784))
w1 = np.random.normal(0.0, 1.0, (2, 10))
W = [w0, w1]
#Réglage du biais b
b0 = np.ones((10, 1))
b1 = np.ones((2, 1))
B = [b0, b1]
#Autres réglages
max_layer = 2 #Définition du nombre de couches
n = 0.5 #Réglage du taux d'apprentissage
Définissez le poids W, la polarisation b et d'autres paramètres.
Chaque terme de la matrice de poids w0 et w1 est un nombre aléatoire qui suit une distribution normale de 0 à 1 afin que l'apprentissage puisse commencer en douceur. Au fait, si vous changez la graine = nombre de np.random.seed (graine = 7)
, la condition de départ de l'apprentissage (qu'elle démarre en douceur ou soit un peu lente) changera. Chaque terme des matrices de biais b0 et b1 vaut 1.
#Boucle d'apprentissage
count = 0
acc = []
for x, t in zip(xs, ts):
#Propagation vers l'avant
x0 = x.flatten().reshape(784, 1)
a0 = W[0].dot(x0) + B[0]
x1 = sigmoid(a0)
a1 = W[1].dot(x1) + B[1]
y = sigmoid(a1)
#X pour la mise à jour des paramètres,Lister un
X = [x0, x1]
A = [a0, a1]
#Mise à jour des paramètres
for l in range(len(X)):
for j in range(W[l].shape[0]):
for k in range(W[l].shape[1]):
W[l][j, k] = W[l][j, k] - n * back(l, j) * X[l][k]
B[l][j] = B[l][j] - n * back(l, j)
C'est une boucle d'apprentissage. La propagation vers l'avant peut être facilement réalisée par le produit interne de la matrice, l'addition, etc. Dans la mise à jour des paramètres,
Lorsque «l = 0», dans la plage de «j = 0 à 9, k = 0 à 783»
W[0][j,k] = W[0][j,k] - n * back(0,j) * X[0][k]
B[0][j] = B[0][j] - n * back(0,j)
Sera mis à jour.
Lorsque «l = 1», dans la plage de «j = 0 à 1, k = 0 à 9»
W[1][j,k] = W[1][j,k] - n * back(0,j) * X[0][k]
B[1][j] = B[1][j] - n * back(0,j)
Sera mis à jour.
Lisez l'ensemble de données MNIST avec Keras et extrayez uniquement "1" et "7".
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
import matplotlib.pyplot as plt
#Affichage numérique
def show_mnist(x):
fig = plt.figure(figsize=(7, 7))
for i in range(100):
ax = fig.add_subplot(10, 10, i+1, xticks=[], yticks=[])
ax.imshow(x[i].reshape((28, 28)), cmap='gray')
plt.show()
#Lecture de l'ensemble de données
(x_train, y_train), (x_test, y_test) = mnist.load_data()
show_mnist(x_train)
# 1,7 extraits
x_data, y_data = [], []
for i in range(len(x_train)):
if y_train[i] == 1 or y_train[i] == 7:
x_data.append(x_train[i])
if y_train[i] == 1:
y_data.append(0)
if y_train[i] == 7:
y_data.append(1)
show_mnist(x_data)
#Convertir du format de liste au format numpy
x_data = np.array(x_data)
y_data = np.array(y_data)
# x_normalisation des données, y_Représentation unique des données
x_data = x_data.astype('float32')/255
y_data = np_utils.to_categorical(y_data)
#Apprenez, obtenez des données de test
xs = x_data[0:200]
ts = y_data[0:200]
xt = x_data[2000:3000]
tt = y_data[2000:3000]
Les données de 0 à 9 et les données dont 1 et 7 sont extraites sont affichées depuis le début.
Préparez 200 éléments de données d'entraînement xs et ts, et 1 000 éléments de données de test xt et tt.
C'est l'ensemble de la mise en œuvre avec l'ajout de la confirmation de la précision par les données de test et l'affichage du graphique de transition de précision pour chaque apprentissage.
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
#Lecture de l'ensemble de données
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 1,Extraire seulement 7 numéros
x_data, y_data = [], []
for i in range(len(x_train)):
if y_train[i] == 1 or y_train[i] == 7:
x_data.append(x_train[i])
if y_train[i] == 1:
y_data.append(0)
if y_train[i] == 7:
y_data.append(1)
#Convertir du format de liste au format numpy
x_data = np.array(x_data)
y_data = np.array(y_data)
# x_normalisation des données, y_Une donnée chaude
x_data = x_data.astype('float32')/255
y_data = np_utils.to_categorical(y_data)
#Acquisition de données d'entraînement et de données de test
xs = x_data[0:200]
ts = y_data[0:200]
xt = x_data[2000:3000]
tt = y_data[2000:3000]
#Fonction Sigmaid
def sigmoid(a):
return 1 / (1 + np.exp(-a))
#Différenciation de la fonction sigmoïde
def sigmoid_d(a):
return (1 - sigmoid(a)) * sigmoid(a)
#Erreur de propagation de retour
def back(l, j):
if l == max_layer - 1:
return (y[j] - t[j]) * sigmoid_d(A[l][j])
else:
output = 0
m = A[l+1].shape[0]
for i in range(m):
output += back(l + 1, i) * W[l + 1][i, j] * sigmoid_d(A[l][j])
return output
#Réglage poids W
np.random.seed(seed=7)
w0 = np.random.normal(0.0, 1.0, (10, 784))
w1 = np.random.normal(0.0, 1.0, (2, 10))
W = [w0, w1]
#Réglage du biais b
b0 = np.ones((10, 1))
b1 = np.ones((2, 1))
B = [b0, b1]
#Autres réglages
max_layer = 2 #Définition du nombre de couches
n = 0.5 #Réglage du taux d'apprentissage
#Boucle d'apprentissage
count = 0
acc = []
for x, t in zip(xs, ts):
#Propagation vers l'avant
x0 = x.flatten().reshape(784, 1)
a0 = W[0].dot(x0) + B[0]
x1 = sigmoid(a0)
a1 = W[1].dot(x1) + B[1]
y = sigmoid(a1)
#X pour la mise à jour des paramètres,Lister un
X = [x0, x1]
A = [a0, a1]
#Mise à jour des paramètres
for l in range(len(X)):
for j in range(W[l].shape[0]):
for k in range(W[l].shape[1]):
W[l][j, k] = W[l][j, k] - n * back(l, j) * X[l][k]
B[l][j] = B[l][j] - n * back(l, j)
#Contrôle de l'exactitude par les données de test
correct, error = 0, 0
for i in range(1000):
#Inférence avec les paramètres appris
x0 = xt[i].flatten().reshape(784, 1)
a0 = W[0].dot(x0) + B[0]
x1 = sigmoid(a0)
a1 = W[1].dot(x1) + B[1]
y = sigmoid(a1)
if np.argmax(y) == np.argmax(tt[i]):
correct += 1
else:
error += 1
calc = correct/(correct+error)
acc.append(calc)
count +=1
print("\r[%s] acc: %s"%(count, calc))
#Affichage du graphique de transition de précision
import matplotlib.pyplot as plt
plt.plot(acc, label='acc')
plt.legend()
plt.show()
En 200 étapes, la précision de la classification était de 97,8%. Ce serait formidable si le réseau de neurones que j'ai implémenté en grattant fonctionne correctement.