Curso / fast.ai / Lección 1
fast.ai · 1 de 14

fast.ai · Practical Deep Learning for Coders · Lección 1

Empezando con fast.ai: de cero a un modelo entrenado en minutos

Después de 12 capítulos de Hugging Face yendo de abajo hacia arriba — atención, tokenización, fine-tuning, GRPO — arranca fast.ai, el curso de Jeremy Howard que enseña exactamente al revés: primero un modelo funcionando de punta a punta, después la teoría que lo explica.

Antes de leerNinguno: es el otro punto de partida del curso. Para el recorrido completo desde acá, seguí la ruta «fundamentos primero» de la portada.

🎯 Al terminar vas a poder

  • Explicar con fundamento mecánico el motor de una red neuronal: tensores de entrada, pesos (W), no-linealidades, forward pass, función de pérdida (Loss), autograd y optimización por gradiente (SGD/Adam).
  • Desarmar la anatomía de un modelo preentrenado: qué es el Backbone (extractor de features universales), qué es la Cabeza (proyector lineal a la tarea) y qué cirugía matemática ocurre al hacer transfer learning.
  • Abrir la fuente de fine_tune y explicar por qué congela el cuerpo (freeze()) antes de descongelarlo, qué son los learning rates discriminativos, y cómo esta misma filosofía dio origen a LoRA y PEFT en LLMs.
  • Distinguir con rigor matemático Loss (función continua y derivable requerida por el optimizador) de Métrica (indicador de negocio para humanos), y entender por qué el loss de validación puede subir mientras el error rate sigue bajando.
  • Explicar la función de Train, Validation y Test set, cómo evitar la contaminación de datos (data leakage) y cómo diagnosticar el sobreajuste (overfitting).

1. Top-down: por qué este curso empieza al revés

Casi toda enseñanza técnica universitaria es bottom-up: fundamentos primero, aplicaciones después — es como aprender música memorizando teoría musical durante años antes de que te dejen tocar una canción. fast.ai apuesta por lo opuesto: mostrar una solución completa, útil, de estado del arte a un problema real en los primeros cinco minutos, y recién después ir bajando capa por capa a entender cómo está hecha esa solución por dentro. Jeremy Howard lo resume así: uno aprende mejor cuando hay un contexto y un propósito que entender intuitivamente, no manipulación de símbolos algebraicos en el vacío.

Esto no es solo una preferencia pedagógica — cambia qué se puede hacer desde el primer día. Con HF LLM Course, entender por qué la atención funciona requería antes entender tokenización, embeddings, matrices de queries/keys/values. Acá, en los primeros minutos vas a entrenar y desplegar un modelo real sin haber visto una sola derivada.

El libro es explícito sobre cómo leerlo: no se aprende deep learning leyendo, se aprende corriendo el código y rompiéndolo. Cada capítulo del libro es un notebook Jupyter (el repositorio fastai/fastbook) pensado para ejecutarse celda por celda, cambiar cosas y ver qué pasa. La recomendación concreta de Howard es leer cada capítulo dos veces: la primera de corrido sin detenerse en lo que no se entiende, la segunda experimentando con cada celda.

2. Qué es deep learning, sin rodeos: el motor mecánico y tensorial

El libro arranca con la definición de machine learning de Arthur Samuel, investigador de IBM que en 1962 formuló el cambio de paradigma frente a la informática tradicional:

  • Programación clásica: Escribís reglas explícitas (código con if/else, loops) + datos de entrada → la computadora ejecuta y produce resultados.
  • Machine Learning: Le das datos de entrada + ejemplos de resultados esperados → la máquina descubre y calibra los parámetros que mejor resuelven el problema.

El esquema original de Samuel era: inputs + weights → un modelo → resultados → una medida de performance → esa medida ajusta los weights → se repite. Es, literalmente, el training loop completo que hoy corre sobre clusters de GPUs H100 o en tu propia máquina.

