<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adrian</title>
    <description>The latest articles on DEV Community by Adrian (@adrian_368e1d3e691afab697).</description>
    <link>https://dev.to/adrian_368e1d3e691afab697</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3983803%2F92991c8b-073d-44be-80c4-584f44d63100.jpg</url>
      <title>DEV Community: Adrian</title>
      <link>https://dev.to/adrian_368e1d3e691afab697</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adrian_368e1d3e691afab697"/>
    <language>en</language>
    <item>
      <title>Bienestar digital con IA: por qué los bloqueadores fallan y cómo hacerlo predictivo, adaptativo y con coach</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 15 Oct 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/bienestar-digital-con-ia-por-que-los-bloqueadores-fallan-y-como-hacerlo-predictivo-adaptativo-y-4b0b</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/bienestar-digital-con-ia-por-que-los-bloqueadores-fallan-y-como-hacerlo-predictivo-adaptativo-y-4b0b</guid>
      <description>&lt;p&gt;El mercado de las apps de control de pantalla está saturado —Screen Time, Digital Wellbeing, Freedom, Opal— y casi todas comparten las mismas tres debilidades: límites &lt;strong&gt;estáticos&lt;/strong&gt; que pones una vez y luego ignoras, alertas &lt;strong&gt;reactivas&lt;/strong&gt; que llegan cuando ya te has pasado, y un &lt;strong&gt;muro de bloqueo&lt;/strong&gt; que desactivas en dos clics. Desconecta es un ejercicio sobre lo que de verdad falta ahí: no más "contar minutos", sino una capa de inteligencia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Predictivo en vez de reactivo
&lt;/h2&gt;

&lt;p&gt;El primer modelo es un forecaster (XGBoost) que aprende tu patrón —lags, medias móviles, día de la semana, tendencia— y predice los minutos de mañana por app. De ese forecast, proyectado sobre tu perfil horario, sale algo accionable: &lt;em&gt;a qué hora&lt;/em&gt; cruzarás el límite. "A este ritmo llegarás a tu tope de TikTok hacia las 20:08." La intervención llega antes del atracón, no después.&lt;/p&gt;

&lt;h2&gt;
  
  
  Riesgo explicable, no caja negra
&lt;/h2&gt;

&lt;p&gt;Un segundo modelo estima el riesgo de atracón del día como un score, y con &lt;strong&gt;SHAP&lt;/strong&gt; expone &lt;em&gt;por qué&lt;/em&gt;: fin de semana, racha de uso al alza, uso nocturno reciente, perfil impulsivo. Diseñé el generador de datos con esa estructura causal precisamente para que el modelo aprendiera señal real y SHAP la pudiera atribuir. Cuando seleccionas "fin de semana" en la demo, el trigger dominante que aparece es justo ese.&lt;/p&gt;

&lt;h2&gt;
  
  
  Límites adaptativos con glide-path
&lt;/h2&gt;

&lt;p&gt;Un límite fijo que no puedes cumplir es un límite que abandonas. En vez de eso, un controlador transparente parte de tu uso real y desciende hacia tu objetivo (p.ej. −30%) de forma sostenible, sin recortes de más del 10% por semana. Nada de RL opaco: una política explicable que puedes auditar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un coach que negocia, no un muro
&lt;/h2&gt;

&lt;p&gt;En el momento crítico interviene un coach LLM (Groq, Qwen3.8 27B) en tres modos: &lt;strong&gt;reflexionar&lt;/strong&gt; (una pregunta que te hace notar por qué sigues), &lt;strong&gt;negociar&lt;/strong&gt; una prórroga consciente a cambio de un motivo, o &lt;strong&gt;cerrar&lt;/strong&gt; con una micro-acción alternativa. Breve, empático, sin sermones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Una arquitectura, dos clientes
&lt;/h2&gt;

&lt;p&gt;El detalle de ingeniería que más me gusta: el backend FastAPI es el cerebro, y lo consumen dos clientes. La &lt;strong&gt;demo web&lt;/strong&gt; enseña la inteligencia sobre datos sintéticos de uso de móvil; y una &lt;strong&gt;app Android&lt;/strong&gt; nativa hace la monitorización y el bloqueo reales del sistema (&lt;code&gt;UsageStatsManager&lt;/code&gt;) delegando todo el análisis a esa misma API. Un cerebro, dos caras.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honestidad con las métricas
&lt;/h2&gt;

&lt;p&gt;Aquí viene la parte incómoda y, para mí, la más importante. El forecasting de uso por app da &lt;strong&gt;R²≈0.77&lt;/strong&gt; (batiendo a un baseline naïve de 0.57) y el riesgo de atracón &lt;strong&gt;AUC≈0.79&lt;/strong&gt;. Están por debajo del listón que me pongo para problemas como predicción de precios (R²≥0.88). ¿Por qué no los subí? Porque hacerlo habría implicado &lt;strong&gt;limpiar artificialmente los datos&lt;/strong&gt;, y el uso humano del móvil es intrínsecamente ruidoso: la misma persona pasa de 10 a 90 minutos en una app según el día. Inflar el número falseando los datos es justo lo contrario de lo que un portfolio técnico debería enseñar. El atracón, además, es un evento probabilístico: lo correcto es tratarlo como un score de riesgo (AUC), no como una clasificación dura. Prefiero un 0.77 honesto y defendible que un 0.9 de laboratorio que no sobreviviría al mundo real.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/desconecta-bienestar-digital-ia-predictivo-adaptativo-coach" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bienestardigital</category>
      <category>xgboost</category>
      <category>shap</category>
      <category>seriestemporales</category>
    </item>
    <item>
      <title>Glosarios para traductores con IA: cola asíncrona, map-reduce y un agente que se autocritica</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 08 Oct 2026 10:00:03 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/glosarios-para-traductores-con-ia-cola-asincrona-map-reduce-y-un-agente-que-se-autocritica-5cpd</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/glosarios-para-traductores-con-ia-cola-asincrona-map-reduce-y-un-agente-que-se-autocritica-5cpd</guid>
      <description>&lt;p&gt;Cuando un traductor profesional recibe un manual técnico o un contrato de decenas de páginas, el primer trabajo —el que el cliente no ve— es construir el &lt;strong&gt;glosario&lt;/strong&gt;: identificar los tecnicismos, siglas y expresiones que deben traducirse exactamente igual a lo largo de todo el documento. Si "API Gateway" se traduce de tres formas distintas en tres capítulos, la traducción pierde calidad y consistencia. Hacer ese glosario a mano lleva horas. Esta herramienta lo automatiza conservando algo que las soluciones simples pierden: &lt;strong&gt;el contexto en el que aparece cada término&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema real no es el prompt, es la arquitectura
