Finetuning multimodal con Sentence Transformers para VDR | Keryc
En este artículo te explico, paso a paso y con ejemplos prácticos, cómo afinar modelos multimodales en Sentence Transformers para Visual Document Retrieval (VDR). ¿Te interesa mejorar la recuperación de capturas de documentos con gráficos, tablas y layouts intactos? Entonces esto es para ti.
Por qué afinar un modelo multimodal
Los modelos multimodales generales, como Qwen/Qwen3-VL-Embedding-2B, están entrenados para servir a muchas tareas: emparejar imagen-texto, VQA, comprensión de documentos, y más. Pero la generalidad tiene un costo: rara vez son óptimos para una tarea específica.
¿Y VDR no es diferente a buscar una foto de zapatillas? Exacto. Para VDR necesitas entender layouts, tablas y gráficos en capturas de pantalla. Afinando con datos del dominio, el modelo aprende patrones especializados y mejora sustancialmente.
En el experimento que revisamos, el modelo finetuneado tomaarsen/Qwen3-VL-Embedding-2B-vdr sube de NDCG@10 0.888 a 0.947 en el conjunto de evaluación. Eso lo pone por encima de modelos mucho más grandes. ¿La lección? Afinar en tu dominio suele ser más eficaz que usar un modelo más grande y genérico.
Componentes del entrenamiento multimodal
Entrenar modelos multimodales con Sentence Transformers sigue la misma receta que para texto, pero con algunos matices prácticos:
Modelo: backbone multimodal o combinación de encoders.
Dataset: entradas que mezclan texto e imágenes (o video, audio si aplica).
Función de pérdida: guía la optimización para tareas de recuperación o rerank.
Argumentos de entrenamiento: batch, precisión, logging, etc.
Evaluador: métricas de retrieval como NDCG@10, MAP y Recall@k.
Trainer: coordina todo lo anterior.
A continuación desgloso cada pieza con ejemplos concretos que puedes ejecutar o adaptar.
Modelo: finetune de VLM vs Router
Lo más frecuente es afinar un modelo multimodal preexistente (por ejemplo Qwen3-VL-Embedding-2B). La librería detecta automáticamente las modalidades soportadas por el processor y configura el forward y el pooling.
Ejemplo de carga con parámetros de precisión y control de resolución de imagen:
Alternativa: construir un modelo multimodal por composición con Router, combinando encoders ligeros para cada modalidad. Es útil cuando quieres control fino sobre cada encoder o ahorrar recursos.
Dataset: el ejemplo práctico
Para el tutorial se usó tomaarsen/llamaindex-vdr-en-train-preprocessed, una versión filtrada a ~53k ejemplos en inglés. El formato usado para entrenamiento fue triplets (query, image, negative_0) y para evaluación se preservaron 4 negativos duros por consulta.
Regla práctica: si tu función de pérdida requiere una etiqueta label o score, asegúrate de que la columna exista. Las entradas multimodales pueden ser PIL, rutas, URLs o arrays.
Función de pérdida: CachedMultipleNegativesRankingLoss y Matryoshka
Para retrieval se usó CachedMultipleNegativesRankingLoss, que combina negativos duros (columnas) e in-batch negatives. Su variante cached permite grandes tamaños efectivos de batch con menos memoria.
Mini ejemplo:
from sentence_transformers.sentence_transformer.losses import CachedMultipleNegativesRankingLoss, MatryoshkaLoss
loss = CachedMultipleNegativesRankingLoss(model, mini_batch_size=1)
loss = MatryoshkaLoss(model, loss, matryoshka_dims=[2048,1536,1024,512,256,128,64])
¿Por qué Matryoshka? Entrena el modelo para que truncar la dimensión de embedding siga dando buen desempeño. Resultado práctico: puedes desplegar embeddings de 512 o 256 dimensiones para ahorrar almacenamiento y latencia con mínima pérdida de calidad.
bf16=True es preferible para estabilidad numérica en modelos de visión-lenguaje.
batch_sampler=NO_DUPLICATES evita duplicados y mejora la señal para losses tipo multiple-negatives.
per_device_train_batch_size puede parecer grande para un VLM de 2B, pero mini_batch_size=1 en la pérdida y el caching permiten manejar memoria.
Usar fracciones para eval_steps y save_steps hace que la evaluación y guardado ocurran durante la época, útil para monitoreo.
Evaluador: medir retrieval correctamente
Para seguir el progreso se utilizó InformationRetrievalEvaluator con consultas de texto y un corpus de imágenes que incluye negativos duros. Ejemplo de construcción de corpus y evaluator:
from sentence_transformers.sentence_transformer.evaluation import InformationRetrievalEvaluator
# construir eval_queries y eval_corpus, añadir negativos con offsets
eval_evaluator = InformationRetrievalEvaluator(
queries=eval_queries,
corpus=eval_corpus,
relevant_docs=eval_relevant_docs,
batch_size=1,
show_progress_bar=True,
name="vdr-eval-hard",
)
Usar batch_size=1 aquí evita OOM en modelos grandes.
Trainer: juntar todo
El script final reúne modelo, datos, pérdida, args y evaluator en SentenceTransformerTrainer y lanza el entrenamiento con trainer.train().
Resultados: qué puedes esperar
En solo 1 época de finetuning, el modelo tomaarsen/Qwen3-VL-Embedding-2B-vdr alcanzó NDCG@10 = 0.947 en el set de evaluación (300 queries, 1500 documentos, similitud coseno). El modelo base Qwen/Qwen3-VL-Embedding-2B obtuvo 0.888.
La tabla comparativa mostró que el finetuneado superó a 20 modelos probados, incluidos modelos de 8B parámetros. Con embeddings completos de 2048 dimensiones el pico fue 0.948, pero gracias a Matryoshka el desempeño se mantiene casi intacto incluso truncando a 512 o 1024 dimensiones.
Resumen de la robustez por dimensión:
2048d: finetuned 0.948
1024d: finetuned 0.946
512d: finetuned 0.945
256d: finetuned 0.937
64d: finetuned 0.876
Decisión práctica: el autor guardó el modelo con truncate_dim=1024 por balance entre calidad y almacenamiento.
Rerankers multimodales: otra ruta
También puedes entrenar rerankers multimodales (Cross Encoders) con la misma infraestructura, usando CrossEncoderTrainer y pérdidas específicas como BinaryCrossEntropyLoss.
Dos arquitecturas comunes:
Any-to-Any + LogitScore: usa la cabeza de LM para generar tokens y calcula log-odds.
Feature Extraction + Pooling + Dense: extrae características y proyecta a una puntuación, más eficiente en costo de inferencia.
El flujo es similar: construir datasets por dirección (image_to_text y text_to_image), crear prompts de tarea y entrenar con ejemplos positivos y negativos.
Consejos prácticos para tu proyecto
Si tu corpus tiene layouts o tipos de gráficos consistentes, finetunear te dará ganancias grandes.
Empieza con una época y evalúa; a veces 1-2 épocas son suficientes para VDR con datos sintéticos o bien curados.
Usa Matryoshka si piensas desplegar con restricciones de almacenamiento o latencia.
Prefiere BF16 sobre FP16 en VLMs cuando tu hardware lo soporte.
Si la memoria es un problema, CachedMultipleNegativesRankingLoss con mini_batch_size=1 es tu amigo.
¿No quieres tocar un VLM gigante? Prueba el enfoque Router con encoders más ligeros y luego entrénalos para alinear espacios embebidos.
Reflexión final
Afinar modelos multimodales ya no es solo para laboratorios con GPUs en cluster. Con herramientas como Sentence Transformers puedes llevar un VLM a tu dominio y lograr saltos de calidad significativos en tareas reales como VDR. La inversión en datos de dominio y en una configuración de entrenamiento inteligente suele pagar más que simplemente subir el tamaño del modelo.