Du coup, j'ai commencé à étudier dans le chapitre 3 de "Deep Learning from scratch-The Theory and implementation of deep learning appris with Python". C'est un mémo du voyage.
L'environnement d'exécution est macOS Mojave + Anaconda 2019.10. Pour plus de détails, reportez-vous au Chapitre 1 de ce mémo.
(Vers d'autres chapitres de cette note: Chapitre 1 / Chapitre 2 / 3 Chapitre / Chapitre 4 / Chapitre 5 / [Chapitre 6](https: / /qiita.com/segavvy/items/ca4ac4c9ee1a126bff41) / Chapitre 7 / Chapitre 8 / Résumé)
Ce chapitre décrit le fonctionnement du réseau neuronal.
Il explique la différence dans la façon de compter les couches et la différence entre Perceptron et le réseau de neurones. C'est un peu gênant que différentes personnes comptent différentes couches.
Ceci est une introduction aux types de fonctions d'activation. J'ai essayé de représenter graphiquement les trois types de fonctions qui apparaissent.
# coding: utf-8
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
"""Fonction pas à pas qui renvoie 1 si l'entrée dépasse 0
Args:
x (numpy.ndarray):contribution
Returns:
numpy.ndarray:production
"""
return np.array(x > 0, dtype=np.int)
def sigmoid(x):
"""Fonction Sigmaid
Args:
x (numpy.ndarray):contribution
Returns:
numpy.ndarray:production
"""
return 1 / (1 + np.exp(-x))
def relu(x):
"""Fonction ReLU
Args:
x (numpy.ndarray)):contribution
Returns:
numpy.ndarray:production
"""
return np.maximum(0, x)
#Calcul
x = np.arange(-5.0, 5.0, 0.01) #le pas est petit de sorte que la fonction de pas n'a pas l'air diagonal
y_step = step_function(x)
y_sigmoid = sigmoid(x)
y_relu = relu(x)
#Dessin graphique
plt.plot(x, y_step, label="step")
plt.plot(x, y_sigmoid, linestyle="--", label="sigmoid")
plt.plot(x, y_relu, linestyle=":", label="ReLU")
plt.ylim(-0.1, 5.1)
plt.legend()
plt.show()
La seule chose que je n'ai pas comprise, c'est que la fonction d'activation ne doit pas être une fonction linéaire. J'ai compris de l'explication du livre que s'il n'y a qu'un seul neurone dans chaque couche, il peut être exprimé en une seule couche même s'il est multicouche. Mais même s'il y a plusieurs neurones dans chaque couche, peut-il être exprimé en une seule couche? Je n'ai pas bien compris ici.
L'explication est de remplacer le calcul des tableaux multidimensionnels par le calcul des matrices pour améliorer l'efficacité. J'ai étudié le remplacement avec le calcul matriciel lorsque j'ai suivi le cours d'apprentissage automatique en ligne [^ 1] il y a environ 3 ans, et je l'ai utilisé dans les 100 coups suivants de traitement du langage [^ 2]. J'ai fait.
Implémentez un réseau de neurones à trois couches en utilisant le calcul matriciel de la section précédente. Je n'avais pas d'obstacles particuliers car je n'avais pas la capacité d'apprendre.
Explication de la fonction softmax. ~~ Il n'y avait pas non plus de pierre d'achoppement particulière ici. ~~ Je ne voulais pas trébucher, mais j'ai remarqué une erreur. Si vous n'effectuez pas de traitement par lots, il n'y a pas de problème tel qu'implémenté dans le livre, mais vous devez le modifier lors de la mise en œuvre du "3.6.3 traitement par lots".
Vous trouverez ci-dessous le code Softmax qui prend en charge le traitement par lots.
python
def softmax(x):
"""Fonction Softmax
Args:
x (numpy.ndarray):contribution
Returns:
numpy.ndarray:production
"""
#Pour le traitement par lots, x est(Nombre de lots, 10)Il devient un tableau bidimensionnel de.
#Dans ce cas, il est nécessaire de bien calculer pour chaque image en utilisant la diffusion.
if x.ndim == 2:
#Pour chaque image (axe=1) Calculez la valeur maximale et remodelez-la pour qu'elle puisse être diffusée
c = np.max(x, axis=1).reshape(x.shape[0], 1)
#Calculer la molécule en soustrayant la valeur maximale comme contre-mesure de débordement
exp_a = np.exp(x - c)
#Le dénominateur est également pour chaque image (axe=Remodeler pour qu'il puisse être résumé à 1) et diffuser
sum_exp_a = np.sum(exp_a, axis=1).reshape(x.shape[0], 1)
#Calculé pour chaque image
y = exp_a / sum_exp_a
else:
#S'il ne s'agit pas d'un traitement par lots, implémentez-le selon le livre
c = np.max(x)
exp_a = np.exp(x - c) #Mesures de débordement
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
De plus, dans le référentiel GitHub https://github.com/oreilly-japan/deep-learning-from-scratch de ce livre À une source, il a été transposé pour la radiodiffusion. Peut-être que c'est un avantage de vitesse, mais à première vue, je ne savais pas ce que je faisais, alors j'ai essayé du code qui utilise reshape
.
Il implémente en fait le processus d'inférence du réseau neuronal à l'aide des paramètres entraînés. J'ai besoin d'un sample_weight.pkl
avec les paramètres entraînés stockés, donc le dépôt GitHub de ce livre [https://github.com/oreilly-japan/deep-learning-from-scratch](https: // github. Amenons les fichiers dans le dossier ch3
de com / oreilly-japan / deep-learning-from-scratch) dans le répertoire courant.
Au fur et à mesure que je procédais à la mise en œuvre conformément au livre, j'ai rencontré un avertissement de dépassement de capacité.
/Users/segavvy/Documents/deep-learning-from-scratch/ch03/3.6_mnist.py:19: RuntimeWarning: overflow encountered in exp
return 1 / (1 + np.exp(-x))
Pour cela, reportez-vous à l'explication de Meeting Machine Learning with Python >> Logistic Regression >> Sigmoid Function et définissez la valeur de x. J'ai essayé de le réparer pour qu'il ne déborde pas.
De plus, lors du calcul de la précision de reconnaissance finale, dans le livre, ʻaccuracy_cnt est converti de type en
float`, mais en python3, la division entre les entiers renvoie une virgule flottante, donc cette conversion semble inutile.
Aussi, en l'implémentant, je me demandais quel genre d'image je ne pouvais pas bien déduire, alors j'ai essayé de l'afficher.
Voici le code que j'ai écrit.
# coding: utf-8
import numpy as np
import os
import pickle
import sys
sys.path.append(os.pardir) #Ajouter le répertoire parent au chemin
from dataset.mnist import load_mnist
from PIL import Image
def sigmoid(x):
"""Fonction Sigmaid
Puisqu'il déborde dans la mise en œuvre du livre, il est corrigé en se référant au site suivant.
http://www.kamishima.net/mlmpyja/lr/sigmoid.html
Args:
x (numpy.ndarray):contribution
Returns:
numpy.ndarray:production
"""
#Corriger x sur une plage qui ne déborde pas
sigmoid_range = 34.538776394910684
x2 = np.maximum(np.minimum(x, sigmoid_range), -sigmoid_range)
#Fonction Sigmaid
return 1 / (1 + np.exp(-x2))
def softmax(x):
"""Fonction Softmax
Args:
x (numpy.ndarray):contribution
Returns:
numpy.ndarray:production
"""
#Pour le traitement par lots, x est(Nombre de lots, 10)Il devient un tableau bidimensionnel de.
#Dans ce cas, il est nécessaire de bien calculer pour chaque image en utilisant la diffusion.
if x.ndim == 2:
#Pour chaque image (axe=1) Calculez la valeur maximale et remodelez-la pour qu'elle puisse être diffusée
c = np.max(x, axis=1).reshape(x.shape[0], 1)
#Calculer la molécule en soustrayant la valeur maximale comme contre-mesure de débordement
exp_a = np.exp(x - c)
#Le dénominateur est également pour chaque image (axe=Remodeler pour qu'il puisse être résumé à 1) et diffuser
sum_exp_a = np.sum(exp_a, axis=1).reshape(x.shape[0], 1)
#Calculé pour chaque image
y = exp_a / sum_exp_a
else:
#S'il ne s'agit pas d'un traitement par lots, implémentez-le selon le livre
c = np.max(x)
exp_a = np.exp(x - c) #Mesures de débordement
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
def load_test_data():
"""Image de test MNIST et acquisition d'étiquettes de test
La valeur de l'image est 0.0〜1.Normalisé à 0.
Returns:
numpy.ndarray, numpy.ndarray:Image de test,Étiquette de test
"""
(x_train, t_train), (x_test, t_test) \
= load_mnist(flatten=True, normalize=True)
return x_test, t_test
def load_sapmle_network():
"""Obtenez des exemples de paramètres de poids entraînés
Returns:
dict:Paramètres de pondération et de biais
"""
with open("sample_weight.pkl", "rb") as f:
network = pickle.load(f)
return network
def predict(network, x):
"""Inférence par réseau neuronal
Args:
network (dict):Paramètres de pondération et de biais
x (numpy.ndarray):Entrée dans le réseau neuronal
Returns:
numpy.ndarray:Sortie de réseau neuronal
"""
#Récupération des paramètres
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
#Calcul du réseau neuronal (avant)
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = softmax(a3)
return y
def show_image(img):
"""Affichage de l'image
Args:
image (numpy.ndarray):Image bitmap
"""
pil_img = Image.fromarray(np.uint8(img))
pil_img.show()
#Lecture des données de test MNIST
x, t = load_test_data()
#Charger les paramètres de poids de l'échantillon
network = load_sapmle_network()
#Inférence, calcul de la précision de la reconnaissance
batch_size = 100 #Unité de traitement par lots
accuracy_cnt = 0 #Le nombre de bonnes réponses
error_image = None #Image non reconnue
for i in range(0, len(x), batch_size):
#Préparation des données par lots
x_batch = x[i:i + batch_size]
#inférence
y_batch = predict(network, x_batch)
p = np.argmax(y_batch, axis=1)
#Nombre de réponses correct
accuracy_cnt += np.sum(p == t[i:i + batch_size])
#Erreur sur l'image non reconnue_Connectez-vous à l'image
for j in range(0, batch_size):
if p[j] != t[i + j]:
if error_image is None:
error_image = x_batch[j]
else:
error_image = np.concatenate((error_image, x_batch[j]), axis=0)
print("Précision de reconnaissance:" + str(accuracy_cnt / len(x)))
#Afficher des images non reconnues
error_image *= 255 #La valeur de l'image est 0.0〜1.Puisqu'il est normalisé à 0, remettez-le de 0 à 255 pour qu'il puisse être affiché.
show_image(error_image.reshape(28 * (len(x) - accuracy_cnt), 28))
Et le résultat de l'exécution.
Précision de reconnaissance:0.9352
Puisque les images échouées ~~ 793 ~~ 648 sont simplement connectées verticalement, une image ridiculement longue est affichée, mais il y a certainement beaucoup de caractères difficiles à comprendre. Cependant, certains caractères peuvent être reconnus.
~~ En outre, le livre dit que la précision de la reconnaissance sera "0,9352", mais pour une raison quelconque, elle est devenue "0,9207". Même si j'ai renvoyé la fonction sigmoïde dans l'état où l'avertissement a été émis, cela n'a pas changé, il peut donc y avoir une autre erreur ... ~~
~~ Le chapitre 3 a également eu beaucoup de critiques pour moi, donc je n'ai pas fait une grosse pierre d'achoppement, mais je m'inquiète de la différence de précision de reconnaissance à la fin. ~~ Je n'avais pas l'intention de tomber sur le chapitre 3, mais j'ai remarqué les deux points suivants plus tard.
Comme @tunnel l'a souligné, j'ai découvert pourquoi la précision de la reconnaissance était différente de celle du livre! À l'origine, il était nécessaire d'utiliser la valeur des données d'image normalisée de 0,0 à 1,0, mais celle qui restait de 0 à 255 a été utilisée. Merci @tunnel! Même dans ce cas, si les valeurs sont différentes jusqu'à présent, la précision de la reconnaissance est susceptible d'être en lambeaux, mais il est intéressant qu'elle ne soit pas devenue si mauvaise.
Même si j'ai appris pour une raison quelconque au chapitre 4, la fonction de perte n'est pas devenue petite, et lorsque j'en recherchais la cause, j'ai remarqué que la fonction Softmax ne pouvait pas prendre en charge le traitement par lots. Le code ci-dessus a été corrigé. (J'étais content si cela était expliqué un peu plus au chapitre 3 ...)
(Vers d'autres chapitres de cette note: Chapitre 1 / Chapitre 2 / 3 Chapitre / Chapitre 4 / Chapitre 5 / [Chapitre 6](https: / /qiita.com/segavvy/items/ca4ac4c9ee1a126bff41) / Chapitre 7 / Chapitre 8 / Résumé)
[^ 1]: Ceci est une conférence Machine Learning fournie par l'Université de Stanford dans le service de cours en ligne appelé Coursera. Les volontaires ont ajouté des sous-titres japonais, donc même si je n'étais pas bon en anglais, c'était plutôt bon. La technique de remplacement des calculs matriciels par des calculs matriciels est décrite sous le nom de vectorisation. [^ 2]: Je l'ai utilisé lorsque j'ai résolu le problème 73 du Chapitre 8 de 100 Language Processing Knock 2015. Apprenez des notes à ce moment-là [100 coups de traitement du langage amateur: 73](https://qiita.com/segavvy/items/5ad0d5742a674bdf56cc#%E3%83%99%E3%82%AF%E3%83%88% Publié comme E3% 83% AB% E5% 8C% 96).