&lt;/h2&gt;

&lt;p&gt;Es fácil escribir un prompt que extraiga terminología de un párrafo. Lo difícil es hacerlo con un documento pesado sin que se rompa nada: los límites de tokens del LLM, los &lt;em&gt;rate limits&lt;/em&gt; de la API y la experiencia de usuario (nadie quiere una petición HTTP colgada dos minutos). Por eso el núcleo del proyecto es un &lt;strong&gt;flujo asíncrono desacoplado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Al subir el documento, FastAPI lo almacena, crea una tarea en estado &lt;code&gt;PENDING&lt;/code&gt; en SQLite e inyecta su ID en una &lt;strong&gt;cola Redis&lt;/strong&gt;. Un consumidor recoge la tarea, la procesa por fases y va actualizando el progreso, mientras el frontend hace &lt;em&gt;polling&lt;/em&gt; y muestra una barra real (fragmento 3 de 12…). El detalle de ingeniería que me gusta: ese consumidor no es un &lt;em&gt;daemon&lt;/em&gt; aparte, sino una tarea &lt;code&gt;asyncio&lt;/code&gt; dentro del propio servicio FastAPI. Un solo proceso, cero RAM residente de más, y compatible con el sistema de arranque bajo demanda del portfolio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Map → Reduce → Reflect
&lt;/h2&gt;

&lt;p&gt;La extracción no es un único prompt, sino un pipeline de tres fases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Map&lt;/strong&gt;: el texto se divide en fragmentos de ~1.800 palabras (respetando los límites de párrafo) y cada fragmento se envía a Groq (Qwen3.8 27B) con un prompt especializado en localización que devuelve JSON estricto: término, contexto exacto, traducción propuesta y justificación.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduce&lt;/strong&gt;: se deduplican los términos que aparecen repetidos en páginas distintas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reflect&lt;/strong&gt;: aquí está la diferencia. Un segundo paso &lt;strong&gt;agéntico&lt;/strong&gt; recibe el glosario consolidado y se autocritica: elimina falsos positivos (palabras comunes que se colaron), fusiona variantes del mismo concepto ("API Gateway" y "gateway de API") y unifica traducciones inconsistentes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esa fase de reflexión es lo que convierte un extractor mecánico en algo que se acerca al criterio de un lingüista jefe revisando el trabajo. No es marketing: es una segunda llamada con un rol y una tarea de control de calidad distintos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Respetar los rate limits es parte del diseño
&lt;/h2&gt;

&lt;p&gt;Procesar 25 fragmentos en paralelo es la forma rápida de comerse el rate limit y recibir 429. La herramienta usa concurrencia acotada (dos llamadas simultáneas), &lt;em&gt;backoff&lt;/em&gt; exponencial cuando aparece un 429 y una cadena de modelos (70B → 8B) como red de seguridad. Es más lento que disparar todo a la vez, pero es lo que hace que la demo no se caiga bajo carga.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export en formatos que el sector usa de verdad
&lt;/h2&gt;

&lt;p&gt;Un glosario que no se puede importar no sirve. La herramienta exporta en &lt;strong&gt;CSV&lt;/strong&gt; (universal) y en &lt;strong&gt;TBX&lt;/strong&gt; — TermBase eXchange, ISO 30042 —, el estándar de intercambio de bases terminológicas que importan SDL Trados, memoQ y el resto de herramientas CAT. Generar TBX-Basic válido (con su &lt;code&gt;langSet&lt;/code&gt; por idioma de origen y destino) es lo que separa un juguete de una herramienta que un traductor puede meter en su flujo de trabajo real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honestidad sobre los límites
&lt;/h2&gt;

&lt;p&gt;La extracción la hace un LLM: el resultado es un punto de partida de alta calidad, no una verdad absoluta. El profesional debe revisarlo antes de usarlo en producción. Y en la demo se procesan solo los primeros fragmentos de documentos muy largos, para mantener el coste y los tiempos acotados. Pero la arquitectura —cola, fases, reflexión, export estándar— es exactamente la que usarías en producción.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/glosarios-contextuales-procesamiento-asincrono-documentos-traductores" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>localizacion</category>
      <category>llm</category>
      <category>procesamientoasincrono</category>
      <category>redis</category>
    </item>
    <item>
      <title>Voice-to-SQL: hablarle a tu base de datos con un LLM (y por qué la seguridad es lo difícil)</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 01 Oct 2026 10:00:03 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/voice-to-sql-hablarle-a-tu-base-de-datos-con-un-llm-y-por-que-la-seguridad-es-lo-dificil-1gj1</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/voice-to-sql-hablarle-a-tu-base-de-datos-con-un-llm-y-por-que-la-seguridad-es-lo-dificil-1gj1</guid>
      <description>&lt;p&gt;Las preguntas sobre datos casi nunca las hace quien sabe SQL. Las hace alguien de negocio, marketing o dirección, que tiene que pedírselas a un analista y esperar. Voice-to-SQL ataca justo esa fricción: que cualquiera pueda preguntarle a la base de datos &lt;strong&gt;hablando&lt;/strong&gt;, y obtener la respuesta al instante.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Quería una demo que tradujera lenguaje natural —por voz o texto— a SQL, lo ejecutara sobre una base de datos real y devolviera la tabla, mostrando el SQL generado. Tres requisitos no negociables: que la voz no costara dinero, que fuera &lt;em&gt;seguro&lt;/em&gt; dejar que un LLM tocara la base de datos, y que la respuesta llegara en menos de un segundo.&lt;/p&gt;

