Spickzettel für künstliche Intelligenz - werfen Sie den Überschuss weg, lehren Sie die Hauptsache. Trainingssequenz-Verarbeitungstechnik

Dies ist der zweite Artikel über die Analyse und Untersuchung von Materialien aus dem Wettbewerb um die Suche nach Schiffen auf See. Aber jetzt werden wir die Eigenschaften von Trainingssequenzen untersuchen. Versuchen wir, überschüssige Informationen und Redundanz in den Quelldaten zu finden und zu löschen.



Dieser Artikel ist auch einfach das Ergebnis von Neugier und müßigem Interesse, nichts davon ist in der Praxis anzutreffen, und für praktische Aufgaben gibt es fast nichts zum Kopieren und Einfügen. Dies ist eine kleine Studie über die Eigenschaften der Trainingssequenz. Die Argumentation und der Code des Autors werden vorgestellt. Sie können alles selbst überprüfen / ergänzen / ändern.

Der Kaggle Marine Search Wettbewerb ist vor kurzem beendet. Airbus schlug vor, Satellitenbilder des Meeres mit und ohne Schiffe zu analysieren. Insgesamt 192555 Bilder 768x768x3 - sind 340 720 680 960 Bytes, wenn uint8 und dies ist eine riesige Menge an Informationen, und es bestand der vage Verdacht, dass nicht alle Bilder für das Training des Netzwerks benötigt werden, und bei dieser Menge an Informationen sind die Wiederholung und Redundanz offensichtlich. Beim Training eines Netzwerks ist es üblich, einige der Daten zu trennen und nicht für das Training zu verwenden, sondern die Qualität des Trainings zu überprüfen. Und wenn ein und derselbe Abschnitt des Meeres in zwei verschiedene Bilder und gleichzeitig ein Bild in die Trainingssequenz und das andere in die Verifizierungssequenz fiel, verliert die Verifikation ihre Bedeutung und das Netzwerk wird umgeschult. Wir werden die Fähigkeit des Netzwerks zur Verallgemeinerung von Informationen nicht überprüfen, da die Daten dieselben sind. Der Kampf gegen dieses Phänomen hat die GPU der Teilnehmer viel Zeit und Mühe gekostet. Wie üblich haben es Gewinner und Preisträger nicht eilig, ihren Fans die Geheimnisse der Meisterschaft zu zeigen und den Code zu entwerfen, und es gibt keine Möglichkeit, ihn zu studieren und zu lernen, also werden wir die Theorie aufgreifen.

Eine einfache visuelle Überprüfung ergab, dass es wirklich zu viele Daten gibt, derselbe Abschnitt des Meeres in verschiedene Bilder zerfiel, siehe Beispiele









Aus diesem Grund sind wir nicht an realen Daten interessiert, es gibt viele falsche Abhängigkeiten, unnötige Verbindungen zu uns, schlechtes Markup und andere Mängel.

Im ersten Artikel haben wir uns Bilder mit Ellipsen und Rauschen angesehen und werden sie weiter untersuchen. Der Vorteil dieses Ansatzes besteht darin, dass, wenn Sie ein attraktives Merkmal eines Netzwerks finden, das auf einem beliebigen Satz von Bildern trainiert wurde, nicht klar ist, ob dies eine Netzwerkeigenschaft oder eine Eigenschaft eines Trainingssatzes ist. Die statistischen Parameter von Sequenzen aus der realen Welt sind unbekannt. Vor kurzem sprach Großmeister Pleskov Pavel paske57 darüber, wie einfach es manchmal ist, eine Segmentierungs- / Klassifizierungsklassifizierung von Bildern zu gewinnen, wenn es gut ist, sich selbst mit den Daten zu befassen, siehe beispielsweise Fotometadaten. Und es gibt keine Garantie dafür, dass es in den realen Daten keine solchen Abhängigkeiten gibt, die unfreiwillig verbleiben. Um die Eigenschaften des Netzwerks zu untersuchen, machen wir Bilder mit Ellipsen und Rechtecken und bestimmen den Ort und die Farbe sowie andere Parameter mithilfe eines Zufallszahlengenerators eines Computers (der einen Pseudozufallsgenerator hat, der einen Generator hat, der auf anderen nicht digitalen Algorithmen und physikalischen Eigenschaften der Substanz basiert). Wir werden dies jedoch in diesem Artikel nicht diskutieren.

