Guía del usuario

Capítulo 4 — Ventanas auxiliares

Además de la ventana principal (vista 3D más Inspector), RadianceKit gestiona siete ventanas adicionales, todas abribles desde el menú Help. La lista de arriba a abajo: User Guide (⌘?), Keyboard Shortcuts (⌘/), Open Training Logs… (no abre una ventana de la app sino el Finder; por eso no se trata más aquí), Manage Storage…, Pareto Dashboard… (⇧⌘D), Holdout Analysis… (⇧⌘H), BayesOpt Console… (⇧⌘B). Tres de ellas — Dashboard, Holdout, BayesOpt — son herramientas de análisis autónomas. Cada una tiene su propia pila view-model, lee o escribe archivos JSON en disco, y existe un argumento CLI por cada una que permite apuntar la ventana a un archivo concreto directamente al arrancar la app (–dashboard-dir, –holdout-file, –bayesopt-autorun).

Las cuatro ventanas simples (User Guide, Keyboard Shortcuts, Manage Storage, más las entradas del submenú Open Training Logs / Open Exports Folder) reciben una entrada corta por control. Las tres ventanas de análisis se documentan con más detalle — cada una con una introducción que explica qué ves en la ventana, cuándo deberías abrirla y cómo interpretar la imagen mostrada.

Al final del capítulo hay una sección de referencia cruzada al Inspector de la ventana principal: qué puedes leer de forma significativa del gráfico de loss en vivo y de la visualización del número de Gaussianos durante un entrenamiento en curso.

User Guide (W1–W4)

Ventana User Guide con barra lateral a la izquierda y contenido Markdown renderizado a la derecha
Ventana User Guide con barra lateral a la izquierda y contenido Markdown renderizado a la derecha

Qué es: Una ventana de ayuda integrada que renderiza el guide_<language>.md entregado con la app. El idioma se deriva de los Ajustes (pestaña General → Idioma) o, si allí está "System", de las preferencias de idioma de macOS. El layout es clásico: barra lateral con todos los títulos a la izquierda, texto del cuerpo a la derecha.

Cuando necesitas un recordatorio rápido de un único punto — como sustituto de palabra clave. La referencia completa es este manual; la ventana de ayuda integrada se parece más a lo que sería un –help en la línea de comandos. Se actualiza con cada lanzamiento de la app, pero está mantenida deliberadamente más superficial en contenido.

W1NavigationSplitView (Sidebar + Detail)

DÓNDE

Help → User Guide (⌘?).

TÉCNICO

Layout de dos columnas con una barra lateral estrecha (al menos 180 pt de ancho) para el árbol de contenido y un área de detalle desplazable para el contenido Markdown real. La ventana tiene un tamaño mínimo de 700 × 500 pt. En la primera apertura, la ventana carga el guide_<lang>.md correspondiente del bundle de la app (fallback guide_en.md), lo parsea en registros de bloque (títulos H1–H4, párrafos, listas, tablas, separadores) y extrae por separado la estructura de títulos para la barra lateral. El formato inline (negrita, cursiva, code-span) se renderiza mediante el motor Markdown integrado. El idioma se lee de los ajustes de la app, con el caso especial del chino (zh-Hans) y el portugués brasileño (pt-BR), que se mantienen como tags de locale completos porque esas variantes difieren respectivamente del zh y del pt.

W2List (barra lateral de títulos)

DÓNDE

Columna izquierda en la ventana User Guide.

TÉCNICO

Lista de todos los títulos H2 y H3 del documento Markdown actual. Las entradas H2 aparecen sin sangría con peso Medium, las H3 con 16 pt de sangría a la izquierda y un estilo de foreground reducido. H4 y más profundos se ignoran, porque la profundidad haría la barra lateral confusa. Los IDs de anchor se generan a partir del texto del título mediante slugificación (minúsculas + espacios a guiones + filtrado de letras/números/guiones — el mismo algoritmo que usa GitHub para sus anchors Markdown, así que URLs externas a la doc también podrían aterrizar en el mismo anchor). La lista usa el estilo nativo de macOS.

W3Button (título → salto a anchor)

DÓNDE

Un botón por fila de la barra lateral.

TÉCNICO

Cada entrada de la barra lateral es un botón que fija el anchor actual, pero visualmente se ve como una entrada de lista. Una variable observadora dispara entonces el salto de scroll al anchor correspondiente con una animación suave de 0,3 s. Tras el salto, el valor del anchor se restablece para que el siguiente clic sobre el mismo anchor dispare otra vez (de lo contrario el observador no volvería a dispararse porque el valor no habría cambiado).

W4ScrollView (contenido de detalle)

DÓNDE

Columna derecha.

TÉCNICO

Área de contenido desplazable, apilada verticalmente, con renderizado lazy, porque guías más largas pueden tener fácilmente más de 200 bloques Markdown — una variante no lazy los instanciaría todos a la vez. Cada bloque recibe su propio ID, ya sea el anchor del título (para H1–H3 saltables) o un placeholder de índice. El ancho máximo es de 720 pt, padding 32 horizontal / 24 vertical, así que las líneas largas mantienen un layout bien legible. Las tablas se renderizan celda a celda con stacks horizontales y separadores; el código inline mediante el motor Markdown integrado. Los bloques de código reales se tratan actualmente como párrafos — una limitación conocida de la ventana Help.

Keyboard Shortcuts (W5–W6)

Ventana Keyboard Shortcuts — cinco grupos Navigation/Views/Capture/Editor/Training con columna de hotkey a la izquierda y descripción a la derecha
Ventana Keyboard Shortcuts — cinco grupos Navigation/Views/Capture/Editor/Training con columna de hotkey a la izquierda y descripción a la derecha

Lista de referencia estática en cinco secciones. Navigation: Mouse Drag (Orbit/Fly), Shift+Drag/Right-Drag (Pan), Scroll (Zoom), WASD (movimiento fly-through), Q/E (Up/Down), F (Toggle Orbit/Fly), doble clic (re-centrar), Cmd+Scroll (ajuste FoV). Views: R (Reset Camera), T (Auto-Rotation), P (Camera Playback), B (ciclo de fondo), 0–9 (saltar a cámara de entrenamiento 1=10 %/5=50 %/0=última), Flecha Izquierda/Derecha (Prev/Next Cam). Capture: S (captura al escritorio), V (Turntable Video), C (Copy Camera Info). Editor: Tab (modo edición), Click/Drag (Paint-Select), Option+Click (Deselect), X / Delete (eliminar selección), Cmd-Z (deshacer último borrado), [ / ] (tamaño de pincel menor/mayor), Esc (limpiar selección). Training: Start, Pause/Resume, Cancel, Continue +5K/+10K/+20K vía los atajos de menú en M9–M14.

