Criar convoluções e executar pools

1. Antes de começar

Neste codelab, você aprenderá sobre as convoluções e por que elas são tão poderosas em cenários de visão computacional.

No codelab anterior, você criou uma rede neural profunda simples (DNN, na sigla em inglês) para visão computacional de itens de moda. Isso era limitado porque exigia que o item de vestuário fosse a única coisa na imagem e precisava ser centralizado.

Obviamente, esse não é um cenário realista. Você precisa que a DNN consiga identificar o item de vestuário em imagens com outros objetos ou onde ele não esteja posicionado na frente e no centro. Para fazer isso, você precisará usar convoluções.

Pré-requisitos

Este codelab baseia-se em um trabalho concluído em duas partes anteriores: Conheça o mundo de aprendizado de máquina e Crie um modelo de visão computacional. Conclua esses codelabs antes de continuar.

O que você aprenderá

  • O que são convoluções
  • Como criar um mapa de elementos
  • O que é um pool

O que você vai criar

  • Um mapa de recursos de uma imagem

O que é necessário

O código do restante do codelab está sendo executado no Colab.

Você também precisará do TensorFlow instalado e das bibliotecas instaladas no codelab anterior.

2. O que são convoluções?

Uma convolução é um filtro que passa por uma imagem, processa e extrai elementos importantes.

Digamos que você tenha a imagem de uma pessoa usando um tênis. Como você detectaria a presença de um tênis na imagem? Para que seu programa "veja" a imagem como um tênis, você precisará extrair os elementos importantes e desfocar os recursos não essenciais. Isso é chamado de mapeamento de recursos.

Em teoria, o processo de mapeamento de recursos é simples. Você digitalizará cada pixel da imagem e analisará seus pixels vizinhos. Multiplique os valores desses pixels pelos pesos equivalentes em um filtro.

Exemplo:

Convolução na imagem

Nesse caso, é especificada uma matriz de convolução 3 x 3, ou kernel de imagem.

O valor atual do pixel é 192. Você pode calcular o valor do novo pixel analisando os valores vizinhos, multiplicando-os pelos valores especificados no filtro e fazendo com que o novo valor seja o valor final.

Agora é hora de explorar como as convoluções funcionam criando uma convolução básica em uma imagem em escala de cinza 2D.

Você demonstrará isso com a imagem ascendente da SciPy. É uma boa imagem integrada com muitos ângulos e linhas.

3. Começar a programar

Comece importando algumas bibliotecas do Python e a imagem de subida:

import cv2
import numpy as np
from scipy import misc
i = misc.ascent()

Em seguida, use a biblioteca Pyplot matplotlib para desenhar a imagem e ver qual é a aparência dela:

import matplotlib.pyplot as plt
plt.grid(False)
plt.gray()
plt.axis('off')
plt.imshow(i)
plt.show()

edb460dd5397f7f4.png

Você pode ver que a imagem é uma escada. Existem vários recursos que você pode experimentar e isolar. Por exemplo, há linhas verticais fortes.

A imagem é armazenada como uma matriz NumPy, então podemos criar a imagem transformada apenas copiando essa matriz. As variáveis size_x e size_y conterão as dimensões da imagem para que você possa repeti-la mais tarde.

i_transformed = np.copy(i)
size_x = i_transformed.shape[0]
size_y = i_transformed.shape[1]

4. Criar a matriz de convolução

Primeiro, crie uma matriz de convolução (ou kernel) como uma matriz 3x3:

# This filter detects edges nicely
# It creates a filter that only passes through sharp edges and straight lines. 
# Experiment with different values for fun effects.
#filter = [ [0, 1, 0], [1, -4, 1], [0, 1, 0]] 
# A couple more filters to try for fun!
filter = [ [-1, -2, -1], [0, 0, 0], [1, 2, 1]]
#filter = [ [-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]
 # If all the digits in the filter don't add up to 0 or 1, you 
# should probably do a weight to get it to do so
# so, for example, if your weights are 1,1,1 1,2,1 1,1,1
# They add up to 10, so you would set a weight of .1 if you want to normalize them
weight  = 1

Agora, calcule os pixels de saída. Itere na imagem, deixando uma margem de 1 pixel e multiplique cada um dos vizinhos do pixel atual pelo valor definido no filtro.