Nehmen Sie also das Meer np.random.sample () * 0.75 , wir brauchen keine Wellen, Wind, Küsten und andere versteckte Muster und Gesichter. Die Schiffe / Ellipsen werden ebenfalls in der gleichen Farbe lackiert. Um das Meer vom Boot und von Störungen zu unterscheiden, fügen Sie dem Meer oder dem Boot / Störsender 0,25 hinzu, und sie haben alle die gleiche Form - Ellipsen unterschiedlicher Größe und Ausrichtung. Die Interferenz wird auch nur Rechtecke der gleichen Farbe wie die Ellipse sein - dies ist wichtig, Information und Interferenz der gleichen Farbe vor dem Hintergrund des Rauschens. Wir werden nur eine kleine Änderung an der Farbgebung vornehmen und np.random.sample () für jedes Bild und für jede Ellipse / jedes Rechteck ausführen , d. H. Weder der Hintergrund noch die Farbe der Ellipse / des Rechtecks ​​werden wiederholt. Weiter im Text gibt es einen Code des Programms zum Erstellen von Bildern / Masken und ein Beispiel von zehn zufällig ausgewählten Paaren.

Nehmen Sie eine sehr verbreitete Version des Netzwerks (Sie können Ihr Lieblingsnetzwerk verwenden) und versuchen Sie, die Redundanz einer großen Trainingssequenz zu identifizieren und anzuzeigen, um zumindest einige qualitative und quantitative Merkmale der Redundanz zu erhalten. Das heißt, Der Autor glaubt, dass viele Gigabyte an Trainingssequenzen im Wesentlichen redundant sind, es gibt viele unnötige Bilder, es ist nicht erforderlich, Dutzende von GPUs zu laden und unnötige Berechnungen durchzuführen. Die Redundanz von Daten manifestiert sich nicht nur und nicht so sehr in der Tatsache, dass dieselben Teile in unterschiedlichen Bildern angezeigt werden, sondern auch in der Redundanz von Informationen in diesen Daten. Daten können redundant sein, auch wenn sie nicht genau wiederholt werden. Bitte beachten Sie, dass dies keine strikte Definition von Informationen und deren Angemessenheit oder Redundanz ist. Wir möchten nur herausfinden, um wie viel Sie den Zug reduzieren können, welche Bilder Sie aus der Trainingssequenz herauswerfen können und wie viele Bilder für ein akzeptables Training (wir werden die Genauigkeit selbst im Programm festlegen) ausreichen. Dies ist ein spezifisches Programm, ein spezifischer Datensatz, und es ist möglich, dass auf Ellipsen mit Dreiecken als Hindernis nichts so gut funktioniert wie auf Ellipsen mit Rechtecken (meine Hypothese ist, dass alles gleich und gleich sein wird. Aber wir überprüfen es jetzt nicht , wir führen keine Analyse durch und beweisen keine Theoreme).

Also gegeben:

  • Lernsequenz von Bild / Masken-Paaren. Wir können beliebig viele Bilder / Maskenpaare erzeugen. Ich werde die Frage sofort beantworten - warum sind Farbe und Hintergrund zufällig? Ich werde einfach, kurz, klar und umfassend antworten, dass es mir so gut gefällt, dass eine zusätzliche Einheit in Form einer Grenze nicht benötigt wird.
  • Das Netzwerk ist ein gewöhnliches, gewöhnliches U-Netz, leicht modifiziert und wird häufig zur Segmentierung verwendet.

Idee zu testen:

  • In der konstruierten Sequenz werden wie bei realen Aufgaben Gigabyte an Daten verwendet. Der Autor ist der Ansicht, dass die Größe der Trainingssequenz nicht so kritisch ist und es nicht viele Daten geben sollte, aber sie sollten „viele“ Informationen enthalten. Eine solche Menge, zehntausend Paar Bilder / Masken, ist nicht erforderlich, und das Netzwerk lernt aus einer viel geringeren Datenmenge.

Beginnen wir, wählen Sie 10.000 Paare aus und betrachten Sie sie sorgfältig. Wir werden das gesamte Wasser und alle unnötigen Teile dieser Trainingssequenz auspressen und alle trockenen Rückstände verwenden und in die Praxis umsetzen.

Sie können jetzt Ihre Intuition überprüfen und davon ausgehen, wie viele Paare von 10.000 ausreichen, um ein anderes zu trainieren und vorherzusagen, aber auch eine Folge von 10.000 Paaren mit einer Genauigkeit von mehr als 0,98 erstellen. Schreiben Sie nach dem Vergleich auf ein Blatt Papier.