&lt;h2&gt;
  
  
  La voz: en el navegador, no en el backend
&lt;/h2&gt;

&lt;p&gt;La tentación es mandar el audio al servidor y transcribirlo con Whisper o un servicio de pago. Lo descarté: añade latencia, coste y complejidad. En su lugar uso la &lt;strong&gt;Web Speech API&lt;/strong&gt;, que viene integrada en Chrome, Edge y Safari. La transcripción ocurre en el dispositivo del usuario; el backend solo recibe texto. Coste de transcripción: &lt;strong&gt;cero&lt;/strong&gt;. El precio a pagar es la dependencia del navegador, un compromiso razonable para una demo web.&lt;/p&gt;

&lt;h2&gt;
  
  
  De texto a SQL: el esquema es el prompt
&lt;/h2&gt;

&lt;p&gt;El corazón del sistema es la conversión texto→SQL con un LLM (&lt;strong&gt;Qwen3.8 27B vía Groq&lt;/strong&gt;, por su latencia muy baja). La clave no es el modelo, sino lo que se le da: junto a la pregunta, le paso el &lt;strong&gt;esquema exacto&lt;/strong&gt; de la base de datos —tablas, columnas, tipos, valores posibles de los enums y un par de reglas del dialecto SQLite—. Sin ese contexto, el modelo inventa columnas. Con él, genera &lt;code&gt;JOIN&lt;/code&gt;s y &lt;code&gt;GROUP BY&lt;/code&gt; correctos a la primera.&lt;/p&gt;

&lt;h2&gt;
  
  
  La parte difícil: que sea seguro
&lt;/h2&gt;

&lt;p&gt;Aquí está el verdadero reto de ingeniería. Dar a un LLM una conexión a tu base de datos es una receta para el desastre: una pregunta mal interpretada (o malintencionada) podría acabar en un &lt;code&gt;DROP TABLE&lt;/code&gt;. La regla de oro es &lt;strong&gt;no confiar nunca en el SQL generado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Por eso, antes de ejecutar nada, el SQL pasa por un sanitizador que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Solo acepta sentencias que empiecen por &lt;code&gt;SELECT&lt;/code&gt; o &lt;code&gt;WITH&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rechaza por lista negra &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;DROP&lt;/code&gt;, &lt;code&gt;ALTER&lt;/code&gt;, &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;PRAGMA&lt;/code&gt;, &lt;code&gt;ATTACH&lt;/code&gt; y &lt;code&gt;REPLACE&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bloquea el encadenamiento de varias sentencias con punto y coma (un clásico vector de inyección).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Impone un tope absoluto de 100 filas por respuesta.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Es una defensa en capas, deliberadamente conservadora: prefiero rechazar una consulta legítima rara antes que permitir una destructiva. El LLM propone; el sanitizador dispone.&lt;/p&gt;

&lt;h2&gt;
  
  
  La base de datos demo
&lt;/h2&gt;

&lt;p&gt;Para que las preguntas tengan sentido, generé con un script una base SQLite que imita el back-office de una empresa SaaS: 200 clientes (país, sector, plan, churn), 8 productos (planes y add-ons) y 5.000 ventas del último año con su aporte al MRR. Así se pueden hacer preguntas analíticas de verdad —ingresos por país, MRR por plan, churn— y no juguetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultado
&lt;/h2&gt;

&lt;p&gt;De la pregunta a la tabla, en torno a medio segundo (la mayor parte es la generación del LLM; SQLite ejecuta en local en milisegundos). Y cada respuesta enseña el SQL que se generó, lo que convierte la demo en algo didáctico además de útil.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que en un sistema "LLM + base de datos", el modelo es la parte fácil. Lo que de verdad decide si esto se puede poner delante de un usuario es la &lt;strong&gt;capa de seguridad&lt;/strong&gt; y la &lt;strong&gt;calidad del contexto&lt;/strong&gt; (el esquema) que le das al modelo. La inteligencia genera el SQL; la ingeniería hace que sea seguro ejecutarlo.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/voice-to-sql-hablarle-a-tu-base-de-datos-con-un-llm" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nlp</category>
      <category>texttosql</category>
      <category>llm</category>
      <category>webspeechapi</category>
    </item>
    <item>
      <title>Detección de fraude: por qué el AUC-ROC engaña y el AUC-PR no (datos reales)</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 24 Sep 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/deteccion-de-fraude-por-que-el-auc-roc-engana-y-el-auc-pr-no-datos-reales-1gik</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/deteccion-de-fraude-por-que-el-auc-roc-engana-y-el-auc-pr-no-datos-reales-1gik</guid>
      <description>&lt;p&gt;En detección de fraude hay una trampa estadística que infla currículums: el &lt;strong&gt;AUC-ROC&lt;/strong&gt;. Sobre datos extremadamente desbalanceados es fácil presumir de un 0.99 que no significa casi nada. Este proyecto está construido alrededor de la métrica que sí importa —el &lt;strong&gt;AUC-PR&lt;/strong&gt;— y de no esconder los fallos del modelo.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;El objetivo: marcar transacciones fraudulentas en tiempo real y, sobre todo, de forma &lt;em&gt;honesta&lt;/em&gt; y &lt;em&gt;explicable&lt;/em&gt;. Un analista necesita saber por qué se marca una operación, no un número opaco.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los datos: ULB, reales y brutalmente desbalanceados
&lt;/h2&gt;

&lt;p&gt;Entrené sobre el dataset &lt;strong&gt;ULB Credit Card Fraud Detection&lt;/strong&gt;: &lt;strong&gt;284.807 transacciones reales&lt;/strong&gt; de tarjetas europeas, con features anonimizadas por PCA (V1–V28) más importe y tiempo. El detalle clave es el desbalanceo: solo el &lt;strong&gt;0,17%&lt;/strong&gt; son fraude (unas 492 de 284.807). Esa rareza es justo lo que hace difícil —y honesto— el problema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué el AUC-ROC engaña (y el AUC-PR no)
&lt;/h2&gt;

