Automated Hyperparameter Optimisation — LLM Tuning + Grid Search
Hyperparameter Tuning Guidance
When starting with a new dataset, tune parameters in this order — earlier parameters have larger impact and adjusting them makes later parameters more stable.
Step 1 — Validate basic learning:
Set epochs: 1, num_boost_round: 50. The goal is to confirm the pipeline runs end to end and produces a non-random PR-AUC (> 0.5). If AUC stays at 0.5, check data quality: label distribution, feature normalisation, index correctness.
Step 2 — Tune GNN epochs and learning rate: Increase epochs until validation AUC plateaus. If AUC drops after plateau, you are overfitting — stop earlier. Learning rates between 0.001 and 0.01 work well for most datasets.
Step 3 — Tune XGBoost num_boost_round and learning_rate:
A smaller learning rate (0.01–0.05) with more rounds (500–1000) usually outperforms a large rate with fewer rounds. Set log_period: 5 to see per-round progress clearly. Important: when using a slow learning rate, increase early_stopping_rounds proportionally (e.g. early_stopping_rounds: 200) — at LR=0.01 the per-round improvement is very small and the default patience of 50 rounds may trigger early stopping before the model converges.
Step 4 — Tune XGBoost regularisation for overfitting:
If training PR-AUC is much higher than validation PR-AUC, increase min_child_weight (try 5, 10, 20), set gamma to 0.1–1.0, and reduce subsample to 0.7–0.8.
Step 5 — Try a deeper GNN or different encoder:
Once the baseline is stable, experiment with num_gnn_layers: 3 (and a corresponding third value in num_neighbors), or switch to encoder: gat.
Both tools ship inside the container as /workspace/mnmg/llm_tune.sh and /workspace/mnmg/grid_search.py. No extra software is needed on the host — you only need Docker, your data, and a config file.
Strategy Overview
Run both phases sequentially. Phase 1 narrows you down to a good neighbourhood; Phase 2 exhaustively tests the combinations inside that neighbourhood.
Get the Image
Both tools are baked into the image. Pull it from NGC:
Phase 1 — LLM-Guided Iterative Tuning
llm_tune.sh runs N iterations of a train → analyse → propose → apply loop entirely inside the container. Each iteration:
- Trains the model with the current working config
- Saves a per-iteration snapshot:
metrics_iter_NN.json - Tracks the best config by validation PR-AUC — saves it to
best_llm_tuned_config.yamlwhenever a new best is found - Queries an LLM with the parsed training log — the LLM reads loss curves, grad norms, XGBoost eval history, embedding statistics, and validation metrics
- Shows the proposed config changes with a reason for each change
- Asks for approval (
y/N/q) — or applies automatically in--automode - Repeats from step 1 with the improved config
At the end, two config files are written to the output directory:
A metrics comparison table is printed at the end showing val PR-AUC, test PR-AUC, and F1 across all iterations.
config.yaml is never modified — the script copies it to a local working path inside the container at startup. You may mount it with or without :ro.LLM Provider Selection
The script accepts any OpenAI-compatible endpoint. Pass credentials as environment variables. All three LLM_* variables must be set together to activate the custom endpoint; if any is missing the script falls back to the --provider registry (defaults to Anthropic). If an API key is invalid the script retries the next available endpoint automatically.
Automated Mode — NVIDIA NIM (Recommended)
--entrypoint bash must come before the image name — it is a docker run flag, not a command argument. The script /workspace/mnmg/llm_tune.sh is passed as the command after the image name.Substitute the three env vars for your provider:
Anthropic: Use
ANTHROPIC_API_KEY=sk-ant-...instead ofLLM_API_KEY— Anthropic’s API is not OpenAI-compatible and is accessed using the--provider anthropicpath, not theLLM_*env vars.
Interactive Mode (Review Each Suggestion Before Applying)
Drop --auto and replace -i with -it to review and approve each LLM suggestion:
Custom Tuning Prompt
The LLM system prompt is baked into the container at /workspace/mnmg/tune_prompt.txt. The full content is in Appendix C.
To override it without rebuilding the image, extract the default first and edit it:
llm_tune.sh Options Reference
What the Output Looks Like
Use best_llm_tuned_config.yaml (iteration 3 above) as the base for Phase 2 grid search — not final_llm_tuned_config.yaml.
Phase 2 — Grid Search
grid_search.py runs a Cartesian product of GNN and XGBoost parameter values. Each trial writes its own config to /tmp/grid_search/trial_NNN/config.yaml inside the container and saves training output to output/grid_search/trial_NNN/. At the end — and after any interruption — a ranked leaderboard is written to output/grid_search/leaderboard.json and leaderboard.csv.
The config file is read-only for grid search — mount it with :ro.
With the Built-In Default Grid
The default grid tests a narrow range around a typical starting config:
With a Custom Grid File
Create grid.yaml on your host — list only the parameters you want to vary:
Limit Trial Count With Random Sampling
When the Cartesian product is too large, --max-trials randomly samples a subset:
Resume an Interrupted Run
--resume works at the grid level, not per-trial. When passed, grid_search.py checks every trial’s output directory for a log containing "Training complete!". Trials that have it are skipped; all others are re-run from scratch.
Example — grid interrupted at trial 7 of 20:
--resume does not resume a trial mid-epoch. A trial that was killed halfway through GNN training restarts from epoch 1. To avoid repeating the GNN phase, set skip_gnn_train: true in the base config.yaml before resuming (only safe if a completed GNN checkpoint exists in checkpoint_dir or output_dir).Re-Run a Specific Trial
Each trial saves its full merged config (base config + trial overrides) to output/grid_search/trial_NNN/training_config.<ext> where <ext> matches the extension of the base config (.yaml, .yml, or .json). Use it to re-run that exact trial independently:
This is useful for verifying the best trial with a longer run or different random seed without re-running the full grid.
Dry-Run — Preview the Trial Plan Without Training
grid_search.py Options Reference
Searchable Parameters
GNN (gnn section in grid YAML): hidden_channels, num_gnn_layers, encoder, heads, concat, epochs, batch_size, learning_rate
num_neighborsis auto-generated fromnum_gnn_layerswhen not listed explicitly — each additional layer halves the previous neighbour count.
XGBoost (xgb section in grid YAML): num_boost_round, num_parallel_tree, max_depth, learning_rate, subsample, colsample_bytree, min_child_weight, gamma
Output Structure
Leaderboard Sample
Copy the hyperparameters from the top-ranked row into your config.yaml for production training.
Recommended Two-Phase Workflow
Running with Your Dataset — Single Node
This section gives ready-to-run commands. Replace the three host paths shown below with your actual locations — everything else is handled automatically.
Step 0 — Verify GPU Count
Check how many GPUs are visible to the container before you start:
Then set num_gpus in your config.yaml to match. Both Phase 1 and Phase 2 read this value automatically — no flags needed.
Step 1 — Phase 1: LLM Tuning on Your Data
The script copies the mounted config to an internal working path at startup and never writes back to the mount. You may mount config with or without :ro. At the end of the loop, two configs are written to the output directory: best_llm_tuned_config.yaml (highest val PR-AUC) and final_llm_tuned_config.yaml (last iteration).
Each iteration prints the proposed changes and asks [y/N/q]. Add --auto (and replace -it with -i) to apply all suggestions without prompts:
When the container exits, the tuned configs are in the output directory: /path/to/your/output/best_llm_tuned_config.yaml (best by val PR-AUC) and /path/to/your/output/final_llm_tuned_config.yaml (last iteration). The original /path/to/your/config.yaml is unchanged.
Step 2 — Phase 2: Grid Search on Your Data
Create grid.yaml on your host with values to test around whatever Phase 1 settled on:
Config is mounted with :ro — grid search writes per-trial configs internally and never modifies the base config.
Results land in /path/to/your/output/grid_search/:
Step 3 — Final Production Training With the Best Config
Copy the top-ranked trial’s hyperparameters into config.yaml, then run the standard entrypoint: