Blog

Modelos pequeños en el navegador: análisis de sentimiento sin servidor

Un experimento práctico para ejecutar un clasificador de texto localmente en el navegador y etiquetar una frase como positiva, neutra o negativa.

11 de julio de 20266 min de lecturaSergi
Un navegador ejecutando localmente un modelo compacto de análisis de sentimiento

Un clasificador compacto puede convertir una frase en una señal útil sin enviarla a un servidor de la aplicación.

Cuando hablamos de integrar IA en un producto, la conversación suele saltar directamente a modelos de lenguaje grandes, APIs, agentes e infraestructura bastante compleja. Pero muchos problemas útiles son mucho más pequeños.

Imaginemos un formulario de feedback. Antes de enviar un comentario queremos identificar si su tono es positivo, neutro o negativo. No necesitamos un modelo capaz de escribir poesía, usar herramientas o mantener una conversación. Necesitamos un clasificador: entra texto y sale 1 de 3 etiquetas.

Este tipo de tarea encaja bien con un modelo pequeño ejecutándose directamente en el navegador.

¿Por qué poner un modelo en el navegador?

La arquitectura más obvia sería enviar la frase a una API y clasificarla en un servidor. Funciona, pero también introduce una petición de red, un endpoint que mantener y un lugar por el que el texto del usuario sale de su dispositivo.

Ejecutar el modelo en el navegador cambia el equilibrio:

  • El texto se queda en local: la inferencia ocurre en el dispositivo del usuario. El navegador sigue descargando los archivos del modelo desde donde estén alojados, pero la frase no tiene que enviarse al servidor de nuestra aplicación.
  • No hay una API de inferencia que mantener: un hosting estático puede ser suficiente para toda la funcionalidad.
  • La inferencia es rápida después de la primera carga: el navegador puede guardar el modelo en caché y reutilizarlo.
  • La funcionalidad puede seguir disponible sin conexión: siempre que la aplicación y los archivos del modelo se hayan almacenado previamente.

El precio se paga en el otro extremo. La primera visita tiene que descargar el modelo, la memoria y el rendimiento cambian según el dispositivo, y tenemos menos control sobre el entorno de ejecución. La inferencia en cliente no es automáticamente mejor; es, sencillamente, una opción muy interesante para tareas pequeñas y acotadas.

El experimento útil más pequeño

Para este experimento uso Transformers.js, que ejecuta modelos Transformer compatibles mediante ONNX Runtime en el navegador. Su API de pipelines se encarga de la tokenización, la ejecución del modelo y la conversión del resultado en etiquetas.

El clasificador es Xenova/twitter-roberta-base-sentiment-latest. Devuelve exactamente las 3 etiquetas que necesitamos: positive, neutral y negative. Está entrenado con texto en inglés y un lenguaje parecido al de las redes sociales. Es un contexto importante, no un detalle que debamos ocultar.

La integración esencial es sorprendentemente pequeña:

import { pipeline } from "@huggingface/transformers";

const classify = await pipeline(
  "text-classification",
  "Xenova/twitter-roberta-base-sentiment-latest",
  { dtype: "q8" },
);

const [result] = await classify(
  "The release is useful, but the new navigation is frustrating.",
);

console.log(result);
// { label: "negative", score: 0.89 }

El valor exacto cambia según el entorno y la revisión del modelo, pero la forma del resultado es lo importante: una etiqueta y un nivel de confianza.

En una página estática pequeña, la librería también se puede importar desde un CDN:

const { pipeline } =
  await import("https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.2.0");

En una aplicación normal instalaría el paquete con el gestor de paquetes del proyecto y dejaría que el bundler lo gestionase. La versión del CDN resulta práctica para un experimento autocontenido.

Demo en el navegador

Prueba el clasificador

La primera ejecución descarga el modelo. Después, la frase se procesa localmente en tu navegador.

No cargarlo antes de necesitarlo

El JavaScript es diminuto en comparación con el modelo. Eso cambia cómo planteo la carga de la funcionalidad. No quiero que un clasificador compita con los recursos críticos de la página antes de que el usuario haya mostrado intención de utilizarlo.

La demo espera hasta el primer clic para crear el pipeline:

let classifierPromise;

function loadClassifier() {
  classifierPromise ??= pipeline(
    "text-classification",
    "Xenova/twitter-roberta-base-sentiment-latest",
    { dtype: "q8" },
  );

  return classifierPromise;
}

Guardar la promesa también evita que 2 clics rápidos carguen 2 copias del modelo. En la primera ejecución, Transformers.js descarga el modelo desde Hugging Face Hub y lo guarda en la caché del navegador. Las siguientes ejecuciones son mucho más rápidas.

Por defecto, la inferencia en el navegador usa la CPU mediante WebAssembly. Transformers.js también puede utilizar WebGPU, pero su compatibilidad y el hardware disponible varían. Lo trataría como una mejora y mantendría WebAssembly como base fiable. La cuantización también importa: usar pesos de 8 bits reduce la descarga y la memoria, normalmente a cambio de una pequeña pérdida de precisión.

El modelo es solo una parte del producto

Una probabilidad no es una decisión de producto. Incluso en este pequeño ejemplo hay preguntas alrededor del modelo:

  • ¿Qué debe hacer la interfaz cuando la confianza es baja?
  • ¿Una frase con sentimientos mezclados queda bien representada por 1 etiqueta?
  • ¿Deberíamos tratar de otra manera el sarcasmo, el slang o un idioma distinto?
  • ¿Se parece el dominio de entrenamiento al texto que escribirán nuestros usuarios?
  • ¿Qué ocurre en un teléfono antiguo con poca memoria?

También evitaría presentar el resultado como una lectura objetiva de una persona. Es un modelo estimando un patrón lingüístico, no midiendo una intención o una emoción. Puede resultar útil para ordenar feedback o adaptar el tono de una interfaz, pero no debería tomar decisiones importantes sobre los usuarios.

Para llevarlo a producción probaría el clasificador con ejemplos reales y consentidos del dominio del producto, definiría un umbral de confianza, mediría la descarga inicial en dispositivos móviles y añadiría un fallback en el servidor solo si el caso de uso realmente lo necesitase. Si la aplicación admite español, elegiría y evaluaría un modelo multilingüe en lugar de asumir que un clasificador en inglés va a generalizar.

Los modelos pequeños hacen la web más interesante

Lo que me gusta de este experimento no es el análisis de sentimiento en sí. Es la forma de la arquitectura: una página estática puede descargar una capacidad concreta y ejecutarla localmente, sin un servicio de inferencia en medio.

Hay muchas tareas acotadas parecidas: detectar el idioma, clasificar una petición de soporte, producir embeddings para una búsqueda local, identificar texto tóxico o extraer entidades. No todas necesitan un gran modelo generativo. A veces la cantidad correcta de IA es un modelo pequeño, cargado solo cuando hace falta y haciendo bien 1 trabajo.

Thanks for reading, Hack the Planet!