Krippe für künstliche Intelligenz - zu viel wegwerfen, Hauptsache lehren. Trainingssequenzen für die Verarbeitung der Technik

Dies ist der zweite Artikel zur Analyse und zum Studium des Materialwettbewerbs um Schiffe auf See zu finden. Aber jetzt werden wir die Eigenschaften von Trainingssequenzen untersuchen. Versuchen wir, in den Quelldaten zusätzliche Informationen und Redundanz zu finden und zu entfernen.



Auch dieser Artikel ist einfach das Ergebnis von Neugier und leerem 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 des Trainingsablaufs - die Begründung des Autors und der Code werden dargelegt, alles kann von uns geprüft / ergänzt / modifiziert werden.

Vor kurzem endete Kaggle Wettbewerb um Schiffe auf See zu finden. Airbus bot an, Satellitenbilder des Meeres sowohl mit als auch ohne Schiffe zu analysieren. Insgesamt 192555 768x768x3 Bilder sind 340.720.680.960 Bytes, wenn uint8 und dies ist eine riesige Menge an Informationen, und es besteht der unklare Verdacht, dass nicht alle Bilder für das Netzwerktraining benötigt werden. Beim Trainieren eines Netzwerks ist es üblich, einige Daten zu trennen und nicht zum Training zu verwenden, sondern um die Qualität des Trainings zu testen. Und wenn der gleiche Abschnitt des Meeres zwei verschiedene Schnappschüsse traf und ein Schnappschuss in die Trainingssequenz kam und der andere in die Testsequenz geriet, verliert die Prüfung ihre Bedeutung und das Netzwerk wird neu trainiert. Wir werden die Eigenschaften des Netzwerks nicht überprüfen, da die Daten identisch sind. Der Kampf gegen dieses Phänomen hat den GPU-Teilnehmern viel Zeit und Mühe gekostet. Wie üblich haben die Gewinner und Zweitplatzierten keine Eile, um ihren Fans die Geheimnisse der Meisterschaft zu zeigen und den Code festzulegen, und es gibt keine Möglichkeit, es zu lernen und zu lernen, also lassen Sie uns die Theorie machen.

Die einfachste visuelle Verifizierung zeigte, dass tatsächlich zu viele Daten vorhanden waren, derselbe Abschnitt des Meeres in unterschiedliche Bilder geraten ist, siehe Beispiele.









Aus diesem Grund sind die tatsächlichen Daten für uns nicht interessant, es gibt viele parasitäre Abhängigkeiten, keine für uns notwendigen Verbindungen, schlechte Markup-Werte und andere Mängel.

