Ejercicios — Inteligencia Artificial
Mezclá teoría (entender qué hace cada algoritmo) con práctica (correrlos en Python con scikit-learn / numpy).
Cap. 1 — Búsqueda y heurística
1.1 — BFS vs DFS (básico)
Para el grafo:
A - B - D
| |
C - E - F
a) Listá el orden de visita con BFS desde A. b) Listá el orden de visita con DFS desde A.
✅ Solución
a) BFS: A, B, C, D, E, F.
b) DFS: A, B, D, E, C, F (depende del orden de los vecinos en la lista; este es uno válido).
1.2 — Heurística admisible (intermedio)
A* es óptimo si la heurística es admisible: para todo , donde es el costo real al objetivo. ¿Cuál de estas heurísticas es admisible para encontrar el camino más corto en una cuadrícula con movimientos de 1 paso a 4 vecinos?
a) = distancia Manhattan. b) = distancia Euclidiana. c) = distancia Manhattan.
✅ Solución
- a) Manhattan: admisible y consistente. Es el mínimo de pasos posibles si solo se mueve en 4 direcciones.
- b) Euclidiana: admisible (siempre Manhattan), pero subestima demasiado — guía peor que Manhattan en grillas con 4 movimientos.
- c) Manhattan: NO admisible. Sobreestima → A* puede dar caminos no óptimos.
Lección: admisibilidad garantiza optimalidad pero sobrestimar rompe la garantía.
Cap. 2 — Aprendizaje supervisado
2.1 — Train/test split (básico)
Cargá el dataset Iris de scikit-learn, partilo 80/20 con stratify, entrená un KNN () y reportá accuracy.
✅ Solución
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target,
test_size=0.20, stratify=iris.target, random_state=42
)
clf = KNeighborsClassifier(n_neighbors=5).fit(X_train, y_train)
print(clf.score(X_test, y_test)) # ~0.97 típico
stratify=y mantiene la proporción de clases. Sin él, podrías quedarte con 0 ejemplos de una clase en test.
2.2 — Identificar overfitting (intermedio)
Tu modelo da 99% de accuracy en train y 70% en test. ¿Qué está pasando? ¿Qué hacés?
✅ Solución
Diagnóstico: overfitting. El modelo memorizó train, no generaliza.
Soluciones (en orden de costo):
- Más datos (siempre la mejor opción cuando se puede).
- Modelo más simple (menos profundidad en árbol, más alto en KNN, regularización en regresión).
- Validación cruzada para detectarlo antes.
- Data augmentation (en visión/NLP).
- Regularización (L1, L2, dropout).
- Early stopping (en redes).
2.3 — Validación cruzada (avanzado)
Implementá k-fold cross-validation a mano (sin cross_val_score) sobre Iris con un árbol de decisión.
✅ Solución
import numpy as np
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import KFold
iris = load_iris()
X, y = iris.data, iris.target
kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores = []
for train_idx, test_idx in kf.split(X):
clf = DecisionTreeClassifier(random_state=42)
clf.fit(X[train_idx], y[train_idx])
scores.append(clf.score(X[test_idx], y[test_idx]))
print(f"Media: {np.mean(scores):.3f}, std: {np.std(scores):.3f}")
Reportar media ± std es más informativo que un solo número.
Cap. 3 — Redes neuronales
3.1 — Forward pass a mano (intermedio)
Una red 2-2-1 con activación sigmoide y pesos:
- , .
- , .
Calculá la salida para input .
✅ Solución
Capa oculta: .
.
.
Salida: .
3.2 — Curva de pérdida (básico)
¿Qué problema sugiere cada curva durante el entrenamiento?
a) Pérdida train baja, pérdida val baja. b) Pérdida train baja, pérdida val alta. c) Pérdida train alta, pérdida val alta. d) Pérdida train baja, val baja al inicio, después val sube.
✅ Solución
a) Modelo bueno. Generaliza bien. b) Overfitting. Memoriza train. c) Underfitting. Modelo demasiado simple (o LR mal configurado). d) Comienzo de overfitting. Activá early stopping (parar cuando val deja de bajar).
Cap. 5 — Ética y aplicaciones
5.1 — Auditoría de sesgo (intermedio)
Tu modelo de aprobación de créditos tiene 85 % accuracy global. Por subgrupo:
- Hombres jóvenes: 92 %
- Hombres mayores: 88 %
- Mujeres jóvenes: 80 %
- Mujeres mayores: 70 %
¿Hay sesgo? ¿Qué métricas adicionales pedirías para confirmarlo?
✅ Solución
Sí, evidencia clara de sesgo demográfico. Pero accuracy sola es insuficiente.
Métricas adicionales:
- Demographic parity: ¿la tasa de aprobación es similar entre grupos? Pueden tener accuracy similar pero aprobar mucho menos a un grupo.
- Equal opportunity: ¿el true positive rate (dado que merecen crédito, qué porcentaje aprueba) es similar?
- Equalized odds: TPR y FPR similares.
Decidir cuál métrica priorizar es una decisión ética + de producto, no solo técnica. Cada definición de fairness puede ser incompatible con otra (Chouldechova 2017).
Cap. 6 — LLMs y transformers
6.1 — Tokenización (básico)
Usá tiktoken (o el tokenizer del LLM que tengas a mano) para contar los tokens de:
a) "pupusa con queso y curtido" b) "Programación Orientada a Objetos en El Salvador" c) Un párrafo de 100 palabras de tu propio texto en español.
✅ Solución
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o-mini")
textos = [
"pupusa con queso y curtido",
"Programación Orientada a Objetos en El Salvador",
]
for t in textos:
print(t, "→", len(enc.encode(t)), "tokens")
Típicamente: ~1.3 tokens por palabra en español. Lenguajes con menos representación gastan más tokens. Si tu app facturara por token, pasar de inglés a español duplica el costo aproximadamente.
6.2 — Prompt engineering (intermedio)
Un usuario te pide "extraer el nombre, edad y email" de un texto libre. Diseñá:
a) Zero-shot prompt. b) Few-shot con 2 ejemplos. c) Con chain-of-thought. d) Con output estructurado (JSON).
✅ Solución
zero_shot = """Extraé nombre, edad y email del texto.
Texto: {texto}"""
few_shot = """Extraé nombre, edad y email del texto.
Ejemplo 1:
Texto: "Hola, soy Ana, tengo 25 años. Mi correo es ana@ejemplo.com"
Salida: nombre=Ana, edad=25, email=ana@ejemplo.com
Ejemplo 2:
Texto: "Bryan (32) – bryan@x.com"
Salida: nombre=Bryan, edad=32, email=bryan@x.com
Texto: {texto}"""
cot = """Extraé nombre, edad y email del texto.
Pensá paso a paso:
1. Identificá el nombre (suele estar al principio).
2. Buscá un número que represente edad (cerca de "años" o entre paréntesis).
3. Buscá patrón email (algo@algo.com).
Texto: {texto}"""
structured = """Extraé información del texto y devolvé SOLO un JSON válido.
Esquema:
{
"nombre": string,
"edad": int,
"email": string
}
Texto: {texto}"""
Tip: para producción, usá la API de structured outputs del proveedor (OpenAI/Anthropic) en lugar del prompt-only. Garantiza JSON válido.
Reto integrador
R.1 — Modelo + auditoría + RAG
Construí un sistema completo en un Jupyter notebook:
- Dataset: UCI Adult (predecir > 50k USD/año) o COMPAS (riesgo reincidencia). Ambos son los datasets clásicos de auditoría de fairness.
- Modelo base: Random Forest con cross-validation. Reportar accuracy, F1.
- Auditoría: demographic parity y equal opportunity por subgrupo (raza, género, edad). Mostrar disparidad numérica.
- Mitigación: aplicá una técnica (reweighting, threshold per group, o adversarial debiasing). Comparar antes/después.
- Explicabilidad: SHAP sobre 5 predicciones individuales — qué features pesan más.
- Card del modelo: estilo Hugging Face — datasource, intended use, limitations, fairness considerations.
- (Bonus) RAG: buscador semántico sobre la documentación del modelo (las cards de las features).
💡 Pista de arquitectura
Librerías:
pandas,scikit-learn,numpypara modelo.aif360ofairlearnpara auditoría/mitigación.shappara explicabilidad.sentence-transformers+chromadbpara RAG.
Estructura del notebook: 1 sección por punto, cada una con visualización y comentario.
✅ Criterio de evaluación
Un revisor sin background técnico debería leer tu notebook (especialmente la "card") y entender:
- Qué predice el modelo.
- Cuándo confiar.
- Cuándo NO confiar.
- Quién paga el costo si el modelo se equivoca.
Es portafolio fuerte para roles de IA aplicada y data science responsable.
Para más práctica: Kaggle competitions con kernels públicos, fast.ai cursos.