Für diejenigen, die bereits tiefes Lernen verstehen und den Mechanismus von der Bildklassifizierung bis zur Objekterkennung beherrschen möchten
Da es viele Formeln gibt, gehen Sie wie folgt vor, wenn Sie den Code überprüfen möchten
[Spezifisches Implementierungsbeispiel](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)
Übersetzte ein Buch über Keras. Es deckt ein breites Spektrum von Bildidentifikation, Bilderzeugung, Verarbeitung natürlicher Sprache, Zeitreihenvorhersage und Verstärkungslernen ab. [Intuitives Deep Learning-Rezept zur Gestaltung von Ideen mit 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 = Bücher & dh = UTF8 & qid = 1530227887 & sr = 1-1 & Schlüsselwörter =% E7% 9B % B4% E6% 84% 9F + Deep + Learning)
Ich habe es geschrieben, weil ich die Technologie zur Objekterkennung systematisch zusammenfassen und die Codebasis verstehen wollte.
Dieser Artikel wird unter Bezugnahme auf das Kapitel zur Objekterkennung in der Bilderkennung erstellt, das ein gutes Buch ist.
[Bilderkennung](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 "Bilderkennung")
Es ist grob in drei Phasen unterteilt.
1: Extraktion von Objektbereichskandidaten
Es ist eine Methode, um Flächenkandidaten eines Objekts aus dem Bild zu extrahieren. Es ist der Teil, der Genauigkeit und Geschwindigkeit beeinflusst. Wie in der Abbildung gezeigt, gibt es eine Methode, um ein kleines Fenster (Begrenzungsrahmen) vorzubereiten und Flächenkandidaten zu extrahieren, während eine bestimmte Anzahl von Pixeln verschoben wird. Wenn dies für jedes Pixel verschoben wird, muss die Bildgröße W * H ausgewertet werden. Um diese Berechnungskosten zu reduzieren, ist es daher üblich, die Kandidaten durch ein Verfahren einzugrenzen, das die Bildqualität des Objekts bewertet.
2: Objekterkennung von Objektbereichskandidaten
Sie müssen wissen, was Sie in den Kandidaten sehen. Dies kann mit einem allgemeinen überwachten Klassifizierungsproblem gelöst werden. Wichtig hierbei ist die Auswahl der Lehrerdaten. Negative Beispiele (falsche Daten) sind Klassifizierer, die nur einfache Probleme lösen können, es sei denn, Sie wählen Beispiele aus, die schwer zu klassifizieren sind, und die Leistung ist nicht praktikabel. Daher ist es wichtig, es als negatives Beispiel auszuwählen, das schwer zu klassifizieren ist. In dieser Abbildung ist der Teil, in dem das gesamte Bild der Kuh aufgenommen wurde, als negatives Beispiel geeignet.
3: Grenzen Sie den Erkennungsbereich ein
Selbst wenn nur ein Zielobjekt vorhanden ist, werden mehrere Bereiche angezeigt. Der geeignete Erkennungsbereich wird durch Auswahl des Teils bestimmt, in dem die Erkennungsbewertung nur der Maximalwert ist.
Extraktion von Objektbereichskandidaten | Objekterkennung von Objektbereichskandidaten | Eingrenzen des Erkennungsbereichs |
---|---|---|
Schiebefenstermethode(Ineffizient aber einfach) | HOG-Funktionen+Lineare SVM | NMS(IoU wird verwendet) |
Selektive Suchmethode(Effizient) | DPM(Betrachten Sie die Verformung des Objekts) | |
Zweigbegrenzungsmethode(Effizient) | Schwein Features+ LatentSVM(Berücksichtigung der Filterposition) | |
attentional cascade(Schnelle Objekterkennung, erfordert jedoch einen Klassifikator) | Exampler-SVM(Klassifizieren Sie einzelne Objekte) | |
Rechteckige Merkmale+ Adaboost(Hohe Klassifizierungsleistung bei geringen Kosten) |
Der Weg, gute negative Fälle bei der Objekterkennung von Objektbereichskandidaten zu sammeln, besteht darin, die negativen Fälle zu speichern, die der Klassifizierer als Cache falsch klassifiziert hat, und effizient zu sammeln und zu lernen, indem die negativen Fallkandidaten entfernt werden, die klassifiziert werden könnten. Ich kann.
Die Objekterkennung durch tiefes Lernen hat den Vorteil, dass CNN-Merkmale, die qualitativ hochwertige Merkmale extrahieren können, im Vergleich zu dem obigen Verfahren verwendet werden können. Lassen Sie uns tatsächlich den Fluss der Objekterkennung durch tiefes Lernen sehen.
Von nun an wird das kleine Fenster als Begrenzungsrahmen bezeichnet.
R-CNN (Region)
Es ist eine Methode zum Zurückziehen des Begrenzungsrahmens, aber des vorgeschlagenen Begrenzungsrahmens
\vec{r} = (r_x, r_y, r_w, r_h)^T
Echter Begrenzungsrahmen
\vec{g} = (g_x, g_y, g_w, g_h)^T
Der Modellparameter W, um den wahren Begrenzungsrahmen zu erhalten, wird unten gelöst.
\vec{W} = argmin_{\vec{w}}\sum^{N}_{n=1}({\vec{t}_n - \vec{W}^Tf(\vec{r}_n)})^2 + \lambda\|\vec{W}\|^2_{F}
Wert für die Zielregression hier
\vec{t} = (t_x, t_y, t_w, t_h)^T
t verwendet den zuvor definierten wahren Begrenzungsrahmen g und den vorgeschlagenen Begrenzungsrahmen 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),
Definieren Sie wie oben. Der Grund für das oben Gesagte wird wie folgt angenommen. Der Grund für die Vermutung ist, dass ich nicht untersucht habe, dass es nicht in dem Buch war. Wenn Sie interessiert sind, überprüfen Sie es bitte.
Da es verschiedene Arten von Begrenzungsrahmengrößen gibt, wird die Mittelposition durch das Verhältnis des Breitenwerts zum Differenzwert berechnet. Der Breitenwert wird als Verhältnis zum wahren Wert berechnet. Da der Wert jedoch extrem klein oder groß sein kann, wird der Effekt durch den Logarithmus verringert.
Es scheint, dass wir für das Obige entwickeln, ohne den Wert direkt zu verwenden.
Fast R-CNN
R-CNN musste CNN für jeden Objektbereich ausführen. Schnelles R-CNN unterscheidet sich darin, dass die CNN-Funktion das gesamte Bild verwendet. Da der CNN-Merkmalsbetrag zu diesem Zeitpunkt für jeden zugeschnittenen Bildbereich unterschiedlich ist, ist er insofern unterschiedlich, als er durch RoI-Pooling in einen Merkmalsbetrag fester Länge konvertiert werden muss.
Auf Seite 10 unten erfahren Sie, was RoI-Pooling ist.
Artikeleinführung: Schnelles R-CNN und schnelleres R-CNN
Nehmen Sie eine Methode zur Optimierung des Multitasking-Verlusts für das gleichzeitige Lernen der Klassenerkennung und kehren Sie zum Begrenzungsrahmen zurück.
Angenommen, jeder richtige Begrenzungsrahmen erhält die richtige Position t und Bezeichnung u, die bei der Berechnung durch R-CNN verwendet wurden. Der Multitasking-Verlust wird durch die folgende Formel ausgedrückt.
Wahrscheinlichkeit nach dem Unterricht
\vec{p} = (p^0, p^1,... p^N_c)^T
Relative Position und Größe des Begrenzungsrahmens
\vec{v} = (v_x, v_y, v_w, v_h)^T
basierend auf dem oben genannten
J(\vec{p}, u, \vec{v}, \vec{t}) = J_{cls}(\vec{p}, u) + \lambda[u >= 1]J_{loc}(\vec{v}, \vec{t})
J_cls ist der Verlust der Klassenerkennung und J_loc ist der Verlust der Bounding-Box-Regression.
J_cls wird als negativer Logarithmus der posterioren Wahrscheinlichkeit p ^ u für die wahre Klasse u berechnet.
J_{cls}(\vec{p}, u) = -\log{p^u}
J_loc ist
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.
Die Glättungsfunktion korrigiert die relative Positionsdifferenz so, dass sie groß wird, wenn sie kleiner als 1 ist, und ansonsten wird sie vom Medianwert von 0,5 subtrahiert, damit sie nicht zu einem extrem großen Wert wird.
Effizienz der Berechnung
Da die vollständige Kombination für jedes Bild verarbeitet wird, arbeitet der Mini-Batch mit einem Bild, sodass die Feature-Map effizient verwendet werden kann. Insbesondere wird N (die Anzahl der Bilder) verringert und R (die Anzahl der Begrenzungsrahmen) wird erhöht.
Faster R-CNN
In Fast R-CNN war es erforderlich, den Objektbereichskandidaten in einem anderen Modul zu berechnen (selektive Erkennungsmethode). Schnelleres R-CNN verwendet eine Methode zum Erstellen eines Regionsnetzwerks, das die Objektregion aus einer Feature-Map namens RPN schätzt und in Fast R-CNN integriert.
Wir schlagen einen Begrenzungsrahmen mit einer Punktzahl von RPN vor. Die RPN besteht aus einem Teil, der die Parameter des Begrenzungsrahmens lernt, und einem separaten Netzwerk, das das Vorhandensein oder Fehlen eines Objekts vorhersagt, und dies wird kombiniert, um die RPN zu realisieren.
Bereiten Sie K Ankerkästen vor, deren Form im Voraus festgelegt wurde. Bereiten Sie einen Standard-Begrenzungsrahmen vor, der auf dem lokalen Bereich der Eingabe zentriert ist (dies wird durch Berechnung der Kante usw. abgeleitet). Dieser Bereich ist ein Hyperparameterelement. Die Begrenzungsrahmenvorhersage gibt einen 4k-dimensionalen Vektor aus, der die relative Position und das Seitenverhältnis von jedem Ankerkasten enthält. (x, y, w, h) * k
[Seitenverhältnis](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%B9%E3%83%9A%E3%82%AF%E3%83%88%E6% AF% 94)
Da das Klassifizierungsnetzwerk das Vorhandensein oder Fehlen eines Objekts in zwei Klassen beurteilt, gibt es einen 2k-dimensionalen Vektor aus. (Ja, nein) * k
Leitet den optimalen Begrenzungsrahmen ab, indem der Multitasking-Verlust ähnlich wie bei Fast R-CNN minimiert wird. Wenn Sie abwechselnd RPN und Fast R-CNN lernen, lernen Sie das gesamte Netzwerk von Faster R-CNN. Lernen Sie zuerst nur mit RPN, damit der optimale Begrenzungsrahmen abgeleitet werden kann, und lernen Sie dann R-CNN und dann Fast R-CNN.
YOLO (You only look once)
Bisher ging es darum, einen guten Begrenzungsrahmen zu finden, aber es gibt Versuche, Objekte direkt zu erkennen. Das ist YOLO.
YOLO-Verfahren
1: Teilen Sie das Eingabebild in S * S-Bereiche 2: Abgeleitet die Klassenwahrscheinlichkeit von Objekten in der Region 3: Berechnen Sie die Parameter (x, y, h, w) und die Zuverlässigkeit von B-Begrenzungsrahmen (Hyperparameter).
Grad der Zuverlässigkeit
q = P_r(Obj) \times IoU^{truth}_{pred}
IoU^{truth}_{pred}
Ist der Grad der Übereinstimmung zwischen der Vorhersage und dem richtigen Begrenzungsrahmen. Das Produkt aus der Objektklassenwahrscheinlichkeit und der Zuverlässigkeit jedes Begrenzungsrahmens wird zur Objekterkennung verwendet.
P_r(C_i|Obj) \times P_r(Obj) \times IoU^{truth}_{pred}
Das YOLO-Netzwerk ist wie folgt.
Die Ausgabe ist die Anzahl der Begrenzungsrahmen und die Anzahl der Klassen, einschließlich des Bildbereichs, unterteilt in S * S, (x, y, h, w) und Zuverlässigkeit.
Die Zuverlässigkeit wird durch die folgende Formel ausgedrückt. Messen Sie die Übereinstimmung des Begrenzungsrahmens.
IoU = \frac{area(R_p \bigcap R_g)}{area(R_p \bigcup R_g)}
SSD
Ich werde auch auf SSD eingehen, die nicht im Buch enthalten war, aber als Methode nützlich ist.
・ Geschwindigkeitsvergleich
・ Genauigkeitsvergleich
Der Unterschied zwischen SSD512 und SSD300 ist die Größe des Eingabebildes
Vorteil
Grund für den Vorteil
Die obige Abbildung vergleicht das End-to-End-Modell YOLO und SSD. Bei SSD werden mehrere Feature-Maps mit unterschiedlichen Seitenverhältnissen erstellt und in die endgültige Ebene eingegeben, sodass sie auch bei unterschiedlichen Bildauflösungen angewendet werden können.
Die Bedeutung von 8732 in der Abbildung ist die Anzahl der Begrenzungsrahmen. Wenn die Anzahl groß ist, erhöht sich die Genauigkeit, aber die Geschwindigkeit nimmt ab, sodass ein Kompromiss entsteht.
Die Ausgabeschicht hat die Anzahl der Klassen C und den Versatz (x, y, h, w) und die Anzahl der ihnen zugeordneten Begrenzungsrahmen k. Da sie für jede Feature-Map vorbereitet werden müssen, beträgt die Größe der Feature-Map m * n. In diesem Fall ist das Folgende die Größe der Ausgabeebene.
(c+4)kmn
Die Verlustfunktion findet zwei Punkte: die Abweichung der Position des Objekts und die Abweichung der Klassifizierung der Klasse. N ist die Anzahl der übereinstimmenden Standardbegrenzungsrahmen (0 wird auf 0 gesetzt, da der Verlust gegen unendlich abweicht). α steuert die Bedeutung der Identifizierung von Hyperparameterklassen oder der Offset-Regression) Wobei x 1 ist, wenn die Box der wahren Daten j und die Box der vorhergesagten Daten i übereinstimmen, 0, wenn sie nicht übereinstimmen (p ist die Klasse)
x^p_{ij} = {1, 0}
L(x,c,l,g) = 1/N(L_{conf}(x, c) + \alpha L_{loc}(x,l,g))
Verlustfunktion auf Position
l ist die vorhergesagte Position
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.
Der Standardbegrenzungsrahmen wird durch d dargestellt, der wahre Begrenzungsrahmen wird durch g dargestellt und der wahre Wert wird auf die Skala des Begrenzungsrahmens normiert.
\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),
Verlustfunktion für die Klasse
Der erste Term repräsentiert die Vorhersagen für jede Klasse und der zweite Term repräsentiert die Hintergrundvorhersagen.
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)
Die Klassifizierung ist eine Softmax-Funktion
\hat{c}^p_i = \frac{\exp(c^p_i)}{\sum_p{\exp(c^p_i)}}
Choosing scales and aspect ratios for default boxes
Da die Feature-Maps mehrskalig sind, gibt jede Feature-Map dem zu erkennenden Objekt eine Rolle. Je größer m, desto kleiner der Maßstab. Dies bedeutet, dass das Objekt umso kleiner erkannt wird, je tiefer das Modell ist.
s_k = s_{min} + \frac{s_{max} - s_{min}}{m-1}(k-1)
Stellen Sie das Seitenverhältnis des standardmäßig vorbereiteten Begrenzungsrahmens wie folgt ein
a_r = {1, 2, 3, 1/2, 1/3}
Berechnen Sie jeweils die Breite und Höhe und bereiten Sie einen Begrenzungsrahmen vor.
Breite
w^a_k = s_k \sqrt{a_r}
Höhe
h^a_k = s_k / \sqrt{a_r}
Wenn das Seitenverhältnis 1 ist, bereiten Sie einen Begrenzungsrahmen vor, auf den die folgende Skala angewendet wird.
s_k' = \sqrt{s_ks_k+1}
Hard negative mining
Da viele negative Begrenzungsrahmen angezeigt werden, sortieren Sie sie in der Reihenfolge ihrer Zuverlässigkeit, nehmen Sie sie von oben auf und ändern Sie sie so, dass das Verhältnis 3: 1 beträgt (negatives Beispiel: positives Beispiel).
Data augmentation
Sie können Leute sagen hören, dass sie die abstrakten Konzepte und Methoden verstehen und wie sie implementiert werden. Implementieren Sie mit Keras v2.0 anhand des folgenden Codes.
A port of SSD: Single Shot MultiBox Detector to Keras framework.
Da der ursprüngliche Code nicht mit der Keras 2.0-Serie kompatibel ist, verweise ich auf den Code, der durch Pull-Anfrage korrigiert wurde. Beschrieb die Umgebungsbereitstellung durch Docker.
https://github.com/SnowMasaya/ssd_keras
Tensorflow verfügt über ein Visualisierungstool namens Tensorboard, das zur Visualisierung verwendet wird.
Stellen Sie das Tensorboard-Modell grafisch dar, um das Gesamtbild zu erhalten.
--Offset (Position): mbox_loc
mbox_conf
--Jedes Begrenzungsfeld: mbox_priorbox
Wird mithilfe der kombinierten Feature-Map vorhergesagt
Nachdem Sie das konzeptionelle Diagramm verstanden haben, verstehen Sie die spezifische Verarbeitung.
ssd_v2.py
In ssd werden verschiedene Ebenen der Feature-Map kombiniert und ausgegeben.
Das Folgende ist der Prozess der Verkettung der Versatz- bzw. Klassenidentifikationsschichten. Da die 0. Dimension die Dimension von Daten ist, nimmt die Dimension der Merkmalsmenge der 1. Dimension zu.
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
Die Anzahl der Felder kann ermittelt werden, indem der Merkmalsbetrag der Position (alle verkettet) durch 4 geteilt wird. Verwenden Sie also diesen Wert.
Die unten verketteten Dimensionen werden in die Versatzdimension und die Klassenidentifikationsdimension geändert.
Num_boxes-Dimension: 7308 Dimension von mbox_loc: 29232 (7308 * 4) Dimension von mbox_conf: 153468 (7308 * Anzahl der Klassen (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)
In der Ausgabeebene ist dies ein Prozess, der Offset, Klassenidentifikation und Begrenzungsrahmen (x, y, h, w und Varianz von jeweils 4 Koordinaten) verkettet. Da die 0. Dimension die Dimension von Daten und die 1. Dimension die Dimension der Anzahl der Begrenzungsrahmen ist, nimmt die Dimension des Merkmalsbetrags der 2. Dimension zu.
predictions = concatenate([mbox_loc,
mbox_conf,
mbox_priorbox],
axis=2,
name='predictions')
ssd_utils.py konfiguriert den Begrenzungsrahmen.
Methodenliste
--decode_boxes: Konvertieren von Positionsvorhersagen in übereinstimmende Begrenzungsrahmenwerte.
Als Argumente werden vier Positionsversätze, ein Begrenzungsrahmenversatz und eine Begrenzungsrahmenverteilung verwendet. Der Grund für die Verwendung der Verteilung ist, dass der Wert nicht eindeutig bestimmt wird, sodass Vorhersagen mit einem bestimmten Bereich möglich sind.
1: Ermitteln Sie die mittlere Position, Breite und Höhe aus den Versatzinformationen des Begrenzungsrahmens. 2: Um die zu decodierende Grenzbox zu erhalten, werden die Mittelposition, Breite und Höhe der decodierten Grenzbox unter Verwendung der obigen Werte und der Dispersion erhalten. Da der vorhergesagte Wert klein ist, wird er durch exp in einen Wert von ausreichender Größe umgewandelt. Es ist zu beachten, dass die Dispersion berücksichtigt wird. Der vorhergesagte Mittelpunkt, die Breite und die Höhe werden unter Berücksichtigung der Wahrscheinlichkeit berücksichtigt, um Abweichungen der Werte aufgrund der Streuung zu berücksichtigen. 3: Konvertieren Sie von der Mittelposition für minimale und maximale Offsets 4: Kombinieren Sie die erhaltenen Werte zu einem Vektor 5: Geben Sie nur den Bereich 0 oder mehr und 1 oder weniger vom konvertierten Wert zurück.
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: Gibt das vorhergesagte Ergebnis zurück
1: Holen Sie sich Position, Varianz, Begrenzungsrahmen und Vertrauen aus vorhergesagten Werten 2: Konvertieren Sie den Positionswert in einen Begrenzungsrahmen 3: Wenn die Sicherheit der Klasse über einem bestimmten Niveau liegt, ermitteln Sie den Wert des Begrenzungsrahmens. 4: Geben Sie die Top 200 Ergebnisse zurück
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
legt die Klasse von PriorBox
fest, die die Größe des Begrenzungsrahmens bestimmt. Die schwarzen und roten Linien in der Abbildung.
1: Ermitteln Sie die Breite und Höhe der Feature-Map 2: Ermitteln Sie die Breite und Höhe des Eingabebildes 3: Fügen Sie die Größe des Begrenzungsrahmens dem Seitenverhältnis hinzu 4: Die Verarbeitung hängt davon ab, ob das Seitenverhältnis 1 beträgt oder nicht. 5: Definition der Mittelposition der Box 6: Minimale und maximale Begrenzungsrahmeneinstellungen 7: Verteilungseinstellung 8: Begrenzungsrahmen und Verteilung festlegen und im Tensorflow-Format zurückgeben
class PriorBox(Layer):
#Kürzung
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)
#Ermitteln Sie die Schrittweite, indem Sie die Bildgröße durch die Feature-Größe dividieren
step_x = img_width / layer_width
step_y = img_height / layer_height
#Linspace-Verarbeitung
# 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. ])
#Holen Sie sich vertikale und horizontale Arrays für die Anzahl der Features für jede Schrittweite
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)
#Verarbeitung von 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.]])
#Passen Sie das zuvor erstellte Feature-Array an
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
Der Lernprozess wird mit SSD_training.ipynb
durchgeführt.
Da der Lernprozess von "model.fit_generator" ausgeführt wird, wird der Generatorprozess einschließlich der Datenerweiterung von "Generator" ausgeführt.
Lehrerdaten (Beschriftungen, Versätze, Begrenzungsrahmen)
Es wird in assign_boxes
von ssd_utils.py
gesetzt.
ssd_utils.py
legt den Begrenzungsrahmen fest.
Der Begrenzungsrahmen hat für jeden Versatz einen Versatzwert und einen Verteilungswert
priors[i] = [xmin, ymin, xmax, ymax, varxc, varyc, varw, varh].
Methodenliste
--assign_boxes: Weisen Sie während des Lernens nur Prioritätsfelder zu
--encode_box: Der Prozess des Änderns des Begrenzungsrahmens in einen tiefen Lernraum, der von "assign_boxes" aufgerufen wird
--iou: Berechnen Sie die Anzahl der von encode_box
aufgerufenen Bounding-Box-Schnittpunkte
iou
1: Ermitteln Sie die Koordinaten oben links und unten rechts mithilfe des True-Felds und des vorhergesagten Felds. 2: Berechnen Sie den Bereich der Überlappung zwischen der wahren Box und der vorhergesagten Box basierend auf den erhaltenen Koordinaten. 3: Berechnen Sie die vorhergesagte Boxfläche. 4: Berechnen Sie die Fläche der True Box. 5: Subtrahieren Sie den inneren Bereich von der Gesamtfläche der wahren Box und der vorhergesagten Box. 6: Teilen Sie die Fläche des überlappenden Teils durch den Wert 5 (die Fläche des nicht überlappenden Teils).
Die Bedeutung von 6 ist, dass je größer die Fläche des überlappenden Teils ist, desto kleiner die Fläche des nicht überlappenden Teils ist und es ein Index ist, um zu verstehen, wie nahe die vorhergesagte Box an der wahren Box liegt.
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: Spielen Sie unter den von "iou" erworbenen Feldern den Begrenzungsrahmen mit einem Schnittverhältnis von 0,5 oder weniger. 2: Ermitteln Sie die Mittelposition und Breite der True Box Ermitteln Sie die Mittelposition und Breite der vorhergesagten Box, die die 3: 1-Bedingung erfüllt 4: Bereiten Sie eine Encode-Box zum Lernen und Verwenden vor 5: Zeichnen Sie die Mittelposition der wahren Box und die Mittelposition der vorhergesagten Box (Sie können sehen, wie groß der Unterschied ist). Teilen Sie den Wert 6: 5 durch die Breite des vorhergesagten Felds (Sie können das Verhältnis sehen). Teilen Sie den 7: 6-Wert durch die vorhergesagte Box-Varianz 8: Teilen Sie die Breite der codierten Box durch die Breite der wahren Box und die Breite der vorhergesagten Box, um den Logarithmus zu erhalten. 9: Teilen Sie die Breite der codierten Box durch die vorhergesagte Breite der Box
Die Verarbeitung 8 und 9 ist ein Umwandlungsprozess für die Verlustfunktion in Bezug auf die 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: Bereiten Sie einen Begrenzungsrahmen und Zuweisungen vor, die mit der Anzahl der Klassen, dem Versatz und der Varianz initialisiert wurden. 2: Bereiten Sie eine codierte True Box vor 3: Holen Sie sich nur den Maximalwert der True Box und der vorhergesagten Box 4: Holen Sie sich nur den Index des Maximalwerts der wahren Box und der vorhergesagten Box Wählen Sie nur solche mit einem Verhältnis von 5: 0 oder mehr aus 6: Ersetzen Sie den Versatz des Felds, das den zuzuweisenden Versatz codiert, der die obigen Bedingungen erfüllt. 7: Klassenzuordnung 8: Bereiten Sie positive Proben im Voraus vor, um mit positiven und negativen Proben zu studieren
#
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, :]
#Weisen Sie codierte Koordinaten zu
assignment[:, :4][best_iou_mask] = encoded_boxes[best_iou_idx,
np.arange(assign_num),
:4]
assignment[:, 4][best_iou_mask] = 0
#Klassenzuordnung
assignment[:, 5:-8][best_iou_mask] = boxes[best_iou_idx, 4:]
#Zuordnung positiver Stichproben zum Lernen
assignment[:, -8][best_iou_mask] = 1
return assignment
ssd_training.py
Die Verlustfunktion zur Positions- und Klassenidentifikation ist in ssd_training.py
eingestellt.
Die erste Werteinstellung bestimmt die Anzahl der Klassen, das Verhältnis der Klassenverlustfunktion zur Positionsverlustfunktion und das Verhältnis der negativen Beispiele.
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
Unten ist die Funktion l1_smooth
aufgeführt, die in der Positionsverlustfunktion verwendet wird.
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)
Unten ist die Funktion soft_max
aufgeführt, die in der Klassenverlustfunktion verwendet wird.
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
Der Mehrfachverlust wird berechnet, indem die Positionsverlustfunktion und die Klassenidentifikationsverlustfunktion unten hinzugefügt werden.
1: Berechnen Sie die Identifikation und den Positionsverlust 2: Berechnen Sie den positiven Verlust 3: Berechnen Sie den Verlust negativer Fälle und erhalten Sie nur solche mit hoher Sicherheit 4: Berechnen Sie den Gesamtverlust von negativen und positiven Fällen
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])
#Berechnen Sie den Verlust aller Boxen
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])
#Berechnen Sie den positiven Verlust
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)
#Berechnen Sie negative Verluste und erhalten Sie nur solche mit hohem Vertrauen
#Ermitteln Sie die Anzahl der negativen Fälle
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
Bilddaten Etikettendaten: Versatz und Klassenbeschreibung
Etikettendaten werden in XML im folgenden Format beschrieben. Sie können die Klassenbezeichnung und den Versatz sehen.
<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>
Da auch ein Bild mehrere Offsets haben kann, gibt es so viele Etikettendatenformate wie Begrenzungsrahmen.
Der Begrenzungsrahmen ist als "prior_boxes_ssd300.pkl" definiert.
prior_box_variance
repräsentiert die Bounding-Box-Varianz.
[xmin, ymin, xmax, ymax, binary_class_label[Hängt von der Anzahl der Klassen ab], 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[Hängt von der Anzahl der Klassen ab], 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,]
:
Ich denke, es gibt eine Bitte, Trainingsdaten selbst vorzubereiten und zu kommentieren. Wenn Sie das folgende Tool verwenden, können Sie die mit Anmerkungen versehenen Daten im selben XML-Format wie dieses Mal vorbereiten. Es wird daher empfohlen.
https://github.com/tzutalin/labelImg
Ich bin jedoch süchtig nach der Installation, daher werde ich Ihnen den Fall mitteilen, von dem ich in meiner Umgebung abhängig war.
OS: macOS Sierra 10.12.5 (16F73)
Bitte richten Sie die virtuelle Umgebung von Python ein! !! Es kann verschiedene Dinge geben, aber wenn Sie dies nicht tun, wird es katastrophal sein, wenn Sie süchtig werden.
https://riverbankcomputing.com/software/sip/download
cd {download folder}/SIP
python configure.py
make
make install
Ich habe PyQt5 installiert, weil ich es in einer Python3-Umgebung versucht habe. Die neueste Version 5.8 wird aufgrund eines Fehlers nicht gestartet. Geben Sie daher die vorherige Version explizit an und laden Sie sie herunter.
pip install PyQt5==5.7.1
Da die libxml-Verarbeitung ausgeführt wird, installieren Sie sie unten. (Für Mac)
brew install libxml2
Beim Speichern einer Datei mit einem japanischen Namen werden verstümmelte Zeichen angezeigt, sodass dies behoben wurde. Bitte überprüfen und beheben Sie Folgendes, bis die Pull-Anforderung zusammengeführt wird.
https://github.com/SnowMasaya/labelImg/commit/066eb78704fb0bc551dbb5aebccd8804dae3ed9e
Caffe bietet eine Reihe von ausgebildeten Modellen an. Wenn Sie das trainierte Modell mit Keras verwenden möchten, benötigen Sie einen Konverter.
Sie können das trainierte Modell von Caffe unten erhalten.
https://github.com/weiliu89/caffe/tree/ssd
Verwenden Sie für die Konvertierung Folgendes.
deploy.prototxt
*.caffemodel
deploy.protxt muss die Eingabeebene wie folgt konvertieren. Der * Teil hängt vom Modell ab.
Vor der Konvertierung
input: "data"
input_shape {
dim: *
dim: *
dim: *
dim: *
}
Nach der Konvertierung
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.
Lernen wir den Objekterkennungsalgorithmus (SSD: Single Shot MultiBox Detector)
SSD: Single Shot MultiBox Detector
Recommended Posts