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.
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.