&lt;p&gt;Sobre datos tan desbalanceados, el &lt;strong&gt;AUC-ROC&lt;/strong&gt; sale altísimo casi sin esfuerzo (aquí ~0,91) porque premia clasificar bien la clase mayoritaria, que es trivial. El &lt;strong&gt;AUC-PR&lt;/strong&gt; (precisión-recall) cuenta la verdad: mi modelo logra &lt;strong&gt;0,67&lt;/strong&gt; frente a un &lt;em&gt;baseline&lt;/em&gt; de 0,0012 — es decir, &lt;strong&gt;~550× mejor que el azar&lt;/strong&gt; en lo que de verdad cuesta. Por eso la demo muestra el AUC-PR, no el ROC inflado.&lt;/p&gt;

&lt;h2&gt;
  
  
  El modelo
&lt;/h2&gt;

&lt;p&gt;Un clasificador &lt;strong&gt;LightGBM&lt;/strong&gt; con &lt;code&gt;scale_pos_weight&lt;/code&gt; para penalizar más los errores sobre la clase fraude, hiperparámetros buscados con &lt;strong&gt;Optuna&lt;/strong&gt; y umbral de decisión calibrado (0,9) para equilibrar precisión y recall según el coste real de cada error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados (honestos)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AUC-PR: 0,67&lt;/strong&gt; (baseline 0,0012 → ~550× sobre el azar) — la métrica que importa.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AUC-ROC: 0,91&lt;/strong&gt; — alto, pero engañoso en desbalanceo; lo reporto solo para contraste.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Precisión 0,88 · Recall 0,71 · F1 0,79 · MCC 0,79&lt;/strong&gt; sobre el fraude real.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No hay 0,99 de fantasía: el modelo acierta mucho y falla algo, y la demo lo enseña tal cual.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explicabilidad con SHAP
&lt;/h2&gt;

&lt;p&gt;Cada predicción se acompaña de un desglose &lt;strong&gt;SHAP&lt;/strong&gt;: cuánto empujó cada feature (V11, importe, etc.) hacia "fraude" o "legítimo". Eso convierte el modelo en una herramienta que un equipo humano puede auditar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que en problemas desbalanceados &lt;strong&gt;elegir bien la métrica es un acto de honestidad&lt;/strong&gt;. Es fácil publicar un AUC-ROC de 0,99 y parecer un genio; es más útil —y más honrado— reportar el AUC-PR real y explicar por qué. Un modelo que conoce y muestra sus límites vale más que uno que presume de una cifra vacía.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/deteccion-fraude-lightgbm-auc-pr-datos-reales" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>lightgbm</category>
      <category>fraude</category>
      <category>shap</category>
    </item>
    <item>
      <title>Detectar volatilidad anómala en los mercados con un Temporal Fusion Transformer</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 17 Sep 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/detectar-volatilidad-anomala-en-los-mercados-con-un-temporal-fusion-transformer-20c2</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/detectar-volatilidad-anomala-en-los-mercados-con-un-temporal-fusion-transformer-20c2</guid>
      <description>&lt;p&gt;Predecir el precio de una acción es casi imposible y, francamente, poco útil. Hay una pregunta más alcanzable y más valiosa: &lt;strong&gt;¿cuándo está a punto de volverse anormalmente inestable un activo?&lt;/strong&gt; Los picos de volatilidad suelen preceder a las noticias, no seguirlas. Ese era el objetivo de este "sensor de mercado".&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;En lugar de estimar un único número (el retorno esperado), quería estimar la &lt;strong&gt;distribución completa&lt;/strong&gt; de retornos para cada activo y horizonte, y marcar como anómalos los momentos en los que la realidad se sale de los cuantiles que el modelo considera plausibles.&lt;/p&gt;

&lt;h2&gt;
  
  
  La arquitectura: TFT + regresión cuantílica
&lt;/h2&gt;

&lt;p&gt;Elegí el &lt;strong&gt;Temporal Fusion Transformer&lt;/strong&gt; porque combina tres cosas que necesitaba:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Variable Selection Networks&lt;/strong&gt; que aprenden qué inputs importan en cada momento, dándome interpretabilidad sin renunciar a la potencia.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Atención multi-cabeza&lt;/strong&gt; sobre la ventana temporal, que captura dependencias largas (un patrón de hace semanas) mejor que una LSTM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Salida de &lt;strong&gt;regresión cuantílica&lt;/strong&gt;: en vez de un valor, predice varios cuantiles (p10, p50, p90...). La distancia entre cuantiles &lt;em&gt;es&lt;/em&gt; la incertidumbre estimada.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cuando un retorno real cae fuera del intervalo cuantílico esperado, salta la alerta de anomalía.&lt;/p&gt;

&lt;h2&gt;
  
  
  La métrica que de verdad valida el modelo
&lt;/h2&gt;

&lt;p&gt;Un modelo cuantílico solo sirve si está &lt;strong&gt;bien calibrado&lt;/strong&gt;: si dice que algo ocurre el 90% de las veces, debe ocurrir el 90% de las veces. La calibración cuantílica de este modelo resultó prácticamente perfecta, lo que significa que sus intervalos de incertidumbre son fiables, no decorativos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;F1-score (detección de anomalías): 0.82&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MCC: 0.80&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Calibración cuantílica: ≈ perfecta&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que reformular el problema —de "predecir el precio" a "predecir la incertidumbre"— lo vuelve tratable y útil. Y que en finanzas, un modelo que conoce sus propios límites (su calibración) vale más que uno que da respuestas precisas y falsas.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/volatilidad-anomala-temporal-fusion-transformer" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>transformers</category>
      <category>finanzascuantitativas</category>
      <category>seriestemporales</category>
    </item>
    <item>
      <title>Regímenes de mercado con Hidden Markov Models: un score de riesgo que bate al buy-and-hold</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 10 Sep 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/regimenes-de-mercado-con-hidden-markov-models-un-score-de-riesgo-que-bate-al-buy-and-hold-ipg</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/regimenes-de-mercado-con-hidden-markov-models-un-score-de-riesgo-que-bate-al-buy-and-hold-ipg</guid>
      <description>&lt;p&gt;Los mercados no se comportan igual todo el tiempo. Hay periodos de calma alcista, periodos neutrales y periodos de pánico. El problema es que ese "régimen" es un &lt;strong&gt;estado oculto&lt;/strong&gt;: no viene etiquetado en los datos, solo lo intuimos por cómo se comportan los retornos y la volatilidad. Es el escenario perfecto para un Hidden Markov Model.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Quería un score de riesgo &lt;strong&gt;en tiempo real&lt;/strong&gt; que no se limitara a mirar la volatilidad pasada, sino que identificara en qué régimen está el mercado &lt;em&gt;ahora&lt;/em&gt; y ajustara la exposición en consecuencia.&lt;/p&gt;

