7.2. O que o ML mudou

O módulo image inclui alguns métodos de deteção legadosfind_features() para deteção de rostos por cascata de Haar, find_eye() para o localizador fixo de pupila, find_hog() para resumos de direção de gradiente, os percursos find_keypoints() e find_lbp() para pontos-chave arbitrários. Todos eles continuam a funcionar; todos eles foram superados pelo pipeline de aprendizagem automática.

7.2.1. A divisão clássica: resumos desenhados manualmente, decisões aprendidas

Um pipeline clássico de visão era uma operação em dois passos. O primeiro passo convertia pixels brutos num conjunto compacto de números escolhidos para resumir o conteúdo da imagem – não os próprios valores de pixel, mas uma descrição mais curta dos padrões presentes e onde apareciam. O segundo passo pegava nesse resumo e tomava uma decisão: rosto ou não, este objeto ou aquele, mesmo alvo ou diferente.

A divisão importava porque os dois passos tinham autores diferentes. O primeiro passo era escrito por um humano. Alguém decidiu que a diferença de brilho entre dois retângulos específicos era um bom resumo de uma região de olho, que a direção de aresta dominante em cada célula de uma grelha era um bom resumo do contorno de uma pessoa de pé, que o padrão claro-ou-escuro em redor de cada pixel era um bom resumo da textura local. Cada uma dessas escolhas era um algoritmo escrito manualmente – escrito, depurado e publicado. Os métodos legados acima referidos eram todos resumos deste tipo que se tornaram ferramentas padrão:

  • find_features() resume uma janela da imagem somando o brilho dentro de vários retângulos e comparando os totais. Os layouts dos retângulos foram escolhidos porque os rostos humanos mostram contrastes fiáveis de claro-contra-escuro: sobrancelhas contra bochechas, órbitas oculares contra testa, nariz contra a pele circundante.

  • find_hog() resume uma imagem percorrendo uma grelha de pequenas células e registando qual a direção de aresta dominante em cada célula. A grelha foi escolhida porque o contorno de uma pessoa de pé produz um padrão reconhecível de direções de aresta independentemente da roupa ou da iluminação.

  • find_lbp() resume a vizinhança de cada pixel codificando quais dos pixels circundantes são mais brilhantes e quais são mais escuros. A codificação foi escolhida porque estes padrões de mais-brilhante-que / mais-escuro-que capturam a textura de uma superfície independentemente da iluminação geral.

  • find_keypoints() encontra pontos de canto na imagem e descreve a área em redor de cada canto de forma que se mantém constante quando o canto é rodado. O esquema de canto-e-rotação foi escolhido porque os mesmos cantos reaparecem quando uma cena é vista de um ângulo diferente.

Uma vez escrito manualmente um resumo, um pequeno passo de aprendizagem por cima dele podia combinar os números numa decisão. O algoritmo de deteção de rostos adicionou um passo de aprendizagem ao resumo de diferença de retângulos, treinando-o em imagens de rosto e não-rosto etiquetadas para aprender quais as combinações de diferenças que assinalam um rosto. O resumo de direção de aresta alimentou um passo de aprendizagem treinado em imagens de pessoa e não-pessoa etiquetadas. Os descritores de cantos alimentaram um passo de correspondência que aprendeu o peso a dar a cada canto. Cada um destes segundos passos é um algoritmo de aprendizagem – pequeno para os padrões modernos, mas ainda assim um algoritmo de aprendizagem.

A divisão de contribuição era o que importava. O humano contribuía com o resumo. A máquina aprendia a combinação. Adicionar um novo alvo significava escrever um novo resumo.

7.2.2. O que as redes neuronais mudaram

Uma rede neuronal elimina a divisão. As primeiras camadas da rede fazem o trabalho de resumo que os algoritmos escritos manualmente faziam – detetando arestas, cantos, barras orientadas, texturas, exatamente as coisas para as quais cada um dos métodos legados listados acima estava ajustado – mas não são escritas manualmente. São aprendidas a partir dos mesmos dados de treino de que o passo de decisão é aprendido, numa única passagem de treino que ajusta ambas as metades da rede em simultâneo. As camadas mais profundas fazem a combinação que o pequeno passo de aprendizagem por cima dos resumos escritos manualmente fazia, também aprendida, na mesma passagem.

A mudança em quem projeta o quê é total:

  • O humano projeta a entrada – fotogramas capturados de um determinado tamanho e formato.

  • O humano projeta a saída – o layout do tensor de resultado (uma pontuação por classe para classificação, uma lista de caixas para deteção, uma grelha de pontos-chave para marcos).

  • O humano fornece dados de treino etiquetados – exemplos suficientes do alvo e exemplos suficientes de não-alvos para que o processo de treino tenha algo a aprender.

Tudo entre a entrada e a saída é gerado pelo processo de treino. Não existe um passo separado de escrita de resumos. As primeiras camadas estabilizam em detetores de arestas e texturas não porque alguém as programou assim, mas porque a deteção de arestas e texturas é o que faz as previsões da rede coincidir com as etiquetas. As camadas mais profundas estabilizam em detetores de formas e objetos pela mesma razão. Ambas as metades são treinadas em conjunto, o que permite que os resumos que cada camada produz sejam exatamente os resumos que a camada seguinte necessita – não os genéricos com que um pipeline escrito manualmente tinha de se contentar.

7.2.3. Composição com o módulo image

Os pipelines de redes neuronais ainda capturam através das mesmas APIs do sensor, desenham resultados através dos mesmos primitivos draw_rectangle() e draw_circle(), e delimitam o trabalho através dos mesmos ROIs (x, y, w, h). Um pipeline típico captura um fotograma, opcionalmente encontra um alvo grosseiro com um detetor clássico como find_blobs() e passa a sua caixa delimitadora para a inferência como ROI, executa a inferência e anota as deteções devolvidas de volta no fotograma original. Os primitivos clássicos são o substrato; a rede é o novo passo no meio.