sophialan commited on
Commit
a525f5a
·
verified ·
1 Parent(s): 3ab6eee

PG-MAP NeurIPS 2026 — Gradio demo v1.0

Browse files
Files changed (3) hide show
  1. README.md +39 -8
  2. app.py +172 -0
  3. requirements.txt +13 -0
README.md CHANGED
@@ -1,13 +1,44 @@
1
  ---
2
- title: Pg Map Demo
3
- emoji: 🏃
4
- colorFrom: red
5
- colorTo: red
6
  sdk: gradio
7
- sdk_version: 6.14.0
8
- python_version: '3.13'
9
  app_file: app.py
10
- pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: PG-MAP Demo
3
+ emoji: 🎨
4
+ colorFrom: indigo
5
+ colorTo: pink
6
  sdk: gradio
7
+ sdk_version: 4.44.0
 
8
  app_file: app.py
9
+ pinned: true
10
+ license: mit
11
+ short_description: PG-MAP inference-time alignment (NeurIPS 2026)
12
  ---
13
 
14
+ # PG-MAP Demo · NeurIPS 2026
15
+
16
+ Interactive demo for **PG-MAP** (Preference-Guided Adaptive MAP) — a training-free framework that re-optimizes the conditioning $c$ and the latent $z_t$ at every denoising step. Supports SD 1.5, SDXL, and SD3.5-medium (UG-FM) backbones.
17
+
18
+ - 🔗 **Paper** (NeurIPS 2026, anonymous): [github.com/sophialanlan/PG-MAP](https://github.com/sophialanlan/PG-MAP)
19
+ - 🤗 **Custom diffusers pipelines**: [pg-map-sd15](https://huggingface.co/sophialan/pg-map-sd15) · [pg-map-sdxl](https://huggingface.co/sophialan/pg-map-sdxl) · [pg-map-sd3](https://huggingface.co/sophialan/pg-map-sd3)
20
+ - 📦 **PyPI**: `pip install pg-map`
21
+
22
+ ## Hardware
23
+
24
+ Tested on A10G small (24 GB VRAM). SDXL and SD3.5 need a GPU runtime — go to **Settings → Hardware** and select A10G or larger. SD 1.5 fits on T4.
25
+
26
+ ## Local development
27
+
28
+ ```bash
29
+ git clone https://huggingface.co/spaces/sophialan/pg-map-demo
30
+ cd pg-map-demo
31
+ pip install -r requirements.txt
32
+ python app.py
33
+ ```
34
+
35
+ ## Citation
36
+
37
+ ```bibtex
38
+ @inproceedings{sun2026pgmap,
39
+ title={{PG-MAP}: Joint {MAP} Optimization for Inference-Time Alignment of Diffusion and Flow-Matching Models},
40
+ author={Sun, Ruolan and Polak, Pawel},
41
+ booktitle={Advances in Neural Information Processing Systems (NeurIPS)},
42
+ year={2026}
43
+ }
44
+ ```
app.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PG-MAP demo Space — Gradio app for SD 1.5 / SDXL / SD3.5-medium.
2
+
3
+ Single-file Gradio app deployed at https://huggingface.co/spaces/sophialan/pg-map-demo.
4
+
5
+ Pipeline is loaded lazily on first use (per backbone) so the Space spins up
6
+ without immediately downloading 7 GB of weights. Backbones are unloaded
7
+ when the user switches via the dropdown to keep VRAM under the A10G
8
+ small-tier 24 GB budget.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import gc
13
+ import os
14
+ from dataclasses import replace
15
+
16
+ import gradio as gr
17
+ import torch
18
+
19
+
20
+ # Lazy loading: only instantiate when the user picks a backbone.
21
+ _PIPE = {"backbone": None, "obj": None}
22
+
23
+
24
+ def _load(backbone: str):
25
+ """Load (or swap) the appropriate PG-MAP pipeline."""
26
+ if _PIPE["backbone"] == backbone and _PIPE["obj"] is not None:
27
+ return _PIPE["obj"]
28
+
29
+ # Free the previous backbone first
30
+ if _PIPE["obj"] is not None:
31
+ del _PIPE["obj"]
32
+ _PIPE["obj"] = None
33
+ gc.collect()
34
+ torch.cuda.empty_cache()
35
+
36
+ from diffusers import DiffusionPipeline
37
+ spec = {
38
+ "SD 1.5 (512²)": ("runwayml/stable-diffusion-v1-5", "sophialan/pg-map-sd15", {}),
39
+ "SDXL (1024²)": ("stabilityai/stable-diffusion-xl-base-1.0","sophialan/pg-map-sdxl", {"variant": "fp16"}),
40
+ "SD3.5-medium (1024²)": ("stabilityai/stable-diffusion-3.5-medium", "sophialan/pg-map-sd3", {}),
41
+ }[backbone]
42
+ model_id, custom_pipe, extra = spec
43
+ pipe = DiffusionPipeline.from_pretrained(
44
+ model_id,
45
+ custom_pipeline=custom_pipe,
46
+ torch_dtype=torch.float16,
47
+ safety_checker=None,
48
+ requires_safety_checker=False,
49
+ **extra,
50
+ ).to("cuda" if torch.cuda.is_available() else "cpu")
51
+ _PIPE["backbone"] = backbone
52
+ _PIPE["obj"] = pipe
53
+ return pipe
54
+
55
+
56
+ def generate(prompt, backbone, seed, steps, guidance, lambda_reward, eta_z, K_inner,
57
+ enable_pgmap, progress=gr.Progress()):
58
+ """Run a single generation."""
59
+ if not prompt or not prompt.strip():
60
+ return None, "Please enter a prompt."
61
+
62
+ if not torch.cuda.is_available():
63
+ return None, ("This demo needs a CUDA GPU. The Space is configured with the A10G "
64
+ "small tier — go to **Settings → Hardware** and pick a GPU runtime.")
65
+
66
+ progress(0.0, desc=f"Loading {backbone}…")
67
+ try:
68
+ pipe = _load(backbone)
69
+ except Exception as e:
70
+ return None, f"Pipeline load failed: {e!r}"
71
+
72
+ progress(0.2, desc="Running PG-MAP…" if enable_pgmap else "Running baseline…")
73
+
74
+ g = torch.Generator(device="cuda").manual_seed(int(seed))
75
+
76
+ if not enable_pgmap:
77
+ out = pipe(
78
+ prompt=prompt,
79
+ num_inference_steps=int(steps),
80
+ guidance_scale=float(guidance),
81
+ generator=g,
82
+ )
83
+ return out.images[0], f"Vanilla {backbone} baseline (no PG-MAP)."
84
+
85
+ # PG-MAP path
86
+ from pgmap_config import (
87
+ sd15_defaults, sdxl_defaults,
88
+ )
89
+ presets = {
90
+ "SD 1.5 (512²)": sd15_defaults,
91
+ "SDXL (1024²)": sdxl_defaults,
92
+ "SD3.5-medium (1024²)": sdxl_defaults, # SD3.5 reads K_inner / eta_z from config too
93
+ }
94
+ cfg = presets[backbone]()
95
+ cfg = replace(cfg, num_steps=int(steps), seed=int(seed), guidance_scale=float(guidance))
96
+ cfg.refinement.K = int(K_inner)
97
+ cfg.refinement.eta_z = float(eta_z)
98
+ cfg.reward.lambda_reward = float(lambda_reward)
99
+ # For SD3.5 default to UG-FM (z-only, data-side); to switch to full PG-MAP-FM, set optimize_c.
100
+ if backbone.startswith("SD3.5"):
101
+ cfg.optimize_c = False
102
+ cfg.optimize_z = True
103
+
104
+ out = pipe(
105
+ prompt=prompt,
106
+ pg_map_config=cfg,
107
+ num_inference_steps=int(steps),
108
+ guidance_scale=float(guidance),
109
+ )
110
+ return out.images[0], f"PG-MAP on {backbone}: λ={lambda_reward}, η_z={eta_z}, K={K_inner}"
111
+
112
+
113
+ DESCRIPTION = """\
114
+ # PG-MAP Demo  ·  NeurIPS 2026
115
+
116
+ **Inference-time alignment for diffusion + flow-matching** — re-optimize the
117
+ conditioning $c$ and the latent $z_t$ at every denoising step under a
118
+ trajectory-level Gibbs-MAP / proximal energy objective. No training required.
119
+
120
+ 🔗 Code: [github.com/sophialanlan/PG-MAP](https://github.com/sophialanlan/PG-MAP) · Paper: NeurIPS 2026 (anonymous review materials in repo) · HF Pipelines: [sd15](https://huggingface.co/sophialan/pg-map-sd15) · [sdxl](https://huggingface.co/sophialan/pg-map-sdxl) · [sd3](https://huggingface.co/sophialan/pg-map-sd3)
121
+
122
+ Pick a backbone, write a prompt, hit **Generate**. Toggle PG-MAP off to compare against the static baseline at the same seed. Default hyperparameters match the paper table; the sliders expose the productive ranges.
123
+ """
124
+
125
+ EXAMPLES = [
126
+ ["a phoenix rising from ashes, vivid orange and red feathers, dramatic lighting"],
127
+ ["a tea cup with a tiny galaxy swirling inside"],
128
+ ["a cinematic photo of a red panda astronaut in a white space suit"],
129
+ ["an old sailboat sailing through a thunderstorm with massive lightning bolts overhead"],
130
+ ["a swordsman mid-leap slashing through a glowing magical barrier"],
131
+ ]
132
+
133
+
134
+ def build_app():
135
+ with gr.Blocks(title="PG-MAP Demo · NeurIPS 2026", theme=gr.themes.Soft()) as demo:
136
+ gr.Markdown(DESCRIPTION)
137
+ with gr.Row():
138
+ with gr.Column(scale=2):
139
+ prompt = gr.Textbox(label="Prompt", lines=2, max_lines=4,
140
+ placeholder="a phoenix rising from ashes…")
141
+ with gr.Row():
142
+ backbone = gr.Dropdown(
143
+ ["SD 1.5 (512²)", "SDXL (1024²)", "SD3.5-medium (1024²)"],
144
+ value="SDXL (1024²)", label="Backbone",
145
+ )
146
+ enable = gr.Checkbox(value=True, label="Enable PG-MAP (uncheck = vanilla baseline)")
147
+ with gr.Accordion("Generation settings", open=False):
148
+ seed = gr.Number(value=42, label="Seed", precision=0)
149
+ steps = gr.Slider(8, 60, value=30, step=1, label="Denoising steps")
150
+ guidance = gr.Slider(1.0, 15.0, value=5.0, step=0.5, label="CFG scale")
151
+ with gr.Accordion("PG-MAP hyperparameters", open=False):
152
+ lambda_reward = gr.Slider(0.0, 0.5, value=0.10, step=0.01,
153
+ label="λ (reward weight)")
154
+ eta_z = gr.Slider(0.0, 0.5, value=0.005, step=0.001,
155
+ label="η_z (latent step size)")
156
+ K_inner = gr.Slider(1, 6, value=2, step=1,
157
+ label="K (inner gradient steps per denoising step)")
158
+ btn = gr.Button("Generate", variant="primary")
159
+ gr.Examples(EXAMPLES, inputs=prompt)
160
+ with gr.Column(scale=3):
161
+ out_img = gr.Image(label="Output", height=512)
162
+ out_status = gr.Markdown()
163
+ btn.click(
164
+ generate,
165
+ inputs=[prompt, backbone, seed, steps, guidance, lambda_reward, eta_z, K_inner, enable],
166
+ outputs=[out_img, out_status],
167
+ )
168
+ return demo
169
+
170
+
171
+ if __name__ == "__main__":
172
+ build_app().queue().launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Gradio Space dependencies. PG-MAP installs from the GitHub release at the
2
+ # pinned v1.2.0 tag so the demo behavior is reproducible.
3
+ gradio>=4.0.0
4
+ torch>=2.1.0
5
+ torchvision>=0.16.0
6
+ diffusers>=0.30.0,<0.32.0
7
+ transformers>=4.40.0
8
+ accelerate>=0.30.0
9
+ safetensors>=0.4.0
10
+ open-clip-torch>=2.24.0
11
+ numpy<2.0
12
+ Pillow>=10.0.0
13
+ git+https://github.com/sophialanlan/PG-MAP@v1.2.0