Testeo de Sistemas de Visión por Computadora
La visión por computadora impulsa vehículos autónomos, diagnósticos médicos, sistemas de seguridad y QA en manufactura. A diferencia del software tradicional, los modelos CV lidian con ambigüedad, variabilidad visual y complejidad del mundo real.
El testing de visión por computadora es una disciplina especializada dentro del testing de sistemas de IA y ML. Las técnicas de diseño de casos de prueba tradicionales se adaptan para manejar datasets de imágenes y métricas de precisión. Para integrar estas pruebas en pipelines automatizados, consulta nuestra guía de testing continuo en DevOps.
Testear sistemas CV requiere evaluar precisión bajo condiciones diversas, robustez adversarial, equidad entre demografías y restricciones de rendimiento en tiempo real.
Estrategias de Testing Core
1. Métricas de Precisión
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
import numpy as np
class EvaluadorModeloCV:
def __init__(self, modelo):
self.modelo = modelo
def evaluar_clasificacion(self, imagenes_test, etiquetas_verdaderas):
"""Evaluar modelo de clasificación"""
predicciones = self.modelo.predict(imagenes_test)
etiquetas_predichas = np.argmax(predicciones, axis=1)
# Precisión general
precision = accuracy_score(etiquetas_verdaderas, etiquetas_predichas)
# Métricas por clase
precision_clase, recall, f1, support = precision_recall_fscore_support(
etiquetas_verdaderas,
etiquetas_predichas,
average=None
)
return {
'precision': precision,
'metricas_por_clase': {
self.modelo.nombres_clases[i]: {
'precision': precision_clase[i],
'recall': recall[i],
'f1_score': f1[i],
'support': support[i]
}
for i in range(len(self.modelo.nombres_clases))
}
}
2. Validación de Dataset
import cv2
from collections import Counter
class ValidadorDataset:
def verificar_balance_clases(self):
"""Detectar desbalance de clases"""
conteos_etiquetas = Counter(self.dataset.etiquetas)
total = len(self.dataset.etiquetas)
reporte_desbalance = {}
for nombre_clase, conteo in conteos_etiquetas.items():
porcentaje = (conteo / total) * 100
reporte_desbalance[nombre_clase] = {
'conteo': conteo,
'porcentaje': porcentaje,
'desbalanceado': porcentaje < 5 or porcentaje > 50
}
return reporte_desbalance
def analizar_calidad_imagen(self):
"""Verificar imágenes de baja calidad"""
problemas_calidad = []
for ruta_img in self.dataset.rutas_imagenes:
img = cv2.imread(ruta_img)
# Verificar resolución
alto, ancho = img.shape[:2]
if alto < 224 or ancho < 224:
problemas_calidad.append({
'imagen': ruta_img,
'problema': 'baja_resolucion',
'resolucion': f"{ancho}x{alto}"
})
# Verificar brillo
gris = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
brillo = np.mean(gris)
if brillo < 30 or brillo > 225:
problemas_calidad.append({
'imagen': ruta_img,
'problema': 'brillo_pobre',
'brillo': brillo
})
return problemas_calidad
3. Testing Adversarial
import tensorflow as tf
class TestadorAdversarial:
def __init__(self, modelo):
self.modelo = modelo
def ataque_fgsm(self, imagen, etiqueta_verdadera, epsilon=0.01):
"""Ataque Fast Gradient Sign Method"""
tensor_imagen = tf.convert_to_tensor(imagen[np.newaxis, ...])
with tf.GradientTape() as tape:
tape.watch(tensor_imagen)
prediccion = self.modelo(tensor_imagen)
perdida = tf.keras.losses.sparse_categorical_crossentropy(
[etiqueta_verdadera], prediccion
)
gradiente = tape.gradient(perdida, tensor_imagen)
grad_firmado = tf.sign(gradiente)
# Crear imagen adversarial
imagen_adversarial = imagen + epsilon * grad_firmado.numpy()[0]
imagen_adversarial = np.clip(imagen_adversarial, 0, 1)
# Testear si ataque tuvo éxito
pred_adv = self.modelo.predict(imagen_adversarial[np.newaxis, ...])
etiqueta_adv = np.argmax(pred_adv)
return {
'etiqueta_original': etiqueta_verdadera,
'etiqueta_adversarial': etiqueta_adv,
'ataque_exitoso': etiqueta_adv != etiqueta_verdadera,
'imagen_adversarial': imagen_adversarial
}
def testear_robustez(self, conjunto_test, valores_epsilon=[0.01, 0.05, 0.1]):
"""Testear robustez entre fuerzas de ataque"""
resultados = {eps: {'exitos': 0, 'total': 0} for eps in valores_epsilon}
for imagen, etiqueta in conjunto_test:
for epsilon in valores_epsilon:
resultado = self.ataque_fgsm(imagen, etiqueta, epsilon)
resultados[epsilon]['total'] += 1
if resultado['ataque_exitoso']:
resultados[epsilon]['exitos'] += 1
# Calcular puntajes de robustez
puntajes_robustez = {
eps: 1 - (datos['exitos'] / datos['total'])
for eps, datos in resultados.items()
}
return puntajes_robustez
4. Testing de Augmentación
import albumentations as A
class TestadorAugmentacion:
def testear_con_augmentaciones(self, imagen, etiqueta_verdadera):
"""Testear consistencia del modelo bajo augmentaciones"""
augmentaciones = [
('rotacion', A.Rotate(limit=15, p=1)),
('brillo', A.RandomBrightness(limit=0.2, p=1)),
('blur', A.Blur(blur_limit=3, p=1)),
('ruido', A.GaussNoise(var_limit=(10, 50), p=1)),
('flip', A.HorizontalFlip(p=1))
]
prediccion_original = self.modelo.predict(imagen[np.newaxis, ...])[0]
clase_original = np.argmax(prediccion_original)
resultados = {}
for nombre_aug, augmentacion in augmentaciones:
aumentada = augmentacion(image=imagen)['image']
pred_aug = self.modelo.predict(aumentada[np.newaxis, ...])[0]
clase_aug = np.argmax(pred_aug)
resultados[nombre_aug] = {
'prediccion_cambio': clase_aug != clase_original,
'aun_correcto': clase_aug == etiqueta_verdadera
}
# Calcular puntaje de invariancia
puntaje_invariancia = sum(
1 for r in resultados.values() if not r['prediccion_cambio']
) / len(resultados)
return {
'resultados_augmentacion': resultados,
'puntaje_invariancia': puntaje_invariancia
}
Testing de Rendimiento
import time
class TestadorRendimiento:
def benchmark_inferencia(self, imagenes_test, tamanos_batch=[1, 8, 32]):
"""Benchmark de velocidad de inferencia"""
resultados = {}
for tamano_batch in tamanos_batch:
latencias = []
for i in range(0, len(imagenes_test), tamano_batch):
lote = imagenes_test[i:i+tamano_batch]
inicio = time.time()
_ = self.modelo.predict(lote)
fin = time.time()
latencia_ms = (fin - inicio) * 1000 / len(lote)
latencias.append(latencia_ms)
resultados[f'batch_{tamano_batch}'] = {
'latencia_promedio_ms': np.mean(latencias),
'latencia_p95_ms': np.percentile(latencias, 95),
'throughput_fps': 1000 / np.mean(latencias)
}
return resultados
Mejores Prácticas
| Práctica | Descripción |
|---|---|
| Conjunto de Test Diverso | Incluir variada iluminación, ángulos, fondos |
| Colección Casos Extremos | Oclusiones, ángulos extremos, poca luz |
| Validación Cross-Dataset | Testear en datos de diferentes fuentes |
| Endurecimiento Adversarial | Incluir ejemplos adversariales en entrenamiento |
| Evaluación Continua | Monitorear drift de rendimiento en producción |
| Testing de Equidad | Testear entre demografías (tonos piel, edades) |
Conclusión
El testing de visión por computadora va más allá de métricas de precisión—requiriendo testing de robustez, validación de dataset, defensas adversariales y evaluación de equidad. A medida que los sistemas CV se despliegan en aplicaciones críticas para seguridad, el testing riguroso se vuelve esencial.
Ver También
- Testing de Sistemas de IA y ML - Fundamentos completos de testing para inteligencia artificial
- Técnicas de Diseño de Casos de Prueba - Metodologías adaptadas para testing de modelos CV
- Testing Continuo en DevOps - Integración de testing de CV en pipelines automatizados
- Testing Exploratorio - Exploración manual para descubrir casos extremos en sistemas CV
- Containerización para Testing - Entornos reproducibles para testing de modelos de visión
