Capítulo 1 de 37 · básico
Qué es el machine learning
Qué cubre este capítulo
El machine learning es una sola idea con muchos disfraces: en lugar de escribir las reglas a mano, escribes cómo se vería una buena regla y dejas que la computadora busque la que mejor se ajusta a tus datos. Eso es todo. Cada capítulo después de este es una respuesta distinta a las mismas tres preguntas — qué familia de reglas estoy buscando, cómo califico una regla contra los datos, y cómo encuentro la mejor — así que vale la pena tener clara la forma de la idea antes de que aparezca cualquier maquinaria.
Vamos a construir el ejemplo honesto más pequeño que corre: un aprendiz de
cinco líneas que ajusta un solo número a un set de juguete etiquetado
minimizando una pérdida, más su gemelo no supervisado que encuentra grupos en
los mismos puntos con las etiquetas quitadas. Después scikit-learn hace ambas
cosas en una línea cada una, usando exactamente la misma interfaz .fit /
.predict que usa todo modelo de este curso. Sin redes neuronales, sin
gradientes, nada que no puedas sostener en la cabeza. El punto no es el modelo
— el modelo aquí es deliberadamente tonto. El punto es el ciclo: elige una
familia de modelos, define una pérdida, minimízala sobre los datos, y luego
verifica que sigue funcionando con datos que no ha visto. Aprende a ver ese
ciclo y ya aprendiste a leer el resto del libro.
Un poco de historia
El término es más viejo de lo que la mayoría del campo cree. En 1959 Arthur Samuel, un ingeniero de IBM, escribió un programa de damas que mejoraba jugando partidas contra sí mismo y ajustando cómo valoraba las posiciones del tablero, y en el paper que lo describe acuñó la frase "machine learning" — una computadora mejorando en una tarea a partir de la experiencia y no de un programador dictando cada movimiento. Su jugador de damas terminó venciendo a aficionados respetables, lo cual a finales de los años cincuenta era genuinamente sorprendente, porque la alternativa que todos asumían era que tendrías que codificar a mano cada regla del buen juego.
Un año antes, en 1958, Frank Rosenblatt había construido el perceptrón — una máquina que ajustaba un conjunto de pesos hasta poder separar dos clases de entrada, aprendiendo la frontera a partir de ejemplos en lugar de que alguien le dijera dónde estaba. Fue el primer algoritmo de aprendizaje que se parece a lo que hacemos hoy: features de entrada, una decisión ponderada de salida, pesos empujados por los errores que cometía. La idea se sobrevendió, chocó con lo que un solo perceptrón demostrablemente no podía hacer, y todo el enfoque quedó en silencio durante años.
Lo que regresó no fue tanto el perceptrón como su premisa. Durante los años setenta y ochenta, la forma ambiciosa de construir un sistema inteligente era el sistema experto: sentar a un ingeniero junto a un especialista y transcribir el conocimiento del especialista en miles de reglas if-then escritas a mano. Funcionó hasta que dejó de funcionar — las bases de reglas se volvieron frágiles, se contradecían entre sí, y nadie podía mantenerlas. El giro que creó el ML moderno fue rendirse a escribir las reglas y volver a la apuesta de Samuel y Rosenblatt: muéstrale a la máquina suficientes ejemplos etiquetados y deja que infiera la regla. Esa apuesta es toda la materia. Todo en este curso es una manera de hacer que rinda.
La intuición
Aquí está la diferencia entre programar y machine learning, en una línea. Programar es cuando conoces la regla y la escribes. Machine learning es cuando no conoces la regla, pero tienes ejemplos de ella en acción, y quieres que la computadora la recupere.
Digamos que quieres clasificar el correo entrante en spam y no-spam. Podrías intentar escribir las reglas — marcar cualquier cosa con "dinero gratis", cualquier cosa de un remitente desconocido, cualquier cosa con demasiados links. Nunca vas a terminar, las reglas van a pelearse entre sí, y los spammers van a rodear cada una que escribas. O puedes entregarle a un modelo diez mil correos que la gente ya clasificó, y dejar que encuentre el patrón que los separa. Cuando la regla es corta y la conoces, escríbela — no arrastres un modelo para decidir si un número es par. Cuando la regla es una maraña de diez mil excepciones difusas que nadie puede enunciar completa, aprenderla de ejemplos es lo único que escala.
Hay dos sabores de "aprenderlo de ejemplos", y la manera más limpia de ver la división es mirar los mismos puntos dos veces. A la izquierda, cada punto viene con una etiqueta que tú diste — este es clase 0, aquel es clase 1 — y el trabajo del modelo es aprender una frontera que reproduzca tus etiquetas. Eso es aprendizaje supervisado: tú entregaste la hoja de respuestas, el modelo aprende a igualarla. A la derecha, los mismos puntos llegan sin ninguna etiqueta, y el trabajo del modelo es encontrar los grupos que ya están sentados en los datos por sí solos. Eso es aprendizaje no supervisado: sin hoja de respuestas, pura estructura.
Los mismos ochenta puntos, dos preguntas distintas. A la izquierda el modelo dibuja una línea vertical — la frontera naranja — porque las etiquetas que diste parten el plano en izquierda y derecha, y esa línea reproduce tu partición. A la derecha, nadie mencionó izquierda ni derecha; el modelo encontró cuatro grupos, porque cuatro grupos es lo que los datos realmente contienen. Ninguna imagen es más correcta que la otra. Están respondiendo preguntas distintas sobre los mismos puntos, y en cuál estás depende por completo de si trajiste etiquetas.
Hay un tercer sabor que este curso deja mayormente de lado: el aprendizaje por refuerzo, donde no hay un dataset fijo en absoluto. Un agente toma acciones, recibe recompensas o castigos, y aprende una política que acumula recompensa con el tiempo — el programa de damas de Samuel que jugaba contra sí mismo fue una probadita temprana de esto. Es un planteamiento distinto con matemáticas distintas, y no es de lo que tratan los siguientes treinta y tantos capítulos, así que lo nombro y sigo adelante.
Las matemáticas
Quita el vocabulario y el aprendizaje supervisado es un solo problema de optimización. Tienes un dataset de ejemplos, cada uno un vector de features emparejado con un objetivo:
Apila los vectores de features en una matriz de forma (una fila por ejemplo, una columna por feature) y los objetivos en un vector . Eliges una familia de reglas candidatas indexada por parámetros — el modelo — y una predicción es simplemente el modelo aplicado a una entrada:
Una pérdida califica qué tan equivocada está una sola predicción. Aprender es elegir la que hace la pérdida promedio sobre todo el dataset tan pequeña como sea posible:
Esa única línea es el motor de todo el campo. Cambia y cambias el modelo — un umbral, una línea, un árbol, una red. Cambia y cambias lo que significa "equivocado" — error cuadrático, cross-entropy, hinge loss. Cambia cómo resuelves el y cambias el algoritmo de entrenamiento — fuerza bruta, descenso de gradiente, una fórmula cerrada. Elige esas tres cosas y especificaste un método de aprendizaje por completo. Eso no es una simplificación para un primer capítulo; es la estructura real, y voy a estar señalándola durante todo el libro.
Nuestro modelo concreto de este capítulo es el más simple que no es una constante: un umbral sobre un solo feature, que predice clase 1 cuando el feature lo rebasa.
Y nuestra pérdida es la más llana que existe — la pérdida 0-1, uno cuando la predicción está mal y cero cuando está bien. Su promedio sobre los datos es la tasa de clasificación errónea, un número sin unidades entre 0 y 1:
El aprendizaje no supervisado encaja en la misma plantilla con un término faltante: no hay , así que la pérdida solo puede calificar una predicción contra los datos mismos, no contra una hoja de respuestas. Para agrupar por centro más cercano, los parámetros son las ubicaciones de los centros , y cada punto paga la distancia al cuadrado hasta el centro al que quedó asignado:
La misma forma — features de entrada, una regla con parámetros, una pérdida que minimizar — solo que sin etiquetas alimentándola. Esa ausente es toda la diferencia entre los dos lados de la imagen de arriba.
Para qué sirve, para qué no
El caso honesto a favor del machine learning es estrecho, y vale la pena decirlo sin rodeos porque el campo lo sobrevende. Recurre al aprendizaje cuando la regla es real pero demasiado grande o demasiado difusa para escribirla a mano — cuando genuinamente existe un patrón que separa el spam del no-spam, o los tumores del tejido sano, pero vive en mil features que interactúan y ningún experto puede dictarlo como lógica if-then limpia. Ahí es donde un modelo se gana el sueldo: lee el patrón de los ejemplos que tú nunca terminarías de transcribir. La otra victoria real es la deriva. Las reglas codificadas a mano se pudren cuando el mundo se mueve; un modelo que puedes reentrenar con datos frescos se mantiene al día.
El caso en contra es igual de real. Si conoces la regla, escribe la regla — un modelo que decide si un número es positivo es más lento, más tonto y menos confiable que una comparación. Aprender necesita datos, y suficientes como para que los ejemplos de verdad cubran el patrón; con unas cuantas docenas de filas casi siempre te conviene más una heurística. Y un modelo aprendido es opaco de una forma en que una regla no lo es — obtienes una respuesta, no una razón, lo cual es un problema en el momento en que alguien pregunta por qué. La verdad incómoda debajo de todo esto es que el algoritmo importa menos que los datos. Un modelo mediocre con datos limpios, representativos y bien etiquetados le gana a uno ingenioso con basura todas las veces, y la mayor parte del trabajo real en este oficio son los datos, no el modelo. Guárdate eso en el bolsillo para todo el curso.
Los datos
Para hacer concreto eso de "aprender = minimizar pérdida" necesitas datos que puedas ver completos, así que es un set de juguete con semilla fija: ochenta puntos en dos dimensiones, acomodados como cuatro blobs gaussianos en una cuadrícula de dos por dos, generados con una semilla aleatoria fija para que la foto nunca se mueva. Carga dos tipos de verdad a propósito. Las etiquetas parten los puntos en izquierda y derecha por su coordenada x — cuarenta de clase 0 a la izquierda, cuarenta de clase 1 a la derecha — que es lo que el aprendiz supervisado va a intentar reproducir. Y los cuatro blobs son una segunda estructura, independiente, que el aprendiz no supervisado va a intentar encontrar sin ver jamás una etiqueta. Los blobs están lo bastante separados como para que un humano vea los cuatro grupos al instante, que es exactamente lo que quieres cuando estás verificando si un algoritmo está de acuerdo contigo. Es el mismo set dibujado dos veces en la imagen de arriba.
Constrúyelo, una función a la vez
Cinco funciones cortas, sin librería, y se apilan en el orden en que las escribirías: primero el modelo, luego la pérdida que lo califica, luego la búsqueda que minimiza la pérdida — y después la misma forma otra vez con las etiquetas quitadas.
El modelo es la regla de decisión, y es una comparación. Predice clase 1 cuando el feature rebasa el umbral:
def predict(x, theta):
"""The model f_theta: predict class 1 when feature x clears the threshold.
x broadcasts, so this is one point against one theta, or a whole column of
points against one theta, or a column of points against a row of candidate
thetas — the same three characters of code either way.
"""
return (x >= theta).astype(int)
Como esa comparación hace broadcast, los mismos tres caracteres manejan un punto contra un umbral o una columna entera de puntos contra un umbral — nos apoyamos en eso en la búsqueda. Ahora la pérdida: qué tan seguido se equivoca la regla sobre los datos etiquetados.
def loss(theta, x, y):
"""Average 0-1 loss: the fraction of labeled points the rule gets wrong.
Unitless, in [0, 1]. Zero means every prediction matched its label; 0.5 is
a coin flip. This single number is what 'learning' is going to minimize —
there is no other objective hiding anywhere.
"""
return float((predict(x, theta) != y).mean())
Este es el número que el aprendizaje minimiza, y no hay nada más. Cero significa que la regla acertó cada etiqueta; un medio es un volado. Ahora el aprendiz mismo, y esta es la parte donde vale la pena frenar, porque es la idea completa en tres líneas:
def fit(x, y, candidates):
"""Learning, in three lines: score every candidate threshold by its loss on
the data, then keep the one whose loss is smallest.
There is no formula that hands you theta. You try, you measure, you choose
what fits the data best. Returns the winning theta and the loss at every
candidate (so the chapter can plot the objective coming down).
"""
losses = np.array([loss(t, x, y) for t in candidates])
theta_star = float(candidates[int(np.argmin(losses))])
return theta_star, losses
No hay una fórmula que calcule por ti. Tomas una lista de umbrales candidatos, mides la pérdida de cada uno sobre los datos, y te quedas con el candidato cuya pérdida es la más pequeña. Probar, medir, elegir — eso es lo que significa "aprender de los datos", despojado de cualquier otra palabra. Todo lo más sofisticado de este libro es una manera más ingeniosa de hacer esta misma búsqueda cuando los candidatos son demasiados para listarlos.
Ahora el gemelo no supervisado. Sin las etiquetas, la pérdida no puede comparar contra una hoja de respuestas, así que compara los puntos entre sí: pon cada punto con el centro que le queda más cerca.
def nearest_center(points, centers):
"""Assign each point to the center it's closest to.
One (N, k) grid of squared distances, argmin down the centers axis. With no
labels to match, 'nearest center' is the only handle the algorithm has.
"""
d = ((points[:, None, :] - centers[None, :, :]) ** 2).sum(axis=2) # (N, k)
return d.argmin(axis=1)
Ese es un paso de asignación. Por sí solo necesita centros a los cuales asignar, y centros buenos es exactamente lo que no tenemos, así que alternamos: asigna los puntos al centro más cercano, luego mueve cada centro a la media de los puntos que lo eligieron, y repite hasta que nada se mueva.
def cluster(points, k, seed=0, iters=10):
"""The unsupervised twin of fit: no y, so 'learning' is discovering groups.
Start from k points chosen at random, then repeat two moves: assign every
point to its nearest center, and slide each center to the mean of the points
that chose it. The centers walk into the middle of each blob and stop.
"""
rng = np.random.default_rng(seed)
centers = points[rng.choice(len(points), k, replace=False)].astype(float)
labels = nearest_center(points, centers)
for _ in range(iters):
centers = np.array([points[labels == j].mean(axis=0) for j in range(k)])
new_labels = nearest_center(points, centers)
if np.array_equal(new_labels, labels):
break
labels = new_labels
return labels, centers
Los centros arrancan en puntos aleatorios y caminan hasta el centro de cada blob, jalados por los puntos que los siguen eligiendo. Ninguna etiqueta entra jamás a la función — encuentra los cuatro grupos solo con la geometría. El mismo esqueleto que el lado supervisado: parámetros, una noción de costo, un ciclo que empuja el costo hacia abajo. Lo único que cambió es que el costo dejó de necesitar .
Míralo trabajar
Aquí está el aprendiz supervisado corriendo, y es la única animación con la que vale la pena sentarse en este capítulo porque es el aprendizaje en su totalidad sin nada escondido. El panel de arriba son los puntos de juguete, coloreados por su etiqueta verdadera, con la línea naranja como el umbral actual. Un punto recibe un anillo rojo en el instante en que la regla de su lado de la línea está en desacuerdo con su etiqueta. El panel de abajo es la pérdida — la fracción de puntos actualmente mal clasificados — trazada mientras el umbral se desliza.
Dale play. La línea arranca muy a la izquierda, donde la regla llama clase 1 a cada punto y la mitad están mal, así que la pérdida se queda cerca de un medio. Conforme la línea barre hacia la derecha los anillos rojos se despejan, la pérdida baja por la curva hasta su mínimo, y luego, cuando la línea sigue de largo más allá del hueco, los anillos se encienden del otro lado y la pérdida vuelve a subir. Aprender es ese descenso hasta el fondo de la curva, y nada más. Reinicia y córrelo de nuevo — es igual cada vez, porque la búsqueda es determinista.
La curva que traza el panel de abajo es la pérdida como función del único parámetro que estamos ajustando, y vale la pena verla por sí sola con el ganador marcado. El mínimo aquí es un valle plano, no un solo punto — las etiquetas se separan limpiamente en el hueco entre los blobs de la izquierda y los de la derecha, así que cualquier umbral que caiga en ese hueco acierta cada punto y saca una pérdida de cero. Nuestra búsqueda toma el primero al que llega:
Que exista siquiera una regla perfecta es un lujo de los datos de juguete limpios. Los datasets reales casi nunca te entregan un umbral de pérdida cero; el valle toca fondo en algún punto arriba de cero, y donde toca fondo es el piso de errores que la mejor regla de tu familia sigue cometiendo. La forma es la lección, no el cero en particular.
La implementación completa
El archivo entero, de arriba a abajo — ambos aprendices, sin librería. Esto es exactamente lo que corrió la animación:
"""What machine learning is, reduced to code you can hold in your head.
Two tiny learners, no ML library anywhere in this file:
* a supervised one — given features X and labels y, learn a threshold rule
f_theta(x) = 1[x >= theta] by picking the theta with the smallest average
loss on the data. That last clause is the whole definition of learning.
* an unsupervised one — given the same points with the labels taken away,
find structure by grouping each point with the center it's nearest to, and
letting the centers chase the density.
Everything is pure NumPy. Each function shows up in the chapter one step at a
time; the `# region:` markers are what the book's include directives pull in.
"""
import numpy as np
# region: predict
def predict(x, theta):
"""The model f_theta: predict class 1 when feature x clears the threshold.
x broadcasts, so this is one point against one theta, or a whole column of
points against one theta, or a column of points against a row of candidate
thetas — the same three characters of code either way.
"""
return (x >= theta).astype(int)
# endregion
# region: loss
def loss(theta, x, y):
"""Average 0-1 loss: the fraction of labeled points the rule gets wrong.
Unitless, in [0, 1]. Zero means every prediction matched its label; 0.5 is
a coin flip. This single number is what 'learning' is going to minimize —
there is no other objective hiding anywhere.
"""
return float((predict(x, theta) != y).mean())
# endregion
# region: fit
def fit(x, y, candidates):
"""Learning, in three lines: score every candidate threshold by its loss on
the data, then keep the one whose loss is smallest.
There is no formula that hands you theta. You try, you measure, you choose
what fits the data best. Returns the winning theta and the loss at every
candidate (so the chapter can plot the objective coming down).
"""
losses = np.array([loss(t, x, y) for t in candidates])
theta_star = float(candidates[int(np.argmin(losses))])
return theta_star, losses
# endregion
# region: nearest_center
def nearest_center(points, centers):
"""Assign each point to the center it's closest to.
One (N, k) grid of squared distances, argmin down the centers axis. With no
labels to match, 'nearest center' is the only handle the algorithm has.
"""
d = ((points[:, None, :] - centers[None, :, :]) ** 2).sum(axis=2) # (N, k)
return d.argmin(axis=1)
# endregion
# region: cluster
def cluster(points, k, seed=0, iters=10):
"""The unsupervised twin of fit: no y, so 'learning' is discovering groups.
Start from k points chosen at random, then repeat two moves: assign every
point to its nearest center, and slide each center to the mean of the points
that chose it. The centers walk into the middle of each blob and stop.
"""
rng = np.random.default_rng(seed)
centers = points[rng.choice(len(points), k, replace=False)].astype(float)
labels = nearest_center(points, centers)
for _ in range(iters):
centers = np.array([points[labels == j].mean(axis=0) for j in range(k)])
new_labels = nearest_center(points, centers)
if np.array_equal(new_labels, labels):
break
labels = new_labels
return labels, centers
# endregion
La versión de librería
Nadie escribe la búsqueda a mano en la práctica, y una vez que la viste ya no
necesitas hacerlo. Cada modelo en scikit-learn — y en cada capítulo posterior
de este libro — viste la misma interfaz de tres movimientos: constrúyelo, llama
.fit(X, y) para aprender los parámetros, llama .predict(X) para usarlos. Un
árbol de decisión de profundidad 1 es exactamente nuestro umbral de un feature,
así que el gemelo supervisado es una línea de preparación y dos de llamadas:
def sklearn_threshold(x, y):
"""The supervised twin of our fit(): a depth-1 decision tree is exactly a
one-feature threshold rule. Construct, .fit(X, y), .predict(X).
sklearn expects X as a 2-D array (rows = samples, columns = features), so
the single feature gets reshaped to a column. Returns the learned threshold
and the predictions.
"""
model = DecisionTreeClassifier(max_depth=1, random_state=0)
model.fit(x.reshape(-1, 1), y)
theta = float(model.tree_.threshold[0])
preds = model.predict(x.reshape(-1, 1))
return model, theta, preds
La diferencia bajo el cofre es pequeña y vale la pena conocerla: nuestra
búsqueda minimiza la clasificación errónea cruda, el árbol minimiza la impureza
de Gini. Pérdida distinta, y en estos datos separables aterrizan en el mismo
hueco, así que aquí no importa — pero esa es exactamente la perilla de "cambia
la pérdida" de la sección de matemáticas apareciendo en su hábitat natural. El
gemelo no supervisado viste casi la misma interfaz, con el único cambio del que
ha tratado todo el capítulo — no hay y que pasarle a .fit, y el estimador
te devuelve grupos que descubrió en lugar de etiquetas que tú diste:
def sklearn_cluster(points, k):
"""The unsupervised twin of our cluster(): scikit-learn's KMeans. Almost the
same interface — there's no y to pass to .fit, and .fit_predict hands back
discovered cluster ids instead of known labels. Same Lloyd loop we wrote by
hand, with k-means++ seeding and restarts done for us.
"""
model = KMeans(n_clusters=k, n_init=10, random_state=0)
labels = model.fit_predict(points)
return model, labels, model.cluster_centers_
Aprende esta interfaz una vez y el resto de la librería es gratis. Un random forest, un gradient booster, una máquina de vectores de soporte — llamas a cada uno exactamente así, y esa es la razón silenciosa por la que scikit-learn ganó.
De cero contra librería
Ahora el ciclo sobre el que corre todo el curso: ajustar con una rebanada de los datos, evaluar con una rebanada que el modelo nunca vio. Partimos el set de juguete 70/30, aprendemos el umbral con los 56 puntos de entrenamiento, y medimos accuracy — la fracción de puntos reservados clasificados correctamente, un número sin unidades de 0 a 1 — sobre los 24 puntos de prueba, tanto para nuestra búsqueda como para el árbol de sklearn.
Ambos alcanzan 1.000 de accuracy de prueba — cada punto reservado del lado correcto de la línea. Que los dos aterricen en el número idéntico, desde dos umbrales distintos (nuestra búsqueda se detiene en el borde izquierdo del hueco, el árbol de sklearn en −0.18), es el punto: cualquier regla dentro del hueco es perfecta, así que lo que importa es la familia de modelos y la pérdida, no el ganador en particular. Eso sí, no le leas un 1.000 triunfal a esto. Es 1.000 porque los datos de juguete son limpiamente separables y la familia de reglas resulta contener una respuesta perfecta. Esa es la excepción, no la regla — diseñé los datos así para que la maquinaria fuera visible. Con datos reales el número queda en algún punto debajo de uno, y la brecha entre el score de entrenamiento y este score reservado es donde descubres si el modelo aprendió el patrón o solo memorizó el set de entrenamiento.
El lado no supervisado no tiene etiquetas contra las cuales medirse durante el
entrenamiento, así que lo calificamos después del hecho contra los cuatro ids
de blob que mantuvimos sellados todo el tiempo, usando el índice de Rand
ajustado — un score de acuerdo sin unidades de 0 a 1, donde 1 es una
coincidencia perfecta y 0 es el azar. Tanto nuestro clustering desde cero como
el KMeans de sklearn sacan 1.000: cada punto cayó en el grupo del que
realmente fue extraído, recuperado solo de la geometría sin una sola etiqueta.
El mismo resultado, la misma forma de ciclo, la hoja de respuestas abierta solo
al final para verificar.
Conclusiones
La única cosa que hay que llevarse de este capítulo es el ciclo, porque cada capítulo que sigue es una variación sobre él: elige una familia de modelos, define una pérdida que diga qué significa un buen ajuste, minimiza esa pérdida sobre tus datos, y luego verifica el resultado con datos que reservaste. Umbral, árbol, forest, red — el modelo cambia, la pérdida cambia, la manera de resolver la minimización cambia, pero el ciclo no. Una vez que puedes mirar cualquier método y nombrar sus tres piezas, el libro deja de ser una lista de algoritmos sin relación y se convierte en una sola idea con treinta disfraces.
La versión de ingeniero de esa idea es una regla de decisión sobre cuándo siquiera recurrir a esto. El machine learning vale la pena cuando la regla que quieres es real pero demasiado grande o demasiado difusa para escribirla a mano, y se mueve tan seguido que las reglas escritas a mano se pudrirían. Cuando la regla es corta y estable, escribe la regla — vas a entregar más rápido y dormir mejor. Y cuando sí recurras a un modelo, invierte tu tiempo en los datos antes que en el algoritmo, porque datos limpios, representativos y etiquetados con honestidad le ganan a un modelo ingenioso con datos sucios casi siempre, y lo inverso casi nunca pasa. El resto de este curso te enseña un estante de familias de modelos y pérdidas entre las cuales elegir. Este capítulo es la razón por la que cualquiera de ellas funciona: todas son solo maneras de ajustar una función a datos, y luego ser honesto sobre si generaliza.