El esquema de Arthur Samuel y su traducción al vocabulario actual Inputs y weights entran a un modelo que produce resultados; una medida de performance los compara con lo esperado y ajusta los weights; el ciclo se repite. En vocabulario moderno: los weights son parámetros, el programa es el modelo, los resultados son predicciones, la performance es la loss y el ajuste es SGD. Una arquitectura con parámetros concretos es un modelo; loss y metric no son lo mismo. inputs datos weights parámetros program modelo = arquitectura + parámetros results predicciones performance loss (para SGD) metric aparte, para humanos la medida ajusta los weights: hoy, gradiente + SGD (Lección 3) 1962, IBM: «mostrarle ejemplos y dejar que descubra cómo resolverlo» · es el training loop completo, medio siglo antes de las GPUs
El esquema de 1962 con los nombres de 2026 encima. Nada le falta: datos y parámetros entran a un modelo, la loss compara lo que salió con lo esperado, y esa comparación mueve los parámetros. Lo único que agregó el deep learning es cómo se calcula el ajuste: con un gradiente.

Qué ocurre físicamente dentro de una red neuronal

¿Qué es una red neuronal en términos de tensores? Es una función matemática compuesta por una secuencia de capas:

  1. Los datos entran como tensores: Una imagen se representa como una matriz tridimensional de números (canales de color, alto, ancho: [3,224,224]). Un texto en un LLM entra como una secuencia de vectores de embeddings ([N,4096]).
  2. Transformación lineal (Pesos y Sesgos): Cada capa multiplica el tensor de entrada x por una matriz de pesos W y suma un vector de sesgo (bias) b: z=Wx+b. Los valores dentro de W y b son los parámetros (los 34 millones en ResNet-34, o los 8 mil millones en Llama-3-8B).
  3. No-linealidad (Función de activación): Inmediatamente después de la multiplicación lineal, se aplica una función no lineal elemento a elemento: a=σ(z) (como ReLU: max(0,z), GELU o SwiGLU).

    🟢 Por qué la no-linealidad es obligatoria

    Si no hubiera funciones no lineales entre medio, apilar 50 capas lineales colapsaría matemáticamente en una sola: W3(W2(W1x))=(W3W2W1)x=Wtotalx. Una red profunda de 50 capas sin no-linealidades equivaldría a una simple regresión lineal de una sola capa. La no-linealidad es la bisagra que permite "doblar" el espacio geométrico y aproximar funciones arbitrariamente complejas (el Teorema de Aproximación Universal).

El ciclo de 4 tiempos del aprendizaje

Todo entrenamiento de redes neuronales, desde un clasificador chico hasta el entrenamiento de GPT-4, sigue exactamente cuatro tiempos:

  • 1. Forward Pass: Los datos viajan hacia adelante a través de las capas de la red produciendo una predicción cruda (logits).
  • 2. Función de Pérdida (Loss Function): Un número escalar derivable que cuantifica qué tan lejos estuvo la predicción del objetivo real (por ejemplo, Cross-Entropy Loss).
  • 3. Backward Pass (Autograd): Mediante la regla de la cadena del cálculo multivariable, la GPU calcula el gradiente de la pérdida respecto a cada uno de los millones de pesos: ∇WL=∂L∂W. El gradiente es un vector que apunta en la dirección en que la pérdida sube más rápido.
  • 4. Paso de Optimización (SGD / Adam): El optimizador actualiza cada peso empujándolo un pequeño paso en la dirección opuesta al gradiente: W←W-η∇WL, donde η es la tasa de aprendizaje (learning rate).

Un detalle que vale la pena fijar: el libro cita las visualizaciones de Zeiler y Fergus (2013) sobre qué aprende cada capa de una red profunda entrenada en ImageNet:

  • Capa 1: Detecta primitivas de bajo nivel: bordes orientados, contrastes blanco/negro y gradientes de color.
  • Capa 2: Combina bordes para detectar esquinas, curvas y rayas.
  • Capa 3: Combina esquinas para identificar patrones geométricos, texturas repetidas y mallas.
  • Capas 4 y 5: Ensambla partes complejas de objetos: ojos de perros, ruedas de bicicleta, patas de pájaros.

