Ejercicios — Módulo 01: Fundamentos¶
AI Master Academy · Módulo 01-FUNDAMENTOS 30 ejercicios para consolidar todo lo aprendido en los 7 capítulos del módulo.
Instrucciones de uso¶
Antes de empezar, lee estas reglas. Son la diferencia entre aprender de verdad y creer que aprendiste:
- Intenta cada ejercicio durante al menos 15 minutos antes de mirar la solución. La lucha es donde ocurre el aprendizaje. Si lo resuelves en 2 minutos, perfecto; si no, insiste. Solo después de 15 minutos de intento honesto, abre el desplegable de la solución.
- Teclea el código, no lo copies y pegues. Tus dedos también aprenden. Copiar y pegar da la ilusión de dominio; teclear obliga a tu cerebro a procesar cada línea.
- Ejecuta todo el código. Cada bloque de código de este archivo es funcional. Crea un archivo
ejercicios.py(o un notebook) y comprueba que obtienes los mismos resultados. - En los ejercicios conceptuales, escribe tu respuesta antes de leer la propuesta. Escribir obliga a ordenar ideas. Comparar tu texto con la solución te muestra huecos que no sabías que tenías.
- Verifica los cálculos a mano. En la Sección B, primero calcula con papel y calculadora, y después verifica con NumPy. El objetivo es que entiendas qué hace la máquina, no que la máquina piense por ti.
- Anota tus dudas. Si tras leer una solución sigues sin entender algo, vuelve al capítulo correspondiente antes de continuar.
Nota
necesitas Python 3.10+ con numpy y pandas instalados (pip install numpy pandas). Todo lo demás es biblioteca estándar.
Niveles de dificultad¶
| Símbolo | Nivel | Qué esperar |
|---|---|---|
| Básico | Aplicación directa de un concepto del capítulo | |
| Intermedio | Combina 2-3 conceptos o requiere razonamiento propio | |
| Avanzado | Integra varios capítulos o exige pensamiento crítico |
Criterio de aprobación del módulo¶
- Un ejercicio cuenta como superado si lo resolviste correctamente por tu cuenta, o si tras ver la solución fuiste capaz de reproducirla al día siguiente sin mirarla.
- Necesitas 25 de 30 ejercicios superados (≈83%) para dar por aprobado el módulo y pasar al Módulo 02.
- Si no llegas a 25: identifica en qué sección fallaste más y repasa el capítulo asociado antes de reintentar.
Índice de ejercicios¶
| # | Ejercicio | Sección | Capítulo | Dificultad |
|---|---|---|---|---|
| 1 | Explicar IA vs ML vs DL a un directivo | A — Conceptual | Cap. 1 | |
| 2 | Clasificar 5 casos reales por paradigma de aprendizaje | A — Conceptual | Cap. 6 | |
| 3 | ¿Este problema necesita IA o bastan reglas? | A — Conceptual | Cap. 1 | |
| 4 | Modelo propietario vs open source con requisitos de privacidad | A — Conceptual | Cap. 7 | |
| 5 | Ordenar hitos de la historia de la IA y justificar su importancia | A — Conceptual | Cap. 2 | |
| 6 | Los inviernos de la IA: causas y lecciones para hoy | A — Conceptual | Cap. 2 | |
| 7 | Entrenamiento vs inferencia: explicárselo al equipo de finanzas | A — Conceptual | Cap. 6 | |
| 8 | Overfitting explicado con analogías (y cómo detectarlo) | A — Conceptual | Cap. 6 | |
| 9 | Roles profesionales: ¿a quién contrato para mi equipo de IA? | A — Conceptual | Cap. 7 | |
| 10 | Evaluar el hype: ¿este anuncio revoluciona la IA o es humo? | A — Conceptual | Caps. 1, 2, 7 | |
| 11 | Producto punto a mano y con NumPy | B — Matemáticas | Cap. 3 | |
| 12 | Similitud coseno entre documentos de juguete | B — Matemáticas | Cap. 3 | |
| 13 | Normalizar vectores (norma L2) | B — Matemáticas | Cap. 3 | |
| 14 | Aplicar softmax e interpretar la temperatura | B — Matemáticas | Cap. 3 | |
| 15 | Un paso de descenso de gradiente a mano para f(x) = x² | B — Matemáticas | Cap. 3 | |
| 16 | Teorema de Bayes: el test médico que engaña | B — Matemáticas | Cap. 3 | |
| 17 | Media y desviación estándar de latencias con NumPy | B — Matemáticas | Caps. 3-4 | |
| 18 | Mini buscador semántico con similitud coseno | B — Matemáticas | Caps. 3-5 | |
| 19 | FizzBuzz con giro: monitor de lotes de entrenamiento | C — Python | Cap. 4 | |
| 20 | Funciones con type hints: calculadora de coste de tokens | C — Python | Cap. 4 | |
| 21 | Comprehensions sobre datos de clientes | C — Python | Cap. 4 | |
| 22 | Parsear el JSON de una API de IA ficticia | C — Python | Cap. 4 | |
| 23 | Clase Documento con métodos |
C — Python | Cap. 4 | |
| 24 | Manejo de excepciones con datos sucios | C — Python | Cap. 4 | |
| 25 | NumPy: matrices y vectorización vs bucle (benchmark) | C — Python | Cap. 4 | |
| 26 | Pandas: análisis de ventas con CSV inline | C — Python | Cap. 4 | |
| 27 | Pipeline completo: limpiar datos y calcular estadísticas | D — Integrador | Caps. 3-5 | |
| 28 | Regresión lineal con descenso de gradiente desde cero | D — Integrador | Caps. 3-6 | |
| 29 | Clasificador de sentimiento por reglas y sus límites vs ML | D — Integrador | Caps. 1, 4, 6 | |
| 30 | Caso de empresa: ¿reglas, ML clásico o LLM? | D — Integrador | Todos |
SECCIÓN A — Ejercicios conceptuales¶
Comprensión profunda de los capítulos 1, 2, 6 y 7. Escribe tus respuestas antes de mirar las soluciones.
Ejercicio 1 — Explicar IA vs ML vs DL a un directivo¶
Dificultad: · Capítulo relacionado: Cap. 1 (¿Qué es la IA?)
Contexto: Trabajas como consultor junior de IA. El director financiero (CFO) de una cadena de supermercados te dice en una reunión: "Todo el mundo habla de IA, machine learning y deep learning como si fueran lo mismo. ¿Me lo puedes explicar en dos minutos, sin tecnicismos, y decirme cuál necesito para predecir la demanda de productos frescos?"
Tu tarea: Escribe una explicación de máximo 200 palabras que: 1. Diferencie los tres conceptos con una analogía comprensible. 2. Deje clara la relación de inclusión entre ellos (cuál contiene a cuál). 3. Termine con una recomendación concreta para su caso de predicción de demanda.
Ver solución
**Respuesta modelo (adáptala a tu estilo, lo importante son las ideas):** > "Piense en tres círculos, uno dentro de otro, como muñecas rusas. > > El círculo grande es la **Inteligencia Artificial**: cualquier sistema que hace tareas que asociamos con inteligencia humana — decidir, clasificar, predecir. Incluye desde reglas escritas a mano ('si el cliente gasta más de 500 €, es VIP') hasta sistemas que aprenden solos. > > Dentro está el **Machine Learning**: en vez de escribir las reglas nosotros, le damos a la máquina ejemplos históricos y *ella deduce las reglas*. Le enseñamos tres años de ventas y aprende qué factores mueven la demanda. > > Y dentro del ML está el **Deep Learning**: machine learning con redes neuronales de muchas capas. Brilla con datos masivos y complejos: imágenes, voz, texto. Es lo que hay detrás de ChatGPT. > > Para predecir demanda de frescos, no necesita deep learning: con **machine learning clásico** sobre su histórico de ventas, calendario y meteorología obtendrá resultados excelentes, más baratos y más fáciles de explicar al consejo." **Por qué esta respuesta funciona:** - **Analogía de círculos concéntricos / muñecas rusas:** transmite la relación de inclusión (DL ⊂ ML ⊂ IA) sin dibujar un diagrama de Venn formal. Este es el punto que más se suele evaluar: DL es un subconjunto de ML, y ML es un subconjunto de IA. Decir "IA o ML" como si fueran alternativas excluyentes es un error conceptual. - **Cero tecnicismos:** no aparece "gradiente", "red convolucional" ni "parámetros". Un directivo necesita el mapa, no el motor. - **Recomendación accionable y honesta:** un error típico de junior es recomendar deep learning para todo. La predicción de demanda con datos tabulares (ventas, fechas, clima) es terreno clásico de ML tradicional (regresiones, árboles de decisión): más barato, más interpretable, y con datasets medianos suele rendir igual o mejor que una red profunda. **Errores comunes a evitar:** 1. Presentar los tres términos como sinónimos o como tecnologías rivales. 2. No responder la pregunta de negocio (el CFO quería una recomendación, no solo una clase). 3. Superar las 200 palabras: la concisión es parte del ejercicio. Comunicar IA a negocio es una habilidad profesional en sí misma. **Consejo profesional:** practica esta explicación en voz alta hasta que dure 90 segundos. La pregunta "¿qué diferencia hay entre IA y ML?" aparece en la mayoría de entrevistas y reuniones con clientes.Ejercicio 2 — Clasificar 5 casos reales por paradigma de aprendizaje¶
Dificultad: · Capítulo relacionado: Cap. 6 (Cómo aprende una máquina)
Contexto: Eres el nuevo responsable de IA de una consultora. Cinco clientes distintos te describen sus proyectos. Para asignar el equipo correcto, debes clasificar cada caso en uno de los tres paradigmas: aprendizaje supervisado, no supervisado o por refuerzo.
Casos:
- Un banco tiene 200 000 transacciones antiguas, cada una etiquetada por sus analistas como "fraude" o "legítima", y quiere un sistema que marque las transacciones nuevas.
- Una tienda online quiere descubrir "tipos de clientes" en su base de datos, pero no tiene ninguna categoría predefinida: quiere que el sistema encuentre los grupos por sí solo.
- Un estudio de videojuegos quiere un bot que aprenda a jugar a su juego de carreras probando acciones y recibiendo puntos por adelantar y penalizaciones por chocar.
- Un hospital tiene 50 000 radiografías, cada una revisada por radiólogos que anotaron "neumonía" o "sin neumonía", y quiere un modelo de apoyo al diagnóstico.
- Una plataforma de música quiere agrupar canciones "que suenan parecido" a partir de sus características de audio, sin géneros predefinidos, para descubrir estilos emergentes.
Tu tarea: Para cada caso, indica el paradigma y justifica en una frase qué pista del enunciado te lo revela.
Ver solución
| Caso | Paradigma | Pista clave en el enunciado | |------|-----------|------------------------------| | 1. Fraude bancario | **Supervisado** (clasificación) | Hay datos históricos **etiquetados** ("fraude" / "legítima"): la máquina aprende de ejemplos con respuesta conocida. | | 2. Tipos de clientes | **No supervisado** (clustering) | "**No tiene ninguna categoría predefinida**": no hay etiquetas, el objetivo es descubrir estructura oculta. | | 3. Bot de carreras | **Por refuerzo** | El agente **prueba acciones** y recibe **recompensas y penalizaciones**: aprendizaje por interacción con un entorno. | | 4. Radiografías | **Supervisado** (clasificación) | Cada imagen tiene una **etiqueta puesta por expertos** ("neumonía" / "sin neumonía"). | | 5. Canciones parecidas | **No supervisado** (clustering) | Se buscan **grupos por similitud** "sin géneros predefinidos": de nuevo, ausencia de etiquetas. | **El razonamiento general (memoriza estas tres preguntas):** 1. **¿Tengo ejemplos con la respuesta correcta ya anotada?** → Supervisado. La palabra clave en los enunciados suele ser "etiquetado", "anotado", "clasificado por expertos", "histórico con resultados conocidos". 2. **¿Tengo datos sin respuesta y quiero descubrir estructura (grupos, patrones, anomalías)?** → No supervisado. Palabras clave: "descubrir", "agrupar", "segmentar", "sin categorías previas". 3. **¿Hay un agente que actúa en un entorno y recibe recompensas/castigos por sus acciones?** → Refuerzo. Palabras clave: "probar acciones", "puntos", "penalización", "aprender jugando". **Matices que distinguen a un profesional:** - Los casos 1 y 4 son ambos supervisados, pero fíjate: uno usa datos tabulares (transacciones) y otro imágenes (radiografías). El *paradigma* es el mismo; la *arquitectura* del modelo cambiará (ML clásico probablemente para el 1, deep learning para el 4). Paradigma ≠ tecnología. - En el caso 2, después de descubrir los grupos, el negocio podría etiquetarlos ("cazadores de ofertas", "compradores premium"...) y entrenar luego un clasificador supervisado que asigne clientes nuevos a esos grupos. Los paradigmas se encadenan en proyectos reales. - El caso 3 es refuerzo puro: no hay dataset previo; los "datos" se generan al interactuar. Este es el rasgo que más lo diferencia de los otros dos.Ejercicio 3 — ¿Este problema necesita IA o bastan reglas?¶
Dificultad: · Capítulo relacionado: Cap. 1 (¿Qué es la IA?)
Contexto: Eres consultor. Una empresa de logística te plantea cuatro necesidades y quiere "ponerles IA a todas" porque lo pide su junta directiva. Tu trabajo (y tu reputación) consiste en recomendar IA solo donde aporta valor.
Necesidades:
- Cálculo de tarifas de envío: el precio depende del peso, el volumen, la distancia y si es urgente. Las fórmulas están documentadas en un PDF oficial de 3 páginas y cambian una vez al año.
- Clasificación de incidencias: llegan 2 000 correos diarios de clientes en lenguaje libre ("mi paquete llegó roto", "¿dónde está mi pedido?", "quiero factura") que hoy clasifican a mano 6 personas en 12 categorías.
- Validación de códigos postales: comprobar que el código postal introducido en un formulario existe y coincide con la provincia.
- Predicción de retrasos: estimar qué envíos llegarán tarde combinando tráfico histórico, meteorología, temporada y rendimiento de cada ruta.
Tu tarea: Para cada necesidad, decide entre sistema de reglas o IA/ML, y justifica con el criterio "¿puedo escribir las reglas explícitamente?".
Ver solución
| Necesidad | Recomendación | Justificación | |-----------|---------------|---------------| | 1. Tarifas de envío | **Reglas** | La lógica está **completamente especificada** en un documento, es determinista y cambia una vez al año. Programarla directamente es más barato, exacto al 100%, auditable y trivial de mantener. Usar ML aquí introduciría errores donde hoy no los hay. | | 2. Clasificación de correos | **IA/ML** | El lenguaje natural libre **no se puede capturar con reglas**: hay infinitas formas de decir "paquete roto" (con erratas, ironía, mezcla de temas). Es el caso de uso clásico de clasificación de texto supervisada; además hay volumen (2 000/día) y coste humano (6 personas) que justifican la inversión. | | 3. Códigos postales | **Reglas** (una consulta a tabla) | Es una **verificación exacta contra una lista oficial finita**. Un `lookup` en una tabla resuelve el 100% de los casos. Meter ML aquí sería absurdo: no hay patrón que aprender, hay un dato que consultar. | | 4. Predicción de retrasos | **IA/ML** | La relación entre tráfico + clima + temporada + ruta y el retraso es **estadística, no determinista, y demasiado compleja para enumerarla a mano**. Hay datos históricos abundantes: terreno ideal para ML supervisado (regresión o clasificación "llegará tarde sí/no"). | **El criterio maestro (guárdalo para toda tu carrera):** > **Si un humano puede escribir todas las reglas de forma explícita, completa y estable → programa las reglas.** > **Si las reglas son desconocidas, difusas, cambiantes o hay demasiadas excepciones, pero tienes ejemplos históricos → machine learning.** **Señales de que un problema pide reglas:** lógica documentada, resultado exacto exigido (facturación, legal), pocos casos límite, cambia con poca frecuencia. **Señales de que un problema pide ML:** entrada en lenguaje natural o imágenes, patrones estadísticos, muchas excepciones, los propios expertos "lo saben hacer pero no saben explicar cómo", y existen datos históricos etiquetados o etiquetables. **El argumento de negocio que debes dar a la junta:** aplicar IA donde bastan reglas no es "innovación", es asumir coste, latencia, mantenimiento y errores probabilísticos a cambio de nada. El prestigio del consultor está en decir "aquí NO" con argumentos. Recomendar reglas en los casos 1 y 3 te da credibilidad para pedir presupuesto serio en los casos 2 y 4. **Advertencia:** "tiene que llevar IA porque lo pide la junta" es la primera frase de muchos proyectos fracasados. La tecnología se elige por el problema, no por la moda.Ejercicio 4 — Modelo propietario vs open source con requisitos de privacidad¶
Dificultad: · Capítulo relacionado: Cap. 7 (Ecosistema y herramientas)
Contexto: Un hospital privado quiere un asistente interno que resuma historiales clínicos para sus médicos. Requisitos del cliente:
- Los datos de pacientes no pueden salir de la infraestructura del hospital (normativa sanitaria y política interna estricta).
- Presupuesto: tienen servidores propios con GPUs adquiridos el año pasado, y un equipo técnico pequeño (2 ingenieros).
- La calidad del resumen debe ser alta, pero el volumen es moderado (unos 500 resúmenes al día).
- Necesitan tenerlo funcionando en 4 meses.
Tu tarea: 1. Decide entre: (a) API de modelo propietario (p. ej. GPT/Claude/Gemini vía nube), (b) modelo open source (p. ej. Llama, Mistral, Qwen) desplegado en sus servidores, o (c) una solución híbrida. 2. Justifica con al menos 4 argumentos. 3. Menciona 2 riesgos de tu opción elegida y cómo mitigarlos.
Ver solución
**Decisión recomendada: (b) modelo open source desplegado en la infraestructura del hospital (on-premise).** **Justificación (argumento por argumento):** 1. **El requisito de privacidad es eliminatorio.** "Los datos no pueden salir de la infraestructura" descarta de raíz cualquier API en la nube pública: al llamar a un modelo propietario, el historial clínico viaja a servidores de un tercero. Aunque los proveedores ofrezcan contratos de no-retención y certificaciones, la política interna del hospital lo prohíbe explícitamente. Cuando un requisito es eliminatorio, ordena toda la decisión. 2. **Ya tienen el hardware.** El mayor coste de la opción open source (GPUs) ya está amortizado. Un modelo open source de tamaño medio (7B-70B parámetros, según sus GPUs) corre en servidores propios con motores de inferencia como vLLM u Ollama. 3. **El volumen es moderado.** 500 resúmenes/día no exige la escala elástica de la nube; un despliegue local lo maneja de sobra. Si fueran 500 000/día, la conversación sería otra. 4. **Control y auditabilidad.** Con un modelo local, el hospital controla la versión exacta del modelo (no cambia sin avisar, cosa que sí ocurre con APIs), puede auditar registros internamente y cumple mejor ante inspecciones regulatorias. 5. **Resumir es una tarea "asequible".** El resumen de documentos es una de las tareas donde los modelos open source actuales rinden cerca de los propietarios, sobre todo si se ajusta el prompt o se hace un fine-tuning ligero con ejemplos del hospital. **Riesgos de la opción elegida y mitigaciones:** | Riesgo | Mitigación | |--------|------------| | **Equipo pequeño (2 ingenieros) para desplegar y mantener** un stack de inferencia, monitorización y actualizaciones. | Usar herramientas maduras "llave en mano" (vLLM, Ollama, contenedores preconfigurados), empezar con un solo modelo y una sola tarea, y contratar soporte externo puntual para el despliegue inicial. Los 4 meses de plazo son viables si el alcance se limita a resúmenes. | | **Calidad inferior al mejor modelo propietario** en casos clínicos complejos o jerga muy especializada. | Evaluación piloto con médicos antes de producción (comparar resúmenes del modelo vs resúmenes humanos en 100 casos), plantilla de prompt cuidada, y mantener SIEMPRE al médico como revisor final: el sistema asiste, no decide. | **¿Cuándo habría ganado la opción (a) o la (c)?** - **(a) API propietaria:** si el dato no fuera sensible o la normativa permitiera nubes certificadas, y no hubiera hardware propio: menor tiempo de puesta en marcha y máxima calidad sin gestionar infraestructura. - **(c) Híbrida:** patrón muy común en empresas reales: datos sensibles → modelo local; tareas sin datos personales (p. ej. redactar plantillas genéricas, documentación interna) → API propietaria. Aquí no hace falta porque el caso de uso es único y 100% sensible. **Lección del ejercicio:** en decisiones de ecosistema, primero identifica los requisitos *eliminatorios* (privacidad, normativa, presupuesto duro) y luego optimiza el resto (calidad, velocidad, comodidad). Muchos debates "propietario vs open source" se resuelven solos al mirar quién puede legalmente tocar los datos.Ejercicio 5 — Ordenar hitos de la historia de la IA y justificar su importancia¶
Dificultad: · Capítulo relacionado: Cap. 2 (Historia de la IA)
Contexto: Preparas una charla introductoria para nuevos empleados de tu empresa. Tienes estas seis tarjetas desordenadas y debes colocarlas en una línea temporal correcta y explicar, en una frase por hito, por qué importó.
Tarjetas (desordenadas):
- A. Deep Blue derrota a Garry Kasparov en ajedrez.
- B. Conferencia de Dartmouth: se acuña el término "Artificial Intelligence".
- C. Lanzamiento público de ChatGPT.
- D. Alan Turing publica "Computing Machinery and Intelligence" (el test de Turing).
- E. AlexNet gana ImageNet y detona la era del deep learning.
- F. Se publica "Attention Is All You Need" (la arquitectura Transformer).
Tu tarea: Ordena cronológicamente (con el año aproximado) y escribe una frase de importancia por cada hito.
Ver solución
**Orden cronológico correcto: D → B → A → E → F → C** | Orden | Hito | Año | Por qué importó | |-------|------|-----|-----------------| | 1º | **D. Test de Turing** | 1950 | Antes de que existieran los ordenadores modernos, Turing reformuló "¿pueden pensar las máquinas?" como una pregunta operativa y medible ("¿puede una máquina hacerse pasar por humana en una conversación?"), fundando el debate conceptual de todo el campo. | | 2º | **B. Conferencia de Dartmouth** | 1956 | Acto fundacional: se acuña el nombre "Artificial Intelligence" y la disciplina nace como campo de investigación formal con sus primeros investigadores y programas. | | 3º | **A. Deep Blue vence a Kasparov** | 1997 | Primera vez que una máquina derrota al campeón mundial de ajedrez: demostró que la búsqueda masiva + heurísticas superan al humano en dominios cerrados y bien definidos (aunque no "entendía" ajedrez). | | 4º | **E. AlexNet gana ImageNet** | 2012 | El momento "big bang" del deep learning moderno: una red neuronal profunda entrenada en GPUs pulverizó a todos los métodos clásicos de visión, demostrando que datos masivos + GPUs + redes profundas era la fórmula ganadora. | | 5º | **F. Paper del Transformer** | 2017 | "Attention Is All You Need" introdujo la arquitectura sobre la que se construyen TODOS los grandes modelos de lenguaje actuales (la T de GPT significa Transformer). Posiblemente el paper más influyente de la década. | | 6º | **C. Lanzamiento de ChatGPT** | 2022 (30 de noviembre) | No fue un avance científico radical sino de *producto y acceso*: puso un LLM potente en manos de cualquier persona, alcanzó 100 millones de usuarios en 2 meses y desató la actual ola de adopción masiva e inversión en IA. | **Claves para tu charla (el hilo narrativo que une los hitos):** 1. **1950-1956:** la IA nace como *pregunta filosófica* (Turing) y luego como *disciplina* (Dartmouth). 2. **1997:** la fuerza bruta computacional gana en mundos cerrados (ajedrez), pero el mundo abierto sigue lejos. 3. **2012:** cambio de paradigma: dejar de programar el conocimiento y dejar que las redes lo *aprendan* de datos masivos. Las GPUs (¡diseñadas para videojuegos!) resultan ser el hardware perfecto. 4. **2017-2022:** el Transformer permite escalar el aprendizaje sobre texto a escala de internet, y ChatGPT convierte esa tecnología en un fenómeno social. **Error frecuente:** situar AlexNet o el Transformer "cerca de ChatGPT" sin distinguirlos. Conviene recordar la cadena causal: sin Transformer (2017) no hay GPT; sin la demostración de AlexNet (2012) nadie habría apostado tan fuerte por las redes profundas; sin GPUs baratas, nada de lo anterior habría sido práctico.Ejercicio 6 — Los inviernos de la IA: causas y lecciones para hoy¶
Dificultad: · Capítulo relacionado: Cap. 2 (Historia de la IA)
Contexto: Tu directora de estrategia ha leído que la IA ya vivió "inviernos" (períodos de desilusión y recortes de financiación) y te pregunta, preocupada por las inversiones de la empresa: "¿Qué causó los inviernos anteriores y qué señales deberíamos vigilar para saber si viene otro?"
Tu tarea: Redacta una respuesta que cubra: 1. Qué fue cada uno de los dos grandes inviernos de la IA (épocas aproximadas y causas). 2. El patrón común que los provocó. 3. Tres diferencias entre aquellas épocas y la situación actual. 4. Una señal de alerta razonable que vigilar hoy.
Ver solución
**1. Los dos grandes inviernos:** - **Primer invierno (aprox. 1974-1980):** tras el optimismo desbordado de los años 50-60 ("en una generación tendremos máquinas tan inteligentes como humanos"), los resultados reales fueron mucho más modestos. Informes gubernamentales críticos (como el informe Lighthill en Reino Unido, 1973) concluyeron que la IA no había cumplido sus promesas, y la financiación pública se desplomó. Causas técnicas de fondo: potencia de cálculo ridícula para las ambiciones, y la "explosión combinatoria" — los métodos que funcionaban en problemas de juguete no escalaban al mundo real. - **Segundo invierno (aprox. 1987-1993):** el auge comercial de los **sistemas expertos** (programas con miles de reglas "si-entonces" escritas por expertos) creó una industria entera en los 80. Colapsó porque los sistemas eran carísimos de mantener (cada regla nueva podía romper otras), frágiles fuera de su dominio exacto, y el hardware especializado (máquinas Lisp) fue arrasado por los PC genéricos. Empresas quebradas, inversores quemados, y "IA" se convirtió en palabra maldita en las propuestas de financiación durante años. **2. El patrón común (esta es la parte importante):** > **Expectativas infladas muy por encima de la capacidad técnica real → promesas incumplidas → pérdida de confianza → retirada masiva de financiación.** El ciclo es: hype → decepción → invierno. No fue que la tecnología "fallara" de repente: fue que se prometió lo que no se podía entregar. **3. Tres diferencias relevantes con la situación actual:** 1. **La IA actual ya genera ingresos reales y masivos.** Los inviernos anteriores golpearon a una IA que era esencialmente investigación con demos; hoy la IA está en producción en buscadores, recomendadores, traducción, programación asistida, atención al cliente... Hay valor de negocio verificable, no solo promesas. 2. **La infraestructura es genérica y barata en comparación.** El segundo invierno mató un ecosistema de hardware especializado y frágil; hoy la IA corre sobre nubes y GPUs de propósito general con un ecosistema de software abierto gigantesco (PyTorch, Hugging Face...). 3. **Adopción masiva por usuarios finales.** Cientos de millones de personas usan herramientas de IA directamente. Los inviernos anteriores ocurrieron en tecnologías que el público general nunca llegó a tocar. **4. Señal de alerta a vigilar hoy:** La distancia entre **lo prometido y lo facturado**. Si las inversiones multimillonarias en IA generativa no encuentran modelos de negocio que las justifiquen en un plazo razonable (es decir: si los costes de entrenar e inferir crecen más rápido que los ingresos que generan), puede producirse una corrección fuerte de inversión — quizá no un "invierno" total, pero sí un enfriamiento sectorial. La lección operativa para tu empresa: **invertir en casos de uso con ROI medible y a corto plazo, no en "tener IA" como fin en sí mismo.** Esa disciplina es exactamente lo que faltó antes de los dos inviernos.Ejercicio 7 — Entrenamiento vs inferencia: explicárselo al equipo de finanzas¶
Dificultad: · Capítulo relacionado: Cap. 6 (Cómo aprende una máquina)
Contexto: El equipo de finanzas de tu empresa está revisando el presupuesto del proyecto de IA y no entiende dos partidas: una enorme y puntual llamada "entrenamiento del modelo" y otra pequeña pero recurrente llamada "inferencia". El responsable te pregunta: "¿Por qué pagamos dos veces por lo mismo? ¿No basta con entrenar el modelo una vez?"
Tu tarea: 1. Explica la diferencia entre entrenamiento e inferencia con una analogía no técnica. 2. Explica por qué tienen perfiles de coste tan distintos (puntual y caro vs recurrente y barato por unidad). 3. Indica qué fase usa cada equipo: ¿quién entrena y quién "solo" infiere? 4. Responde a su pregunta: ¿de verdad se entrena una sola vez?
Ver solución
**1. La analogía (respuesta modelo):** > "Piense en formar a un médico. El **entrenamiento** es la carrera de Medicina: años de estudio, miles de casos revisados, un coste enorme que se paga una vez. La **inferencia** es cada consulta que ese médico pasa después: usa lo que ya aprendió para atender a un paciente nuevo en minutos. No pagamos dos veces por lo mismo: pagamos una vez por *crear la capacidad* y luego pagamos un poco cada vez que *la usamos*." **2. Por qué los perfiles de coste son tan distintos:** | | Entrenamiento | Inferencia | |---|---------------|------------| | **Qué ocurre** | El modelo ajusta millones de parámetros viendo todos los datos históricos muchas veces (épocas), calculando errores y corrigiéndose con descenso de gradiente. | El modelo, ya con los parámetros congelados, recibe UNA entrada nueva y produce UNA salida. Solo hay un "pase hacia delante". | | **Cómputo** | Masivo: días o semanas de GPUs trabajando en paralelo. | Mínimo por petición: milisegundos o segundos. | | **Frecuencia** | Puntual (o periódica: reentrenos). | Continua: cada usuario, cada petición, 24/7. | | **Perfil de coste** | Tipo inversión: grande y de una vez. | Tipo coste operativo: pequeño por unidad, pero se multiplica por el volumen de uso. | **Nota:** aunque la inferencia sea barata *por unidad*, a gran escala puede superar con mucho el coste total del entrenamiento. Un modelo que atiende millones de peticiones al día gasta más en inferencia acumulada que lo que costó entrenarlo. Finanzas debe presupuestar la inferencia como coste variable ligado al uso. **3. Quién hace cada cosa:** - **Entrenan:** los laboratorios/equipos de data science o ML engineering (o directamente el proveedor, si usas un modelo como GPT/Claude vía API: tú jamás entrenas ese modelo). - **Infieren:** las aplicaciones de producción y, por tanto, indirectamente cada usuario final. Cuando tu app llama a la API de un LLM, estás pagando inferencia. **4. ¿Se entrena una sola vez?** No exactamente. El mundo cambia y los datos con él (a esto se le llama *deriva de datos* o data drift): los patrones de fraude evolucionan, aparecen productos nuevos, el lenguaje de los clientes cambia. Por eso los modelos se **reentrenan periódicamente** (cada semana, mes o trimestre según el dominio). La respuesta honesta a finanzas: "el entrenamiento es puntual pero no único; presupuesten reentrenos periódicos más un coste de inferencia proporcional al uso". **Consejo profesional:** en las facturas de APIs de LLMs, el precio por token *es* el precio de la inferencia. Entender esta distinción te permite estimar costes de proyecto: (nº peticiones/día) × (tokens por petición) × (precio por token). Harás este cálculo decenas de veces en tu carrera.Ejercicio 8 — Overfitting explicado con analogías (y cómo detectarlo)¶
Dificultad: · Capítulo relacionado: Cap. 6 (Cómo aprende una máquina)
Contexto: Una startup te contrata para auditar su modelo de predicción de impagos. El equipo está eufórico: "¡Nuestro modelo acierta el 99,2% en los datos de entrenamiento!". Pero al desplegarlo con clientes reales, el acierto cae al 63%. El CEO, que no es técnico, exige una explicación de qué ha pasado y cómo evitarlo.
Tu tarea: 1. Diagnostica el problema y dale nombre. 2. Explícaselo al CEO con DOS analogías distintas (para que al menos una le encaje). 3. Explica cómo se detecta a tiempo (antes de desplegar). 4. Propón tres medidas concretas para corregirlo.
Ver solución
**1. Diagnóstico:** el modelo sufre **overfitting (sobreajuste)**: ha memorizado los datos de entrenamiento en lugar de aprender los patrones generales que hay detrás. Por eso brilla en los datos que ya vio (99,2%) y fracasa con datos nuevos (63%). La diferencia enorme entre ambas cifras es la firma inconfundible del overfitting. **2. Las dos analogías para el CEO:** - **El estudiante que memoriza exámenes:** "Imagine un estudiante que, en vez de entender la asignatura, memoriza las respuestas de los exámenes de años anteriores. En el simulacro (preguntas que ya vio) saca un 10. En el examen real, con preguntas nuevas, suspende. Nuestro modelo hizo exactamente eso: memorizó los casos históricos en lugar de aprender qué hace que un cliente impague." - **El traje a medida extrema:** "Es como un traje cosido tan exactamente al cuerpo de una persona concreta — cada lunar, cada asimetría — que no le sirve a nadie más. Queríamos un patrón de tallas que sirviera a la mayoría; hicimos un traje para un solo maniquí: nuestros datos históricos." **3. Cómo se detecta a tiempo (la práctica que faltó):** Nunca se evalúa un modelo con los mismos datos con los que se entrenó. El procedimiento estándar: 1. **Dividir los datos ANTES de entrenar** en conjunto de entrenamiento (p. ej. 80%) y conjunto de prueba/test (20%) que el modelo jamás ve durante el entrenamiento (el famoso *train/test split* del Capítulo 5). 2. **Comparar rendimiento en ambos conjuntos.** Si entrenamiento ≫ test (como 99% vs 63%), hay overfitting. Si ambos son similares y buenos, el modelo generaliza. 3. Durante el entrenamiento, monitorizar además un conjunto de **validación**: cuando el error de validación empieza a subir mientras el de entrenamiento sigue bajando, es el momento de parar (*early stopping*). **4. Tres medidas correctoras concretas:** 1. **Más datos (o datos más variados):** cuantos más ejemplos reales vea el modelo, más difícil le resulta memorizar y más se ve obligado a generalizar. 2. **Simplificar el modelo:** un modelo con demasiada capacidad (demasiados parámetros para tan pocos datos) memoriza. Reducir complejidad o aplicar **regularización** (penalizar parámetros extremos) fuerza a aprender solo lo esencial. 3. **Validación rigurosa como proceso:** train/test split estricto, validación cruzada, y una regla de oro organizativa: **ninguna cifra de rendimiento se comunica a dirección si viene del conjunto de entrenamiento.** **Advertencia:** el error de esta startup es dolorosamente común: reportar métricas de entrenamiento como si fueran métricas reales. Cuando te presenten un "99% de acierto", tu primera pregunta profesional debe ser siempre: *"¿medido sobre qué datos?"*.Ejercicio 9 — Roles profesionales: ¿a quién contrato para mi equipo de IA?¶
Dificultad: · Capítulo relacionado: Cap. 7 (Ecosistema y herramientas)
Contexto: Una empresa mediana de e-commerce va a crear su primer equipo de IA con presupuesto para 3 contrataciones. Su plan de proyectos para el primer año: (1) un dashboard de métricas de negocio con análisis de datos, (2) un sistema de recomendación de productos, y (3) un chatbot de atención al cliente construido sobre la API de un LLM comercial. La directora de RRHH te enseña las descripciones de puesto que ha encontrado por internet — Data Analyst, Data Scientist, Data Engineer, ML Engineer, AI Engineer, MLOps Engineer, Prompt Engineer — y te pregunta: "¿Cuáles tres contrato y por qué? ¿Qué hace cada uno?"
Tu tarea: 1. Define en una frase cada uno de los 7 roles. 2. Elige las 3 contrataciones para ESTA empresa y ESTOS proyectos, justificando. 3. Explica qué proyectos cubre cada contratación.
Ver solución
**1. Los 7 roles en una frase:** | Rol | Qué hace | |-----|----------| | **Data Analyst** | Analiza datos históricos y construye informes/dashboards para responder preguntas de negocio (SQL, BI, estadística descriptiva). | | **Data Scientist** | Explora datos y construye modelos (prototipos) para predecir o clasificar; fuerte en estadística y experimentación. | | **Data Engineer** | Construye y mantiene las tuberías de datos: recolección, limpieza, almacenamiento; sin él, nadie tiene datos fiables con los que trabajar. | | **ML Engineer** | Lleva los modelos a producción: los convierte de notebook a servicio robusto, escalable y monitorizado. | | **AI Engineer** | Construye aplicaciones sobre modelos ya existentes (sobre todo LLMs vía API): integración, prompts, RAG, agentes. Rol emergente post-ChatGPT. | | **MLOps Engineer** | Automatiza el ciclo de vida del ML (despliegue, versionado, monitorización, reentrenos); es al ML lo que DevOps al software. | | **Prompt Engineer** | Especialista en diseñar y optimizar instrucciones para LLMs; en la práctica, cada vez más una *habilidad* dentro de otros roles que un puesto independiente. | **2. Las 3 contrataciones recomendadas:** 1. **Data Engineer** — la contratación menos glamurosa y más crítica. Los tres proyectos dependen de datos limpios y accesibles (ventas para el dashboard, historial de compras para el recomendador, catálogo y FAQs para el chatbot). Sin esta base, los otros dos contratados perderán el 70% de su tiempo peleándose con datos rotos. Regla del sector: los proyectos de IA fracasan más por datos que por modelos. 2. **Data Scientist (con perfil híbrido hacia analista)** — cubre el dashboard (proyecto 1) y construye el sistema de recomendación (proyecto 2), el proyecto de ML "clásico" del año. En una empresa pequeña, un buen data scientist absorbe el trabajo de data analyst. 3. **AI Engineer** — el chatbot (proyecto 3) no requiere entrenar ningún modelo: se construye SOBRE la API de un LLM comercial. Eso es exactamente el perfil AI Engineer: integración de APIs, diseño de prompts, conexión con la base de conocimiento de la empresa y evaluación de respuestas. El prompt engineering lo aporta como habilidad, no como puesto aparte. **3. Cobertura de proyectos:** | Proyecto | Responsable principal | Apoyo | |----------|----------------------|-------| | Dashboard de métricas | Data Scientist | Data Engineer (pipelines) | | Recomendador de productos | Data Scientist | Data Engineer (datos), y a futuro un ML Engineer para escalar | | Chatbot sobre LLM | AI Engineer | Data Engineer (base de conocimiento) | **¿Y los descartados?** ML Engineer y MLOps son roles de *escala*: imprescindibles cuando tienes varios modelos en producción con tráfico serio, prematuros para el año 1 (el propio equipo puede cubrir un despliegue sencillo). Prompt Engineer como puesto aislado rara vez se justifica: es una competencia del AI Engineer. **Consejo profesional:** cuando dimensiones equipos de IA, pregunta siempre primero "¿en qué estado están los datos?". La respuesta decide si la primera contratación es un perfil de datos o un perfil de modelos. Casi siempre es de datos.Ejercicio 10 — Evaluar el hype: ¿este anuncio revoluciona la IA o es humo?¶
Dificultad: · Capítulos relacionados: Caps. 1, 2 y 7
Contexto: Tu CEO te reenvía una nota de prensa con el asunto "¿¿Deberíamos comprar esto??":
"La startup NeuroQuantum AI presenta OmniMind-X, el primer sistema de Inteligencia Artificial General (AGI) del mercado. OmniMind-X 'piensa como un humano', 'entiende cualquier industria sin necesidad de datos' y 'elimina el 100% de los errores humanos'. Según su fundador, 'los modelos tradicionales quedarán obsoletos en 6 meses'. La empresa, fundada hace 8 meses, no ha publicado benchmarks pero afirma que sus resultados internos 'superan a todos los modelos existentes'. Disponible mediante licencia anual de 400 000 €."
Tu tarea: Escribe un análisis crítico para tu CEO que: 1. Identifique al menos 5 señales de alarma (red flags) concretas en el texto. 2. Explique qué relación tiene esto con la historia del hype en IA (Cap. 2). 3. Proponga una lista de 4 preguntas de verificación ("due diligence") antes de considerar cualquier compra. 4. Termine con una recomendación clara.
Ver solución
**1. Señales de alarma, una por una:** | # | Red flag | Por qué es alarmante | |---|----------|----------------------| | 1 | **"Primer sistema de AGI del mercado"** | La AGI (inteligencia artificial general, capaz de cualquier tarea intelectual humana) **no existe hoy**; ningún laboratorio serio — con miles de investigadores y miles de millones en cómputo — afirma haberla logrado. Que lo afirme una startup de 8 meses es extraordinariamente inverosímil, y las afirmaciones extraordinarias exigen pruebas extraordinarias. | | 2 | **"Entiende cualquier industria sin necesidad de datos"** | Contradice el fundamento mismo del machine learning: los modelos aprenden DE datos. Un sistema sin datos de tu dominio no puede conocer tu dominio. Es una imposibilidad técnica presentada como ventaja. | | 3 | **"Elimina el 100% de los errores"** | Los sistemas de ML son estadísticos: SIEMPRE tienen una tasa de error. Prometer el 100% de acierto es la mentira más fácil de detectar de todo el marketing de IA. | | 4 | **Sin benchmarks públicos, solo "resultados internos"** | El estándar del sector es publicar evaluaciones en benchmarks reconocidos y reproducibles, o someterse a evaluación independiente. "Confía en nuestras cifras secretas" + precio de 400 000 € = combinación tóxica. | | 5 | **"Los modelos tradicionales quedarán obsoletos en 6 meses"** | Predicción apocalíptica con plazo concreto: retórica clásica de presión comercial (miedo a quedarse fuera), no de comunicación científica. | | 6 | *(extra)* **Lenguaje antropomórfico: "piensa como un humano"** | Vender mecanismo humano en lugar de rendimiento medible es marketing; los sistemas actuales hacen procesamiento estadístico de patrones, no "piensan como humanos". | **2. Conexión con la historia (Cap. 2):** Este anuncio es un ejemplar de manual del ciclo **hype → decepción** que provocó los dos inviernos de la IA. En los años 60 se prometió "inteligencia humana en una generación"; en los 80, los sistemas expertos se vendieron como solución universal a precios astronómicos. Ambas burbujas compartían los mismos rasgos que OmniMind-X: promesas ilimitadas, plazos cortos, precios altos y ausencia de evidencia verificable. La historia no dice que toda innovación sea falsa; dice que **la carga de la prueba está en quien promete**, y que las promesas sin evidencia acaban en facturas sin retorno. **3. Las 4 preguntas de due diligence:** 1. **"Muéstrenme resultados en benchmarks públicos y reproducibles, o permitan una evaluación independiente con NUESTROS datos y casos de uso."** (Si se niegan, fin de la conversación.) 2. **"¿Qué arquitectura y qué datos de entrenamiento usa el sistema, y qué limitaciones documentadas tiene?"** (Todo sistema real tiene limitaciones documentadas; solo los humos no tienen ninguna.) 3. **"¿Qué clientes reales lo usan en producción y podemos hablar con dos de ellos?"** (Referencias verificables, no logos en una web.) 4. **"¿Ofrecen una prueba piloto acotada y medible antes de la licencia anual?"** (Un proveedor serio acepta demostrar valor en pequeño.) **4. Recomendación al CEO (respuesta modelo):** > "Recomiendo no avanzar. El anuncio contiene afirmaciones técnicamente imposibles (funcionar sin datos, 0% de errores) y la afirmación central — AGI — no la sostiene hoy ningún actor del sector. No hay evidencia verificable y el patrón retórico coincide con las burbujas que precedieron a los inviernos de la IA. Si el área de negocio que motivó tu interés es real, definamos el problema concreto y evaluemos soluciones establecidas y medibles con un piloto. El presupuesto de 400 000 € da para mucho valor real." **Consejo profesional:** desarrolla el reflejo de traducir todo anuncio de IA a tres preguntas: *¿qué tarea concreta hace?, ¿cómo se mide?, ¿quién lo verificó?* Si el anuncio no permite responder ninguna, no estás ante tecnología: estás ante marketing.SECCIÓN B — Matemáticas con código¶
Los conceptos del Capítulo 3, primero a mano y después verificados con NumPy. No te saltes la parte manual: es donde se construye la intuición.
Ejercicio 11 — Producto punto a mano y con NumPy¶
Dificultad: · Capítulo relacionado: Cap. 3 (Matemáticas para IA)
Contexto: En un sistema de recomendación, el "interés" de un usuario por un producto se calcula como el producto punto entre el vector de gustos del usuario y el vector de características del producto. Tienes:
- Usuario:
u = [2, 1, 3](cuánto le gustan: acción, romance, ciencia ficción) - Película:
p = [4, 0, 2](cuánto tiene de: acción, romance, ciencia ficción)
Tu tarea:
1. Calcula u · p a mano, escribiendo cada paso.
2. Verifica el resultado con NumPy de dos formas distintas (np.dot y el operador @).
3. Explica en una frase qué significa el número obtenido en el contexto del recomendador.
Ver solución
**1. Cálculo a mano:** El producto punto multiplica los vectores componente a componente y suma los resultados: **2. Verificación con NumPy:**import numpy as np # Importamos NumPy con su alias estándar
u = np.array([2, 1, 3]) # Vector de gustos del usuario
p = np.array([4, 0, 2]) # Vector de características de la película
resultado_dot = np.dot(u, p) # Forma 1: función np.dot
resultado_arroba = u @ p # Forma 2: operador @ (recomendado en Python moderno)
print(resultado_dot) # 14
print(resultado_arroba) # 14
print(resultado_dot == resultado_arroba) # True: ambas formas son equivalentes
Ejercicio 12 — Similitud coseno entre documentos de juguete¶
Dificultad: · Capítulo relacionado: Cap. 3 (Matemáticas para IA)
Contexto: El equipo de soporte quiere agrupar tickets parecidos. Como prototipo, representas cada ticket contando cuántas veces aparece cada palabra del vocabulario ["modelo", "precio", "envío"]:
- Ticket A:
[2, 1, 0](habla 2 veces de "modelo", 1 de "precio", 0 de "envío") - Ticket B:
[1, 2, 0] - Ticket C:
[0, 1, 2]
Tu tarea:
1. Calcula a mano la similitud coseno entre A y B, y entre A y C. Fórmula: cos(v, w) = (v · w) / (||v|| · ||w||).
2. Verifica con NumPy escribiendo una función similitud_coseno(v, w).
3. ¿Qué par de tickets es más parecido y por qué tiene sentido?
4. Pregunta extra: ¿por qué usamos coseno y no simplemente el producto punto?
Ver solución
**1. Cálculo a mano:** Primero las normas (longitudes) de cada vector: (Los tres vectores tienen la misma longitud √5 ≈ 2.236 — elegido a propósito para que el cálculo sea limpio.) Similitud A-B: Similitud A-C: **2. Verificación con NumPy:**import numpy as np
def similitud_coseno(v: np.ndarray, w: np.ndarray) -> float:
"""Devuelve la similitud coseno entre dos vectores."""
producto_punto = v @ w # Numerador: producto punto
norma_v = np.linalg.norm(v) # ||v||: longitud del primer vector
norma_w = np.linalg.norm(w) # ||w||: longitud del segundo vector
return producto_punto / (norma_v * norma_w) # Fórmula del coseno
A = np.array([2, 1, 0])
B = np.array([1, 2, 0])
C = np.array([0, 1, 2])
print(f"cos(A, B) = {similitud_coseno(A, B):.2f}") # 0.80
print(f"cos(A, C) = {similitud_coseno(A, C):.2f}") # 0.20
Ejercicio 13 — Normalizar vectores (norma L2)¶
Dificultad: · Capítulo relacionado: Cap. 3 (Matemáticas para IA)
Contexto: Las bases de datos vectoriales suelen almacenar los embeddings normalizados (longitud 1) para acelerar las búsquedas: con vectores unitarios, la similitud coseno se reduce a un simple producto punto.
Tu tarea:
1. Normaliza a mano el vector v = [3, 4] (divide cada componente entre la norma L2).
2. Haz lo mismo con w = [1, 1, 1, 1].
3. Escribe una función NumPy normalizar(v) y verifica que los vectores resultantes tienen norma 1.
4. Comprueba con código la afirmación del contexto: que con vectores normalizados, producto punto = similitud coseno.
Ver solución
**1. A mano, `v = [3, 4]`:**||v|| = √(3² + 4²) = √(9 + 16) = √25 = 5
v_normalizado = [3/5, 4/5] = [0.6, 0.8]
Comprobación: √(0.6² + 0.8²) = √(0.36 + 0.64) = √1 = 1 ✅
||w|| = √(1 + 1 + 1 + 1) = √4 = 2
w_normalizado = [0.5, 0.5, 0.5, 0.5]
Comprobación: √(4 × 0.25) = √1 = 1 ✅
import numpy as np
def normalizar(v: np.ndarray) -> np.ndarray:
"""Devuelve el vector con la misma dirección y norma 1."""
norma = np.linalg.norm(v) # Calculamos la longitud (norma L2)
if norma == 0: # Caso límite: el vector cero no se puede normalizar
raise ValueError("No se puede normalizar el vector cero")
return v / norma # NumPy divide TODAS las componentes a la vez (broadcasting)
v = np.array([3.0, 4.0])
w = np.array([1.0, 1.0, 1.0, 1.0])
v_n = normalizar(v)
w_n = normalizar(w)
print(v_n) # [0.6 0.8]
print(w_n) # [0.5 0.5 0.5 0.5]
print(np.linalg.norm(v_n)) # 1.0 ← norma unitaria confirmada
print(np.linalg.norm(w_n)) # 1.0
a = normalizar(np.array([2.0, 1.0, 0.0]))
b = normalizar(np.array([1.0, 2.0, 0.0]))
producto = a @ b # Producto punto de los normalizados
coseno = (a @ b) / (np.linalg.norm(a) * np.linalg.norm(b)) # Fórmula completa del coseno
print(f"{producto:.4f}") # 0.8000
print(f"{coseno:.4f}") # 0.8000 ← idénticos: al dividir entre 1 × 1 no cambia nada
Ejercicio 14 — Aplicar softmax e interpretar la temperatura¶
Dificultad: · Capítulo relacionado: Cap. 3 (Matemáticas para IA)
Contexto: Un modelo de lenguaje, antes de elegir la siguiente palabra, produce puntuaciones brutas (logits) para cada candidata. Para la frase "El gato se subió al...", el modelo produce:
| Palabra | Logit |
|---|---|
| tejado | 2.0 |
| árbol | 1.0 |
| coche | 0.1 |
Tu tarea:
1. Aplica softmax a mano (con calculadora: softmax(zᵢ) = e^zᵢ / Σe^zⱼ) y obtén las tres probabilidades.
2. Verifica con una función NumPy softmax(z).
3. Aplica ahora softmax a los logits divididos por una temperatura T=0.5 y T=2.0. Compara las tres distribuciones.
4. Explica qué hace la temperatura y por qué los proveedores de LLMs la exponen como parámetro.
Ver solución
**1. A mano:**e^2.0 = 7.389
e^1.0 = 2.718
e^0.1 = 1.105
Suma = 7.389 + 2.718 + 1.105 = 11.212
P(tejado) = 7.389 / 11.212 ≈ 0.659 (65.9%)
P(árbol) = 2.718 / 11.212 ≈ 0.242 (24.2%)
P(coche) = 1.105 / 11.212 ≈ 0.099 ( 9.9%)
Comprobación: 0.659 + 0.242 + 0.099 = 1.0 ✅ (softmax SIEMPRE suma 1)
import numpy as np
def softmax(z: np.ndarray) -> np.ndarray:
"""Convierte logits en una distribución de probabilidad."""
exp_z = np.exp(z - np.max(z)) # Restamos el máximo: truco de estabilidad numérica
# (no cambia el resultado, evita overflow con logits grandes)
return exp_z / exp_z.sum() # Cada exponencial dividida por la suma total
logits = np.array([2.0, 1.0, 0.1])
probs = softmax(logits)
print(np.round(probs, 3)) # [0.659 0.242 0.099]
print(probs.sum()) # 1.0
Ejercicio 15 — Un paso de descenso de gradiente a mano para f(x) = x²¶
Dificultad: · Capítulo relacionado: Cap. 3 (Matemáticas para IA)
Contexto: El descenso de gradiente es el algoritmo con el que aprende prácticamente toda la IA moderna. Antes de aplicarlo a redes con miles de millones de parámetros, domínalo en el caso más simple posible: minimizar f(x) = x² (cuyo mínimo, obviamente, está en x = 0). La regla de actualización es:
Tu tarea:
1. Deriva f(x) = x² (¿cuánto vale f'(x)?).
2. Partiendo de x₀ = 4 y con η = 0.1, calcula a mano 3 pasos de descenso de gradiente.
3. Implementa el bucle en Python (sin NumPy, no hace falta) y ejecuta 20 pasos. ¿A qué valor se acerca x?
4. Repite con η = 1.1 y observa qué ocurre. Explica el fenómeno.
Ver solución
**1. La derivada:** `f'(x) = 2x`. La derivada nos dice la pendiente: si x = 4, la pendiente es 8 (cuesta arriba hacia la derecha), así que para bajar hay que moverse hacia la izquierda. El signo negativo de la regla hace exactamente eso: moverse **en contra** del gradiente. **2. Tres pasos a mano (x₀ = 4, η = 0.1):**Paso 1: x₁ = 4 − 0.1 × (2 × 4) = 4 − 0.8 = 3.2
Paso 2: x₂ = 3.2 − 0.1 × (2 × 3.2) = 3.2 − 0.64 = 2.56
Paso 3: x₃ = 2.56 − 0.1 × (2 × 2.56) = 2.56 − 0.512 = 2.048
def gradiente_descendente(x_inicial: float, eta: float, pasos: int) -> float:
x = x_inicial # Punto de partida
for i in range(pasos):
gradiente = 2 * x # f'(x) = 2x: la pendiente en el punto actual
x = x - eta * gradiente # La regla: un paso en dirección contraria a la pendiente
print(f"Paso {i+1:2d}: x = {x:.6f}, f(x) = {x**2:.6f}")
return x
x_final = gradiente_descendente(x_inicial=4.0, eta=0.1, pasos=20)
# Paso 1: x = 3.200000, f(x) = 10.240000
# Paso 2: x = 2.560000, f(x) = 6.553600
# Paso 3: x = 2.048000, f(x) = 4.194304
# ...
# Paso 20: x = 0.046117, f(x) = 0.002127 ← convergiendo suavemente hacia x = 0
Ejercicio 16 — Teorema de Bayes: el test médico que engaña¶
Dificultad: · Capítulo relacionado: Cap. 3 (Matemáticas para IA)
Contexto: Una aseguradora médica evalúa un test de cribado para una enfermedad. Datos:
- Prevalencia: el 1% de la población tiene la enfermedad. →
P(E) = 0.01 - Sensibilidad: si tienes la enfermedad, el test da positivo el 99% de las veces. →
P(+|E) = 0.99 - Falsos positivos: si NO tienes la enfermedad, el test da positivo el 5% de las veces. →
P(+|¬E) = 0.05
Un cliente recibe un positivo y, aterrado, pregunta: "¿Significa que tengo la enfermedad con un 99% de seguridad?"
Tu tarea:
1. Calcula a mano con el teorema de Bayes la probabilidad real de tener la enfermedad dado el positivo: P(E|+).
2. Verifica el cálculo con Python.
3. Explica el resultado con el "método de las 10 000 personas" (frecuencias naturales), que es como se le explica a un paciente.
4. ¿Qué tiene que ver esto con los sistemas de IA de detección (fraude, spam, diagnóstico)?
Ver solución
**1. Teorema de Bayes a mano:** P(+|E) · P(E) 0.99 × 0.01
P(E|+) = ————————————————— = ———————————————————————————————————————
P(+) 0.99 × 0.01 + 0.05 × 0.99
(positivos reales) (falsos positivos)
= 0.0099 / (0.0099 + 0.0495)
= 0.0099 / 0.0594
≈ 0.1667 → ¡Solo un 16.7%!
p_enfermedad = 0.01 # P(E): prevalencia
p_pos_dado_e = 0.99 # P(+|E): sensibilidad
p_pos_dado_sano = 0.05 # P(+|¬E): tasa de falsos positivos
p_sano = 1 - p_enfermedad # P(¬E) = 0.99
# Probabilidad total de positivo: los dos caminos que llevan a un "+"
p_positivo = (p_pos_dado_e * p_enfermedad) + (p_pos_dado_sano * p_sano)
# Bayes: de todos los positivos, ¿qué fracción está realmente enferma?
p_e_dado_positivo = (p_pos_dado_e * p_enfermedad) / p_positivo
print(f"P(E|+) = {p_e_dado_positivo:.4f}") # 0.1667
print(f"Es decir: {p_e_dado_positivo:.1%}") # 16.7%
Ejercicio 17 — Media y desviación estándar de latencias con NumPy¶
Dificultad: · Capítulos relacionados: Caps. 3-4
Contexto: Monitorizas la latencia (en milisegundos) de las respuestas de tu API de IA durante 12 peticiones consecutivas:
(Sí, hay una petición que tardó 1.9 segundos: algo pasó ahí.)
Tu tarea: 1. Calcula la media a mano (suma y divide) de las primeras 4 latencias para calibrar tu intuición. 2. Con NumPy: calcula media, mediana y desviación estándar del array completo. 3. Compara media y mediana. ¿Por qué son tan distintas y cuál representa mejor la experiencia típica del usuario? 4. Detecta programáticamente los valores atípicos usando la regla "más de 2 desviaciones estándar de la media".
Ver solución
**1. A mano (primeras 4):** Esa es la zona "normal" del servicio: unos 225 ms. **2. Con NumPy:**import numpy as np
latencias = np.array([220, 235, 210, 240, 225, 1900, 230, 215, 245, 228, 232, 218])
media = latencias.mean() # Suma de todos / cantidad
mediana = np.median(latencias) # El valor central al ordenarlos
desviacion = latencias.std() # Cuánto se alejan los valores de la media, en promedio
print(f"Media: {media:.1f} ms") # 366.5 ms
print(f"Mediana: {mediana:.1f} ms") # 229.0 ms
print(f"Desv. est.: {desviacion:.1f} ms") # 462.5 ms
limite_superior = media + 2 * desviacion # Umbral: media + 2 desviaciones
limite_inferior = media - 2 * desviacion # (por abajo también, por completitud)
# Máscara booleana: True donde el valor es atípico
es_outlier = (latencias > limite_superior) | (latencias < limite_inferior)
print(latencias[es_outlier]) # [1900] ← NumPy filtra el array con la máscara
print(np.where(es_outlier)[0]) # [5] ← posición del outlier en el array
Ejercicio 18 — Mini buscador semántico con similitud coseno¶
Dificultad: · Capítulos relacionados: Caps. 3-5
Contexto: Vas a construir el esqueleto de un buscador semántico — la pieza central de cualquier sistema RAG — en versión juguete. La "base de conocimiento" de una tienda online tiene 4 documentos, representados como vectores sobre el vocabulario ["envío", "devolución", "pago", "producto"] (conteo de menciones de cada tema):
documentos = {
"politica_envios": [5, 0, 0, 1],
"politica_devoluciones": [1, 6, 0, 2],
"metodos_de_pago": [0, 0, 7, 1],
"garantia_producto": [0, 2, 0, 5],
}
Llega la consulta de un cliente, ya vectorizada igual: consulta = [2, 1, 0, 0] ("¿puedo cambiar la dirección de envío de mi pedido?": menciona 2 veces envío, 1 devolución).
Tu tarea:
1. Escribe una función buscar(consulta, documentos) que calcule la similitud coseno de la consulta contra cada documento y devuelva el ranking ordenado de mayor a menor.
2. Ejecuta la búsqueda e interpreta el resultado: ¿qué documento debería mostrarse al cliente?
3. Explica qué cambiaría en un sistema real (pista: ¿de dónde saldrían los vectores?).
Ver solución
**1 y 2. Implementación completa:**import numpy as np
def similitud_coseno(v: np.ndarray, w: np.ndarray) -> float:
"""Similitud coseno entre dos vectores (reutilizada del Ejercicio 12)."""
return (v @ w) / (np.linalg.norm(v) * np.linalg.norm(w))
def buscar(consulta: list[float], documentos: dict[str, list[float]]) -> list[tuple[str, float]]:
"""Devuelve los documentos ordenados por similitud con la consulta (mayor primero)."""
q = np.array(consulta, dtype=float) # Convertimos la consulta a array NumPy
resultados = []
for nombre, vector in documentos.items(): # Recorremos cada documento de la base
d = np.array(vector, dtype=float) # Vector del documento
score = similitud_coseno(q, d) # Puntuación de parecido consulta-documento
resultados.append((nombre, score)) # Guardamos (nombre, puntuación)
# Ordenamos por puntuación descendente: el más parecido primero
resultados.sort(key=lambda par: par[1], reverse=True)
return resultados
documentos = {
"politica_envios": [5, 0, 0, 1],
"politica_devoluciones": [1, 6, 0, 2],
"metodos_de_pago": [0, 0, 7, 1],
"garantia_producto": [0, 2, 0, 5],
}
consulta = [2, 1, 0, 0]
for nombre, score in buscar(consulta, documentos):
print(f"{nombre:25s} → {score:.3f}")
SECCIÓN C — Python para IA¶
Dificultad creciente sobre el Capítulo 4. Todo el código usa contextos del mundo de la IA: no es Python "de juguete", es el Python que escribirás en tu trabajo.
Ejercicio 19 — FizzBuzz con giro: monitor de lotes de entrenamiento¶
Dificultad: · Capítulo relacionado: Cap. 4 (Python para IA)
Contexto: Estás monitorizando un entrenamiento que procesa 30 lotes (batches) de datos. Las reglas del sistema de logging son:
- Si el número de lote es divisible por 3, imprime
"checkpoint"(se guarda el modelo). - Si es divisible por 5, imprime
"validacion"(se evalúa con el conjunto de validación). - Si es divisible por ambos, imprime
"checkpoint+validacion". - En cualquier otro caso, imprime
"lote N procesado".
Tu tarea: Escribe un bucle que recorra los lotes 1 a 30 aplicando las reglas. Pregunta extra: ¿por qué hay que comprobar el caso "ambos" PRIMERO?
Ver solución
for lote in range(1, 31): # range(1, 31) genera 1, 2, ..., 30 (el tope se excluye)
if lote % 15 == 0: # Divisible por 3 Y por 5 equivale a divisible por 15
print("checkpoint+validacion") # El caso combinado SIEMPRE se comprueba primero
elif lote % 3 == 0: # % es el módulo: resto de la división (resto 0 = divisible)
print("checkpoint")
elif lote % 5 == 0:
print("validacion")
else:
print(f"lote {lote} procesado") # f-string: interpola la variable dentro del texto
Ejercicio 20 — Funciones con type hints: calculadora de coste de tokens¶
Dificultad: · Capítulo relacionado: Cap. 4 (Python para IA)
Contexto: Tu empresa usa un LLM por API que cobra 3 € por millón de tokens de entrada y 15 € por millón de tokens de salida. Necesitas una función para estimar costes de proyectos.
Tu tarea: Escribe una función coste_llamada con type hints completos que:
1. Reciba tokens_entrada: int, tokens_salida: int y, opcionalmente, los precios (con los valores por defecto de arriba).
2. Devuelva el coste en euros como float.
3. Lance ValueError si algún número de tokens es negativo.
4. Úsala para calcular: (a) una llamada de 1 200 tokens de entrada y 350 de salida; (b) el coste mensual de un chatbot con 10 000 llamadas/día como esa.
Ver solución
def coste_llamada(
tokens_entrada: int, # Tokens que enviamos al modelo (prompt + contexto)
tokens_salida: int, # Tokens que el modelo genera (la respuesta)
precio_entrada: float = 3.0, # € por MILLÓN de tokens de entrada (valor por defecto)
precio_salida: float = 15.0, # € por MILLÓN de tokens de salida
) -> float: # La función devuelve un float: el coste en euros
"""Calcula el coste en euros de una llamada a la API del LLM."""
if tokens_entrada < 0 or tokens_salida < 0:
# Validar las entradas al principio ("fail fast"): un valor absurdo debe explotar
# aquí con un mensaje claro, no propagarse como un coste negativo silencioso.
raise ValueError("El número de tokens no puede ser negativo")
coste_entrada = (tokens_entrada / 1_000_000) * precio_entrada # Regla de tres sobre el millón
coste_salida = (tokens_salida / 1_000_000) * precio_salida # 1_000_000: los guiones bajos
return coste_entrada + coste_salida # son solo legibilidad
# (a) Una llamada individual
coste_unitario = coste_llamada(tokens_entrada=1200, tokens_salida=350)
print(f"Coste por llamada: {coste_unitario:.6f} €") # 0.008850 €
# (b) Proyección mensual: 10 000 llamadas/día × 30 días
llamadas_mes = 10_000 * 30
coste_mensual = coste_unitario * llamadas_mes
print(f"Coste mensual estimado: {coste_mensual:.2f} €") # 2655.00 €
Ejercicio 21 — Comprehensions sobre datos de clientes¶
Dificultad: · Capítulo relacionado: Cap. 4 (Python para IA)
Contexto: Recibes datos de clientes como lista de diccionarios (el formato en que casi todas las APIs devuelven registros):
clientes = [
{"nombre": "Ana", "gasto_anual": 4200, "pais": "ES", "activo": True},
{"nombre": "Luis", "gasto_anual": 1500, "pais": "MX", "activo": False},
{"nombre": "Carla", "gasto_anual": 8800, "pais": "ES", "activo": True},
{"nombre": "Pedro", "gasto_anual": 300, "pais": "AR", "activo": True},
{"nombre": "Sofía", "gasto_anual": 6100, "pais": "MX", "activo": True},
]
Tu tarea: Resuelve cada punto con UNA comprehension (sin bucles for tradicionales):
1. Lista con los nombres de todos los clientes.
2. Lista con los nombres de los clientes activos con gasto > 4000 (los VIP).
3. Diccionario {nombre: gasto_anual} solo de clientes activos.
4. Lista de los gastos con un descuento del 10% aplicado, solo para clientes de "ES".
5. (Extra) ¿Cuándo NO deberías usar una comprehension?
Ver solución
# 1. Extraer un campo de cada registro: la comprehension más básica
nombres = [c["nombre"] for c in clientes]
# ["Ana", "Luis", "Carla", "Pedro", "Sofía"]
# 2. Extraer con filtro: el "if" al final selecciona qué elementos entran
vips = [c["nombre"] for c in clientes if c["activo"] and c["gasto_anual"] > 4000]
# ["Ana", "Carla", "Sofía"] ← Ana entra por poco: 4200 > 4000 y está activa
# 3. Dict comprehension: pares clave: valor entre llaves
gastos_activos = {c["nombre"]: c["gasto_anual"] for c in clientes if c["activo"]}
# {"Ana": 4200, "Carla": 8800, "Pedro": 300, "Sofía": 6100}
# 4. Transformación + filtro combinados: la expresión de la izquierda transforma,
# el if de la derecha filtra
gastos_es_descuento = [c["gasto_anual"] * 0.9 for c in clientes if c["pais"] == "ES"]
# [3780.0, 7920.0] ← solo Ana y Carla son de "ES"; Luis (MX) no entra aunque esté inactivo... ni por eso
Ejercicio 22 — Parsear el JSON de una API de IA ficticia¶
Dificultad: · Capítulo relacionado: Cap. 4 (Python para IA)
Contexto: Tu aplicación llama a la API de un LLM ficticio ("MegaBrain API") y recibe esta respuesta JSON como texto. Tu trabajo: extraer los datos de forma robusta, porque las APIs a veces devuelven estructuras incompletas.
respuesta_api = '''
{
"id": "resp_8f4a2",
"modelo": "megabrain-4",
"opciones": [
{
"mensaje": {"rol": "asistente", "contenido": "La capital de Australia es Canberra."},
"motivo_fin": "stop"
}
],
"uso": {"tokens_entrada": 18, "tokens_salida": 9}
}
'''
Tu tarea:
1. Parsea el texto JSON a un diccionario Python.
2. Extrae e imprime: el texto de la respuesta, el modelo y el total de tokens usados.
3. Hazlo a prueba de fallos: si falta cualquier campo (por ejemplo "uso"), el programa no debe romperse, sino usar valores por defecto sensatos.
4. Empaqueta la lógica en una función extraer_respuesta(texto_json) -> dict que devuelva {"texto": ..., "modelo": ..., "tokens_totales": ...}.
Ver solución
import json # Módulo estándar para JSON: viene con Python
def extraer_respuesta(texto_json: str) -> dict:
"""Extrae los campos útiles de una respuesta de la MegaBrain API.
Devuelve siempre un dict con las claves texto/modelo/tokens_totales,
aunque la respuesta venga incompleta o malformada.
"""
# --- Paso 1: parsear el texto a estructuras Python ---
try:
datos = json.loads(texto_json) # loads = "load string": texto JSON → dict/list
except json.JSONDecodeError: # Si el texto no es JSON válido...
# ...devolvemos la estructura por defecto: quien nos llama no tiene que romperse
return {"texto": "", "modelo": "desconocido", "tokens_totales": 0}
# --- Paso 2: extraer campos con .get() y valores por defecto ---
# dict.get(clave, defecto) devuelve el defecto si la clave no existe,
# en lugar de lanzar KeyError como haría datos["clave"]
modelo = datos.get("modelo", "desconocido")
# Para rutas anidadas, encadenamos .get() con defectos intermedios:
# si "opciones" no existe → lista vacía → el if la detecta
opciones = datos.get("opciones", [])
if opciones: # Lista no vacía: hay al menos una respuesta
mensaje = opciones[0].get("mensaje", {}) # Primer candidato; {} como defecto anidado
texto = mensaje.get("contenido", "")
else:
texto = "" # Sin opciones: respuesta vacía pero válida
# "uso" puede faltar entero: {} como defecto permite encadenar los .get() interiores
uso = datos.get("uso", {})
tokens_totales = uso.get("tokens_entrada", 0) + uso.get("tokens_salida", 0)
# --- Paso 3: devolver SIEMPRE la misma estructura ---
return {"texto": texto, "modelo": modelo, "tokens_totales": tokens_totales}
# Uso normal
resultado = extraer_respuesta(respuesta_api)
print(resultado["texto"]) # La capital de Australia es Canberra.
print(resultado["modelo"]) # megabrain-4
print(resultado["tokens_totales"]) # 27
# Pruebas de robustez: nada de esto debe romper el programa
print(extraer_respuesta('{"modelo": "megabrain-4"}'))
# {"texto": "", "modelo": "megabrain-4", "tokens_totales": 0}
print(extraer_respuesta("esto no es json"))
# {"texto": "", "modelo": "desconocido", "tokens_totales": 0}
Ejercicio 23 — Clase Documento con métodos¶
Dificultad: · Capítulo relacionado: Cap. 4 (Python para IA)
Contexto: Estás construyendo la base de un futuro sistema RAG y necesitas una estructura para representar documentos con comportamiento propio, no solo datos sueltos.
Tu tarea: Implementa una clase Documento con:
1. Constructor que reciba titulo: str, contenido: str y opcionalmente fuente: str = "desconocida".
2. Método num_palabras() -> int.
3. Método resumen(n: int = 10) -> str que devuelva las primeras n palabras seguidas de "...".
4. Método contiene(termino: str) -> bool que busque sin distinguir mayúsculas/minúsculas.
5. Método especial __repr__ para que al imprimir el objeto se vea algo útil.
6. Crea dos documentos y demuestra todos los métodos.
Ver solución
class Documento:
"""Representa un documento de texto para un pipeline de IA."""
def __init__(self, titulo: str, contenido: str, fuente: str = "desconocida"):
# __init__ es el constructor: se ejecuta al crear cada objeto.
# self es el objeto concreto que se está creando; todo lo que
# guardemos en self.* será un atributo de ESE objeto.
self.titulo = titulo
self.contenido = contenido
self.fuente = fuente
def num_palabras(self) -> int:
"""Número de palabras del contenido."""
# .split() sin argumentos parte por CUALQUIER espacio en blanco
# (espacios, saltos de línea, tabuladores) y descarta los vacíos
return len(self.contenido.split())
def resumen(self, n: int = 10) -> str:
"""Las primeras n palabras, con puntos suspensivos si hay más."""
palabras = self.contenido.split()
if len(palabras) <= n: # Documento corto: no hace falta truncar
return self.contenido
return " ".join(palabras[:n]) + "..." # Rebanada [:n] + reconstrucción con join
def contiene(self, termino: str) -> bool:
"""¿Aparece el término en el contenido? (ignorando mayúsculas)."""
# Bajamos AMBOS lados a minúsculas: así "Python" encuentra "python" y viceversa
return termino.lower() in self.contenido.lower()
def __repr__(self) -> str:
# __repr__ define qué se muestra al hacer print(objeto) o verlo en consola.
# Sin él, verías algo inútil como <__main__.Documento object at 0x7f3a...>
return f"Documento(titulo='{self.titulo}', palabras={self.num_palabras()}, fuente='{self.fuente}')"
# --- Demostración ---
doc1 = Documento(
titulo="Política de devoluciones",
contenido="Los clientes disponen de treinta dias naturales para devolver cualquier producto sin justificacion y con reembolso completo del importe abonado",
fuente="intranet",
)
doc2 = Documento("Nota rápida", "Revisar el contrato con el proveedor de GPUs")
print(doc1) # Documento(titulo='Política de devoluciones', palabras=21, fuente='intranet')
print(doc2) # Documento(titulo='Nota rápida', palabras=8, fuente='desconocida')
print(doc1.num_palabras()) # 21
print(doc1.resumen(5)) # Los clientes disponen de treinta...
print(doc1.contiene("REEMBOLSO")) # True ← insensible a mayúsculas
print(doc1.contiene("garantía")) # False
print(doc2.resumen()) # Revisar el contrato con el proveedor de GPUs (corto: sin truncar)
Ejercicio 24 — Manejo de excepciones con datos sucios¶
Dificultad: · Capítulo relacionado: Cap. 4 (Python para IA)
Contexto: Recibes lecturas de gasto mensual de clientes exportadas de un sistema antiguo. Vienen como strings y están sucias: números válidos mezclados con basura. En datos reales, esto es la norma, no la excepción.
Tu tarea: Escribe una función limpiar_lecturas(lecturas) -> tuple[list[float], list] que:
1. Convierta a float todo lo convertible.
2. Descarte lo no convertible SIN romper el programa, acumulándolo en una lista de rechazos.
3. Rechace también los números negativos (un gasto negativo es un error de origen).
4. Devuelva (valores_validos, rechazados) e imprima un informe: cuántos entraron, cuántos se rechazaron y el total.
Ver solución
def limpiar_lecturas(lecturas: list) -> tuple[list[float], list]:
"""Convierte lecturas sucias a floats válidos; devuelve (válidos, rechazados)."""
validos: list[float] = []
rechazados: list = []
for lectura in lecturas:
try:
# float() es quien "sabe" qué es convertible; no intentamos adivinarlo
# nosotros con ifs. Filosofía Python: EAFP — "es más fácil pedir perdón
# que pedir permiso": intenta la conversión y captura el fallo.
valor = float(lectura)
except (ValueError, TypeError):
# ValueError: strings no numéricos ("N/A", "", "error", "1.200,00")
# TypeError: tipos no convertibles (None)
# Capturamos SOLO estas dos: cualquier otro error (un bug real)
# debe seguir explotando para que lo veamos.
rechazados.append(lectura)
continue # Pasamos a la siguiente lectura
if valor < 0:
# Convertible pero inválido por regla de negocio: también se rechaza.
# Nótese que esto NO es una excepción: es una validación normal con if.
rechazados.append(lectura)
continue
validos.append(valor)
# Informe final
print(f"Lecturas recibidas: {len(lecturas)}")
print(f"Válidas: {len(validos)}")
print(f"Rechazadas: {len(rechazados)} → {rechazados}")
print(f"Gasto total: {sum(validos):.2f} €")
return validos, rechazados
lecturas = ["1250.50", "890", "N/A", "2100.75", "", "error", "3400", "-50", "1.200,00", None]
validos, rechazados = limpiar_lecturas(lecturas)
Ejercicio 25 — NumPy: matrices y vectorización vs bucle (benchmark)¶
Dificultad: · Capítulo relacionado: Cap. 4 (Python para IA)
Contexto: Te han dicho mil veces que "NumPy es más rápido que los bucles de Python", pero un profesional no repite eslóganes: los mide.
Tu tarea:
1. Crea una matriz 3×4 con los números del 1 al 12 (reshape) y muestra: su forma, su transpuesta, la suma por filas y por columnas.
2. Crea dos vectores aleatorios de 1 000 000 de elementos y calcula su producto punto de dos maneras: (a) bucle Python puro con zip, (b) np.dot.
3. Cronometra ambas con time.perf_counter() y calcula cuántas veces más rápida es la versión vectorizada.
4. Explica POR QUÉ NumPy es más rápido (la explicación importa más que el número).
Ver solución
**1. La matriz y sus operaciones básicas:**import numpy as np
M = np.arange(1, 13).reshape(3, 4) # arange genera [1..12]; reshape lo reorganiza en 3 filas × 4 columnas
print(M)
# [[ 1 2 3 4]
# [ 5 6 7 8]
# [ 9 10 11 12]]
print(M.shape) # (3, 4) ← (filas, columnas): SIEMPRE en ese orden
print(M.T) # Transpuesta: filas ↔ columnas, ahora es 4×3
print(M.sum(axis=1)) # [10 26 42] ← axis=1: colapsa las columnas → suma de cada FILA
print(M.sum(axis=0)) # [15 18 21 24] ← axis=0: colapsa las filas → suma de cada COLUMNA
import time
n = 1_000_000
rng = np.random.default_rng(seed=42) # Generador aleatorio con semilla: resultados reproducibles
a = rng.random(n) # 1 millón de floats en [0, 1)
b = rng.random(n)
# --- Versión A: bucle Python puro ---
inicio = time.perf_counter() # perf_counter: el reloj de mayor precisión para medir código
total = 0.0
for x, y in zip(a, b): # zip empareja elemento a elemento
total += x * y # multiplicar y acumular... un millón de veces
tiempo_bucle = time.perf_counter() - inicio
# --- Versión B: NumPy vectorizado ---
inicio = time.perf_counter()
total_np = np.dot(a, b) # UNA línea: toda la operación baja a código C compilado
tiempo_numpy = time.perf_counter() - inicio
print(f"Bucle Python: {tiempo_bucle:.4f} s")
print(f"NumPy dot: {tiempo_numpy:.4f} s")
print(f"Aceleración: {tiempo_bucle / tiempo_numpy:.0f}x")
print(f"¿Mismo resultado? {np.isclose(total, total_np)}") # True
Ejercicio 26 — Pandas: análisis de ventas con CSV inline¶
Dificultad: · Capítulo relacionado: Cap. 4 (Python para IA)
Contexto: El equipo de dirección te pasa las ventas del último trimestre y tres preguntas de negocio. Para que el ejercicio sea autocontenido, el CSV va incrustado en el código con io.StringIO (que hace que un string se comporte como un archivo).
Preguntas de negocio: 1. ¿Qué categoría genera más ingresos totales? 2. ¿Cuál es el ticket medio (importe medio por venta) de cada canal (web/tienda)? 3. ¿Cuántas ventas superiores a 500 € hubo, y qué porcentaje del total de ingresos representan?
Tu tarea: Carga los datos con Pandas y responde las tres preguntas con código, mostrando los resultados con print.
Ver solución
import io
import pandas as pd
# io.StringIO envuelve el string para que pd.read_csv lo lea como si fuera un archivo.
# En un proyecto real harías: pd.read_csv("ventas_q4.csv")
datos_csv = io.StringIO("""fecha,categoria,canal,importe
2026-04-03,electronica,web,899.99
2026-04-05,hogar,tienda,120.50
2026-04-08,electronica,tienda,1450.00
2026-04-12,ropa,web,89.90
2026-04-15,hogar,web,340.00
2026-04-20,electronica,web,650.75
2026-05-02,ropa,tienda,210.00
2026-05-06,hogar,web,95.25
2026-05-11,electronica,tienda,2100.00
2026-05-18,ropa,web,150.60
2026-05-23,hogar,tienda,480.00
2026-06-01,electronica,web,720.30
2026-06-07,ropa,tienda,65.99
2026-06-14,hogar,web,890.00
2026-06-21,electronica,tienda,330.45
2026-06-28,ropa,web,540.20
""")
df = pd.read_csv(datos_csv) # Cargamos el CSV en un DataFrame
# Exploración mínima obligatoria antes de responder nada:
print(df.shape) # (16, 4): 16 ventas, 4 columnas
print(df.dtypes) # importe es float64: correcto; fecha es object (string): nos vale hoy
print(df.head(3)) # Primeras filas: verificar visualmente que cargó bien
# --- Pregunta 1: categoría con más ingresos totales ---
ingresos_por_categoria = df.groupby("categoria")["importe"].sum()
# groupby("categoria"): agrupa las filas por valor de esa columna
# ["importe"].sum(): dentro de cada grupo, suma los importes
print(ingresos_por_categoria.sort_values(ascending=False))
# electronica 6151.49
# hogar 1925.75
# ropa 1056.69
print(f"Líder: {ingresos_por_categoria.idxmax()}") # idxmax: el ÍNDICE del valor máximo → 'electronica'
# --- Pregunta 2: ticket medio por canal ---
ticket_medio = df.groupby("canal")["importe"].mean()
print(ticket_medio.round(2))
# tienda 679.56
# web 486.33
# --- Pregunta 3: ventas > 500 € y su peso en los ingresos ---
grandes = df[df["importe"] > 500] # Filtrado por máscara booleana (igual que en NumPy)
num_grandes = len(grandes)
peso = grandes["importe"].sum() / df["importe"].sum()
print(f"Ventas > 500 €: {num_grandes} de {len(df)}")
print(f"Representan el {peso:.1%} de los ingresos")
# Ventas > 500 €: 7 de 16
# Representan el 79.4% de los ingresos
SECCIÓN D — Ejercicios integradores¶
Cuatro ejercicios finales que combinan varios capítulos. Son el examen real del módulo: si los superas, estás listo para el Módulo 02.
Ejercicio 27 — Pipeline completo: limpiar datos y calcular estadísticas¶
Dificultad: · Capítulos relacionados: Caps. 3-5
Contexto: Una fintech te pasa las puntuaciones de riesgo (0-100) que su sistema antiguo asignó a solicitudes de crédito. El export está sucio y te piden un pipeline reutilizable: una función que reciba los datos crudos y devuelva datos limpios más un informe estadístico. Este patrón (ingesta → limpieza → validación → estadísticas) es el esqueleto de cualquier preparación de datos para ML.
datos_crudos = ["72", "85", "N/A", "91", "45", "", "105", "63", None, "78", "-12", "88", "56", "error", "94", "70"]
Reglas de negocio: las puntuaciones válidas son números entre 0 y 100 (inclusive). Todo lo demás se descarta con registro.
Tu tarea: Escribe un pipeline con tres funciones puras y una orquestadora:
1. convertir(datos) -> tuple[list[float], list]: convierte a float lo convertible, separa lo demás.
2. validar_rango(valores, minimo, maximo) -> tuple[list[float], list]: aplica la regla 0-100.
3. calcular_estadisticas(valores) -> dict: media, mediana, desviación estándar, mínimo, máximo y los valores normalizados min-max (escala 0-1) usando NumPy.
4. pipeline(datos_crudos) -> dict: encadena las tres y devuelve un informe completo.
Ver solución
import numpy as np
def convertir(datos: list) -> tuple[list[float], list]:
"""Etapa 1: conversión de tipos. Separa lo convertible de la basura."""
convertidos, rechazados = [], []
for dato in datos:
try:
convertidos.append(float(dato)) # Intentamos la conversión...
except (ValueError, TypeError): # ...y capturamos SOLO los fallos esperables
rechazados.append(dato) # La basura se guarda como evidencia, no se tira
return convertidos, rechazados
def validar_rango(valores: list[float], minimo: float, maximo: float) -> tuple[list[float], list]:
"""Etapa 2: reglas de negocio. Un número puede ser convertible pero inválido."""
validos = [v for v in valores if minimo <= v <= maximo] # Los que cumplen la regla
fuera_rango = [v for v in valores if not (minimo <= v <= maximo)] # Los que no (105, -12)
return validos, fuera_rango
def calcular_estadisticas(valores: list[float]) -> dict:
"""Etapa 3: estadísticas descriptivas + normalización min-max con NumPy."""
arr = np.array(valores) # A partir de aquí, todo vectorizado
minimo, maximo = arr.min(), arr.max()
# Normalización min-max: (x - min) / (max - min) → todos los valores en [0, 1].
# Es la misma transformación que aplicarás a features antes de entrenar modelos (Cap. 5).
rango = maximo - minimo
normalizados = (arr - minimo) / rango if rango > 0 else np.zeros_like(arr)
return {
"n": len(arr),
"media": round(arr.mean(), 2),
"mediana": round(float(np.median(arr)), 2),
"desviacion": round(arr.std(), 2),
"minimo": minimo,
"maximo": maximo,
"normalizados": np.round(normalizados, 3).tolist(),
}
def pipeline(datos_crudos: list) -> dict:
"""Orquestadora: encadena las etapas y produce el informe final."""
convertidos, no_convertibles = convertir(datos_crudos) # Etapa 1
validos, fuera_rango = validar_rango(convertidos, 0, 100) # Etapa 2
estadisticas = calcular_estadisticas(validos) # Etapa 3
return {
"recibidos": len(datos_crudos),
"no_convertibles": no_convertibles, # Trazabilidad: QUÉ se rechazó y POR QUÉ etapa
"fuera_de_rango": fuera_rango,
"validos": len(validos),
"estadisticas": estadisticas,
}
datos_crudos = ["72", "85", "N/A", "91", "45", "", "105", "63", None, "78", "-12", "88", "56", "error", "94", "70"]
informe = pipeline(datos_crudos)
for clave, valor in informe.items():
print(f"{clave}: {valor}")
Ejercicio 28 — Regresión lineal con descenso de gradiente desde cero¶
Dificultad: · Capítulos relacionados: Caps. 3-6
Contexto: Este es EL ejercicio del módulo: vas a implementar, sin ninguna biblioteca de ML, el ciclo completo de entrenamiento del Capítulo 6 — el mismo que, a escala gigante, entrena a GPT. Una empresa de reparto quiere predecir el tiempo de entrega (minutos) a partir de la distancia (km):
distancias = [1.0, 2.0, 3.0, 4.0, 5.0] # km
tiempos = [12.0, 19.0, 25.0, 33.0, 39.0] # minutos observados
El modelo es una recta: predicción = w · distancia + b, donde w y b son los dos parámetros a aprender. La función de pérdida es el error cuadrático medio (MSE).
Tu tarea:
1. Implementa el entrenamiento con descenso de gradiente: inicializa w = 0, b = 0, tasa de aprendizaje 0.05, 1 000 épocas. Las derivadas del MSE son:
- dw = -2/n · Σ xᵢ·(yᵢ − pred_i)
- db = -2/n · Σ (yᵢ − pred_i)
2. Imprime la pérdida cada 100 épocas para VER cómo aprende.
3. Reporta los w y b finales e interpreta su significado de negocio.
4. Predice el tiempo para una entrega de 7 km.
Ver solución
import numpy as np
# --- Los datos (convertidos a arrays para operar vectorizado) ---
X = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) # Entrada: distancia en km
y = np.array([12.0, 19.0, 25.0, 33.0, 39.0]) # Objetivo: minutos reales observados
n = len(X)
# --- Paso 0 del ciclo: inicializar los parámetros (el modelo "no sabe nada") ---
w = 0.0 # Pendiente: minutos extra por cada km adicional
b = 0.0 # Intercepto: minutos fijos independientes de la distancia
eta = 0.05 # Tasa de aprendizaje: el tamaño del paso (recuerda el Ejercicio 15)
for epoca in range(1, 1001):
# --- 1. FORWARD: el modelo hace sus predicciones con los parámetros actuales ---
pred = w * X + b # Vectorizado: predice los 5 ejemplos de golpe
# --- 2. PÉRDIDA: ¿cómo de mal lo está haciendo? ---
error = y - pred # Diferencia entre realidad y predicción
mse = (error ** 2).mean() # Error cuadrático medio: castiga más los errores grandes
# --- 3. GRADIENTES: ¿hacia dónde ajustar cada parámetro para reducir la pérdida? ---
dw = (-2 / n) * (X * error).sum() # Derivada del MSE respecto a w
db = (-2 / n) * error.sum() # Derivada del MSE respecto a b
# --- 4. ACTUALIZACIÓN: un paso en contra del gradiente (descenso) ---
w = w - eta * dw
b = b - eta * db
# --- 5. REPETIR (y observar el aprendizaje cada 100 épocas) ---
if epoca % 100 == 0:
print(f"Época {epoca:4d} | pérdida (MSE) = {mse:8.4f} | w = {w:.3f} | b = {b:.3f}")
print(f"\nModelo aprendido: tiempo ≈ {w:.2f} × distancia + {b:.2f}")
# --- Predicción sobre un caso nuevo (inferencia) ---
distancia_nueva = 7.0
prediccion = w * distancia_nueva + b
print(f"Entrega de {distancia_nueva} km → {prediccion:.1f} minutos estimados")
Época 100 | pérdida (MSE) = 0.2190 | w = 6.955 | b = 4.640
Época 200 | pérdida (MSE) = 0.1620 | w = 6.828 | b = 5.098
Época 300 | pérdida (MSE) = 0.1601 | w = 6.805 | b = 5.181
Época 400 | pérdida (MSE) = 0.1600 | w = 6.801 | b = 5.197
Época 500 | pérdida (MSE) = 0.1600 | w = 6.800 | b = 5.199
Época 600 | pérdida (MSE) = 0.1600 | w = 6.800 | b = 5.200
Época 700 | pérdida (MSE) = 0.1600 | w = 6.800 | b = 5.200
Época 800 | pérdida (MSE) = 0.1600 | w = 6.800 | b = 5.200
Época 900 | pérdida (MSE) = 0.1600 | w = 6.800 | b = 5.200
Época 1000 | pérdida (MSE) = 0.1600 | w = 6.800 | b = 5.200
Modelo aprendido: tiempo ≈ 6.80 × distancia + 5.20
Entrega de 7.0 km → 52.8 minutos estimados
Ejercicio 29 — Clasificador de sentimiento por reglas y sus límites vs ML¶
Dificultad: · Capítulos relacionados: Caps. 1, 4 y 6
Contexto: Una cadena hotelera quiere clasificar reseñas como positivas o negativas. Antes de invertir en ML, el CTO propone algo razonable: "Probemos primero lo simple: un clasificador por reglas con listas de palabras. Si funciona, nos ahorramos el proyecto de ML." Tu trabajo: construirlo BIEN, medirlo con honestidad y emitir un veredicto informado.
Datos de prueba (con etiqueta real anotada):
resenas = [
("La habitación era excelente y el personal muy amable", "positivo"),
("Servicio horrible, la habitación estaba sucia", "negativo"),
("Una estancia maravillosa, volveremos seguro", "positivo"),
("Ruido terrible toda la noche, experiencia pésima", "negativo"),
("El desayuno estaba bueno y las vistas eran bonitas", "positivo"),
("No me gustó nada, todo mal desde el principio", "negativo"),
("No está mal, aunque esperaba algo mejor", "negativo"),
("El hotel no es malo, de hecho es bastante bueno", "positivo"),
]
Tu tarea:
1. Implementa clasificar(texto) -> str con dos listas de palabras (positivas/negativas): cuenta apariciones y decide por mayoría.
2. Evalúa el clasificador sobre las 8 reseñas y calcula su acierto (accuracy).
3. Analiza los fallos: ¿qué tienen en común las reseñas que falla?
4. Escribe la recomendación final al CTO: ¿reglas o ML? ¿Y qué haría falta para la opción ML?
Ver solución
**1 y 2. Implementación y evaluación:**PALABRAS_POSITIVAS = ["excelente", "amable", "maravillosa", "bueno", "bonitas", "mejor", "gustó"]
PALABRAS_NEGATIVAS = ["horrible", "sucia", "terrible", "pésima", "mal", "malo", "ruido", "nada"]
def clasificar(texto: str) -> str:
"""Clasifica el sentimiento contando palabras positivas y negativas."""
texto = texto.lower() # Normalizamos a minúsculas
puntos_pos = sum(1 for p in PALABRAS_POSITIVAS if p in texto) # Cuántas positivas aparecen
puntos_neg = sum(1 for p in PALABRAS_NEGATIVAS if p in texto) # Cuántas negativas aparecen
# Decisión por mayoría; en caso de empate, elegimos negativo
# (política conservadora: mejor revisar de más una reseña dudosa)
return "positivo" if puntos_pos > puntos_neg else "negativo"
aciertos = 0
for texto, etiqueta_real in resenas:
prediccion = clasificar(texto)
correcto = prediccion == etiqueta_real
aciertos += correcto
marca = "✅" if correcto else "❌"
print(f"{marca} real={etiqueta_real:8s} pred={prediccion:8s} | {texto[:50]}")
print(f"\nAccuracy: {aciertos}/{len(resenas)} = {aciertos/len(resenas):.0%}")
✅ real=positivo pred=positivo | La habitación era excelente y el personal muy amab
✅ real=negativo pred=negativo | Servicio horrible, la habitación estaba sucia
✅ real=positivo pred=positivo | Una estancia maravillosa, volveremos seguro
✅ real=negativo pred=negativo | Ruido terrible toda la noche, experiencia pésima
✅ real=positivo pred=positivo | El desayuno estaba bueno y las vistas eran bonita
✅ real=negativo pred=negativo | No me gustó nada, todo mal desde el principio
✅ real=negativo pred=negativo | No está mal, aunque esperaba algo mejor
❌ real=positivo pred=negativo | El hotel no es malo, de hecho es bastante bueno
Accuracy: 7/8 = 88%
Ejercicio 30 — Caso de empresa: ¿reglas, ML clásico o LLM?¶
Dificultad: · Capítulos relacionados: Todos los del módulo
Contexto: Cierre del módulo: un caso completo de arquitectura de solución. "SegurPlus", aseguradora mediana (600 empleados), quiere automatizar su gestión de partes de accidente de coche. El proceso actual, 100% manual, por cada parte:
- Comprobar que el formulario tiene todos los campos obligatorios y que la póliza está vigente.
- Estimar el coste de la reparación a partir de datos estructurados del parte (modelo del coche, antigüedad, tipo de daño, taller de la zona). Tienen 8 años de histórico con costes finales reales.
- Redactar la carta personalizada al cliente explicando la resolución, cobertura y próximos pasos, en tono cercano pero formal.
Restricciones: presupuesto moderado, equipo de datos de 3 personas, los datos de clientes son sensibles (sector regulado), y dirección quiere resultados visibles en 6 meses.
Tu tarea: Para cada uno de los tres subprocesos, elige la tecnología adecuada — reglas programadas, ML clásico o LLM — y justifica. Después responde: ¿en qué orden implementarías las tres piezas y por qué? ¿Qué supervisión humana mantendrías?
Ver solución
**La decisión, pieza a pieza:** | Subproceso | Tecnología | Justificación | |------------|------------|---------------| | 1. Validación de formularios y pólizas | **Reglas programadas** | Lógica 100% especificable: campos obligatorios presentes, formato correcto, consulta del estado de la póliza en la base de datos. Determinista, auditable, exacta — y en un sector regulado, la auditabilidad no es negociable. Usar ML aquí sería introducir probabilidad donde se exige certeza (criterio del Cap. 1 / Ejercicio 3). | | 2. Estimación del coste de reparación | **ML clásico (regresión supervisada)** | Es una predicción numérica a partir de **datos tabulares** con un histórico etiquetado de lujo: 8 años de partes con coste final real (la etiqueta perfecta). Terreno ideal de regresión — la versión industrial del Ejercicio 28. Un LLM sería peor opción: los LLMs no destacan prediciendo números a partir de tablas, no se pueden calibrar estadísticamente igual de bien, y costarían más por predicción. Además, un modelo clásico es interpretable ("el coste sube por la antigüedad del vehículo"), lo que el regulador y los peritos agradecerán. | | 3. Redacción de cartas personalizadas | **LLM** | Generación de lenguaje natural con tono controlado: EXACTAMENTE la tarea para la que los LLMs son la tecnología correcta. Con reglas serían plantillas rígidas y robóticas; con ML clásico, imposible. La carta se genera a partir de los datos ya validados (pieza 1) y la estimación (pieza 2), con una plantilla de prompt corporativa. Por la sensibilidad de los datos: API con contrato empresarial de no-retención de datos o, si la política interna lo exige, un modelo open source desplegado en su infraestructura (el análisis del Ejercicio 4 aplica directo). | **Orden de implementación recomendado (y su porqué):** 1. **Primero las reglas (mes 1-2):** valor inmediato, riesgo casi nulo, y de paso fuerza a limpiar y estructurar el flujo de datos — cimiento para todo lo demás. Dirección ve resultados en semanas, lo que compra confianza (y presupuesto) para las fases siguientes. 2. **Después el modelo de costes (mes 2-5):** es el proyecto con más valor económico (estimaciones consistentes, detección de desviaciones) y requiere el trabajo serio de datos: limpieza del histórico (Ejercicio 27), train/test split (Cap. 5), línea base simple primero (media por tipo de daño — ¡Ejercicio 29, la lección de la línea base!), luego el modelo. 3. **Por último la carta con LLM (mes 4-6, en paralelo parcial):** depende de las salidas de 1 y 2, y es la pieza con más matices de riesgo reputacional (una carta con un error factual llega directamente a un cliente). Se beneficia de que las otras dos piezas ya le den datos fiables. **Supervisión humana que se mantiene (imprescindible en el diseño):** - **Pieza 1:** los rechazos de validación los revisa una persona antes de comunicar nada al cliente (un falso rechazo de póliza vigente es un problema serio). - **Pieza 2:** el perito revisa las estimaciones fuera de rango típico (o de baja confianza) — el modelo asiste, el perito decide en los casos raros. - **Pieza 3:** al principio, TODAS las cartas pasan revisión humana antes de enviarse; cuando la tasa de corrección baje de forma sostenida, se muestrea (p. ej. revisar el 10%). Nunca envío 100% automático de comunicaciones legales sin período de rodaje medido. **La lección final del módulo:** la pregunta profesional nunca es *"¿qué puede hacer la IA?"* sino *"¿qué necesita este problema?"*. La solución real de SegurPlus no es "un sistema de IA": son **tres tecnologías distintas bien elegidas** — reglas donde hay certeza, ML clásico donde hay historia y números, LLM donde hay lenguaje — cosidas con supervisión humana proporcional al riesgo. Si este razonamiento te resulta natural, el Módulo 01 ha cumplido su misión. **Consejo profesional:** en entrevistas de AI Engineer, los casos como este (descomponer un proceso y asignar tecnología a cada pieza) son cada vez más habituales que las preguntas de teoría. Practica el formato: descomponer → clasificar cada pieza con criterio → ordenar por valor/riesgo → definir la supervisión humana.Cierre del módulo¶
Si has llegado hasta aquí con 25 o más ejercicios superados: enhorabuena, has aprobado el Módulo 01. Ya no eres alguien "que ha leído sobre IA": sabes distinguir cuándo usarla, entiendes la matemática que la mueve, escribes el Python que la implementa y razonas como profesional ante casos reales.
Nota
antes de pasar al siguiente módulo, haz los Laboratorios del módulo si aún no los hiciste — son la práctica guiada que complementa estos ejercicios.
Advertencia
si no llegaste a 25, no avances todavía. Los módulos siguientes construyen sobre estos cimientos, y las grietas de hoy son derrumbes mañana. Repasa el capítulo de la sección donde más fallaste y reintenta en unos días: la segunda pasada siempre es más rápida.
| Anterior | Módulo | Siguiente |
|---|---|---|
| Capítulo 7: Ecosistema | 01-FUNDAMENTOS | Laboratorios |