Qué es: Una visión general estática simple de todos los atajos de teclado — Navigation, Views, Capture, Editor, Training. El contenido está codificado en duro, sin carga de Markdown.

Cuando buscas la forma más rápida de hacer algo en la vista. WASD fly-through, R para reset de cámara, B para ciclo de fondo — están todos aquí.

W5ScrollView (área de contenido)

DÓNDE

Help → Keyboard Shortcuts (⌘/).

TÉCNICO

Un área de scroll simple con una lista vertical dentro. Padding 20 alrededor, sin árbol de navegación en barra lateral (la lista es lo bastante corta). El contenido se agrupa en cinco secciones (Navigation, Views, Capture, Editor, Training). Una fila por combinación de teclas con texto traducible en ambas columnas. La columna izquierda (código de tecla) tiene ancho fijo de 180 pt, así las descripciones a la derecha se mantienen alineadas verticalmente. Sin interacción más allá del scroll — hacer clic en una fila no dispara nada; los atajos son modificadores de teclado reales en el menú y la vista.

W6VStack (secciones de atajos)

DÓNDE

Dentro del ScrollView.

TÉCNICO

Secciones apiladas alineadas a la izquierda con espaciado de 16 pt. Dentro de las cinco secciones, cada una contiene una cabecera + secuencia de filas. Las cabeceras usan un estilo secondary subheadline — deliberadamente no un formato de título, porque las secciones no necesitan ser navegables. El contenido es deliberadamente plano (sin disclosure, sin búsqueda, sin filtro) para que el componente corra sin cambios en cualquier versión de macOS y el archivo se mantenga legible.

Manage Storage (W7–W12)

Ventana Manage Storage — cabecera muestra „693 items · 16,74 GB total”, tabla con archivos PLY de exportación ordenados por fecha, cada uno con insignia de formato + nombre + tamaño + fecha
Ventana Manage Storage — la cabecera muestra "693 items · 16,74 GB total", tabla con archivos PLY de exportación ordenados por fecha, cada uno con insignia de formato + nombre + tamaño + fecha

Vista de tabla de todos los archivos gestionados por RadianceKit. La cabecera cuenta 693 ítems, 16,74 GB de tamaño total. Toolbar arriba: "Show in Finder" + "Refresh". Cada fila: icono PLY, nombre de archivo (p. ej., training_20260527T211321Z.ply), fecha de exportación, tamaño (varía de 7 KB a 218 MB), icono de lupa (Reveal) e icono de papelera (Move to Trash). Los archivos están ordenados por fecha, los más nuevos arriba. En esta grabación demo, las exportaciones PLY dominan porque se trabajó mucho con –benchmark.

Qué es: Una visión general del uso de disco de todo lo que RadianceKit almacena en ~/Documents/RadianceKit/ — Logs, Exports, Scenes, paquetes de Capture (de la companion iOS), Imports (copias de staging de las imágenes de entrada). Un tamaño en bytes por entrada y dos botones: "Show in Finder" y "Move to Trash". Esto NO es una limpieza automática — la app no elimina nada por sí misma; decides por entrada.

Cuando el disco se llena. Los logs en particular se acumulan (un JSONL por intento de entrenamiento, más el _qualityMetrics.json); las exportaciones también, claro (PLY 100 % datos crudos, una por exportación). También útil tras un crash, cuando el directorio de staging de imports aún tiene copias viejas de las imágenes de entrada (véase "Incidente de presión de disco" en dev_v549f-needle-reduction.md).

W7Botón "Show in Finder"

DÓNDE

Arriba a la derecha de la cabecera en la ventana del navegador de almacenamiento.

TÉCNICO

Abre todo el directorio de RadianceKit (~/Documents/RadianceKit/) en el Finder, así puedes ver directamente la estructura de carpetas y también manipularla con el propio Finder. La acción abre una nueva ventana del Finder y no cambia al contenedor sandbox de la app — ~/Documents/RadianceKit/ es el dominio normal de Documentos accesible a las apps, no una ruta de contenedor sandbox.

W8Botón "Refresh"

DÓNDE

Cabecera, junto al botón Finder.

TÉCNICO

Dispara un escaneo en segundo plano que corre como tarea asíncrona iniciada por el usuario para que escanear árboles de directorios grandes no bloquee la UI. La caminata real recorre cada subcarpeta conocida (Logs, Exports, Scenes, Captures, Imports) y produce una entrada de almacenamiento por hijo directo. Por entrada se determina el tamaño recursivo — preferentemente el uso real de disco (incluido el sharing de hardlinks de APFS) con fallback al tamaño de archivo lógico.

W9List (entradas de almacenamiento)

DÓNDE

Contenido principal bajo la cabecera.

TÉCNICO

Lista con este layout por fila: icono SF Symbols específico de categoría (documento para Logs, flecha de subida para Exports, cubo para Scenes, bandeja para Imports), nombre + subtítulo (etiqueta Kind + fecha de modificación formateada), contador de bytes a la derecha (alineado a la derecha, monoespaciado), botón Reveal (icono de lupa), botón papelera (papelera). Orden: primario por Kind (Scenes primero, luego Exports, Logs, Captures, Imports, Other), secundario por fecha de modificación descendente (lo más nuevo arriba). Si el escaneo sigue en curso, el área muestra en su lugar un indicador de progreso "Scanning…". Si no se encontró nada, una visualización empty-state con un icono de bandeja.

W10Botón de fila "Reveal in Finder"

DÓNDE

Por fila, icono de lupa a la derecha.

TÉCNICO

Abre el Finder y selecciona el ítem específico (archivo o carpeta). Diferencia con W7: W7 abre el directorio raíz; W10 resalta exactamente esta entrada. Flujo práctico: identificar una entrada grande, hacer clic en la lupa, después copiarla, por ejemplo, a un volumen externo.

W11Botón de fila "Move to Trash"

DÓNDE

Por fila, icono de papelera a la derecha junto a la lupa.

TÉCNICO

Dispara el diálogo de confirmación (W12). Solo tras la confirmación corre la operación estándar de macOS "move to trash" (es decir, reversible, sin borrado directo). Tras tirar a la papelera con éxito, la entrada se elimina de la lista y el contador total de bytes se actualiza. En caso de errores, se muestra un diálogo modal de error.