En los LLMs ocurre un fenómeno análogo: las primeras capas de atención aprenden sintaxis local, concordancia y morfología; las capas intermedias capturan relaciones temáticas y estilo; y las capas profundas resuelven razonamiento abstracto, recuperación de hechos y lógica en contexto.

📜 Arquitectura vs. Modelo: una distinción técnica fundamental

"Arquitectura" y "modelo" no son sinónimos:

  • La arquitectura es la plantilla matemática / código que define la forma de las capas (por ejemplo, "un ResNet-34" o "un Llama-3-8B"). Son operaciones vacías sin valores numéricos fijados.
  • El modelo es esa arquitectura con un conjunto concreto de pesos numéricos ya entrenados (por ejemplo, el checkpoint de Llama-3-8B-Instruct).

En código: resnet34 es la función de arquitectura; learn.model tras el entrenamiento es el modelo.

3. El ejemplo insignia: gatos vs. perros en 6 líneas

El primer modelo real del curso clasifica gatos y perros usando el Oxford-IIIT Pet Dataset (7.349 fotos, 37 razas), con una convención de etiquetado simple: si el nombre del archivo empieza con mayúscula, es gato.

from fastai.vision.all import *

path = untar_data(URLs.PETS)/'images'
def is_cat(x): return x[0].isupper()

dls = ImageDataLoaders.from_name_func(
    path, get_image_files(path), valid_pct=0.2, seed=42,
    label_func=is_cat, item_tfms=Resize(224))

learn = vision_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(1)   # error_rate final: menor a 0.02

Menos de 2% de error en una sola época. Pero en vez de mirar esto como magia enlatada, desarmemos la anatomía física y matemática de lo que ocurrió:

1. La geometría de entrada: por qué Resize(224)

Las GPUs ejecutan operaciones matemáticas a velocidades astronómicas porque procesan datos en lotes (batches) mediante multiplicación paralela de matrices (GEMM). Para que la GPU pueda apilar 64 fotos distintas en un único tensor homogéneo de dimensión [64,3,224,224] (batch, canales RGB, alto, ancho), todas las imágenes deben tener exactamente los mismos píxeles. Resize(224) no es una sugerencia estética: es un requisito geométrico indispensable para que el cómputo matricial funcione.

2. Anatomía de una red preentrenada: Backbone vs. Cabeza (Head)

Cualquier red neuronal moderna para visión o lenguaje se divide en dos piezas estructurales:

  • El Cuerpo o Backbone: Son las capas profundas de la red (en ResNet-34, 33 capas convolucionales con millones de parámetros). Su trabajo es tomar datos crudos (píxeles) y transformarlos gradualmente en un vector denso de características abstractas (un embedding latente de 512 números). El backbone no sabe qué tarea específica estás resolviendo hoy: solo sabe "ver" el mundo.
  • La Cabeza (Head): Es la última capa lineal (una simple matriz de pesos) conectada al final del backbone. Su único trabajo es proyectar ese vector latente de 512 dimensiones al número de clases que requiere tu problema. En el modelo original entrenado en ImageNet, la cabeza era una matriz de pesos de dimensión [512,1000] para predecir 1.000 categorías.

¿Qué hace vision_learner cuando lo invocás? Hace una cirugía de tensores: toma el backbone de ResNet-34 con sus pesos ya entrenados en ImageNet, arranca la cabeza original de 1.000 clases, y le acopla una cabeza nueva de dimensión [512,2] cuyos pesos están inicializados al azar con números aleatorios.

3. Qué ocurre matemáticamente durante predict()

img = PILImage.create('images/chapter1_cat_example.jpg')
learn.predict(img)
→ ('True', tensor(1), tensor([0.0011, 0.9989]))

El flujo interno en la GPU para llegar a ese resultado consta de cuatro pasos matemáticos:

  1. La imagen pasa por el backbone y sale comprimida como un vector de 512 números continuos.
  2. Ese vector de 512 se multiplica por la matriz de la cabeza nueva ([512,2]): el resultado son dos números reales sin acotar llamados logits (por ejemplo: [-2.14, 4.65]).
  3. Los logits pasan por la función Softmax: P(i)=ezi∑ezj. Esto transforma los números crudos en probabilidades positivas que suman exactamente 1,0: tensor([0.0011, 0.9989]).
  4. Se aplica argmax para seleccionar el índice del valor más alto (índice 1). Ese índice se busca en el vocabulario dls.vocab ([False, True]), devolviendo el string 'True' (es gato).