&lt;h2&gt;
  
  
  La arquitectura: Gaussian HMM
&lt;/h2&gt;

&lt;p&gt;Entrené un &lt;strong&gt;GaussianHMM&lt;/strong&gt; (con &lt;code&gt;hmmlearn&lt;/code&gt;) sobre 5 años de datos reales de cinco activos representativos: SPY, IBEX35, STOXX50E, TLT (bonos) y GLD (oro). El modelo asume que en cada momento el mercado está en uno de &lt;strong&gt;3 estados ocultos&lt;/strong&gt; —Bull, Neutral, Bear— y que los retornos observados se generan a partir de una gaussiana distinta según el estado. El algoritmo de Baum-Welch aprende las matrices de transición y las distribuciones de emisión sin que yo etiquete nada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Del régimen a la decisión
&lt;/h2&gt;

&lt;p&gt;Una vez el modelo infiere el régimen actual (vía el algoritmo de Viterbi/forward), la estrategia ajusta la exposición: plena en Bull, reducida en Neutral, defensiva en Bear. Sobre esa base calculo además VaR al 95%, Sortino y un score de riesgo normalizado de 0 a 100.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados (HMM vs. buy-and-hold)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sharpe: 2.12&lt;/strong&gt; frente a 1.11 del comprar-y-mantener.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CVaR (riesgo de cola): reducido un 32%.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Máximo drawdown: de -21,9% a -7,5%.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lo interesante no es solo el mayor Sharpe, sino la brutal reducción del drawdown: el modelo se sale a tiempo de los regímenes bajistas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que los modelos de estados latentes son una forma honesta de capturar algo que los traders saben intuitivamente —"esto ha cambiado de tono"— sin caer en el sobreajuste de intentar predecir el precio exacto. El HMM no adivina el futuro; reconoce el presente mejor que la media móvil.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/score-riesgo-cartera-hidden-markov-models" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>finanzascuantitativas</category>
      <category>hiddenmarkovmodels</category>
      <category>gestionderiesgo</category>
      <category>seriestemporales</category>
    </item>
    <item>
      <title>Value Betting Engine: del modelo de probabilidad a la apuesta de valor con el criterio de Kelly</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 03 Sep 2026 10:00:03 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/value-betting-engine-del-modelo-de-probabilidad-a-la-apuesta-de-valor-con-el-criterio-de-kelly-a4n</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/value-betting-engine-del-modelo-de-probabilidad-a-la-apuesta-de-valor-con-el-criterio-de-kelly-a4n</guid>
      <description>&lt;p&gt;Hay un malentendido fundamental sobre las apuestas deportivas: la gente cree que se trata de &lt;em&gt;acertar&lt;/em&gt; quién gana. No es así. Se trata de encontrar situaciones en las que la &lt;strong&gt;cuota del mercado está equivocada&lt;/strong&gt; respecto a la probabilidad real. Eso es el &lt;em&gt;value betting&lt;/em&gt;, y es matemáticamente idéntico a buscar activos infravalorados en bolsa.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Necesitaba dos cosas: (1) una estimación propia y fiable de la probabilidad de cada resultado, y (2) una forma de compararla con las cuotas del mercado para detectar valor y dimensionar la apuesta sin arruinarme.&lt;/p&gt;

&lt;h2&gt;
  
  
  La probabilidad: reutilizar el Sports Engine
&lt;/h2&gt;

&lt;p&gt;Las probabilidades vienen del modelo del &lt;a href="https://dev.to/proyecto/sports-engine"&gt;Sports Performance Engine&lt;/a&gt; (ensemble LightGBM + XGBoost sobre datos StatsBomb). Reutilizar ese motor fue una decisión de arquitectura: un buen modelo de predicción deportiva es la base sobre la que se construye el value betting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detectar valor: EV &amp;gt; 0
&lt;/h2&gt;

&lt;p&gt;Para cada mercado consulto las cuotas de &lt;strong&gt;más de 350 casas&lt;/strong&gt; vía OddsPapi. La señal de valor es simple pero implacable: el &lt;strong&gt;valor esperado&lt;/strong&gt; de la apuesta, &lt;code&gt;EV = p · (cuota − 1) − (1 − p)&lt;/code&gt;, donde &lt;code&gt;p&lt;/code&gt; es mi probabilidad estimada. Solo entran las apuestas con &lt;strong&gt;EV positivo&lt;/strong&gt;: aquellas donde el mercado paga más de lo que el riesgo justifica.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dimensionar: el criterio de Kelly
&lt;/h2&gt;

&lt;p&gt;Detectar valor no basta; hay que decidir &lt;em&gt;cuánto&lt;/em&gt; apostar. El &lt;strong&gt;criterio de Kelly&lt;/strong&gt; da la fracción del capital que maximiza el crecimiento geométrico a largo plazo: &lt;code&gt;f = (p · b − q) / b&lt;/code&gt;. En la práctica uso un Kelly fraccional (una fracción de lo que recomienda) porque el Kelly puro es demasiado agresivo y muy sensible a errores en la estimación de &lt;code&gt;p&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validación: backtesting 2022–2024
&lt;/h2&gt;

