File size: 20,740 Bytes
47cf0df 91bfeb5 47cf0df 13490a2 47cf0df ba06200 47cf0df 5714d4a 47cf0df 5023a8e 47cf0df 91bfeb5 47cf0df 5023a8e 47cf0df ba06200 47cf0df f19e63b 47cf0df f19e63b 47cf0df 13490a2 91bfeb5 47cf0df f19e63b 91bfeb5 47cf0df 864fbd7 47cf0df 13490a2 47cf0df 13490a2 47cf0df 13490a2 47cf0df f19e63b 47cf0df 13490a2 47cf0df ba06200 47cf0df f19e63b 47cf0df 91bfeb5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | # Canter API and inference parameters
[Model card](README.md) · [Example gallery](GALLERY.md) ·
[Technical report](TECHNICAL_REPORT.md)
Canter exposes a high-level image pipeline and a lower-level latent inference
engine. Configuration uses frozen dataclasses and enums.
The installed `canter` package always supplies the inference implementation.
For remote model IDs, `revision` selects checkpoint artifacts only; selecting
an older checkpoint does not load or execute the Python package bundled in
that historical repository snapshot.
## Minimal use
```python
from canter import CanterPipeline
pipe = CanterPipeline.from_pretrained("data-archetype/canter")
output = pipe("A red rally car driving around a wet forest road")
image = output.image
```
### Generation arguments
| Parameter | Default | Description |
| --- | --- | --- |
| `prompts` | required | One prompt string or a sequence containing one prompt per image. |
| `negative_prompts` | `None` | Optional negative prompt string or sequence for CFG and contrastive PDG. The count must match `prompts`. `None` uses the learned unconditional token. |
| `config` | `CanterPipelineConfig()` | Inference and output settings. |
| `initial_noise` | `None` | Optional float32 latent noise tensor with the configured batch and spatial shape. |
| `progress` | `None` | Optional callback receiving completed and total solver updates. |
| `preview` | `None` | Optional callback receiving asynchronous latent-RGB PIL images plus completed and total solver updates. |
`CanterPipeline` loads the flow-matching denoiser and text tokenizer with the
bundled
[`SmolLM2-360M`](https://huggingface.co/HuggingFaceTB/SmolLM2-360M) weights. It
compiles the selected text backend, downloads DINAC-AE-D2, generates latents,
and decodes them into images.
## Loading the pipeline
```python
from canter import CanterPipeline, TextAttentionBackend, WeightDType
pipe = CanterPipeline.from_pretrained(
"data-archetype/canter",
dtype=WeightDType.BFLOAT16,
text_backend=TextAttentionBackend.DENSE,
device="cuda",
revision=None,
cache_dir=None,
compile_model=True,
)
```
### `CanterPipeline.from_pretrained`
| Parameter | Default | Description |
| --- | --- | --- |
| `path_or_repo_id` | required | A Hugging Face repository ID or downloaded model repository directory. |
| `dtype` | `WeightDType.BFLOAT16` | Weight storage dtype. Compute still uses bfloat16 AMP with explicit float32 operations. |
| `text_backend` | `TextAttentionBackend.DENSE` | Bundled text-encoder, text-refinement, and cross-attention layout. |
| `device` | `"cuda"` | CUDA device string or `torch.device`. |
| `revision` | `None` | Hugging Face checkpoint branch, commit, or immutable release tag. `None` uses the installed package's pinned default checkpoint. An explicit tag such as `"v0001"` loads those weights with the currently installed code. |
| `cache_dir` | `None` | Optional Hugging Face cache directory. |
| `compile_model` | `True` | Compile the selected inference kernels. Compilation errors are reported directly. |
| `vae` | `None` | Optional compatible `CanterVae` instance. `None` downloads DINAC-AE-D2 automatically. |
### Weight dtypes
| Enum | Value | Description |
| --- | --- | --- |
| `WeightDType.BFLOAT16` | `"bfloat16"` | Default compact release with required float32 parameter islands retained. |
| `WeightDType.FLOAT32` | `"float32"` | Full-float32 weight storage release. Model compute remains bfloat16 AMP. |
### Text backends
| Enum | Value | Description |
| --- | --- | --- |
| `TextAttentionBackend.DENSE` | `"dense"` | Compatibility default using padded tensors and standard PyTorch scaled dot-product attention throughout text encoding and denoising. |
| `TextAttentionBackend.JAGGED` | `"jagged"` | Explicit optimization using packed text, CUDA FlashAttention, and jagged NestedTensor kernels throughout text encoding and denoising. |
Only the selected backend is compiled. The backend cannot be changed after
compilation.
## Pipeline configuration
```python
from canter import (
CanterInferenceConfig,
CanterOutputType,
CanterPipelineConfig,
)
config = CanterPipelineConfig(
inference=CanterInferenceConfig(),
output_type=CanterOutputType.PIL,
)
output = pipe("A portrait lit by a large north-facing window", config=config)
```
### `CanterPipelineConfig`
| Field | Default | Description |
| --- | --- | --- |
| `inference` | `CanterInferenceConfig()` | Latent generation settings. |
| `output_type` | `CanterOutputType.PIL` | Selected decoded or latent output representation. |
### Output types
| Enum | Result |
| --- | --- |
| `CanterOutputType.PIL` | `output.images` contains one PIL image per prompt. `output.image` returns the sole image for batch size one. |
| `CanterOutputType.TENSOR` | `output.image_tensor` contains clamped float32 RGB pixels in `[-1, 1]`. |
| `CanterOutputType.LATENT` | VAE decoding is skipped. `output.latents` contains whitened float32 model latents. |
Every output also contains the descending float32 solver schedule in
`output.schedule`.
## Live latent previews
```python
def show_preview(images, completed, total):
images[0].show()
output = pipe(
"A lighthouse in a winter storm",
preview=show_preview,
)
```
The bundled float32 linear projection maps the whitened 128-channel DINAC
latent directly to RGB at one eighth of the requested image size. Every solver
update is eligible by default. If the previous preview is still transferring,
converting, or running the callback, the new update is dropped instead of
blocking sampling. The callback receives this native one-eighth-size image;
the web UI lets the browser scale it for display.
Accepted previews perform only a 1×1 projection and pixel shuffle on CUDA.
The small RGB result is copied non-blockingly into pinned host memory. CUDA
event waiting, PIL conversion, and the application callback all run on a
single background worker. Every step is eligible; the one-worker busy-drop
policy supplies backpressure without a separate callback cap.
## Inference configuration
### Defaults
```python
from canter import (
CanterInferenceConfig,
CfgGuidance,
PdgCurve,
PdgGuidance,
PdgMode,
Schedule,
Solver,
)
config = CanterInferenceConfig(
height=1216,
width=832,
steps=50,
solver=Solver.ABM2,
schedule=Schedule.BETA,
log_snr_shift=0.0,
cfg=CfgGuidance(
enabled=False,
scale=None,
start_step=0,
stop_step=None,
),
pdg=PdgGuidance(
enabled=True,
mode=PdgMode.FULL,
curve=PdgCurve.CONSTANT,
noisy_scale=2.5,
clean_scale=2.5,
power=3.0,
start_step=0,
stop_step=None,
),
self_attention_gain=-0.03,
sde_noise_multiplier=1.0,
seed=42,
generator=None,
)
```
### `CanterInferenceConfig`
| Field | Default | Description |
| --- | --- | --- |
| `height` | `1216` | Output height in pixels. Must be positive and divisible by 16. |
| `width` | `832` | Output width in pixels. Must be positive and divisible by 16. |
| `steps` | `50` | Number of solver state updates. A run with 50 updates uses 51 schedule points. |
| `solver` | `Solver.ABM2` | Numerical solver. |
| `schedule` | `Schedule.BETA` | Timestep spacing. |
| `log_snr_shift` | `0.0` | Additive log-SNR schedule shift. Zero leaves the selected schedule unchanged. |
| `cfg` | disabled | Classifier-free guidance settings. |
| `pdg` | full-path PDG 2.5 | Path-drop guidance settings. |
| `self_attention_gain` | `-0.03` | Gain applied exclusively to image self-attention on the main denoiser path. |
| `sde_noise_multiplier` | `1.0` | Shared non-negative stochastic noise multiplier used by Euler-Maruyama and ER-SDE. |
| `seed` | `42` | Random seed. Set to `None` when supplying `generator`. |
| `generator` | `None` | Optional CUDA `torch.Generator` on the same device as the model. Exactly one of `seed` and `generator` is required. |
The seed controls latent noise, SPRINT routing, and stochastic solver
operations. The VAE decoder retains its published seed behavior.
### PNG metadata
Images downloaded from the Gradio interface contain a `canter` PNG text field
with compact JSON. The object begins with the prompt, optional negative prompt,
effective per-image seed, width, height, steps, solver, and schedule. It then
records PDG, CFG,
self-attention gain, logSNR shift, the Euler-Maruyama and ER-SDE noise
multipliers, installed code version, numbered checkpoint release, and weight
dtype.
### Solvers
| Enum | Value | Description |
| --- | --- | --- |
| `Solver.EULER` | `"euler"` | First-order deterministic Euler updates. |
| `Solver.EULER_MARUYAMA` | `"euler_maruyama"` | Stochastic reverse-SDE updates. Uses `sde_noise_multiplier`. |
| `Solver.ER_SDE` | `"er_sde"` | Third-stage VP ER-SDE with 16-point Gauss-Legendre correction quadrature. Uses `sde_noise_multiplier`. |
| `Solver.DPMPP_2M` | `"dpmpp_2m"` | Flow-matching DPM++ 2M updates with finite pre-shift endpoint handling. |
| `Solver.ABM2` | `"abm2"` | Variable-step Adams-Bashforth-Moulton updates with corrected-state reevaluation. |
ABM2 is the default and performs additional denoiser evaluations for its
corrected states.
### Schedules
| Enum | Value | Description |
| --- | --- | --- |
| `Schedule.LINEAR` | `"linear"` | Uniform spacing from noisy to clean. |
| `Schedule.BETA` | `"beta"` | Beta(0.6, 0.6) quantile spacing with more schedule density near the endpoints. |
Schedules and solver state remain float32.
In the bundled web interface, selecting Beta(0.6, 0.6) resets the log-SNR shift
to `0.0`; selecting Linear resets it to `-2.3`. The shift remains independently
editable after either preset is applied. Direct API configurations use the
explicit `log_snr_shift` supplied by the caller.
## CFG
```python
from canter import (
CanterInferenceConfig,
CanterPipelineConfig,
CfgGuidance,
PdgGuidance,
PdgMode,
)
cfg = CfgGuidance(
enabled=True,
scale=3.0,
start_step=0,
stop_step=29,
)
output = pipe(
"A studio portrait with soft natural light",
negative_prompts="oversaturated, harsh contrast",
config=CanterPipelineConfig(
inference=CanterInferenceConfig(
cfg=cfg,
pdg=PdgGuidance(mode=PdgMode.COMBINED_CFG_PDG),
),
),
)
```
### `CfgGuidance`
| Field | Default | Description |
| --- | --- | --- |
| `enabled` | `False` | Enable classifier-free guidance. |
| `scale` | `None` | Non-negative guidance scale. Required when CFG is enabled or when the selected PDG mode uses CFG. |
| `start_step` | `0` | First active solver update, inclusive. |
| `stop_step` | `None` | Last active solver update, inclusive. `None` selects the final update. |
Step indices run from `0` through `steps - 1`.
An explicit negative prompt replaces the learned unconditional token on CFG
and contrastive PDG branches. Negative prompts require enabled CFG or a PDG
mode that consumes contrastive text. For prompt batches, supply one negative
prompt per positive prompt.
## PDG
```python
from canter import PdgCurve, PdgGuidance, PdgMode
pdg = PdgGuidance(
enabled=True,
mode=PdgMode.FULL,
curve=PdgCurve.POWER,
noisy_scale=2.0,
clean_scale=2.5,
power=3.0,
start_step=0,
stop_step=None,
)
```
### `PdgGuidance`
| Field | Default | Description |
| --- | --- | --- |
| `enabled` | `True` | Enable path-drop guidance. |
| `mode` | `PdgMode.FULL` | Path and CFG interaction policy. |
| `curve` | `PdgCurve.CONSTANT` | Scale interpolation from noisy to clean. |
| `noisy_scale` | `2.5` | PDG scale at the first noisy schedule point. |
| `clean_scale` | `2.5` | PDG scale at the final clean schedule point. |
| `power` | `3.0` | Positive exponent used by the power curve. |
| `start_step` | `0` | First active solver update, inclusive. |
| `stop_step` | `None` | Last active solver update, inclusive. `None` selects the final update. |
Constant PDG requires equal `noisy_scale` and `clean_scale`. Disabled PDG
requires `enabled=False` and `mode=PdgMode.NONE`.
Increasing the PDG scale sharpens the generated distribution. Moderate values
improve image structure and fine detail at the cost of reduced variety.
Pushing the scale too far can introduce structural defects, excessive
contrast, and oversaturation.
### PDG curves
| Enum | Value | Scale behavior |
| --- | --- | --- |
| `PdgCurve.CONSTANT` | `"constant"` | Uses one scale throughout inference. |
| `PdgCurve.LINEAR` | `"linear"` | Interpolates linearly from `noisy_scale` to `clean_scale`. |
| `PdgCurve.POWER` | `"power"` | Interpolates with `position ** power`. |
### PDG modes
| Enum | Value | Behavior |
| --- | --- | --- |
| `PdgMode.NONE` | `"none"` | No PDG branch. Required when PDG is disabled. |
| `PdgMode.FULL` | `"full"` | Guides from the middle-skipped path toward the full main path. |
| `PdgMode.FULL_CONTRASTIVE` | `"full_contrastive"` | Uses negative text on the middle-skipped path, or learned unconditional text when no negative prompt is supplied. |
| `PdgMode.THREE_QUARTER` | `"three_quarter"` | Guides from the 75 percent SPRINT path toward the full main path. |
| `PdgMode.ALTERNATE_PDG_FIRST` | `"alternate_pdg_first"` | Alternates PDG on even updates and CFG on odd updates within the PDG window. |
| `PdgMode.ALTERNATE_CFG_FIRST` | `"alternate_cfg_first"` | Alternates CFG on even updates and PDG on odd updates within the PDG window. |
| `PdgMode.COMBINED_CFG_PDG` | `"combined_cfg_pdg"` | Evaluates CFG and PDG together and averages their guidance deltas. |
| `PdgMode.PDG_WITH_ALTERNATING_CFG` | `"pdg_with_alternating_cfg"` | Applies PDG on every active update and adds CFG on odd updates. |
| `PdgMode.CFG_TO_PDG` | `"cfg_to_pdg"` | Uses CFG before the PDG window, then uses full-path PDG inside the window. |
The five compound modes require `cfg.scale`. `PdgMode.FULL_CONTRASTIVE` uses
the PDG scale only. `start_step=0` with `PdgMode.CFG_TO_PDG` begins directly
with PDG.
`pdg_branch_conditioning(mode)` returns `PdgBranchConditioning.POSITIVE` or
`PdgBranchConditioning.CONTRASTIVE` for the selected PDG alternative path.
## Self-attention gain
`self_attention_gain` changes image self-attention only on the full main path.
The model multiplies main-path attention queries by
`exp(self_attention_gain)`. Negative values reduce the attention-logit scale
and therefore increase the effective softmax temperature. This softens the
main prediction and helps moderate PDG oversaturation. Text self-attention,
cross-attention, and weak guidance paths retain their trained scales.
The default is `-0.03`. A value of `0.0` uses the trained main-path
self-attention scale without adjustment. Smaller images generally benefit from
more negative values. As image size increases, the gain should move closer to
zero.
## Custom configuration example
```python
from canter import (
CanterInferenceConfig,
CanterOutputType,
CanterPipelineConfig,
CfgGuidance,
PdgCurve,
PdgGuidance,
PdgMode,
Schedule,
Solver,
)
inference = CanterInferenceConfig(
height=1024,
width=1024,
steps=40,
solver=Solver.DPMPP_2M,
schedule=Schedule.LINEAR,
log_snr_shift=0.5,
cfg=CfgGuidance(
enabled=True,
scale=3.0,
start_step=0,
stop_step=11,
),
pdg=PdgGuidance(
enabled=True,
mode=PdgMode.CFG_TO_PDG,
curve=PdgCurve.POWER,
noisy_scale=2.0,
clean_scale=2.5,
power=3.0,
start_step=12,
stop_step=39,
),
self_attention_gain=-0.03,
sde_noise_multiplier=1.0,
seed=123,
generator=None,
)
config = CanterPipelineConfig(
inference=inference,
output_type=CanterOutputType.PIL,
)
image = pipe("A glass greenhouse during heavy rain", config=config).image
```
## Prompt batches
A sequence of prompts generates one image per prompt:
```python
output = pipe(
[
"A windswept beach under dark clouds",
"A sunlit kitchen with white tiled walls",
]
)
first, second = output.images
```
Batch size one is the primary inference path. The jagged backend packs active
prompt tokens without padding them through text refinement and
cross-attention. Prompts longer than 512 tokens are truncated with a warning.
## Latent generation
Use `CanterInferenceEngine` to generate latents without loading or calling the
VAE:
```python
from canter import CanterComponents, CanterInferenceEngine
components = CanterComponents.from_pretrained("data-archetype/canter")
engine = CanterInferenceEngine(components)
output = engine.generate("A mountain road in winter")
latents = output.latents
schedule = output.schedule
```
`CanterInferenceEngine.generate` accepts:
| Parameter | Default | Description |
| --- | --- | --- |
| `prompts` | required | One string or a sequence of strings. |
| `negative_prompts` | `None` | Optional negative prompt string or sequence for CFG and contrastive PDG branches. The count must match `prompts`; blank text uses learned unconditional conditioning. |
| `config` | `CanterInferenceConfig()` | Latent inference settings. |
| `initial_noise` | `None` | Optional float32 noise with shape `[batch, 128, height / 16, width / 16]`. |
| `progress` | `None` | Optional callback receiving `(completed_updates, total_updates)`. |
| `state_callback` | `None` | Optional synchronous callback receiving `(state, completed_updates, total_updates)` after every solver update. Prefer pipeline previews when full CUDA latent access is not required. |
An empty or whitespace-only prompt uses the learned unconditional token without
running the text encoder. Every prompt in a batch must be either blank or
nonblank.
The returned latents use float32 and channels-last memory format.
## Direct VAE encoding and decoding
`CanterVae` exposes both directions of the pinned DINAC-AE latent interface:
```python
import torch
from canter import CanterVae
vae = CanterVae.from_pretrained(
"data-archetype/dinac_ae_d2",
device="cuda",
)
images = torch.zeros((1, 3, 1216, 832), device="cuda", dtype=torch.float32)
latents = vae.encode(images)
reconstructed = vae.decode(latents, height=1216, width=832)
```
Encoder inputs and decoder outputs are float32 BCHW tensors in the DINAC image
range `[-1, 1]`. Encoded latents are whitened float32 tensors with 128 channels
and a spatial stride of 16.
## Conditioning, guidance, and solver APIs
The public inference API exposes text conditioning, guidance, schedules,
solvers, and the latent-RGB preview projection as separate components:
```python
import torch
from canter import (
LOGSNR_SOLVER_START_EPS,
CanterGuidanceConfig,
CanterGuidedVelocity,
LatentRgbProjection,
Schedule,
Solver,
build_schedule,
solve,
)
conditional = engine.prepare_conditioning("A mountain road in winter")
unconditional = engine.prepare_unconditional(batch=1)
guidance = CanterGuidanceConfig()
generator = torch.Generator(device=engine.device).manual_seed(42)
velocity = CanterGuidedVelocity(
components.model,
conditional,
unconditional,
guidance,
generator,
)
schedule = build_schedule(
Schedule.BETA,
steps=guidance.steps,
log_snr_shift=0.0,
finite_noisy_endpoint_epsilon=None,
device=engine.device,
)
latents = solve(
velocity,
initial_noise,
schedule,
solver=Solver.ABM2,
generator=generator,
)
preview_projection = LatentRgbProjection.bundled()
```
`prepare_conditioning` validates a string or prompt sequence and performs the
bundled text-encoder/refinement pass. An entirely blank batch returns learned
unconditional conditioning; mixed blank and nonblank batches are rejected.
`prepare_unconditional` creates learned conditioning for an explicit positive
batch size.
`CanterGuidanceConfig` contains the solver-step guidance state: step count,
CFG, PDG, and self-attention gain. `CanterGuidedVelocity`, `build_schedule`,
and `solve` preserve the same PDG, CFG, self-attention gain, stochastic
generator, and float32 solver behavior as `CanterInferenceEngine.generate`.
For `Solver.DPMPP_2M` and `Solver.ER_SDE`, pass
`LOGSNR_SOLVER_START_EPS` as `finite_noisy_endpoint_epsilon`; other solvers use
`None`. `LatentRgbProjection.bundled()` returns the validated CPU float32 1×1
projection weight and bias used by native previews.
## Pipeline metadata
`pipe.metadata` records the installed Canter code version, resolved checkpoint
release, weight dtype, source digests, text-encoder revision, VAE repository,
and resolved immutable VAE revision. Applications that require reproducibility
should store this metadata and pin both the package version and checkpoint tag.
|