Como criar rótulo de classe para aumento do mosaico na classificação de imagens?

Dec 07 2020

Para criar um rótulo de classe CutMixou MixUpaumento de tipo, podemos usar betacomo np.random.betaou scipy.stats.betae fazer o seguinte para dois rótulos:

label = label_one*beta + (1-beta)*label_two

Mas e se tivermos mais de duas imagens? No YoLo4 , eles tentaram um aumento interessante chamado Aumento do mosaico para problemas de detecção de objetos. Ao contrário de CutMixou MixUp, este aumento cria amostras aumentadas com 4 imagens. Em casos de detecção de objetos, podemos calcular a mudança das coordenadas de cada instância e, assim, ser possível obter a verdade básica adequada aqui . Mas apenas para casos de classificação de imagem, como podemos fazer isso?

Aqui está um começo .

import tensorflow as tf
import matplotlib.pyplot as plt 
import random

(train_images, train_labels), (test_images, test_labels) = \
tf.keras.datasets.cifar10.load_data()
train_images = train_images[:10,:,:]
train_labels = train_labels[:10]
train_images.shape, train_labels.shape

((10, 32, 32, 3), (10, 1))

Aqui está uma função que escrevemos para este aumento; (muito feio com um inner-outerloop! Por favor, sugira se podemos fazer isso de forma eficiente.)

def mosaicmix(image, label, DIM, minfrac=0.25, maxfrac=0.75):
    '''
    image, label : batches of samples 
    '''
    xc, yc  = np.random.randint(DIM * minfrac, DIM * maxfrac, (2,))
    indices = np.random.permutation(int(image.shape[0]))
    mosaic_image = np.zeros((DIM, DIM, 3), dtype=np.float32)
    final_imgs = []
    final_lbs  = []
    
    # Iterate over the full indices 
    for j in range(len(indices)): 
        # Take 4 sample for to create a mosaic sample randomly 
        rand4indices = [j] + random.sample(list(indices), 3) 
        
        # Make mosaic with 4 samples 
        for i in range(len(rand4indices)):
            if i == 0:    # top left
                x1a, y1a, x2a, y2a =  0,  0, xc, yc
                x1b, y1b, x2b, y2b = DIM - xc, DIM - yc, DIM, DIM # from bottom right        
            elif i == 1:  # top right
                x1a, y1a, x2a, y2a = xc, 0, DIM , yc
                x1b, y1b, x2b, y2b = 0, DIM - yc, DIM - xc, DIM # from bottom left
            elif i == 2:  # bottom left
                x1a, y1a, x2a, y2a = 0, yc, xc, DIM
                x1b, y1b, x2b, y2b = DIM - xc, 0, DIM, DIM-yc   # from top right
            elif i == 3:  # bottom right
                x1a, y1a, x2a, y2a = xc, yc,  DIM, DIM
                x1b, y1b, x2b, y2b = 0, 0, DIM-xc, DIM-yc    # from top left
                
            # Copy-Paste
            mosaic_image[y1a:y2a, x1a:x2a] = image[i,][y1b:y2b, x1b:x2b]

        # Append the Mosiac samples
        final_imgs.append(mosaic_image)
        
    return final_imgs, label

As amostras aumentadas, atualmente com os rótulos errados.

data, label = mosaicmix(train_images, train_labels, 32)
plt.imshow(data[5]/255)


No entanto, aqui estão mais alguns exemplos de como motivar você. Os dados são do concurso Folha de Mandioca .


No entanto, para obter o rótulo adequado dessa amostra aumentada, tentamos algo assim, digamos que para cada interação sobre os lotes dentro do loop externo e do loop interno se pudermos calcular a distribuição das 4 amostras, como cada uma delas cobre a área interna mosaic_image, de modo que possamos multiplicar cada uma com probabilidade de distribuição de a.

    # Iterate over the full indices 
    for j in range(len(indices)): 
        
        b = tf.random.uniform([],0,1) # this is beta dist with alpha=1.0
        P = tf.cast( tf.random.uniform([],0,1)<=1.0, tf.int32) 

        for i in range(len(rand4indices)):
            ....
            WIDTH = tf.cast( DIM * tf.math.sqrt(1-b),tf.int32) * P  
            a = tf.cast(WIDTH*WIDTH/DIM/DIM,tf.float32)
            

Respostas

4 UzzalPodder Dec 07 2020 at 20:43

Já sabemos que, no CutMix , λé um número flutuante da distribuição beta Beta (α, α). Vimos, quando α=1, ele tem o melhor desempenho. Agora, se concedermos α==1sempre, podemos dizer que λé uma amostra da distribuição uniforme. .

Podemos simplesmente dizer que λé apenas um número de ponto flutuante cujo valor será de 0 a 1.

Portanto, apenas para 2 imagens, se usarmos λpara a 1ª imagem, podemos calcular a parte desconhecida restante simplesmente por 1-λ.

Mas para 3 imagens, se usarmos λpara a 1ª imagem, não podemos calcular outras 2 incógnitas daquela única λ. Se realmente quisermos fazer isso, precisamos de 2 números aleatórios para 3 imagens. Da mesma forma, podemos dizer que para o nnúmero de imagens, precisamos da n-1variável aleatória número. E em todos os casos, o somatório deve ser 1. (por exemplo, λ + (1-λ) == 1). Se a soma não for 1, o rótulo estará errado!

Para este propósito, a distribuição de Dirichlet pode ser útil porque ajuda a gerar quantidades que somam 1. Uma variável aleatória distribuída por Dirichlet pode ser vista como uma generalização multivariada de uma distribuição Beta.

>>> np.random.dirichlet((1, 1), 1)  # for 2 images. Equivalent to λ and (1-λ)
array([[0.92870347, 0.07129653]])  
>>> np.random.dirichlet((1, 1, 1), 1)  # for 3 images.
array([[0.38712673, 0.46132787, 0.1515454 ]])
>>> np.random.dirichlet((1, 1, 1, 1), 1)  # for 4 images.
array([[0.59482542, 0.0185333 , 0.33322484, 0.05341645]])

No CutMix , o tamanho da parte recortada de uma imagem tem uma relação com a λqual pesa os rótulos correspondentes.

Portanto, para múltiplos λ, você também precisa calculá-los de acordo.

# let's say for 4 images
# I am not sure the proper way. 

image_list = [4 images]
label_list = [4 label]
new_img = np.zeros((w, h))

beta_list = np.random.dirichlet((1, 1, 1, 1), 1)[0]
for idx, beta in enumerate(beta_list):
    x0, y0, w, h = get_cropping_params(beta, full_img)  # something like this
    new_img[x0, y0, w, h] = image_list[idx][x0, y0, w, h]
    label_list[idx] = label_list[idx] * beta
1 MostlyClueless Jan 13 2021 at 13:46

Outra maneira de ver esse problema é considerar as linhas de separação para as dimensões de largura e altura. Ao construir a imagem em mosaico, o objetivo é combinar 4 imagens em uma única imagem. Podemos conseguir isso amostrando aleatoriamente os pontos médios (denotando os pontos de separação) em cada dimensão. Isso remove o requisito bastante complicado de amostrar 4 números somando 1. Em vez disso, o objetivo agora é amostrar 2 valores independentes de uma distribuição uniforme - uma alternativa muito mais simples e intuitiva.

Então, essencialmente, amostramos dois valores:

w = np.random.uniform(0, 1)
h = np.random.uniform(0, 1)

Para gerar mosaicos realistas onde cada imagem tem uma contribuição perceptível, podemos amostrar valores de [0,25 0,75], ao invés de [0, 1]

Esses dois valores são suficientes para parametrizar o problema do mosaico. Cada imagem no mosaico ocupa áreas abrangidas pelas seguintes coordenadas: Considere que a imagem do mosaico tem dimensões LxH e os pontos médios de cada dimensão são representados por w e h respectivamente.

  • superior esquerdo - (0, 0) a (w, h)
  • superior direito - (w, 0) a (W, h)
  • inferior esquerdo - (0, h) a (w, H)
  • inferior direito - (w, h) a (W, H)

Os pontos médios amostrados também ajudam no cálculo dos rótulos das classes. Vamos supor que decidamos usar a área que cada imagem ocupa dentro do mosaico como sua contribuição correspondente para o rótulo geral da classe. Por exemplo, considere 4 imagens pertencentes a 4 classes {0, 1, 2, 3}. Agora suponha que a imagem '0' ocupe a parte superior esquerda, '1' a parte superior direita, '2' a parte inferior esquerda e '3' a parte inferior direita. Podemos construir o rótulo de classe 'L' da seguinte maneira

você pode ver a equação neste link