4. Cómo correr esto hoy, y cómo leer la fuente de fastai

El libro asume Paperspace Gradient o Colab de 2020. Hoy las tres opciones que funcionan sin drama:

Kaggle Notebooks

Lo que usa la edición 2022 del curso. GPU gratis por semana (cuota limitada), fastai preinstalado. Activá "Internet" en la configuración del notebook para que untar_data pueda descargar.

Google Colab

GPU gratis con límites de uso. fastai suele venir instalado pero desactualizado: corré !pip install -U fastai en la primera celda.

Local

Con PyTorch 2.x ya instalado, pip install fastai alcanza. fastai es una capa fina sobre PyTorch: no trae su propio runtime de GPU.

# Kaggle / Colab
!pip install -U fastai

# Local, en el entorno donde ya tenés PyTorch 2.x
pip install fastai
python -c "import fastai, torch; print(fastai.__version__, torch.__version__, torch.cuda.is_available())"

Transfer Learning con fundamento: qué hace fine_tune por dentro

Un hábito clave para cualquier ingeniero es no depender de cajas negras. En Jupyter, con learn.fine_tune?? podés inspeccionar el código fuente exacto de la función. Lo que vas a encontrar es revelador:

def fine_tune(self, epochs, base_lr=2e-3, freeze_epochs=1, lr_mult=100,
              pct_start=0.3, div=5.0, **kwargs):
    self.freeze()                                   # 1. Congela el cuerpo pretrained
    self.fit_one_cycle(freeze_epochs, slice(base_lr), pct_start=0.99, **kwargs)  # 2. Entrena solo la cabeza
    base_lr /= 2
    self.unfreeze()                                 # 3. Descongela todo el modelo
    self.fit_one_cycle(epochs, slice(base_lr/lr_mult, base_lr),
                       pct_start=pct_start, div=div, **kwargs)   # 4. Entrena todo, con learning rates por capa

Desarmemos la razón física de cada una de esas cuatro líneas:

  • 1. ¿Por qué self.freeze() es indispensable?
    En PyTorch, freeze() recorre todas las capas del backbone y establece param.requires_grad = False. Recordá que la cabeza nueva fue creada hace un segundo con números aleatorios. En el primer batch, el modelo predice basura y la función de pérdida (Loss) da un número gigantesco. Si no congelaras el cuerpo, ese error descomunal mandaría gradientes masivos hacia atrás que destruirían instantáneamente los pesos del backbone (fenómeno conocido como olvido catastrófico). Habrías arruinado semanas de preentrenamiento en una sola pasada. Además, al congelar, la GPU no gasta memoria VRAM ni ciclos en computar gradientes para el 99% de la red.
  • 2. Entrenar la cabeza nueva sola: Durante una época (freeze_epochs=1), solo los parámetros de la matriz [512,2] se actualizan. La cabeza aprende a mapear las features ya existentes hacia las clases gato/perro hasta alcanzar una mínima estabilidad.
  • 3. self.unfreeze(): Vuelve a activar requires_grad = True en toda la red. Ahora que la cabeza tiene pesos coherentes, ya es seguro involucrar a todo el modelo.
  • 4. Learning Rates discriminativos (slice(base_lr/100, base_lr)): No todas las capas deben aprender a la misma velocidad. Las primeras capas (que detectan bordes y colores) son útiles para cualquier imagen y casi no necesitan cambiar; por eso se les asigna una tasa de aprendizaje 100 veces menor (lrbase/100). Las capas profundas se entrenan con el learning rate completo para adaptarse a las particularidades del dataset.

🟢 El puente directo con los LLMs modernos: LoRA y PEFT