Im ersten Artikel haben wir uns Bilder mit Ellipsen und Rauschen angesehen. Lasst uns sie nun weiter untersuchen. Der Vorteil dieses Ansatzes besteht darin, dass, wenn Sie ein attraktives Merkmal eines Netzwerks finden, das mit einem beliebigen Satz von Bildern trainiert wird, nicht klar ist, ob dies eine Eigenschaft des Netzwerks oder eine Eigenschaft des Trainingssatzes ist. Die statistischen Parameter von Sequenzen aus der realen Welt sind unbekannt. Vor kurzem erzählte Großmeister Paleskov Pavel paske57Wie oft ist es leicht, einen Wettbewerb zur Segmentierung / Klassifizierung von Bildern zu gewinnen, wenn Sie selbst in die Daten eintauchen, um beispielsweise die Metadaten von Fotos zu betrachten. Und es gibt keine Garantie dafür, dass es in realen Daten keine Abhängigkeiten von solchen unfreiwilligen Links gibt. Wir untersuchen daher die Eigenschaften der Netzwerkbilder mit Ellipsen und Rechtecken, und Ort und Farbe sowie andere Parameter werden mit einem Zufallszahlengenerator des Computers bestimmt (der einen Pseudo-Zufallsgenerator besitzt, der einen Generator hat, der auf anderen nicht-digitalen Algorithmen und physikalischen Eigenschaften einer Substanz basiert. Aber das wird in diesem Artikel nicht besprochen.

Also nimm das sea np.random.sample () * 0.75Wir brauchen keine Wellen, Wind, Ufer und andere verborgene Muster und Gesichter. Wir werden die Schiffe / Ellipsen in der gleichen Farbe einfärben. Um das Meer vom Schiff zu unterscheiden, fügen wir dem Meer oder dem Schiff / der Störung 0,25 hinzu und alle haben die gleiche Form - Ellipsen unterschiedlicher Größe und Orientierung. Als Interferenz gelten auch nur Rechtecke mit der gleichen Farbe wie die Ellipse. Dies ist wichtig, um Informationen und Interferenzen derselben Farbe gegenüber dem Hintergrund von Rauschen zu erhalten. Wir nehmen nur eine kleine Änderung an der Färbung vor und führen für jedes Bild und für jede Ellipse / jedes Rechteck np.random.sample () aus, d. H. Weder der Hintergrund noch die Farbe der Ellipse / des Rechtecks ​​werden wiederholt. Weiter im Text ist der Code des Programms zum Erstellen von Bildern / Masken und ein Beispiel von zehn zufällig ausgewählten Paaren.

Nehmen Sie eine sehr häufige Version des Netzwerks (Sie können Ihr bevorzugtes Netzwerk verwenden) und versuchen Sie, die Redundanz einer großen Trainingssequenz zu identifizieren und zu zeigen, und erhalten Sie zumindest einige qualitative und quantitative Merkmale der Redundanz. Ie Der Autor ist der Ansicht, dass viele Gigabytes 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 äußert sich nicht nur und nicht so sehr darin, dass dieselben Teile in verschiedenen Bildern dargestellt werden, sondern auch in der Redundanz von Informationen in diesen Daten. Daten können redundant sein, auch wenn sie sich nicht exakt wiederholen. Bitte beachten Sie, dass dies keine strikte Definition von Informationen und deren ausreichender oder Redundanz ist. Wir wollen nur herausfinden, wie viel Sie den Zug schneiden können, welche Bilder aus dem Trainingsablauf geworfen werden können und wie viele Bilder für ein akzeptables Training ausreichen (lassen Sie uns die Genauigkeit des Programms angeben). Dies ist ein spezifisches Programm, ein bestimmter Datensatz, und es ist möglich, dass auf Ellipsen mit Dreiecken als Hindernis nichts so funktioniert wie auf Ellipsen mit Rechtecken (meine Hypothese ist, dass alles gleich und gleich ist. Aber jetzt prüfen wir es nicht Analyse wird nicht durchgeführt und wir beweisen keine Theoreme).

Also gegeben:

  • Trainingssequenz für Bild- / Maskenpaare. Wir können beliebig viele Bilder / Masken erzeugen. Beantworten Sie sofort die Frage - 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 Essenz in Form einer Grenze nicht erforderlich ist;
  • Das Netzwerk ist ein gewöhnliches, gewöhnliches U-Netz, leicht modifiziert und für die Segmentierung weit verbreitet.

Idee zur Verifizierung:

  • In der erstellten Reihenfolge werden wie in realen Aufgaben Gigabytes an Daten verwendet. Der Autor ist der Ansicht, dass die Größe der Trainingssequenz nicht so kritisch ist und die Daten nicht unbedingt viel sein sollten, aber sie sollten „viele“ Informationen enthalten. Diese Zahl, zehntausend Paare von Bildern / Masken, wird nicht benötigt, und das Netzwerk lernt eine viel geringere Datenmenge.

Beginnen wir, wählen Sie 10.000 Paare aus und prüfen Sie sie sorgfältig. Wir werden alles Wasser aus dieser Trainingssequenz herauspressen, alle Bits, die wir nicht brauchen, und wir werden den gesamten trockenen Rückstand verwenden und verwenden.

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

Für die praktische Anwendung beachten Sie bitte, dass sowohl das Meer als auch Schiffe mit Geräuschen künstlich ausgewählt werden. Dies ist np.random.sample () .

Laden Sie die Bibliothek, und bestimmen Sie die Größe des Bilder-Arrays
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


Verlust- und Genauigkeitsfunktionen bestimmen
defdice_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
defdice_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)
    return1. - score
defbce_dice_loss(y_true, y_pred):return binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)
defget_iou_vector(A, B):# Numpy version
    batch_size = A.shape[0]
    metric = 0.0for batch in range(batch_size):
        t, p = A[batch], B[batch]
        true = np.sum(t)
        pred = np.sum(p)
        # deal with empty mask firstif 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
defmy_iou_metric(label, pred):# Tensorflow versionreturn 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 . Lassen Sie mich die Leser daran erinnern, dass wir die Pixelmaske vorhersagen werden - dies ist das "Meer" oder "Schiff", und bewerten Sie die Wahrheit oder Falschheit der Vorhersage. Ie 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 „Sees“ oder „Schiffs“ gemacht haben. 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 falsche Vorhersagen und je wahrer, desto genauer das Ergebnis und desto besser der Netzwerkbetrieb.

