Aide-mémoire pour l'intelligence artificielle - jetez l'excédent, enseignez l'essentiel. Technique de traitement des séquences de formation

Il s'agit du deuxième article sur l'analyse et l'étude des matériaux de la compétition pour la recherche de navires en mer. Mais maintenant, nous allons étudier les propriétés des séquences d'entraînement. Essayons de trouver les informations supplémentaires, la redondance dans les données source et supprimons-les.



Cet article est aussi simplement le résultat de la curiosité et d'un intérêt oiseux, rien de cela ne se rencontre dans la pratique, et pour les tâches pratiques, il n'y a presque rien pour le copier-coller. Ceci est une petite étude des propriétés de la séquence de formation - le raisonnement et le code de l'auteur sont présentés, vous pouvez tout vérifier / compléter / changer vous-même.

Le concours de recherche marine de kaggle a récemment pris fin. Airbus a proposé d'analyser les images satellites de la mer avec et sans navires. Un total de 192555 images 768x768x3 - soit 340 720 680 960 octets si uint8 et ceci est une énorme quantité d'informations et il y avait un vague soupçon que toutes les images ne sont pas nécessaires pour former le réseau et dans cette quantité d'informations, la répétition et la redondance sont évidentes. Lors de la formation d'un réseau, il est habituel de séparer certaines données et de ne pas les utiliser dans la formation, mais de les utiliser pour vérifier la qualité de la formation. Et si une seule et même étendue de la mer est tombée dans deux images différentes et en même temps une image est tombée dans la séquence d'entraînement et l'autre dans la séquence de vérification, alors la vérification perdra son sens et le réseau sera recyclé, nous ne vérifierons pas la capacité du réseau à généraliser les informations, car les données sont les mêmes. La lutte contre ce phénomène a pris beaucoup de temps et d'efforts au GPU des participants. Comme d'habitude, les gagnants et les lauréats ne sont pas pressés de montrer à leurs fans les secrets de la maîtrise et de définir le code, et il n'y a aucun moyen de l'étudier et de l'apprendre, nous allons donc reprendre la théorie.

Une simple vérification visuelle a montré qu'il y a vraiment trop de données, la même étendue de mer est tombée en images différentes, regardez les exemples









C'est pour cette raison que nous ne sommes pas intéressés par les données réelles, il y a beaucoup de dépendances parasites, des connexions inutiles avec nous, un mauvais balisage et d'autres défauts.

Dans le premier article, nous avons regardé des images avec des ellipses et du bruit, et nous continuerons à les étudier. L'avantage de cette approche est que si vous trouvez une caractéristique attrayante d'un réseau formé sur un ensemble arbitraire d'images, il n'est pas clair s'il s'agit d'une propriété de réseau ou d'une propriété d'un ensemble de formation. Les paramètres statistiques des séquences tirées du monde réel sont inconnus. Récemment, le grand maître Pleskov Pavel paske57 a expliqué qu'il est parfois facile de gagner une classification de segmentation / classification des images s'il est bon de se plonger dans les données vous-même, par exemple, voir les métadonnées des photos. Et il n'y a aucune garantie que dans les données réelles il n'y a pas de telles dépendances, involontairement laissées. Par conséquent, pour étudier les propriétés du réseau, nous prenons des photos avec des ellipses et des rectangles, et déterminons l'emplacement et la couleur et d'autres paramètres à l'aide d'un générateur de nombres aléatoires d'un ordinateur (qui a un générateur pseudo-aléatoire, qui a un générateur basé sur d'autres algorithmes non numériques et les propriétés physiques de la substance, Mais nous n'en discuterons pas dans cet article).

Alors, prenez la mer np.random.sample () * 0,75 , nous n'avons pas besoin de vagues, de vent, de côtes et d'autres motifs et faces cachés. Les navires / ellipses seront également peints de la même couleur, et pour distinguer la mer du bateau et des interférences, ajoutez 0,25 à la mer ou au bateau / brouilleur, et ils auront tous la même forme - des ellipses de tailles et d'orientations différentes. L'interférence ne sera également que des rectangles de la même couleur que l'ellipse - c'est important, les informations et les interférences de la même couleur sur le fond du bruit. Nous n'apporterons qu'une petite modification à la coloration et nous exécuterons np.random.sample () pour chaque image et pour chaque ellipse / rectangle, c'est-à-dire Ni l'arrière-plan ni la couleur de l'ellipse / du rectangle ne sont répétés. Plus loin dans le texte, il y a un code du programme pour créer des images / masques et un exemple de dix paires sélectionnées au hasard.

Prenez une version très courante du réseau (vous pouvez prendre votre réseau préféré) et essayez d'identifier et de montrer la redondance d'une grande séquence de formation, pour obtenir au moins une sorte de caractéristiques qualitatives et quantitatives de la redondance. C'est-à-dire l'auteur pense que de nombreux gigaoctets de séquences d'entraînement sont substantiellement redondants, il y a beaucoup d'images inutiles, il n'est pas nécessaire de charger des dizaines de GPU et de faire des calculs inutiles. La redondance des données se manifeste non seulement et non pas tant dans le fait que les mêmes parties sont affichées dans des images différentes, mais aussi dans la redondance des informations dans ces données. Les données peuvent être redondantes même si elles ne sont pas répétées exactement. Veuillez noter qu'il ne s'agit pas d'une définition stricte des informations et de leur suffisance ou redondance. Nous voulons simplement savoir combien vous pouvez réduire le train, quelles images vous pouvez jeter de la séquence de formation et combien de photos suffisent pour une formation acceptable (nous définirons nous-mêmes la précision dans le programme). Il s'agit d'un programme spécifique, d'un ensemble de données spécifique, et il est possible que sur les ellipses avec des triangles, comme obstacle, rien ne fonctionne aussi bien que sur les ellipses avec des rectangles (mon hypothèse est que tout sera le même et le même. Mais nous ne le vérifions pas maintenant , nous n'effectuons pas d'analyse et ne prouvons pas de théorèmes).

Donc, étant donné:

  • séquence d'apprentissage de paires image / masque. Nous pouvons générer un nombre illimité de paires d'images / masques. Je répondrai immédiatement à la question - pourquoi la couleur et l'arrière-plan sont-ils aléatoires? Je répondrai simplement, brièvement, clairement et complètement que je l'aime tellement, une entité supplémentaire sous la forme d'une frontière n'est pas nécessaire;
  • le réseau est ordinaire, U-net ordinaire, légèrement modifié et largement utilisé pour la segmentation.

Idée à tester:

  • dans la séquence construite, comme dans les tâches réelles, des gigaoctets de données sont utilisés. L'auteur estime que la taille de la séquence de formation n'est pas si critique et qu'il ne devrait pas y avoir beaucoup de données, mais qu'elles devraient contenir «beaucoup» d'informations. Une telle quantité, dix mille paires d'images / masques, n'est pas nécessaire et le réseau apprendra à partir d'une quantité de données beaucoup plus petite.

Commençons, sélectionnons 10 000 paires et considérons-les attentivement. Nous allons extraire toute l'eau, tous les morceaux inutiles de cette séquence d'entraînement et utiliser et mettre en pratique tous les résidus secs.

Vous pouvez maintenant tester votre intuition et supposer combien de paires sur 10 000 suffisent pour former et prédire une autre, mais également créer une séquence de 10 000 paires avec une précision de plus de 0,98. Écrivez sur un morceau de papier, après comparaison.

Pour une utilisation pratique, veuillez prendre en compte que la mer et les navires avec interférence sont sélectionnés artificiellement, c'est np.random.sample () .

Nous chargeons des bibliothèques, nous déterminons les tailles d'un tableau d'images
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 


déterminer les fonctions de perte et de précision
 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 }) 


Nous utiliserons la métrique du premier article . Permettez-moi de rappeler aux lecteurs que nous allons prédire le masque du pixel - c'est la «mer» ou le «navire» et évaluer la vérité ou la fausseté de la prédiction. C'est-à-dire Les quatre options suivantes sont possibles - nous avons correctement prédit qu'un pixel est une «mer», correctement prédit qu'un pixel est un «navire» ou fait une erreur en prédisant une «mer» ou un «navire». Et donc, pour toutes les images et tous les pixels, nous estimons le nombre des quatre options et calculons le résultat - ce sera le résultat du réseau. Et moins les prévisions sont erronées et vraies, plus le résultat est précis et meilleur est le réseau.

Et pour la recherche, prenons l'option du U-net bien étudié, qui est un excellent réseau pour la segmentation d'images. La version pas si classique d'U-net a été choisie, mais l'idée est la même, le réseau effectue une opération très simple avec les images - il réduit la taille de l'image avec quelques transformations étape par étape, puis essaie de récupérer le masque de l'image compressée. C'est-à-dire la dimension de l'image dans notre cas est portée à 16x16 puis nous essayons de restaurer le masque en utilisant les données de toutes les couches de compression précédentes.

Nous examinons le réseau comme une «boîte noire», nous ne regarderons pas ce qui se passe avec le réseau à l'intérieur, comment les poids changent et comment les gradients sont choisis - c'est le sujet d'une autre étude.

U-net avec blocs
 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() 


La fonction de génération de paires image / masque. Sur une image couleur 128x128 remplie de bruit aléatoire avec une sélection aléatoire de deux plages, soit 0,0 ... 0,75 ou 0,25..1,0. Placez au hasard une ellipse orientée au hasard dans l'image et placez un rectangle au même endroit. Nous vérifions qu'elles ne se coupent pas et, si nécessaire, décalons le rectangle sur le côté. Chaque fois que nous recalculons les valeurs de la coloration de la mer / bateau. Pour plus de simplicité, nous allons mettre le masque avec l'image dans un tableau, comme quatrième couleur, c'est-à-dire Red.Green.Blue.Mask, c'est plus facile.

 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 

Créons une séquence d'entraînement de paires, voir au hasard 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()) 



Première étape. Essayons de nous entraîner sur un ensemble minimal


La première étape de notre expérience est simple, nous essayons de former le réseau pour ne prédire que 11 premières images.

 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]

Nous avons sélectionné les 11 premiers dans la séquence initiale et formé le réseau sur eux. Peu importe que le réseau mémorise ces images spécifiquement ou les résume, l'essentiel est qu'il puisse reconnaître ces 11 images de la manière dont nous avons besoin. En fonction de l'ensemble de données sélectionné et de sa précision, la formation réseau peut durer très, très longtemps. Mais nous n'avons que quelques itérations. Je répète qu’il n’est plus important pour nous maintenant de savoir comment et ce que le réseau a appris ou appris, l’essentiel est qu’il ait atteint la précision de prédiction établie.

Commencez maintenant l'expérience principale


Nous prendrons de nouvelles paires image / masque de la séquence construite et essaierons de les prédire par le réseau formé sur la séquence déjà sélectionnée. Au début, il ne s'agit que de 11 paires d'image / masque et le réseau est entraîné, peut-être pas très correctement. Si dans une nouvelle paire le masque de l'image est prédit avec une précision acceptable, alors nous rejetons cette paire, elle n'a pas de nouvelles informations pour le réseau, elle sait déjà et peut calculer le masque à partir de cette image. Si la précision de la prédiction est insuffisante, nous ajoutons cette image avec un masque à notre séquence et commençons à entraîner le réseau jusqu'à ce qu'un résultat de précision acceptable soit atteint sur la séquence sélectionnée. C'est-à-dire Cette image contient de nouvelles informations et nous les ajoutons à notre séquence de formation et extrayons les informations qu'elle contient par formation.

 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] 

Ici, la précision est utilisée dans le sens de «précision», et non comme une métrique standard de kéros, et le sous-programme «my_iou_metric» est utilisé pour calculer la précision. Il est très intéressant d'observer la précision et le nombre d'images étudiées et ajoutées. Au début, presque toutes les paires image / masque sont ajoutées par le réseau, et quelque part autour de 70, il commence à se jeter. Plus près de 8000 jette presque toutes les paires.

Vérifiez les paires visuellement aléatoires sélectionnées par le réseau:

 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()) 

Rien de spécial ou de surnaturel:



Ce sont des paires sélectionnées par le réseau à différentes étapes de la formation. Lorsque le réseau a reçu une paire d'entrée de cette séquence, il n'a pas pu calculer le masque avec la précision spécifiée et cette paire a été incluse dans la séquence d'apprentissage. Mais rien de spécial, de photos ordinaires.

Vérification du résultat et de la précision


Vérifions la qualité du programme de formation du réseau, assurez-vous que la qualité ne dépend pas de manière significative de l'ordre de la séquence initiale, pour laquelle nous mélangeons la séquence initiale des paires image / masque, prenons les 11 autres en premier et de la même manière, entraînons le réseau et coupons l'excédent.

 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() 

Code d'entraînement
 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] 


Le résultat ne dépend pas de manière significative de l'ordre des paires de la séquence d'origine. Dans le cas précédent, le réseau a choisi 271, maintenant 408, si vous le mélangez, le réseau peut choisir une quantité différente. Nous ne vérifierons pas, l'auteur estime qu'il y en aura toujours sensiblement moins de 10 000.

Vérifier la précision de la prédiction du réseau sur une nouvelle séquence indépendante

 _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 


Résumé et conclusions


Nous avons donc pu extraire de moins de trois à quatre cents sélectionnés parmi 10000 paires, la précision de la prédiction est de 0,99278, nous avons pris toutes les paires qui contiennent au moins des informations utiles et jeté le reste. Nous n'avons pas aligné les paramètres statistiques de la séquence d'apprentissage, ajouté la répétabilité des informations, etc. et n'a pas du tout utilisé de méthodes statistiques. Nous prenons une photo qui contient des informations encore inconnues du réseau et nous en extrayons tout dans le poids du réseau. Si le réseau rencontre au moins une image «mystérieuse», il utilisera tout cela dans les affaires.

Un total de 271 paires image / masque contiennent des informations pour prédire 10000 paires avec une précision d'au moins 0,8075 sur chaque paire, c'est-à-dire que la précision totale sur toute la séquence est plus élevée, mais dans chaque image elle n'est pas inférieure à 0,8075, nous n'avons pas d'images que nous n'avons pas nous pouvons prédire et nous connaissons la limite inférieure de cette prédiction. (ici, bien sûr, l'auteur s'est vanté, comment sans cela, l'article ne vérifie pas cette déclaration, environ 0,8075, ou les preuves, mais très probablement, cela est vrai)

Pour former le réseau, il n'est pas nécessaire de charger le GPU avec tout ce qui vient à portée de main, vous pouvez retirer le cœur du train et y former le réseau au début de la formation. Au fur et à mesure que vous obtenez de nouvelles images, vous pouvez manuellement marquer celles que le réseau n'a pas pu prédire et les ajouter au cœur du train, en recyclant à nouveau le réseau pour extraire toutes les informations des nouvelles images. Et il n'est pas nécessaire de distinguer une séquence de validation; nous pouvons supposer que tout le reste, sauf celui sélectionné, est une séquence de validation.

Encore une remarque mathématique non stricte, mais très importante. Il est sûr de dire que chaque paire image / masque contient «beaucoup» d'informations. Chaque paire contient «beaucoup» d'informations, bien que dans la plupart des paires image / masque, les informations se croisent ou se répètent. Chacune des 271 paires image / masque contient des informations essentielles pour la prédiction, et cette paire ne peut pas simplement être jetée.

Eh bien, une petite remarque sur les plis, de nombreux experts et kagglers divisent la séquence d'entraînement en plis et les forment séparément, combinant les résultats obtenus de manière plus délicate. Dans notre cas, vous pouvez également le diviser en plis, si vous supprimez 271 paires de 10 000, vous pouvez créer une nouvelle séquence racine dans les autres, ce qui fournira évidemment un résultat différent mais comparable. Vous pouvez simplement mélanger et prendre les 11 autres initiaux, comme indiqué ci-dessus.

L'article fournit un code et montre comment former U-net pour la segmentation d'images. C'est un exemple concret, et dans l'article intentionnellement il n'y a pas de généralisations à d'autres réseaux, à d'autres séquences, il n'y a pas de mathématiques rigoureuses, tout est raconté et montré «sur les doigts». Juste un exemple de la façon dont vous pouvez apprendre le réseau tout en atteignant une précision acceptable.

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


All Articles