Anotación de videos con SAM 3.1
Un experimento de aprendizaje iterativo con un humano en el medio
Construí Annotator porque quería probar una idea bastante simple: usar SAM 3.1 como asistente para reducir la carga manual de anotar múltiples videos, y después enseñarle a un student model (alumno) a imitar parte de ese trabajo.
El flujo es bastante directo: subo un video, SAM propone etiquetas y máscaras, yo apruebo o corrijo esas propuestas, exporto la data en formato COCO/YOLO, entreno el student y uso ese modelo para anotar la siguiente tanda de videos.
En otras palabras, una pequeña fábrica de etiquetas: SAM propone, el humano (yo en este caso) corrige, el student aprende, y el proceso se repite con menos trabajo manual.
Cómo funciona el flywheel
Nuestra pequeña fábrica de etiquetas tiene tres actores principales: SAM, el humano, , y el alumno.
SAM funciona como profesor, o teacher model. Hace el primer pase sobre el video y propone etiquetas, máscaras o tracks. No necesito que sea perfecto. De hecho, si esperara perfección probablemente nunca construiría nada útil con esto. Lo que necesito es que produzca una primera versión razonable, algo suficientemente bueno como para que corregirlo sea más rápido que empezar desde cero.
El humano está en el medio. En este caso, yo.
Mi trabajo no es dibujar cada objeto manualmente. Mi trabajo es mirar las propuestas de SAM y decidir: esto sirve, esto está mal, esto necesita corrección, esto no debería estar. Ese cambio parece pequeño, pero cambia bastante la economía de la anotación. Pasas de crear etiquetas desde cero a editar las etiquetas generadas por un modelo.
Luego viene el alumno, o student model.
El alumno no intenta ser más general que SAM. Esa no es la idea. SAM ya tiene el conocimiento amplio. El alumno aprende algo más específico: mi distribución. Mis videos, mis clases, mis ángulos de cámara, mis decisiones sobre qué cuenta como etiqueta válida y qué no.
Entonces el ciclo queda así:
SAM hace el primer pase.
El humano aprueba, corrige o rechaza.
Las anotaciones aprobadas se exportan como COCO o YOLO.
Entreno un student con esa data.
El student anota la siguiente tanda.
El humano vuelve a revisar.
Y otra vez.
Al inicio, SAM carga casi todo el peso porque todavía no hay dataset. Después de algunas rondas, el student empieza a hacerse útil en las partes repetibles. Probablemente se equivoca bastante al comienzo, sobre todo si la data es escasa. Pero ese también es el punto: sus errores vuelven al loop.
El modelo nunca deja mi sala
Una decisión de diseño fue que Annotator corra localmente. Los videos no se suben a ningún servicio externo, las máscaras se guardan en disco, la base vive en SQLite y los modelos corren en la GB10 (NVIDIA DGX Spark).
Esto suena cómodo hasta que recuerdas que ahora todos los problemas también son tuyos. Latencia, memoria, drivers, modelos cargados, tracking de video, exportaciones, entrenamiento del student. No hay una nube mágica absorbiendo el caos al otro lado.
La primera decisión fue no tratar a SAM como un servicio remoto y stateless. En Annotator, SAM vive dentro del backend. El modelo se carga una vez por sesión para evitar recargarlo cada vez que mandamos una imagen o video. De esta forma, las iteraciones posteriores son más baratas. Es una decisión pequeña, pero cambia la sensación de la app.
En video, el problema es distinto. Ya no estás peleando solo contra una imagen pesada, sino contra una secuencia de frames. SAM puede trackear objetos, pero cada frame ocupa un espacio en la memoria, y la GB10 tiene un pool grande compartido entre CPU y GPU, no una cantidad infinita.
Así que Annotator no procesa videos largos como si fueran una sola cosa. Extrae frames con un límite, muestra cuántos frames vas a generar antes de empezar, procesa tracking por chunks y libera memoria entre pasadas.
También puse límites explícitos de memoria. Un OOM local no es una excepción abstracta en algún servidor lejano. Es tu máquina congelándose, modo zombie, en tu sala mientras tú miras la pantalla con cara de “ah, claro”.
Después vino la parte más específica del modelo.
Gran parte del costo estaba en el vision encoder, no en el prompt. Probé varios caminos. Algunos dieron mejoras marginales. Otros fueron más educativos, como TensorRT corriendo rápido pero produciendo salidas que no coincidían bien con la implementación de PyTorch.
Lo que sí funcionó, utilizar torch.compile, y cambiar a FP8 las capas más pesadas del encoder visual, de forma iterativa, cuidando no colapsar la calidad del modelo. Con eso terminé con una mejora end-to-end de 1.47x sin pérdida medible de calidad.
Aprendizajes
El punto del flywheel es bastante simple: reducir costo manual y aumentar velocidad con cada vuelta.
SAM hace el primer pase. Yo corrijo. El student aprende de lo aprobado. Si el loop funciona, la siguiente tanda debería tomar menos tiempo que la anterior. No porque el sistema sea perfecto, sino porque cada ronda funciona mejor.
La otra parte es correr todo localmente. Me gusta tener los videos, las máscaras, la base y los modelos en mi máquina, pero eso también significa que siempre hay algo que optimizar: memoria, latencia, tracking, exports, training, estabilidad.