Und für die Forschung nutzen wir die Wahl eines gut untersuchten U-Netzes. Dies ist ein hervorragendes Netzwerk für die Bildsegmentierung. Die nicht ganz klassische Version von U-net wurde gewählt, aber die Idee ist die gleiche: Das Netzwerk führt eine sehr einfache Operation mit Bildern durch - Schritt für Schritt reduziert es die Größe des Bildes bei einigen Transformationen und versucht dann, die Maske aus dem komprimierten Bild wiederherzustellen. Ie Die Größe des Bildes wird in unserem Fall auf 16x16 eingestellt und dann versuchen wir, die Maske mit den Daten aller vorherigen Kompressionsebenen wiederherzustellen.

Wir betrachten das Netzwerk als „Black Box“, wir untersuchen nicht, was im Netzwerk passiert, wie sich Gewichte ändern und wie Gradienten ausgewählt werden - dies ist ein Thema für eine andere Studie.

U-Netz mit Blöcken
defconvolution_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
defresidual_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 modeldefbuild_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 der Erzeugung von Bild- / Maskenpaaren. Auf dem Farbbild sind 128x128 mit zufälligem Rauschen gefüllt, wobei ein zufällig aus zwei Bereichen ausgewählt wird, oder 0,0 ... 0,75 oder 0,25,1,0. Zufällig platzieren wir eine zufällig ausgerichtete Ellipse im Bild und platzieren ein Rechteck an derselben Stelle. Wir prüfen, dass sie sich nicht schneiden, und verschieben das Rechteck bei Bedarf zur Seite. Jedes Mal, wenn wir die Werte der Färbung des Meeres / Schiffes überdenken. Der Einfachheit halber platzieren wir die Maske mit einem Bild in einem Array als vierte Farbe, d. H. Red.Green.Blue.Mask ist einfacher.

defnext_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) > 0and len(in_sc_cc) > 0:
        if len(in_sc_rr) > 0:
            _delta_rr = np.max(in_sc_rr) - np.min(in_sc_rr) + 1if 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) + 1if 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

Erstellen Sie eine Trainingssequenz von Paaren, 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())



Der erste Schritt Versuchen wir, das Minimum zu lernen


Der erste Schritt unseres Experiments ist einfach: Wir versuchen, das Netzwerk so zu trainieren, dass nur 11 der ersten 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()
whileTrue:
    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 ersten Sequenz ausgewählt und das Netzwerk darauf trainiert. Jetzt spielt es keine Rolle, ob das Netzwerk diese bestimmten Bilder lernt oder verallgemeinert. Die Hauptsache ist, dass es diese 11 Bilder erkennt, wenn wir sie brauchen. Abhängig von dem gewä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 oder was das Netzwerk gelernt oder gelernt hat. Die Hauptsache ist, dass es die etablierte Vorhersagegenauigkeit erreicht hat.

Beginnen wir nun mit dem Hauptversuch.


Wir werden neue Bild- / Maskenpaare aus der erstellten Sequenz herausnehmen und versuchen, sie mit einem Netzwerk vorherzusagen, das auf die bereits ausgewählte Sequenz trainiert wird. Am Anfang sind es nur 11 Paare von Bild / Maske und das Netzwerk ist trainiert, vielleicht nicht sehr gut. Wenn die neue Maske im Bild mit akzeptabler Genauigkeit vorhergesagt wird, werfen wir dieses Paar aus, es enthält keine neuen Informationen für das Netzwerk, es kennt die Maske bereits und kann aus diesem Bild die Maske berechnen. Wenn die Vorhersagegenauigkeit nicht ausreicht, fügen wir dieses maskierte Bild zu unserer Sequenz hinzu und beginnen das Netzwerk zu trainieren, bis eine akzeptable Genauigkeit für die ausgewählte Sequenz erreicht wird. Ie Dieses Bild enthält neue Informationen, und wir fügen sie unserer Schulungssequenz 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 = 1whileTrue:
    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 = 0else:
        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 += 1break
        raw_len += (kk+1)
        id_train = 1if 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]

Genauigkeit wird hier im Sinne von „Genauigkeit“ verwendet und nicht wie die Standardmetrik von Keras 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 fügt man fast alle Paare von Bild- / Maskennetzwerken hinzu, und irgendwo um 70 herum fängt es schon an zu werfen. Näher an 8000 Würfen fast alle Paare.

Überprüfen Sie visuell zufällige, vom Netzwerk ausgewählte Paare:

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:



Diese Paare werden vom Netzwerk in verschiedenen Lernstadien ausgewählt. Wenn das Netzwerk ein Paar aus dieser Sequenz als Eingabe 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 Genauigkeit