Esto que acabás de ver en fastai es exactamente la misma lógica que rige los LLMs hoy en día:

  • Preentrenar un LLM base (como Llama 3, Gemma o Qwen) cuesta millones de dólares procesando billones de tokens de texto. Es el equivalente a construir el backbone universal del lenguaje.
  • Cuando querés que ese LLM resuelva tu tarea específica (por ejemplo, extraer datos de facturas o hablar en un tono particular), no reentrenás todos los parámetros.
  • En LoRA (Low-Rank Adaptation), hacés exactamente lo mismo que el freeze() de fastai: congelás el 100% de los pesos del modelo base (requires_grad=False) para que no pierda su conocimiento, y solo inyectas y entrenas pequeñas matrices de bajo rango (A×B) añadidas a las matrices de atención (WQ,WV). Ahorrás un 75% de VRAM en optimizadores y evitás el olvido catastrófico.
Qué hace fine_tune por dentro: congelar, entrenar la cabeza, descongelar, entrenar todo Un modelo preentrenado tiene un cuerpo con pesos de ImageNet y una cabeza nueva con pesos aleatorios. fine_tune primero congela el cuerpo y entrena solo la cabeza una época con fit_one_cycle; después descongela todo y entrena las épocas pedidas con learning rates por capa: las capas tempranas cien veces más lento que la cabeza. La curva del learning rate en cada corrida sube y después baja: one-cycle. 1 · freeze() + fit_one_cycle(1) solo la cabeza nueva se mueve cuerpo ImageNet congelado cuerpo congelado cuerpo congelado cabeza al azar 2 clases lr: base la cabeza aleatoria no puede arrastrar al cuerpo bueno: todavía no lo toca 2 · unfreeze() + fit_one_cycle(epochs) todo se mueve, cada grupo con su learning rate capas tempranas base/100 medias … finales … cabeza base/2 slice(base_lr/100, base_lr): las features genéricas se perturban 100× menos que la cabeza lr en cada corrida one-cycle: sube, después baja fine_tune(1) son 2 épocas: 1 con el cuerpo congelado + 1 con todo · lo mismo, con otros nombres, que gradual unfreezing en la Lección 4
Las cuatro llamadas que esconde fine_tune. Primero se entrena la cabeza nueva sola, para que sus pesos aleatorios no destruyan lo que el cuerpo ya sabe; después se descongela todo con learning rates que crecen de las capas tempranas a la cabeza. El learning rate de cada corrida sigue la política one-cycle.

5. Terminología que hay que fijar bien: rigor matemático

Varios términos que generan confusión continua si no se entienden desde su fundamento formal:

  • Loss vs. Metric: la distinción matemática crucial
    ¿Por qué no le decimos a la GPU que optimice directamente el porcentaje de aciertos (Accuracy o error_rate)?
    Porque el Accuracy es una función escalonada no derivable: si para una imagen la probabilidad predicha de "gato" sube de 0,10 a 0,45, el modelo sigue prediciendo "perro" (la clase mayoritaria). El Accuracy no cambió nada; su derivada matemática es exactamente cero. Un gradiente de cero significa que la GPU no tiene ninguna pista de hacia dónde mover los pesos.
    El Loss (como la Cross-Entropy Loss) es una función continua, suave y derivable que castiga no solo si acertaste, sino con cuánta seguridad acertaste o te equivocaste (-logp).
    ¿Por qué el valid_loss puede subir mientras el error_rate sigue bajando? Porque el modelo está acertando más casos límite (baja el error), pero en los pocos casos donde se equivoca, lo hace con un exceso de confianza absurdo (por ejemplo, asignando 99,9% a la clase errónea). La pérdida logarítmica castiga duramente esa arrogancia matemática.
  • Train / Validation / Test: la triada anti-autoengaño
    • Training Set: El conjunto de datos que la GPU usa para calcular gradientes y actualizar pesos.
    • Validation Set: Un subconjunto apartado (típicamente 20%) que la GPU nunca usa para calcular gradientes. Sirve para evaluar si el modelo generaliza o si simplemente memorizó los ejemplos de entrenamiento (overfitting).
    • Test Set: Un tercer conjunto reservado bajo llave. ¿Por qué hace falta un Test set si ya tenemos Validation? Porque cada vez que vos, como ingeniero, ajustás un hiperparámetro (cambiás la arquitectura, el learning rate o la cantidad de épocas) porque viste que mejoraba en el Validation set, estás sobreajustando el modelo a ese Validation set con tus propias decisiones humanas. El Test set solo se evalúa una vez al final para tener una medición honesta e incorruptible.
  • Epoch: Una pasada completa por la totalidad de los datos del Training Set.
  • CNN (Convolutional Neural Network): Arquitectura especializada en explotar la localidad espacial (un píxel se relaciona principalmente con sus píxeles vecinos). resnet34 indica una red convolucional con 34 capas de profundidad.