W12ConfirmationDialog (confirmación de borrado)

DÓNDE

Disparado por W11, mostrado como sheet de macOS.

TÉCNICO

Diálogo estándar de confirmación con un título dinámico "Delete <name>?" y una línea de mensaje que indica explícitamente que la entrada va a la papelera y puede restaurarse desde allí (hasta que se vacíe la papelera). Dos botones: "Move to Trash" como acción destructiva (mostrada en rojo) y "Cancel" con vínculo automático a Esc. El diálogo es no modal en el sentido de que solo bloquea esta ventana, no toda la app — es el estándar de macOS para borrados reversibles.

Pareto Dashboard (W13–W22)

Pareto Dashboard — estado vacío antes de la importación de reportes
Pareto Dashboard — estado vacío antes de la importación de reportes

Estado vacío (tras la primera apertura) — estado vacío con call-to-action "Open Reports Folder…". Los puntos de datos aparecen en cuanto se cargan los reportes de entrenamiento, véase la siguiente captura.

Pareto Dashboard con 129 reportes de benchmark cargados — Gaussianos vs PSNR con frente de Pareto, filtros Scene/Strategy/Mip
Pareto Dashboard con 129 reportes de benchmark cargados — Gaussianos vs PSNR con frente de Pareto, filtros Scene/Strategy/Mip

La barra de herramientas de la cabecera muestra "129 reports of 129" (todos los reportes de la carpeta seleccionada parseados con éxito — 21 archivos adicionales no pudieron parsearse por formato antiguo, véase la lista de avisos a la derecha). Ejes: selector de eje X en Gaussians, selector de eje Y en PSNR (dB). Scatter plot: puntos verdes = estrategia Classic, puntos azules = MCMC. La línea discontinua del frente de Pareto corre a lo largo de los mejores valores de PSNR alcanzados y se aplana hacia PSNR≈30 dB a partir de unos 500K Gaussianos. Chips de filtro a la derecha: 7 escenas (bicycle, bonsai, family, flowers, kitchen, stump, truck), 2 estrategias (classic, mcmc), 3 opciones de Mip-Splatting (All, On, Off). En este momento todos los filtros están abiertos, de ahí el denso cluster de puntos.

Qué es: Una herramienta de comparación multi-run. En el pasado entrenaste varias escenas, o la misma escena con distintos ajustes preestablecidos — cada uno de esos runs produce (si pasaste –benchmark o lo llamaste mediante la función Benchmark) un archivo de reporte JSON que contiene, entre otras cosas, PSNR final, SSIM, LPIPS, número de Gaussianos y tiempo wallclock. El Dashboard lee toda una carpeta de esos reportes a la vez y los representa como scatter 2D con ejes seleccionables. Además, se dibuja el frente de Pareto (el conjunto de puntos no dominados) como línea discontinua.

Después de haber producido al menos tres o cuatro reportes de entrenamiento. Con menos puntos, la línea de frente no es significativa. Caso de uso típico: intentaste reconstruir una escena exterior y ejecutaste sucesivamente P3 Balanced (Classic), P4 Quality (Classic), P7 MCMC Quality y P9 Outdoor (tuned) — ahora quieres saber qué configuración entrega el mejor PSNR por segundo de tiempo de entrenamiento, o cuál requiere menos Gaussianos para un PSNR dado.

Ambos ejes son libremente seleccionables (eje X: psnr, ssim, lpips, …; eje Y lo mismo). La lógica de frente de Pareto en ParetoFront2D.indices sabe para cada métrica si "menor = mejor" (p. ej., LPIPS, Loss, Time) o "mayor = mejor" (PSNR, SSIM) — así que, según la elección de eje, la línea va de abajo-izquierda a arriba-derecha o de arriba-izquierda a abajo-derecha, siempre a lo largo de la mejor combinación alcanzada. Un punto es Pareto-óptimo si NINGÚN otro punto es al menos tan bueno en AMBAS dimensiones (es decir, ningún otro punto lo domina). Los puntos Pareto-óptimos están sobre la línea, los demás a la derecha/encima (según la orientación del eje). Los puntos SOBRE la línea son los candidatos reales para "mejor ajuste preestablecido"; los puntos LEJOS de la línea son tiempo de entrenamiento desperdiciado.

Puedes restringir la selección a una escena concreta (si solo quieres comparar runs de exteriores, p. ej.), a una estrategia concreta (Classic o MCMC) o a Mip-Splatting on/off (relevante tras la Fase Q1.5, donde Mip permanece como flag avanzado opt-in).

Tienes tres reportes para la escena "truck" en ~/Documents/RadianceKit/Reports/: Run A (P4 Quality, 40K iter, 524K Gs, 105 s, PSNR 23,4), Run B (P7 MCMC, 200K iter, 150K Gs, 693 s, PSNR 24,6), Run C (P9 Outdoor, 100K iter, 1,25M Gs, 312 s, PSNR 25,8). Pon el eje X en trainingTime, el eje Y en PSNR. El Run B queda arriba a la derecha, el Run C aún más arriba a la derecha, el Run A abajo a la izquierda. El frente de Pareto conecta A y C — ambos no dominados. El Run B está "perdido" (C es mejor en Time Y PSNR). Conclusión: para "truck" el default MCMC no compensa; o rápido+ok (A) o largo+muy bueno (C). Guarda la configuración de C como ajuste preestablecido propio (Inspector → I1 Save Preset).

Siguiente acción: Guardar la mejor configuración como ajuste preestablecido. Concretamente: mira los puntos de Pareto (al hacer hover se muestra PSNR/SSIM/LPIPS/Gs/Time en el tooltip), decide cuál te encaja mejor en el trade-off tiempo-vs-calidad, abre el reporte correspondiente (el nombre de archivo contiene la marca de tiempo del run), copia su configuración de entrenamiento a un nuevo run, o guárdala tras la siguiente sesión de entrenamiento como ajuste preestablecido mediante el Inspector.

W13Botón "Open Reports Folder…"

DÓNDE

Toolbar, arriba a la izquierda.

TÉCNICO

Abre un diálogo selector de carpeta con el prompt "Select a folder containing benchmark .json reports". Tras la confirmación, corre una tarea en segundo plano que parsea secuencialmente todos los archivos .json de la carpeta. Los reportes defectuosos (JSON roto, schema incorrecto) se recopilan y se muestran al pie de la barra lateral como "N file failed to parse" — sin crash. Si ocurre un segundo clic mientras una primera carga sigue en curso, la tarea anterior se cancela para que dos resultados no escriban a la vez en el estado.

También vía CLI: –dashboard-dir /ruta/a/reports carga la carpeta directamente al arrancar la app.

W14Selector "X-Axis"

DÓNDE

Encima del gráfico, a la izquierda.

TÉCNICO

Selector tipo menú con todos los ejes de métrica disponibles del módulo de dashboard (PSNR, SSIM, LPIPS, número de Gaussianos, tiempo de entrenamiento, etc.). El predeterminado es número de Gaussianos. Al cambiar, el punto sobre el que estaba el hover se restablece, porque una posición resaltada previamente en el viejo sistema de coordenadas ya no tiene sentido tras el cambio de eje. El selector está constreñido a ancho de contenido para que no abarque todo el ancho.

W15Selector "Y-Axis"

DÓNDE

Encima del gráfico, junto a X-Axis.

TÉCNICO

Idéntico a W14, salvo que el predeterminado es PSNR. La elección de eje se almacena de forma independiente, así que el usuario también puede elegir combinaciones sin sentido (X=PSNR, Y=PSNR — pondría todos los puntos en una diagonal). Esas combinaciones no se atrapan, sin embargo; una decisión deliberada, porque una comparación "SSIM vs PSNR" es bastante interesante para ver lo consistentes que son las métricas.

W16Conmutador "Show Pareto Front"

DÓNDE

A la derecha de los selectores de ejes.

TÉCNICO

Conmutador estándar de macOS. Si está activo, se dibuja en el gráfico de Pareto una línea con el frente de Pareto 2D calculado, además de la nube de puntos. Estilo: discontinua (patrón de guiones 4–4), gris semitransparente, ancho de línea 1,5 pt. El cálculo de Pareto corre en el hilo principal — con el número típico de reportes (≤ ~50) es rápido sin problemas. Si el conmutador está apagado, la línea se omite y solo quedan los puntos.

W17Chips de filtro "Scene"

DÓNDE

Barra lateral derecha en la ventana del Dashboard.

TÉCNICO

Chips de filtro para cada escena que aparece en los reportes cargados. Layout flow personalizado que envuelve automáticamente chips en varias líneas en cuanto se agota el ancho. Los chips activos reciben el fondo de acento, los inactivos un fondo material estándar neutro. La multiselección es posible (semántica de conjunto); si no hay chip seleccionado, todas las escenas cuentan como "pasadas" — es decir, la lógica de conjunto es "selección vacía = todo", no "selección vacía = nada".

W18Chips de filtro "Strategy"

DÓNDE

Bajo el filtro Scene en la barra lateral.

TÉCNICO

Exactamente como W17, pero para estrategias de entrenamiento — típicamente los dos valores "classic" y "mcmc", derivados del campo strategy de los JSONs de reportes de benchmark. Útil si tienes reportes mezclados de ambas estrategias y solo quieres ver un tipo (p. ej., "solo mostrar runs de MCMC porque ya he excluido Classic").

W19Chips de filtro "Mip-Splatting"

DÓNDE

Bajo el filtro Strategy en la barra lateral.

TÉCNICO

Filtro de tres valores (en lugar de un conjunto como W17/W18): "All" / "On" / "Off". Trasfondo: Mip-Splatting se evaluó en la Fase Q1.5 como mejora multi-escala experimental y el veredicto final fue "no hay una victoria clara a través de todo; mantener como flag opt-in". Cuando haces comparaciones Mip on/off a menudo quieres separar muy nítidamente. De ahí el filtro ternario dedicado con los estados "deja pasar todo", "solo Mip on", "solo Mip off". La sección de la barra lateral solo se muestra si en el conjunto de datos hay al menos un reporte Mip Y al menos un reporte no-Mip (de lo contrario, filtrar no tendría sentido).

W20ChipButton (conmutador de filtro, all/on/off)

DÓNDE

Componente auxiliar, usado en W17/W18/W19.

TÉCNICO

Wrapper de botón minimalista. Contenido: texto de etiqueta con tamaño de fuente caption y padding 10 horizontal / 5 vertical. Fondo condicional: si está activo → color de acento de la app con texto blanco; en otro caso, fondo material estándar neutro con texto negro. La forma es de cápsula (tipo pill). Estilo plain-button para que el material de la cápsula no quede solapado por un borde del sistema.

W21Chart (scatter de Pareto)

DÓNDE

Área central del Dashboard.

TÉCNICO

Diagrama Swift Charts con dos capas: 1. un punto por reporte — posición a partir de las métricas X e Y elegidas, color por estrategia, símbolo por estado Mip. Tamaño de símbolo normal 80, resaltado 200 (si el ID coincide con el reporte sobre el que está actualmente el hover). 2. una línea para el frente de Pareto, solo si el conmutador está activo.

Overlay del chart: un rectángulo transparente registra el movimiento del ratón; por fotograma se determina la posición del punto más cercano en distancia euclídea en el plot frame y el reporte hovered se actualiza si la distancia es menor de 24 px (en otro caso se resetea). Así obtienes el tooltip sin hacer clic — basta con pasar por encima.

W22Tooltip (detalle por hover)

DÓNDE

Bajo el gráfico, mostrado al hacer hover.

TÉCNICO

Stack horizontal: nombre de escena (headline), tag de estrategia (caption), separador, después métricas PSNR/SSIM/LPIPS/Gs/Time cada una en un pequeño grupo vertical (etiqueta + valor monoespaciado). Si Mip estaba activado, adicionalmente una etiqueta cápsula "Mip" en color de acento. Fondo semitransparente con blur, rectángulo redondeado con radio de 8 pt. Solo se muestra cuando el ratón está realmente sobre un punto. Desaparece automáticamente al salir.

Holdout Analysis (W23–W29)

Holdout Analysis — estado vacío antes de cargar un transforms.json
Holdout Analysis — estado vacío antes de cargar un transforms.json

Estado vacío con empty state y call-to-action "Open transforms.json…". Acepta el formato de NeRF Studio e Instant-NGP.

Estado vacío (tras la primera apertura) — los marcadores de cámara aparecen en cuanto se carga un transforms.json, véase la siguiente captura.

Globo de Holdout con 100 cámaras NeRF Blender mic, 5 folds de 20 cámaras cada uno, estrategia angular activa
Globo de Holdout con 100 cámaras NeRF Blender mic, 5 folds de 20 cámaras cada uno, estrategia angular activa

