Programación del espacio de pesos
Mejora un modelo abierto pequeño en un objetivo editando directamente sus pesos, sin optimizador. Esto sigue el Experimento 8 de
"Sparks of Weight-Space Programming" (Takehi, Teoh, Zhang, Khattab, MIT CSAIL, 2 de noviembre de 2026): los bucles de pensamiento de Qwen3.5-0.8B se rastrearon hasta una dirección residual escrita por los MLP de las capas 15 y 19, y una edición de rango 1 a 64 columnas down_proj por capa elevó MATH-500 del 7,9% al 48,1%.
Sigue la ruta rápida a continuación en orden. Cada script está incluido en scripts/ y probado. No rediseñes el flujo de trabajo ni escribas nuevas herramientas a menos que un paso falle.
Lo que la habilidad garantiza, y lo que no puede. Ningún método puede prometer que una edición ayude: algunos modelos no tienen un único hábito que desactivar. La habilidad garantiza el proceso en su lugar:
- sin trabajo perdido: los fallos se reinician con un lote más pequeño y se reanudan;
- ninguna edición llega a la prueba reservada sin pasar una verificación práctica medida;
- una verificación fallida escala a través de pasos fijos en lugar de detenerse;
- cada ejecución termina con un veredicto claro y números, y la copia se restaura si nada funciona.
Referencias, solo para detalles:
references/goals.md: otros objetivos; cómo escribir pares; calificadores; recetas.references/engines.md: qué motor en qué SO y GPU, instalación, dimensionamiento, dificultades, prueba de humo.references/evaluation.md: divisiones, protocolo de muestreo, especificidades de MMLU-Pro, estadísticas pareadas, plantilla de informe.references/method.md: las matemáticas de cada paso y la receta exacta de los autores.
Ruta rápida
Establece cuatro marcadores de posición una vez (PowerShell: $env:SKILL = "...", luego $env:SKILL):
SKILL=<carpeta de esta habilidad> BASE=<carpeta del modelo base> COPY=<carpeta del modelo base>-<objetivo> WORK=<carpeta de resultados>
Ejecuta cada trabajo de generación en segundo plano (Claude Code: la herramienta Bash run_in_background, no un & del shell) y espera su aviso de finalización en lugar de sondear. La generación domina el tiempo, así que informa al usuario del presupuesto de antemano: 1.5–3 horas en una GPU de consumo para los preajustes, mucho menos con vLLM en una GPU de centro de datos.
0. Pregunta solo lo que necesitas (una pregunta). Pregunta por el objetivo. Si es matemáticas o ciencia, usa el preajuste: no más preguntas. De lo contrario, también pregunta o ayuda a escribir:
- prompts que muestren el fallo, como líneas JSON
{"id", "prompt", "answer"?, "group"?}; - cómo calificarlos (
references/goals.mdsección 3); - para comportamientos que no sean bucles, 12+ pares coincidentes (sección 2).
Confirma "editar pesos, sin entrenamiento".
1. Verifica la máquina (2 min): python $SKILL/scripts/device.py --model $BASE --jobs 2. En una máquina nueva, también ejecuta la prueba de humo en references/engines.md sección 6.
2. Copia el modelo (1 min). Cada edición va a los archivos de esta copia; cada evaluación carga la copia.
python $SKILL/scripts/copy_model.py --base $BASE --copy $COPY
3. Divisiones (segundos).
- Preajuste de ciencia:
python $SKILL/scripts/make_splits.py --task mmlu --source <parquet de prueba MMLU-Pro> --out $WORK/splits. - Preajuste de matemáticas: lo mismo con
--task math --source <test.jsonl de MATH-500> --practice 25 --heldout 75. - Objetivo propio: divide tu archivo de prompts en
practice.jsonlyheldout.jsonlmediante una regla fija, más untest_ids.txtde aproximadamente 30 ID reservados, todos elegidos antes de ver cualquier respuesta.
Abajo, T es la tarea (mmlu, math o jsonl) y G son las opciones del calificador (ninguna para los preajustes; consulta references/goals.md en caso contrario).
4. Inicia dos ejecuciones base a la vez:
python $SKILL/scripts/run_eval.py --task $T --model $BASE --label base --data $WORK/splits/practice.jsonl --out $WORK/practice/base.jsonl --max-tokens 16384 --jobs 2
python $SKILL/scripts/run_eval.py --task $T --model $BASE --label base --data $WORK/splits/heldout.jsonl --ids-file $WORK/splits/test_ids.txt --out $WORK/test/base.jsonl --jobs 2
Usa el muestreo de la tarjeta del modelo para otros modelos (--profile, o las banderas de anulación; references/engines.md). La ejecución reservada no depende de la edición.
5. Obtén el contraste.
- Bucles (preajustes, o cualquier objetivo donde las respuestas alcancen el límite): los bucles aparecen solo cuando una respuesta alcanza el límite. Cuéntalos con
grep -c '"length"' $WORK/practice/base.jsonl; si son 15 o más, detén la ejecución de práctica. - Otros comportamientos: necesitas los pares, y la ejecución de práctica solo necesita terminar para dar la línea base.
Luego califica:
python $SKILL/scripts/grade.py --task $T $G $WORK/practice/base.jsonl --out $WORK/practice/summary.json
Pasa/no pasa. Estima el margen de mejora: la proporción de respuestas que muestran el comportamiento, multiplicada por la puntuación en respuestas que no lo muestran. Si es inferior a aproximadamente el 10%, informa al usuario que espere poco antes de continuar.
6. Lee la dirección, construye candidatos, evalúa (minutos, solo pases hacia adelante). Bucles:
python $SKILL/scripts/probe.py collect --task $T --model $BASE --data $WORK/splits/practice.jsonl --runs $WORK/practice/base.jsonl --out $WORK/probes
Pares (agrega --runs, --data y --task para ventanas limpias de efectos secundarios):
python $SKILL/scripts/probe.py collect-pairs --model $BASE --pairs pairs.jsonl --thinking on --out $WORK/probes
Luego construye los candidatos de primera ronda, el control y evalúa:
for spec in "15 19:128:6" "15 19:256:6" "15 19:64:6" "11 15 17 19 21:128:6"; do IFS=: read L K A <<< "$spec"; python $SKILL/scripts/probe.py build --probes $WORK/probes --model $BASE --layers $L --k $K --alpha $A --out "$WORK/edits/L${L// /-}_k${K}_a${A}.safetensors"; done
python $SKILL/scripts/probe.py build --probes $WORK/probes --model $BASE --layers 15 19 --k 128 --alpha 6 --control random --out $WORK/edits/random.safetensors
python $SKILL/scripts/probe.py screen --probes $WORK/probes --model $BASE --edits $WORK/edits/*.safetensors
En Windows PowerShell, ejecuta cada línea build por separado. Las capas anteriores son adecuadas para un modelo de 24 capas. Para otros, usa las capas alrededor del 60% y 80% de la profundidad, una dispersión de cinco capas y la más profunda sola.
Clasifica los candidatos por la caída en unwanted, conservando solo aquellos cuyas caídas en wanted y clean sean inferiores a un tercio de ella. El control aleatorio debe permanecer cerca de 0; si se mueve tanto como el mejor candidato, la dirección no es específica, así que ve al paso de detención.
7. Escribe el mejor candidato en la copia y verifica (1 min). Nunca evalúes una edición en memoria.
python $SKILL/scripts/apply_edit.py --base $BASE --copy $COPY --edit $WORK/edits/<candidato>.safetensors
python $SKILL/scripts/diff_edit.py --base $BASE --edited $COPY --out $WORK/edit_report.json
La diferencia debe mostrar solo los tensores down_proj previstos, changed_columns = k, una proporción de rango 1 de aproximadamente 1 y una α mediana de aproximadamente la α elegida.
8. Verificación práctica: la puerta (15–25 min, junto con la ejecución base reservada). Ejecuta la copia en cada pregunta de práctica que la base falló (corte, incorrecta o marcada), más 6 que manejó bien:
python -c "import json; g=json.load(open('$WORK/practice/summary.json'))['graded']['base']; bad=[x['id'] for x in g if x['cut'] or x['correct'] is False or x['flagged']]; ok=[x['id'] for x in g if x['id'] not in bad]; open('$WORK/check_ids.txt','w').write('\n'.join(bad+ok[::max(1,len(ok)//6)][:6])+'\n')"
python $SKILL/scripts/run_eval.py --task $T --model $COPY --label copy --data $WORK/splits/practice.jsonl --ids-file $WORK/check_ids.txt --out $WORK/practice/copy.jsonl --max-tokens 16384 --jobs 2
python $SKILL/scripts/grade.py --task $T $G $WORK/practice/base.jsonl $WORK/practice/copy.jsonl
La puerta se pasa solo si se cumplen las tres condiciones:
- corregidas ≥ max(3, 20% de los fallos base verificados),
- rotas ≤ corregidas ÷ 4,
- y, para objetivos de bucle, la proporción de corte cae al menos una cuarta parte.
Pasa: ve al paso 10. Falla: paso 9.
9. Escala en este orden; cada peldaño es una verificación práctica más.
- El siguiente candidato clasificado. Restaura la copia (
copy_model.py --base $BASE --copy $COPY --restore), aplícalo y repite los pasos 7–8. Haz esto para un máximo de dos candidatos más. - Ronda 2, cuando el comportamiento se ha movido en lugar de desaparecer. Por ejemplo, el pensamiento ahora se cierra pero el bucle se reinicia después de
response, o aparece un nuevo patrón no deseado en las respuestas de la copia. Trata la copia editada como la nueva base:- crea
COPY2concopy_model.py --base $COPY --copy $COPY2; - ejecuta
probe.py collect(ocollect-pairs) con--model $COPY --runs $WORK/practice/copy.jsonl; - construye y evalúa con
--model $COPY; - aplica con
--base $COPY --copy $COPY2; - verifica
COPY2como en el paso 8.
- crea
- Detener. Sin mejora confiable después de tres verificaciones (o después del presupuesto de tiempo que el usuario estableció). Restaura la copia e informa el veredicto "sin mejora confiable" con los números de cada candidato. Para Qwen3.5-0.8B, ofrece la edición de matemáticas publicada como comparación (redujo la proporción de corte de MMLU-Pro del 28.9% al 0.5% en los registros de los autores; consulta el final de este archivo). Úsala solo si el usuario está de acuerdo.
Nunca uses resultados reservados para elegir; solo las verificaciones prácticas deciden.
10. Prueba reservada desde la copia (30–60 min). Iníciala cuando la ejecución base reservada haya pasado sus respuestas más largas:
python $SKILL/scripts/run_eval.py --task $T --model $COPY --label copy --data $WORK/splits/heldout.jsonl --ids-file $WORK/splits/test_ids.txt --out $WORK/test/copy.jsonl --jobs 2
python $SKILL/scripts/grade.py --task $T $G $WORK/test/base.jsonl $WORK/test/copy.jsonl --out $WORK/test/summary.json
Da el veredicto del resultado pareado:
- "Mejora clara" cuando corregidas > rotas y p < 0.05 en la prueba de signos.
- "Consistente con una mejora, no probada" cuando corregidas > rotas pero p ≥ 0.05; recomienda una prueba más grande.
- "Sin mejora" cuando corregidas ≤ rotas; recomienda restaurar la copia.
11. Informe con la plantilla en references/evaluation.md:
- la puntuación reservada antes y después, y los recuentos pareados de corregidas/rotas;
- las tasas de comportamiento (corte, marcado, pensamiento cerrado);
- cada candidato con sus números de evaluación y verificación, y el control aleatorio;
- los pesos cambiados;
- la advertencia: desactivar un hábito no es una nueva habilidad.
Errores que cuestan horas antes (no repetir)
- Lote demasiado grande, o dos trabajos largos en sus picos. Un fallo de memoria mata las respuestas en curso, que son los bucles. Usa
--batch auto --jobs 2, y nunca inicies un segundo trabajo de 32k mientras el primero está en sus respuestas más largas. El supervisor reinicia las ejecuciones fallidas con la mitad del lote. - Esperar ejecuciones completas. Unos 15 bucles son suficientes.
- Ediciones en memoria para evaluación. Solo
probe.py screenaplica ediciones en memoria, y solo para lecturas forzadas por el profesor. - Confiarse solo de la evaluación. Una caída de evaluación de −0.10 en bucles rompió solo 3 de 26 bucles en generación, así que siempre ejecuta la puerta.
- Calificar una ejecución parcial, o elegir preguntas de prueba por cuáles terminaron primero.
- Detener trabajos con
pkill -f <texto>, que también mata a los observadores cuya línea de comandos contiene ese texto.
Reglas (como en el artículo)
Mantenlas y di en el informe que lo hiciste.
- Sin entrenamiento. Sin pases hacia atrás, autograd, optimizadores o ajuste iterativo de pesos a una pérdida. Ajustar α o pesos hasta que una métrica mejore cuenta como ajuste. Elegir entre la lista fija de candidatos anterior no.
- Se permiten pases hacia adelante para medición: generación, log-probabilidades forzadas por el profesor, estados ocultos y activaciones de MLP, y estadísticas de estos o de los pesos (medias, diferencias, SVD).
- Fuera de alcance:
- los embeddings, la cabeza de salida (compartida) y la norma final (
weights.pylos rechaza); - multiplicar un tensor completo por una constante;
- afilado o aplanamiento global;
- forzar el pensamiento a cerrarse temprano independientemente del problema;
- eliminar el comportamiento de seguridad: rechazos, salvaguardas, cumplimiento de solicitudes dañinas.
- los embeddings, la cabeza de salida (compartida) y la norma final (
- Solo cambian los valores de los pesos. Mantén nombres, formas, dtype, configuración, tokenizador, plantilla de chat y configuraciones de decodificación.
- Disciplina de división. Conjuntos de práctica y reservados disjuntos, fijos antes de cualquier edición. Mira los resultados reservados una vez, después de que la edición esté congelada.
- Selección revelada. El informe enumera cada candidato probado y la métrica que eligió al ganador.
Por qué cada paso es como es
- Contraste: la dirección es la diferencia media, en la salida de cada capa, entre el texto no deseado y el deseado que por lo demás está emparejado.
- Para bucles,
loops.pytoma los 1,024 tokens justo antes del inicio del bucle y 1,024 tokens dentro de él: misma pregunta, misma respuesta, casi la misma posición. El inicio es el primer punto donde el 90% de los siguientes 512 tokens repiten un 16-grama anterior. - Para otros comportamientos, los pares dan la coincidencia.
- Para bucles,
- Columnas escritoras: puntuación_j = (media no deseada − media deseada de activación de la neurona j) × (u · W_down[:, j]); toma las k mejores. La escritura estática u·W sola no es suficiente: solo 21–22 de las 64 columnas publicadas por capa están en sus 64 principales.
- La edición: W'[:, S] = W[:, S] − α u (uᵀ W[:, S]), calculado en float32 a partir del modelo que se está editando. α = 1 elimina el componente, que no hizo nada para los bucles; α = 6 lo invierte a −5c, por lo que esas neuronas escriben en contra del comportamiento.
- Evaluación: el cambio en la log-probabilidad media con la edición aplicada. El texto no deseado debería volverse menos probable; el texto deseado y limpio debería permanecer. Una dirección aleatoria con norma coincidente en las mismas columnas es el control.
- La puerta: los números de evaluación predicen la generación solo de manera aproximada (consulta los resultados a continuación), por lo que una verificación de generación corta en preguntas de práctica decide antes de la prueba reservada.
- Guardado:
weights.pyreescribe solo los fragmentos safetensors tocados de los archivos sin procesar. Los motores de inferencia reescriben o eliminan tensores al cargar, por lo que nunca guardes pesos de uno. - Protocolo: pensamiento activado, temperatura 0.6, top_p 0.95, top_k 20, penalización de presencia 1.5 solo en tokens generados, 32,768 tokens nuevos (
--profile thinking). Todos los motores coinciden con la semántica de vLLM. - Emparejamiento: los motores y los tamaños de lote cambian los números aleatorios, por lo que empareja por pregunta dentro de una comparación. Nunca compares puntuaciones medidas en diferentes preguntas o con diferentes motores.
R
...
[truncado para su traducción]