[PYTHON] Détection d'objets par apprentissage profond pour comprendre en profondeur par Keras

Pour qui

Pour ceux qui comprennent déjà l'apprentissage profond et veulent maîtriser le mécanisme de la classification des images à la détection d'objets

Puisqu'il existe de nombreuses formules, si vous souhaitez vérifier le code, passez à la suivante

[Exemple d'implémentation spécifique](http://qiita.com/GushiSnow/private/8c946208de0d6a4e31e7#%E5%85%B7%E4%BD%93%E7%9A%84%E3%81%AA%E5%AE % 9F% E8% A3% 85% E4% BE% 8B)

prime

Traduit un livre sur Keras. Il couvre un large éventail de l'identification d'images, la génération d'images, le traitement du langage naturel, la prédiction de séries chronologiques et l'apprentissage par renforcement. [Recette intuitive d'apprentissage en profondeur pour façonner des idées avec Python x Keras](https://www.amazon.co.jp/Deep-Learning-%E2%80%95Python%C3%97Keras%E3%81%A7% E3% 82% A2% E3% 82% A4% E3% 83% 87% E3% 82% A2% E3% 82% 92% E5% BD% A2% E3% 81% AB% E3% 81% 99% E3% 82% 8B% E3% 83% AC% E3% 82% B7% E3% 83% 94-Antonio-Gulli / dp / 4873118263 / ref = sr_1_1? S = livres & ie = UTF8 & qid = 1530227887 & sr = 1-1 & mots-clés =% E7% 9B % B4% E6% 84% 9F + Deep + Learning)

Objectif

Je l'ai écrit parce que je voulais systématiquement résumer la technologie liée à la détection d'objets et comprendre la base de code.

Cet article est créé en se référant au chapitre sur la reconnaissance d'objets dans la reconnaissance d'image, qui est un bon livre.

[Reconnaissance d'image](https://www.amazon.co.jp/%E7%94%BB%E5%83%8F%E8%AA%8D%E8%AD%98-%E6%A9%9F%E6 % A2% B0% E5% AD% A6% E7% BF% 92% E3% 83% 97% E3% 83% AD% E3% 83% 95% E3% 82% A7% E3% 83% 83% E3% 82 % B7% E3% 83% A7% E3% 83% 8A% E3% 83% AB% E3% 82% B7% E3% 83% AA% E3% 83% BC% E3% 82% BA-% E5% 8E% 9F% E7% 94% B0-% E9% 81% 94% E4% B9% 9F / dp / 4061529129 "Reconnaissance d'image")

Screen Shot 2017-06-22 at 9.37.01.png

Vue d'ensemble

Screen Shot 2017-06-22 at 9.59.59.png

Il est grossièrement divisé en trois phases.

1: Extraction des candidats zone objet

C'est une méthode pour extraire des zones candidates d'objets de l'image. C'est la partie qui affecte la précision et la vitesse. Comme le montre la figure, il existe une méthode pour préparer une petite fenêtre (cadre de sélection) et extraire des zones candidates tout en décalant un certain nombre de pixels. Si celle-ci est décalée pour chaque pixel, il est nécessaire d'évaluer la taille d'image W * H. Par conséquent, afin de réduire ce coût de calcul, il est courant d'affiner les candidats par une méthode qui évalue la qualité d'image de l'objet.

2: Reconnaissance d'objets des candidats de zone d'objets

Vous devez être conscient de ce que vous voyez chez les candidats. Cela peut être résolu avec un problème général de classification supervisée. L'important ici est la sélection des données des enseignants. Les exemples négatifs (données erronées) seront des classificateurs qui ne peuvent résoudre que des problèmes simples à moins que vous ne sélectionniez des exemples difficiles à classer, et les performances ne seront pas pratiques. Par conséquent, il est important de le sélectionner comme exemple négatif difficile à classer. Dans cette figure, la partie où l'image entière de la vache est prise est appropriée comme exemple négatif.

3: rétrécissez la zone de détection

Même s'il n'y a qu'un seul objet cible, plusieurs zones apparaîtront. La zone de détection appropriée est déterminée en sélectionnant la partie où le score de détection n'est que la valeur maximale.

Chaque méthode

Extraction des candidats de zone d'objets Reconnaissance d'objets des candidats de zone d'objets Réduire la zone de détection
Méthode de la fenêtre coulissante(Inefficace mais simple) Fonctionnalités HOG+SVM linéaire NMS(IoU est utilisé)
Méthode de recherche sélective(Efficace) DPM(Considérez la déformation de l'objet)
Méthode de limitation de branche(Efficace) Caractéristiques du porc+ LatentSVM(Prise en compte de la position du filtre)
attentional cascade(Détection rapide des objets mais nécessite un classificateur) Exampler-SVM(Classer des objets individuels)
Caractéristiques rectangulaires+ Adaboost(Performances de classification élevées à faible coût)

Screen Shot 2017-06-22 at 10.26.32.png

Le moyen de collecter les bons cas négatifs dans la reconnaissance d'objets des candidats de zone d'objet est de stocker les cas négatifs que le classificateur a mal classés en tant que cache, et de collecter et apprendre efficacement en supprimant les candidats de cas négatifs qui pourraient être classés. Je peux.

Détection d'objets par apprentissage en profondeur

La détection d'objets par apprentissage en profondeur présente l'avantage que les fonctionnalités CNN capables d'extraire des fonctionnalités de haute qualité peuvent être utilisées par rapport à la méthode ci-dessus. Voyons en fait le flux de détection d'objets par apprentissage profond.

Désormais, la petite fenêtre est appelée boîte englobante.

R-CNN (Region)

Screen Shot 2017-06-22 at 10.55.35.png

C'est une méthode de retrait de la boîte englobante, mais la boîte englobante proposée

\vec{r} = (r_x, r_y, r_w, r_h)^T

Véritable boîte englobante

\vec{g} = (g_x, g_y, g_w, g_h)^T

Le paramètre de modèle W pour obtenir la vraie boîte englobante est résolu ci-dessous.

\vec{W} = argmin_{\vec{w}}\sum^{N}_{n=1}({\vec{t}_n - \vec{W}^Tf(\vec{r}_n)})^2 + \lambda\|\vec{W}\|^2_{F}

Valeur de la régression cible ici

\vec{t} = (t_x, t_y, t_w, t_h)^T

t utilise la vraie boîte englobante g définie précédemment et la boîte englobante proposée r

t_x = (g_x - r_x) / r_w,
t_y = (g_y - r_y) / r_h,
t_w = log(g_w / r_w),
t_h = log(g_h / r_h),

Définissez comme ci-dessus. La raison de faire ce qui précède est présumée comme suit. La raison de cette supposition est que je n'ai pas enquêté sur le fait que ce n'était pas dans le livre. Si vous êtes intéressé, veuillez le vérifier.

Puisqu'il existe différents types de tailles de boîte englobante, la position centrale est calculée par le rapport de la valeur de largeur à la valeur de différence. La valeur de largeur est calculée comme un rapport à la valeur réelle. Cependant, comme la valeur peut être extrêmement petite ou grande, l'effet est réduit par logarithme.

Il semble que nous concevons pour ce qui précède sans utiliser directement la valeur.

Fast R-CNN

R-CNN devait faire CNN pour chaque zone d'objet. Fast R-CNN diffère en ce que la fonction CNN utilise l'image entière. Étant donné que la quantité de caractéristiques CNN est différente pour chaque zone d'image recadrée à ce moment-là, elle est différente en ce sens qu'il est nécessaire de la convertir en une quantité de caractéristiques de longueur fixe par regroupement RoI.

Screen Shot 2017-06-22 at 11.24.35.png

Voir page 10 ci-dessous pour savoir qu'est-ce que la mutualisation RoI?

Introduction de l'article: Fast R-CNN & Faster R-CNN

Méthode d'apprentissage

Prenez une méthode d'optimisation de la perte multitâche pour l'apprentissage simultané de la reconnaissance de classe et revenez à la boîte englobante.

On suppose que chaque boîte englobante de réponse correcte reçoit la position de réponse correcte t et l'étiquette u utilisée lorsqu'elle est calculée par R-CNN. La perte multitâche est exprimée par la formule suivante.

Probabilité post-classe

\vec{p} = (p^0, p^1,... p^N_c)^T

Position relative et taille du cadre de sélection

\vec{v} = (v_x, v_y, v_w, v_h)^T

Basé sur ce qui précède

J(\vec{p}, u, \vec{v}, \vec{t}) = J_{cls}(\vec{p}, u) + \lambda[u >= 1]J_{loc}(\vec{v}, \vec{t})

J_cls est la perte de reconnaissance de classe et J_loc est la perte de régression de boîte englobante.

J_cls est calculé comme une logarithmique négative de la probabilité postérieure p ^ u pour la vraie classe u.

J_{cls}(\vec{p}, u) = -\log{p^u} 

J_loc est

J_{loc} = \sum_{i \in { \{x,y,w,h} \}}smooth_{L1}(t_i - v_i)
smooth_{L1}(x) = \left\{
\begin{array}{ll}
0.5x^2 & if (|x| < 1) \\
|x| - 0.5 &otherwise
\end{array}
\right.

La fonction de lissage corrige la différence de position relative afin qu'elle devienne grande lorsqu'elle est inférieure à 1, et sinon elle est soustraite de la valeur médiane de 0,5 afin qu'elle ne devienne pas une valeur extrêmement grande.

Efficacité du calcul

Comme la combinaison complète est traitée pour chaque image, le mini-lot fonctionne sur une image afin que la carte des caractéristiques puisse être utilisée efficacement. Plus précisément, N (le nombre d'images) est réduit et R (le nombre de cadres de délimitation) est augmenté.

Faster R-CNN

Dans Fast R-CNN, il était nécessaire de calculer la zone objet candidate dans un autre module (méthode de détection sélective). Faster R-CNN utilise une méthode de création d'un réseau de région qui estime la région de l'objet à partir d'une carte de caractéristiques appelée RPN et l'intègre avec Fast R-CNN.

Screen Shot 2017-06-22 at 12.10.38.png

RPN suggère une boîte englobante avec un score. Le RPN se compose d'une partie qui apprend les paramètres de la boîte englobante et d'un réseau séparé qui prédit la présence ou l'absence d'un objet, et ceci est combiné pour réaliser le RPN.

Préparez des boîtes d'ancrage K dont la forme a été décidée à l'avance. Préparez un cadre de délimitation standard centré sur la zone locale de l'entrée (cela est dérivé en calculant le bord, etc.). Cette zone est un élément hyper paramétrique. La prédiction de la boîte englobante produit un vecteur de 4k dimensions contenant la position relative et le rapport hauteur / largeur de chaque boîte d'ancrage. (x, y, w, h) * k

[Rapport hauteur / largeur](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%B9%E3%83%9A%E3%82%AF%E3%83%88%E6% AF% 94)

Puisque le réseau de classification juge la présence ou l'absence d'un objet dans deux classes, il produit un vecteur à 2k dimensions. (Oui, non) * k

Dérive le cadre de délimitation optimal en minimisant la perte multitâche similaire à Fast R-CNN. En apprenant alternativement RPN et Fast R-CNN, vous apprendrez tout le réseau de Faster R-CNN. Tout d'abord, apprenez uniquement avec RPN afin que la boîte englobante optimale puisse être dérivée, puis apprenez R-CNN, puis apprenez Fast R-CNN.

YOLO (You only look once)

Jusqu'à présent, le thème a été de trouver une bonne boîte englobante, mais il y a des tentatives pour détecter directement les objets. C'est YOLO.

Screen Shot 2017-06-22 at 12.43.34.png

Procédure YOLO

1: divise l'image d'entrée en zones S * S 2: Dérivé de la probabilité de classe des objets dans la région 3: Calculer les paramètres (x, y, h, w) et la fiabilité des boîtes englobantes B (hyper paramètres)

Degré de fiabilité

q = P_r(Obj) \times IoU^{truth}_{pred}
IoU^{truth}_{pred}

Est le degré d'accord entre la prédiction et la boîte englobante correcte. Le produit de la probabilité de classe d'objets et de la fiabilité de chaque boîte englobante est utilisé pour la détection d'objets.


P_r(C_i|Obj) \times P_r(Obj) \times IoU^{truth}_{pred}

Le réseau YOLO est le suivant.

Screen Shot 2017-06-22 at 12.57.23.png

La sortie est le nombre de cadres de délimitation et le nombre de classes, y compris la zone d'image divisée en S * S, (x, y, h, w) et la fiabilité.

La fiabilité est exprimée par la formule suivante. Mesurez le degré de correspondance de la boîte englobante.

IoU = \frac{area(R_p \bigcap R_g)}{area(R_p \bigcup R_g)}

Screen Shot 2017-06-22 at 13.02.20.png

SSD

J'aborderai également le SSD, qui n'était pas dans le livre mais qui est utile comme méthode.

・ Comparaison de vitesse

Screen Shot 2017-06-30 at 10.53.05.png

・ Comparaison de précision

La différence entre SSD512 et SSD300 est la taille de l'image d'entrée

Screen Shot 2017-06-30 at 10.54.04.png

avantage

Raison de l'avantage

comparaison de modèles

Screen Shot 2017-06-26 at 10.19.02.png

La figure ci-dessus compare le modèle de bout en bout YOLO et SSD. Dans le cas du SSD, plusieurs cartes de caractéristiques avec différents rapports d'aspect sont préparées et entrées dans la couche finale afin qu'elles puissent être appliquées même si les résolutions d'image sont différentes.

À propos de la couche de sortie

Screen Shot 2017-06-26 at 10.17.24.png

La signification de 8732 dans la figure est le nombre de cadres de délimitation. Si le nombre est grand, la précision augmentera, mais la vitesse diminuera, il y aura donc un compromis.

La couche en sortie a le nombre de classes C et de décalage (x, y, h, w) et le nombre de cadres de délimitation qui leur sont associés k. Puisqu'il est nécessaire de les préparer pour chaque carte d'entités, la taille de la carte d'entités est m * n. Dans ce cas, voici la taille de la couche de sortie.


(c+4)kmn

La fonction de perte trouvera deux points: l'écart de la position de l'objet et l'écart de la classification de la classe. N est le nombre de cadres de délimitation par défaut mis en correspondance (0 est défini sur 0 car la perte diverge à l'infini). α contrôle l'importance de l'identification de la classe d'hyperparamètres ou de la régression de décalage) Où x est 1 si la boîte de données vraies j et la boîte de données prédites i correspondent, 0 si elles ne correspondent pas (p est la classe)

x^p_{ij} = {1, 0} 

L(x,c,l,g) = 1/N(L_{conf}(x, c) + \alpha L_{loc}(x,l,g))

Fonction perte sur position

l est la position prédite

L_{loc}(x,l,g) = \sum^N_{i \in Pos}\sum_{m \in {cx, cy, w, h}} x^k_{ij} {\rm smooth_{L1}}(l^m_i-\hat{g}^m_j)
smooth_{L1}(x) = \left\{
\begin{array}{ll}
0.5x^2 & if (|x| < 1) \\
|x| - 0.5 &otherwise
\end{array}
\right.

La boîte englobante par défaut est représentée par d, la vraie boîte englobante est représentée par g et la vraie valeur est normalisée à l'échelle de la boîte englobante.


\hat{g}^{cx}_j = (g^{cx}_j - d^{cx}_i) / d^{w}_i,
\hat{g}^{cy}_j = (g^{cy}_j - d^{cy}_i) / d^{h}_i,
\hat{g}^{w}_j = \log(g^{w}_j / d^{w}_i),
\hat{g}^{h}_j = \log(g^{h}_j / d^{h}_i),

Fonction de perte pour la classe

Le premier terme représente les prédictions pour chaque classe et le deuxième terme représente les prédictions d'arrière-plan.

L_{conf}(x, c) = -\sum^N_{i \in Pos}x^p_{ij}\log(\hat{c}^p_i) -\sum^N_{i \in Neg}x^p_{ij}\log(\hat{c}^0_i)

La classification est une fonction softmax

\hat{c}^p_i = \frac{\exp(c^p_i)}{\sum_p{\exp(c^p_i)}}

Choosing scales and aspect ratios for default boxes

Étant donné que les cartes d'entités sont multi-échelles, chaque carte d'entités attribue un rôle à la taille d'objet à détecter. Plus le m est grand, plus l'échelle est petite. Cela signifie que plus le modèle est profond, plus l'objet est détecté petit.

s_k = s_{min} + \frac{s_{max} - s_{min}}{m-1}(k-1)

Définissez le rapport hauteur / largeur du cadre de sélection préparé par défaut comme suit


a_r = {1, 2, 3, 1/2, 1/3}

Calculez la largeur et la hauteur de chacun et préparez un cadre de délimitation.

largeur


w^a_k = s_k \sqrt{a_r}

la taille


h^a_k = s_k / \sqrt{a_r}

Si le rapport hauteur / largeur est 1, préparez un cadre englobant auquel l’échelle suivante est appliquée.


s_k' = \sqrt{s_ks_k+1}

Hard negative mining

Étant donné que de nombreuses boîtes englobantes négatives apparaissent, triez-les par ordre de fiabilité, ramassez-les par le haut et modifiez-les pour que le rapport soit de 3: 1 (exemple négatif: exemple positif).

Data augmentation

Exemple d'implémentation spécifique

Vous pouvez entendre les gens dire qu'ils comprennent les concepts et méthodes abstraits et comment les mettre en œuvre. Implémentez avec Keras v2.0 en vous référant au code ci-dessous.

A port of SSD: Single Shot MultiBox Detector to Keras framework.

Le code d'origine n'étant pas compatible avec la série keras 2.0, je ferai référence au code qui a été corrigé par pull request. Décrit la mise à disposition de l'environnement par Docker.

https://github.com/SnowMasaya/ssd_keras

Comprendre le modèle

Tensorflow dispose d'un outil de visualisation appelé Tensorboard, qui est utilisé pour la visualisation.

--Visualisation du modèle

Représentez graphiquement le modèle Tensorboard pour avoir une vue d'ensemble.

Couche CNN

Screen Shot 2017-06-26 at 14.33.10.png

La partie où la carte des caractéristiques est unifiée

--Offset (position): mbox_loc --Confiance: mbox_conf --Chaque cadre de délimitation: mbox_priorbox

Screen Shot 2017-06-26 at 14.33.44.png

Couche finale

Prédiction à l'aide de la carte des caractéristiques combinées

Screen Shot 2017-06-26 at 14.37.28.png

Comprendre le code spécifique

Une fois que vous aurez compris le diagramme conceptuel, vous comprendrez le traitement spécifique.

Description du modèle

ssd_v2.py

Screen Shot 2017-06-26 at 10.19.02.png

Dans ssd, différentes couches de la carte d'entités sont combinées et produites.

Ce qui suit est le processus de concaténation des couches de décalage et d'identification de classe respectivement. Étant donné que la dimension 0 est la dimension des données, la dimension de la quantité d'entités de la première dimension augmente.

        mbox_loc = concatenate([conv4_3_norm_mbox_loc_flat,
                                fc7_mbox_loc_flat,
                                conv6_2_mbox_loc_flat,
                                conv7_2_mbox_loc_flat,
                                conv8_2_mbox_loc_flat,
                                pool6_mbox_loc_flat],
                               axis=1, name='mbox_loc')
        mbox_conf = concatenate([conv4_3_norm_mbox_conf_flat,
                                 fc7_mbox_conf_flat,
                                 conv6_2_mbox_conf_flat,
                                 conv7_2_mbox_conf_flat,
                                 conv8_2_mbox_conf_flat,
                                 pool6_mbox_conf_flat],
                                axis=1, name='mbox_conf')
num_boxes = mbox_loc._keras_shape[-1] // 4

Le nombre de boîtes peut être obtenu en divisant la quantité de fonction de la position (toutes concaténées) par 4, utilisez donc cette valeur.

Les cotes concaténées ci-dessous sont modifiées selon la cote de décalage et la cote d'identification de classe.

Dimension Num_boxes: 7308 Dimension de mbox_loc: 29232 (7308 * 4) Dimension de mbox_conf: 153468 (7308 * nombre de classes (21))

        mbox_loc = Reshape((num_boxes, 4),
                           name='mbox_loc_final')(mbox_loc)
        mbox_conf = Reshape((num_boxes, num_classes),
                            name='mbox_conf_logits')(mbox_conf)

La couche de sortie est un processus qui concatène le décalage, l'identification de classe et la boîte englobante (x, y, h, w et variance de 4 coordonnées chacun). Étant donné que la 0ème dimension est la dimension des données et la 1ère dimension est la dimension du nombre de boîtes englobantes, la dimension de la quantité d'entités de la 2ème dimension augmente.

    predictions = concatenate([mbox_loc,
                               mbox_conf,
                               mbox_priorbox],
                              axis=2,
                              name='predictions')

Description du traitement requis pour SSD

ssd_utils.py configure la boîte englobante.

Liste des méthodes

--decode_boxes: conversion des prédictions de position en valeurs de boîte englobante correspondantes.

Il utilise quatre décalages de position, un décalage de boîte englobante et une distribution de boîte englobante comme arguments. La raison de l'utilisation de la distribution est que la valeur n'est pas déterminée de manière unique, il est donc possible de faire des prédictions avec une certaine plage.

1: Obtenez la position centrale, la largeur et la hauteur à partir des informations de décalage du cadre de sélection. 2: Afin d'obtenir la boîte limite à décoder, la position centrale, la largeur et la hauteur de la boîte limite décodée sont obtenues en utilisant les valeurs et la dispersion ci-dessus. La valeur prédite étant petite, elle est convertie en une valeur de taille suffisante par exp. Un point à noter est que la dispersion est prise en considération. Le point central, la largeur et la hauteur prévus sont inclus en tenant compte des probabilistes pour tenir compte des écarts de valeurs dus à la dispersion. 3: Conversion à partir de la position centrale pour les décalages minimum et maximum 4: Combinez les valeurs obtenues en un seul vecteur 5: Renvoie uniquement la zone 0 ou plus et 1 ou moins à partir de la valeur convertie.


    def decode_boxes(self, mbox_loc, mbox_priorbox, variances):
        prior_width = mbox_priorbox[:, 2] - mbox_priorbox[:, 0]
        prior_height = mbox_priorbox[:, 3] - mbox_priorbox[:, 1]
        prior_center_x = 0.5 * (mbox_priorbox[:, 2] + mbox_priorbox[:, 0])
        prior_center_y = 0.5 * (mbox_priorbox[:, 3] + mbox_priorbox[:, 1])

        decode_bbox_center_x = mbox_loc[:, 0] * prior_width * variances[:, 0]
        decode_bbox_center_x += prior_center_x
        decode_bbox_center_y = mbox_loc[:, 1] * prior_width * variances[:, 1]
        decode_bbox_center_y += prior_center_y
        decode_bbox_width = np.exp(mbox_loc[:, 2] * variances[:, 2])
        decode_bbox_width *= prior_width
        decode_bbox_height = np.exp(mbox_loc[:, 3] * variances[:, 3])
        decode_bbox_height *= prior_height

        decode_bbox_xmin = decode_bbox_center_x - 0.5 * decode_bbox_width
        decode_bbox_ymin = decode_bbox_center_y - 0.5 * decode_bbox_height
        decode_bbox_xmax = decode_bbox_center_x + 0.5 * decode_bbox_width
        decode_bbox_ymax = decode_bbox_center_y + 0.5 * decode_bbox_height

        decode_bbox = np.concatenate((decode_bbox_xmin[:, None],
                                      decode_bbox_ymin[:, None],
                                      decode_bbox_xmax[:, None],
                                      decode_bbox_ymax[:, None]), axis=-1)
       
        decode_bbox = np.minimum(np.maximum(decode_bbox, 0.0), 1.0)

        return decode_bbox

detection_out: renvoie le résultat prédit

1: Obtenez la position, la variance, la boîte englobante et la confiance à partir des valeurs prédites 2: Convertir la valeur de position en cadre de sélection 3: Si la certitude de la classe est supérieure à un certain niveau, trouvez la valeur de la boîte englobante. 4: Retournez les 200 meilleurs résultats


    def detection_out(self, predictions, background_label_id=0, keep_top_k=200,
                      confidence_threshold=0.01):

        mbox_loc = predictions[:, :, :4]
        variances = predictions[:, :, -4:]
        mbox_priorbox = predictions[:, :, -8:-4]
        mbox_conf = predictions[:, :, 4:-8]
        results = []
        for i in range(len(mbox_loc)):
            results.append([])
            
            decode_bbox = self.decode_boxes(mbox_loc[i],
                                            mbox_priorbox[i], variances[i])
            
            for c in range(self.num_classes):
                if c == background_label_id:
                    continue
                c_confs = mbox_conf[i, :, c]
                c_confs_m = c_confs > confidence_threshold
                if len(c_confs[c_confs_m]) > 0:
                    boxes_to_process = decode_bbox[c_confs_m]
                    confs_to_process = c_confs[c_confs_m]
                    feed_dict = {self.boxes: boxes_to_process,
                                 self.scores: confs_to_process}
                    idx = self.sess.run(self.nms, feed_dict=feed_dict)
                    good_boxes = boxes_to_process[idx]
                    confs = confs_to_process[idx][:, None]
                    labels = c * np.ones((len(idx), 1))
                    c_pred = np.concatenate((labels, confs, good_boxes),
                                            axis=1)
                    results[-1].extend(c_pred)
            
            if len(results[-1]) > 0:
                results[-1] = np.array(results[-1])
                argsort = np.argsort(results[-1][:, 1])[::-1]
                results[-1] = results[-1][argsort]
                results[-1] = results[-1][:keep_top_k]
        return results

ssd_layers.py définit la classe de PriorBox qui détermine la taille du cadre de sélection. Les lignes noires et rouges de la figure.

Screen Shot 2017-06-26 at 17.00.25.png

1: Obtenez la largeur et la hauteur de la carte des caractéristiques 2: Obtenez la largeur et la hauteur de l'image d'entrée 3: Ajouter la taille de la zone de délimitation en fonction du format d'image 4: Le traitement diffère selon que le rapport hauteur / largeur est de 1 ou non. 5: Définition de la position centrale de la boîte 6: Paramètres de boîte englobante minimum et maximum 7: Paramètre de distribution 8: Définir le cadre de délimitation et la distribution et retourner au format Tensorflow


class PriorBox(Layer):
    
    #réduction

    def call(self, x, mask=None):
        if hasattr(x, '_keras_shape'):
            input_shape = x._keras_shape
        elif hasattr(K, 'int_shape'):
            input_shape = K.int_shape(x)

        layer_width = input_shape[self.waxis]
        layer_height = input_shape[self.haxis]
         
        img_width = self.img_size[0]
        img_height = self.img_size[1]
        # define prior boxes shapes
        box_widths = []
        box_heights = []

        for ar in self.aspect_ratios:
            if ar == 1 and len(box_widths) == 0:
                box_widths.append(self.min_size)
                box_heights.append(self.min_size)
            elif ar == 1 and len(box_widths) > 0:
                box_widths.append(np.sqrt(self.min_size * self.max_size))
                box_heights.append(np.sqrt(self.min_size * self.max_size))
            elif ar != 1:
                box_widths.append(self.min_size * np.sqrt(ar))
                box_heights.append(self.min_size / np.sqrt(ar))
        box_widths = 0.5 * np.array(box_widths)
        box_heights = 0.5 * np.array(box_heights)

        #Obtenez la largeur du pas en divisant la taille de l'image par la taille de l'élément
        step_x = img_width / layer_width
        step_y = img_height / layer_height
        #traitement de l'espace linspace
        #     https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html
        # np.linspace(2.0, 3.0, num=5)
        #   -> array([ 2.  ,  2.25,  2.5 ,  2.75,  3.  ])
        #Obtenez des tableaux verticaux et horizontaux pour le nombre d'entités pour chaque largeur de pas
        linx = np.linspace(0.5 * step_x, img_width - 0.5 * step_x,
                           layer_width)
        liny = np.linspace(0.5 * step_y, img_height - 0.5 * step_y,
                           layer_height)
        #traitement de meshgrid
        #     https://docs.scipy.org/doc/numpy/reference/generated/numpy.meshgrid.html
        # xv, yv = np.meshgrid(x, y)
        # xv
        # -> array([[ 0. ,  0.5,  1. ],
        #          [ 0. ,  0.5,  1. ]])
        # yv
        # -> array([[ 0.,  0.,  0.],
        #          [ 1.,  1.,  1.]])
        #Faites correspondre le tableau des fonctionnalités créées précédemment
        centers_x, centers_y = np.meshgrid(linx, liny)
        centers_x = centers_x.reshape(-1, 1)
        centers_y = centers_y.reshape(-1, 1)

        num_priors_ = len(self.aspect_ratios)
        prior_boxes = np.concatenate((centers_x, centers_y), axis=1)
        prior_boxes = np.tile(prior_boxes, (1, 2 * num_priors_))
        prior_boxes[:, ::4] -= box_widths
        prior_boxes[:, 1::4] -= box_heights
        prior_boxes[:, 2::4] += box_widths
        prior_boxes[:, 3::4] += box_heights
        prior_boxes[:, ::2] /= img_width
        prior_boxes[:, 1::2] /= img_height
        prior_boxes = prior_boxes.reshape(-1, 4)
        if self.clip:
            prior_boxes = np.minimum(np.maximum(prior_boxes, 0.0), 1.0)
        num_boxes = len(prior_boxes)
        if len(self.variances) == 1:
            variances = np.ones((num_boxes, 4)) * self.variances[0]
        elif len(self.variances) == 4:
            variances = np.tile(self.variances, (num_boxes, 1))
        else:
            raise Exception('Must provide one or four variances.')
        prior_boxes = np.concatenate((prior_boxes, variances), axis=1)
        prior_boxes_tensor = K.expand_dims(K.variable(prior_boxes), 0)
        if K.backend() == 'tensorflow':
            pattern = [tf.shape(x)[0], 1, 1]
            prior_boxes_tensor = tf.tile(prior_boxes_tensor, pattern)
        return prior_boxes_tensor

Apprentissage

Le processus d'apprentissage se fait avec SSD_training.ipynb.

Etant donné que le processus d'apprentissage est effectué par "model.fit_generator", le processus de générateur comprenant l'augmentation des données est effectué par "Generator". Données de l'enseignant (étiquettes, décalages, cadres de sélection) Il est défini dans ʻassign_boxes de ssd_utils.py`.

ssd_utils.py définit le cadre de sélection.

Le cadre englobant a une valeur de décalage et une valeur de distribution pour chaque décalage

priors[i] = [xmin, ymin, xmax, ymax, varxc, varyc, varw, varh].

Liste des méthodes

--assign_boxes: n'attribue que les cases prioritaires pendant l'apprentissage --encode_box: ʻAppelée par assign_boxes pour changer la boîte englobante en un espace d'apprentissage profond --iou: ʻAppelée avec encode_box pour calculer le nombre d'intersections de boîtes englobantes

iou

Screen Shot 2017-06-27 at 13.42.35.png

1: Obtenez les coordonnées en haut à gauche et en bas à droite en utilisant la case vraie et la case prédite. 2: Calculez l'aire de chevauchement entre la vraie boîte et la boîte prédite en fonction des coordonnées obtenues. 3: Calculez la surface prédite de la boîte. 4: Calculez l'aire de la vraie boîte. 5: Soustrayez la zone intérieure de la surface totale de la vraie boîte et de la boîte prédite. 6: Divisez la zone de la partie superposée par la valeur de 5 (la zone de la partie non superposée).

La signification de 6 est que plus la zone de la partie superposée est grande, plus la zone de la partie non superposée est petite, et c'est un indice pour comprendre à quel point la boîte prédite est proche de la vraie boîte.

        inter_upleft = np.maximum(self.priors[:, :2], box[:2])
        inter_botright = np.minimum(self.priors[:, 2:4], box[2:])
        inter_wh = inter_botright - inter_upleft
        inter_wh = np.maximum(inter_wh, 0)
        inter = inter_wh[:, 0] * inter_wh[:, 1]
        # compute union
        area_pred = (box[2] - box[0]) * (box[3] - box[1])
        area_gt = (self.priors[:, 2] - self.priors[:, 0])
        area_gt *= (self.priors[:, 3] - self.priors[:, 1])
        union = area_pred + area_gt - inter
        # compute iou
        iou = inter / union
        return iou

encode_box

1: Parmi les boîtes acquises par «iou», jouez la boîte englobante avec un rapport d'intersection de 0,5 ou moins. 2: Obtenez la position centrale et la largeur de la vraie boîte Obtenez la position centrale et la largeur de la boîte prévue qui remplit la condition 3: 1 4: Préparez une boîte d'encodage pour l'apprentissage et l'utilisation 5: Dessinez la position centrale de la vraie boîte et la position centrale de la boîte prédite (vous pouvez voir la différence) Divisez la valeur de 6: 5 par la largeur de la boîte prévue (vous pouvez voir le rapport) Divisez la valeur 7: 6 par la variance prédite de la boîte 8: Divisez la largeur de la boîte codée par la largeur de la vraie boîte et la largeur de la boîte prédite pour obtenir la logarithmique. 9: Divisez la largeur de la boîte encodée par la largeur prévue de la boîte

Le traitement 8 et 9 est un processus de conversion de la fonction de perte par rapport à la position.

        iou = self.iou(box)
        encoded_box = np.zeros((self.num_priors, 4 + return_iou))
        assign_mask = iou > self.overlap_threshold
        if not assign_mask.any():
            assign_mask[iou.argmax()] = True
        if return_iou:
            encoded_box[:, -1][assign_mask] = iou[assign_mask]
        assigned_priors = self.priors[assign_mask]
        box_center = 0.5 * (box[:2] + box[2:])
        box_wh = box[2:] - box[:2]
        assigned_priors_center = 0.5 * (assigned_priors[:, :2] +
                                        assigned_priors[:, 2:4])
        assigned_priors_wh = (assigned_priors[:, 2:4] -
                              assigned_priors[:, :2])
        # we encode variance
        encoded_box[:, :2][assign_mask] = box_center - assigned_priors_center
        encoded_box[:, :2][assign_mask] /= assigned_priors_wh
        encoded_box[:, :2][assign_mask] /= assigned_priors[:, -4:-2]
        encoded_box[:, 2:4][assign_mask] = np.log(box_wh /
                                                  assigned_priors_wh)
        encoded_box[:, 2:4][assign_mask] /= assigned_priors[:, -2:]
        return encoded_box.ravel()

assign_boxes

1: Préparez une boîte englobante et des affectations initialisées avec le nombre de classes, le décalage et la variance. 2: Préparez une vraie boîte encodée 3: Obtenez uniquement la valeur maximale de la vraie boîte et de la boîte prédite 4: Obtenez uniquement l'index de la valeur maximale de la boîte vraie et de la boîte prédite Sélectionnez uniquement ceux avec un ratio de 5: 0 ou plus 6: Remplacez le décalage de la case qui code le décalage à affecter qui satisfait aux conditions ci-dessus. 7: Attribution de cours 8: Préparez des échantillons positifs à l'avance pour étudier avec des échantillons positifs et négatifs

        # 
        assignment = np.zeros((self.num_priors, 4 + self.num_classes + 8))
        assignment[:, 4] = 1.0
        if len(boxes) == 0:
            return assignment
        encoded_boxes = np.apply_along_axis(self.encode_box, 1, boxes[:, :4])
        encoded_boxes = encoded_boxes.reshape(-1, self.num_priors, 5)
        best_iou = encoded_boxes[:, :, -1].max(axis=0)
        best_iou_idx = encoded_boxes[:, :, -1].argmax(axis=0)
        best_iou_mask = best_iou > 0
        best_iou_idx = best_iou_idx[best_iou_mask]
        assign_num = len(best_iou_idx)
        encoded_boxes = encoded_boxes[:, best_iou_mask, :]
        #Attribuer des coordonnées codées
        assignment[:, :4][best_iou_mask] = encoded_boxes[best_iou_idx,
                                                         np.arange(assign_num),
                                                         :4]
        assignment[:, 4][best_iou_mask] = 0
        #Affectation de classe
        assignment[:, 5:-8][best_iou_mask] = boxes[best_iou_idx, 4:]
        #Attribution d'échantillons positifs pour l'apprentissage
        assignment[:, -8][best_iou_mask] = 1
        return assignment

ssd_training.py

La fonction de perte pour l'identification de la position et de la classe est définie dans ssd_training.py. Le premier paramètre de valeur détermine le nombre de classes, le rapport de la fonction de perte de classe à la fonction de perte de position et le rapport des exemples négatifs.

class MultiboxLoss(object):

    def __init__(self, num_classes, alpha=1.0, neg_pos_ratio=3.0,
                 background_label_id=0, negatives_for_hard=100.0):
        self.num_classes = num_classes
        self.alpha = alpha
        self.neg_pos_ratio = neg_pos_ratio
        if background_label_id != 0:
            raise Exception('Only 0 as background label id is supported')
        self.background_label_id = background_label_id
        self.negatives_for_hard = negatives_for_hard

Vous trouverez ci-dessous la fonction l1_smooth utilisée dans la fonction de perte de position.


    def _l1_smooth_loss(self, y_true, y_pred):
        abs_loss = tf.abs(y_true - y_pred)
        sq_loss = 0.5 * (y_true - y_pred)**2
        l1_loss = tf.where(tf.less(abs_loss, 1.0), sq_loss, abs_loss - 0.5)
        return tf.reduce_sum(l1_loss, -1)

Vous trouverez ci-dessous la fonction soft_max utilisée dans la fonction de perte de classe.


    def _softmax_loss(self, y_true, y_pred):
        y_pred = tf.maximum(tf.minimum(y_pred, 1 - 1e-15), 1e-15)
        softmax_loss = -tf.reduce_sum(y_true * tf.log(y_pred),
                                      axis=-1)
        return softmax_loss

La perte multi-pertes est calculée en ajoutant la fonction de perte de position et la fonction de perte d'identification de classe ci-dessous.

1: Calculer l'identification et la perte de position 2: Calculer la perte positive 3: Calculez la perte de cas négatifs et n'obtenez que ceux avec une certitude élevée 4: Calculez la perte totale des cas négatifs et positifs


    def compute_loss(self, y_true, y_pred):
        batch_size = tf.shape(y_true)[0]
        num_boxes = tf.to_float(tf.shape(y_true)[1])

        #Calculez la perte de toutes les boîtes
        conf_loss = self._softmax_loss(y_true[:, :, 4:-8],
                                       y_pred[:, :, 4:-8])
        loc_loss = self._l1_smooth_loss(y_true[:, :, :4],
                                        y_pred[:, :, :4])

        #Calculer la perte positive
        num_pos = tf.reduce_sum(y_true[:, :, -8], axis=-1)
        pos_loc_loss = tf.reduce_sum(loc_loss * y_true[:, :, -8],
                                     axis=1)
        pos_conf_loss = tf.reduce_sum(conf_loss * y_true[:, :, -8],
                                      axis=1)

        #Calculez les pertes négatives et n'obtenez que celles avec une grande confiance
        #Obtenez le nombre de cas négatifs
        num_neg = tf.minimum(self.neg_pos_ratio * num_pos,
                             num_boxes - num_pos)
        # 
        pos_num_neg_mask = tf.greater(num_neg, 0)
        has_min = tf.to_float(tf.reduce_any(pos_num_neg_mask))
        num_neg = tf.concat(axis=0, values=[num_neg,
                                [(1 - has_min) * self.negatives_for_hard]])
        num_neg_batch = tf.reduce_min(tf.boolean_mask(num_neg,
                                                      tf.greater(num_neg, 0)))
        num_neg_batch = tf.to_int32(num_neg_batch)
        confs_start = 4 + self.background_label_id + 1
        confs_end = confs_start + self.num_classes - 1
        max_confs = tf.reduce_max(y_pred[:, :, confs_start:confs_end],
                                  axis=2)
        _, indices = tf.nn.top_k(max_confs * (1 - y_true[:, :, -8]),
                                 k=num_neg_batch)
        batch_idx = tf.expand_dims(tf.range(0, batch_size), 1)
        batch_idx = tf.tile(batch_idx, (1, num_neg_batch))
        full_indices = (tf.reshape(batch_idx, [-1]) * tf.to_int32(num_boxes) +
                        tf.reshape(indices, [-1]))
        neg_conf_loss = tf.gather(tf.reshape(conf_loss, [-1]),
                                  full_indices)
        neg_conf_loss = tf.reshape(neg_conf_loss,
                                   [batch_size, num_neg_batch])
        neg_conf_loss = tf.reduce_sum(neg_conf_loss, axis=1)

        # loss is sum of positives and negatives
        total_loss = pos_conf_loss + neg_conf_loss
        total_loss /= (num_pos + tf.to_float(num_neg_batch))
        num_pos = tf.where(tf.not_equal(num_pos, 0), num_pos,
                            tf.ones_like(num_pos))
        total_loss += (self.alpha * pos_loc_loss) / num_pos
        return total_loss

Données d'entraînement

données d'image Données d'étiquette: Décalage et description de la classe

Les données d'étiquette sont décrites en xml dans le format suivant. Vous pouvez voir le libellé de la classe et le décalage.

<annotation>
        <folder>VOC2007</folder>
        <filename>000032.jpg</filename>
        <source>
                <database>The VOC2007 Database</database>
                <annotation>PASCAL VOC2007</annotation>
                <image>flickr</image>
                <flickrid>311023000</flickrid>
        </source>
        <owner>
                <flickrid>-hi-no-to-ri-mo-rt-al-</flickrid>
                <name>?</name>
        </owner>
        <size>
                <width>500</width>
                <height>281</height>
                <depth>3</depth>
        </size>
        <segmented>1</segmented>
        <object>
                <name>aeroplane</name>
                <pose>Frontal</pose>
                <truncated>0</truncated>
                <difficult>0</difficult>
                <bndbox>
                        <xmin>104</xmin>
                        <ymin>78</ymin>
                        <xmax>375</xmax>
                        <ymax>183</ymax>
                </bndbox>
        </object>
        <object>
                <name>aeroplane</name>
                <pose>Left</pose>
                <truncated>0</truncated>
                <difficult>0</difficult>
                <bndbox>
                        <xmin>133</xmin>
                        <ymin>88</ymin>
                        <xmax>197</xmax>
                        <ymax>123</ymax>
                </bndbox>
        </object>
        <object>
                <name>person</name>
                <pose>Rear</pose>
                <truncated>0</truncated>
                <difficult>0</difficult>
                <bndbox>
                        <xmin>195</xmin>
                        <ymin>180</ymin>
                        <xmax>213</xmax>
                        <ymax>229</ymax>
                </bndbox>
        </object>
        <object>
                <name>person</name>
                <pose>Rear</pose>
                <truncated>0</truncated>
                <difficult>0</difficult>
                <bndbox>
                        <xmin>26</xmin>
                        <ymin>189</ymin>
                        <xmax>44</xmax>
                        <ymax>238</ymax>
                </bndbox>
        </object>
</annotation>

Étant donné qu'une même image peut avoir plusieurs décalages, il existe autant de formats de données d'étiquettes que de cadres de délimitation. La boîte englobante est définie comme prior_boxes_ssd300.pkl. prior_box_variance représente la variance de la boîte englobante.

[xmin, ymin, xmax, ymax, binary_class_label[Dépend du nombre de classes],  prior_box_xmin, prior_box_ymin, prior_box_xmax, prior_box_ymax, prior_box_variance_xmin, prior_box_variance_ymin, prior_box_variance_xmax, prior_box_variance_ymax,]
[xmin, ymin, xmax, ymax, binary_class_label[Dépend du nombre de classes],  prior_box_xmin, prior_box_ymin, prior_box_xmax, prior_box_ymax, prior_box_variance_xmin, prior_box_variance_ymin, prior_box_variance_xmax, prior_box_variance_ymax,]

:

Comment préparer vos propres données d'entraînement

Je pense qu'il y a une demande pour préparer vous-même les données d'entraînement et les annoter. Si vous utilisez l'outil suivant, vous pouvez préparer les données annotées dans le même format xml que cette fois-ci, c'est donc recommandé.

https://github.com/tzutalin/labelImg

Cependant, je suis accro à l'installation, donc je vais vous raconter le cas auquel j'étais accro dans mon environnement.

OS: macOS Sierra 10.12.5 (16F73)

Veuillez configurer l'environnement virtuel de python! !! Il peut y avoir diverses choses, mais si vous ne le faites pas, ce sera désastreux lorsque vous deviendrez accro.

Téléchargement et installation SIP

https://riverbankcomputing.com/software/sip/download

cd  {download folder}/SIP

python configure.py
make
make install

Téléchargez et installez PyQt5

J'ai installé PyQt5 parce que je l'ai essayé dans l'environnement python3. La dernière version 5.8 ne démarre pas en raison d'un bug. Par conséquent, spécifions explicitement la version précédente et téléchargeons-la.

pip install PyQt5==5.7.1

installation de libxml

Puisque le traitement de libxml est effectué, installez-le ci-dessous. (Pour Mac)

brew install libxml2

Lors de l'enregistrement d'un fichier avec un nom japonais, des caractères déformés apparaissent, ce problème a donc été corrigé. Veuillez vérifier et corriger les éléments suivants jusqu'à ce que la demande d'extraction soit fusionnée.

https://github.com/SnowMasaya/labelImg/commit/066eb78704fb0bc551dbb5aebccd8804dae3ed9e

Modèle formé

Caffe propose un certain nombre de modèles formés. Si vous souhaitez utiliser le modèle entraîné avec Keras, vous avez besoin d'un convertisseur.

Screenshot from 2017-07-18 08:47:54.png

Vous pouvez obtenir le modèle formé de Caffe ci-dessous.

https://github.com/weiliu89/caffe/tree/ssd

Utilisez ce qui suit pour la conversion.

deploy.prototxt
*.caffemodel

deploy.protxt doit convertir la «couche d'entrée» comme ci-dessous. La pièce * change en fonction du modèle.

Avant la conversion

input: "data"
input_shape {
  dim: *
  dim: *
  dim: *
  dim: *
}

Après la conversion

layer {
  name: "input_1"
  type: "Input"
  top: "data"
  input_param {
    # These dimensions are purely for sake of example;
    # see infer.py for how to reshape the net to the given input size.
    shape { dim: * dim: * dim: * dim: * }
  }
}

référence

Creating object detection network using SSD

SSD: Single Shot MultiBox Detector (ECCV2016)

SSD: Détecteur MultiBox Single Shot Essayez une démonstration de détection d'objets en temps réel haute vitesse avec Keras

Irasutoya

A port of SSD: Single Shot MultiBox Detector to Keras framework.

Liu, Wei, et al. "Ssd: Single shot multibox detector." European conference on computer vision. Springer, Cham, 2016.

SSD: Détecteur MultiBox Single Shot Essayez une démonstration de détection d'objets en temps réel haute vitesse avec Keras

Apprenons l'algorithme de détection d'objets (SSD: Single Shot MultiBox Detector)

SSD: Single Shot MultiBox Detector

Recommended Posts

Détection d'objets par apprentissage profond pour comprendre en profondeur par Keras
Apprentissage parallèle du deep learning par Keras et Kubernetes
[Détection d'anomalies] Détecter la distorsion de l'image par apprentissage à distance
Classer les visages d'anime par suite / apprentissage profond avec Keras
Compréhension de base de l'estimation de la profondeur par caméra mono (Deep Learning)
Apprentissage profond appris par l'implémentation 1 (édition de retour)
L'apprentissage en profondeur
Deep learning 2 appris par l'implémentation (classification d'images)
Produisez de belles vaches de mer par apprentissage profond
[Apprentissage en profondeur] Détection de visage Nogisaka ~ Pour les débutants ~
Chainer et deep learning appris par approximation de fonction
Mémorandum d'apprentissage profond
Commencer l'apprentissage en profondeur
Compréhension de base de l'estimation de la profondeur stéréo (Deep Learning)
99,78% de précision avec apprentissage en profondeur en reconnaissant les hiragana manuscrits
Interpolation d'images vidéo par apprentissage en profondeur, partie 1 [Python]
Apprentissage en profondeur Python
J'ai essayé d'exécuter le didacticiel de détection d'objets en utilisant le dernier algorithme d'apprentissage en profondeur
Apprentissage profond × Python
Apprentissage profond appris par mise en œuvre (segmentation) ~ Mise en œuvre de SegNet ~
[Pour les débutants en apprentissage profond] Implémentation d'une classification binaire simple par couplage complet à l'aide de Keras
Apprentissage profond de la détection d'objets 7 articles à lire et résumé des points [Road to Efficient Det]
Investissement en actions par apprentissage approfondi (méthode du gradient de politique) (1)
Détection de valeur anormale par apprentissage non supervisé: distance Maharanobis (implémentation)
Détection d'anomalies des données de séries chronologiques par LSTM (Keras)
Analyse d'images par apprentissage profond à partir de Kaggle et Keras
Techniques pour comprendre la base des décisions d'apprentissage en profondeur
Détection de valeur anormale par apprentissage non supervisé: distance de Maharanobis (théorie)
Premier apprentissage profond ~ Lutte ~
Python: pratique du Deep Learning
Fonctions d'apprentissage en profondeur / d'activation
Apprentissage profond à partir de zéro
Deep learning 1 Pratique du deep learning
Machine Sommelier par Keras-
Première solution d'apprentissage en profondeur ~
[AI] Apprentissage métrique profond
J'ai essayé le deep learning
Python: réglage du Deep Learning
Technologie d'apprentissage en profondeur à grande échelle
Fonction d'apprentissage profond / softmax
Application de la conversion affine par tenseur - de la détection de base à la détection d'objet -
J'ai essayé d'implémenter la détection d'anomalies par apprentissage de structure clairsemée
"Apprenez en créant! Développement en deep learning par PyTorch" sur Colaboratory.
Créez une IA qui identifie le visage de Zuckerberg grâce à l'apprentissage en profondeur ③ (Apprentissage des données)
Chanson auto-exploitée par apprentissage en profondeur (édition Stacked LSTM) [DW Day 6]
Détection d'anomalies par encodeur automatique à l'aide de keras [Exemple d'implémentation pour les débutants]