[PYTHON] [Keras] inférence par lots d'arcface

Aperçu

Fondamentalement, il est bien connu que l'apprentissage en profondeur ne peut être précis que si le nombre de données est important dans une certaine mesure, mais "l'apprentissage métrique" est précis même si le nombre de N est petit. Veuillez vous référer aux articles suivants pour des explications détaillées et des méthodes de mise en œuvre.

Explication: Méthode moderne d'apprentissage des métriques profondes: SphereFace, CosFace, ArcFace Implémentation de keras: [Keras] J'ai essayé de classer les bouteilles pour animaux en utilisant MobileNetV2 + ArcFace!

Arcface, l'un des apprentissages métriques, utilise le vecteur d'entités après le regroupement moyen global de CNN au moment de l'inférence. La classification est effectuée en calculant la similitude de cos (angle entre les vecteurs multidimensionnels) avec les données d'évaluation sur la base du vecteur de quantité de caractéristique représentative des données d'apprentissage. Puisqu'il est cos, il prend une valeur de -1 à 1, et s'il vaut 1, cela signifie que l'angle est 0, c'est-à-dire que l'angle formé par les deux vecteurs est 0, et on peut dire que les deux vecteurs de quantité de caractéristiques sont similaires. Par conséquent, s'il s'agit d'une classification à 3 classes, c'est un moyen de préparer un vecteur représentatif pour chaque classe et de sortir celui avec la similitude cos la plus élevée en tant que valeur prédite.

Soit dit en passant, après avoir appris le modèle du deep learning, il est possible de réaliser ce que vous voulez faire en le laissant calculer avec un PC Edge, mais si vous utilisez un GPU et que c'est une prémisse pour déduire plusieurs feuilles, il est préférable d'inférer plusieurs feuilles à la fois. Bien entendu, le temps de calcul de est plus rapide. Je ne pense pas qu'il soit si difficile de déduire par lots plusieurs feuilles à la fois (je ne sais pas si ce mot est correct ...) avec une classification multi-classe normale. Cependant, dans le cas de la similitude de cos, un peu d'ingéniosité est nécessaire, donc j'écrirai ce qui suit.

① Comment inférer par lots la similarité cos utilisée dans l'inférence arcface (2) Incorporer la similarité cos dans les keras (tensorflow) en combinant le modèle de keras et le backend

c'est tout. Veuillez noter que l'apprentissage de l'arcface lui-même et l'implémentation pour obtenir le vecteur représentatif sont ignorés.

supposition

tensorflow 1.15.0 keras 2.3.1 Python 3.7.6 numpy 1.18.1

core i7 GTX1080ti

① Comment inférer par lots la similarité cos utilisée dans l'inférence arcface

Implémentation de keras: [Keras] J'ai essayé de classer les bouteilles pour animaux en utilisant MobileNetV2 + ArcFace! Je vais me référer à.

Il s'agit d'une fonction pour calculer la similitude cos préalable.

cosine_similarity.py


def cosine_similarity(x1, x2): 
    if x1.ndim == 1:
        x1 = x1[np.newaxis]
    if x2.ndim == 1:
        x2 = x2[np.newaxis]
    x1_norm = np.linalg.norm(x1, axis=1)
    x2_norm = np.linalg.norm(x2, axis=1)
    cosine_sim = np.dot(x1, x2.T)/(x1_norm*x2_norm+1e-10)
    return cosine_sim

Tout d'abord, comment trouver la similitude cos une par une.

backend_result.py


from keras.applications.xception import Xception
from keras.preprocessing.image imload_img, img_to_array
import time
import os

imgpath = "Un chemin contenant plusieurs images appropriées"

#Définition du modèle
#Créer temporairement un modèle avec GAP attaché à xception
#La sortie est(N, 2048)
input_tensor = Input(shape=(299, 299, 3))
xception_model = Xception(include_top=False, weights='imagenet', input_tensor=input_tensor)
x = xception_model.output
outputs = GlobalAveragePooling2D()(x)
model = Model(inputs=input_tensor, outputs=outputs)

#Définir un vecteur représentatif provisoire → 1 vecteur d'image pour chacune des 10 classes
#Cette fois, définissez les 1 vecteurs de manière appropriée.
master_vector = np.ones([10, 2048])

#Liste des noms d'images
#Il est normal d'utiliser glob
img_name_list = os.listdir(imgpath)

for i in imglist:
    absname = os.path.join(imgpath, i)  
    img_rgb = load_img(absname, color_mode='rgb', target_size=(299,299))
    img_rgb = img_to_array(img_rgb)
    img_rgb = np.expand_dims(img_rgb, axis=0)
    pred_vector = model.predict(img_rgb)
    #Calculer la similitude du cos
    #pred_vector=(1, 2048)×master_vector=(10, 2048)→(1, 10)
    cos_sim = cosine_similarity(pred_vector, master_vector)
    pred_class = np.argmax(cos_sim[0])
    print(pred_class)

La méthode suivante consiste à trouver la similitude de cos pour plusieurs images à la fois. Changeons la fonction qui calcule la similitude cos. Le changement est que lors du calcul du produit intérieur, il est d'abord divisé par la norme carrée de chaque vecteur, puis calculé. Lors du calcul de la norme au carré avec np.linalg.norm, keepdims = True est défini, par exemple

Forme vectorielle caractéristique pour 10 images: (10, 2048)

Dans le cas de

Forme de la norme carrée du vecteur caractéristique pour 10 images: (10, 1)

Et vous pourrez vous diviser normalement. (Après cela, ajoutez une petite valeur pour qu'elle ne soit pas divisée par 0) Avec cette méthode, il est possible de calculer quelle que soit la forme de chaque vecteur d'entités. (Si vous êtes intéressé, veuillez essayer tout en vérifiant la forme du vecteur avec la forme)

cosine_similarity_batch.py


def cosine_similarity_batch(x1, x2): 
    if x1.ndim == 1:
        x1 = x1[np.newaxis]
    if x2.ndim == 1:
        x2 = x2[np.newaxis]
    x1_norm = np.linalg.norm(x1, axis=1, keepdims=True)
    x2_norm = np.linalg.norm(x2, axis=1, keepdims=True)
    #Commencez par diviser par la norme carrée (longueur du vecteur) de chaque vecteur de caractéristiques, puis calculez le produit intérieur
    cosine_sim = np.dot(x1/(x1_norm+1e-10), (x2/(x2_norm+1e-10)).T)
    return cosine_sim

Après cela, vous pouvez l'utiliser pour déduire par lots, puis calculer avec la fonction ci-dessus.

backend_result.py


from keras.applications.xception import Xception
from keras.preprocessing.image imload_img, img_to_array
import time
import os

imgpath = "Un chemin contenant plusieurs images appropriées"

#Définition du modèle
#Créer temporairement un modèle avec GAP attaché à xception
#La sortie est(N, 2048)
input_tensor = Input(shape=(299, 299, 3))
xception_model = Xception(include_top=False, weights='imagenet', input_tensor=input_tensor)
x = xception_model.output
outputs = GlobalAveragePooling2D()(x)
model = Model(inputs=input_tensor, outputs=outputs)

#Définir un vecteur représentatif provisoire → 1 vecteur d'image pour chacune des 10 classes
#Cette fois, définissez les 1 vecteurs de manière appropriée.
master_vector = np.ones([10, 2048])

#Liste des noms d'images
#Il est normal d'utiliser glob
img_name_list = os.listdir(imgpath)

#Mettez-le dans une liste vide avec l'instruction for et faites-en un tableau numpy
imgs = []
for i in imglist:
    absname = os.path.join(imgpath, i)  
    img_rgb = load_img(absname, color_mode='rgb', target_size=(299,299))
    img_rgb = img_to_array(img_rgb)
    imgs.append(img_rgb)
imgs = np.array(imgs, np.float32)
#Inférer et calculer avec la fonction précédente
#Calculer la similitude du cos
#pred_vector=(N feuilles, 2048)×master_vector=(10, 2048)→(N, 10)
pred_vector = model.predict(imgs)
cos_sim = cosine_similarity_batch(pred_vector, master_vecter)
#Étant donné qu'une similitude de 10 cos sort pour le nombre de lots, argmax est l'axe=Courir sur 1
pred_class = np.argmax(cos_sim, axis=1)
print(pred_class)

Après cela, vous pouvez faire bouillir ou cuire le résultat et l'aimer.

(2) Incorporer la similarité cos dans les keras (tensorflow) en combinant le modèle de keras et le backend

C'est le sujet principal. Puisque nous utilisons keras (tensorflow), qui est bon pour calculer le même traitement pour plusieurs images à la fois, il sera plus rapide d'incorporer le calcul avec numpy après inférence dans le modèle. C'est un plan. Cela dit, ce n'est pas si difficile car il vous suffit de réimplémenter ce que vous faites avec numpy dans le backend keras.

cosine_simlarity_kerasbackend.py


def cosine_similarity_eval(args):
    x1, x2 = args
    x2 = K.constant(x2)
    x1_norm = K.sqrt(K.sum(K.square(x1), axis=1, keepdims=True))
    x2_norm = K.sqrt(K.sum(K.square(x2), axis=1, keepdims=True))
    cosine_sim = K.dot(x1/(x1_norm+1e-10), K.transpose(x2/(x2_norm+1e-10)))
    return cosine_sim

Voici les différences par rapport à l'implémentation numpy. -Le vecteur représentatif est préparé par numpy et converti en un tenseur constant dans cette fonction. -Comme le calcul de la norme carrée ne semble pas avoir la même fonction que numpy, il est implémenté de force. (Par souci de clarté, je l'ai combiné avec l'implémentation précédente de numpy, mais si vous utilisez l2_normalize, vous pouvez l'écrire sur une ligne) Ensuite, comment se connecter avec le modèle keras.

cosine_simlarity_kerasbackend.py


#Définit un modèle qui génère un vecteur d'entités
input_tensor = Input(shape=(299, 299, 3))
xception_model = Xception(include_top=False, weights='imagenet', input_tensor=input_tensor)
x = xception_model.output
outputs = GlobalAveragePooling2D()(x)
model = Model(inputs=input_tensor, outputs=outputs)

#Définir un vecteur représentatif provisoire → 1 vecteur d'image pour chacune des 10 classes
#Cette fois, définissez les 1 vecteurs de manière appropriée.
master_vector = np.ones([10, 2048])

#Entrez le tenseur du vecteur de caractéristiques et le vecteur représentatif, qui sont les sorties du modèle.
cosine_sim = cosine_similarity_eval([model.output, master_vecter])

Si vous faites cela, le graphe de calcul de tensorflow qui sort le cosinus sim avec l'entrée du modèle comme entrée est terminé. Après cela, utilisez ceci pour diffuser des données avec sess.run et vous avez terminé. Ci-dessous le code complet.

cosine_simlarity_kerasbackend.py


from keras.applications.xception import Xception
from keras.preprocessing.image imload_img, img_to_array
from keras import backend as K
import time
import os
#Définir une session
sess = K.get_session()

imgpath = "Un chemin contenant plusieurs images appropriées"

#Définition du modèle
#Créer temporairement un modèle avec GAP attaché à xception
#La sortie est(N, 2048)
input_tensor = Input(shape=(299, 299, 3))
xception_model = Xception(include_top=False, weights='imagenet', input_tensor=input_tensor)
x = xception_model.output
outputs = GlobalAveragePooling2D()(x)
model = Model(inputs=input_tensor, outputs=outputs)

#Entrez le tenseur du vecteur de caractéristiques et le vecteur représentatif, qui sont les sorties du modèle.
cosine_sim = cosine_similarity_eval([model.output, master_vecter])

#Définir un vecteur représentatif provisoire → 1 vecteur d'image pour chacune des 10 classes
#Cette fois, définissez les 1 vecteurs de manière appropriée.
master_vector = np.ones([10, 2048])

#Liste des noms d'images
#Il est normal d'utiliser glob
img_name_list = os.listdir(imgpath)

#Mettez-le dans une liste vide avec l'instruction for et faites-en un tableau numpy
imgs = []
for i in imglist:
    absname = os.path.join(imgpath, i)  
    img_rgb = load_img(absname, color_mode='rgb', target_size=(299,299))
    img_rgb = img_to_array(img_rgb)
    imgs.append(img_rgb)
imgs = np.array(imgs, np.float32)

cos_sim = sess.run(
            cosine_sim,
            feed_dict={
                model.input: imgs,
                K.learning_phase(): 0
            })
#Étant donné qu'une similitude de 10 cos sort pour le nombre de lots, argmax est l'axe=Courir sur 1
#Vous pouvez également l'intégrer dans le modèle ici
pred_class = np.argmax(cos_sim, axis=1)
print(pred_class)

Chaque temps d'inférence

Je vais le mettre pour référence C'est le temps entre le chargement de l'image et l'inférence.

Méthode temps[s/100 feuilles]
Inférer un par un 1.37
Inférence par lots avec numpy 0.72
Inférence par lots avec keras 0.66

C'est un peu rapide. Cette méthode utilisant le backend keras peut être appliquée dans de nombreux endroits, donc je pense que cela vaut la peine de s'en souvenir.

c'est tout. Si vous avez des questions ou des préoccupations, veuillez laisser un commentaire.

Recommended Posts

[Keras] inférence par lots d'arcface
Résumé de Tensorflow / Keras
Keras comme wrapper de Theano et TensorFlow
Détournement de couches de modèle de keras entraînées
Implémentation de Light CNN (Python Keras)
4/22 prédiction de l'onde de péché avec keras