[PYTHON] Reconnaissance des nombres manuscrits par un réseau neuronal multicouche

1. 1. Qu'est-ce que la reconnaissance par l'intelligence artificielle?

Ces dernières années, le domaine de l'intelligence artificielle a fait des progrès remarquables. Un algorithme appelé réseau de neurones est généralement utilisé pour reconnaître et juger les choses par l'intelligence artificielle. Il s'agit d'une tentative d'imiter le fonctionnement du cerveau humain, car il apprend automatiquement d'une approche d'ingénierie. Cette fois, dans le cadre de cela, laissez l'ordinateur reconnaître les chiffres manuscrits afin que les humains puissent les reconnaître naturellement.

2. Reconnaissance d'image par réseau neuronal

Un réseau neuronal est un modèle d'apprentissage qui imite le nerf cérébral humain. En effectuant un grand nombre de transformations non linéaires, c'est un substitut qui peut produire des résultats très complexes à partir de données données. Dans le domaine de la reconnaissance d'image, on peut dire que la reconnaissance d'image est réalisée en appliquant un processus de corrélation appelé template matching au réseau neuronal. (Bien qu'il puisse y avoir des malentendus, il ne s'agit que d'un processus de corrélation en termes de convolution à partir de pixels et de luminosité.)

L'image à reconnaître cette fois est un numéro manuscrit de MNIST.

Téléchargeons. Comme ce ne sont pas des données d'image qui peuvent être utilisées telles quelles après le téléchargement Convertissons-les en données d'image en se référant aux documents suivants sur le site officiel.


TRAINING SET LABEL FILE (train-labels-idx1-ubyte):

[offset] [type] [value] [description] 0000 32 bit integer 0x00000801(2049) magic number (MSB first) 0004 32 bit integer 60000 number of items 0008 unsigned byte ?? label 0009 unsigned byte ?? label ........ xxxx unsigned byte ?? label The labels values are 0 to 9.

TRAINING SET IMAGE FILE (train-images-idx3-ubyte):

[offset] [type] [value] [description] 0000 32 bit integer 0x00000803(2051) magic number 0004 32 bit integer 60000 number of images 0008 32 bit integer 28 number of rows 0012 32 bit integer 28 number of columns 0016 unsigned byte ?? pixel 0017 unsigned byte ?? pixel ........ xxxx unsigned byte ?? pixel Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).


Vous pouvez voir que le signal de l'enseignant est stocké après l'offset 8 et les données d'apprentissage sont stockées après l'offset 16. Par conséquent, si vous l'ouvrez en tant que fichier binaire et extrayez la chaîne d'octets pour chaque valeur de pixel (28 * 28), vous pouvez obtenir le fichier image. Si vous effectuez la binarisation de manière appropriée, vous pouvez obtenir les données d'image suivantes de nombres manuscrits pour 60000 données. image_0_1.png image_1_3.png image_2_5.png Nous utiliserons ces données de test supplémentaires dans la formation du réseau neuronal.

3. 3. Modèle de réseau neuronal multicouche

Le modèle du réseau neuronal utilisé cette fois est défini par les exigences suivantes.

--Une couche d'entrée, deux couches cachées, une couche de sortie --Toutes les fonctions d'activation sont sigmoïdes

C'est un réseau simple et extrêmement neuronal, mais est-ce le fait qu'il utilise deux couches cachées? Il y a beaucoup de choses à améliorer, mais pour l'instant, mettons cela en pratique.

L'implémentation du code est illustrée ci-dessous.

MultiLayerPerceptron_MNIST.py


# 1. Preprocess
import random
# 1.1. Make NeuralLetwork
# 1.1.1. Define Layers
n_hidden = 2
n_layer = n_hidden + 2

# 1.1.2. Define Units
n_unit_i = 7 * 7 + 1
n_unit_h = 20 # all 
n_unit_o = 10

unit_i = [0 for u in range(n_unit_i)]
unit_h1 = [0 for u in range(n_unit_h)]
unit_h2 = [0 for u in range(n_unit_h)]
unit_o = [0 for u in range(n_unit_o)]

# 1.1.3. Initialize weight
w1 = [[random.uniform(-1, 1) for u_before in range(n_unit_i)] for u_after in range(n_unit_h)]
w2 = [[random.uniform(-1, 1) for u_before in range(n_unit_h)] for u_after in range(n_unit_h)]
w3 = [[random.uniform(-1, 1) for u_before in range(n_unit_h)] for u_after in range(n_unit_o)]

# 1.2. Define dataset

import mydatasets

n_data = 100
ipt = mydatasets.inputdata("digit")
res = ipt.load_data2(size=(7, 7), num=n_data)

train = res[0][0]
for k in train:
    for n in k:
        n.insert(0, 1)

def maketeach(kind):
    buf = []
    for o in range(n_unit_o):
        if o == kind:
            buf.append(1) 
        else:
            buf.append(0)
    return buf
    
# 1.3 Implement forward propagation
import math

# 1.3.1 Define activation fucntion
def sigmoid(z):
    if z > 10: return 0.99999
    elif z < -10: return 0.00001
    else: return 1 / (1 + math.exp(-1 * z))
    