🟢 Todo esto es la base conceptual de los LLMs

Loss de entropía cruzada (Cross-Entropy), perplejidad, overfitting, train vs. validation splits, discriminación por capas: son exactamente las mismas matemáticas que gobiernan el entrenamiento de cualquier modelo de lenguaje moderno. Entenderlas acá en un problema visual de 6 líneas te da el mapa mental exacto para interpretar los logs de entrenamiento de cualquier LLM sin marearte.

6. Los tres mitos que el curso quiere tirar abajo

El capítulo dedica una sección entera a desmentir tres creencias que, según el libro, alejan innecesariamente a gente capaz del deep learning:

MitoLo que dice el libro
"Hace falta mucha matemática avanzada"Alcanza con matemática de secundaria.
"Hacen falta muchos datos"Hay resultados récord documentados con menos de 50 ejemplos.
"Hace falta hardware carísimo"Se puede alcanzar trabajo de nivel estado-del-arte gratis o alquilando GPU por centavos la hora.

El libro cita incluso a Elon Musk diciendo que "un PhD definitivamente no es requerido" para trabajar en el campo. Vale la pena leer esto con la distancia de 2026: sigue siendo cierto que no hace falta un doctorado para usar deep learning con criterio — pero la barra para entrenar desde cero un modelo de frontera sí subió muchísimo (los LLMs de escala moderna cuestan millones de dólares de cómputo). Lo que el mito desmiente sigue siendo válido para el 95% de los problemas prácticos reales, justo los que este curso enseña a resolver.

7. Errores comunes

Casi todos los tropiezos de la primera semana con fastai son conceptuales, no de código — y por eso conviene fijarlos ahora, antes de que el pipeline se complique.

SíntomaCausa probableQué hacer
Cada vez que corrés el notebook el error_rate cambia, y no sabés si tu cambio ayudó.El split train/validation se hizo sin seed: cada corrida separa imágenes distintas, así que comparás modelos evaluados sobre conjuntos distintos.Pasá siempre seed=42 (o el que sea, pero fijo) a from_name_func / RandomSplitter. Compará solo corridas con el mismo split.
Métricas excelentes en el notebook, decepcionantes con datos nuevos.Elegiste arquitectura, épocas y LR mirando el mismo conjunto con el que reportás el resultado final. El validation set se "gastó" en tus decisiones.Reservá un test set que no mires hasta el final. En Kaggle eso ya está hecho por vos: el leaderboard privado es el test set.
Las imágenes de dls.show_batch() se ven aplastadas o estiradas.Resize(224) por defecto hace crop al centro; con method='squish' deforma la imagen. Ninguno de los dos aprovecha toda la foto.Para entrenar, RandomResizedCrop(224, min_scale=0.5) como item_tfms: recorte aleatorio distinto por época, sin distorsión, y funciona como data augmentation (lo ves en la Lección 2).
"Descargué el modelo resnet34" — pero no hay nada entrenado para tu problema.Confundir arquitectura con modelo: resnet34 es la plantilla; los pesos de ImageNet son un modelo para ImageNet; tu modelo recién existe después de fine_tune.Pensá siempre en el par (arquitectura, pesos). Lo que se guarda y se despliega es el modelo (learn.export()), no la arquitectura.
El valid_loss sube pero el error_rate baja — "¿está overfitteando o no?"Loss y métrica miden cosas distintas: el loss castiga la falta de confianza; el error_rate solo cuenta aciertos.Para decidir si el modelo mejora, mirá la métrica. Para decidir si seguir entrenando, mirá la tendencia de la métrica en validación, no solo el loss.
untar_data falla o tarda para siempre en Kaggle.El notebook no tiene internet habilitado (opción de la barra lateral, requiere verificar el teléfono en la cuenta).Activá "Internet" en Settings del notebook. En local, revisá el proxy.