Isso significa que o vizinho do pixel atual acima e à esquerda será multiplicado pelo item superior esquerdo no filtro. Em seguida, multiplique o resultado pelo peso e verifique se ele está no intervalo de 0 a 255.

Por fim, carregue o novo valor na imagem transformada:

for x in range(1,size_x-1):
  for y in range(1,size_y-1):
      output_pixel = 0.0
      output_pixel = output_pixel + (i[x - 1, y-1] * filter[0][0])
      output_pixel = output_pixel + (i[x, y-1] * filter[0][1])
      output_pixel = output_pixel + (i[x + 1, y-1] * filter[0][2])
      output_pixel = output_pixel + (i[x-1, y] * filter[1][0])
      output_pixel = output_pixel + (i[x, y] * filter[1][1])
      output_pixel = output_pixel + (i[x+1, y] * filter[1][2])
      output_pixel = output_pixel + (i[x-1, y+1] * filter[2][0])
      output_pixel = output_pixel + (i[x, y+1] * filter[2][1])
      output_pixel = output_pixel + (i[x+1, y+1] * filter[2][2])
      output_pixel = output_pixel * weight
      if(output_pixel<0):
        output_pixel=0
      if(output_pixel>255):
        output_pixel=255
      i_transformed[x, y] = output_pixel

5. Analisar os resultados

Agora, plote a imagem para ver o efeito que transmite o filtro sobre ela:

# Plot the image. Note the size of the axes -- they are 512 by 512
plt.gray()
plt.grid(False)
plt.imshow(i_transformed)
#plt.axis('off')
plt.show()   

48ff667b2df812ad.png

Considere os valores de filtro a seguir e o impacto deles na imagem.

O uso de [-1,0,1,-2,0,2,-1,0,1] fornece um conjunto muito forte de linhas verticais:

Como detectar o filtro de linhas verticais

O uso de [-1,-2,-1,0,0,0,1,2,1] fornece linhas horizontais:

Como detectar linhas horizontais

Explore valores diferentes! Além disso, use filtros de tamanhos diferentes, como 5 x 5 ou 7 x 7.

6. Como entender o pool

Agora que você identificou os recursos essenciais da imagem, o que fazer? Como usar o mapa de recursos resultante para classificar imagens?

Assim como as convoluções, o pooling ajuda na detecção de recursos. As camadas de pool reduzem a quantidade total de informações em uma imagem e mantêm os recursos detectados como presente.

Há vários tipos diferentes de pool, mas você usará um que chama de pool máximo (máximo).

Itere na imagem e, em cada ponto, considere o pixel e seus vizinhos imediatos para a direita, abaixo e abaixo dele. Use a maior delas (portanto, o pool max) e carregue-a na nova imagem. Assim, a nova imagem terá um quarto do tamanho da antiga. Pool máximo

7. Escreva o código para pooling

O código a seguir mostra um pooling (2, 2). Execute-o para ver a saída.

Você verá que, embora a imagem tenha um quarto do tamanho do original, todos os recursos foram mantidos.

new_x = int(size_x/2)
new_y = int(size_y/2)
newImage = np.zeros((new_x, new_y))
for x in range(0, size_x, 2):
  for y in range(0, size_y, 2):
    pixels = []
    pixels.append(i_transformed[x, y])
    pixels.append(i_transformed[x+1, y])
    pixels.append(i_transformed[x, y+1])
    pixels.append(i_transformed[x+1, y+1])
    pixels.sort(reverse=True)
    newImage[int(x/2),int(y/2)] = pixels[0]
 
# Plot the image. Note the size of the axes -- now 256 pixels instead of 512
plt.gray()
plt.grid(False)
plt.imshow(newImage)
#plt.axis('off')
plt.show()

1f5ebdafd1db2595.png

Observe os eixos do gráfico. A imagem agora tem 256 x 256, um quarto do tamanho original, e os recursos detectados foram aprimorados, apesar de menos dados estarem na imagem.

8. Parabéns

Você criou seu primeiro modelo de visão computacional! Para saber como aprimorar ainda mais seus modelos de visão computacional, consulte Criar redes neurais convolucionais (CNNs) para aprimorar a visão computacional.