# 1.3.2 forward propagation

def forward(train_vec):
    
    for i in range(n_unit_i):
        unit_i[i] = train_vec[i]
    unit_i[0] = 1
        
    # 1.3.2.1 forward between input-hidden1
    for h1 in range(n_unit_h):
        buf = 0
        for i in range(n_unit_i):
            buf += unit_i[i] * w1[h1][i]
        unit_h1[h1] = sigmoid(buf)
    unit_h1[0] = 1
        
    # 1.3.2.2 forward between hidden1-hidden2
    for h2 in range(n_unit_h):
        buf = 0
        for h1 in range(n_unit_h):
            buf += unit_h1[h1] * w2[h2][h1]
        unit_h2[h2] = sigmoid(buf)
    unit_h2[0] = 1

    # 1.3.2.3 forward between hidden2-output
    for o in range(n_unit_o):
        buf = 0
        for h2 in range(n_unit_h):
            buf += unit_h2[h2] * w3[o][h2]
        unit_o[o] = sigmoid(buf)

# 1.3.3 back propagation

alpha = 0.1
def backpropagation(teach_vec):
    
    # 1.3.3.1 get cost
    buf = 0
    for o in range(n_unit_o):
        buf += (teach_vec[o] - unit_o[o]) ** 2
    cost = buf / 2
    
    # 1.3.3.2 get grad between hidden2-output
    for o in range(n_unit_o):
        for h2 in range(n_unit_h):
            delta = (unit_o[o] - teach_vec[o]) * unit_o[o] * (1 - unit_o[o]) * unit_h2[h2]
            w3[o][h2] -= alpha * delta
            
    # 1.3.3.3 get grad
    for o in range(n_unit_o):
        for h2 in range(n_unit_h):
            for h1 in range(n_unit_h):
                delta = ((unit_o[o] - teach_vec[o]) * unit_o[o] * (1 - unit_o[o])
                         * w3[o][h2] * unit_h2[h2] * (1 - unit_h2[h2]) * unit_h1[h1])
                w2[h2][h1] -= alpha * delta
                
    # 1.3.3.4 get grad
    for o in range(n_unit_o):
        for h2 in range(n_unit_h):
            for h1 in range(n_unit_h):
                for i in range(n_unit_i):
                    delta = ((unit_o[o] - teach_vec[o]) * unit_o[o] * (1 - unit_o[o])
                             * w3[o][h2] * unit_h2[h2] * (1 - unit_h2[h2])
                             * w2[h2][h1] * unit_h1[h1] * (1 -unit_h1[h1]) * unit_i[i])
                    w1[h1][i] -= alpha *delta
                    
    return cost
    
import matplotlib.pyplot as plt
plt_x = []
plt_y = []

n_epoch = 30
n_train = len(train)
n_kind = 10
n = 0
error_threshold = 0.001

print("Backpropagation training is started now.")

def training(n):
    for e in range(n_epoch):
        for d in range(n_data):
            for k in range(n_kind):
                try:
                    n += 1
                    forward(train[k][d])
                    c = backpropagation(maketeach(k))
                    plt_x.append(n)
                    plt_y.append(c)
                    if n % 100 == 0:
                        print("learn num: {0}".format(n))
                    if c < error_threshold and e > n_epoch // 2:
                        print("cost is least than error threshold. (n: {})".format(n))
                        return 1
                except Exception as e:
                    print("n:{}, d:{}, k{}, Error:{}".format(n, d, k, e.args))
                    pass
    return 0

def forecast(train_data, dim):
    forward(train_data)
    res = unit_o
    n = 0
    for r in res:
        if r == max(res):
            max_score = n
        n += 1
    print("max score : {}".format(max_score))
    print("scores is below : ")
    print(res)
    
    import numpy as np
    import cv2
    mat = []
    row = []
    cnt = 0
    n = 0
    for t in range(1, len(train_data)):
        row.append(train_data[t])
        cnt += 1
        n += 1
        if cnt == 7:
            #print("if statement is called at n:{}".format(n))
            mat.append(row)
            row = []
            cnt = 0
    cv2.imwrite('forecast_input.png', np.array(mat)*255)
    
    return max_score
    
def validation(vaild_sets):
    n_dim = 7
    correct = 0
    incorrect = 0
    n_kind = 10
    for d in range(n_data):
        for k in range(n_kind):
            if forecast(train[k][d], n_dim) == k:
                correct += 1
            else:
                incorrect += 1
    total = correct + incorrect
    print("validation result:: correct answer is {} / {}".format(correct, total))

training(n)
plt.plot(plt_x, plt_y)
plt.xlim(0, 30000)
plt.ylim(0.0, 1.51)
plt.show()

valid = res[1][0]
for k in valid:
    for n in k:
        n.insert(0, 1)
        
validation(valid)

4. Résultat d'exécution

En prenant le nombre de graphiques d'erreur d'apprentissage, les résultats suivants ont été obtenus.

result_MLP_MNIST.png