Beachten Sie für den praktischen Gebrauch, dass sowohl das Meer als auch die Schiffe mit Störungen künstlich ausgewählt werden. Dies ist np.random.sample () .

Wir laden Bibliotheken, wir bestimmen die Größe eines Arrays von Bildern
import numpy as np import matplotlib.pyplot as plt %matplotlib inline import math from tqdm import tqdm from skimage.draw import ellipse, polygon from keras import Model from keras.optimizers import Adam from keras.layers import Input,Conv2D,Conv2DTranspose,MaxPooling2D,concatenate from keras.layers import BatchNormalization,Activation,Add,Dropout from keras.losses import binary_crossentropy from keras import backend as K import tensorflow as tf import keras as keras w_size = 128 train_num = 10000 radius_min = 10 radius_max = 20 


Bestimmen Sie die Verlust- und Genauigkeitsfunktionen
 def dice_coef(y_true, y_pred): y_true_f = K.flatten(y_true) y_pred = K.cast(y_pred, 'float32') y_pred_f = K.cast(K.greater(K.flatten(y_pred), 0.5), 'float32') intersection = y_true_f * y_pred_f score = 2. * K.sum(intersection) / (K.sum(y_true_f) + K.sum(y_pred_f)) return score def dice_loss(y_true, y_pred): smooth = 1. y_true_f = K.flatten(y_true) y_pred_f = K.flatten(y_pred) intersection = y_true_f * y_pred_f score = (2. * K.sum(intersection) + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth) return 1. - score def bce_dice_loss(y_true, y_pred): return binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred) def get_iou_vector(A, B): # Numpy version batch_size = A.shape[0] metric = 0.0 for batch in range(batch_size): t, p = A[batch], B[batch] true = np.sum(t) pred = np.sum(p) # deal with empty mask first if true == 0: metric += (pred == 0) continue # non empty mask case. Union is never empty # hence it is safe to divide by its number of pixels intersection = np.sum(t * p) union = true + pred - intersection iou = intersection / union # iou metrric is a stepwise approximation of the real iou over 0.5 iou = np.floor(max(0, (iou - 0.45)*20)) / 10 metric += iou # teake the average over all images in batch metric /= batch_size return metric def my_iou_metric(label, pred): # Tensorflow version return tf.py_func(get_iou_vector, [label, pred > 0.5], tf.float64) from keras.utils.generic_utils import get_custom_objects get_custom_objects().update({'bce_dice_loss': bce_dice_loss }) get_custom_objects().update({'dice_loss': dice_loss }) get_custom_objects().update({'dice_coef': dice_coef }) get_custom_objects().update({'my_iou_metric': my_iou_metric }) 


Wir werden die Metrik aus dem ersten Artikel verwenden . Ich erinnere die Leser daran, dass wir die Maske des Pixels vorhersagen werden - dies ist das "Meer" oder "Schiff" - und die Wahrheit oder Falschheit der Vorhersage bewerten werden. Das heißt, Die folgenden vier Optionen sind möglich: Wir haben richtig vorausgesagt, dass ein Pixel ein „Meer“ ist, richtig vorausgesagt, dass ein Pixel ein „Schiff“ ist, oder einen Fehler bei der Vorhersage eines „Meeres“ oder eines „Schiffs“ gemacht. Daher schätzen wir für alle Bilder und alle Pixel die Anzahl aller vier Optionen und berechnen das Ergebnis - dies ist das Ergebnis des Netzwerks. Und je weniger fehlerhafte Vorhersagen und wahrer, desto genauer das Ergebnis und desto besser das Netzwerk.

Nehmen wir für die Forschung die Option des gut untersuchten U-Netzes, das ein hervorragendes Netzwerk für die Bildsegmentierung darstellt. Die nicht so klassische U-Net-Option wurde gewählt, aber die Idee ist dieselbe: Das Netzwerk führt eine sehr einfache Operation mit Bildern durch - es reduziert die Bilddimension mit einigen Transformationen Schritt für Schritt und versucht dann, die Maske aus dem komprimierten Bild wiederherzustellen. Das heißt, In unserem Fall wird die Bildgröße auf 16 x 16 gebracht. Anschließend versuchen wir, die Maske mithilfe von Daten aus allen vorherigen Komprimierungsebenen wiederherzustellen.

Wir untersuchen das Netzwerk als „Black Box“, wir werden nicht untersuchen, was mit dem Netzwerk im Inneren passiert, wie sich Gewichte ändern und wie Gradienten ausgewählt werden - dies ist das Thema einer anderen Studie.