Überprüfen Sie die Qualität des Trainingsprogramms des Netzwerks, stellen Sie sicher, dass die Qualität nicht wesentlich von der Reihenfolge der Originalsequenz abhängt, für die wir die Originalsequenz der Bild- / Maskenpaare mischen, die anderen 11 ersten verwenden und mit derselben Methode 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()
whileTrue:
    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 = 1whileTrue:
    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 = 0else:
        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 += 1break
        raw_len += (kk+1)
        id_train = 1if 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 vorigen Fall wählte das Netzwerk 271, jetzt 408. Wenn Sie es vermischen, kann das Netzwerk eine andere Menge wählen. Wir werden das nicht prüfen, der Autor geht davon aus, dass es immer wesentlich weniger als 10.000 sein wird. Wir werden

die Richtigkeit der Netzwerkvorhersage in einer neuen unabhängigen Sequenz überprüfen.

_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


Ergebnisse und Schlussfolgerungen


So konnten wir aus weniger als drei oder vierhundert, aus 10.000 Paaren ausgewählten, herauspressen, die Vorhersagegenauigkeit war 0.99278, wir haben alle Paare genommen, die mindestens einige nützliche Informationen enthalten, und den Rest wegwerfen. Wir haben die statistischen Parameter der Trainingssequenz nicht angeglichen, Wiederholbarkeit der Informationen hinzugefügt usw. und verwendete überhaupt keine statistischen Methoden. Wir machen ein Foto, das Informationen enthält, die dem Netzwerk noch nicht bekannt sind, und drückt alles aus dem Netzwerk heraus. Wenn das Netzwerk mindestens ein "geheimnisvolles" Bild trifft, wird es alles im Geschäft verwenden.

Insgesamt 271 Paare von Bildern / Masken enthalten Informationen zum Vorhersagen von 10.000 Paaren mit einer Genauigkeit von nicht weniger als 0,8075 für jedes Paar, dh die Gesamtgenauigkeit der gesamten Sequenz ist höher, aber in jedem Bild ist es nicht weniger als 0,8075. Wir haben keine Bilder, die wir nicht haben Wir können vorhersagen und kennen die untere Grenze dieser Vorhersage. (hier hat sich der Autor natürlich rühmen können, da der Artikel ohne diese Aussage weder etwa 0,8075 noch den Beweis bestätigt, aber höchstwahrscheinlich trifft dies zu.)

Um ein Netzwerk zu trainieren, ist es nicht erforderlich, die GPU mit allem zu laden, was zur Verfügung stand. Sie können einen solchen Kern aus dem Zug herausziehen und das Netzwerk darauf als Trainingsbeginn trainieren. Wenn Sie neue Bilder empfangen, können Sie manuell diejenigen markieren, die das Netzwerk nicht vorhersagen konnte, und sie zum Kern des Zugs hinzufügen, nachdem Sie das Netzwerk erneut trainiert haben, um alle Informationen aus den neuen Bildern herauszuholen. Die Validierungssequenz muss nicht herausgegriffen werden. Wir können davon ausgehen, dass alles andere als die ausgewählte Validierungssequenz ist.

Ein weiterer mathematisch nicht strenger, aber sehr wichtiger Hinweis. Man kann mit Sicherheit sagen, dass jedes Bild- / Maskenpaar „viele“ Informationen enthält. Jedes Paar enthält „viele“ Informationen, obwohl sich die Bild- / Maskeninformationspaare zum größten Teil überlappen oder wiederholen. Jedes der 271 Bild- / Maskenpaare enthält Informationen, die für die Vorhersage wesentlich sind, und dieses Paar kann nicht einfach weggeworfen werden.

Nun, und ein kleiner Hinweis zu den Falten, viele Experten und Kagglers teilen die Trainingssequenz in Falten auf und trainieren sie separat, wobei sie die Ergebnisse mit cleveren Methoden kombinieren. In unserem Fall ist es auch möglich, auf genau dieselbe Weise in Falten zu unterteilen, wenn von 10.000 271 Paaren entfernt werden, können die verbleibenden auch dazu verwendet werden, eine neue Wurzelsequenz zu erzeugen, was offensichtlich ein anderes, aber vergleichbares Ergebnis ergibt. Sie können die anderen 11 einfach mischen und nehmen, wie oben gezeigt.

Der Artikel enthielt den Code und zeigt, wie U-net für die Bildsegmentierung trainiert wird. Dies ist ein konkretes Beispiel, und der Artikel enthält absichtlich keine Verallgemeinerungen zu anderen Netzwerken, zu anderen Sequenzen, es gibt keine strenge Mathematik, alles wird erzählt und „an den Fingern“ gezeigt. Nur ein Beispiel, wie Sie das Netzwerk erlernen und gleichzeitig eine akzeptable Genauigkeit erreichen.

Jetzt auch beliebt: