Diario
Secciones
Ajustes de lectura
Ninguna voz para este idioma en este navegador.
Más voces
Código y términos técnicos en inglés
Desactivado: la voz de la página lo lee todo, sin pausa entre dos voces.
Ninguna voz para este idioma en este navegador.
Más voces
Más voces: Microsoft Edge ofrece voces naturales en línea, y Windows (Configuración › Hora e idioma › Voz), macOS y Android instalan otras.
Progreso
Sección titulada «Progreso»- IX clonado y fijado en el commit
490c395; el código del curso depende de siete de sus crates - Datos:
builds.csvyjobs.csv, extraídos del historial de CI y del historial de Git de este repositorio - CI: formato, clippy, pruebas unitarias y cada ejemplo comparado con
expected/en tres sistemas operativos, y una comprobación cruzada con numpy y scikit-learn en Linux - Lección 1: datos, características y evaluación
- Lección 2: regresión lineal y descenso de gradiente
- Lección 3: clasificación
- Lección 4: agrupamiento
- Lección 5: componentes principales
- Lección 6: conjuntos
- Lección 7: redes neuronales
- Lección 8: optimización
- Lección 9: cinco reductores más, con MDS escrita a mano y comprobaciones independientes
- Apéndice: el mapa de API, generado desde el commit fijado
- Lecciones 10 a 21
- Traducciones al francés y al español
IX es la biblioteca de otros, fijada en 490c395. Las lecciones anteriores registraron diecinueve hallazgos; la lección 9 añade dos hallazgos de API o documentación. La primera columna indica lo que esperaría quien llama. Ninguno se ha reportado como incidencia de IX. Varias son decisiones defendibles más que defectos, y la columna del medio dice cuáles.
| Esperado | Lo que pasa | Dónde | Medición | Estado |
|---|---|---|---|---|
| Las filas de prueba no participan en dar forma al preprocesado | Con normalize, la tubería ajusta StandardScaler y el PCA sobre todas las filas y divide después |
ml_pipeline.rs 190-203 |
La primera fila de prueba vale 1,440 frente a 1,042 si la división va primero | Reproducido, no reportado 2026-09-14 |
| Un vector de etiquetas enteras es un objetivo de regresión salvo indicación contraria | infer_task_type llama clasificación a todo vector de enteros no negativos con a lo sumo 20 valores distintos |
preprocessing.rs 234-254 |
Veinte valores distintos es toda la regla | Reproducido, no reportado 2026-09-14 |
| Un empate entre vecinos más cercanos se rompe como en scikit-learn | max_by_key devuelve el último máximo: el empate va al índice de clase mayor |
knn.rs 53 |
Exactitud 0,947 frente a 0,974 | Reproducido, no reportado 2026-09-14 |
| Un clúster k-means vacío se resiembra, como hace scikit-learn | La actualización de centroides parte de ceros y salta los clústeres sin filas: el centroide se queda en el origen | kmeans.rs 137-152 |
KMeans(3) sobre [5, 5, 9, 9] da [5.0, 9.0, 0.0] |
Reproducido, no reportado 2026-09-14 |
| La silueta de una fila sola en su clúster vale 0, como la definen Rousseeuw y scikit-learn | silhouette_score_exact pone a = 0, de donde s = 1 |
ix-voicings/lib.rs 664-678 |
0,9296 frente a 0,5963 | Reproducido, no reportado 2026-09-14 |
| Las líneas de import de los tutoriales compilan | use ix_unsupervised::{KMeans, Clusterer}; falla con E0432: la crate solo exporta módulos |
ix-unsupervised/lib.rs 5-14 |
E0432 |
Reproducido, no reportado 2026-09-14 |
KMeans::fit hace varios arranques y se queda con el mejor |
Hace un único arranque k-means++; la herramienta MCP ix_kmeans además fija la semilla en 42 |
kmeans.rs, handlers.rs 322 |
Un solo arranque | Reproducido, no reportado 2026-09-14 |
ix_voicings::cluster compara las k que prueba |
Se queda con k = 5 en cuanto su silueta llega a 0,15, y solo prueba k = 3 por debajo |
ix-voicings/lib.rs 770-776 |
Un umbral, no una comparación | Reproducido, no reportado 2026-09-14 |
| Una mezcla gaussiana se protege del colapso de una componente | GMM pone un suelo de 1e-6 a cada varianza, lo que deja que una componente colapse sobre un punto |
gmm.rs 170-171 |
1e-6 |
Reproducido, no reportado 2026-09-14 |
explained_variance_ratio es una parte de la varianza total |
Cada autovalor se divide por la suma de los autovalores conservados, así que las proporciones siempre suman 1 | pca.rs 46-57 |
[0,5635, 0,4365] frente a las verdaderas [0,3867, 0,2996], que suman 0,6863 |
Reproducido, no reportado 2026-09-15 |
| La iteración de la potencia encuentra el eje principal | Parte del vector fijo (1, …, 1)/√n, y puede devolver el eje secundario |
pca.rs 107 |
Un arranque que no tiene nada de aleatorio | Reproducido, no reportado 2026-09-15 |
| Los autovalores se pueden volver a leer | PCA::explained_variance es privado y la proporción está normalizada como arriba: save_state() es la única salida |
pca.rs |
Un solo acceso, y es un serializador | Reproducido, no reportado 2026-09-15 |
| Un bosque aleatorio sortea sus variables en cada corte, como promete su propio comentario | Las sortea una vez por árbol, antes de construirlo | random_forest.rs 68-84 |
Con max_features = 1: versión a mano 0,9474, IX 0,7632 |
Reproducido, el comentario y el código se contradicen, no reportado 2026-09-15 |
| Un empate en el voto del bosque se rompe como en scikit-learn | max_by conserva el último máximo: va al índice de clase mayor |
random_forest.rs 97 |
La misma forma que el hallazgo 3 | Reproducido, no reportado 2026-09-15 |
| El gradiente analítico es igual a uno por diferencias finitas | Dense::backward divide una segunda vez por el tamaño del lote: grad_output ya lleva el 1/n que puso mse_gradient |
ix-nn/layer.rs 40-52 |
Razón de 65,0000 exactamente frente al oráculo por diferencias finitas | Reproducido, no reportado 2026-09-15 |
mse_gradient es el gradiente de mse_loss |
Lo es para una columna de salida. mse_loss promedia sobre filas y columnas; mse_gradient solo divide por las filas |
ix-nn/loss.rs 6-16 |
Razones 1, 2, 3, 4 para 1 a 4 columnas | Reproducido, no reportado 2026-09-15 |
Dense::new admite una semilla |
Sortea del generador del hilo: una red no es reproducible | ix-nn/layer.rs 27 |
Ningún parámetro de semilla | Reproducido, no reportado 2026-09-15 |
Un Sequential de dos capas Dense puede aprender el XOR |
Dense es el único tipo que implementa Layer: no hay activación que meter entre dos aplicaciones afines, y toda la red es una sola aplicación afín |
ix-nn |
La pérdida se estanca en 0,25 tras 50 000 épocas y predice 0,5 en las cuatro esquinas | Reproducido, funcionalidad ausente más que defecto, no reportado 2026-09-15 |
minimize distingue una ejecución divergida de una que agotó iteraciones |
Devuelve el punto de partida de quien llama, etiquetado como el mejor visto, con el mismo converged: false que una no convergencia corriente |
ix-optimize/gradient.rs 124-165 |
Rosenbrock con paso 0,01 informa best f 24.200000, el valor de partida |
Reproducido, no reportado 2026-09-15 |
TSNE::transform(new_rows) coloca esas filas en el mapa ajustado |
El argumento se ignora: devuelve la proyección 12 × 2 guardada incluso con una entrada 1 × 1 |
tsne.rs 249-253 |
La salida es exactamente la proyección original | Reproducido, no reportado 2026-09-24 |
| El encabezado Barnes-Hut del módulo t-SNE describe la implementación | compute_q y el gradiente recorren todos los pares de puntos |
tsne.rs 160-178, 205-217 |
Bucles cuadráticos visibles en el código; rendimiento no medido | Confirmado en código, no reportado 2026-09-24 |
Experimentos
Sección titulada «Experimentos»Un curso que encuentra divergencias debe mostrar qué comprobó y halló correcto. Las seis comprobaciones iniciales siguen abajo; la quinta falló y produjo los hallazgos 15 y 16. La lección 9 añade dos comprobaciones geométricas exploratorias, no prerregistradas.
| Pregunta | Hipótesis | Resultado | Veredicto | Dónde |
|---|---|---|---|---|
¿Siguen los SGD, Momentum y Adam de IX paso a paso a implementaciones escritas a mano? |
Una implementación correcta debe coincidir exactamente, no aproximadamente | Los mismos recuentos de pasos — 5000, 4129, 2822 —, los mismos puntos finales y el mismo primer paso | Confirmada | 2026-09-15 |
¿Coincide la descomposición propia del PCA de IX con Jacobi y con la SVD de scikit-learn? |
Sobreentendida por las cinco varianzas que imprime | Jacobi hasta 1,28e-10, scikit-learn hasta los dígitos impresos | Confirmada — solo la proporción y el arranque fijo están mal | 2026-09-15 |
¿Coincide GradientBoostedClassifier con una versión a mano y una comprobación con numpy? |
Sobreentendida | Coincide en cada fila de prueba y con cualquier número de rondas, con los mismos log-priors suavizados y los mismos tocones en la primera ronda | Confirmada | 2026-09-15 |
¿Puede un xorshift de 64 bits repetible sustituir al StdRng no repetible de IX en una comprobación por bootstrap? |
Escrita de antemano: un generador repetible usado igual en Rust y en Python cae sobre las mismas filas fuera de bolsa | La comprobación con numpy cae sobre las mismas 48 filas dejadas fuera | Confirmada | 2026-09-15 |
¿Es el gradiente analítico de ix_nn igual a su gradiente por diferencias finitas? |
Escrita de antemano: la razón debe valer 1 | 65,0000, y luego 1, 2, 3, 4 según el número de columnas — hallazgos 15 y 16 | Refutada | 2026-09-15 |
| ¿Cambia la suma en coma flotante en macOS-ARM alguna afirmación de las lecciones? | Escrita de antemano: solo debe diferir el signo de un valor que ya era cero | -0.0000 frente a 0.0000 en dos esquinas, todos los demás valores idénticos a cuatro decimales (ejecución 35039180659) |
Confirmada | 2026-09-16 |
| ¿Conserva la MDS clásica escrita a mano las mismas distancias del cuadrado que IX? | Ambas deberían recuperar las seis distancias, independientemente de la orientación de los ejes | Error máximo menor de 1e-10 en ambas; numpy coincide |
Confirmada, exploratoria | 2026-09-24, l09_reducers.rs |
| ¿Separa el primer eje de PCA con núcleo RBF dos anillos concéntricos? | La similitud no lineal debería poner primero el contraste radial | IX y scikit-learn lo sitúan en el eje 4; separación de 0.5798 en IX, cero en el eje 1 |
Refutada, exploratoria | 2026-09-24, l09_reducers.rs |
2026-09-14 — IX, fijado
Sección titulada «2026-09-14 — IX, fijado»- IX está clonado aparte de mi copia de trabajo, con
git clone --filter=blob:none, y en el checkout de490c39533627d296bf9f8f050e6fafc14d7a20c2, un commit demaindel 2026-09-14, 21:40 UTC. El workspace tiene 83 carpetas bajocrates/(su README dice 81 crates); el curso leeix-math,ix-supervised,ix-optimize,ix-unsupervised,ix-voicings,ix-ioeix-agent, y depende de los cinco primeros. Cargo.tomlnombra cada crate congityrev, yCargo.lockestá en el commit. Cargo descarga una sola vez el repositorio completo de IX para los cinco crates, incluido su submódulogovernance/demerzel.- El código del curso comparte
ndarray0.17 con IX: las matrices pasan de las versiones a mano a las funciones de IX sin conversión. - Las herramientas MCP de IX de mi sesión de Claude Code me ayudaron a encontrar los algoritmos; cada resultado de las lecciones viene de los ejemplos compilados, no de las herramientas.
2026-09-14 — Los datos
Sección titulada «2026-09-14 — Los datos»data/extract.pyleecode/duckdb/data/runs.jsonyjobs.json, la exportación que hizo el curso de DuckDB el 2026-09-14, y ejecutagit ls-treeen cada commit compilado para contar sus archivos.mdy.mdx.builds.csv: los jobsbuildexitosos de Deploy to GitHub Pages, 65 filas, con los segundos del paso cuyo nombre empieza por Install, build.jobs.csv: los jobs que terminaron con éxito o con fallo y tienen a la vez un paso de checkout y su paso posterior, 186 filas. El sistema operativo es la etiqueta del runner sin-latest.queue_ses el tiempo desde la creación del job hasta su inicio.- Cada tiempo es un número entero de segundos (la diferencia de dos marcas de tiempo, truncada). La lección 1 muestra lo que eso le hace a la inferencia de tarea de IX, la lección 4 lo que le hace a una mezcla gaussiana.
- No se descargó ningún conjunto de datos: ambos archivos se derivan de este repositorio público.
2026-09-14 — El CI
Sección titulada «2026-09-14 — El CI»ml-ix-examples.ymlejecutacheck.shenubuntu-latest,windows-latestymacos-latest:cargo fmt --check,cargo clippy --release --all-targets -- -D warnings,cargo test --release(pruebas unitarias y doctests, incluida la importacióncompile_failde la lección 4), y luego cada ejemplo, cuya salida y código de salida se comparan conexpected/mediantediff --strip-trailing-cr.UPDATE=1reescribeexpected/,CROSSCHECK=1añade la comprobación en Python en local.- La comprobación cruzada solo se ejecuta en Linux, con Python 3.13, numpy 2.4.2 y scikit-learn 1.8.0: recalcula las métricas, la recta de regresión, el árbol de decisión, las exactitudes de los k vecinos más cercanos, las siluetas, DBSCAN y una mezcla gaussiana, y compara su salida con
expected/crosscheck.txt. No puede reproducir el generador de números aleatorios de IX, así que no reproduce los inicios de k-means ni las divisiones aleatorias de IX. - Los valores de punto flotante se imprimen con un número fijo de decimales (
fmt_vecensrc/lib.rs), así que las salidas son idénticas en los tres sistemas; ninguna necesitó un archivo por sistema operativo. - Las ejecuciones 34903197003 (los ejemplos de las lecciones, commit
d9ef7fb) y 34903462618 (las soluciones de los ejercicios, commit15cde43) pasaron en los cuatro jobs al primer push.
2026-09-14 — Decisiones al escribir el código
Sección titulada «2026-09-14 — Decisiones al escribir el código»train_test_split, k-means++, el inicio del GMM yStratifiedKFoldde IX sacan sus números deStdRngde Rust, que Python no puede reproducir. Para ellos, la versión a mano parte del resultado ajustado de IX y comprueba que un paso más no cambia nada (k-means, EM), y la comprobación cruzada compara lo que no depende del inicio: tamaños, inercias del mejor de n, log-verosimilitudes.- La lección 3 prueba con uno de cada cinco jobs, una división que ambos lenguajes pueden hacer.
- La lección 3 ejecuta
k = 4junto ak = 5por el desempate de IX: el empate que vi primero desapareció cuando el conjunto de prueba pasó a ser uno de cada cinco jobs, yk = 4estandarizado vuelve a mostrar uno, en una fila de prueba real. - La primera comparación de la mezcla gaussiana no coincidía con scikit-learn. Rastrearla llevó a las varianzas colapsadas de
complete_s, que se convirtieron en una sección de la lección 4 en lugar de una nota al pie.
2026-09-14 — Donde IX difiere
Sección titulada «2026-09-14 — Donde IX difiere»Nueve puntos en los que la respuesta de IX, o su documentación, difiere del libro de texto o de scikit-learn, cada uno mostrado por código compilado en el curso salvo que se indique lo contrario. Ninguno está registrado como issue de IX.
- Escalar antes de dividir. Con
normalizeactivado, el pipeline de ML ajustaStandardScaler(y PCA) sobre todas las filas y después divide: las filas de prueba influyen en el escalado (ml_pipeline.rs, líneas 190-203, luego 537-538 y 704-705). Leído en el código; el mecanismo se muestra en la lección 1 (primera fila de prueba 1.440 frente a 1.042), sin ejecutar la herramienta en sí. - Las etiquetas enteras se vuelven clases.
infer_task_typeconsidera clasificación cualquier vector de etiquetas de enteros no negativos con como mucho 20 valores distintos (preprocessing.rs, líneas 234-254): los segundos de build danMulticlassClassification { n_classes: 18 }, y sumar 0.5 a una fila daRegression. El pipeline lo usa cuando la tarea esauto. - Empates en los k vecinos más cercanos.
KNN::predictusamax_by_key, que devuelve el último máximo: un empate va al índice de clase más alto (knn.rs, línea 53). En los jobs estandarizados conk = 4, la fila de prueba 27 vota[2, 0, 2]: IX dice macOS, la versión a mano y scikit-learn Ubuntu, el sistema operativo real; exactitud 0.947 frente a 0.974. - Un cluster vacío de k-means se va al origen. La actualización de centroides parte de ceros y omite los clusters sin filas (
kmeans.rs, líneas 137-152).KMeans(3)sobre[5, 5, 9, 9]da los centroides[5.0, 9.0, 0.0], ypredict([1.0])devuelve el cluster vacío; scikit-learn da[5.0, 9.0, 9.0]con una advertencia. - La silueta de un singleton es 1.
silhouette_score_exactenix-voicingsponea = 0para una fila sola en su cluster, así ques = 1, donde Rousseeuw y scikit-learn usan 0 (lib.rs, líneas 664-678): 0.9296 frente a 0.5963 en[0, 1 | 10]. - Las importaciones de los tutoriales no compilan.
use ix_unsupervised::{KMeans, Clusterer};falla conE0432, porque el crate solo exporta módulos (lib.rs, líneas 5-14). La misma forma aparece endocs/unsupervised-learning/kmeans.md(líneas 60 y 91),dbscan.md(línea 50),pca.md(líneas 63 y 101),docs/use-cases/fraud-detection.md(línea 43),gis-spatial-analysis.md(líneas 79, 375, 427),docs/foundations/rust-for-ml.md(línea 141), y en sus versiones en francés. El doctest del curso comprueba la primera. - Un solo inicio de k-means.
KMeans::fitejecuta un único inicio k-means++, y la herramienta MCPix_kmeansfija la semilla en 42 (handlers.rs, línea 322). En los jobs, las semillas 0 a 9 dan inercias de 411.23 a 618.02; parak = 6, la semilla 42 da 223.366 donde el mejor de 50 de scikit-learn da 214.040, con una silueta de 0.3646 frente a 0.4400. - La regla de voicings no compara.
ix_voicings::clusterse queda conk = 5en cuanto su silueta alcanza 0.15, y solo pruebak = 3por debajo de eso (lib.rs, líneas 770-776). En los jobs, se queda conk = 5(0.4350) en lugar dek = 3(0.4997). Una decisión de diseño más que un error, pero el comentario de documentación llama al umbral uno para «aceptar el agrupamiento», no para elegirk. - Colapso de la mezcla gaussiana.
GMMpone un mínimo de1e-6a cada varianza (gmm.rs, líneas 170-171) y ejecuta un solo inicio aleatorio. Con los tiempos en segundos enteros, las componentes colapsan sobrecomplete_s, y la log-verosimilitud depende de la semilla, de −102.666 a 552.879; el mejor de 10 inicios de scikit-learn es −102.667. Comparar las log-verosimilitudes de IX elige el modelo más colapsado.
Diferencias que vale la pena conocer, que no cuento como errores:
LogisticRegressionno tiene regularización ni criterio de parada: con datos separables, sus pesos crecen con el número de iteraciones (lección 3).ix_optimize::gradient::SGDes descenso de gradiente por lote completo:minimizele pasa el gradiente sobre todas las filas.DBSCANetiqueta el ruido con0y los clusters desde1, como dice su documentación; el código escrito para el-1de scikit-learn fusiona el ruido con un cluster.ix_io::csv_io::read_csvconvierte los campos de texto enNaNsin error.train_test_splitsiempre baraja, y toma las etiquetas de clasificación comof64; no hay opción estratificada ni por orden temporal.
2026-09-15 — Lecciones 5 a 8
Sección titulada «2026-09-15 — Lecciones 5 a 8»- Cuatro lecciones de una tanda: componentes principales, conjuntos, redes neuronales, optimización. El curso depende ahora de siete crates de IX, tras añadir
ix-ensembleeix-nnen el mismo commit fijado, y ejecuta dieciséis ejemplos en el CI. - La comprobación cruzada creció con ellas. La lección 5 es la primera en la que scikit-learn puede comprobar IX directa y completamente —
PCAes determinista, no toma semilla y tiene una convención de signo documentada — así que cada número de esa lección tiene tres fuentes independientes. - La lección 6 introdujo un generador xorshift de 64 bits en el código del curso. IX extrae de
StdRng, que Python no puede reproducir; tres desplazamientos y tres XOR sí se reproducen exactamente, y la comprobación con numpy cae en las mismas 48 filas que dejó fuera el mismo bootstrap. Eso cierra la brecha de la que se quejaba la nota del 2026-09-14, para los algoritmos que el curso escribe él mismo. - La lección 7 es la primera que usa las diferencias finitas como oráculo en lugar de scikit-learn. Cada afirmación sobre lo que calcula
ix_nnse midió moviendo un peso y observando la pérdida, no se dedujo leyendo — lo cual resultó importar, porque dos de los tres hallazgos se desvían por un factor que nadie adivinaría leyendo el código. - La lección 8 no encontró nada malo en las reglas de actualización de
ix_optimize: SGD, Momentum y Adam igualan a las versiones a mano paso a paso y aterrizan en los mismos puntos. El hallazgo allí es sobre el tipo de resultado deminimize, no sobre su aritmética.
2026-09-15 — Donde IX difiere, continuación
Sección titulada «2026-09-15 — Donde IX difiere, continuación»Diez puntos más en los que la respuesta de IX, o su documentación, difiere del libro de texto o de scikit-learn, cada uno mostrado por código compilado en el curso. Con los nueve del 2026-09-14 son diecinueve. Ninguno está registrado como issue de IX.
- La razón de varianza explicada está normalizada sobre las componentes retenidas.
explained_variance_ratiodivide cada valor propio por la suma de los valores propios retenidos (pca.rs, líneas 46-57), así que las razones suman 1 para cualquiern_componentsy el «proportion of total variance» del comentario de documentación solo se cumple si se guardan todas las componentes. Sobre los jobs estandarizados,PCA::new(2)informa de[0.5635, 0.4365]donde la verdad, y scikit-learn, dicen[0.3867, 0.2996], con suma 0.6863. La pregunta habitual «¿cuántas componentes para el 90 %?» no se le puede hacer. - La iteración de potencia parte de un vector fijo y puede devolver el eje menor. El inicio es
(1, …, 1)/√n(pca.rs, línea 107). En una nube de seis puntos estirada a lo largo de(1, -1), cuya covarianza es[[2.4, -1.6], [-1.6, 2.4]], ese inicio es el vector propio menor, así quePCA::new(1)devuelve varianza 0.8 a lo largo de(1, 1)en lugar de 4.0 a lo largo de(1, -1).PCA::new(2)es peor: la deflación deja una matriz que envía el inicio a cero, salta la protecciónnorm < 1e-15, y el modelo acaba con la misma componente dos veces y una varianza de 0. La SVD de scikit-learn devuelve 4.0 y(0.7071, -0.7071). Un caso al filo de la navaja — necesita varianzas exactamente iguales — pero nada en la salida dice en qué caso estás. - Las varianzas son inalcanzables.
PCA::explained_variancees un campo privado, yexplained_variance_ratio()está normalizado como en el hallazgo 10, así quesave_state()es la única forma de sacar los valores propios de un modelo ajustado. - Un bosque aleatorio sortea sus variables una vez por árbol, no una vez por división. El comentario de documentación promete «random feature subsets (sqrt(n_features) features per split)»; el código las sortea antes de hacer crecer el árbol y construye todo el árbol con esas columnas (
random_forest.rs, líneas 68-84). Con el valor por defecto de tres variables de cinco ambos coinciden, llegando los dos a 0.9211 en los jobs de prueba; conmax_features = 1la versión a mano, que sortea en cada división, llega a 0.9474 e IX a 0.7632, porque cincuenta árboles unidimensionales no pueden combinar variables. - Un empate en el voto del bosque va al mayor índice de clase.
predictusamax_by, que se queda con el último máximo (random_forest.rs, línea 97). Dos tocones con la semilla 6 dan probabilidades[0.500, 0.500], e IX responde la clase 1 donde scikit-learn ynp.argmaxresponden 0. La misma forma que el hallazgo 3 sobreKNN::predict. Dense::backwarddivide dos veces por el tamaño del lote.grad_outputya lleva el1/nque puso ahímse_gradient, ybackwardvuelve a dividir porn(layer.rs, líneas 40-52). Medido contra el gradiente por diferencias centradas demse_losssobre las 65 compilaciones, la razón es 65.0000 exactamente. El entrenamiento converge igualmente, ya que cada capa divide por el mismon, pero la tasa de aprendizaje que pasas es la tasa de aprendizaje dividida por el tamaño del lote.mse_gradientno es el gradiente demse_lossmás allá de una columna de salida.mse_losspromedia sobre filas y columnas,mse_gradientdivide solo por las filas (loss.rs, líneas 6-16). Medido de una a cuatro columnas, la razón es 1, 2, 3, 4. Combinado con el hallazgo 15, una capa de dos salidas sobre 65 filas se mueve por el gradiente verdadero dividido por 32.5.Dense::newno toma semilla. Extrae del generador del hilo (layer.rs, línea 27), así que dos capas construidas en la misma ejecución ya difieren y ningún resultado deSequentialpuede reproducirse ni someterse a pruebas de regresión. Todos los demás algoritmos aleatorios de IX toman una —KMeans,RandomForest,Dropout,transformer::FeedForward. Los camposweightsybiasson públicos, que es la salida y lo que hace el curso.- Un
Sequentialde capasDensees una sola aplicación afín.Densees el único tipo que implementa el traitLayer:ix-nnno tiene capa de activación queSequential::pushpudiera aceptar entre dos aplicaciones afines. En el o exclusivo, dos capasDenseconvergen a una pérdida de 0.25 — la varianza de los objetivos, lo mejor que puede hacer una constante — y predicen 0.5 en las cuatro esquinas, mientras que la red a mano del mismo tamaño con una sigmoide entre sus capas llega a 0. Tras 50 000 épocas la pila sigue satisfaciendof(0,0) + f(1,1) - f(0,1) - f(1,0) = 0hasta el último bit: no está poco entrenada, no puede representar la función. Las no linealidades reales del crate viven entransformer::gelu, dentro deFeedForward, que trabaja sobreArray3y no es unLayer. minimizedevuelve el punto de partida para una ejecución que divergió. Recuerda el mejor punto por el que ha pasado y devuelve ese (gradient.rs, líneas 124-165). El descenso simple a tasa 0.01 sobre Rosenbrock se va a NaN en sus primeros pasos; el resultado informa debest f 24.200000, que esfen el punto que pasó el llamante, conconverged: false— la misma bandera que recibe una ejecución perfectamente buena que simplemente agotó sus iteraciones. Nibest_valueniconvergedseparan ambos casos.
Diferencias que vale la pena conocer, que no cuento como errores:
RegressionStumppone la media de los residuos en cada hoja, donde la receta de Friedman y scikit-learn usan un paso de Newton. Es una simplificación defendible, y la versión a mano la copia para que ambas se puedan comparar; con ella, IX y la versión a mano coinciden en cada una de las 38 filas de prueba con 1, 5, 10, 25 y 50 rondas.GradientBoostedClassifierno tiene parada temprana ni puntuación sobre un conjunto reservado. En los jobs alcanza un máximo de 0.9474 tras cinco rondas y se asienta en 0.9211 hacia las veinticinco, y nada en la API lo mide.ix-ensembleno tiene puntuación out-of-bag, que el bagging regala y scikit-learn expone comooob_score=True.ClosureObjectivenunca redefineObjectiveFunction::gradient, así que envolver una closure compra en silencio un gradiente medido a costa de dos evaluaciones extra por coordenada y por paso — 1001 evaluaciones frente a 201 para 200 pasos de Adam en dos dimensiones. Sobre un objetivo suave no cuesta nada en precisión: ambas ejecuciones terminaron en1.061e-16.
2026-09-15 — Lo que coincidió
Sección titulada «2026-09-15 — Lo que coincidió»Merece anotarse junto a los hallazgos, porque las lecciones hasta ahora han encontrado sobre todo lo contrario:
- SGD, Momentum y Adam de
ix_optimizeigualan exactamente a las versiones a mano — mismos recuentos de pasos (5000, 4129, 2822), mismos puntos finales, mismo primer paso desde el mismo gradiente. GradientBoostedClassifieriguala a la versión a mano en cada fila de prueba con cualquier número de rondas, y numpy reproduce las mismas exactitudes, los mismos log-priores suavizados y los mismos tocones de la primera ronda.- Las cinco varianzas de
PCAcoinciden con las de Jacobi hasta1.28e-10y con las de scikit-learn hasta los dígitos impresos; solo la razón y el vector inicial fijo están en cuestión.
2026-09-15 — El mapa de API
Sección titulada «2026-09-15 — El mapa de API»- El plan promete una lección por familia de algoritmos, lo que aún dejará intacta la mayor parte del workspace.
scripts/sync-ix-api-map.mjsllena el hueco por el otro lado: un sologit grepsobre el commit fijado, y una página generada por idioma que lista cada declaraciónpubbajocrates/*/src/, crate a crate y módulo a módulo. 80 crates, 3398 declaraciones, de las cuales 2307 son funciones. - Está generada, nunca escrita a mano, y
npm run sync:ix-api-mapla reconstruye — así que no puede alejarse del commit fijado, que es lo que suele pudrir una página así. La revisión se lee delCargo.tomldel curso en vez de escribirse dos veces. - Cuenta lo que dice el código fuente, no lo que un llamante puede alcanzar: un
pubdentro de un módulo privado aparece listado y es invisible desde fuera del crate. El hallazgo 12 — un campo privado que deja la razón inservible — es exactamente el tipo de cosa que la página no puede contarte, y las lecciones sí.
2026-09-16 — Un cero negativo en macOS
Sección titulada «2026-09-16 — Un cero negativo en macOS»- La primera ejecución de CI del código de las lecciones 5 a 8 salió roja solo en
macos-latest, enl07_networks: la red del o exclusivo imprimíapredictions [-0.0000, 1.0000, 1.0000, -0.0000]donde Windows y Linux imprimen0.0000en esas dos esquinas. Ejecución 35039180659; los otros tres jobs pasaron, y también los otros quince ejemplos. - En las dos esquinas nulas la red aterriza por debajo del cuarto decimal, y solo difiere el signo — las mismas sumas tomadas en otro orden en ARM.
-0.0y0.0son el mismo número y se comparan iguales: el signo no es un resultado, y ninguna afirmación de las lecciones cambia. fmt_vecexiste para que «las salidas no dependan de cómo imprime cada sistema los últimos dígitos», lo que lo convierte en el lugar del arreglo: todo lo que se redondea a cero imprime ahora0.0000, nunca-0.0000, y cualquier otro signo sobrevive. Una prueba unitaria fija ambas mitades. Ningún archivoexpected/cambió, así que ninguna lección tuvo que volver a citarse.- La nota del 2026-09-14 — decimales fijos, así que las salidas son idénticas en los tres sistemas — acertaba con el remedio y se quedaba a un caso de estar completa. Los decimales fijos no deciden el signo de un cero, y un curso escrito en una sola máquina no puede descubrirlo.
2026-09-24 — Cinco reductores, tres entradas
Sección titulada «2026-09-24 — Cinco reductores, tres entradas»l09_reducers.rsse ejecutó en Windows con el commit IX fijado: un cuadrado para MDS, dos anillos para PCA con núcleo y los datos CI de 186 jobs para NMF y LDA (las primeras doce filas estandarizadas para t-SNE). MDS escrita a mano e IX recuperan las seis distancias del cuadrado con error menor de1e-10.- En los anillos, la separación del primer eje lineal es
0.0000. La del primer eje RBF también es0.0000; el cuarto llega a0.5798. Esto refuta la expectativa exploratoria sobre el primer eje. Como el signo y la rotación de los autovectores son arbitrarios, la comparación usa diferencias absolutas de medias sin inferir una puntuación de clasificación. - NMF de rango dos reconstruye la matriz bruta de tiempos con error cuadrático medio de
0.505segundos cuadrados por celda y rechaza las entradas estandarizadas negativas. LDA produce dos ejes finitos para tres etiquetas de sistema operativo, sin afirmación de clasificación fuera de muestra. t-SNE con semilla genera una proyección finita12 × 2, perotransformdevuelve exactamente la misma proyección al recibir una entrada no relacionada1 × 1. - La comprobación independiente con numpy/scikit-learn 1.8.0 reproduce las distancias del cuadrado, el resultado de los ejes de los anillos, la reconstrucción NMF finita, los dos ejes LDA y la proyección t-SNE finita. Son comprobaciones de invariantes, no afirmaciones de igualdad de factores NMF o coordenadas t-SNE entre implementaciones. Las hipótesis no se anotaron antes de la ejecución: son hallazgos exploratorios, no confirmaciones prerregistradas. La CI en tres sistemas para la lección 9 sigue pendiente.
Por verificar
Sección titulada «Por verificar»- La herramienta
ix_ml_pipelinede principio a fin: el orden del escalado del hallazgo 1, la inferencia de tarea del hallazgo 2 sobre un archivo CSV, y el errorAll rows contain NaN valuespara un archivo con una columna de texto. Los tres están leídos en el código, no ejecutados. LinearRegression::fitcon dos características idénticas: siix_math::linalg::inversedevuelve un error yfithace panic, o devuelve una respuesta errónea.- Un cluster de k-means que se vacía a mitad de una ejecución real, y no al inicio como en el hallazgo 4.
ix_voicings::clustersobre los voicings de GA: la exportación necesitaFretboardVoicingsCLIde GA, que el curso no compila.- La causa del build de 48 segundos de
95a3830. - Si los hallazgos 1 a 9 ya se conocen en el proyecto original: no he buscado en los issues de IX.
- Los ejemplos en runners Linux ARM: el CI solo cubre
ubuntu-latest(x64),windows-latestymacos-latest(ARM). - Si
PCAdevuelve alguna vez varianzas sin ordenar con datos reales, y no con la nube construida del hallazgo 11. - Si un
Sequentialmás profundo que dos capasDensese usa en algún sitio de IX, donde mordería el hallazgo 18. ix-autograd: su cinta es el crate que debería hacer innecesario el hallazgo 15, y la lección 12 lo medirá.- Si los hallazgos 10 a 19 ya se conocen en el proyecto original: sigo sin buscar en los issues de IX.
- El mapa de API cuenta declaraciones
pub, no las alcanzables; cuánto se separan ambos números está sin medir. - Los valores impresos por un
println!("{:.6}")directo y no a través defmt_vec— elintercept -0.000000de la lección 8 es uno — llevan el mismo riesgo de cero con signo y no están normalizados. Ese coincidió en los tres sistemas en la ejecución 35039180659; los demás no se han enumerado. - Repetir las salidas numéricas de la lección 9 en CI Linux y macOS; comprobar si el error NMF y la separación del cuarto eje RBF se redondean igual. Medir por separado cómo escala el tiempo de t-SNE antes de asignarle un coste.