U-Netz mit Blöcken
 def convolution_block(x, filters, size, strides=(1,1), padding='same', activation=True): x = Conv2D(filters, size, strides=strides, padding=padding)(x) x = BatchNormalization()(x) if activation == True: x = Activation('relu')(x) return x def residual_block(blockInput, num_filters=16): x = Activation('relu')(blockInput) x = BatchNormalization()(x) x = convolution_block(x, num_filters, (3,3) ) x = convolution_block(x, num_filters, (3,3), activation=False) x = Add()([x, blockInput]) return x # Build model def build_model(input_layer, start_neurons, DropoutRatio = 0.5): conv1 = Conv2D(start_neurons * 1, (3, 3), activation=None, padding="same" )(input_layer) conv1 = residual_block(conv1,start_neurons * 1) conv1 = residual_block(conv1,start_neurons * 1) conv1 = Activation('relu')(conv1) pool1 = MaxPooling2D((2, 2))(conv1) pool1 = Dropout(DropoutRatio/2)(pool1) conv2 = Conv2D(start_neurons * 2, (3, 3), activation=None, padding="same" )(pool1) conv2 = residual_block(conv2,start_neurons * 2) conv2 = residual_block(conv2,start_neurons * 2) conv2 = Activation('relu')(conv2) pool2 = MaxPooling2D((2, 2))(conv2) pool2 = Dropout(DropoutRatio)(pool2) conv3 = Conv2D(start_neurons * 4, (3, 3), activation=None, padding="same")(pool2) conv3 = residual_block(conv3,start_neurons * 4) conv3 = residual_block(conv3,start_neurons * 4) conv3 = Activation('relu')(conv3) pool3 = MaxPooling2D((2, 2))(conv3) pool3 = Dropout(DropoutRatio)(pool3) conv4 = Conv2D(start_neurons * 8, (3, 3), activation=None, padding="same")(pool3) conv4 = residual_block(conv4,start_neurons * 8) conv4 = residual_block(conv4,start_neurons * 8) conv4 = Activation('relu')(conv4) pool4 = MaxPooling2D((2, 2))(conv4) pool4 = Dropout(DropoutRatio)(pool4) # Middle convm = Conv2D(start_neurons * 16, (3, 3), activation=None, padding="same")(pool4) convm = residual_block(convm,start_neurons * 16) convm = residual_block(convm,start_neurons * 16) convm = Activation('relu')(convm) deconv4 = Conv2DTranspose(start_neurons * 8, (3, 3), strides=(2, 2), padding="same")(convm) uconv4 = concatenate([deconv4, conv4]) uconv4 = Dropout(DropoutRatio)(uconv4) uconv4 = Conv2D(start_neurons * 8, (3, 3), activation=None, padding="same")(uconv4) uconv4 = residual_block(uconv4,start_neurons * 8) uconv4 = residual_block(uconv4,start_neurons * 8) uconv4 = Activation('relu')(uconv4) deconv3 = Conv2DTranspose(start_neurons * 4, (3, 3), strides=(2, 2), padding="same")(uconv4) uconv3 = concatenate([deconv3, conv3]) uconv3 = Dropout(DropoutRatio)(uconv3) uconv3 = Conv2D(start_neurons * 4, (3, 3), activation=None, padding="same")(uconv3) uconv3 = residual_block(uconv3,start_neurons * 4) uconv3 = residual_block(uconv3,start_neurons * 4) uconv3 = Activation('relu')(uconv3) deconv2 = Conv2DTranspose(start_neurons * 2, (3, 3), strides=(2, 2), padding="same")(uconv3) uconv2 = concatenate([deconv2, conv2]) uconv2 = Dropout(DropoutRatio)(uconv2) uconv2 = Conv2D(start_neurons * 2, (3, 3), activation=None, padding="same")(uconv2) uconv2 = residual_block(uconv2,start_neurons * 2) uconv2 = residual_block(uconv2,start_neurons * 2) uconv2 = Activation('relu')(uconv2) deconv1 = Conv2DTranspose(start_neurons * 1, (3, 3), strides=(2, 2), padding="same")(uconv2) uconv1 = concatenate([deconv1, conv1]) uconv1 = Dropout(DropoutRatio)(uconv1) uconv1 = Conv2D(start_neurons * 1, (3, 3), activation=None, padding="same")(uconv1) uconv1 = residual_block(uconv1,start_neurons * 1) uconv1 = residual_block(uconv1,start_neurons * 1) uconv1 = Activation('relu')(uconv1) uconv1 = Dropout(DropoutRatio/2)(uconv1) output_layer = Conv2D(1, (1,1), padding="same", activation="sigmoid")(uconv1) return output_layer # model input_layer = Input((w_size, w_size, 3)) output_layer = build_model(input_layer, 16) model = Model(input_layer, output_layer) model.compile(loss=bce_dice_loss, optimizer="adam", metrics=[my_iou_metric]) model.summary() 


