Ciencia de Datos Retro Probando las Primeras Versiones de YOLO

Data Science Retro Testing YOLO's Initial Versions.

Viajemos 8 años atrás en el tiempo

Detección de objetos con YOLO, Imagen del autor

El mundo de la ciencia de datos está en constante cambio. A menudo, no podemos ver estos cambios solo porque ocurren lentamente, pero después de algún tiempo, es fácil mirar hacia atrás y ver que el panorama se volvió drásticamente diferente. Las herramientas y bibliotecas que estaban en la vanguardia del progreso hace solo 10 años, hoy pueden ser completamente olvidadas.

YOLO (You Only Look Once) es una popular biblioteca de detección de objetos. Su primera versión fue lanzada hace bastante tiempo, en 2015. YOLO funcionaba rápido, proporcionaba buenos resultados y los modelos pre-entrenados estaban disponibles para el público. El modelo rápidamente se volvió popular y el proyecto todavía está mejorando activamente hoy en día. Esto nos da la oportunidad de ver cómo han evolucionado las herramientas y bibliotecas de ciencia de datos a lo largo de los años. En este artículo, probaré diferentes versiones de YOLO, desde la primera V1 hasta la última V8.

Para realizar pruebas adicionales, utilizaré una imagen del tutorial de YOLO de OpenCV:

Imagen de prueba, Fuente © https://opencv-tutorial.readthedocs.io

Los lectores que deseen reproducir los resultados por sí mismos pueden abrir ese enlace y descargar la imagen original.

Empecemos.

YOLO V1..V3

El primer documento, “You Only Look Once: Unified, Real-Time Object Detection”, sobre YOLO fue lanzado en 2015. Y sorprendentemente, la versión de YOLO V1 todavía está disponible para descargar. Como escribió Mr. Redmon, uno de los autores del documento original, está manteniendo esta versión “para fines históricos”, y esto es realmente agradable. ¿Pero podemos ejecutarla hoy? El modelo se distribuye en forma de dos archivos. El archivo de configuración “yolo.cfg” contiene detalles sobre el modelo de red neuronal:

[net]batch=1height=448width=448channels=3momentum=0.9decay=0.0005...[convolutional]batch_normalize=1filters=64size=7stride=2pad=1activation=leaky

Y el segundo archivo “yolov1.weights“, como sugiere el nombre, contiene los pesos del modelo pre-entrenado.

Este tipo de formato no es de PyTorch o Keras. Resultó que el modelo fue creado usando Darknet, un marco de red neuronal de código abierto escrito en C. Este proyecto todavía está disponible en GitHub, pero parece estar abandonado. En el momento de escribir este artículo, hay 164 solicitudes de extracción y 1794 problemas abiertos; los últimos compromisos se realizaron en 2018 y más tarde solo se cambió README.md (bueno, probablemente así es como se ve la muerte del proyecto en el mundo digital moderno).

El proyecto original de Darknet está abandonado, esta es una mala noticia. La buena noticia es que el método readNetFromDarknet todavía está disponible en OpenCV, y está presente incluso en las últimas versiones de OpenCV. Por lo tanto, podemos intentar cargar fácilmente el modelo original de YOLO V1 usando el entorno Python moderno:

import cv2model = cv2.dnn.readNetFromDarknet("yolo.cfg", "yolov1.weights")

Lamentablemente, no funcionó, solo obtuve un error:

darknet_io.cpp:902: error: (-212:Parsing error) Unknown layer type: local in function 'ReadDarknetFromCfgStream'

Resultó que “yolo.cfg” tiene una capa llamada “local”, que no es compatible con OpenCV, y no sé si hay una solución alternativa para eso. De todos modos, la configuración de YOLO V2 ya no tiene esta capa, y este modelo se puede cargar correctamente en OpenCV:

import cv2
model = cv2.dnn.readNetFromDarknet("yolov2.cfg", "yolov2.weights")

Utilizar el modelo no es tan fácil como podríamos esperar. Primero, necesitamos encontrar las capas de salida del modelo:

