Tendencias del momento
#
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 el repositorio /karpathy/autoresearch línea por línea.
el ángulo de "agentes de IA haciendo investigación" es lo que está recibiendo toda la atención, pero creo que lo más interesante es lo que hay realmente dentro del script de entrenamiento y las decisiones de ingeniería que hacen que el bucle de búsqueda sea eficiente. es una de las configuraciones de entrenamiento en un solo archivo más densas que he leído.
déjame comenzar con lo que hace posible todo el proyecto: el presupuesto de tiempo está fijado en 300 segundos de reloj. no pasos fijos, no tokens fijos, no flops fijos. segundos de reloj. esto suena como un detalle menor, pero es la razón completa por la que el bucle autónomo funciona. el agente puede hacer que el modelo sea 3 veces más grande, reducir a la mitad el tamaño del lote, intercambiar por una arquitectura completamente diferente, y el resultado sigue siendo directamente comparable a cada otro experimento porque todos obtuvieron exactamente 5 minutos de entrenamiento en la misma gpu. si fijaras los pasos en su lugar, un modelo más grande recibiría menos actualizaciones de gradiente por segundo y lo estarías penalizando injustamente. si fijaras los tokens, tendrías el mismo problema. fijar el tiempo de pared significa que estás haciendo la pregunta correcta: dado este hardware y este 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 del tamaño del modelo frente al rendimiento frente a la velocidad de convergencia sin que ninguno de esos compromisos se confunda con el protocolo de evaluación.
la métrica también está cuidadosamente elegida. 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 32k tokens y un modelo con 8k tokens tendrán valores de pérdida muy diferentes incluso si comprimen los datos igualmente bien. bpb normaliza esto sumando la entropía cruzada por token en nats, sumando las longitudes de bytes utf-8 de los tokens objetivo, y convirtiendo nats por byte a bits por byte. así que incluso si el agente cambia algo que afecta la distribución efectiva de tokens, la comparación sigue siendo justa. estas dos elecciones, tiempo de pared fijo y una métrica invariante al vocabulario, convierten lo que sería una búsqueda desordenada e incomparable en un problema de optimización limpio.
ahora el modelo en sí. es un GPT pero con un montón de trucos modernos que vale la pena entender. primero, RMSnorm en todas partes. en las entradas del bloque (pre-norm), y también en consultas y claves justo antes del producto punto de atención. esta cosa de QK-norm es importante porque sin ella las normas de q y k pueden crecer de manera ilimitada durante el entrenamiento, causando que los logits de atención se agudicen y softmax se sature. normalizar q y k mantiene los productos punto en un rango estable sin importar cuán profunda sea la red o cómo evolucionen las dinámicas de entrenamiento. la atención en sí es FA 3, cargada a través de la biblioteca de kernels. utiliza la implementación de varunneal en hopper (sm_90) y recurre a una construcción comunitaria en gpus más antiguas. el patrón de atención es "SSSL" lo que significa tres capas de atención de ventana deslizante (ventana = la mitad de la longitud de la secuencia) seguidas de una capa de atención causal completa, repitiendo. este es el patrón escaso a denso que ves en mistral y gemma2.
las capas de atención local son computacionalmente baratas porque la matriz de atención es 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 capas 0,1,2 locales, capa 3 global, capas 4,5,6 locales, capa 7 global. la última capa es forzada a ser global sin importar el patrón.
la cosa de la incrustación de valores es sutil y creo que subestimada. cada otra capa obtiene su propia tabla de incrustación, completamente separada de la incrustación de tokens principal, que mapea los ids de tokens directamente a vectores de dimensión de valor. estos se mezclan en los valores de atención a través de una puerta aprendida: v = v + 2 * sigmoid(W_gate @ x:32) * ve. el peso de la puerta se inicializa en cero, así que sigmoid(0) = 0.5, multiplicado por 2 da 1.0, que es un punto de partida neutral. durante el entrenamiento, el modelo puede aprender a amplificar o suprimir la incrustación de valor por cabeza en función de las primeras 32 dimensiones del estado oculto. esto proviene de la línea de trabajo de ResFormer y la intuición es que le da a la atención un atajo directo a la identidad del token. los vectores de valor pueden llevar información sobre "qué token está en esta posición" sin que esa información tenga que sobrevivir a las transformaciones de la corriente residual de capas anteriores. es esencialmente una conexión de salto desde la entrada directamente a los valores de atención, controlada para que el modelo pueda decidir cuándo es útil.
también hay 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 manera independiente cuánto escucha la corriente residual en ejecución frente a la entrada original. las lambdas residuales comienzan en 1.0, las lambdas x0 comienzan en 0.1. esta es una versión suave de la idea de "residual disentangled". en un transformador estándar, la corriente residual es una suma de todas las salidas de capas anteriores y se contamina cada vez más a medida que profundizas. 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 suavemente limitados a 15 a través de tanh(logits/15)*15, lo que evita que el modelo esté demasiado seguro al principio del entrenamiento cuando las representaciones aún son ruidosas.
pero honestamente, 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 (incrustación de tokens, incrustaciones de valor, cabeza de desincrustación) y los escalares por capa obtienen AdamW estándar con diferentes tasas de aprendizaje para cada grupo. la diferencia es salvaje. la tasa de aprendizaje de incrustación es 0.6, la tasa de aprendizaje de desincrustación es 0.004, eso es una diferencia de 150x, y es intencional. la matriz de incrustación ve cada token y necesita actualizarse agresivamente. la matriz de desincrustación es una sonda lineal en la representación final y se beneficia de la estabilidad. las tasas de aprendizaje de incrustación, incrustación de valor y desincrustación se escalan 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 las dinámicas de aprendizaje de características invariables en escala. las tasas de aprendizaje escalares para las lambdas por capa se manejan por separado y no reciben este escalado.
las matrices de peso 2D en el transformador, proyecciones de atención y pesos de mlp, obtienen Muon, y aquí es donde se vuelve genuinamente interesante. muon toma el gradiente, aplica momento de nesterov, luego ejecuta una iteración de newton-schulz para aproximar la descomposición polar de la matriz de gradiente. la descomposición polar factoriza una matriz G en G = U * S donde U es ortogonal y S es simétrico positivo semidefinido. muon calcula U, la matriz ortogonal más cercana al gradiente, y usa eso como la dirección de actualización. la iteración de newton-schulz es 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 luego X -> aX + (bA + cA^2) @ X. los coeficientes están codificados de una precomputación. lo llaman "polar express." todo se compila en un solo kernel fusionado a través de torch.compile.
¿por qué importa esto? porque para las matrices de peso, el gradiente de norma de frobenius (lo que adam y sgd usan) es geométricamente incorrecto. la dirección de descenso más empinada "correcta" para una matriz de peso 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 hace actualizaciones efectivas mucho más grandes porque no está desperdiciando tamaño de paso en escalar los valores singulares. solo los rota. por eso muon converge significativamente más rápido que adam en matrices de peso de transformador. muon mantiene búferes de momento por elemento (la misma forma que los parámetros, apilados a través de cada grupo de forma), pero a diferencia de adam, no rastrea momentos segundos por elemento. las estimaciones del segundo momento son por fila o por columna después de la ortogonalización, no por elemento. ahí es donde entra NorMuon.
encima de la base muon hay NorMuon, un esquema de reducción de varianza. después de la ortogonalización, calcula estimaciones del segundo momento por fila (o por columna dependiendo de la relación de aspecto), mantiene un promedio móvil exponencial de esos, y reescala la actualización para que cada dimensión de salida obtenga su propio tamaño de paso adaptativo. es esencialmente la idea de adaptabilidad de adam pero aplicada en el sistema de coordenadas ortogonalizado en lugar del espacio de parámetros en bruto. la descomposición de peso también es no estándar. es "cautelosa", lo que significa que solo descompone parámetros donde la dirección de actualización de muon coincide con el signo del parámetro: máscara = (g * params) >= 0. esto evita el modo de falla conocido donde la descomposición de peso empuja parámetros hacia cero en contra de los deseos de la actualización, lo que puede desestabilizar el entrenamiento.
detalle pequeño que aprecié: después del primer paso de entrenamiento, el código llama a gc.collect(), gc.freeze(), gc.disable() para apagar completamente el recolector de basura de python. el GC de python se ejecuta periódicamente y causa pausas de ~500ms. cuando tu presupuesto total es de 300 segundos y cada paso es tal vez de 300ms, una pausa aleatoria de GC te cuesta casi 2 pasos de entrenamiento. ellos desencadenan manualmente gc.collect() cada 5000 pasos como un compromiso. este es el tipo de cosa que solo aprendes al perfilar ejecuciones de entrenamiento reales y notar caídas misteriosas en el rendimiento.
los primeros 11 pasos (0 a 10) tampoco se cuentan para el presupuesto de tiempo. esa es la fase de calentamiento donde torch.compile hace su trabajo y los kernels de CUDA se JIT. sin esta exclusión, diferentes experimentos obtendrían diferentes cantidades de "entrenamiento real" dependiendo de cuánto tiempo toma la compilación para esa configuración de modelo particular. nuevamente, una elección de diseño que parece pequeña pero es crítica para hacer que los experimentos sean comparables.
ahora, ampliemos la vista. el bucle de autoresearch real es: el agente lee program.md (un archivo markdown que describe su trabajo), modifica train.py, hace commit, corre durante 5 minutos, verifica si val_bpb mejoró, mantiene o revierte, repite. program.md dice explícitamente "NUNCA TE DETENGAS." el agente se ejecuta indefinidamente hasta que el humano lo detiene. ~12 experimentos por hora, ~100 durante la noche mientras duermes.
...
Parte superior
Clasificación
Favoritos