Die Funktion zum Erzeugen von Bild / Masken-Paaren. Auf einem 128x128-Farbbild, das mit zufälligem Rauschen gefüllt ist und zufällig aus zwei Bereichen ausgewählt wurde, entweder 0,0 ... 0,75 oder 0,25..1,0. Platzieren Sie zufällig eine zufällig ausgerichtete Ellipse im Bild und platzieren Sie ein Rechteck an derselben Stelle. Wir prüfen, ob sie sich nicht schneiden, und verschieben gegebenenfalls das Rechteck zur Seite. Jedes Mal berechnen wir die Werte der Färbung des Meeres / Bootes neu. Der Einfachheit halber werden wir die Maske mit dem Bild in einem Array als vierte Farbe platzieren, d.h. Red.Green.Blue.Mask, es ist einfacher.

 def next_pair(): img_l = (np.random.sample((w_size, w_size, 3))* 0.75).astype('float32') img_h = (np.random.sample((w_size, w_size, 3))* 0.75 + 0.25).astype('float32') img = np.zeros((w_size, w_size, 4), dtype='float') p = np.random.sample() - 0.5 r = np.random.sample()*(w_size-2*radius_max) + radius_max c = np.random.sample()*(w_size-2*radius_max) + radius_max r_radius = np.random.sample()*(radius_max-radius_min) + radius_min c_radius = np.random.sample()*(radius_max-radius_min) + radius_min rot = np.random.sample()*360 rr, cc = ellipse( r, c, r_radius, c_radius, rotation=np.deg2rad(rot), shape=img_l.shape ) p1 = np.rint(np.random.sample()* (w_size-2*radius_max) + radius_max) p2 = np.rint(np.random.sample()* (w_size-2*radius_max) + radius_max) p3 = np.rint(np.random.sample()* (2*radius_max - radius_min) + radius_min) p4 = np.rint(np.random.sample()* (2*radius_max - radius_min) + radius_min) poly = np.array(( (p1, p2), (p1, p2+p4), (p1+p3, p2+p4), (p1+p3, p2), (p1, p2), )) rr_p, cc_p = polygon(poly[:, 0], poly[:, 1], img_l.shape) in_sc_rr = list(set(rr) & set(rr_p)) in_sc_cc = list(set(cc) & set(cc_p)) if len(in_sc_rr) > 0 and len(in_sc_cc) > 0: if len(in_sc_rr) > 0: _delta_rr = np.max(in_sc_rr) - np.min(in_sc_rr) + 1 if np.mean(rr_p) > np.mean(in_sc_rr): poly[:,0] += _delta_rr else: poly[:,0] -= _delta_rr if len(in_sc_cc) > 0: _delta_cc = np.max(in_sc_cc) - np.min(in_sc_cc) + 1 if np.mean(cc_p) > np.mean(in_sc_cc): poly[:,1] += _delta_cc else: poly[:,1] -= _delta_cc rr_p, cc_p = polygon(poly[:, 0], poly[:, 1], img_l.shape) if p > 0: img[:,:,:3] = img_l.copy() img[rr, cc,:3] = img_h[rr, cc] img[rr_p, cc_p,:3] = img_h[rr_p, cc_p] else: img[:,:,:3] = img_h.copy() img[rr, cc,:3] = img_l[rr, cc] img[rr_p, cc_p,:3] = img_l[rr_p, cc_p] img[:,:,3] = 0. img[rr, cc,3] = 1. return img 

Lassen Sie uns eine Trainingssequenz von Paaren erstellen, siehe Zufall 10

 _txy = [next_pair() for idx in range(train_num)] f_imgs = np.array(_txy)[:,:,:,:3].reshape(-1,w_size ,w_size ,3) f_msks = np.array(_txy)[:,:,:,3:].reshape(-1,w_size ,w_size ,1) del(_txy) #    10   fig, axes = plt.subplots(2, 10, figsize=(20, 5)) for k in range(10): kk = np.random.randint(train_num) axes[0,k].set_axis_off() axes[0,k].imshow(f_imgs[kk]) axes[1,k].set_axis_off() axes[1,k].imshow(f_msks[kk].squeeze()) 



