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)
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)
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")
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.
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) |
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.
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)
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.
Voir page 10 ci-dessous pour savoir qu'est-ce que la mutualisation RoI?
Introduction de l'article: Fast R-CNN & Faster R-CNN
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.
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.
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.
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)}
SSD
J'aborderai également le SSD, qui n'était pas dans le livre mais qui est utile comme méthode.
・ Comparaison de vitesse
・ Comparaison de précision
La différence entre SSD512 et SSD300 est la taille de l'image d'entrée
avantage
Raison de l'avantage
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.
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
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
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.
--Offset (position): mbox_loc
--Confiance: mbox_conf
--Chaque cadre de délimitation: mbox_priorbox
Prédiction à l'aide de la carte des caractéristiques combinées
Une fois que vous aurez compris le diagramme conceptuel, vous comprendrez le traitement spécifique.
ssd_v2.py
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')
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.
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
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
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'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,]
:
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.
https://riverbankcomputing.com/software/sip/download
cd {download folder}/SIP
python configure.py
make
make install
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
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
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.
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: * }
}
}
Creating object detection network using SSD
SSD: Single Shot MultiBox Detector (ECCV2016)
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.
Apprenons l'algorithme de détection d'objets (SSD: Single Shot MultiBox Detector)
SSD: Single Shot MultiBox Detector
Recommended Posts