Au début, le réseau neuronal ne peut pas faire la distinction entre 0 (bonne réponse) et 1 (mauvaise réponse), et fait un jugement semi-fini de 0,5. Au fur et à mesure que vous apprenez, vous pourrez discriminer de plus en plus, et si le résultat est correct, vous pourrez porter un jugement proche de 0, et si le résultat est incorrect, vous pourrez porter un jugement proche de 1.

Ceci est le résultat de l'utilisation de sigmoïde pour la fonction d'activation de sortie. sigmoïde a pour effet de normaliser la valeur dans la plage de 0 à 1, ce qui indique le succès ou l'échec de la reconnaissance sous forme de probabilité.

Suite à la validation, le taux de reconnaissance des numéros manuscrits était de 84,7%. Si vous pouvez obtenir un tel résultat avec l'algorithme primitif ci-dessus, vous pouvez dire que vous avez réussi à reconnaître les nombres manuscrits pour le moment.

4.5. PostScript du résultat de l'exécution (16/06/06)

On m'a fait remarquer que la signification du résultat de l'exécution est difficile à lire, je vais donc ajouter une explication.

Commençons par affiner la gamme des résultats d'exécution.

ダウンロード (2).png

La figure ci-dessus est un graphique du taux d'erreur jusqu'à l'essai 29970 ... 30000. Vous pouvez voir la périodicité de ce graphique. La périodicité pour chaque type de nombre (0 ... 9), comme une erreur faible lorsque le chiffre des unités est 0 ou 1 et une erreur élevée lorsque le chiffre des unités est 9

Cette vibration est due à l'algorithme d'apprentissage. Dans l'algorithme utilisé cette fois, Shioume, comme l'apprentissage de 1 après l'apprentissage de 0, est amené à apprendre à chaque fois les types de nombres suivants. Par conséquent, le taux d'erreur fluctue considérablement en fonction du nombre d'essais. Il n'y a pas de lien direct avec des essais consécutifs, mais avec des essais basés sur 10. Vous apprenez la reconnaissance de 0 en essayant 0 → 10 → 20 → 30 et la reconnaissance de 1 en essayant 1 → 11 → 21.

Le taux de reconnaissance moyen diffère en fonction de chaque nombre. À un moment donné, un modèle peut bien reconnaître 0 et 9 non. C'est pourquoi les perceptions varient et semblent vibrer.

Et à mesure que l'apprentissage progresse, le modèle apprend ce qu'on appelle une «valeur idéale». Le taux d'erreur augmentera également. En effet, lorsque la valeur idéale est solidifiée, le degré de déviation de la valeur d'entrée devient plus clair qu'à l'état neutre. Regardons un exemple de la valeur idéale et du degré de déviation.

output_and_error.jpg

La figure ci-dessus montre le résultat de la saisie d'une partie de l'ensemble de validation 0 et son taux d'erreur. Le taux d'erreur est la dernière partie du délimiteur de soulignement dans le nom de fichier, avant l'extension. Le numéro d'index de l'image est avant le taux d'erreur. Dans cette image, la 93ème image d'entrée montre le taux d'erreur le plus élevé (0,4708). Le modèle a appris qu'il était difficile de reconnaître que c'était 0 quand j'ai vu cette forme terne. De plus, dans l'image ci-dessus, le n ° 98 a le taux d'erreur le plus bas, de sorte qu'il est également appris que 0 incliné vers la droite est le plus proche de la forme idéale de 0.

5. Conclusion

Cette fois, j'ai montré une implémentation simple d'un réseau neuronal. Cependant, avec l'algorithme actuel, il y a encore des mécontentements en termes de nombre de formations et d'erreurs. La prochaine fois, je résoudrai ce mécontentement avec plusieurs approches.

Recommended Posts

Reconnaissance des nombres manuscrits par un réseau neuronal multicouche
Vérification de la normalisation des lots avec un réseau neuronal multicouche
Construction d'un réseau neuronal qui reproduit XOR par Z3
Implémentation d'un réseau de neurones à deux couches 2
Etude du réseau neuronal récurrent (RNN) par Chainer ~ Vérification de l'exactitude des nombres aléatoires dans Excel et R ~
Créez un classificateur avec un taux de reconnaissance de l'écriture manuscrite de 99,2% à l'aide du réseau neuronal convolutif TensorFlow
Implémentation d'un réseau neuronal à 3 couches (pas d'apprentissage)
Implémentation de réseaux neuronaux "flous" avec Chainer
[Chainer] Classification des documents par réseau de neurones convolutifs
Visualisez la couche interne du réseau neuronal
Réseau neuronal paramétrique
Présentation de DNC (Differentiable Neural Computers) + Implémentation par Chainer
Mise en œuvre de l'optimisation bayésienne des hyper paramètres du réseau de neurones (Chainer + GPyOpt)
Bases de PyTorch (2) -Comment créer un réseau de neurones-
Implémentation d'un réseau de neurones convolutifs utilisant uniquement Numpy
J'ai essayé un réseau de neurones convolutifs (CNN) avec un tutoriel TensorFlow sur Cloud9-Classification des images manuscrites-