7.2. Qué cambió el aprendizaje automático

El módulo image incluye un puñado de métodos de detección heredadosfind_features() para la detección de rostros con cascada de Haar, find_eye() para el buscador fijo de pupilas, find_hog() para resúmenes de dirección de gradiente, y las vías find_keypoints() y find_lbp() para puntos clave arbitrarios. Todos ellos siguen funcionando; todos ellos han sido reemplazados por el flujo de aprendizaje automático.

7.2.1. La división clásica: resúmenes diseñados a mano, decisiones aprendidas

Un flujo de visión clásico era algo de dos pasos. El primer paso convertía los píxeles en bruto en un conjunto compacto de números elegidos para resumir lo que había en la imagen – no los valores de los píxeles en sí, sino una descripción más breve de qué patrones aparecían y dónde. El segundo paso tomaba ese resumen y tomaba una decisión: rostro o no, este objeto o aquel, el mismo objetivo o uno diferente.

La división importaba porque los dos pasos tenían autores distintos. El primer paso lo escribía un humano. Alguien se sentaba y decidía que la diferencia de brillo entre dos rectángulos concretos era un buen resumen de la región de un ojo, que la dirección dominante de los bordes en cada celda de una cuadrícula era un buen resumen de la silueta de una persona de pie, que el patrón de claro u oscuro alrededor de cada píxel era un buen resumen de la textura local. Cada una de esas elecciones era un algoritmo escrito a mano – escrito, depurado y publicado. Los métodos heredados anteriores eran todos resúmenes de este tipo que se habían convertido en herramientas estándar:

  • find_features() resume una ventana de la imagen sumando el brillo dentro de varios rectángulos y comparando los totales. Las disposiciones de los rectángulos se eligieron porque los rostros humanos muestran contrastes fiables de claro contra oscuro: cejas contra mejillas, cuencas de los ojos contra la frente, nariz contra la piel circundante.

  • find_hog() resume una imagen recorriendo una cuadrícula de celdas pequeñas y registrando qué dirección de borde domina en cada celda. La cuadrícula se eligió porque la silueta de una persona de pie produce un patrón reconocible de direcciones de borde independientemente de la ropa o la iluminación.

  • find_lbp() resume el vecindario de cada píxel codificando cuáles de los píxeles que lo rodean son más brillantes y cuáles más oscuros. La codificación se eligió porque estos patrones de más brillante/más oscuro capturan la textura de una superficie independientemente de la iluminación general.

  • find_keypoints() encuentra puntos de esquina en la imagen y describe el área alrededor de cada esquina de una manera que permanece igual cuando la esquina se rota. El esquema de esquina y rotación se eligió porque las mismas esquinas reaparecen cuando una escena se ve desde un ángulo diferente.

Una vez que se había escrito a mano un resumen, un pequeño paso de aprendizaje sobre él podía combinar los números en una decisión. El algoritmo de detección de rostros acoplaba un paso de aprendizaje al resumen de diferencias de rectángulos, entrenándolo con imágenes etiquetadas de rostro y de no rostro para aprender qué combinaciones de diferencias señalan un rostro. El resumen de dirección de bordes alimentaba un paso de aprendizaje entrenado con imágenes etiquetadas de persona y de no persona. Los descriptores de esquinas alimentaban un paso de emparejamiento que aprendía cuánto peso dar a cada esquina. Cada uno de estos segundos pasos es un algoritmo de aprendizaje – pequeño según los estándares modernos, pero un algoritmo de aprendizaje.

La división de las contribuciones es lo que importaba. El humano aportaba el resumen. La máquina aprendía la combinación. Añadir un nuevo objetivo significaba escribir un nuevo resumen.

7.2.2. Qué cambiaron las redes neuronales

Una red neuronal borra la división. Las primeras capas de la red hacen el trabajo de resumen que solían hacer los algoritmos escritos a mano – detectar bordes, esquinas, barras orientadas, texturas, exactamente las cosas que cada uno de los métodos heredados enumerados anteriormente estaba ajustado para detectar – pero no están escritas a mano. Se aprenden a partir de los mismos datos de entrenamiento de los que se aprende el paso de decisión, en una única pasada de entrenamiento que ajusta ambas mitades de la red a la vez. Las capas más profundas hacen la combinación que solía hacer el pequeño paso de aprendizaje sobre los resúmenes escritos a mano, también aprendida, en la misma pasada.

El cambio en quién diseña qué es total:

  • El humano diseña la entrada – fotogramas capturados de un tamaño y formato determinados.

  • El humano diseña la salida – la disposición del tensor de resultado (una puntuación por clase para clasificación, una lista de cuadros para detección, una cuadrícula de puntos clave para puntos de referencia).

  • El humano aporta datos de entrenamiento etiquetados – suficientes ejemplos del objetivo y suficientes ejemplos de no objetivos como para que el proceso de entrenamiento tenga algo de lo que aprender.

Todo lo que hay entre la entrada y la salida lo genera el proceso de entrenamiento. No hay un paso aparte de escritura de resúmenes. Las capas tempranas se asientan en detectores de bordes y texturas no porque alguien las escribiera de esa manera, sino porque la detección de bordes y texturas es lo que hace que las predicciones de la red coincidan con las etiquetas. Las capas más profundas se asientan en detectores de formas y objetos por la misma razón. Ambas mitades se entrenan juntas, lo que permite que los resúmenes que produce cada capa sean exactamente los resúmenes que necesita la capa siguiente – no los genéricos con los que tenía que conformarse un flujo escrito a mano.

7.2.3. Componer con el módulo image

Los flujos basados en redes neuronales siguen capturando a través de las mismas API del sensor, dibujan resultados mediante las mismas primitivas draw_rectangle() y draw_circle(), y acotan el trabajo mediante las mismas ROI (x, y, w, h). Un flujo típico captura un fotograma, opcionalmente encuentra un objetivo aproximado con un detector clásico como find_blobs() y pasa su cuadro delimitador a la inferencia como una ROI, ejecuta la inferencia y anota las detecciones devueltas de nuevo en el fotograma original. Las primitivas clásicas son el sustrato; la red es el nuevo paso en el medio.