Temas en tendencia
#
Bonk Eco continues to show strength amid $USELESS rally
#
Pump.fun to raise $1B token sale, traders speculating on airdrop
#
Boop.Fun leading the way with a new launchpad on Solana.
Pasé unas horas revisando /karpathy/autoresearch repositorio línea por línea.
El ángulo de "agentes de IA haciendo investigación" es lo que llama toda la atención, pero creo que lo más interesante es lo que realmente hay dentro del script de entrenamiento y las decisiones de ingeniería que hacen que el bucle de búsqueda sea muy cerrado. Es uno de los sistemas de entrenamiento más densos en fila única que he leído.
Permíteme empezar por lo que hace posible todo el proyecto: el presupuesto de tiempo está fijado en 300 segundos de reloj de pared. No pasos fijos, ni tokens fijos, ni flops fijos. segundos de reloj de pared. Esto parece un detalle menor, pero es la razón principal por la que funciona el bucle autónomo. El agente puede hacer el modelo 3 veces más grande, reducir el tamaño del lote a la mitad, cambiar por una arquitectura completamente diferente, y el resultado sigue siendo directamente comparable a cualquier otro experimento porque todos recibieron exactamente 5 minutos de entrenamiento en la misma GPU. Si arreglaras los pasos en su lugar, un modelo más grande tendría menos actualizaciones de gradiente por segundo y lo penalizarías injustamente. Si arreglaras los tokens, tendrías el mismo problema. Arreglar el tiempo de pared significa que estás haciendo la pregunta correcta: con este hardware y tanto tiempo, ¿cuál es el mejor modelo que puedes producir? Todo lo demás es una variable libre. El agente puede explorar toda la superficie de Pareto entre tamaño del modelo, rendimiento y velocidad de convergencia, sin que ninguno de esos compromisos se vea complicado por el protocolo de evaluación.
La métrica también se elige cuidadosamente. Son bits por byte, no pérdida de entropía cruzada. La entropía cruzada depende del tamaño de tu vocabulario. Un modelo con tokens de 32k y otro con tokens de 8k tendrán valores de pérdida muy diferentes incluso si comprimen los datos de forma igual. BPB normaliza esto sumando la entropía cruzada por token en NATs, sumando las longitudes UTF-8 bytes de los tokens objetivo y convirtiendo NATs por byte a bits por byte. Así que, incluso si el agente cambia algo que afecta a la distribución efectiva del token, la comparación sigue siendo justa. Estas dos opciones, tiempo de pared fijo y métrica invariante al vocabulario, convierten lo que sería una búsqueda incomparable y desordenada en un problema de optimización limpia.
Ahora el modelo en sí. es un GPT pero con un montón de trucos modernos que merece la pena entender. primero, RMSnorm en todas partes. En las entradas de bloque (antes de la norma), y también en consultas y teclas justo antes del producto escalar de atención. Esto de la norma QK es importante porque sin ella las normas de Q y K pueden crecer sin límites durante el entrenamiento, haciendo que los logits de atención se aguden y el softmax se saturen. Normalizar Q y K mantiene los productos escalar en un rango estable independientemente de la profundidad de la red o de cómo evolucionen las dinámicas de entrenamiento. la atención en sí es FA 3, cargada a través de la biblioteca kernels. Utiliza la implementación de Varunneal en Hopper (sm_90) y recurre a una build comunitaria sobre GPUs antiguas. el patrón de atención es "SSSL", que significa tres capas de atención en ventana deslizante (ventana = la mitad de la longitud de la secuencia) seguidas de una capa de atención causal completa, repitiéndose. Este es el patrón de escaso a denso que ves en Mistral y Gemma2.
Las capas de atención local son computacionalmente baratas porque la matriz de atención está en bandas, y la capa global periódica permite que la información fluya a través de todo el contexto. Con 8 capas y un patrón de 4 caracteres obtienes las capas 0, 1, 2 local, capa 3 global, capas 4, 5, 6 local, capa 7 global. La última capa se fuerza a ser global independientemente del patrón.
El tema de la incrustación de valor es sutil y creo que poco valorado. Cada otra capa tiene su propia tabla de incrustación, completamente separada de la incrustación principal de tokens, que mapea los IDs de token directamente a vectores valor-dimensión. Estos se mezclan en los valores de atención a través de una puerta aprendida: v = v + 2 * sigmoide(W_gate @ x:32) * ve. El peso de la puerta es inicializado en cero, así que sigmoide(0) = 0,5, por 2 da 1,0, que es un punto de partida neutro. En exceso de entrenamiento, el modelo puede aprender a amplificar o suprimir la incrustación de valores por cabeza basándose en las primeras 32 dimensiones del estado oculto. esto es de la línea de trabajo ResFormer y la intuición es que da a la atención un atajo directo a la identidad del token. Los vectores de valor pueden transportar información sobre "qué token está en esta posición" sin que esa información tenga que sobrevivir a las transformaciones residuales de flujo de capas anteriores. Básicamente es una conexión de salto desde la entrada directamente hacia los valores de atención, bloqueada para que el modelo pueda decidir cuándo es útil.
También existen escalares aprendibles por capa en la corriente residual: x = lambda_residi * x + lambda_x0i * x0, donde x0 es la incrustación normalizada de la capa 0. Cada capa puede controlar de forma independiente cuánto escucha el residual en ejecución frente a la entrada original. los lambdas residuales comienzan en 1,0, los lambdas x0 en 0,1. Esta es una versión suave de la idea del "residual desenredado". En un transformador estándar, el flujo residual es la suma de todas las salidas de la capa anterior y se contamina cada vez más a medida que se profundiza. Dar a cada capa acceso a la incrustación original limpia significa que no tiene que aprender a "deshacer" capas anteriores para recuperar información de bajo nivel. Los logits están limitados en 15 mediante tanh(logits/15)*15, lo que evita que el modelo tenga demasiada confianza al principio del entrenamiento, cuando las representaciones aún son ruidosas.
Pero sinceramente, la parte más interesante de todo el archivo es el optimizador. MuonAdamW es un optimizador combinado que despacha diferentes reglas de actualización según el grupo de parámetros. Las incrustaciones (embedding de token, embeddings de valores, cabeza de desincrustación) y los escalares por capa reciben AdamW estándar con diferentes tasas de aprendizaje para cada grupo. La distribución es salvaje. El embedding LR es 0,6, el desincrustado LR es 0,004, eso es una diferencia de 150x, y es intencionado. La matriz de incrustación detecta cada token y necesita actualizarse de forma agresiva. La matriz de desincrustación es una sonda lineal sobre la representación final y se beneficia de la estabilidad. las tasas de aprendizaje de incrustación, incrustación de valores y desincrustación se escalan todas por (d_model / 768)^(-0,5), que es una corrección inspirada en muP. A medida que cambia el ancho del modelo, esas tasas de aprendizaje se ajustan para mantener la dinámica de aprendizaje de características invariante a escala. Las tasas de aprendizaje escalar para las lambdas por capa se gestionan por separado y no obtienen este escalado.
las matrices de pesos 2D en el transformador, las proyecciones de atención y los pesos MLP, obtienen Muon, y aquí es donde se pone realmente interesante. El muón toma el gradiente, aplica el momento de Nesterov y luego ejecuta una iteración de Newton-Schulz para aproximar la descomposición polar de la matriz del gradiente. la descomposición polar factoriza una matriz G en G = U * S donde U es ortogonal y S es simétrica positiva semidefinida. muón calcula U, la matriz ortogonal más cercana al gradiente, y usa esa como dirección de actualización. La iteración de Newton-Schulz consta de 5 pasos. para matrices altas (más filas que columnas), A = X^T @ X luego X -> aX + X @ (bA + cA^2). para matrices anchas, A = X @ X^T entonces X -> aX + (bA + cA^2) @ X. Los coeficientes se codifican de forma fija a partir de un precálculo. Lo llaman "Polar Express". Todo se compila en un único núcleo fusionado a través de Torch.compile.
¿Por qué importa esto? Porque para matrices de pesos el gradiente de normas de Frobenius (que usan Adam y SGD) es geométricamente incorrecto. La dirección de descenso "correcta" más empinada para una matriz de pesos es la que minimiza la pérdida, sujeta a la restricción de que la actualización tenga norma espectral unitaria, no norma de Frobenius unitaria. El factor polar ortogonal te da exactamente esto. En la práctica, significa que Muon realiza actualizaciones efectivas mucho mayores porque no está desperdiciando el tamaño del paso escalando los valores singulares. Solo los rota. Por eso el muón converge significativamente más rápido que el adán en matrices de peso de transformador. El muón mantiene buffers de momento por elemento (la misma forma que los parámetros, apilados en cada grupo de forma), pero a diferencia de Adam, no registra los momentos de segundo por elemento. las estimaciones del segundo momento son por fila o columna tras ortogonalización, no por elemento. ahí es donde entra NorMuon.
encima del muón base está NorMuon, un esquema de reducción de varianza. Tras la ortogonalización, calcula estimaciones de segundo momento por fila (o por columna según la relación de aspecto), mantiene una media móvil exponencial de esas y reescala la actualización para que cada dimensión de salida tenga su propio tamaño adaptativo de paso. Es esencialmente la idea de adaptividad de Adam, pero aplicada en el sistema de coordenadas ortogonalizadas en lugar del espacio de parámetros en bruto. La disminución del peso también no es estándar. es "cauteloso", lo que significa que solo decae parámetros donde la dirección de actualización del muón coincide con el signo de parámetro: máscara = (g * parámetros) >= 0. Esto evita el modo de fallo conocido donde la disminución del peso empuja los parámetros hacia cero en contra de la voluntad de la actualización, lo que puede desestabilizar el entrenamiento.
Un pequeño detalle que aprecié: tras el primer paso de entrenamiento, el código llama a gc.collect(), gc.freeze(), gc.disable() para apagar completamente el recogedor de basura de Python. El GC de Python se ejecuta periódicamente y causa bloqueos de ~500 ms. Cuando tu presupuesto total es de 300 segundos y cada paso puede ser de 300 ms, una pausa aleatoria de GC te cuesta casi 2 pasos de entrenamiento. Activan manualmente gc.collect() cada 5000 pasos como un compromiso. Este tipo de cosas solo se aprenden perfilando entrenamientos reales y notando misteriosas caídas de rendimiento en el ritmo.
Los primeros 11 pasos (del 0 al 10) tampoco se cuentan para el presupuesto de tiempo. ese es el calentamiento donde torch.compile hace su trabajo y los kernels CUDA reciben JIT. Sin esta exclusión, diferentes experimentos recibirían distintas cantidades de entrenamiento "real" dependiendo de cuánto tiempo tarde la compilación para esa configuración particular de modelo. De nuevo, una elección de diseño que parece pequeña pero es fundamental para que los experimentos sean comparables.
Ahora aleja la vista. El bucle de autoinvestigación real es: el agente lee program.md (un archivo markdown que describe su trabajo), modifica el .py de entrenamiento, hace un commit, se ejecuta durante 5 minutos, comprueba si val_bpb mejorado, mantiene o revierte, y repite. program.md dice explícitamente "NUNCA PARES." El agente corre indefinidamente hasta que el humano lo mata. ~12 experimentos por hora, ~100 durante la noche mientras duermes.
...
Populares
Ranking
Favoritas