&lt;p&gt;Toda la lógica se validó con &lt;strong&gt;backtesting sobre datos de 2022 a 2024&lt;/strong&gt;, simulando el bankroll a lo largo del tiempo para comprobar que la estrategia sobrevive a las rachas malas —que las hay, incluso con EV positivo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que la gestión del riesgo (Kelly) es tan importante como la detección de la oportunidad (EV). Un buen modelo con mal sizing quiebra; un modelo modesto con sizing disciplinado sobrevive. La misma lección que en cualquier mesa de trading.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/value-betting-engine-kelly-criterion" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>finanzascuantitativas</category>
      <category>apuestasdeportivas</category>
      <category>kellycriterion</category>
      <category>backtesting</category>
    </item>
    <item>
      <title>Sports Performance Engine: predecir fútbol con datos StatsBomb y un ensemble LightGBM+XGBoost</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 27 Aug 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/sports-performance-engine-predecir-futbol-con-datos-statsbomb-y-un-ensemble-lightgbmxgboost-d18</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/sports-performance-engine-predecir-futbol-con-datos-statsbomb-y-un-ensemble-lightgbmxgboost-d18</guid>
      <description>&lt;p&gt;El fútbol es uno de los deportes más difíciles de predecir: tiene pocos goles, mucho azar y un equipo inferior gana con sorprendente frecuencia. Eso lo convierte en un banco de pruebas honesto para el machine learning, porque no puedes esconderte detrás de métricas infladas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los datos: StatsBomb
&lt;/h2&gt;

&lt;p&gt;Usé los datos abiertos de &lt;strong&gt;StatsBomb&lt;/strong&gt; (vía &lt;code&gt;statsbombpy&lt;/code&gt;), que van mucho más allá del marcador: eventos por partido, posición de cada acción, expected goals (xG), presiones, pases progresivos. Cuando faltaban datos, completé con un generador sintético calibrado de partidos de LaLiga y Champions, hasta unos 3.200 partidos analizados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature engineering: el corazón del proyecto
&lt;/h2&gt;

&lt;p&gt;El modelo no mira un partido aislado, sino la &lt;strong&gt;forma reciente&lt;/strong&gt; de cada equipo. Construí features &lt;em&gt;rolling&lt;/em&gt;: medias móviles de xG a favor y en contra, de presiones, de rendimiento, en ventanas de los últimos N partidos. Esto captura "este equipo llega en racha" sin que el modelo haga trampa mirando el futuro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validación: TimeSeriesSplit, no K-Fold
&lt;/h2&gt;

&lt;p&gt;Este es el error más común en ML deportivo y financiero: usar validación cruzada aleatoria. Si entrenas con partidos de mayo y validas con partidos de enero, estás &lt;strong&gt;filtrando el futuro&lt;/strong&gt;. Usé &lt;strong&gt;TimeSeriesSplit&lt;/strong&gt; (división temporal 70/15/15), de modo que el modelo siempre se evalúa sobre partidos posteriores a los de entrenamiento.&lt;/p&gt;

&lt;h2&gt;
  
  
  El modelo: ensemble + Optuna
&lt;/h2&gt;

&lt;p&gt;Combiné &lt;strong&gt;LightGBM y XGBoost&lt;/strong&gt; en un ensemble —dos implementaciones de gradient boosting con sesgos ligeramente distintos cuyas predicciones promediadas generalizan mejor— con hiperparámetros buscados por &lt;strong&gt;Optuna&lt;/strong&gt; (30+25 trials).&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AUC-ROC: 0.71&lt;/strong&gt; — un número modesto en apariencia, pero &lt;em&gt;realista&lt;/em&gt; para fútbol; desconfía de cualquiera que te prometa 0.95 prediciendo resultados de fútbol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;3.200 partidos&lt;/strong&gt; analizados, &lt;strong&gt;25+ features&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Explicabilidad &lt;strong&gt;SHAP&lt;/strong&gt; por predicción.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que un AUC de 0.71 honesto vale más que un 0.95 fruto de fugas de datos. La integridad de la validación temporal es lo que separa un modelo que funciona en producción de uno que solo brilla en el notebook.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/sports-performance-engine-statsbomb-ensemble" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>deportes</category>
      <category>lightgbm</category>
      <category>xgboost</category>
    </item>
    <item>
      <title>Predicción de precio inmobiliario: por qué un ensemble GBM + red neuronal bate a cualquiera por separado</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 20 Aug 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/prediccion-de-precio-inmobiliario-por-que-un-ensemble-gbm-red-neuronal-bate-a-cualquiera-por-2b4k</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/prediccion-de-precio-inmobiliario-por-que-un-ensemble-gbm-red-neuronal-bate-a-cualquiera-por-2b4k</guid>
      <description>&lt;p&gt;El precio de una vivienda depende de factores que se comportan de formas muy distintas. Algunos son cuasi-reglas ("más de 3 baños dispara el precio en esta zona"); otros son interacciones suaves y continuas (la relación entre metros, antigüedad y barrio). Ningún tipo de modelo captura bien ambas cosas a la vez. Por eso usé un &lt;strong&gt;ensemble&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Estimar el precio de mercado de una vivienda a partir de sus características, con un error lo bastante bajo como para ser útil en una valoración real, y exponerlo como una API de predicción en tiempo real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los dos modelos del ensemble
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gradient Boosting Machine:&lt;/strong&gt; excelente capturando relaciones no lineales y "reglas" abruptas sobre datos tabulares. Es el caballo de batalla de los datos estructurados.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Perceptrón multicapa (MLP):&lt;/strong&gt; una red neuronal que modela bien las interacciones suaves y continuas entre variables, algo donde los árboles, por su naturaleza escalonada, son más toscos.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Las predicciones de ambos se combinan. El ensemble funciona porque sus errores están &lt;strong&gt;poco correlacionados&lt;/strong&gt;: donde uno falla por su sesgo, el otro tiende a compensar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decisiones técnicas
&lt;/h2&gt;

&lt;p&gt;El preprocesado trató con cuidado las variables sesgadas (el precio y la superficie se distribuyen log-normalmente, no normalmente) y las categóricas de localización. El stack es &lt;code&gt;scikit-learn&lt;/code&gt; para el GBM y &lt;strong&gt;PyTorch&lt;/strong&gt; para el MLP, servido todo con &lt;strong&gt;FastAPI&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;R² ≈ 0.90:&lt;/strong&gt; el modelo explica alrededor del 90% de la varianza del precio.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MAE ≈ ±$62K:&lt;/strong&gt; error medio absoluto.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;~11% de error porcentual relativo.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que el ensembling no es hacer trampa: es reconocer que distintos modelos tienen distintos puntos ciegos, y que combinarlos es casi siempre más robusto que pelearse por exprimir el último 0,5% de un único modelo.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/prediccion-precio-inmobiliario-ensemble-gbm-mlp" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ensemble</category>
      <category>gradientboosting</category>
      <category>redesneuronales</category>
    </item>
    <item>
      <title>Un chatbot RAG multilingüe sobre tus PDFs con FAISS y reranking (coste de búsqueda: 0 €)</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 13 Aug 2026 10:00:03 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/un-chatbot-rag-multilingue-sobre-tus-pdfs-con-faiss-y-reranking-coste-de-busqueda-0-eu-3bjc</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/un-chatbot-rag-multilingue-sobre-tus-pdfs-con-faiss-y-reranking-coste-de-busqueda-0-eu-3bjc</guid>
      <description>&lt;p&gt;En un sistema RAG (Retrieval-Augmented Generation), todo el mundo se fija en el LLM. Es un error. La calidad de las respuestas depende muchísimo más de &lt;strong&gt;qué fragmentos recuperas&lt;/strong&gt; que del modelo que los redacta. Si recuperas el contexto equivocado, el mejor LLM del mundo te dará una respuesta equivocada con total seguridad.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Construir un asistente que responda preguntas sobre documentos PDF —manuales, normativas, catálogos— en varios idiomas, con respuestas fundamentadas en el documento y no inventadas, y a coste de infraestructura cercano a cero.&lt;/p&gt;

&lt;h2&gt;
  
  
  El pipeline de recuperación (donde está el truco)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embeddings multilingües:&lt;/strong&gt; uso &lt;code&gt;intfloat/multilingual-e5-large&lt;/code&gt; para vectorizar los fragmentos. El modelo entiende que "precio" y "price" están cerca en el espacio vectorial, lo que da soporte multilingüe de serie.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Búsqueda vectorial con FAISS:&lt;/strong&gt; los vectores se indexan en &lt;strong&gt;FAISS&lt;/strong&gt;, la librería de Facebook para búsqueda de similitud. Corre en CPU, en local, sin servicios gestionados: de ahí el &lt;strong&gt;coste de búsqueda de 0 €&lt;/strong&gt;. Los índices estáticos se pre-generan por idioma; los PDFs que sube el usuario se indexan en RAM por sesión.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reranking con cross-encoder:&lt;/strong&gt; la búsqueda vectorial es rápida pero imprecisa. Por eso añado un segundo paso: un &lt;strong&gt;cross-encoder&lt;/strong&gt; (&lt;code&gt;mmarco-mMiniLMv2&lt;/code&gt;) que re-puntúa los candidatos leyendo pregunta y fragmento &lt;em&gt;juntos&lt;/em&gt;. Es más lento, pero solo se aplica a un puñado de candidatos, y mejora drásticamente la precisión del contexto.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  La generación
&lt;/h2&gt;

&lt;p&gt;Solo entonces entra el LLM (&lt;strong&gt;Llama vía Groq&lt;/strong&gt;, por su latencia bajísima), que redacta la respuesta a partir del contexto ya filtrado. El resultado: respuesta media &lt;strong&gt;por debajo de 3 segundos&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  De demo a producto: SaaS multi-tenant
&lt;/h2&gt;

&lt;p&gt;Por encima del motor RAG monté una capa &lt;strong&gt;SaaS multi-tenant&lt;/strong&gt; con SQLite: clientes, planes (free/basic/pro/enterprise), autenticación por &lt;code&gt;X-API-Key&lt;/code&gt; y registro de consumo de tokens con límites por plan. Lo que empezó como una demo es, en arquitectura, un producto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que la frase "RAG es solo meter documentos en un LLM" es engañosa. El valor está en el &lt;strong&gt;pipeline de recuperación&lt;/strong&gt; —embeddings + búsqueda + reranking— y en hacerlo barato y rápido. El LLM es la última milla, no el motor.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/chatbot-rag-multilingue-faiss-reranking" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nlp</category>
      <category>rag</category>
      <category>faiss</category>
      <category>embeddings</category>
    </item>
    <item>
      <title>FeliniAI: un triple pipeline (visión + clínico + LLM) para detectar alergias felinas con F1 0.97</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 06 Aug 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/feliniai-un-triple-pipeline-vision-clinico-llm-para-detectar-alergias-felinas-con-f1-097-2jac</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/feliniai-un-triple-pipeline-vision-clinico-llm-para-detectar-alergias-felinas-con-f1-097-2jac</guid>
      <description>&lt;p&gt;Cuando el objetivo es algo tan delicado como un diagnóstico asistido, confiar en un único modelo es arriesgado. FeliniAI usa &lt;strong&gt;tres pipelines complementarios&lt;/strong&gt; que se refuerzan entre sí, igual que un veterinario combina lo que ve, lo que mide y lo que sabe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipeline 1 — Visión: MobileNetV2
&lt;/h2&gt;

&lt;p&gt;Una CNN &lt;strong&gt;MobileNetV2&lt;/strong&gt; (PyTorch, transfer learning) clasifica imágenes de la piel/pelaje del gato en categorías visuales. Elegí MobileNetV2 por su equilibrio entre precisión y ligereza: corre rápido en CPU, lo que mantiene la inferencia por debajo de 1 segundo. Alcanza un &lt;strong&gt;93,4% de accuracy visual&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipeline 2 — Clínico: XGBoost + ICADA
&lt;/h2&gt;