ln = model.getLayerNames()
output_layers = [ln[i - 1] for i in model.getUnconnectedOutLayers()]

Luego necesitamos cargar la imagen y convertirla en formato binario, que el modelo pueda entender:

img = cv2.imread('horse.jpg')
H, W = img.shape[:2]
blob = cv2.dnn.blobFromImage(img, 1/255.0, (608, 608), swapRB=True, crop=False)

Finalmente, podemos ejecutar la propagación hacia adelante. Un método “forward” ejecutará los cálculos y devolverá las salidas de capa solicitadas:

model.setInput(blob)
outputs = model.forward(output_layers)

Hacer la propagación hacia adelante es sencillo, pero analizar la salida puede ser un poco complicado. El modelo produce vectores de características de 85 dimensiones como salida, donde los primeros 4 dígitos representan rectángulos de objeto, el quinto dígito es una probabilidad de la presencia de un objeto y los últimos 80 dígitos contienen la información de probabilidad para las 80 categorías en las que se entrenó el modelo. Con esta información, podemos dibujar las etiquetas sobre la imagen original:

threshold = 0.5
boxes, confidences, class_ids = [], [], []
# Obtener todas las cajas y etiquetas
for output in outputs:
    for detection in output:
        scores = detection[5:]
        class_id = np.argmax(scores)
        confidence = scores[class_id]
        if confidence > threshold:
            center_x, center_y = int(detection[0] * W), int(detection[1] * H)
            width, height = int(detection[2] * W), int(detection[3] * H)
            left = center_x - width//2
            top = center_y - height//2
            boxes.append([left, top, width, height])
            class_ids.append(class_id)
            confidences.append(float(confidence))
# Combinar cajas usando supresión no máxima
indices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
# Todas las clases COCO
classes = "persona;bicicleta;coche;moto;avión;autobús;tren;camión;barco;semáforo;boca de incendios;señal de stop;parquímetro;banco;pájaro;" \
          "gato;perro;caballo;oveja;vaca;elefante;oso;cebra;jirafa;mochila;paraguas;bolso;corbata;maleta;frisbee;esquís;snowboard;pelota de deportes;cometa;" \
          "béisbol;guante de béisbol;patineta;tabla de surf;raqueta de tenis;botella;copa de vino;taza;tenedor;cuchillo;cuchara;cuenco;plátano;manzana;sándwich;" \
          "naranja;brócoli;zanahoria;perro caliente;pizza;donut;pastel;silla;sofá;planta en maceta;cama;mesa de comedor;inodoro;monitor de tv;ordenador portátil;ratón;mando a distancia;teclado;" \
          "teléfono móvil;microondas;horno;tostadora;fregadero;nevera;libro;reloj;jarrón;tijeras;oso de peluche;secador de pelo;cepillo de dientes".split(";")
# Dibujar rectángulos en la imagen
colors = np.random.randint(0, 255, size=(len(classes), 3), dtype='uint8')
for i in indices.flatten():
    x, y, w, h = boxes[i]
    color = [int(c) for c in colors[class_ids[i]]]
    cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
    text = f"{classes[class_ids[i]]}: {confidences[i]:.2f}"
    cv2.putText(img, text, (x + 2, y - 6), cv2.FONT_HERSHEY_COMPLEX, 0.5, color, 1)