8. Resumen

  1. fast.ai enseña top-down: un sistema funcional completo primero, la descomposición teórica después. Pero comprender la física de los tensores y gradientes es lo que te permite anticipar problemas y tomar decisiones de ingeniería.
  2. Deep learning es una máquina de 4 tiempos: Forward pass (multiplicaciones de matrices + no-linealidades) → Loss function (escalar derivable) → Backward pass con Autograd (gradientes vía regla de la cadena) → Optimizador (SGD/Adam moviendo pesos en dirección opuesta al gradiente).
  3. Las no-linealidades (ReLU, GELU, SwiGLU) son obligatorias: sin ellas, cualquier cantidad de capas profundas colapsaría en una sola matriz lineal.
  4. Todo modelo preentrenado se divide en Backbone (extractor universal de características) y Head (proyección lineal a la tarea). Transfer learning consiste en reemplazar la cabeza y conservar el backbone.
  5. fine_tune congela el cuerpo (freeze()) para evitar que los gradientes salvajes de la cabeza aleatoria destruyan el conocimiento previo (olvido catastrófico). Luego descongela con learning rates discriminativos. Esta misma filosofía es la que hace posible LoRA y PEFT en LLMs modernos.
  6. Arquitectura ≠ modelo; Loss (función continua que optimiza la máquina) ≠ Metric (indicador para humanos); Validation ≠ Test.

9. Ponete a prueba

Respondé antes de abrir cada respuesta. Si fallás una, volvé a la sección indicada.

1. En el esquema de Samuel, ¿qué son hoy "weight assignment", "program", "results" y "performance"?

Parámetros, modelo, predicciones y loss, respectivamente. El punto es que el loop entero (predecir → medir → calcular gradientes → ajustar pesos → repetir) ya estaba formalizado conceptualmente antes de que existiera el término deep learning. (§2)

2. ¿Por qué una red neuronal profunda no puede prescindir de funciones de activación no lineales?

Porque el producto de matrices lineales es siempre otra matriz lineal: W2(W1x)=(W2W1)x=Wtotalx. Sin no-linealidades entre capas, una red de 100 capas colapsaría en una simple regresión lineal de una sola capa, incapaz de resolver patrones complejos. (§2)

3. learn.predict(img) devuelve ('True', tensor(1), tensor([0.0011, 0.9989])). ¿Qué es cada cosa y qué pasos matemáticos ocurrieron?

La imagen pasa por el backbone y produce un vector latente de 512 números. Se multiplica por la matriz de la cabeza [512, 2] generando dos logits. Softmax los transforma en probabilidades positivas que suman 1 (el tensor). Argmax extrae el índice del mayor (1), y se mapea con dls.vocab[1] para dar 'True'. (§3)

4. ¿Por qué fine_tune congela el backbone en la primera época en vez de entrenar todo el modelo de entrada?

Porque la nueva cabeza fue inicializada con valores aleatorios. En el primer paso, su error es gigante: si se calcularan gradientes hacia todo el modelo, esos gradientes masivos y caóticos destruirían las características preentrenadas del backbone (olvido catastrófico). Al congelar con requires_grad=False, el backbone queda intacto y solo se calibra la cabeza. Es exactamente el mismo principio por el que LoRA congela el modelo base en LLMs. (§4)

5. ¿Por qué el optimizador no puede usar directamente Accuracy o error_rate como función objetivo?

Porque el Accuracy es una función escalonada no continua: pequeños cambios en los pesos no alteran el porcentaje de aciertos si ninguna predicción cruza el umbral del 50%, por lo que su derivada (gradiente) es cero casi en todos lados. El optimizador necesita una Loss continua y suave (como Cross-Entropy) donde cada milésima de probabilidad mueva el gradiente. (§5)