&lt;p&gt;El núcleo del sistema es un clasificador &lt;strong&gt;XGBoost&lt;/strong&gt; que trabaja sobre &lt;strong&gt;33 features clínicas&lt;/strong&gt; derivadas de los criterios &lt;strong&gt;ICADA&lt;/strong&gt; (los criterios estandarizados de dermatitis atópica felina): estacionalidad, distribución de las lesiones, prurito, respuesta a tratamientos previos. Sobre un dataset de &lt;strong&gt;8.000 casos&lt;/strong&gt;, este módulo logra un &lt;strong&gt;F1 macro de 0.9675&lt;/strong&gt; en validación cruzada 5-fold. La búsqueda de hiperparámetros se hizo con Optuna y la explicabilidad con SHAP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipeline 3 — LLM: la síntesis
&lt;/h2&gt;

&lt;p&gt;Finalmente, un LLM (&lt;strong&gt;Llama 3.3 70B vía Groq&lt;/strong&gt;) integra las salidas de los dos modelos anteriores y las traduce en una recomendación legible: qué tipo de alergia es más probable, con qué confianza y qué pasos sugerir. El LLM no diagnostica solo: &lt;em&gt;orquesta y comunica&lt;/em&gt; lo que han calculado los modelos especializados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué tres pipelines y no uno
&lt;/h2&gt;

&lt;p&gt;Porque cada uno cubre el punto ciego del otro. La visión capta lo que una foto muestra pero un cuestionario no; el modelo clínico capta el historial que una foto no puede mostrar; el LLM convierte ambos en algo accionable. Es un patrón de &lt;strong&gt;ensemble heterogéneo&lt;/strong&gt; aplicado a datos de naturaleza distinta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;F1 macro (clínico): 0.9675&lt;/strong&gt;, accuracy 0.9909.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Accuracy visual: 93,4%.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;4 tipos de alergia, 33 features clínicas, &amp;lt;1s&lt;/strong&gt; de inferencia.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que en dominios sensibles, la arquitectura correcta no es "el modelo más grande", sino &lt;strong&gt;varios modelos especializados orquestados&lt;/strong&gt;, cada uno haciendo aquello en lo que es bueno.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/feliniai-triple-pipeline-vision-clinico-llm" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>visionporcomputador</category>
      <category>xgboost</category>
      <category>salud</category>
    </item>
    <item>
      <title>Separar canciones en stems con HTDemucs v4 en un servidor con poca RAM</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Thu, 30 Jul 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/adrian_368e1d3e691afab697/separar-canciones-en-stems-con-htdemucs-v4-en-un-servidor-con-poca-ram-47nf</link>
      <guid>https://dev.to/adrian_368e1d3e691afab697/separar-canciones-en-stems-con-htdemucs-v4-en-un-servidor-con-poca-ram-47nf</guid>
      <description>&lt;p&gt;La separación de fuentes musicales —descomponer una canción mezclada en sus pistas individuales— parecía ciencia ficción hace pocos años. Hoy, modelos como &lt;strong&gt;HTDemucs&lt;/strong&gt; lo hacen sorprendentemente bien. El reto de este proyecto no fue el modelo, sino &lt;strong&gt;ponerlo a funcionar en un servidor modesto, sin GPU.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Separar cualquier canción en 4 stems (voces, batería, bajo y melodía) y permitir remezclarla en el navegador. Todo ello en una máquina con CPU y RAM limitada, donde un proceso descuidado tumba el servidor.&lt;/p&gt;

&lt;h2&gt;
  
  
  El modelo: HTDemucs v4
&lt;/h2&gt;

&lt;p&gt;Uso &lt;strong&gt;HTDemucs v4&lt;/strong&gt; (variante &lt;code&gt;htdemucs_ft&lt;/code&gt;), un modelo híbrido transformer/convolucional que opera tanto en el dominio de la forma de onda como en el espectrograma. Es el estado del arte abierto en separación de fuentes, con licencia MIT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Las dos decisiones de ingeniería que lo hacen viable
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Segmentación (&lt;code&gt;--segment 5&lt;/code&gt;):&lt;/strong&gt; procesar una canción entera de golpe dispara el uso de memoria. Trocear el audio en segmentos cortos mantiene el pico de RAM bajo control, a costa de algo más de tiempo. Sin esto, el proceso muere por OOM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Procesado asíncrono:&lt;/strong&gt; la separación tarda decenas de segundos. Bloquear la petición HTTP sería inaceptable, así que el trabajo se encola y procesa en segundo plano (sin necesidad de Celery: una solución asíncrona ligera), y el frontend consulta el progreso.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El frontend usa &lt;strong&gt;WaveSurfer.js&lt;/strong&gt; para visualizar las ondas y la &lt;strong&gt;Web Audio API&lt;/strong&gt; para la remezcla en tiempo real: ajustar el volumen de cada stem, silenciar la voz para hacer karaoke, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detalle de despliegue
&lt;/h2&gt;

&lt;p&gt;El modelo (~400 MB) se descarga en el primer uso. El servicio corre con límites de memoria de systemd y, como otras demos pesadas del portfolio, arranca bajo demanda y se apaga tras un periodo de inactividad para no malgastar RAM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué aprendí
&lt;/h2&gt;

&lt;p&gt;Que desplegar IA en hardware limitado es una disciplina en sí misma. El modelo es solo el principio; la segmentación, el procesado asíncrono y la gestión de memoria son lo que separa una demo que funciona de una que tumba el servidor.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente en &lt;a href="https://adrianmoreno-dev.com/blog/separar-stems-htdemucs-v4-cpu-ram-limitada" rel="noopener noreferrer"&gt;adrianmoreno-dev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>audioai</category>
      <category>htdemucs</category>
      <category>deeplearning</category>
      <category>procesadoasincrono</category>
    </item>
  </channel>
</rss>
