[PYTHON] Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 3

introduction

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é)

Chapitre 3 Réseau de neurones

Ce chapitre décrit le fonctionnement du réseau neuronal.

3.1 Du Perceptron au 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.

3.2 Fonction d'activation

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()

スクリーンショット 2019-11-23 22.37.42.png 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.

3.3 Calcul du tableau multidimensionnel

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.

3.4 Mise en œuvre d'un réseau neuronal à 3 couches

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.

3.5 Conception de la couche de sortie

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.

3.6 Reconnaissance des numéros manuscrits

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

スクリーンショット 2019-12-27 21.07.37.png

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 ... ~~

3.7 Résumé

~~ 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.

Problème 1 que j'ai remarqué 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.

Problème 2 que j'ai remarqué plus tard

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).

Recommended Posts

Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 1
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 3
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 7
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 5
Un amateur a trébuché dans le Deep Learning à partir de zéro.
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 2
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 5
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 2
Un amateur a trébuché dans le Deep Learning ❷ fait de zéro Note: Chapitre 1
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 4
[Mémo d'apprentissage] Le Deep Learning fait de zéro [Chapitre 7]
Deep learning / Deep learning made from scratch Chapitre 6 Mémo
[Mémo d'apprentissage] Deep Learning fait de zéro [Chapitre 5]
[Mémo d'apprentissage] Le Deep Learning fait de zéro [Chapitre 6]
"Deep Learning from scratch" avec Haskell (inachevé)
Deep learning / Deep learning made from scratch Chapitre 7 Mémo
[Mémo d'apprentissage] Deep Learning fait de zéro [~ Chapitre 4]
Apprentissage profond à partir de zéro
Deep Learning from scratch ① Chapitre 6 "Techniques liées à l'apprentissage"
Apprentissage profond à partir de zéro 1 à 3 chapitres
Deep learning / Deep learning from scratch 2 Chapitre 4 Mémo
Deep learning / Deep learning made from scratch Chapitre 3 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 5 Mémo
Créez un environnement pour "Deep Learning from scratch" avec Docker
Apprentissage profond à partir de zéro (calcul des coûts)
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 7 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 8 Mémo
Deep learning / Deep learning made from scratch Chapitre 5 Mémo
Deep learning / Deep learning made from scratch Chapitre 4 Mémo
Deep learning / Deep learning from scratch 2 Chapitre 3 Mémo
Mémo d'apprentissage profond créé à partir de zéro
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 6 Mémo
"Deep Learning from scratch" Mémo d'auto-apprentissage (n ° 14) Exécutez le programme du chapitre 4 sur Google Colaboratory
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 8) J'ai dessiné le graphique du chapitre 6 avec matplotlib
Pourquoi ModuleNotFoundError: Aucun module nommé'dataset.mnist 'n'apparaît dans "Deep Learning from scratch".
Écrivez vos impressions sur l'édition du framework Deep Learning 3 créée à partir de zéro
Apprentissage profond à partir de zéro (propagation vers l'avant)
Apprentissage profond / Apprentissage profond à partir de zéro 2-Essayez de déplacer GRU
[Windows 10] Construction de l'environnement "Deep Learning from scratch"
Enregistrement d'apprentissage de la lecture "Deep Learning from scratch"
[Deep Learning from scratch] À propos de l'optimisation des hyper paramètres
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 12) Deep learning
Python vs Ruby «Deep Learning from scratch» Chapitre 2 Circuit logique par Perceptron
Python vs Ruby "Deep Learning from scratch" Chapitre 4 Implémentation de la fonction de perte
Mémo d'auto-apprentissage "Deep Learning from scratch" (glossaire illisible)
Un amateur a essayé le Deep Learning avec Caffe (Introduction)
GitHub du bon livre "Deep Learning from scratch"
Un amateur a essayé le Deep Learning en utilisant Caffe (Practice)
[Mémo d'apprentissage] Apprentissage profond à partir de zéro ~ Mise en œuvre de l'abandon ~
Un amateur a essayé le Deep Learning avec Caffe (Vue d'ensemble)
Résumé Python vs Ruby "Deep Learning from scratch"
Mémo d'auto-apprentissage «Deep Learning from scratch» (10) Classe MultiLayerNet
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 11) CNN
Python vs Ruby "Deep Learning from scratch" Chapitre 3 Implémentation d'un réseau neuronal à 3 couches
[Python] [Traitement du langage naturel] J'ai essayé le Deep Learning ❷ fait de toutes pièces en japonais ①
Deep Learning from scratch La théorie et la mise en œuvre de l'apprentissage profond appris avec Python Chapitre 3
Version Lua Deep Learning from scratch Part 5.5 [Rendre les fichiers pkl disponibles dans Lua Torch]
[Pour les débutants] Après tout, qu'est-ce qui est écrit dans Deep Learning fait à partir de zéro?
[Deep Learning from scratch] J'ai implémenté la couche Affine