6. El valid_loss sube dos épocas seguidas pero el error_rate sigue bajando. ¿Qué está pasando matemáticamente?

El modelo acierta más casos límite (baja el error_rate), pero en los pocos ejemplos en los que se equivoca, lo hace con una seguridad extrema (probabilidad predicha cercana al 100% para la clase errónea). La penalización logarítmica de la Cross-Entropy castiga con dureza esa sobreconfianza errónea, haciendo subir la pérdida promedio. (§5)

7. ¿Por qué hace falta un test set independiente si ya usamos un validation set para controlar el overfitting?

Porque cada decisión que tomás como ingeniero mirando el validation set (elegir hiperparámetros, cambiar el learning rate, podar capas) utiliza tu propio cerebro para optimizar sobre ese conjunto. Con el tiempo, el modelo termina indirectamente sobreajustado a las peculiaridades de ese validation set (fuga de información / data leakage). El test set garantiza una medición final no contaminada. (§5)

10. Para practicar

✏️ Ejercicio 1 — Correr el clasificador y leer predict

En Kaggle o Colab con GPU (en CPU también corre, pero tarda mucho más — bajá a resnet18 si lo hacés local sin GPU), entrená el modelo de §3 y probá tres imágenes: un gato, un perro y algo que no sea ninguno de los dos (una taza, un auto). Observá que para la taza el modelo igual devuelve una clase con alguna probabilidad — no tiene forma de decir "ninguna".

from fastai.vision.all import *
path = untar_data(URLs.PETS)/'images'
def is_cat(x): return x[0].isupper()
dls = ImageDataLoaders.from_name_func(path, get_image_files(path), valid_pct=0.2, seed=42,
                                      label_func=is_cat, item_tfms=Resize(224))
learn = vision_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(1)

print(dls.vocab)
for f in ['gato.jpg', 'perro.jpg', 'taza.jpg']:
    cls, idx, probs = learn.predict(PILImage.create(f))
    print(f, cls, idx.item(), probs.tolist())
Pista / qué esperar

Gato y perro con probabilidad alta (> 0.95) en la clase correcta. La taza va a caer en una de las dos, a veces con probabilidad alta — es el problema de "out-of-domain data" que abre la Lección 2.

✏️ Ejercicio 2 — Reescribir fine_tune a mano

Con el mismo dls del ejercicio anterior, creá un learner nuevo y reproducí lo que hace fine_tune(1) con las cuatro llamadas de §4. Compará el error_rate final con el del ejercicio 1 (mismo seed, así que mismo split).

learn2 = vision_learner(dls, resnet34, metrics=error_rate)
learn2.fine_tune??          # leé la fuente primero

learn2.freeze()
learn2.fit_one_cycle(1, slice(2e-3), pct_start=0.99)
learn2.unfreeze()
learn2.fit_one_cycle(1, slice(1e-3/100, 1e-3), pct_start=0.3, div=5.0)
Pista / qué esperar

Error_rate del mismo orden que con fine_tune(1) (no idéntico: la inicialización de la cabeza y el orden de los batches son aleatorios). Si querés igualdad exacta, llamá set_seed(42, reproducible=True) antes de crear cada learner.

✏️ Ejercicio 3 — Ver qué hace cada Resize

Sin entrenar nada, armá tres DataLoaders con distintos item_tfms y mirá un batch de cada uno. Esto corre bien en CPU.

for tfm in [Resize(224), Resize(224, method='squish'), RandomResizedCrop(224, min_scale=0.5)]:
    d = ImageDataLoaders.from_name_func(path, get_image_files(path), valid_pct=0.2, seed=42,
                                        label_func=is_cat, item_tfms=tfm, bs=8)
    d.show_batch(max_n=8, nrows=1)
Pista / qué esperar

El primero recorta el centro (se pierden bordes), el segundo deforma proporciones, el tercero muestra recortes distintos cada vez que lo llamás — y ese es el que querés para entrenar.

11. Fuentes y para seguir