Ir al contenido principal
Laravel, thinking fast.

Capitulo 13

Cuando el modelo se equivoca

Julian Beaujardin

A veces el modelo no se equivoca. Se equivoca la tubería que lo rodea, y la culpa se la lleva el modelo.

Este capítulo es un ejemplo trabajado de eso, porque la lección generaliza más que el ejemplo: cuando un paso de tu tubería está tomando una decisión, escribe la versión determinista y mídela, en lugar de buscar un prompt más listo.

El fallo: publicar la posición cero

Los sitios generados necesitan fotografías. El paso de contenido produce una frase de búsqueda por hueco —«janitorial services celebration», «mopping office floor»— y un proveedor de banco de imágenes devuelve candidatas en su propio orden de relevancia.

Publicábamos lo que estuviera en la posición cero.

Esa fue la mayor fuente de imágenes fuera de tema en los sitios aprovisionados, y la razón está en las consultas. Son compuestas: un término de tema más un término específico del hueco. Un proveedor que no puede satisfacer la frase entera devuelve alegremente algo que sólo encaja con la parte más débil. Pide «janitorial services celebration» y te dan globos y confeti —un encaje excelente con una palabra y nada que ver con el negocio.

Fíjate en que nada de esto es culpa del modelo. La consulta era razonable. El proveedor hizo lo que hace. El defecto fue que aceptábamos la primera fila sin preguntarnos nunca si encajaba.

Cobertura, no ingenio

El arreglo es más o menos tan simple como puede ser un algoritmo: cuenta cuántos términos distintos de la consulta cubre la descripción de cada candidata, y ordena por eso.

foreach ($candidates as $index => $candidate) {
    $scored[] = [
        'index' => $index,
        'coverage' => self::coverage($queryTokens, $candidate['alt']),
        'candidate' => $candidate,
    ];
}

usort($scored, function (array $a, array $b): int {
    return $b['coverage'] <=> $a['coverage']
        ?: $a['index'] <=> $b['index'];
});

Tokeniza, descarta palabras vacías y términos muy cortos, y compara de forma laxa para que las inflexiones simples sigan contando —«clean» con «cleaning», «limpieza» con «limpiezas»— sin arrastrar un lematizador.

Sin llamada a un modelo. Sin embeddings. Sin base de datos vectorial. Una función de puntuación que puedes leer en un minuto, ejecutar un millón de veces por nada y explicarle a alguien que está molesto por una fotografía.

El experimento que se borró

La parte interesante es la versión que no sobrevivió, porque era la más lista y era peor.

La teoría era sólida: en una consulta compuesta la cola es la parte específica del hueco, así que pondera los términos por posición y deja que gane el término específico. Suena bien. Se lee bien en un mensaje de commit.

Medida contra resultados reales, perdió. Para «mopping office floor» prefirió una foto de una oficina en casa cuyas etiquetas casualmente incluían «office» y «floor» frente a una etiquetada de verdad «mopping, floor» —porque un solo término con más peso ganaba a cubrir más de la consulta. En todas las demás consultas medidas, ambos enfoques coincidían.

Así que la ponderación se borró. Añadía un concepto, una explicación y un modo de fallo, y no compraba nada.

Un algoritmo más listo que mide igual es un algoritmo peor. Tiene exactamente el mismo comportamiento y más superficie donde equivocarse.

Esto es el lo aburrido escala del capítulo 1 aplicado a la única parte del sistema donde el ingenio es más tentador, porque es la parte que se parece al aprendizaje automático.