Cuaderno
2026 · 08 · 17
Decision log

La brecha del euskera no era del idioma, era del modelo

Monté un benchmark abierto de búsqueda geográfica en euskera y castellano para emap. Por el camino aprendí que el umbral de abstención no se hereda entre modelos, que la declinación vasca rompe el preprocesado, y que medir bien el euskera empeora tus números antes de mejorarlos.

emap tiene que entender preguntas como «¿dónde lleno la botella cerca de Abando?». Y también su versión en euskera: «non bete dezaket botila Abando ondoan?». No es una búsqueda por palabra clave: hay que decidir qué categoría de infraestructura está pidiendo el usuario —una fuente, un aparcabicis, un desfibrilador— entre 22 capas de datos de Euskadi.

La pregunta obvia era: ¿qué modelo de embeddings hace eso mejor en euskera? Y me encontré con que no había forma de saberlo. MIRACL y MTEB, cuando tratan el euskera, lo hacen como una lengua más de cola larga y sin dominio geográfico. Nadie mide búsqueda hiperlocal en euskera porque a nadie fuera de aquí le hace falta.

Así que construí la medición. 139 casos, 22 capas, cada consulta en los dos idiomas, con la respuesta esperada verificada contra datos reales.

Las dos decisiones que lo hacen honesto

La primera: un held-out estricto. 54 de los 139 casos llevan prefijo ho- y el runner los excluye por defecto. No se miran para calibrar nada. Se corren una vez, con la configuración ya congelada. Los otros 85 son para iterar.

La segunda, y la que más me ha enseñado: 19 casos donde la respuesta correcta es no responder. Van marcados answerable: false. Si el recuperador se inventa una categoría, falla.

Esto suena a detalle de metodología y es justo lo contrario. Un benchmark que solo pregunta cosas contestables premia al modelo que siempre dispara algo, porque adivinar nunca le cuesta nada. En emap el coste es real: mandar a alguien a caminar hasta un desfibrilador que no existe es peor que decirle que no lo sé.

Hay además 4 casos marcados known_gap: preguntas irresolubles porque el dato de origen no existe —el fee=no de los parkings apenas está mapeado en OSM fuera de Bilbao—. Esos no cuentan como fallo del modelo. Son un inventario de agujeros de datos.

El euskera tenía que ser euskera

Las consultas en euskera no las traduje con una API y a correr. Las cotejé con Itzuli, el traductor neuronal del Gobierno Vasco: 27 quedaron idénticas, 38 las adopté de Itzuli, y el resto las mantuve con el motivo anotado.

Aquí viene lo contraintuitivo: con euskera correcto, la brecha medida es mayor, no menor. El euskera de traducción automática sin revisar se parece más al castellano de partida, y eso le pone las cosas fáciles a un modelo multilingüe. Al escribir euskera de verdad, los números en euskera bajaron. La medición empeoró porque por fin era honesta.

El bug que solo aparece en euskera

El sistema recorta la cláusula de ubicación antes de generar el embedding: de «fuente cerca de Moyua» quiere quedarse con «fuente», y buscar el ancla «Moyua» por separado.

En castellano la preposición va delante y es fácil. En euskera el caso locativo va pegado y pospuesto: «Moyuatik gertu». Mi strip_location no reconocía nada, así que el ancla no casaba con «Moyua» y —peor— la cláusula locativa entera se colaba dentro del embedding y lo ensuciaba. El resultado eran capas de datos sin ninguna relación robando el primer puesto en consultas en euskera.

Es exactamente el tipo de fallo que no encuentras leyendo papers de retrieval multilingüe. Aparece cuando escribes los casos en el idioma real.

El error caro: el umbral no se hereda

Al cambiar de modelo hice lo que parecía razonable: me llevé la configuración que ya tenía calibrada. Umbral de abstención τ=0.50, heredado de MiniLM.

Con e5-large el resultado no se movía. Nada. Daba lo mismo poner τ=0.40 que τ=0.70.

Cuando fui a mirar las similitudes en crudo entendí por qué: e5 concentra sus cosenos en una banda alta y estrecha. Entre los casos que llegan a la etapa semántica, los contestables bajaban hasta 0.79 y los no contestables llegaban hasta 0.81. Con el umbral en 0.50 no bajaba de ahí ni un caso: la abstención estaba muerta. El modelo respondía siempre, y mi métrica de abstención no medía nada.

Las dos distribuciones ni siquiera están separadas —se pisan en esos dos centésimas—, así que no hay un umbral que las parta limpiamente. τ=0.80 es el corte que menos se equivoca de los dos lados, y con él e5 pasó de mediocre a ser el mejor de los cuatro. No es una frontera: es el mejor compromiso disponible, y ese solape es parte del resultado.

El umbral no es propiedad del corpus. Es propiedad del par (modelo, corpus). Portar un modelo sin recalibrarlo es el error silencioso más caro de este dominio: no rompe nada, no lanza ninguna excepción, simplemente apaga una parte del sistema sin decírtelo.

Los números

Aciertos en el conjunto held-out, el que nunca se tocó para calibrar:

Configuraciónheld-out ESheld-out EU
baseline de keywords + geo60%62%
híbrido · MiniLM-L1258%63%
híbrido · multilingual-e5-large73%71%

Lo importante no es que e5 gane. Es esa fila de en medio: MiniLM había empezado a perder contra un simple diccionario de palabras clave en castellano. Al pasar de 13 a 21 categorías, la etapa semántica que tenía desplegada había dejado de aportar, y mi propio criterio —el semántico solo se despliega si supera al baseline en los dos idiomas— se estaba incumpliendo sin que yo lo supiera.

Y el euskera, con e5, empata al castellano. La brecha que llevaba meses atribuyendo a la dificultad del idioma se cerró cambiando el modelo. No era el euskera: era MiniLM.

Probé también mpnet-base, con el triple de parámetros que MiniLM. Mejora poco y no cierra la brecha. El salto de calidad no es «un MiniLM más grande».

Lo que cuesta

e5-large ocupa 2,1 GB en disco y tarda una mediana de 92 ms por consulta en CPU, frente a los 12 ms de MiniLM. Ocho veces más lento y nueve veces más grande.

Lo asumo porque la etapa semántica solo se ejecuta cuando las palabras clave no reconocen la consulta, y la mayoría no llega ahí. Está en producción desde el 5 de agosto en un VPS de 16 GB, sin cuello de botella.

Lo que no se sostiene

El corpus lo escribí yo. Las descripciones de categoría y las paráfrasis salen de la misma cabeza, así que hay un sesgo de formulación que no se arregla con más casos. La validación de verdad llegará con consultas reales de usuarios, anonimizadas. Es la mejora pendiente que más multiplica.

Los umbrales están ajustados sobre dev; el held-out mide cuánto generaliza, y baja sin desplomarse. BGE-M3 y Qwen3-Embedding, que el estado del arte apunta como favoritos, aún no los he medido: no están disponibles en el runtime que uso.

El corpus, el harness y los resultados están publicados con licencia Apache-2.0 y DOI citable en 10.5281/zenodo.21282784. Se reproduce en tres comandos, y cada resultado guarda la configuración exacta con la que se obtuvo. Si construyes búsqueda geográfica bilingüe, llévatelo.