Le réseau Tensorflow2 à trois couches entièrement connecté réalise la reconnaissance des chiffres manuscrits

Tensorflow2 implémente la reconnaissance de chiffres manuscrits

0. Introduction à l'ensemble de données MNIST

L'ensemble de données MNIST provient de l'Institut national des normes et technologies (NIST). L'ensemble de formation se compose de chiffres manuscrits provenant de 250 personnes différentes, dont 50 % sont des étudiants du secondaire et 50 % du personnel du Bureau du recensement. L'ensemble de test contient également la même proportion de données numériques manuscrites.

L'ensemble de données MNIST est disponible sur MNIST et se compose de quatre parties :

  • Images de l'ensemble de formation : train-images-idx3-ubyte.gz
    (47 Mo décompressés, contient 60 000 échantillons)
  • Étiquettes des ensembles de formation : train-labels-idx1-ubyte.gz
    (60 Ko après décompression, contient 60 000 étiquettes)
  • Images de l'ensemble de test : t10k-images-idx3-ubyte.gz
    (7,8 Mo après décompression, contient 10 000 échantillons)
  • Étiquettes de l'ensemble de test : t10k-labels-idx1-ubyte.gz
    (10 Ko après décompression, contient 10 000 étiquettes)

Les images de l'ensemble d'entraînement et de l'ensemble de test ici sont des images en niveaux de gris 28 × 28, et chaque pixel est une valeur dans [0, 255].

La valeur dans le jeu d'étiquettes d'étiquettes est une valeur entre [0, 9], qui marque le numéro manuscrit de l'image de position correspondante.

exemple:Numéro 3

 


1. Détails des codes

import tensorflow as tf

Introduire le module Tensorflow avec tf comme alias

 


batch_size = 128
le_r = 0.2

Définir une taille de lot et un taux d'apprentissage

 


mnist = tf.keras.datasets.mnist
(ti, tl), (vi, vl) = mnist.load_data()
print('datasets:', ti.shape, tl.shape, vi.shape, vl.shape)

Utilisez la fonction intégrée de Tensorflow pour importer l'ensemble de données MNIST

Et affichez les dimensions des données :

ensembles de données : (60 000, 28, 28) (60 000,)

(10000, 28, 28) (10000,)

 


def fun(a, b):
    a = tf.cast(a, dtype=tf.float32)
    b = tf.cast(b, dtype=tf.int64)
    return tf.reshape(a, [-1, 28*28])/255.0, tf.one_hot(b, depth=10)

ti, tl = fun(ti, tl)
vi, _ = fun(vi, vl)

Puisque les dimensions de vi et ti sont [-1, 28, 28], nous espérons aplatir chaque échantillon [28, 28] dans [784] pour une saisie facile dans le réseau, donc une fonction nommée fun est définie ici, respectivement. Effectuez un prétraitement des données sur ti, tl et vi (vl n'a pas besoin d'être traité). L'entrée est simplement normalisée, c'est-à-dire divisée par 255,0 (valeur maximale - valeur minimale dans les données). Et un encodage à chaud a été effectué sur les balises.

Après prétraitement, les dimensions de ti et vi sont respectivement [60000, 784] et [10000, 784].

 

exemple:

Après normalisation

Insérer la description de l'image ici

Son étiquette correspondante 3 sera étendue à [0, 0, 0, 1, 0, 0, 0, 0, 0]

À propos de l'extension du prétraitement des données :

 


d1 = tf.data.Dataset.from_tensor_slices((ti, tl)) # ti tl 自动转换为tensor
d1 = d1.shuffle(10000).batch(batch_size)

Appelez la fonction tf.data.Dataset.from_tensor_slices() pour construire une tranche de (ti, tl) : shuffle() peut mélanger les données. batch(batch_size) peut diviser les données en plusieurs groupes de données de la taille d'un batch_size et renvoyer un Iterable objet utilisé pour parcourir des groupes de données individuels.

 


w1 = tf.random.normal([784, 512])
b1 = tf.zeros([512], dtype=tf.float32)
w2 = tf.random.normal([512, 10])
b2 = tf.zeros([10], dtype=tf.float32)

Construire la matrice de poids w et son décalage b des première et deuxième couches, initialiser b à une matrice 0 ; initialiser w selon une distribution normale.

Ici, puisque la dimension ti d'entrée a été traitée à [-1, 784],]', notre entrée est de 784 nœuds et la couche cachée du milieu est de 512 nœuds. Puisqu'il s'agit d'un problème de classification en 10 classes, la sortie est de 10. nœuds, représentant les nombres 0 ~ 9, plus la sortie du nœud est grande, plus il est probable qu'il représente ce nombre.

Concernant l'expansion de l'initialisation du poids :

 


Ensuite, parcourez l'ensemble de données pour l'optimisation de la descente de gradient et affichez la précision du réseau sur l'ensemble de vérification à une période fixe.

for epoch in range(10):
    print('the {0} epoch began'.format(epoch))
    d2 = iter(d1)
    for steps, (x, y) in enumerate(d2):

        with tf.GradientTape() as tape:
            tape.watch([w1, b1, w2, b2])  # 可以去掉tf.variable()包装
            h1 = x@w1 + b1
            a1 = tf.nn.sigmoid(h1)  

            out1 = a1 @ w2 + b2
            loss = tf.reduce_mean(tf.losses.categorical_crossentropy(y, out1, from_logits=True))

        if steps % 100 == 0:
            print(steps, 'finished')
        grads = tape.gradient(loss, [w1, b1, w2, b2])
        w1 = w1 - le_r*grads[0]
        b1 = b1 - le_r*grads[1]
        w2 = w2 - le_r*grads[2]
        b2 = b2 - le_r*grads[3]

    c1 = tf.nn.sigmoid(vi @ w1 + b1)  
    c2 = tf.nn.softmax(c1 @ w2 + b2, axis=1)  

    out2 = tf.cast(tf.argmax(c2, axis=1), dtype=tf.int64)
    acc = tf.reduce_sum(tf.cast(tf.equal(out2, vl), tf.float32))/vl.shape[0]
    print('the {0} epoch finished and the acc ={1}'.format(epoch+1, acc))

 

Vu séparément :

for epoch in range(10):
    print('the {0} epoch began'.format(epoch))
    d2 = iter(d1)
    for steps, (x, y) in enumerate(d2):

La première consiste à exécuter un total de 10 époques, c'est-à-dire 10 grandes boucles. Chaque grande boucle initialise d'abord d2 comme itérateur de d1, puis itère d1 à travers la boucle for. Les dimensions de d1, x et y à l'exception de la les derniers sont [128, 784], [128, 10].

 

 with tf.GradientTape() as tape:
            tape.watch([w1, b1, w2, b2])  # 可以去掉tf.variable()包装
            h1 = x@w1 + b1
            a1 = tf.nn.sigmoid(h1)  # [TensorShape([128, 500])

            out1 = a1 @ w2 + b2
            loss = tf.reduce_mean(tf.losses.categorical_crossentropy(y, out1, from_logits=True))

@ signifie multiplication matricielle

La puissante différenciation automatique de Tensorflow est utilisée ici. tape.watch([w1, b1, w2, b2]) signifie enregistrer les informations de gradient de w1, b1, w2, b2, de sorte qu'il n'est pas nécessaire d'emballer un package pour w1, b1 , w2, b2.Couche tf.Variable().

a1 est la sortie finale de la couche cachée de h2 après avoir été traitée par la fonction d'activation sigmoid().

 

Enfin, définissez la fonction de perte. La valeur de cette fonction représente le degré d'écart entre notre sortie finale de [-1, 10] et l'étiquette [-1, 10]. L'erreur quadratique moyenne peut être utilisée ici, mais il est préférable d'utiliser la fonction d'entropie croisée comme fonction de perte. La fonction d'entropie croisée peut refléter plus précisément l'écart entre deux distributions de probabilité.

Ici, comme nous n'avons pas effectué de traitement softmax sur la sortie out pour la rendre conforme aux caractéristiques de la distribution de probabilité (la somme est 1), nous définissons from_logits sur True, ce qui reviendra à l'appel de softmax_cross_entropy_with_logits_v2() pour nous aider à effectuer softmax traitement au sein de la fonction.

 

Voici une démonstration de la dérivation automatique de tensorflow, et vous pouvez également trouver la dérivée du second ordre par imbrication.

With tf.GradientTape() as tape:
    Build computation graph
    loss=(x)
[w_grad] = tape.gradient(loss,[w])

 


        if steps % 100 == 0:
            print(steps, 'finished')
        grads = tape.gradient(loss, [w1, b1, w2, b2])
        w1 = w1 - le_r*grads[0]
        b1 = b1 - le_r*grads[1]
        w2 = w2 - le_r*grads[2]
        b2 = b2 - le_r*grads[3]

grads = tape.gradient(loss, [w1, b1, w2, b2]) retire la matrice des dérivées partielles de w1, b1, w2, b2 en perte (fonction de perte) (grads = [dw1, db1, dw2, db2] )

Les lignes 4, 5, 6 et 7 mettent à jour les paramètres dans le sens de la réduction du gradient.

Chaque fois que 100 lots sont exécutés (100 descentes de pente sont effectuées), un message d'achèvement est généré.

 

    c1 = tf.nn.sigmoid(vi @ w1 + b1)  
    c2 = tf.nn.softmax(c1 @ w2 + b2, axis=1)  

    out2 = tf.cast(tf.argmax(c2, axis=1), dtype=tf.int64)
    acc = tf.reduce_sum(tf.cast(tf.equal(out2, vl), tf.float32))/vl.shape[0]
    print('the {0} epoch finished and the acc ={1}'.format(epoch+1, acc))

À l'heure actuelle, 1 époque est terminée et c1 et c2 sont le processus de propagation vers l'avant de l'ensemble de vérification. À l'heure actuelle, la dimension c2 est [10000, 10], c'est-à-dire que chaque ligne est la sortie d'un exemple d'image d'ensemble de vérification après traitement réseau. Il n'est pas nécessaire d'effectuer un traitement softmax dessus. Il suffit de supprimer le nœud inférieur du nœud avec la valeur de sortie la plus grande parmi les nœuds 10. Vous pouvez connaître la valeur prédite de cet exemple d'image en utilisant la norme.

La fonction tf.argmax() est utilisée ici pour obtenir l'indice du plus grand élément de chaque ligne. La dimension out2 traitée est [10 000, ], ce qui correspond aux valeurs prédites de 10 000 images d'ensemble de vérification.

Comparez ensuite out2 avec le jeu de balises vl, convertissez les valeurs vraies et fausses en 1 ou 0 et additionnez-les pour obtenir le nombre d'images correctement prédites. Divisez ensuite par vl.shape[0], qui est 10 000. , pour obtenir le nombre prédit. Le taux de précision est en hausse.

 
Insérer la description de l'image ici

Après l'avoir exécuté, vous pouvez constater que la précision de ce réseau dans la prédiction des chiffres manuscrits est d'environ 91 %. Si vous utilisez un réseau convolutif, vous pouvez obtenir une meilleure précision.

 


2. Présentation du code

import tensorflow as tf

batch_size = 128
le_r = 0.2

def fun(a, b):
    a = tf.cast(a, dtype=tf.float32)
    b = tf.cast(b, dtype=tf.int64)
    return tf.reshape(a, [-1, 28*28])/255.0, tf.one_hot(b, depth=10)

mnist = tf.keras.datasets.mnist
(ti, tl), (vi, vl) = mnist.load_data()
print('datasets:', ti.shape, tl.shape, vi.shape, vl.shape)

ti, tl = fun(ti, tl)
vi, _ = fun(vi, vl)

d1 = tf.data.Dataset.from_tensor_slices((ti, tl)) # ti tl 自动转换为tensor
d1 = d1.shuffle(10000).batch(batch_size)

w1 = tf.random.normal([784, 512])
b1 = tf.zeros([512], dtype=tf.float32)
w2 = tf.random.normal([512, 10])
b2 = tf.zeros([10], dtype=tf.float32)

for epoch in range(10):
    print('the {0} epoch began'.format(epoch))
    d2 = iter(d1)
    for steps, (x, y) in enumerate(d2):

        with tf.GradientTape() as tape:
            tape.watch([w1, b1, w2, b2])  # 可以去掉tf.variable()包装
            h1 = x@w1 + b1
            a1 = tf.nn.sigmoid(h1)  

            out1 = a1 @ w2 + b2
            loss = tf.reduce_mean(tf.losses.categorical_crossentropy(y, out1, from_logits=True))

        if steps % 100 == 0:
            print(steps, 'finished')
        grads = tape.gradient(loss, [w1, b1, w2, b2])
        w1 = w1 - le_r*grads[0]
        b1 = b1 - le_r*grads[1]
        w2 = w2 - le_r*grads[2]
        b2 = b2 - le_r*grads[3]

    c1 = tf.nn.sigmoid(vi @ w1 + b1)  
    c2 = tf.nn.softmax(c1 @ w2 + b2, axis=1)  

    out2 = tf.cast(tf.argmax(c2, axis=1), dtype=tf.int64)
    acc = tf.reduce_sum(tf.cast(tf.equal(out2, vl), tf.float32))/vl.shape[0]
    print('the {0} epoch finished and the acc ={1}'.format(epoch+1, acc))

Je suppose que tu aimes

Origine blog.csdn.net/weixin_43461724/article/details/101032310
conseillé
Classement