Erster Schritt. Lassen Sie uns versuchen, auf einem minimalen Satz zu trainieren


Der erste Schritt unseres Experiments ist einfach. Wir versuchen, das Netzwerk so zu trainieren, dass nur 11 erste Bilder vorhergesagt werden.

 batch_size = 10 val_len = 11 precision = 0.85 m0_select = np.zeros((f_imgs.shape[0]), dtype='int') for k in range(val_len): m0_select[k] = 1 t = tqdm() while True: fit = model.fit(f_imgs[m0_select>0], f_msks[m0_select>0], batch_size=batch_size, epochs=1, verbose=0 ) current_accu = fit.history['my_iou_metric'][0] current_loss = fit.history['loss'][0] t.set_description("accuracy {0:6.4f} loss {1:6.4f} ".\ format(current_accu, current_loss)) t.update(1) if current_accu > precision: break t.close() 

accuracy 0.8636 loss 0.0666 : : 47it [00:29, 5.82it/s]

Wir haben die ersten 11 aus der Anfangssequenz ausgewählt und das Netzwerk darauf trainiert. Jetzt spielt es keine Rolle, ob das Netzwerk diese Bilder speziell speichert oder zusammenfasst. Hauptsache, es kann diese 11 Bilder so erkennen, wie wir es brauchen. Abhängig vom ausgewählten Datensatz und der Genauigkeit kann das Netzwerktraining sehr lange dauern. Wir haben aber nur wenige Iterationen. Ich wiederhole, dass es uns jetzt egal ist, wie und was das Netzwerk gelernt oder gelernt hat. Hauptsache, es hat die etablierte Genauigkeit der Vorhersage erreicht.

Starten Sie nun das Hauptexperiment


Wir werden neue Bild / Masken-Paare aus der konstruierten Sequenz nehmen und versuchen, sie durch das trainierte Netzwerk auf der bereits ausgewählten Sequenz vorherzusagen. Am Anfang sind es nur 11 Bild- / Maskenpaare und das Netzwerk ist trainiert, vielleicht nicht sehr richtig. Wenn in einem neuen Paar die Maske aus dem Bild mit akzeptabler Genauigkeit vorhergesagt wird, verwerfen wir dieses Paar, es enthält keine neuen Informationen für das Netzwerk, es kennt die Maske bereits und kann sie aus diesem Bild berechnen. Wenn die Genauigkeit der Vorhersage nicht ausreicht, fügen wir dieses Bild mit einer Maske zu unserer Sequenz hinzu und beginnen, das Netzwerk zu trainieren, bis ein akzeptables Genauigkeitsergebnis für die ausgewählte Sequenz erzielt wird. Das heißt, Dieses Bild enthält neue Informationen und wir fügen sie unserer Trainingssequenz hinzu und extrahieren die darin enthaltenen Informationen durch Training.

 batch_size = 50 t_batch_size = 1024 raw_len = val_len t = tqdm(-1) id_train = 0 #id_select = 1 while True: t.set_description("Accuracy {0:6.4f} loss {1:6.4f}\ selected img {2:5d} tested img {3:5d} ". format(current_accu, current_loss, val_len, raw_len)) t.update(1) if id_train == 1: fit = model.fit(f_imgs[m0_select>0], f_msks[m0_select>0], batch_size=batch_size, epochs=1, verbose=0 ) current_accu = fit.history['my_iou_metric'][0] current_loss = fit.history['loss'][0] if current_accu > precision: id_train = 0 else: t_pred = model.predict( f_imgs[raw_len: min(raw_len+t_batch_size,f_imgs.shape[0])], batch_size=batch_size ) for kk in range(t_pred.shape[0]): val_iou = get_iou_vector( f_msks[raw_len+kk].reshape(1,w_size,w_size,1), t_pred[kk].reshape(1,w_size,w_size,1) > 0.5) if val_iou < precision*0.95: new_img_test = 1 m0_select[raw_len+kk] = 1 val_len += 1 break raw_len += (kk+1) id_train = 1 if raw_len >= train_num: break t.close() 

 Accuracy 0.9830 loss 0.0287 selected img 271 tested img 9949 : : 1563it [14:16, 1.01it/s] 

Hier wird Genauigkeit im Sinne von "Genauigkeit" und nicht als Standard-Keras-Metrik verwendet, und das Unterprogramm "my_iou_metric" wird zur Berechnung der Genauigkeit verwendet. Es ist sehr interessant, die Genauigkeit und Anzahl der untersuchten und hinzugefügten Bilder zu beobachten. Zu Beginn werden fast alle Bild / Masken-Paare vom Netzwerk hinzugefügt, und irgendwo um die 70 beginnt es wegzuwerfen. Näher an 8000 wirft fast alle Paare.

Überprüfen Sie visuell zufällige Paare, die vom Netzwerk ausgewählt wurden:

 fig, axes = plt.subplots(2, 10, figsize=(20, 5)) t_imgs = f_imgs[m0_select>0] t_msks = f_msks[m0_select>0] for k in range(10): kk = np.random.randint(t_msks.shape[0]) axes[0,k].set_axis_off() axes[0,k].imshow(t_imgs[kk]) axes[1,k].set_axis_off() axes[1,k].imshow(t_msks[kk].squeeze()) 

Nichts Besonderes oder Übernatürliches:



Dies sind Paare, die vom Netzwerk in verschiedenen Trainingsphasen ausgewählt werden. Wenn das Netzwerk ein Eingabepaar aus dieser Sequenz erhielt, konnte es die Maske nicht mit der angegebenen Genauigkeit berechnen, und dieses Paar wurde in die Trainingssequenz aufgenommen. Aber nichts Besonderes, gewöhnliche Bilder.

Überprüfung des Ergebnisses und der Richtigkeit


Lassen Sie uns die Qualität des Netzwerktrainingsprogramms überprüfen und sicherstellen, dass die Qualität nicht wesentlich von der Reihenfolge der Anfangssequenz abhängt, für die wir die Anfangssequenz von Bild / Masken-Paaren mischen, die anderen 11 zuerst und auf die gleiche Weise nehmen, das Netzwerk trainieren und den Überschuss abschneiden.

 sh = np.arange(train_num) np.random.shuffle(sh) f0_imgs = f_imgs[sh] f0_msks = f_msks[sh] model.compile(loss=bce_dice_loss, optimizer="adam", metrics=[my_iou_metric]) model.summary() 

Trainingscode
 batch_size = 10 val_len = 11 precision = 0.85 m0_select = np.zeros((f_imgs.shape[0]), dtype='int') for k in range(val_len): m0_select[k] = 1 t = tqdm() while True: fit = model.fit(f0_imgs[m0_select>0], f0_msks[m0_select>0], batch_size=batch_size, epochs=1, verbose=0 ) current_accu = fit.history['my_iou_metric'][0] current_loss = fit.history['loss'][0] t.set_description("accuracy {0:6.4f} loss {1:6.4f} ".\ format(current_accu, current_loss)) t.update(1) if current_accu > precision: break t.close() 

 accuracy 0.8636 loss 0.0710 : : 249it [01:03, 5.90it/s] 

 batch_size = 50 t_batch_size = 1024 raw_len = val_len t = tqdm(-1) id_train = 0 #id_select = 1 while True: t.set_description("Accuracy {0:6.4f} loss {1:6.4f}\ selected img {2:5d} tested img {3:5d} ". format(current_accu, current_loss, val_len, raw_len)) t.update(1) if id_train == 1: fit = model.fit(f0_imgs[m0_select>0], f0_msks[m0_select>0], batch_size=batch_size, epochs=1, verbose=0 ) current_accu = fit.history['my_iou_metric'][0] current_loss = fit.history['loss'][0] if current_accu > precision: id_train = 0 else: t_pred = model.predict( f_imgs[raw_len: min(raw_len+t_batch_size,f_imgs.shape[0])], batch_size=batch_size ) for kk in range(t_pred.shape[0]): val_iou = get_iou_vector( f_msks[raw_len+kk].reshape(1,w_size,w_size,1), t_pred[kk].reshape(1,w_size,w_size,1) > 0.5) if val_iou < precision*0.95: new_img_test = 1 m0_select[raw_len+kk] = 1 val_len += 1 break raw_len += (kk+1) id_train = 1 if raw_len >= train_num: break t.close() 

 Accuracy 0.9890 loss 0.0224 selected img 408 tested img 9456 : : 1061it [21:13, 2.16s/it] 


Das Ergebnis hängt nicht wesentlich von der Reihenfolge der Paare der ursprünglichen Sequenz ab. Im vorherigen Fall hat das Netzwerk 271, jetzt 408 gewählt. Wenn Sie es verwechseln, kann das Netzwerk einen anderen Betrag auswählen. Wir werden nicht überprüfen, der Autor glaubt, dass es immer wesentlich weniger als 10.000 geben wird.

Überprüfen Sie die Genauigkeit der Netzwerkvorhersage in einer neuen unabhängigen Sequenz

 _txy = [next_pair() for idx in range(train_num)] test_imgs = np.array(_txy)[:,:,:,:3].reshape(-1,w_size ,w_size ,3) test_msks = np.array(_txy)[:,:,:,3:].reshape(-1,w_size ,w_size ,1) del(_txy) test_pred_0 = model.predict(test_imgs) t_val_0 = get_iou_vector(test_msks,test_pred_0) t_val_0 

 0.9927799999999938 


Zusammenfassung und Schlussfolgerungen


Wir konnten also weniger als drei bis vierhundert aus 10.000 Paaren auswählen, die Vorhersagegenauigkeit beträgt 0,99278, wir haben alle Paare, die mindestens einige nützliche Informationen enthalten, genommen und den Rest weggeworfen. Wir haben die statistischen Parameter der Trainingssequenz nicht angepasst, die Wiederholbarkeit von Informationen hinzugefügt usw. und benutzte überhaupt keine statistischen Methoden. Wir machen ein Bild, das Informationen enthält, die dem Netzwerk noch unbekannt sind, und drücken alles aus dem Gewicht des Netzwerks heraus. Wenn das Netzwerk mindestens einem „mysteriösen“ Bild entspricht, wird es im Geschäftsleben verwendet.

Insgesamt 271 Bild / Masken-Paare enthalten Informationen zur Vorhersage von 10.000 Paaren mit einer Genauigkeit von mindestens 0,8075 für jedes Paar, d. H. Die Gesamtgenauigkeit über die gesamte Sequenz ist höher, aber in jedem Bild ist sie nicht kleiner als 0,8075. Wir haben keine Bilder, die wir nicht haben wir können vorhersagen und wir kennen die untere Grenze dieser Vorhersage. (Hier prahlte der Autor natürlich, dass der Artikel ohne diese Aussage diese Aussage, etwa 0,8075 oder Beweise, nicht verifiziert, aber höchstwahrscheinlich ist dies wahr)

Um das Netzwerk zu trainieren, muss die GPU nicht mit allem geladen werden, was zur Hand ist. Sie können den Kern des Zuges herausziehen und das Netzwerk zu Beginn des Trainings darauf trainieren. Wenn Sie neue Bilder erhalten, können Sie diejenigen, die das Netzwerk nicht vorhersagen konnte, manuell markieren und zum Kern des Zuges hinzufügen. Dadurch wird das Netzwerk erneut geschult, um alle Informationen aus den neuen Bildern herauszuholen. Und es ist nicht notwendig, eine Validierungssequenz herauszugreifen, wir können davon ausgehen, dass alles andere außer der ausgewählten eine Validierungssequenz ist.

Noch eine mathematisch nicht strenge, aber sehr wichtige Bemerkung. Man kann mit Sicherheit sagen, dass jedes Bild / Masken-Paar „viele“ Informationen enthält. Jedes Paar enthält „viele“ Informationen, obwohl sich die Informationen in den meisten Bild- / Maskenpaaren überschneiden oder wiederholen. Jedes der 271 Bild / Masken-Paare enthält Informationen, die für die Vorhersage wesentlich sind, und dieses Paar kann nicht einfach weggeworfen werden.

Nun, eine kleine Bemerkung über Falten, viele Experten und Kagglers teilen die Trainingssequenz in Falten auf und trainieren sie separat, wobei sie die auf weitere knifflige Weise erzielten Ergebnisse kombinieren. In unserem Fall können Sie es auch in Falten unterteilen. Wenn Sie 271 Paare von 10.000 entfernen, können Sie in den verbleibenden Paaren eine neue Wurzelsequenz erstellen, die offensichtlich ein anderes, aber vergleichbares Ergebnis liefert. Sie können einfach die andere Initiale 11 mischen und nehmen, wie oben gezeigt.

Der Artikel enthält einen Code und zeigt, wie U-Net für die Bildsegmentierung trainiert wird. Dies ist ein konkretes Beispiel, und in dem Artikel gibt es absichtlich keine Verallgemeinerungen zu anderen Netzwerken, zu anderen Sequenzen, es gibt keine strenge Mathematik, alles wird „an den Fingern“ erzählt und gezeigt. Nur ein Beispiel dafür, wie Sie das Netzwerk lernen und gleichzeitig eine akzeptable Genauigkeit erzielen können.

Source: https://habr.com/ru/post/de433946/


All Articles