# Mostrar
cv2.imshow('ventana', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Aquí utilizo np.argmax para encontrar la ID de clase con la máxima probabilidad. El modelo YOLO se entrenó utilizando el conjunto de datos COCO (Objetos Comunes en Contexto, Licencia de Atribución 4.0 Creative Commons) y, por razones de simplicidad, coloqué directamente en el código todos los nombres de las 80 etiquetas. También utilicé el método OpenCV NMSBoxes para combinar los rectángulos incrustados.

El resultado final se ve así:

Resultados de YOLO v2, Imagen del autor

¡Ejecutamos con éxito un modelo lanzado en 2016 en un entorno moderno!

La siguiente versión, YOLO v3, fue lanzada dos años después, en 2018, y también podemos ejecutarlo usando el mismo código (los archivos de peso y configuración están disponibles en línea). Como escribieron los autores en el artículo, el nuevo modelo es más preciso, y podemos verificar fácilmente esto:

Resultados de YOLO v3, Imagen del autor

De hecho, un modelo V3 pudo encontrar más objetos en la misma imagen. Aquellos lectores interesados ​​en detalles técnicos pueden leer este artículo de TDS escrito en 2018.

YOLO V5..V7

Como podemos ver, el modelo cargado con el método readNetFromDarknet funciona, pero el código requerido es bastante “de bajo nivel” y engorroso. Los desarrolladores de OpenCV decidieron facilitar la vida, y en 2019, se agregó una nueva clase DetectionModel a la versión 4.1.2. Podemos cargar el modelo YOLO de esta manera; la lógica general sigue siendo la misma, pero la cantidad requerida de código es mucho menor. El modelo devuelve directamente IDs de clase, valores de confianza y rectángulos en una llamada de método:

import cv2model = cv2.dnn_DetectionModel("yolov7.cfg", "yolov7.weights")model.setInputParams(size=(640, 640), scale=1/255, mean=(127.5, 127.5, 127.5), swapRB=True)class_ids, confidences, boxes = model.detect(img, confThreshold=0.5)# Combine boxes together using non-maximum suppressionindices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)# Todas las clases COCOclasses = "persona;bicicleta;coche;moto;avión;bus;tren;camión;barco;semáforo;boca de incendios;señal de stop;parquímetro;banco;pájaro;" \          "gato;perro;caballo;oveja;vaca;elefante;oso;cebra;jirafa;mochila;paraguas;bolso;corbata;maleta;frisbee;esquís;snowboard;pelota deportiva;cometa;" \          "béisbol;guante de béisbol;monopatín;tabla de surf;raqueta de tenis;botella;copa de vino;vaso;cuchara;cuchillo;tenedor;cuenco;plátano;manzana;sándwich;" \          "naranja;brócoli;zanahoria;perro caliente;pizza;donut;tarta;silla;sofá;planta en maceta;cama;mesa de comedor;inodoro;monitor de TV;portátil;ratón;control remoto;teclado;" \          "teléfono celular;microondas;horno;tostadora;fregadero;nevera;libro;reloj;jarrón;tijeras;oso de peluche;secador de pelo;cepillo de dientes".split(";")# Dibujar rectángulos en la imagencolors = np.random.randint(0, 255, size=(len(classes), 3), dtype='uint8')for i in indices.flatten():    x, y, w, h = boxes[i]    color = [int(c) for c in colors[class_ids[i]]]    cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)    text = f"{classes[class_ids[i]]}: {confidences[i]:.2f}"    cv2.putText(img, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)# Mostrarcv2.imshow('window', img)cv2.waitKey(0)cv2.destroyAllWindows()

Como podemos ver, todo el código de bajo nivel necesario para extraer cuadros y valores de confianza de la salida del modelo ya no es necesario.

El resultado de la ejecución de YOLO v7 es, en general, el mismo, pero el rectángulo alrededor del caballo parece más preciso:

Resultados de YOLO v7, Imagen del autor

YOLO V8

La octava versión fue lanzada en 2023, por lo que no puedo considerarla “retro”, al menos en el momento de escribir este texto. Pero solo para comparar los resultados, veamos el código necesario hoy en día para ejecutar YOLO:

from ultralytics import YOLOimport supervision as svmodel = YOLO('yolov8m.pt')results = model.predict(source=img, save=False, save_txt=False, verbose=False)detections = sv.Detections.from_yolov8(results[0])# Crea una lista de etiquetaslabels = []for ind, class_id in enumerate(detections.class_id):    labels.append(f"{model.model.names[class_id]}: {detections.confidence[ind]:.2f}")# Dibuja los rectángulos en la imagenbox_annotator = sv.BoxAnnotator(thickness=2, text_thickness=1, text_scale=0.4)box_annotator.annotate(scene=img, detections=detections, labels=labels)# Muestracv2.imshow('window', img)cv2.waitKey(0)cv2.destroyAllWindows()

Como podemos ver, el código se volvió aún más compacto. Ya no necesitamos preocuparnos por los nombres de las etiquetas del conjunto de datos (el modelo proporciona una propiedad “nombres”) o cómo dibujar rectángulos y etiquetas en la imagen (hay una clase especial BoxAnnotator para eso). Incluso ya no necesitamos descargar los pesos del modelo; la biblioteca lo hará automáticamente por nosotros. ¡En comparación con 2016, el programa de 2023 “encogió” de alrededor de 50 a alrededor de 5 líneas de código! Obviamente es una gran mejora, y los desarrolladores modernos ya no necesitan conocer la propagación hacia adelante o el formato del nivel de salida. El modelo simplemente funciona como una caja negra con algo de “magia” dentro. ¿Es bueno o malo? No lo sé 🙂

En cuanto al resultado en sí, es más o menos similar:

Resultados de YOLO v8, imagen del autor

El modelo funciona bien, y al menos en mi computadora, la velocidad de cálculo mejoró en comparación con la v7, quizás debido al mejor uso de la GPU.

Conclusión

En este artículo, pudimos probar casi todos los modelos de YOLO, desde 2016 hasta 2023. A primera vista, los intentos de ejecutar un modelo lanzado hace casi 10 años pueden parecer una pérdida de tiempo. Pero para mí, aprendí mucho mientras hacía estas pruebas:

  • Fue interesante ver cómo las herramientas y bibliotecas populares de ciencia de datos han evolucionado a lo largo de los años. La tendencia de pasar de código de bajo nivel a métodos de alto nivel, que hacen todo e incluso descargan el modelo pre-entrenado antes de la ejecución (al menos por ahora, sin pedir una clave de suscripción todavía, pero ¿quién sabe qué será lo próximo dentro de 10 años?), parece clara. ¿Es bueno o malo? Esta es una pregunta interesante y abierta.
  • Fue importante saber que OpenCV es “nativamente” capaz de ejecutar modelos de aprendizaje profundo. Esto permite utilizar modelos de redes neuronales no solo en grandes frameworks como PyTorch o Keras, sino también en aplicaciones de Python puro o incluso C++. No todas las aplicaciones se ejecutan en la nube con recursos virtualmente ilimitados. El mercado de IoT está creciendo, y esto es especialmente importante para ejecutar redes neuronales en dispositivos de baja potencia como robots, cámaras de vigilancia o timbres inteligentes.

En el próximo artículo, lo probaré con más detalle y mostraré cómo YOLO v8 se ejecuta en una placa de baja potencia como una Raspberry Pi, y podremos probar tanto las versiones de Python como las de C++. Manténganse al tanto.

Si disfrutaste esta historia, no dudes en suscribirte a Zepes, y recibirás notificaciones cuando se publiquen mis nuevos artículos, así como acceso completo a miles de historias de otros autores.

We will continue to update Zepes; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

Inteligencia Artificial

Conoce DiffusionDet Un Modelo de Inteligencia Artificial (IA) Que Utiliza Difusión para la Detección de Objetos

La detección de objetos es una técnica poderosa para identificar objetos en imágenes y videos. Gracias al aprendizaje...

Inteligencia Artificial

PhotoGuard de MIT utiliza inteligencia artificial para defenderse contra la manipulación de imágenes por inteligencia artificial

A medida que la inteligencia artificial (IA) avanza, la capacidad de generar y manipular imágenes hiperrealistas se v...

Inteligencia Artificial

Descifrando los misterios de los modelos de lenguaje grandes un análisis detallado de las funciones de influencia y su escalabilidad

Los modelos de lenguaje grandes (LLMs) han acelerado el desarrollo en varios campos del mundo real y han demostrado h...

Inteligencia Artificial

Utilice un modelo de base de IA generativa para la síntesis y respuesta a preguntas utilizando sus propios datos

Los modelos de lenguaje grandes (LLMs) se pueden utilizar para analizar documentos complejos y proporcionar resúmenes...