Este é o terceiro artigo sobre a análise e o estudo de elipses, triângulos e outras formas geométricas.
Os artigos anteriores levantaram algumas questões muito interessantes entre os leitores, em particular, sobre a complexidade ou simplicidade de certas sequências de treinamento. As perguntas são realmente muito interessantes, por exemplo, quanto mais difícil é aprender um triângulo do que um quadrângulo ou outro polígono?

Vamos tentar comparar e, para comparação, temos uma ótima idéia, testada por gerações de estudantes, a idéia - quanto menor a folha de dicas, mais fácil o exame.
Este artigo também é simplesmente o resultado de curiosidade e interesse ocioso, nada disso é encontrado na prática e, para tarefas práticas, existem algumas ótimas idéias, mas não há quase nada para copiar e colar. Este é um pequeno estudo sobre a complexidade das seqüências de treinamento - o raciocínio e o código do autor são apresentados, você pode verificar / complementar / alterar tudo sozinho.
Então, vamos tentar descobrir qual figura geométrica é mais complicada ou mais simples para segmentação, qual curso de palestras para IA é mais compreensível e melhor absorvido.
Existem muitas formas geométricas diferentes, mas compararemos apenas triângulos, quadrângulos e estrelas de cinco pontas. Usaremos um método simples para construir uma sequência de trem - dividiremos imagens monocromáticas de 128x128 em quatro partes e colocaremos aleatoriamente uma elipse e, por exemplo, um triângulo nesses quadrantes. Vamos detectar um triângulo da mesma cor que a elipse. I.e. a tarefa é treinar a rede para distinguir, por exemplo, um polígono quadrangular de uma elipse pintada na mesma cor. Aqui estão exemplos de fotos que iremos estudar



Não detectaremos um triângulo e um quadrilátero em uma imagem, iremos detectá-los separadamente, em trens diferentes, contra o fundo de interferência na forma de uma elipse.
Vamos usar a clássica rede U-net e três tipos de sequências de treinamento com triângulos, quadrângulos e estrelas para pesquisa.
Então, dado:
- três sequências de treinamento de pares foto / máscara;
- a rede. Rede U comum, amplamente utilizada para segmentação.
Idéia para testar:
- determinar qual das seqüências de treinamento é "mais difícil" de aprender;
- como algumas técnicas de pré-processamento afetam o aprendizado
Vamos começar, selecione 10.000 pares de fotos de quadrângulos com elipses e máscaras e considere-os cuidadosamente. Estamos interessados em quão curto será o berço e em qual depende seu comprimento.
Carregamos bibliotecas, determinamos os tamanhos de uma matriz de imagensimport 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
determinar as funções de perda e precisão 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):
Usaremos a métrica do
primeiro artigo . Deixe-me lembrar aos leitores que vamos prever a máscara do pixel - esse é o "plano de fundo" ou o "quadrângulo" e avaliar a verdade ou falsidade da previsão. I.e. As quatro opções a seguir são possíveis - previmos corretamente que um pixel é um plano de fundo, previmos corretamente que um pixel é um quadrilátero ou cometemos um erro ao prever um "segundo plano" ou "quadrilátero". E assim, para todas as imagens e todos os pixels, estimamos o número das quatro opções e calculamos o resultado - este será o resultado da rede. E quanto menos previsões errôneas e mais verdadeiras, mais preciso o resultado e melhor a rede.
Examinamos a rede como uma "caixa preta", não começaremos a ver o que acontece com a rede interna, como os pesos mudam e como os gradientes são escolhidos - examinaremos as entranhas da rede mais tarde, quando compararmos as redes.
rede U simples def build_model(input_layer, start_neurons):
A função de gerar pares de imagem / máscara. Em uma imagem em preto e branco, 128x128 é preenchido com ruído aleatório com um selecionado aleatoriamente em dois intervalos, ou 0,0 ... 0,75 ou 0,25..1,0. Selecione aleatoriamente um quarto da imagem e coloque uma elipse orientada aleatoriamente e, no outro quarto, colocamos um quadrângulo e cores iguais com ruído aleatório.
def next_pair(): img_l = (np.random.sample((w_size, w_size, 1))* 0.75).astype('float32') img_h = (np.random.sample((w_size, w_size, 1))* 0.75 + 0.25).astype('float32') img = np.zeros((w_size, w_size, 2), dtype='float') i0_qua = math.trunc(np.random.sample()*4.) i1_qua = math.trunc(np.random.sample()*4.) while i0_qua == i1_qua: i1_qua = math.trunc(np.random.sample()*4.) _qua = np.int(w_size/4) qua = np.array([[_qua,_qua],[_qua,_qua*3],[_qua*3,_qua*3],[_qua*3,_qua]]) p = np.random.sample() - 0.5 r = qua[i0_qua,0] c = qua[i0_qua,1] 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 ) p0 = np.rint(np.random.sample()*(radius_max-radius_min) + radius_min) p1 = qua[i1_qua,0] - (radius_max-radius_min) p2 = qua[i1_qua,1] - (radius_max-radius_min) p3 = np.rint(np.random.sample()*radius_min) p4 = np.rint(np.random.sample()*radius_min) p5 = np.rint(np.random.sample()*radius_min) p6 = np.rint(np.random.sample()*radius_min) p7 = np.rint(np.random.sample()*radius_min) p8 = np.rint(np.random.sample()*radius_min) poly = np.array(( (p1, p2), (p1+p3, p2+p4+p0), (p1+p5+p0, p2+p6+p0), (p1+p7+p0, p2+p8), (p1, p2), )) rr_p, cc_p = polygon(poly[:, 0], poly[:, 1], img_l.shape) if p > 0: img[:,:,:1] = img_l.copy() img[rr, cc,:1] = img_h[rr, cc] img[rr_p, cc_p,:1] = img_h[rr_p, cc_p] else: img[:,:,:1] = img_h.copy() img[rr, cc,:1] = img_l[rr, cc] img[rr_p, cc_p,:1] = img_l[rr_p, cc_p] img[:,:,1] = 0. img[rr_p, cc_p,1] = 1. return img
Vamos criar uma sequência de treinamento de pares, ver aleatória 10. Deixe-me lembrá-lo de que as imagens são monocromáticas e em escala de cinza.
_txy = [next_pair() for idx in range(train_num)] f_imgs = np.array(_txy)[:,:,:,:1].reshape(-1,w_size ,w_size ,1) f_msks = np.array(_txy)[:,:,:,1:].reshape(-1,w_size ,w_size ,1) del(_txy)

Primeiro passo Treinamos no conjunto inicial mínimo
O primeiro passo de nosso experimento é simples: estamos tentando treinar a rede para prever apenas 11 primeiras fotos.
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.8545 loss 0.0674 lenght 11 : : 793it [00:58, 14.79it/s]
Selecionamos os 11 primeiros da sequência inicial e treinamos a rede neles. Agora, não importa se a rede memoriza essas imagens especificamente ou resume, o principal é que ela possa reconhecer essas 11 fotos conforme necessário. Dependendo do conjunto de dados e da precisão selecionados, o treinamento em rede pode durar muito, muito tempo. Mas temos apenas algumas iterações. Repito que agora não é importante para nós como e o que a rede aprendeu ou aprendeu, o principal é que atingiu a precisão estabelecida da previsão.
Agora comece o experimento principal
Construiremos as folhas de dicas, vamos construir essas folhas separadamente para as três sequências de treinamento e comparar seu comprimento. Tiraremos novos pares de foto / máscara da sequência construída e tentaremos predizê-los pela rede treinada na sequência já selecionada. No começo, são apenas 11 pares de imagem / máscara e a rede é treinada, talvez não muito corretamente. Se em um novo par a máscara da imagem é prevista com precisão aceitável, descartamos esse par, ele não possui novas informações para a rede, ele já sabe e pode calcular a máscara dessa imagem. Se a precisão da previsão for insuficiente, adicionamos esta imagem com uma máscara à nossa sequência e começamos a treinar a rede até que um resultado de precisão aceitável seja alcançado na sequência selecionada. I.e. Esta imagem contém novas informações e as adicionamos à nossa sequência de treinamento e extraímos as informações nele contidas pelo treinamento.
batch_size = 50 t_batch_size = 1024 raw_len = val_len t = tqdm(-1) id_train = 0
Accuracy 0.9338 loss 0.0266 selected img 1007 tested img 9985 : : 4291it [49:52, 1.73s/it]
Aqui, a precisão é usada no sentido de "precisão", e não como a métrica keras padrão, e a sub-rotina "my_iou_metric" é usada para calcular a precisão.
Agora compare a operação da mesma rede com os mesmos parâmetros em uma sequência diferente, em triângulos

E obtemos um resultado completamente diferente
Accuracy 0.9823 loss 0.0108 selected img 1913 tested img 9995 : : 6343it [2:11:36, 3.03s/it]
A rede selecionou 1913 imagens com informações "novas", ou seja, o conteúdo de figuras com triângulos é metade do que com quadrângulos!
Vamos verificar a mesma coisa nas estrelas e executar a rede na terceira sequência

nós temos
Accuracy 0.8985 loss 0.0478 selected img 476 tested img 9985 : : 2188it [16:13, 1.16it/s]
Como você pode ver, as estrelas foram as mais informativas, com apenas 476 fotos em uma folha de dicas.
Tínhamos motivos para julgar a complexidade das formas geométricas quanto à percepção por sua rede neural. A mais simples é a estrela, com apenas 476 fotos na folha de dicas e, em seguida, o quadrângulo com seus 1007 e os mais complexos acabou sendo um triângulo - para o treinamento, você precisa de 1913 fotos.
Lembre-se de que isso é para nós, para as pessoas é uma imagem, mas para a rede é um curso de palestras sobre reconhecimento e o curso sobre triângulos acabou sendo o mais difícil.
Agora sobre o sério
À primeira vista, todas essas elipses e triângulos parecem mimos, bolos de areia e lego. Mas aqui está uma pergunta específica e séria: se aplicarmos algum tipo de pré-processamento, filtrar a sequência inicial, como a complexidade da sequência mudará? Por exemplo, pegamos as mesmas elipses e quadrângulos e aplicamos esse pré-processamento a eles
from scipy.ndimage import gaussian_filter _tmp = [gaussian_filter(idx, sigma = 1) for idx in f_imgs] f1_imgs = np.array(_tmp)[:,:,:,:1].reshape(-1,w_size ,w_size ,1) del(_tmp) fig, axes = plt.subplots(2, 5, figsize=(20, 7)) for k in range(5): kk = np.random.randint(train_num) axes[0,k].set_axis_off() axes[0,k].imshow(f1_imgs[kk].squeeze(), cmap="gray") axes[1,k].set_axis_off() axes[1,k].imshow(f_msks[kk].squeeze(), cmap="gray")

À primeira vista, tudo é o mesmo, as mesmas elipses, os mesmos polígonos, mas a rede começou a funcionar de uma maneira completamente diferente:
Accuracy 1.0575 loss 0.0011 selected img 7963 tested img 9999 : : 17765it [29:02:00, 12.40s/it]
Aqui é necessária uma pequena explicação, não usamos aumento, porque A forma do polígono e a elipse são inicialmente selecionadas aleatoriamente. Portanto, o aumento não fornecerá novas informações e não faz sentido nesse caso.
Mas, como pode ser visto no resultado do trabalho, um simples gaussian_filter criou muitos problemas para a rede, gerou muitas informações novas e provavelmente supérfluas.
Bem, para os amantes da simplicidade em sua forma mais pura, tomamos as mesmas elipses dos polígonos, mas sem nenhuma aleatoriedade na cor

o resultado sugere que a cor aleatória não é uma adição simples.
Accuracy 0.9004 loss 0.0315 selected img 251 tested img 9832 : : 1000it [06:46, 1.33it/s]
A rede valeu completamente a informação extraída de 251 imagens, quase quatro vezes menos do que em muitas fotos pintadas com ruído.
O objetivo do artigo é mostrar algumas ferramentas e exemplos de seu trabalho em exemplos frívolos, o lego na caixa de areia. Temos uma ferramenta para comparar duas seqüências de treinamento, podemos avaliar o quanto nosso pré-processamento complica ou simplifica a sequência de treinamento, como é fácil detectar esse ou aquele primitivo na sequência de treinamento.
A possibilidade de aplicar esse exemplo de Lego em casos reais é óbvia, mas os treinamentos reais e as redes de leitores dependem dos próprios leitores.