Essayez de créer un réseau de neurones en Python sans utiliser de bibliothèque

Code ici: tout le code est également disponible dans le bloc-notes Ipython sur Github.

Dans cet article, nous allons construire un simple réseau neuronal de 1 à 3 couches. Je n'expliquerai pas toutes les mathématiques qui en ressortent, mais j'aimerais expliquer les parties nécessaires d'une manière facile à comprendre. Si vous êtes intéressé par les détails des mathématiques, la plupart sont en anglais, mais voici quelques liens utiles.

Alors pourquoi avons-nous besoin de créer un réseau neuronal à partir de zéro sans utiliser de bibliothèque? Je prévois d'utiliser des bibliothèques de réseaux neuronaux comme PyBrain et Tensorflow dans un article ultérieur, pour la raison. L'expérience de la création d'un réseau neuronal à partir de zéro, même une seule fois, est extrêmement précieuse. Concevons un modèle en sachant comment fonctionne et se construit le réseau de neurones! C'est utile dans de tels moments.

Une mise en garde est que cet article se concentre sur la lisibilité, de sorte que le code ci-dessous n'est pas écrit efficacement. J'expliquerai comment écrire du code efficace dans un prochain article. Dans ce cas, utilisez Theano.

Générer des données

Maintenant, générons d'abord les données. Heureusement, Scikit-learn a un kit de génération de jeux de données utilisable, vous n'avez donc pas à écrire votre propre code. Cette fois, créons des données en forme de lune en utilisant la fonction make_moons.

#Générer et tracer des données
np.random.seed(0)
X, y = sklearn.datasets.make_moons(200, noise=0.20)
plt.scatter(X[:,0], X[:,1], s=40, c=y, cmap=plt.cm.Spectral)

nn-from-scratch-dataset.png

Il existe deux classes de données générées (points rouges et bleus sur le graphique). Par exemple, considérez les points bleus comme des points masculins et rouges comme des échantillons de données de patientes, et les axes X et Y comme des mesures spécifiques.

Notre objectif est de former le modèle de classification pour prédire et donner la classe correcte pour chaque point d'échantillonnage. Il est à noter que ces données ne peuvent pas être classées par ligne droite. Par conséquent, les classificateurs linéaires tels que la régression logistique ne peuvent pas constituer un bon modèle à moins que vous ne créiez vos propres entités non linéaires telles que des polynômes. Cependant, pour ces données, il est possible de créer un bon modèle en dirigeant les caractéristiques polynomiales.

Vous pouvez résoudre ce problème en dirigeant un réseau de neurones. Parce que vous n'avez pas à faire d'ingénierie de fonctionnalités. La couche cachée du réseau neuronal trouve les caractéristiques.

Retour logistique

Avant d'expliquer le réseau de neurones, commençons par former le modèle de régression logistique. Les données d'entrée sont un point sur l'axe X / Y et les données de sortie sont sa classe (0 ou 1). Voici une préparation pour l'explication du réseau de neurones ci-dessous, alors construisons un modèle de régression logistique en utilisant schikit-learn.

#Former un modèle de régression logistique
clf = sklearn.linear_model.LogisticRegressionCV()
clf.fit(X, y)
 
#Tracer la limite de décision
plot_decision_boundary(lambda x: clf.predict(x))
plt.title("Logistic Regression")

nn-from-scratch-lr-decision-boundary.png

Dans le graphique ci-dessus, le modèle de régression logistique est entraîné et les classes sont classées avec la limite de décision comme limite. Cette bordure mène une ligne droite et la classe le plus possible, mais elle ne reconnaît pas le «mois» des données.

Former un réseau de neurones

Construisons maintenant un réseau neuronal à trois couches (1 couche d'entrée, 1 couche cachée, 1 couche de sortie). Le nombre de nœuds dans la couche d'entrée (cercles dans la figure ci-dessous) est le nombre de dimensions des données (2 cette fois). Et le nombre de nœuds dans la couche de sortie est le nombre de classes, cette fois également 2 (Au fait, comme il s'agit de 2 classes, il est possible de faire de 1 ou 0 un nœud de sortie, mais plus tard plusieurs classes Cette fois, nous utiliserons deux nœuds en considération de la manipulation). L'entrée du réseau est le point (X, Y) et la sortie est la probabilité d'être de classe 0 (femelle) ou de classe 1 (mâle). Veuillez vous référer à la figure ci-dessous. nn-from-scratch-3-layer-network-1024x693.png

Ensuite, déterminez les dimensions de la couche masquée (le nombre de nœuds). À mesure que le nombre de nœuds de couche masqués augmente, des modèles plus complexes peuvent être créés. D'autre part, à mesure que le nombre de nœuds augmente, la puissance de calcul est nécessaire pour apprendre et prédire les paramètres. Il est également important de noter que le risque de surajustement augmente à mesure que le nombre de paramètres augmente.

Comment choisissez-vous le nombre de couches cachées? Bien qu'il existe des lignes directrices générales, les choix se font au cas par cas et devraient être considérés plus comme de l'art que de la science. Ci-dessous, nous allons expérimenter avec quelques nombres différents de nœuds de couche masqués pour voir comment ils affectent la sortie.

Une autre chose à décider est la fonction d'activation de la couche cachée. Il s'agit d'une fonction pour transformer les données d'entrée et les sortir. Vous pouvez entraîner des données non linéaires en dirigeant une fonction d'activation non linéaire. Des exemples courants de fonctions d'activation sont Tanh, Sigmaid Function Sigmoid_function) et ReLUs. Cette fois, j'utiliserai tanh, qui peut produire des résultats relativement bons dans divers cas. Une propriété pratique de cette fonction est que vous pouvez diriger la valeur d'origine et calculer la valeur différenciée. Par exemple, la valeur différentielle de $ tanhx $ est $ 1-tanh ^ 2x $. Par conséquent, une fois que $ tanhx $ est calculé, il peut être réutilisé plus tard.

Cette fois, je veux donner une probabilité à la sortie, donc j'utiliserai la fonction Softmax pour la fonction d'activation de la couche de sortie. Vous pouvez convertir des nombres non probables en probabilités en exécutant cette fonction. Si vous êtes familier avec le modèle de régression logistique, pensez à la fonction Softmax comme une version généralisée de plusieurs classes.

Comment fonctionne la prédiction de réseau neuronal

Ce réseau de neurones utilise une sorte de multiplication matricielle appelée propagation directe et une application de la fonction d'activation définie ci-dessus. Si l'entrée x est bidimensionnelle, la valeur prédite $ \ hat {y} $ (également bidimensionnelle) est calculée comme suit.

latex.png

$ z_i $ est la couche d'entrée i et $ a_i $ est la couche de sortie i convertie par la fonction d'activation. $ W_1 $, $ b_1 $, $ W_2 $, $ b_2 $ sont des paramètres réseau qui doivent être appris à partir des données d'entraînement. Vous pouvez le considérer comme une transformation matricielle entre les couches réseau. Si vous regardez la multiplication de la matrice ci-dessus, vous pouvez voir le nombre de dimensions de la matrice. Par exemple, si vous utilisez 500 calques masqués, $ W_1 \ in \ mathbb {R} ^ {2 \ times 500} $, $ b_1 \ in \ mathbb {R} ^ {500} $, $ W_2 \ in \ mathbb {R } ^ {2 \ fois 500} $, $ b_2 \ in \ mathbb {R} ^ {2} $. Vous pouvez donc voir pourquoi augmenter la taille du calque caché augmente également les paramètres.

Paramètres du train

Entraîner les paramètres signifie rechercher les paramètres ($ W_1 $, $ b_1 $, $ W_2 $, $ b_2 $) qui minimisent la valeur d'erreur sur les données d'entraînement. Alors, comment définissez-vous la valeur d'erreur? La fonction qui mesure la valeur d'erreur est appelée la fonction de perte. Pour Softmax, la fonction de perte couramment utilisée [Minimize Cross Entropy](https://ja.wikipedia.org/wiki/%E4%BA%A4%E5%B7%AE%E3%82%A8%E3 Leads% 83% B3% E3% 83% 88% E3% 83% AD% E3% 83% 94% E3% 83% BC) (également connu sous le nom de probabilité log négative). S'il y a N données d'apprentissage et qu'il y a une classe C, la fonction de perte de la valeur prédite $ \ hat {y} $ peut être écrite comme suit pour la valeur de réponse correcte y.

latex-1.png

Cette méthode peut sembler compliquée, mais son rôle est aussi simple que d'ajouter les données d'apprentissage ensemble et d'ajouter la valeur à Loss lorsque la classe est prédite par erreur. Par conséquent, plus les deux distributions de probabilité de la valeur prédite $ \ hat {y} $ et de la valeur de réponse correcte y sont éloignées, plus la perte est grande. Par conséquent, rechercher un paramètre qui minimise la perte équivaut à maximiser la probabilité des données d'apprentissage.

Utilisez Gradient Descent pour calculer la valeur de perte minimale. Cette fois, je mènerai une descente de gradient par lots simple (le taux d'apprentissage est constant), mais [descente de gradient probabiliste](https://ja.wikipedia.org/wiki/%E7%A2%BA%E7%8E%87% E7% 9A% 84% E5% 8B% BE% E9% 85% 8D% E9% 99% 8D% E4% B8% 8B% E6% B3% 95) et la descente de gradient en mini-lots serait plus pratique. Il est également plus pratique de réduire progressivement le taux d'apprentissage.

En entrée, les paramètres de descente de gradient ($ \ frac {\ partial {L}} {\ partial {W_1}} $, $ \ frac {\ partial {L}} {\ partial {b_1}} $, $ \ frac { Calculer la pente de la fonction Loss (valeur différentielle du vecteur) pour \ partial {L}} {\ partial {W_2}} $, $ \ frac {\ partial {L}} {\ partial {b_2}} $) est nécessaire. Utilisez l'algorithme de rétro-propagation pour calculer cette pente. Cet algorithme est un moyen efficace de calculer la pente à partir de la sortie. Ceux qui sont intéressés par ce commentaire mathématique sont ici et [ici](http://cs231n.github.io/optimization- Veuillez lire l'explication en 2 /).

En utilisant la rétro-propagation, ce qui suit est vrai.

latex-2.png

En fait, écrivez le code

Finissons les connaissances académiques jusqu'à présent et écrivons réellement le code! Tout d'abord, définissons les variables et les paramètres pour la descente de gradient.

 num_examples = len(X) #Taille des données pour la formation
 nn_input_dim = 2 #Nombre de dimensions de la couche d'entrée
 nn_output_dim = 2 #Nombre de dimensions de la couche de sortie

# Gradient descent parameters (Utilisez des valeurs couramment utilisées)
 epsilon = 0.01 #Taux d'apprentissage de la descente de gradient
 reg_lambda = 0.01 #Force de régularisation

Écrivons la fonction Loss définie ci-dessus. Vous pouvez diriger cela pour vérifier les performances de votre modèle.

#Fonction d'aide pour calculer toutes les pertes
def calculate_loss(model):
    W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    #Propagation vers l'avant pour le calcul des prévisions
    z1 = X.dot(W1) + b1
    a1 = np.tanh(z1)
    z2 = a1.dot(W2) + b2
    exp_scores = np.exp(z2)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    #Calculer la perte
    corect_logprobs = -np.log(probs[range(num_examples), y])
    data_loss = np.sum(corect_logprobs)
    #Donnez à la perte un terme de réguratisation(optional)
    data_loss += reg_lambda/2 * (np.sum(np.square(W1)) + np.sum(np.square(W2)))
    return 1./num_examples * data_loss

Ecrivez une fonction d'assistance pour calculer la couche de sortie. Dirige la propagation vers l'avant décrite ci-dessus et renvoie la probabilité la plus élevée.

# Helper function to predict an output (0 or 1)
def predict(model, x):
    W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    # Forward propagation
    z1 = x.dot(W1) + b1
    a1 = np.tanh(z1)
    z2 = a1.dot(W2) + b2
    exp_scores = np.exp(z2)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    return np.argmax(probs, axis=1)

Enfin, écrivez le code pour générer le réseau neuronal. Écrivez une descente de gradient par lots en utilisant les différentiels de rétropropagation décrits ci-dessus.

# This function learns parameters for the neural network and returns the model.
# - nn_hdim: Number of nodes in the hidden layer
# - num_passes: Number of passes through the training data for gradient descent
# - print_loss: If True, print the loss every 1000 iterations
def build_model(nn_hdim, num_passes=20000, print_loss=False):
     
    # Initialize the parameters to random values. We need to learn these.
    np.random.seed(0)
    W1 = np.random.randn(nn_input_dim, nn_hdim) / np.sqrt(nn_input_dim)
    b1 = np.zeros((1, nn_hdim))
    W2 = np.random.randn(nn_hdim, nn_output_dim) / np.sqrt(nn_hdim)
    b2 = np.zeros((1, nn_output_dim))
 
    # This is what we return at the end
    model = {}
     
    # Gradient descent. For each batch...
    for i in xrange(0, num_passes):
 
        # Forward propagation
        z1 = X.dot(W1) + b1
        a1 = np.tanh(z1)
        z2 = a1.dot(W2) + b2
        exp_scores = np.exp(z2)
        probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
 
        # Backpropagation
        delta3 = probs
        delta3[range(num_examples), y] -= 1
        dW2 = (a1.T).dot(delta3)
        db2 = np.sum(delta3, axis=0, keepdims=True)
        delta2 = delta3.dot(W2.T) * (1 - np.power(a1, 2))
        dW1 = np.dot(X.T, delta2)
        db1 = np.sum(delta2, axis=0)
 
        # Add regularization terms (b1 and b2 don't have regularization terms)
        dW2 += reg_lambda * W2
        dW1 += reg_lambda * W1
 
        # Gradient descent parameter update
        W1 += -epsilon * dW1
        b1 += -epsilon * db1
        W2 += -epsilon * dW2
        b2 += -epsilon * db2
         
        # Assign new parameters to the model
        model = { 'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}
         
        # Optionally print the loss.
        # This is expensive because it uses the whole dataset, so we don't want to do it too often.
        if print_loss and i % 1000 == 0:
          print "Loss after iteration %i: %f" %(i, calculate_loss(model))
     
    return model

Réseau de neurones avec 3 couches cachées

Créons maintenant un réseau avec trois couches cachées.

#Créer un modèle avec un calque caché en trois dimensions
model = build_model(3, print_loss=True)
 
#Tracer la limite de décision
plot_decision_boundary(lambda x: predict(model, x))
plt.title("Decision Boundary for hidden layer size 3")

nn-from-scratch-h3.png

J'ai bien reconnu la forme de la lune! Vous pouvez diviser correctement les classes par le réseau neuronal.

Vérifiez la taille compatible du calque caché

Dans ce qui précède, j'ai choisi 3 couches cachées. Ci-dessous, comparons en modifiant le nombre de calques cachés.

plt.figure(figsize=(16, 32))
hidden_layer_dimensions = [1, 2, 3, 4, 5, 20, 50]
for i, nn_hdim in enumerate(hidden_layer_dimensions):
    plt.subplot(5, 2, i+1)
    plt.title('Hidden Layer size %d' % nn_hdim)
    model = build_model(nn_hdim)
    plot_decision_boundary(lambda x: predict(model, x))
plt.show()

nn-from-scratch-hidden-layer-varying-1.png

En regardant la figure ci-dessus, nous avons une bonne compréhension du modèle de données dans le cas de couches cachées de faible dimension (par 3 ou 4). En revanche, il semble qu'il existe un risque de sur-ajustement à des dimensions supérieures. Lorsque cela se produit, c'est comme mémoriser les données dans leur intégralité plutôt que de capturer la vraie forme des données. Si vous dirigez les données de test pour vérifier le modèle, vous devriez être en mesure de prédire avec plus de précision qu'il s'agit d'une couche cachée de faible dimension. Il serait plus (informatiquement) «économique» de choisir la couche de masquage de la bonne taille que de régulariser fortement le surajustement.

Eh bien, cette fois, j'ai essayé de créer un réseau à partir de zéro sans utiliser de bibliothèque. Ensuite, j'expliquerai l'apprentissage profond plus en profondeur en utilisant la bibliothèque de réseau neuronal.

Recommended Posts

Essayez de créer un réseau de neurones en Python sans utiliser de bibliothèque
Apprentissage par renforcement 10 Essayez d'utiliser un réseau neuronal formé.
Implémentation de réseau neuronal en python
Essayez d'utiliser LevelDB avec Python (plyvel)
Essayez d'exécuter une fonction écrite en Python à l'aide de Fn Project
Essayez d'utiliser LeapMotion avec Python
Essayez d'utiliser l'API Wunderlist en Python
Essayez d'utiliser l'API Kraken avec Python
Essayez d'envoyer un paquet SYN en Python
Essayez de dessiner une animation simple en Python
Essayez d'utiliser TensorFlow-Part 2-Convolution Neural Network (MNIST)
Scraping de sites Web à l'aide de JavaScript en Python
Essayez le scraping HTML avec la bibliothèque Python
Dessinez une structure arborescente en Python 3 à l'aide de graphviz
Essayez un tube de programmation fonctionnel en Python
Essayez d'utiliser Platypus, une bibliothèque d'optimisation polyvalente
[Python] [Word] [python-docx] Essayez de créer un modèle de phrase de mot en Python en utilisant python-docx
J'ai écrit FizzBuzz en python en utilisant la machine à vecteurs de support (bibliothèque LIVSVM).
Créer un fichier GIF en utilisant Pillow en Python
Calculons en fait le problème statistique avec Python
Essayez d'utiliser l'API BitFlyer Ligntning en Python
J'ai essayé de comprendre attentivement la fonction d'apprentissage dans le réseau de neurones sans utiliser la bibliothèque d'apprentissage automatique (deuxième moitié)
Afficher les avis sur les médicaments à l'aide de listes en Python
Créez un fichier MIDI en Python en utilisant pretty_midi
Essayez d'utiliser l'API ChatWork et l'API Qiita en Python
Essayez d'utiliser l'API DropBox Core avec Python
Créer un diagramme de dispersion elliptique en Python sans utiliser une distribution normale multivariée
[Python] Deep Learning: J'ai essayé d'implémenter Deep Learning (DBN, SDA) sans utiliser de bibliothèque.
Essayez gRPC en Python
Essayez 9 tranches en Python
Essayez d'utiliser Tweepy [Python2.7]
Créer un bot de collecte de données en Python à l'aide de Selenium
Essayez de créer un module Python en langage C
Entraînez les données MNIST avec PyTorch en utilisant un réseau neuronal
(Python) Essayez de développer une application Web en utilisant Django
Essayez d'implémenter la mémoire associative par hop field network en Python
Essayez de rechercher un profil d'un million de caractères en Python
Modèle utilisant un réseau neuronal convolutif dans le traitement du langage naturel
Essayez la détection des visages en temps réel à l'aide d'une webcam
Implémentation d'un réseau de neurones convolutifs utilisant uniquement Numpy
Utiliser l'API de recherche de la Bibliothèque du Parlement national en Python
Comment exécuter une commande à l'aide d'un sous-processus en Python
Mémo de construction d'environnement d'apprentissage automatique par Python
Publier / télécharger une bibliothèque créée en Python vers PyPI
Une note sur l'implémentation de la bibliothèque qui explore les hyperparamètres à l'aide de l'optimisation bayésienne en Python
Implémenter un réseau neuronal à 3 couches
Essayez d'exécuter python dans l'environnement Django créé avec pipenv
Essayez de le faire avec GUI, PyQt en Python
Essayez de créer le format de fichier DeepZoom .DZI en Python
Essayez de créer un réseau de neurones / d'apprentissage en profondeur avec scratch
Créer une fonction en Python
Créer un dictionnaire en Python
[Python] Essayez d'utiliser le canevas de Tkinter
Créer une portée locale en Python sans polluer l'espace de noms
Publication d'une bibliothèque qui masque les données de caractères dans les images Python
Réseau de neurones avec Python (scikit-learn)
Construire un environnement Python sur un Mac, jusqu'au point d'utiliser Jupyter Lab
Essayez une recherche similaire de recherche d'images à l'aide du SDK Python [Recherche]
J'ai créé un lecteur de flux rapide en utilisant feedparser en Python
Création d'un environnement de développement pour les applications Android - Création d'applications Android avec Python