Normalisation des lots: une perspective différente du modèle d’inférence quantifiée – Cloud Computing | Cloud Computing | Centre de données

  • FrançaisFrançais



  • Abstrait

    Les avantages de la normalisation par lots dans la formation sont bien connus pour la réduction du décalage de covariable interne et donc l’optimisation de la formation pour converger plus rapidement. Cet article tente d’apporter une perspective différente, où la perte de quantification est récupérée à l’aide de la couche de normalisation par lots, conservant ainsi la précision du modèle. L’article donne également une implémentation simplifiée de la normalisation par lots pour réduire la charge sur les périphériques périphériques qui auront généralement des contraintes sur le calcul des modèles de réseaux neuronaux.

    Théorie de la normalisation des lots

    Lors de la formation du réseau neuronal, nous devons nous assurer que le réseau apprend plus rapidement. L’un des moyens de le rendre plus rapide consiste à normaliser les entrées du réseau, ainsi qu’à normaliser les couches intermittentes du réseau. Cette normalisation de couche intermédiaire est ce que l’on appelle la normalisation par lots. L’avantage de la norme Batch est également qu’elle aide à minimiser le décalage de covariable interne, comme décrit dans cet article.

    Les frameworks comme TensorFlow, Keras et Caffe ont la même représentation avec différents symboles qui y sont attachés. En général, la normalisation par lots peut être décrite en suivant les calculs mathématiques:

    Équation de normalisation par lots

    Ici, l’équation (1.1) est une représentation de Keras / TensorFlow. alors que l’équation (1.2) est la représentation utilisée par le cadre de Caffe. Dans cet article, le style de l’équation (1.1) est adopté pour la suite du contexte.

    Modifions maintenant l’équation (1.1) comme ci-dessous:

    Maintenant, en observant l’équation de (1.4), il reste une option d’optimisation en réduisant le nombre de multiplications et d’additions. Le facteur de peigne de polarisation (lisez-le comme un biais combiné) peut être calculé hors ligne pour chaque canal. Le rapport «gamma / sqrt (variance)» peut également être calculé hors ligne et peut être utilisé lors de l’implémentation de l’équation de norme Batch. Cette équation peut être utilisée dans le modèle d’inférence quantifiée, pour réduire la complexité.

    Modèle d’inférence quantifié

    Le modèle d’inférence à déployer dans les périphériques de périphérie comprend généralement des processeurs compatibles avec l’arithmétique entière, tels que les processeurs de la série ARM Cortex-M / A ou les périphériques FPGA. Maintenant, pour rendre le modèle d’inférence compatible avec l’architecture des périphériques de périphérie, va créer une simulation en Python. Ensuite, convertissez la chaîne d’entrées, de poids et de sorties du modèle d’inférence au format à virgule fixe. Dans le format virgule fixe, Q pour 8 bits est choisi pour représenter au format integer.fractional. Ce modèle de simulation vous aidera à développer le modèle d’inférence plus rapidement sur l’appareil et vous aidera également à évaluer la précision du modèle.

    par exemple: Q2.6 représente 6 bits de fraction et 2 bits d’un entier.

    Maintenant, la façon de représenter le format Q pour chaque couche est la suivante:

    1. Prenez le maximum et le minimum des entrées, des sorties et de chaque couche / poids.
    2. Obtenez les bits fractionnaires requis pour représenter la plage dynamique (en utilisant Maximum / Minimum) comme ci-dessous en utilisant la fonction Python:

    def get_fract_bits(tensor_float):
      # Assumption is that out of 8 bits, one bit is used as sign
      fract_dout = 7 - np.ceil(np.log2(abs(tensor_float).max()))

      fract_dout = fract_dout.astype('int8')

      return fract_dout

    1. Désormais, les bits entiers sont de 7 bits fractionnaires, car un bit est réservé pour la représentation des signes.
      4. Pour commencer, effectuez ceci sur l’entrée, puis suivi par Layer 1, 2…, ainsi de suite.
      5. Effectuez l’étape de quantification pour les poids, puis pour la sortie en supposant un exemple d’entrée. L’hypothèse est faite que l’entrée est normalisée afin que nous puissions généraliser le format Q, sinon, cela peut entraîner une perte de données lorsque des entrées différentes non normalisées sont alimentées.
      6. Cela définira le format Q pour les entrées, les poids et les sorties.

    Exemple:
    Considérons Resnet-50 comme un modèle à quantifier. Utilisons Keras intégré Resnet-50 formé avec Imagenet.

    #Creating the model

    def model_create():

        model = tf.compat.v1.keras.applications.resnet50.ResNet50(

        include_top=True,

        weights="imagenet",

        input_tensor=None,

        input_shape=None,

    pooling=None,

        classes=1000)

        return model

    Préparons l’entrée pour resnet-50. L’image ci-dessous est tirée du jeu de données ImageNet.

    Image d’éléphant à partir des données Imgenet

    def prepare_input():

          img = image.load_img(

                "D:Elephant_water.jpg",

                target_size=(224,224)

                )

          x_test = image.img_to_array(img)

          x_test = np.expand_dims(x_test,axis=0)

          x = preprocess_input(x_test) # from tensorflow.compat.v1.keras.applications.resnet50 import preprocess_input, decode_predictions

          return x

    Maintenant, appelons les deux fonctions ci-dessus et découvrons le format Q pour l’entrée.

          model = model_create()

          x     = prepare_input()

    Si vous observez l’entrée «x», sa plage dynamique est comprise entre -123,68 et 131,32. Cela rend difficile l’ajustement en 8 bits, car nous n’avons que 7 bits pour représenter ces nombres, en considérant un bit de signe. Par conséquent, le format Q pour cette entrée deviendrait, Q8.0, où 7 bits sont des nombres d’entrée et 1 bit de signe. Par conséquent, il coupe les données entre -128 et +127 (-2⁷ à 2⁷ -1). nous perdrions donc certaines données dans cette conversion de quantification d’entrée (la plus évidente étant que 131,32 est écrêté à 127), dont la perte peut être vue par le rapport signal / quantification du bruit, qui sera décrit bientôt ci-dessous.

    Si vous suivez la même méthode pour chaque poids et sorties des couches, nous aurons un format Q que nous pouvons corriger pour simuler la quantification.

           # Lets get first layer properties

           (padding, _) = model.layers[1].padding

           # lets get second layer properties

           wts = model.layers[2].get_weights()

           strides = model.layers[2].strides

           W=wts[0]

           b=wts[1]

           hparameters =dict(

                        pad=padding[0],

                        stride=strides[0]

                       )

             # Lets Quantize the weights .

             quant_bits = 8 # This will be our data path.

             wts_qn,wts_bits_fract = Quantize(W,quant_bits) # Both weights and biases will be quantized with wts_bits_fract.

             # lets quantize Bias also at wts_bits_fract

             b_qn = (np.round(b *(2<<wts_bits_fract))).astype('int8')

             names_model,names_pair = getnames_layers(model)

             layers_op = get_each_layers(model,x,names_model)

             quant_bits = 8

             print("Running conv2D")

             # Lets extract the first layer output from convolution block.

             Z_fl = layers_op[2] # This Number is first convolution.

             # Find out the maximum bits required for final convolved value.

             fract_dout = get_fract_bits(Z_fl)

             fractional_bits = [0,wts_bits_fract,fract_dout]

             # Quantized convolution here.
             Z, cache_conv = conv_forward(

                             x.astype('int8'),

                             wts_qn,

                             b_qn[np.newaxis,np.newaxis,np.newaxis,...],

                             hparameters,

                             fractional_bits)

    Maintenant, si vous observez l’extrait de code ci-dessus, l’opération de convolution prendra l’entrée, les pondérations et la sortie avec ses bits fractionnaires définis.
    c’est-à-dire: fractional_bits =[0,7,-3]où le 1er élément représente 0 bits pour la représentation fractionnaire de l’entrée (Q8.0)
    Le deuxième élément représente 7 bits pour la représentation fractionnaire des poids (Q1.7).
    Le troisième élément représente -3 bits pour la représentation fractionnaire des sorties (Q8.0, mais nécessite 3 bits supplémentaires pour la représentation entière car la plage est au-delà de la représentation 8 bits).

    Cela devra se répéter pour chaque couche pour obtenir le format Q.

    Maintenant que la familiarité avec la quantification est établie, nous pouvons passer à l’impact de cette quantification sur le SQNR et donc la précision.

    Rapport signal / bruit de quantification

    Comme nous avons réduit la plage dynamique de la représentation en virgule flottante à la représentation en virgule fixe en utilisant le format Q, nous avons discrétisé les valeurs à la représentation entière la plus proche possible. Ceci introduit le bruit de quantification, qui peut être quantifié mathématiquement par le rapport signal / bruit de quantification (voir: https://en.wikipedia.org/wiki/Signal-to-quantization-noise_ratio)

    Comme le montre l’équation ci-dessus, nous mesurerons le rapport entre la puissance du signal et la puissance du bruit. Cette représentation appliquée sur une échelle logarithmique se convertit en dB (10log10SQNR). Ici, le signal est une entrée en virgule flottante que nous quantifions à l’entier le plus proche et le bruit est le bruit de quantification.
    Exemple: L’exemple d’entrée d’éléphant a une valeur maximale de 131,32, mais nous la représentons au nombre entier le plus proche possible, qui est 127. Par conséquent, cela fait un bruit de quantification = 131,32–127 = 4,32.
    Donc SQNR = 131,32² /4,32² = 924,04, soit 29,66 db, indiquant que nous n’avons atteint que près de 30 dB par rapport à 48 dB (6 * no_of_bits) possibilité.

    Cette réflexion du SQNR sur la précision peut être établie pour chaque réseau individuel en fonction de la structure. Mais indirectement, nous pouvons dire que le SQNR est meilleur, plus la précision est élevée.

    Convolution dans des environnements quantifiés:

    L’opération de convolution dans CNN est bien connue, où nous multiplions le noyau avec l’entrée et l’accumulons pour obtenir les résultats. Dans ce processus, nous devons nous rappeler que nous fonctionnons avec 8 bits comme entrées, donc le résultat de la multiplication nécessite au moins 16 bits, puis l’accumule dans un accumulateur de 32 bits, ce qui aiderait à maintenir la précision du résultat. Ensuite, le résultat est arrondi ou tronqué à 8 bits pour transporter une largeur de 8 bits de données.

    def conv_single_step_quantized(a_slice_prev, W, b,ip_fract,wt_fract,fract_dout):

     """

     Apply one filter defined by parameters W on a single slice (a_slice_prev) of the output     activation

     of the previous layer.

     Arguments:

     a_slice_prev -- slice of input data of shape (f, f, n_C_prev)

     W -- Weight parameters contained in a window - matrix of shape (f, f, n_C_prev)

     b -- Bias parameters contained in a window - matrix of shape (1, 1, 1)

    Returns:

     Z -- a scalar value, result of convolving the sliding window (W, b) on a slice x of the     input data

    """

           # Element-wise product between a_slice and W. Do not add the bias yet.

           s = np.multiply(a_slice_prev.astype('int16'),W) # Let result be held in 16 bit

           # Sum over all entries of the volume s.

           Z = np.sum(s.astype('int32')) # Final result be stored in int32.

           # The Result of 32 bit is to be trucated to 8 bit to restore the data path.

           # Add bias b to Z. Cast b to a float() so that Z results in a scalar value.

           # Bring bias to 32 bits to add to Z.

           Z = Z + (b << ip_fract).astype('int32')

           # Lets find out how many integer bits are taken during addition.

           # You can do this by taking leading no of bits in C/Assembly/FPGA programming

           # Here lets simulate

           Z = Z >> (ip_fract+wt_fract - fract_dout)

           if(Z > 127):

                 Z = 127

           elif(Z < -128):

                 Z = -128

           else:

                Z = Z.astype('int8')

           return Z

    Le code ci-dessus est inspiré du cours de spécialisation en apprentissage profond d’AndrewNg, où la convolution à partir de zéro est enseignée. Puis modifié la même chose pour s’adapter à la quantification.

    Norme de lot dans un environnement quantifié

    Comme le montre l’équation 1.4, nous avons modifié la représentation pour réduire la complexité et effectuer la normalisation par lots. Le code ci-dessous montre la même implémentation.

    def calculate_bn(x,bn_param,Bn_fract_dout):

            x_ip = x[0]
            x_fract_bits = x[1]

           bn_param_gamma_s = bn_param[0][0]

           bn_param_fract_bits = bn_param[0][1]

           op = x_ip*bn_param_gamma_s.astype(np.int16) # x*gamma_s

           # This output will have x_fract_bits + bn_param_fract_bits

           fract_bits =x_fract_bits + bn_param_fract_bits

           bn_param_bias = bn_param[1][0]

           bn_param_fract_bits = bn_param[1][1]

           bias = bn_param_bias.astype(np.int16)

           # lets adjust bias to fract bits

           bias = bias << (fract_bits - bn_param_fract_bits)

           op = op + bias # + bias

           # Convert this op back to 8 bits, with Bn_fract_dout as fractional bits

           op = op >> (fract_bits - Bn_fract_dout)

           BN_op = op.astype(np.int8)

           return BN_op

    Maintenant que ces éléments sont en place pour le modèle d’inférence de quantification, nous pouvons maintenant voir l’impact de la norme Batch sur la quantification.

    Résultats

    Le Resnet-50 formé avec ImageNet est utilisé pour la simulation python afin de quantifier le modèle d’inférence. À partir des sections ci-dessus, nous lions les pièces ensemble pour analyser uniquement la première convolution suivie de la couche Batch Norm.

    L’opération de convolution est la plus lourde du réseau en termes de complexité et également de maintien de la précision du modèle. Examinons donc les données de Convolution après les avoir quantifiées sur 8 bits. La figure ci-dessous sur le côté gauche représente la sortie de convolution de la sortie de 64 canaux (ou filtres appliqués) dont la valeur moyenne est prise pour comparaison. La couleur bleue est une référence flottante et la couleur verte est une implémentation quantifiée. Le graphique des différences (côté gauche) donne une indication de la variation entre flottante et quantifiée. La ligne tracée dans ce chiffre de différence est la moyenne, dont la valeur est d’environ 4. ce qui signifie que nous obtenons une différence moyenne entre les valeurs flottantes et quantifiées proches d’une valeur de 4.

    Sorties de convolution et de norme de lot

    Regardons maintenant la figure de droite, qui est la section de normalisation par lots. Comme vous pouvez le voir, les courbes verte et bleue sont si proches et leur plage de différences est réduite à moins de 0,5. La ligne moyenne est d’environ 0,135, qui était d’environ 4 dans le cas de la convolution. Cela indique que nous réduisons nos différences entre la mise en œuvre flottante et quantifiée de la moyenne de 4 à 0,135 (presque proche de 0).

    Examinons maintenant le graphique SQNR pour apprécier l’impact de la norme de lot.

    Rapport signal / bruit de quantification pour la séquence de couches

    Juste au cas où les valeurs ne sont pas visibles, nous avons les numéros SQNR suivants
    Entrée SQNR: 25,58 dB (l’entrée entrant dans le modèle)
    Convolution SQNR: -4,4 dB (la sortie de la 1ère convolution)
    Batch-Norm SQNR: 20,98 dB (sortie de normalisation par lots)

    Comme vous pouvez le voir, le SQNR d’entrée est d’environ 25,58 dB, ce qui est réduit à -4,4 dB, ce qui indique une perte énorme ici, en raison de la limitation de la représentation au-delà de 8 bits. Mais l’espoir n’est pas perdu, car la normalisation par lots aide à récupérer votre SQNR à 20,98 dB en le rapprochant de l’entrée SQNR.

    Conclusion

    1. La normalisation par lots aide à corriger la moyenne, régularisant ainsi la variation de quantification à travers les canaux.
    2. La normalisation par lots récupère le SQNR. Comme vu de la démonstration ci-dessus, nous voyons une récupération de SQNR par rapport à la couche de convolution.
    3. Si le modèle d’inférence quantifié sur le bord est souhaitable, envisagez d’inclure la normalisation par lots car il agit comme une récupération de la perte de quantification et aide également à maintenir la précision, ainsi que les avantages de la formation d’une convergence plus rapide.
    4. La complexité de la normalisation par lots peut être réduite en utilisant (1.4) afin que de nombreux paramètres puissent être calculés hors ligne pour réduire la charge sur le périphérique de périphérie.

    Les références

    La normalisation post-lot: une perspective différente du modèle d’inférence quantifiée est apparue en premier sur NASSCOM Community | The Official Community of Indian IT Industry.

    Source

    N'oubliez pas de voter pour cet article !
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading...

    Laisser un commentaire

    Votre adresse e-mail ne sera pas publiée.