STEVE-1 / README.md
ghosteau's picture
Update README with GPU information
bffb9ed verified
|
Raw
History Blame Contribute Delete
6.06 kB
---
license: mit
tags:
- minecraft
- terrain-generation
- vae
- onnx
- voxel
- film
- transfer-learning
- games
datasets:
- ghosteau/minecraft-chunks
pipeline_tag: other
---
# STEVE-1 β€” Style-conditioned Terrain Encoder VAE
STEVE-1 generates full Minecraft chunks (16 x 384 x 16 voxels, bedrock to build limit) from **latent noise + a biome ID + a style ID** in a **single ONNX forward pass** β€” fast enough to run inside a live PaperMC server. It is also a **pretrained base for transfer learning**: anyone can fine-tune a new terrain *style* onto it from a folder of their own world's chunks, in minutes, training ~1.8% of the weights, without touching the base.
- **Training pipeline, plugin, and fine-tuning notebook:** [github.com/ghosteau/generative-terrain](https://github.com/ghosteau/generative-terrain)
- **Training data:** [ghosteau/minecraft-chunks](https://huggingface.co/datasets/ghosteau/minecraft-chunks) (2,040 chunks, ~200M voxels, 22 biomes)
## Architecture
A conditional VAE built around one hard constraint: in-game inference must be a single fast graph call (no diffusion, no autoregression).
| Component | What it does |
|---|---|
| Spatial latent `z` `[8, 4, 16, 4]` | A coarse 3D latent grid instead of one broadcast vector, so terrain varies from place to place (hills, valleys, caves) |
| Heightmap stage | The decoder first predicts a per-column surface height, then feeds a signed-distance-to-surface channel to the voxel head β€” the model commits to a surface instead of hedging |
| 3-level 3D U-Net (~4.6M params) | Chunk-scale receptive field: at the bottleneck the chunk is 2 x 48 x 2, so horizontal context is global |
| FiLM style conditioning | Every U-Net block and the heightmap head is modulated (per-channel scale/shift) by a style embedding. Style 0 = vanilla; 8 slots reserved for custom styles. This is the fine-tuning mechanism |
| 27 block groups | The model predicts semantic groups (`STONE`, `GRASS`, `WATER`, ores, ...); the plugin expands them to concrete blocks with local context |
Conditioning uses **only what exists before terrain does** (position, biome, noise, style). It deliberately never sees neighbour blocks, light, or surface flags β€” the data-leakage mistake that sank earlier versions of this project.
Training: EMA weights (exported), mixed precision, warmup + cosine LR, KL annealing with free bits, biome-balanced sampling, and biome dropout so a reserved `UNKNOWN` biome row learns generic terrain (that is what makes fine-tuning work on custom worlds with unrecognised biomes).
## Files
| File | Purpose |
|---|---|
| `terrain_vae_decoder.onnx` | The generator: `(z, biome_id, style_id) -> logits [1, 27, 16, 384, 16]` |
| `block_group_mapping.json` | Class id -> block group name |
| `biome_id_mapping.json` | Biome name -> id (includes `UNKNOWN`) |
| `style_mapping.json` | Style name -> id (`BASE` = 0) |
| `base_v1/` | The versioned base checkpoint for fine-tuning: PyTorch weights + a manifest with the exact architecture and mappings |
## Use it in a Minecraft server
Drop `terrain_vae_decoder.onnx` and the three JSON mappings into `<server>/plugins/GenerativeTerrain/` (plugin jar from the [GitHub repo](https://github.com/ghosteau/generative-terrain)), then in game:
```
/generateterrain base style, current chunk
/generateterrain style <name> a fine-tuned custom style
```
## Use it from Python
```python
import numpy as np
import onnxruntime as ort
from huggingface_hub import hf_hub_download
sess = ort.InferenceSession(hf_hub_download("ghosteau/STEVE-1", "terrain_vae_decoder.onnx"))
logits = sess.run(["logits"], {
"z": np.random.randn(1, 8, 4, 16, 4).astype(np.float32), # the variety
"biome_id": np.array([15], dtype=np.int64), # PLAINS (see biome_id_mapping.json)
"style_id": np.zeros(1, dtype=np.int64), # 0 = BASE
})[0]
chunk = logits.argmax(1)[0] # [16, 384, 16] block-group ids, Y index 0 = world Y -64
```
Sample a fresh `z` per chunk; scale it (`z * 0.8`) for tamer terrain, (`z * 1.2`) for wilder.
## Fine-tune it on your own terrain
This is the headline feature. The `base_v1/` directory is a self-contained transfer-learning checkpoint: weights plus a manifest that the pipeline's `fine_tune()` uses to rebuild the exact architecture, so version mismatches are impossible.
1. Export 50+ chunks of your world with the plugin: `/grabchunkarea 5`
2. Clone the [GitHub repo](https://github.com/ghosteau/generative-terrain), open `ml/notebooks/FineTune.ipynb`
3. Point it at `base_v1/`, your CSV folder, and a style name; run it
Fine-tuning trains the new style vector and the FiLM projections (~84k of 4.6M parameters) while everything else stays frozen β€” a unit test in the repo asserts the base weights come out bit-identical. The exported ONNX then carries both the vanilla style and yours, selectable per command. Up to 8 custom styles fit in one model.
## Training results
Vertical block-distribution profile, real vs generated (the terrain-plausibility check β€” bedrock floor, deepslate/stone body, thin surface band, air above):
![Real vs generated vertical profiles](assets/profiles.png)
Training curves (EMA-validated loss):
![Training curves](assets/vae_curves.png)
*Note: this was trained on my personal NVIDIA GeForce RTX 4070.*
## Limitations
- **Chunks generate independently** β€” edges between adjacent generated chunks do not line up yet. Cross-chunk edge conditioning is the next planned lever.
- **Rare biomes are undertrained**: the dataset has 1-5 chunks of TAIGA, FLOWER_FOREST, and SWAMP; those styles lean on the balanced sampler and will look generic.
- Trees and vegetation are coarse (single log/leaf groups expanded with heuristics by the plugin).
- Ores are placed procedurally after generation (as vanilla does), not modelled.
## License and attribution
MIT. Not affiliated with or endorsed by Mojang or Microsoft; "Minecraft" is a trademark of Mojang Synergies AB. The name STEVE is, of course, for Steve.