Instructions to use lvladikov/SeedVR2-1.4B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- SeedVR
How to use lvladikov/SeedVR2-1.4B with SeedVR:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- MLX
How to use lvladikov/SeedVR2-1.4B with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] huggingface-cli download --local-dir SeedVR2-1.4B lvladikov/SeedVR2-1.4B
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- SeedVR2-1.4B β a 6-layer distillation of SeedVR2-7B (sharp)
- Who this is for
- Files
- Using it in ComfyUI
- 1. Download the two files
- 2. Put them in your ComfyUI model folders
- 3. Install the support folder
- 4. Restart ComfyUI
- 5. Load the ready-made workflow
- 6. Check the two file names match
- 7. Pick your image and upscale factor
- 8. Run
- What the support folder does
- Troubleshooting
- Framework compatibility
- Usage
- If you only ever upscale STILL IMAGES: a free β4Γ speedup in the VAE
- Quality vs the teacher
- Performance
- Training
- Known limitations
- Reproducing the evaluation images
- Repository layout
- Credits and licence
- Results
- Also in progress: a 12-layer (2.8B) distillation
SeedVR2-1.4B β a 6-layer distillation of SeedVR2-7B (sharp)
A 1.44 B-parameter, 6-layer one-step diffusion image upscaler distilled from ByteDance-Seed/SeedVR2-7B (the sharp EMA variant, converted to safetensors). It targets the case where the 7B teacher is too large or too slow to be practical: 5.7Γ smaller on disk, and it runs in β4.6 GB where the teacher needs β14β16 GB.
β οΈ Read this before downloading: it will not load in a stock SeedVR2 workflow
This model has 6 transformer blocks. The stock SeedVR2 architecture has 36. Any loader that builds the standard model will look for
blocks.6β¦blocks.35, find nothing, and fail with roughly 930 missing keys. That is a hard failure, not a quality regression.ComfyUI: a drop-in folder fixes it β copy
comfyui/ComfyUI-SeedVR2-1.4B/intocustom_nodes/and restart. No ComfyUI source file is edited. Step-by-step guide below β see Using it in ComfyUI. ComfyUI detects SeedVR2 architecture by inspecting the checkpoint, and it only knowsblocks.31(3B) andblocks.35(7B); the folder adds a branch for six blocks. No setting or filename change can substitute for it. It also carries three memory and speed fixes for still-image upscaling β see what it does.If you are writing your own loading code, it is a one-line change and everything else is standard β see Framework compatibility.
The speed advantage grows with the job. At 4Γ it is β1.6Γ faster; at 8Γ it is 4.7β5.6Γ faster (55 s vs 257β307 s). The bigger the upscale, the more the six-layer transformer pays off β because the transformer's share of the work rises with output size, and at 8Γ the 36-layer teacher also stops fitting comfortably in memory. Details in Performance.
It produces excellent 2Γβ4Γ upscales. At 4Γ it tracks the 36-layer teacher closely on structure, and actually delivers more edge energy than the teacher β outputs read crisp and detailed, not soft. If you are memory-bound, latency-bound, or batching a lot of images, this is the model to reach for. Full side-by-side comparisons are linked in Results so you can judge with your own eyes rather than take a table's word for it.
Everything below is measured on 15 scenes against the teacher, published with per-scene ranges rather than flattering averages.
| this model | teacher | advantage | |
|---|---|---|---|
| transformer layers | 6 | 36 | 6Γ fewer |
| parameters | 1,442,608,252 | 8,239,608,232 | 5.7Γ fewer |
| weights on disk (fp16) | 2.69 GB | 15.35 GB | 5.7Γ smaller |
| 512β2048 (4Γ) | 20.2β22.5 s | 33.0β38.4 s | β1.6Γ faster |
| 512β2048 peak RAM | 4.6 GB | 14.2β16.4 GB | β3Γ lighter |
| 512β4096 (8Γ) | 54.4β55.2 s | 257β307 s * | 4.7β5.6Γ faster |
Measured on an Apple M2 Ultra (64 GB) via MLX, model load included. See Performance for why the teacher's 8Γ figure is what it is.
On the teacher's parameter count: "7B" is its published name, not its size β the checkpoint holds 8,239,608,232 parameters, which is exactly the 15.35 GiB it occupies at fp16. The 5.7Γ figures above are computed from the true counts, and match the disk-size ratio as they should, both models being fp16.
Who this is for
The teacher is an excellent upscaler that many people cannot actually run. At 36 layers and 15.35 GB of weights it wants a workstation; on a 64 GB machine it already thrashes at 8Γ, and it is simply out of reach on consumer laptops, integrated GPUs and phones.
This model exists to move that line. Six layers instead of thirty-six, 2.69 GB instead of 15.35, and a 4.6 GB peak instead of 14β16 GB β which is the difference between "runs on a 16 GB machine" and "does not run at all". The layer count is what drives it: attention and MLP cost scale with depth, so cutting 36 β 6 cuts both the resident weights and the activation working set, not just the file size.
Mobile
Mobile deployment is plausible with this model in a way it is not with the teacher. Three things have to line up, and all three are ordinary engineering rather than research.
1. Stage the pipeline so only one model is resident at a time. This is the single biggest factor on a phone, and it is what makes the whole thing feasible. A generate-then-upscale pipeline must run strictly sequentially, fully releasing each stage before the next loads:
text encoder β run β UNLOAD
diffusion β run β UNLOAD
upscaler β run β UNLOAD
Peak memory then becomes the largest single stage, not the sum of them. Holding two of these resident at once is what actually exhausts a phone; holding one at a time is tractable.
This model is well suited to that pattern for a structural reason: upscaling here is prompt-free. There is no text encoder in the upscale stage at all β just the transformer and the VAE. The stage has nothing else to carry.
2. Quantise the transformer. At fp16 the weights are 2.69 GB (plus a 0.47 GB VAE) and the measured 4Γ peak is 4.6 GB β still too much for most phones as-is. Quantisation is the standard answer and this architecture takes it well:
| precision | transformer | + VAE (fp16) | weights resident |
|---|---|---|---|
| fp16 | 2.69 GB | 0.47 GB | β3.2 GB |
| int8 | β1.4 GB | 0.47 GB | β1.9 GB |
| int4 | β0.7 GB | 0.47 GB | β1.2 GB |
The VAE is small enough to leave at full precision, which is also where quantisation tends to hurt most.
3. Tile the input. The transformer denoises the whole latent grid in one pass, so activation memory scales with output area. Tiling bounds that working set independently of the final output size, so a 4Γ or 8Γ job costs roughly the same peak as a small one β it just takes more passes. A modest overlap (β64 output pixels) blends without visible seams.
Combined β one stage resident, int4 weights, tiled activations β the upscale stage lands in the β1.5 GB region, which is a realistic budget on current high-end phones.
So the accurate claim is not "this runs on your phone today". It is: this is small enough that mobile deployment is an engineering problem rather than an impossibility. The 7B teacher is not in that category at any quantisation β at 15.35 GB of fp16 weights it is out of reach before the first activation is allocated.
If you have the memory and want maximum fidelity, use the teacher. If you are memory- or latency-bound β laptop, integrated GPU, batch jobs, or a mobile target β this trades a measurable amount of fine detail for roughly a fifth of the footprint.
Files
| file | size | what it is |
|---|---|---|
seedvr2_distill_6L_1.4B_sharp_fp16.safetensors |
2.69 GB | the distilled transformer (6 blocks) |
ema_vae_fp16.safetensors |
0.47 GB | SeedVR2 VAE, unmodified from the teacher |
comfyui/seedvr2_distill_6L_1.4B_sharp_fp16_comfyui.safetensors |
2.69 GB | ComfyUI variant β identical weights plus two conditioning tensors |
comfyui/ComfyUI-SeedVR2-1.4B/ |
9 KB | drop-in support folder for ComfyUI β see what it does |
comfyui/seedvr2_1.4b_upscale_image.json |
15 KB | ready-made ComfyUI workflow |
Which transformer do I want?
- PyTorch / MLX / your own code β the root
seedvr2_distill_6L_1.4B_sharp_fp16.safetensors. - ComfyUI β the
comfyui/variant. ComfyUI refuses to load a SeedVR2 checkpoint that does not containpositive_conditioningandnegative_conditioningtensors (required_keysin itssupported_models.py), so the plain file fails its match check even when the architecture is detected correctly.
The two differ by 1.2 MB: positive_conditioning [58, 5120] and negative_conditioning
[64, 5120], both bf16. All 198 transformer tensors are byte-identical between the files β the
variant is a strict superset. Because SeedVR2 upscaling is prompt-free these embeddings are fixed
constants, the same for every SeedVR2 model; they are taken from
Comfy-Org/SeedVR2 (Apache-2.0, same lineage as the
teacher) so that the file matches what ComfyUI expects.
Those two files are the whole runnable set β 3.15 GiB (3.39 GB) together. Upscaling is prompt-free, so there is no text encoder and no config to download.
These weights are not quantised. Every tensor is plain F16 β 198 in the transformer, 250 in the
VAE β with no quantised dtypes and no framework metadata of any kind. If a model-hub sidebar files
this under a "quantized" heading, that is the hub's UI grouping the mlx tag, not a property of the
files. Quantising them is a reasonable thing to do (see Mobile for what int8/int4 buys),
but it has not been done here β what you download is full fp16.
Using it in ComfyUI
ComfyUI supports SeedVR2 natively. It decides which architecture to build by inspecting the
checkpoint, and the branches it ships only recognise blocks.31 (3B) and blocks.35 (7B). This
model has six blocks, so nothing matches and it will not load until ComfyUI is taught about it.
That is handled by a small support folder you drop into custom_nodes/ β no ComfyUI source files
are edited, so it survives updates. It also fixes a performance trap that has nothing to do with
this model but hits it hard; see What the support folder
does if you want to know before installing it.
Everything referenced below is in comfyui/ in this repo. Allow about five minutes.
The finished graph (click to enlarge): Load Image β Upscale Image (SeedVR2 1.4B) β Save Image β Compare Images. 512Γ512 in, 2048Γ2048 out at 4Γ, one step. This is the workflow you drag onto the canvas in step 5 β nothing to wire up.
1. Download the two files
From the Files tab of this repo:
comfyui/seedvr2_distill_6L_1.4B_sharp_fp16_comfyui.safetensorsβ the model (2.69 GB). Use thecomfyui/one, not the file in the repo root β ComfyUI will not load the root file (see Files for why).ema_vae_fp16.safetensorsβ the VAE (0.47 GB)
You need both. The VAE is not bundled into the model file.
2. Put them in your ComfyUI model folders
ComfyUI/models/diffusion_models/seedvr2_distill_6L_1.4B_sharp_fp16_comfyui.safetensors
ComfyUI/models/vae/seedvr2_ema_vae_fp16.safetensors
Note the VAE is renamed on the way in β ComfyUI's SeedVR2 workflow expects
seedvr2_ema_vae_fp16.safetensors.
If you use ComfyUI Desktop, these live under your base directory, not the app folder β the path
is shown in Settings, and is the same folder that contains input/ and output/.
3. Install the support folder
Copy the whole comfyui/ComfyUI-SeedVR2-1.4B/ folder into your
ComfyUI custom_nodes/ directory, so you end up with:
ComfyUI/custom_nodes/ComfyUI-SeedVR2-1.4B/__init__.py
That is the whole installation. It registers no nodes and edits none of ComfyUI's own files β it just tells ComfyUI how to read a 6-block checkpoint, and adds a still-image fast path to the VAE.
It cannot break your existing models. The detection branch fires only when blocks.5 is present
and blocks.6 is absent, so the 7B (blocks 0β35) and 3B (blocks 0β31) still reach their own
branches untouched, and anything that is not SeedVR2 never reaches the check at all.
4. Restart ComfyUI
custom_nodes/ is read at startup. Reloading the browser page is not enough β restart the server
(or quit and reopen ComfyUI Desktop). You should see this in the log:
SeedVR2-1.4B: 6-block detection + single-frame VAE fast path
5. Load the ready-made workflow
Download comfyui/seedvr2_1.4b_upscale_image.json and
drag the file onto the ComfyUI canvas. The whole graph appears, already wired.
That is it β no nodes to add, nothing to connect. It is ComfyUI's own official SeedVR2 template with the model filename swapped, so the sampler settings and wiring are theirs, not mine.
6. Check the two file names match
Open the Upscale Image (SeedVR2 1.4B) node (double-click to enter the subgraph) and confirm:
- UNETLoader β
seedvr2_distill_6L_1.4B_sharp_fp16_comfyui.safetensors - VAELoader β
seedvr2_ema_vae_fp16.safetensors
β οΈ Two easy mistakes here. The model must be the _comfyui variant β the root file will fail
with "Could not detect model type" even with the support folder installed. And the VAE must be renamed to
seedvr2_ema_vae_fp16.safetensors, which is what ComfyUI's SeedVR2 template looks for.
If either dropdown is empty or red, the file is in the wrong folder β go back to step 2.
7. Pick your image and upscale factor
- Load Image node β choose your picture.
- ResizeImageMaskNode (inside the subgraph) β this is where the upscale factor lives. It is
set to
scale by multiplier= 4 withlanczos. Change the 4 to 2 or 3 if you prefer.
4Γ is the recommended operating point β that is where this model is closest to the teacher (see Quality vs the teacher). 8Γ works but degrades noticeably.
Do not change the KSampler. It is steps 1, cfg 1, euler, simple, denoise 1 β SeedVR2 is a
one-step model and raising steps will not improve it.
8. Run
Press Run / Queue Prompt. The first run reads 3.2 GB of weights from disk and will sit still for a while before showing progress; later runs reuse them. If your models live on an external or network drive, that first read alone can take a minute β it is disk speed, not the model.
The workflow includes an ImageCompare node so you can slide between before and after.
β οΈ The speed figures in Performance were measured in a different pipeline (MLX, with the VAE fast path below and no tiling) and will not be reproduced by ComfyUI on PyTorch. A 512β2048 4Γ job measured 44.8 s here against 20.2β22.5 s in MLX β roughly 2Γ. See Performance for what is and is not comparable.
What the support folder does
Four independent patches, all applied at import, all falling back to ComfyUI's own code path the
moment anything looks unfamiliar. One makes the model load at all; the other three are about memory
and speed, and help any SeedVR2 image upscale. Escape hatches: SEEDVR2_1_4B_NO_DETECT=1,
SEEDVR2_1_4B_NO_FASTVAE=1, SEEDVR2_1_4B_NO_TILECACHE=1, SEEDVR2_1_4B_NO_UNLOAD=1.
1. Architecture detection. ComfyUI decides what to build by inspecting the checkpoint, and every
shipped branch keys on blocks.31 or blocks.35. This wraps detect_unet_config() with a branch
for a 6-block SeedVR2, guarded on blocks.5 present and blocks.6 absent. The values are not
guesses β they are ComfyUI's own 7B "separate key" branch with the depth changed, each cross-checked
against the published weights (vid_dim 3072 from blocks.0.ada.vid.attn_gate; heads 24 because
24 Γ head_dim 128 = 3072; mlp_type normal because the keys are mlp.vid.proj_in, not
proj_in_gate). Verified: ComfyUI's own detect_unet_config() then returns image_model=seedvr2, num_layers=6, mm_layers=6, vid_dim=3072, heads=24, mlp_type=normal, rope_type=rope3d, rope_dim=64.
2. Single-frame VAE fast path. This one is not specific to this model β it helps every SeedVR2 image upscale, and it is the single biggest thing standing between ComfyUI and the timings in Performance.
SeedVR2's VAE is a video VAE built from causal 3-D convolutions. Upscaling a still image hands it
one frame. Internally each InflatedCausalConv3d replicates that single frame kt times along the
temporal axis and convolves β so every temporal tap sees the same frame, and the result is exactly
out = Ξ£β W[:, :, t] * frameβ = conv2d(frameβ, Ξ£β W[:, :, t])
a 2-D convolution whose kernel is the sum of the temporal taps. The patch does that directly. It
removes ktβ1 of every kt multiply-accumulates as pure redundancy, and β more importantly for the
out-of-memory failures β never materialises the ktΓ-replicated activation tensor at all.
Measured on an M2 Ultra, ComfyUI's real InflatedCausalConv3d, PyTorch 2.5.1 / MPS:
| stock 3-D | collapsed 2-D | ||
|---|---|---|---|
| one 128-ch 256Γ256 conv | 19.2 ms | 2.1 ms | 9.1Γ |
| full VAE decode β 512Γ512 | 5.36 s | 1.81 s | 3.0Γ |
| full VAE decode β 1024Γ1024 | 12.35 s | 2.69 s | 4.6Γ |
The per-layer number is larger than the end-to-end one because not every layer is a 3-tap conv, and
because MPS's conv2d kernel is simply better optimised than its conv3d.
Correctness. Checked against stock output across all 36 combinations of kernel/stride/padding
used by this VAE Γ both memory-limit regimes Γ every MemoryState, including the internal
memory-cache contents. Zero mismatches. Worst output delta is 3.9 Γ 10β»Β³ in fp16 and 1.0 Γ 10β»β΅ in
fp32 β it scales with precision, which is the signature of floating-point round-off rather than a
logic difference. Video (T > 1) never enters this path.
3. Release the allocator cache between tiles. A 4Γ job is decoded as roughly 40 tiles, each
running the whole decoder stack. PyTorch's MPS caching allocator does not hand freed blocks back to
the system, so the pool ratchets upward tile after tile. That is how a job whose largest single
tensor is about 100 MB reaches an 86 GB footprint and dies in whichever allocation happens to
come next β the traceback blames a torch.cat deep in the decoder, but nothing there is large; the
memory was already gone. Releasing the cache once per tile keeps the footprint flat.
4. Drop the transformer before the VAE decode. Sampling is finished by the time the VAE runs, so
the diffusion model is dead weight for the entire decode β but ComfyUI keeps it resident. Stock logs
show 0 models unloaded immediately before Requested to load VideoAutoencoderKLWrapper, i.e. the
decode is stacked on top of a model it will never use. This evicts it first. Gated to SeedVR2, so no
other VAE changes behaviour. The cost is that the transformer is re-read on the next run; set
SEEDVR2_1_4B_NO_UNLOAD=1 if you batch many images and would rather keep it loaded.
Patches 3 and 4 mirror what the reference MLX pipeline does at every stage boundary β clear the allocator, and free the transformer at the denoiseβdecode handover.
β οΈ Do not raise tile_size
Leave it at 512. It looks wasteful β a 4Γ output becomes β40 overlapping tiles with β1.78Γ of the convolution work spent recomputing overlaps β and raising it is the obvious optimisation. It is a trap, for two independent reasons:
- It crashes the process, not the node. The VAE runs self-attention over the whole tile at latent
resolution, so the attention matmul is O(tokensΒ²) with
tokens = (tile_size/8)Β². At 512 that is 4,096 tokens (16.8 M); at 2048 it is 65,536 tokens (4.3 G). Past the MPS limit Metal does not raise a catchable Python error β it asserts andabort()s, killing ComfyUI with SIGABRT. - It is slower anyway. Bigger VAE tiles measured slower on this VAE: decode peak scales at roughly 14 GB per megapixel, so larger tiles buy memory pressure, and the convolution cost is tiling-invariant. There is no compute saving to collect.
Tiling cannot be removed either: an un-tiled 2000Γ3000 decode fails with matmul 93750,93750 larger than currently supported with or without the fast path. What the patches change is the cost and
the memory behaviour of each tile, not the number of them.
Troubleshooting
| symptom | cause |
|---|---|
Could not detect model type |
support folder not installed or ComfyUI not restarted β check the startup log for the SeedVR2-1.4B: line |
missing keys blocks.6 β¦ blocks.35 |
you loaded the root model file; use the comfyui/ variant |
| model missing from the dropdown | wrong folder β it must be models/diffusion_models/ |
| loads, but output is noise | wrong VAE selected β it must be this repo's SeedVR2 VAE, not an SD/Flux one |
| VAE dropdown has no matching entry | the VAE must be named seedvr2_ema_vae_fp16.safetensors in models/vae/ |
out of memory in VAEDecodeTiled |
confirm the support folder loaded β the per-tile allocator release is what keeps this bounded. Then lower tile_size (256), never raise it |
| ComfyUI dies with SIGABRT / "exited unexpectedly" | tile_size was raised. Metal aborts the process instead of raising; put it back to 512 |
| very slow, minutes per image | check the startup log lists all four patches; also see the note that these timings are MLX figures |
SeedVR2 β¦ nodes do not exist |
your ComfyUI predates native SeedVR2 support β update it |
using the third-party ComfyUI-SeedVR2_VideoUpscaler node |
different mechanism β it picks a YAML by filename; see comfyui/configs_1_4b_thirdparty_node/ |
If ComfyUI adds official support for this model, prefer that over the support folder. If you would
rather patch comfy/model_detection.py by hand than install anything into custom_nodes/, the
equivalent source edit is in
comfyui/seedvr2_1.4b_detection.patch β it gives you
detection only, not the VAE fast path. Do not apply both.
Framework compatibility
These weights are framework-neutral. They were written by PyTorch during training and are
consumed directly by both PyTorch and MLX β no conversion was ever applied in either direction.
The header carries no framework metadata, tensor names follow the upstream NaDiT convention
(blocks.N.β¦, emb_in, txt_in, vid_in, vid_out), and the transformer is Linear-only, so the
usual NCHW/NHWC convolution-layout trap does not apply (the convolutions live in the VAE, which is
the stock file).
The one thing to change versus upstream: instantiate the NaDiT transformer with 6 blocks instead
of 36 (num_layers=6, mm_layers=6). Everything else is the standard SeedVR2 loading path.
Usage
Use the official SeedVR2 inference code, with the transformer built at depth 6 and these weights loaded in place of the 7B checkpoint. In outline:
from safetensors.torch import load_file
# 1. Build the SeedVR2 NaDiT transformer with SIX blocks rather than 36.
# Everything else (hidden size, heads, VAE) is unchanged from the 7B config.
transformer = build_seedvr2_transformer(num_layers=6, mm_layers=6) # upstream constructor
# 2. Load these weights. Keys are the upstream NaDiT names, so no remapping is needed.
sd = load_file("seedvr2_distill_6L_1.4B_sharp_fp16.safetensors")
transformer.load_state_dict(sd) # 198 tensors, 1,442,608,252 parameters
# 3. The VAE is the stock SeedVR2 VAE, unmodified.
vae_sd = load_file("ema_vae_fp16.safetensors")
Upscaling is prompt-free: there is no text encoder to load and no prompt to supply. Inference then follows the upstream one-step diffusion path exactly.
Tip for large outputs. The transformer denoises the whole latent grid in a single pass, so memory scales with output area. For very large targets, tile the input and blend the tiles β this bounds the activation working set independently of output size, and a modest overlap (β64 output pixels) is enough to avoid visible seams.
If you only ever upscale STILL IMAGES: a free β4Γ speedup in the VAE
SeedVR2 inherits a video VAE β a WAN-style decoder built on CausalConv3d. Its weights are
shipped here unmodified, with genuine 3-D kernels (65 tensors of shape [out, in, kT, kH, kW], e.g.
decoder.conv_in.weight = [512, 16, 3, 3, 3]).
For a still image the temporal depth is T = 1. Every 3-D convolution therefore replicates frame 0
into kT identical temporal slices and convolves β doing kTΓ the multiply-accumulate work to
produce a result that is mathematically identical to a 2-D convolution whose kernel is the sum of
the temporal taps:
W2d = W3d.sum(axis=temporal) # for a causal layer with kT > 1 and temporal stride 1
This is an exact identity, not an approximation β the outputs match to numerical precision. So for single-frame use, roughly two thirds of the VAE's convolution work is redundant.
Why it is worth doing: the VAE decode is not a minor stage. On a 4Γ job it measured 52.7% of total runtime (against 24.9% for the transformer itself), so it is the single largest cost in the pipeline at that factor.
To exploit it, intercept the causal-3D-conv forward pass: when T == 1 and the layer is causal with
kT > 1 and temporal stride 1, substitute a single 2-D convolution using the summed kernel, and
cache that 2-D weight per layer. Fall through to the original 3-D path for anything else, so real
video and any non-matching layer are unaffected.
None of this changes the published weights β the file on disk stays the stock 3-D video VAE, and the collapse is a runtime substitution you can add or remove freely. It is mentioned here only because most people using this model will be upscaling stills, and paying 3-D cost for a 1-frame input is pure waste.
ComfyUI users get this for free: it is patch 2 in the support folder, measured there at 3.0Γ on a 512Γ512 decode and 4.6Γ on 1024Γ1024, verified equivalent to stock output across every kernel/stride/memory-state combination this VAE uses. It applies to any SeedVR2 model, not only this one.
Quality vs the teacher
Teacher-relative FFT band energy, 15 scenes, identical inputs for both models. The teacher is the reference, so 1.000 means indistinguishable from the teacher in that band; below 1.0 means the student under-produces detail, above 1.0 means it over-produces (ringing / over-sharpening).
512 β 2048 (4Γ) β the recommended operating point
| band | student / teacher | per-scene range |
|---|---|---|
| mid (0.15β0.40 Nyq) | 0.852 | 0.664 β 1.039 |
| fine (0.40β0.70 Nyq) | 0.696 | 0.406 β 0.871 |
| edges (0.70β1.0 Nyq) | 1.125 | 0.471 β 1.705 |
| MAE vs teacher (8-bit levels) | 5.70 | 2.80 β 9.10 |
This is a strong result for a model with one sixth of the layers. It holds β85% of the teacher's mid-band structure β the band that carries recognisable form and texture β and at the edge band it exceeds the teacher (1.125), so results look crisp and well-defined rather than the softness that usually betrays a distilled model. Mean absolute difference from the teacher is 5.70 on a 0β255 scale, i.e. β2% average pixel deviation from a model five times its size.
The fine band (0.696) is where the teacher keeps a real lead β very small high-frequency texture is partly rebuilt rather than fully recovered. And the edge band's spread (0.471β1.705) means a few scenes come out noticeably crisper than the teacher and a few softer; on most content this reads as pleasing sharpness, on some it can tip into over-sharpening. Both are published so you can see the shape of the model rather than just its average.
2048 β 8192 (4Γ) β upscaling an already-large image
| band | student / teacher | per-scene range |
|---|---|---|
| mid (0.15β0.40 Nyq) | 0.833 | 0.684 β 0.930 |
| fine (0.40β0.70 Nyq) | 0.803 | 0.550 β 1.020 |
| edges (0.70β1.0 Nyq) | 1.213 | 0.791 β 1.665 |
| MAE vs teacher (8-bit levels) | 4.35 | 2.37 β 8.21 |
This is the line where the model is closest to the teacher in absolute fidelity. MAE 4.35 is the best of the three (against 5.70 at 512β2048 and 5.43 at 512β4096), and fine-detail recovery jumps to 0.803 from 0.696 on the 512 px line. That is what a high-resolution source buys: far more real information to work from, and correspondingly less to invent.
The trade-off is at the edge band β 1.213, more overshoot than the 4Γ line's 1.125. Outputs are crisp, occasionally crisper than the teacher. Aggregate deviation ties the 512β2048 line (sum|bandβ1| 0.577 for both), so "best" depends on whether you weight fidelity or band balance.
512 β 4096 (8Γ) β works, but degrades
| band | student / teacher | per-scene range |
|---|---|---|
| mid | 0.443 | 0.349 β 0.595 |
| fine | 0.345 | 0.195 β 0.467 |
| edges | 0.475 | 0.217 β 0.725 |
| MAE vs teacher | 5.43 | 2.64 β 9.33 |
8Γ is this model's stretch goal rather than its home ground: it retains under half the teacher's detail energy per band, and produces a clean, usable 4096Γ4096 image in β55 seconds at 7.5 GB β a job the teacher needs 4β5 minutes for, and only by pushing a 64 GB machine into swapping.
Recommendation: use this as a 2Γβ4Γ upscaler, where it is genuinely close to the teacher. Reach for 8Γ when speed and memory matter more than squeezing out the last of the fine texture β and use the teacher when 8Γ fidelity is the priority. The limit was investigated rather than assumed; see Known limitations.
Performance
Apple M2 Ultra, 64 GB, MLX, model load included, idle machine, two scenes each.
These are pipeline numbers, not just model numbers β read this before comparing. They come from an MLX pipeline that decodes the VAE in a single pass and applies the single-frame 3Dβ2D collapse.
The same 512β2048 job in ComfyUI on the same machine takes 44.8 s (with the support folder installed β the run in the screenshot above), against 20.2β22.5 s in MLX. So expect roughly 2Γ the time in ComfyUI. That gap is the runtime and its memory handling, not the model: your log will show the diffusion step itself finishing in about 13 s either way. Without the support folder it is far worse than 2Γ and frequently runs out of memory outright.
What is a property of the model β and what transfers to any runtime β is the layer count and the memory footprint: 6 blocks instead of 36, the same VAE, the same one step. The teacher pays the same VAE and tiling costs in the same runtime, so the ratios below hold even where the absolute seconds do not.
| job | student | teacher |
|---|---|---|
| 512β2048 (4Γ) | 20.2 / 22.5 s @ 4.64 GB | 38.4 / 33.0 s @ 14.2 / 16.4 GB |
| 512β4096 (8Γ) | 54.4 / 55.2 s @ 7.5 GB | 307.4 / 257.2 s @ 4.7 GB β |
β The teacher's 8Γ memory figure is not a win β read it as a failure signal. 4.7 GB is below its own 15.35 GB weight file, which cannot happen while the weights are resident. At 8Γ the activations no longer fit, so the OS evicts the teacher's memory-mapped weight pages and re-faults them continuously; RSS counts only resident pages, so it collapses precisely when the model is struggling most. The runtime is the honest indicator: 307 s versus 38 s β an 8Γ jump for a 4Γ pixel increase. This model does the same job in β55 s at a stable 7.5 GB.
1.6Γ faster at 4Γ, 4.7β5.6Γ faster at 8Γ, and 3Γ lighter throughout β and the memory figure is the one that changes what is possible. 4.6 GB versus 14β16 GB is the difference between running comfortably on a 16 GB machine and not running at all. On constrained hardware that is not a speed optimisation, it is the whole ballgame.
The speedup grows with the upscale factor, because the bottleneck moves. Measured stage split for the 36-layer teacher on the same hardware:
| stage | at 4Γ | at 8Γ |
|---|---|---|
| VAE encode | 14.9% | 11.5% |
| transformer (the part this model shrinks) | 24.9% | 53.4% |
| VAE decode | 52.7% | 29.1% |
| colour-correct | 7.4% | 5.9% |
The VAE is byte-identical in both models, so only the transformer slice benefits from having six layers instead of thirty-six. At 4Γ that slice is a quarter of the work and the job is VAE-decode-bound, which caps the win at β1.6Γ. At 8Γ the transformer becomes the dominant cost and the gap opens to 4.7β5.6Γ. Bigger jobs favour this model more, not less β and the memory saving applies at every factor.
The teacher's 8Γ time reflects memory pressure. Its peak RSS falls at 8Γ (4.7 GB, below its own 15.35 GB weight file) while runtime grows β8Γ for a 4Γ pixel increase β the OS is evicting weight pages to fit activations and re-faulting them repeatedly, because 65,536 latent tokens through 36 blocks does not fit comfortably even on 64 GB. This model sails through the same job in β55 s at a stable 7.5 GB. Read that row as "stays within memory where the teacher does not" rather than as a 5Γ compute win.
Training
Hardware and wall-clock
Single NVIDIA RTX 3090 (24 GB) under WSL2, 8 CPU threads, 49 GB RAM. Roughly 53 GPU-hours end to end:
| stage | steps | throughput | wall-clock |
|---|---|---|---|
| base distillation | 166,719 (one full epoch) | 1.029 it/s | β45 h |
| fine-tune phase 1 | 12,000 | 1.01 it/s | β3.3 h |
| fine-tune phase 2 | 12,000 | 0.71 it/s | β4.7 h |
Each stage initialises from the previous one's EMA, so the full training history is preserved β neither fine-tune is a rollback or a restart.
Datasets
123,013 source images β 166,735 precomputed teacher-target shards. All targets are the true 36-layer teacher's outputs; no stage ever trained against an intermediate model's outputs, so errors do not compound.
| dataset | images | role |
|---|---|---|
| 4KLSDB | 44,999 | native-4K sources; enable clean high-resolution crops |
| LSDIR | 34,564 | general high-resolution variety |
| CelebA-HQ | 30,000 | faces |
| FFHQ | 10,000 | faces |
| Flickr2K | 2,650 | natural scenes |
| DIV2K | 800 | standard SR benchmark set |
The shard set is built in two passes:
- 43,450 shards at fixed 512 px with a single fixed bicubic degradation β exactly one per image from CelebA-HQ + FFHQ + Flickr2K + DIV2K.
- 123,285 shards with varied resolution (up to 1280 px) and varied degradation, drawing on the whole pool including 4KLSDB and LSDIR.
The second pass exists for two measured reasons, both of which matter if you retrain:
- Windowing-regime mismatch. SeedVR2's adaptive attention window targets a β720p (3,600-token) reference. A fixed 512 px grid is only 28% of that, so training saw near-global attention while real inference at 450β700% of the reference is genuinely local β a regime the model never trained in. The v3 pass samples output resolution across and past that reference.
- Degradation too narrow. A single fixed bicubic down/up pass is a special case next to the multi-kernel second-order degradation (blur, noise, JPEG, mixed resize kernels) the teacher lineage trained on. The v3 pass randomises factor and kernel, and layers a second-order degradation at 50% probability.
Resolution choices are clamped per-image to the source's own native size, so small-native-resolution sources are never upsampled before being "degraded" β which would quietly poison the clean-ground-truth assumption for exactly those images.
How the 6 layers were chosen
The student is initialised by block-dropping, keeping teacher-space blocks
[0, 3, 6, 29, 32, 35].
One detail matters for reproducing this exactly: the slice is not taken from the teacher's own
weights. It is taken from an intermediate 12-layer distillation of the same teacher, whose
blocks had already been trained together as a group β the keep-set is [0,1,2,9,10,11] in that
model's local indexing, which maps to [0,3,6,29,32,35] in teacher space.
training/box_slice_6l.py performs that slice.
Targets throughout remained the true 36-layer teacher's precomputed outputs. The intermediate model supplies initialisation only, never supervision, so its own error does not compound into the student. The reason for the extra hop is the second bullet below.
That 12-layer model is a separate line of work and may be released on its own once its fine-tuning is complete. It is a larger, heavier model β this 6-layer one is the interesting result, for the reasons in Who this is for.
Two alternatives were tested and rejected on measurements:
- Naive first-N truncation (keep the first 6 blocks) produces visible grain. The final block has special end-of-network handling in this architecture; dropping it removes that finishing stage.
- Cherry-picking the teacher's individually most "transformative" blocks β ranked by per-block input/output cosine distance β performs worse despite scoring better on the ranking: grain 91.2 vs 20.4 and reference-MAE 4.64 vs 1.33 on the same test images. Raw per-block importance is not the same as compatibility with neighbours a block never trained beside. Blocks that were co-trained together hand off to each other far better.
training/box_slice_6l.py performs the slice.
Objective
The loss is a weighted sum, with the pixel terms computed through the frozen VAE decoder on a random crop:
| term | what it does |
|---|---|
| Charbonnier on latents | base reconstruction; per-channel normalised |
| latent high-pass L1 | recovers detail that plain L2 leaves on the table |
| Focal Frequency Loss | phase-aware spectral matching on the latent FFT |
| pixel high-pass L1 | decoded-RGB detail, masked to suppress flat regions |
| pixel spectral | Hann-windowed FFT loss with a radial band mask |
| feature-matching KD | supervises internal representations against the teacher |
Plain MSE distillation was tried first and behaves as a low-pass filter of the teacher: it reproduces structure but not texture, because squared-error gradients vanish on exactly the small residuals that carry fine detail. The high-frequency terms above exist to fix that specific failure. Two GAN variants were also tried and both underperformed β gentle had no effect, strong came out softer.
Unattended-safety machinery (all in the trainer): per-term clamping relative to the base loss, a
skip-step guard on loss spikes, nan_to_num on decoder output, OOM-tolerant pixel branch, and
atomic checkpoint writes.
Fine-tune phase 1 β mid-band and feature-matching correction
Two corrections, both toward the recipe's own stated design intent:
- the pixel-spectral radial mask was made flat from R=0.15 instead of ramping 0.10β0.40, raising mid-band supervision +54.7% while leaving fine and edge bands untouched;
w_featmatch0.4671 β 0.6595, bringing the feature-matching term back to its design ratio.
Fine-tune phase 2 β high-resolution tier filter
Training restricted to the 14,822 shards at β₯2048 px (8.9% of the corpus), raising mean tokens per step from 6,771 to 18,727. Exactly one variable changed versus phase 1 β the data distribution β so the effect is attributable. It improved the 4Γ band fit, mostly at the edge band.
Both phases are reproducible as-run from fine-tuning/ft1.sh and fine-tuning/ft2.sh, which carry
the exact calibrated hyperparameters.
Known limitations
8Γ is weak, and it is not a data problem. Two hypotheses were tested and both failed:
- Curation. Phase 2 trained on 100% β₯2048 px shards for 12,000 steps. 8Γ moved marginally.
- Inference tiling. Splitting an 8Γ job so each tile lands back inside the trained token range β including a configuration at exactly the token count where 4Γ performs well β changed nothing (it moved slightly the wrong way, with clean seams confirming nothing masked a gain).
What the evidence points to is upscale factor itself, not token count: 4Γ and 2Kβ8K workloads are healthy at very different token counts, while the 8Γ line is weak at both its native token count and at a reduced one. Asking for 64 output pixels per input pixel instead of 16 is the actual difficulty, and rearranging the forward pass does not change it. Closing it would need more capacity or a different objective.
Edge-band over-shoot at 4Γ (mean 1.125, range 0.471β1.705) β on some content the model over-sharpens relative to the teacher. If you are chaining with a separate sharpening pass, account for it.
MAE is not at teacher level on any line. If exact fidelity matters more than size and speed, use the teacher.
Reproducing the evaluation images
Every image under assets/ started from a text-to-image generation with a fixed prompt and seed.
Each scene's prompt, seed and generating model are recorded in assets/prompts/<scene>.txt:
Prompt: Extreme macro of a spiderweb covered in tiny dew droplets, each droplet refracting light, ...
Seed: 285112985
Model used to generate: Boogu Turbo
All scenes were generated at 2048Γ2048 with the seeds listed below. The prompt and seed are what matter for reproduction β use whichever inference stack you prefer.
| scene | seed | prompt |
|---|---|---|
| rs_01_portrait_fisherman | 555477016 | Close-up portrait of an elderly fisherman with deeply weathered wrinkled skin, gray stubble beard, wearing a yellow raincoat hood, overcast soft light, photorealistic |
| rs_02_market_crowd | 1864840591 | Busy outdoor street market crowded with many people browsing colorful fruit and vegetable stalls |
| rs_03_fox_grass | 1986897913 | A red fox standing alert in tall golden grass, detailed fur, autumn evening light, shallow depth of field |
| rs_04_kingfisher | 1905028566 | A kingfisher bird bursting out of water with spread wings, water droplets frozen mid-air, iridescent blue and orange feathers, high-speed photography |
| rs_05_neon_street | 309014498 | Rainy night city street with glowing neon shop signs and readable text, wet asphalt reflections |
| rs_06_mountain_valley | 2142845029 | Wide mountain valley with a winding river, dense pine forest, morning fog layers, distant snowy peaks |
| rs_07_spiderweb_dew | 285112985 | Extreme macro of a spiderweb covered in tiny dew droplets, each droplet refracting light, dark green blurred background |
| rs_08_rustic_pizza | 175728095 | Overhead shot of a rustic wood-fired pizza with bubbling melted cheese, basil leaves, charred crust |
| rs_09_storefront_sign | 1133058957 | Vintage brick storefront with hand-painted gold lettering on the window reading BAKERY, striped awning |
| rs_10_locomotive | 422126126 | Close-up of an old black steam locomotive driving wheels and connecting rods, rivets, brass pipes |
| rs_11_coral_reef | 1896361262 | Vibrant underwater coral reef scene with schools of small tropical fish, sun rays penetrating clear water |
| rs_12_ski_powder | 829034716 | A skier in a red jacket carving through deep powder snow spraying a white cloud, bright sunny alpine day |
| rs_13_library | 1437621541 | Cozy old library interior with tall wooden bookshelves full of books, a leather armchair, warm lamplight |
| rs_14_oil_swirls | 680017211 | Abstract macro of swirling oil paint in vivid rainbow colors, smooth glossy gradients and thin filaments |
| rs_15_watercolor_castle | 890572502 | Watercolor illustration of a fantasy castle on a cliff above the sea, soft washes of color, paper texture |
Repository layout
seedvr2_distill_6L_1.4B_sharp_fp16.safetensors the model
ema_vae_fp16.safetensors VAE (stock, unmodified)
training/ train_student_v4.py the trainer that produced this model
precompute_targets.py 512px teacher-target shards
precompute_targets_v3.py varied-resolution/degradation shards
box_slice_6l.py 36 -> 6 block slice
fine-tuning/ ft1.sh phase 1, exact hyperparameters
ft2.sh phase 2, exact hyperparameters
build_tier_index.py token-count index phase 2 needs
comfyui/ ComfyUI-SeedVR2-1.4B/ drop into ComfyUI/custom_nodes/
seedvr2_..._fp16_comfyui.safetensors the ComfyUI model variant
seedvr2_1.4b_upscale_image.json ready-made workflow
seedvr2_1.4b_detection.patch manual alternative to the folder
configs_1_4b_thirdparty_node/ for the third-party video-upscaler node
assets/ inputs-512/ teacher-7B/ student-1.4B/ comparison/ thumbs/ prompts/
Scripts take paths from SV2_ROOT and SV2_DATASETS; set both before running.
Credits and licence
Teacher model: ByteDance-Seed/SeedVR2-7B (sharp EMA variant), converted to safetensors. This is a derivative work β the teacher's licence and terms govern its use. The VAE is redistributed unmodified.
Results
Judge it yourself. Left is this 1.4B model, right is the 36-layer 7B teacher in every pair, from byte-identical inputs. Click any image for the full-resolution version.
Two views, because they answer different questions:
- Full frames show what you actually get β the result you would ship. At this scale the two models are hard to tell apart, and that similarity is the result.
- 1:1 crops show where they diverge, at native pixels. The crop location is chosen automatically as the highest-detail region of the teacher's output, so it lands where upscaling is hardest rather than anywhere flattering.
A comparison showing only crops would misrepresent the model by its worst pixels; one showing only full frames would hide the real differences. Both are here.
512 β 2048 (4Γ) β full frames
This is what you actually get. At full-frame scale the two are hard to tell apart, which is the headline result: six layers instead of thirty-six, and the output stands up beside the teacher.
Portrait β skin, hair, fabric:
Motion and water:
Metal, rivets, mechanical detail:
Landscape with fine repeating texture:
Complex scene, many small subjects:
512 β 2048 (4Γ) β 1:1 crops
Now the pixel-level view. These are deliberately unflattering: the crop location is picked automatically as the highest-detail region of the teacher's output, so it lands where upscaling is hardest. Differences that are invisible above become visible here β that is the point of showing both.
Macro detail and refraction:
Skin, hair and fabric:
Fur and foliage:
Text and hard edges β the classic upscaler stress case:
Dense repeating structure:
512 β 4096 (8Γ)
Full frames first β an 8Γ upscale from a 512 px source is a hard ask, and both models produce a coherent 4096Γ4096 image:
At 1:1 the teacher's lead is visible, matching the measured numbers. Shown so you can see the limitation rather than read about it:
2048 β 8192 (4Γ) β the practical case
Upscaling an image that is already high-resolution, out to 8192Γ8192 β 67 megapixels. This is probably the most realistic workflow: not rescuing a thumbnail, but taking a good 2K render up to print or large-display size. It is also the line where the model gets closest to the teacher in absolute fidelity (MAE 4.35, the best of the three) and recovers the most fine detail β a 2K source gives it far more real information to work from than a 512 px one. See Quality vs the teacher for the numbers.
Portrait at 67 MP:
Landscape with fine repeating texture:
Dense structure and text:
Painterly source β a different kind of detail entirely:
And at 1:1, where 67 MP of output actually lives:
All scenes
Full-resolution outputs for every scene and both models:
| inputs | this model (1.4B) | teacher (7B) | |
|---|---|---|---|
| 512 β 2048 (4Γ) | inputs-512 | student 4Γ | teacher 4Γ |
| 512 β 4096 (8Γ) | inputs-512 | student 8Γ | teacher 8Γ |
| 2048 β 8192 (4Γ) | inputs-2k | student 2K | teacher 2K |
All 45 side-by-side comparisons (full frame and 1:1 crop, all three lines) are in
assets/comparison/. Prompts and seeds for every scene are in
assets/prompts/.
Also in progress: a 12-layer (2.8B) distillation
This 1.4B model was built out of a 12-layer one. A 12-layer, 2.8B distillation of the same teacher was trained first; the six blocks here are a slice of its weights
([0,1,2,9,10,11] in its indexing β [0,3,6,29,32,35] in teacher space), and training continued
from there against the true 36-layer teacher's targets. So the 12L is not a sibling project β it is
this model's direct ancestor, and part of its training is inherited rather than repeated. See
How the 6 layers were chosen for why slicing a co-trained model
beats cherry-picking blocks straight from the teacher.
To be precise about what was inherited: the slice was taken from the 12L's completed base training run, before any fine-tuning of that model. The two fine-tune phases described above were then run on the 6-layer model itself. So this model carries the 12L's base training, not its fine-tuning.
That 12-layer model's own fine-tuning is still in progress and is not finished. It sits between this 1.4B model and the full 36-layer teacher: heavier and slower, but closer to the teacher on fidelity, particularly at higher upscale factors where six layers give up the most ground.
It may be published separately once that fine-tuning completes and is evaluated. Nothing about it is final yet β no timeline, no promises, and it only ships if the numbers justify it, measured the same way as here. Treat this section as a note on lineage, not an announcement.
Why a 12-layer model is worth having
Between this 1.4B and the 36-layer teacher there is a large, under-served gap. The 12L is aimed squarely at it:
| 1.4B (6L) | 2.8B (12L) | teacher (36L) | |
|---|---|---|---|
| transformer layers | 6 | 12 | 36 |
| weights on disk (fp16) | 2.69 GB | 5.22 GB | 15.35 GB |
| vs the 7b sharp teacher | 5.7Γ smaller | 2.9Γ smaller | β |
| fidelity | good | closer to the 7b sharp teacher | reference |
Three layers' worth of capacity buys real quality, and 5.22 GB still fits comfortably where 15.35 GB does not β a 16 GB laptop, a mid-range GPU, a machine already holding a generation model in memory. It is also faster than the teacher for the same structural reason this model is: fewer layers means less transformer work and a smaller activation footprint, and the advantage widens at higher upscale factors where the 36-layer teacher starts fighting for memory.
So the three form a ladder rather than a hierarchy with one winner:
- 1.4B β smallest and fastest; the choice when memory or latency is the binding constraint, and the only one of the three with a realistic path onto a phone.
- 2.8B β the balance point: most of the teacher's quality at roughly a third of its footprint. For many people with a normal desktop, this is likely the right default rather than a compromise.
- 7B teacher β maximum fidelity, if you have the machine for it.
Which one is "best" depends entirely on the hardware you are running and how much fine detail your work actually needs.
Quantized