La cabecera muestra el archivo cargado (transforms_train.json) y el número de cámaras ("100 cameras"). Barra lateral izquierda: selector de estrategia con dos opciones — Angular (longitudinal) activo (alinea los folds a lo largo de sectores longitudinales/latitudinales en la esfera, así cada fold de test es geométricamente denso) vs Linear (round-robin) (basado en orden, cada k-ésimo fotograma como conjunto de test). El deslizador de k-folds está en 5, el selector de fold de test en Fold 1. El botón Export produce un fold-assignment.json para Nerfstudio/Instant-NGP. Panel central: proyección de globo 3D de las 100 cámaras — puntos verdes = Train, puntos rojos = fold de test actual (Fold 1 con 20 cámaras). Barra lateral derecha (Angular Correlation): por fold 20 cámaras + Mean Nearest Angle (Fold 1: 7,9°, Fold 2: 7,8°, Fold 3: 7,7°, Fold 4: 7,0°, Fold 5: 6,3°) — un valor más pequeño significa que las cámaras dentro de ese fold están juntas, así que el split de Holdout es espacialmente coherente.

Qué es: Un visualizador 3D para tu disposición de cámaras con lógica de cross-validation. Cargas un transforms.json (el formato estándar de Nerfstudio / Instant-NGP para poses de cámara), la app lee todas las cámaras, proyecta sus direcciones de visión sobre una esfera unidad y las muestra como pequeños marcadores esféricos sobre un globo virtual. Después divide las cámaras en k folds (según la estrategia seleccionada: angular o lineal), marca la porción de entrenamiento en verde y la de test en rojo (Holdout), y calcula por fold una puntuación de Angular Correlation que te dice lo lejos que está el fold de test del fold de entrenamiento en el espacio de ángulos de visión.

Cuando quieres hacer evaluación Holdout — es decir, ¿lo bien que tu modelo generaliza a puntos de vista no vistos? El default en el entrenamiento es "cada 8ª vista como Holdout" (convención Mip-NeRF360), pero es un split muy lineal. Si tus imágenes se agrupan en el tiempo, por ejemplo (un lado del objeto primero, después el otro), entonces "cada 8ª" no es representativo — una posición aleatoria de secuencia acaba en test, pero todos sus vecinos están en entrenamiento, lo que es demasiado fácil. Con "angular" estratificas a lo largo del espacio de ángulos de visión: cada fold contiene cámaras de todas las zonas de la órbita, así que el test sondea de verdad los huecos de generalización.

Angular vs Linear: - Angular (default): divide las cámaras por ángulo longitudinal (coordenada φ alrededor del eje Y) en k sectores iguales. El Fold 0 contiene cámaras con φ ∈ [0°, 360/k°), el Fold 1 las siguientes, y así sucesivamente. Ventaja: cada fold cubre una porción de la órbita; el fold de test es espacialmente compacto pero ampliamente distribuido por el dataset global. Bueno para grabaciones orbitales clásicas. - Linear (round-robin): índice de fold = (image_index modulo k). Ese es el simple split "cada k-ésimo". Funciona si el orden de imagen NO tiene sesgo espacial (p. ej., tomas de dron ordenadas aleatoriamente). Funciona mal si las imágenes se agrupan en el tiempo.

En el globo 3D ves de inmediato: puntos verdes (entrenamiento) y puntos rojos (test). Si los puntos rojos se agrupan todos en una esquina, el Holdout es malo (sin buen test de generalización). Si están repartidos entre los verdes, es bueno. La puntuación de Angular Correlation por fold (barra lateral derecha, en grados) dice además: valor más pequeño = el test está cerca del entrenamiento (cada cámara de test tiene una cámara de entrenamiento cercana, test fácil); valor más grande = el test está lejos del entrenamiento (generalización más difícil).

Capturaste tu escena de truck con 251 imágenes, exporta mediante la entrada de menú M33 (Export SfM transforms.json) un archivo Nerfstudio. Abre la ventana Holdout (⇧⌘H), carga el JSON con "Open transforms.json…", mira el globo. k=5 (default) te da 5 folds. Haz clic en "Fold 3" — comprueba si los marcadores rojos están razonablemente repartidos. Si sí: "Export fold-assignment.json", pon el archivo exportado en la carpeta de reportes, y en el siguiente run de entrenamiento con –benchmark (o los ajustes correspondientes del Inspector) se usa exactamente esa asignación de fold como Holdout de test — en lugar del default "cada 8ª".

W23Botón "Open transforms.json…"

DÓNDE

Toolbar, arriba a la izquierda.

TÉCNICO

Abre un diálogo selector de archivo restringido a archivos JSON. Tras la confirmación, el módulo Holdout carga el archivo. El loader parsea tanto el formato Nerfstudio (intrínsecos de cámara más lista de frames con ruta de imagen y matriz transform) como el formato Instant-NGP (misma estructura). Para cada frame se extrae la dirección de visión de la matriz transform (eje z de la base local de la cámara) y se almacena. Si el parseo falla, se muestra un mensaje de error en el área de estado.

También vía CLI: –holdout-file /ruta/a/transforms.json abre la ventana directamente con el archivo cargado.

W24Selector "Strategy" (angular/linear)

DÓNDE

Barra lateral izquierda, arriba.

TÉCNICO

Selector tipo radio con dos opciones: Angular y Linear. El cambio de estrategia dispara automáticamente un nuevo cálculo de los folds. Las direcciones de visión son una lista de vectores unidad 3D en la esfera; la estrategia angular las proyecta sobre el ángulo longitudinal φ y ordena, la estrategia lineal simplemente hace un split modulo sobre el índice de frame.

W25Deslizador "k Folds"

DÓNDE

Barra lateral izquierda, en el medio.

TÉCNICO

Deslizador de 3 a 10, paso 1. Al cambiar, el cálculo de folds se reinicia automáticamente para que la lista de folds, los índices de entrenamiento/test y la puntuación por fold se recalculen de inmediato. El valor seleccionado se muestra como texto monospaced-digit junto a la etiqueta de la derecha.

Regla práctica: k=5 es el estándar (te da un 20 % de test por fold, lo común para cross-validation). k=10 si tienes muchos datos y necesitas más folds para significancia estadística. k=3 si tienes pocos datos.

W26Selector "Test Fold"

DÓNDE

Barra lateral izquierda, bajo el deslizador k.

TÉCNICO

Selector tipo menú. Las opciones son dinámicamente 0..<k, etiquetas "Fold 1" a "Fold N" (es decir, 1-indexado en la UI, 0-indexado internamente). Si el índice seleccionado previamente es ≥ k (p. ej., porque has bajado k de 10 a 5), se restablece automáticamente a 0. El fold de test seleccionado se muestra en rojo en el globo, todos los demás en verde.

W27Botón "Export fold-assignment.json"

DÓNDE

Barra lateral izquierda, abajo.

TÉCNICO

Abre un diálogo de guardar con nombre predeterminado fold-assignment.json. Tras la confirmación, el módulo Holdout codifica el split actual en un schema JSON (asignación de fold por frame más bloque meta de estrategia). Ese archivo se puede pasar luego al siguiente run de entrenamiento con –benchmark, así se usa el mismo Holdout para la evaluación final de métricas. Los errores de escritura se muestran como texto de error; el éxito en texto verde como "Saved to (filename)".

W28SCNView (globo de cámaras 3D)

DÓNDE

Panel central en la ventana Holdout.

TÉCNICO

Vista de globo SceneKit. La escena consta de: una esfera wireframe (radio 1,0, 36 segmentos, gris oscuro), tres stubs de eje coloreados (rojo/verde/azul para X/Y/Z, cada uno de 1,2 de largo) y, por cámara, una pequeña esfera marcadora (radio 0,03) en la posición correspondiente de la dirección de visión sobre la esfera unidad (un poco por fuera para que no desaparezca DENTRO de la esfera wireframe). Los marcadores NO se reconstruyen en cada cambio de fold — la reconstrucción solo es necesaria cuando cambia la lista de frames (es decir, se carga un nuevo JSON). En su lugar, por actualización corre una actualización in-place de los colores del material: rojo para los índices de test, verde para los de entrenamiento, gris claro si ninguno. Así los ticks del deslizador siguen siendo rápidos incluso con N > 1000 cámaras.

El control de cámara está activado — puedes rotar, hacer zoom y desplazar el globo con el ratón. La iluminación se asegura de que los marcadores no se vean planos. El fondo es gris oscuro.

W29FoldCard (tocar para seleccionar fold)

DÓNDE

Barra lateral derecha, sección "Angular Correlation".

TÉCNICO

Una vista de tarjeta por fold — rectángulo redondeado con radio de 6 pt, padding 10, layout vertical con dos filas (arriba "Fold N" + número de cámaras, abajo "Mean nearest angle:" + valor en grados). Color de fondo condicional: fold activo = color de acento semitransparente, inactivo = material estándar neutro. Tocar selecciona el fold y el globo recolorea en vivo.

La puntuación "Mean nearest angle" es el ángulo más pequeño medio por cámara de test a la cámara de entrenamiento más cercana (calculado internamente en radianes, mostrado en grados en la UI).

BayesOpt Console (W30–W39)

Consola BayesOpt — estado vacío antes del inicio del trial
Consola BayesOpt — estado vacío antes del inicio del trial

Estado vacío con selector de espacio de búsqueda (RadianceKit defaults (6-dim)), deslizador de trial budget (default 40), random seed (42) y tres paneles vacíos para gráfico de convergencia, log de trials y lista de parámetros del espacio de búsqueda.

Estado vacío (tras la primera apertura) — el gráfico de convergencia y la tabla de trials se llenan en cuanto se inicia un run, véase la siguiente captura.

Consola BayesOpt tras 40 trials — el gráfico de convergencia sube con fuerza hasta el trial 15, mejor valor 0,9943, log de trials con tags init/bo/restart
Consola BayesOpt tras 40 trials — el gráfico de convergencia sube con fuerza hasta el trial 15, mejor valor 0,9943, log de trials con tags init/bo/restart

Estado arriba a la derecha "Finished — best 0.9943 after 40 trials". Barra lateral izquierda: selector de espacio de búsqueda en RadianceKit defaults (6-dim), trial budget 40, random seed 42. La lista de parámetros muestra los seis hiperparámetros a tunear con sus rangos de valor: mipSmoothing3DScale [0.05, 0.5], mipFilter2DVariance [0.1, 0.6], densifyGradThreshold [5e-07, 5e-06], ssimWeight [0.05, 0.5], mcmcNoiseScale [1e-05, 0.0001], mcmcRelocationInterval [50, 200]. Centro: gráfico de convergencia (X = índice de trial 1–40, Y = valor objetivo 0–1) — puntos grises = muestras iniciales (LHS), puntos azules = adquisición BayesOpt, puntos naranjas = trials restart (#22 y #31). La línea de mejor valor sube con fuerza hasta el trial ~7, después solo mejora marginal hasta el trial 15, a partir de ahí una meseta plana en 0,99+. Barra lateral derecha: log de trials #1–#34 con score + tag (init/bo/restart). El botón Save Best Config arriba a la derecha escribe bayesopt-best.json.

Qué es: Una consola de optimización bayesiana para búsqueda de hiperparámetros. La optimización bayesiana es un método automático que intenta encontrar el óptimo de una función desconocida con el menor número posible de experimentos — típicamente: "¿qué combinación de mcmcMaxGaussians, capMultiplier, ssimWeight y gradThreshold entrega el mejor PSNR para mi clase de escena?" En lugar de una rejilla de 6^4 = 1296 trials, la optimización bayesiana prueba alrededor de 40–100 trials informados y se acerca así al óptimo.

Importante: La versión actualmente entregada en la app no ejecuta la optimización contra runs de entrenamiento reales (eso tardaría días) sino contra un objetivo demo sintético — un paisaje multi-modal con carácter de hill-climbing más ruido suave. Es deliberado: la ventana está pensada para mostrarte el comportamiento del optimizador (curva de convergencia, puntos de muestra, mejor hasta el momento) y dejarte entender las definiciones del espacio de búsqueda. Para runs reales de BayesOpt dirigidos por entrenamiento (como se hicieron en la Fase Q7 para los ajustes preestablecidos scene-class) se usa un flujo CLI offline separado; la ventana es la variante UI en vivo.

Tres casos de uso: 1. Quieres entender cómo funciona BayesOpt — entonces inicia un run demo y observa el gráfico de convergencia. 2. Estás planificando una nueva clase de escena (como "acuarios" o "muebles antiguos") para la que los 10 ajustes preestablecidos integrados no encajan a la perfección. Define mentalmente un espacio de búsqueda, pruébalo aquí con el "Bowl demo" o el preset "Densify", después exporta la mejor config como JSON y úsala como punto de partida para un run de entrenamiento real. 3. Quieres inspeccionar los espacios de búsqueda por defecto definidos en el paquete RKBayesOpt (subconjunto Mip, RadianceKit defaults) — se listan en el panel de parámetros de la barra lateral izquierda.

- Gráfico de convergencia (columna central): Y = mejor valor de función objetivo alcanzado hasta el momento. X = índice de trial. Sube inicialmente con fuerza (BayesOpt prueba las muestras iniciales aleatoriamente, algunas con suerte), luego se aplana cada vez más porque se agota la región cercana al óptimo. Si la línea se mantiene plana durante 20+ trials, puedes detener el run — trials adicionales no aportarán nada. Los puntos individuales en el gráfico son los valores de trial individuales (es decir, no "mejor hasta el momento"), coloreados por fase: gris = muestra inicial, azul = adquisición BayesOpt, naranja = restart. - Tabla de trials (columna derecha): #1, #2, #3, … cada uno con valor y tag de fase. El mejor trial hasta el momento está marcado con una estrella amarilla. Desde la tabla puedes identificar el mejor trial e inspeccionar sus valores de parámetro después durante la exportación. - Inspector de espacio de búsqueda (barra lateral izquierda): muestra para el preset seleccionado todos los nombres de parámetro y sus rangos de búsqueda [lo, hi]. Si estás en el preset "RadianceKit defaults (6-dim)", ves, p. ej., "densifyGradThreshold [5e-7, 5e-6]" — es decir, log-uniforme entre esos dos valores.

Escoge el preset "RadianceKit defaults (6-dim)", trial budget 40, seed 42. Haz clic en "Start". Observa: los primeros 8 trials son grises (muestras iniciales, LHS Latin hypercube), los siguientes azules (adquiridos por BayesOpt). El gráfico de convergencia sube con fuerza hasta el trial ~15, después se aplana. En el trial ~30–40 el mejor valor se estabiliza. Haz clic en "Save Best Config" — se guarda un bayesopt-best.json con el nombre de preset, índice de trial, valor y los valores de parámetro decodificados. Después puedes copiar manualmente ese JSON a tu definición de ajuste preestablecido.

W30Botón "Start"

DÓNDE

Toolbar a la izquierda, en estado idle/finished.

TÉCNICO

Restablece la lista de trials, cambia al estado running, genera un nuevo run ID (para detección de stale en clics múltiples de Start) y crea una nueva pause gate. Después arranca una tarea en segundo plano que ejecuta el optimizador como stream asíncrono. El tamaño de muestras iniciales es min(8, budget / 4 + 1) — así, típicamente 8 muestras Latin hypercube con budget ≥ 28, menos con budget pequeño. Los updates de trial se reciben de forma incremental y se anexan a la lista. Protección stale-run: si entretanto un segundo clic de Start fija un nuevo run ID, los updates del run antiguo se descartan.

Estilo de acción primaria para el look prominente del botón.

W31Botón "Pause"

DÓNDE

Toolbar a la izquierda, en estado running.

TÉCNICO

Activa la pause gate y cambia al estado paused. El efecto real: el runner espera en un bucle de polling de 50 ms antes de evaluar la siguiente función objetivo. Eso significa que un trial actualmente en curso se ejecuta hasta completarse (es sintético y solo tarda microsegundos), pero no se inicia un trial más. En cuanto Resume corre, continúa donde lo dejó.

W32Botón "Stop"

DÓNDE

Toolbar a la izquierda, en estado running y paused.

TÉCNICO

Cancela la tarea del runner, nulea la referencia, libera la pause gate (si todavía estaba en pause) y cambia al estado finished (si existen trials) o idle (si no). Los trials ya calculados permanecen visibles en la lista — Stop no los elimina. El rol destructivo del botón muestra el botón en rojo porque cancela el run.

W33Botón "Resume"

DÓNDE

Toolbar a la izquierda, en estado paused.

TÉCNICO

Libera la pause gate y vuelve al estado running. La tarea del runner ya está en marcha (está esperando en el bucle de polling); en cuanto el bucle nota que la pausa se ha levantado, continúa e inicia el siguiente trial.

W34Botón "Save Best Config"

DÓNDE

Toolbar a la derecha, siempre visible (pero deshabilitado si no hay bestTrial).

TÉCNICO

Abre un diálogo de guardar con nombre predeterminado bayesopt-best.json, restringido a JSON. Tras la confirmación se construye un diccionario payload: nombre de preset, índice de trial, valor (puntuación objetivo), parámetros (diccionario de nombres de parámetros decodificados → valores). La decodificación proyecta de vuelta las coordenadas del espacio de búsqueda normalizadas en [0,1]^d al rango de valor original (con escalas log-uniforme/lineal/integer según corresponda). La salida JSON está pretty-printed y con claves ordenadas. En errores de escritura (en la versión demo actual) se ignora en silencio — sin UI de error porque es una vía demo.

El botón se mantiene atenuado mientras no haya corrido ningún trial.

W35Selector "Search Space" preset

DÓNDE

Barra lateral izquierda, arriba.

TÉCNICO

Selector tipo menú con cuatro opciones preset: - "RadianceKit defaults (6-dim)" — el espacio de búsqueda estándar completo con todos los hiperparámetros Q7. - "Mip subset (2-dim)" — solo mipSmoothing3DScale [0,05, 0,5] log-uniforme y mipFilter2DVariance [0,1, 0,6] lineal. Útil cuando quieres tunear Mip-Splatting para una clase de escena. - "densify-until + ssim-weight + grad-thresh" — tres parámetros relevantes para Densify (densifyGradThreshold log-uniforme, ssimWeight lineal, densifyUntilIter integer). - "Bowl demo (1-dim)" — espacio de búsqueda pedagógico de un solo parámetro para demos "así es como funciona BayesOpt".

Mientras hay un run activo, el espacio de búsqueda no se puede cambiar (confundiría al optimizador).

W36Deslizador "Trial Budget"

DÓNDE

Barra lateral izquierda, bajo el selector de espacio de búsqueda.

TÉCNICO

Deslizador de 10 a 200, paso 5. Default 40. Eso significa: BayesOpt puede hacer un máximo de N trials. De ellos, los primeros son muestras iniciales (Latin hypercube), el resto son trials BayesOpt reales. Reglas prácticas: un espacio de búsqueda con d dimensiones necesita alrededor de 10*d a 20*d trials para un buen óptimo. En los defaults de 6-dim son 60–120, en el subset Mip de 2-dim 20–40, en la Bowl demo de 1-dim 10–20.

Durante el run el deslizador está deshabilitado.

W37Deslizador "Random Seed"

DÓNDE

Barra lateral izquierda, bajo el deslizador de budget.

TÉCNICO

Deslizador de 1 a 100, paso 1. Default 42. La seed se pasa tanto a las muestras iniciales Latin hypercube como al componente de ruido del objetivo demo. Reproducibilidad: misma seed + mismo espacio de búsqueda + mismo budget da exactamente la misma secuencia de trials. Útil para "¿obtienen todos tus compañeros el mismo run cuando reconstruyen la demo?". Deshabilitado durante el run.

W38Chart (convergencia)

DÓNDE

Columna central de la ventana.

TÉCNICO

Diagrama Swift Charts con dos capas: 1. una línea para "mejor valor hasta el momento" por trial — una curva monotónicamente creciente o constante en color de acento. 2. un punto por trial con el valor objetivo individual, coloreado por fase. Tamaño de símbolo 40. Tres etiquetas de fase: "init" (gris), "bo" (azul), "restart" (naranja).

Una pequeña leyenda muestra los colores de fase arriba a la izquierda. Si la lista de trials está vacía (antes del primer inicio), en su lugar se muestra una visualización empty-state con un icono de chart y el aviso "Press Start to begin a BayesOpt run."

W39Table (log de trials)

DÓNDE

Columna derecha de la ventana.

TÉCNICO

Área de scroll con filas de trial apiladas de forma lazy. Un stack horizontal por fila: número de trial (monoespaciado de 3 dígitos, a la izquierda), valor (monoespaciado, alineado a la derecha, 70 pt de ancho), tag de fase (cápsula, rellena con color de fase al 25 % de opacidad), opcionalmente una estrella amarilla si ese trial es ahora mismo el mejor. Un mecanismo de auto-scroll salta automáticamente al final en cuanto se añade un nuevo trial — así puedes seguir la progresión en vivo al pie de la pantalla sin desplazarte tú mismo.

Ventana principal: curva de loss y número de Gaussianos (I39–I41, referencia cruzada)

Tres de las visualizaciones del Inspector en la ventana principal merecen su propia explicación porque son constantemente visibles durante un entrenamiento en curso y hay reglas prácticas importantes para cuando la curva se ve sana. Las visualizaciones están en el Inspector bajo la sección "Loss Chart" (véase Capítulo 2 — Inspector) y complementan el análisis Holdout de la ventana auxiliar de arriba.

¿Cuándo es sana la curva de loss? Una curva de loss sana muestra tres fases: (1) Warmup — en las primeras 200–500 iteraciones, el loss cae con fuerza desde alto (típicamente 0,15–0,25 para L1+SSIM combinados según la escena) a aproximadamente la mitad. Si el loss NO cae en esta fase, la entrada suele ser incorrecta (imágenes rotas, malas poses SfM, muy pocos Gaussianos iniciales). (2) Densification — entre ~500 y densifyUntilIteration (classic 15K, MCMC hasta 20K o 25K) el loss sigue cayendo, a menudo con pequeños saltos hacia abajo cuando las operaciones densify insertan nuevos Gaussianos y el optimizador los aprovecha. El número de Gaussianos sube en esta fase. (3) Refinement — después de eso el loss entra en una cola que se aplana. Valores finales típicos: Tanks-&-Temples Truck con P4 Quality aterriza en L1 ≈ 0,023, Horse con Full Classic V546 en L1 ≈ 0,0230, escenas exteriores Mip-NeRF360 a menudo peor (0,04–0,07).

¿Qué significa una meseta? Una meseta (la curva de loss corre horizontal durante varios miles de iteraciones) tiene dos interpretaciones: (a) el modelo ha convergido, entrenar más no aportará nada — el buen caso. (b) el modelo está atascado (mínimo local, mala información de gradiente, tope al límite del buffer) — el mal caso. Ambos se ven idénticos en el gráfico. Distinción: mira el número de Gaussianos. Si también está plano Y cerca del tope MCMC (p. ej., 150K de 150K en .fullMCMC), estás en el límite — o sube el tope o acepta la meseta. Si el número de Gaussianos sigue creciendo pero el loss no cae, está atascado.

¿Cuándo abortar vs continuar entrenando? Regla práctica: 10K iteraciones sin mejora del min loss → abortar, las iteraciones siguientes son tiempo perdido. Antes de eso: puedes añadir una extensión vía Cmd+T (menú Training → Continue Training → +5K iterations), si ves una mejora marginal. Atención: con MCMC la meseta es a menudo real — el tope es el límite natural.

Una meseta del número de Gaussianos NO es una señal de "hecho". Solo significa que MCMC ha alcanzado el tope o que la densificación Classic se ha agotado. La pregunta real de "hecho" solo la responde el análisis Holdout — PSNR/SSIM/LPIPS sobre un conjunto de test independiente, evaluado en la ventana Holdout (W23–W29) o mediante el flag –benchmark.

PSNR/Holdout es la verdad, loss es solo un proxy. El loss es una métrica relativa: cae a medida que tu modelo se ajusta a las vistas de entrenamiento. Un loss bajo no significa automáticamente un buen modelo — si el modelo ha memorizado las imágenes de entrenamiento (overfitting), el loss sería pequeño, pero el PSNR sobre vistas no vistas (Holdout) sería malo. Por tanto: para la evaluación final de calidad, siempre mira métricas Holdout, no solo el loss final.

Caja de reglas prácticas

- User Guide y Keyboard Shortcuts son ayuda estática — rápidos para preguntas de palabra clave, para profundidad ten este manual a mano. - Abre Manage Storage en cuanto el disco caiga por debajo del 10 % de espacio libre. Los logs y el staging de imports son los sospechosos habituales. - Pareto Dashboard solo es útil tras al menos tres o cuatro reportes de entrenamiento. Eje X = costes (Time / Gs), eje Y = calidad (PSNR / SSIM). El frente de Pareto muestra las combinaciones eficientes. - Usa Holdout Analysis antes de publicar benchmarks de PSNR con otros — te asegura que tu conjunto de test es realmente representativo. - BayesOpt Console es ante todo una herramienta de aprendizaje y de inspección para definiciones de espacio de búsqueda. Para un tuning real de hiperparámetros dirigido por entrenamiento, usa el flujo CLI offline. - La meseta de loss y la meseta del número de Gaussianos deben interpretarse por separado. El límite del tope no es una señal de "hecho". La calidad real se mide solo por PSNR Holdout. - 10K iteraciones sin mejora del min-loss → detener el entrenamiento.