File size: 14,694 Bytes
b415c47
 
 
 
ebbbb8f
 
b415c47
 
 
ebbbb8f
 
 
b415c47
 
ebbbb8f
b415c47
ebbbb8f
 
 
 
 
b415c47
 
a1b7f41
b415c47
a1b7f41
b415c47
ea18e2b
 
 
 
b415c47
a1be0f2
 
 
 
9fd5dac
 
61f1f03
9fd5dac
a1b7f41
496bb0c
b415c47
ea18e2b
 
 
 
 
 
a1b7f41
 
dedd8ae
 
fe92ffa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e9f69fa
 
 
 
dedd8ae
 
fe92ffa
 
 
 
dedd8ae
fe92ffa
 
 
dedd8ae
fe92ffa
dedd8ae
 
fe92ffa
 
 
 
 
 
 
 
 
dedd8ae
 
a1b7f41
 
 
 
f0ea422
 
 
a1b7f41
f0ea422
 
 
 
 
a1b7f41
f0ea422
 
 
a1b7f41
f0ea422
 
 
a1b7f41
b415c47
f0ea422
 
 
a1b7f41
 
 
 
 
 
e126050
b415c47
a1b7f41
b415c47
ea18e2b
 
 
 
 
b415c47
a1b7f41
 
 
 
b415c47
 
 
a1b7f41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6261f3c
 
 
b415c47
a1b7f41
 
 
 
b415c47
a1b7f41
 
b415c47
fcae874
 
 
 
 
 
a20c1d7
485f865
a20c1d7
af00dc5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379195c
 
 
 
af00dc5
 
 
485f865
 
ea18e2b
 
 
 
 
 
 
 
 
 
485f865
 
a1be0f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16b5a8c
 
 
 
 
d8d9275
 
485f865
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1c6e960
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485f865
 
b415c47
 
 
 
 
 
 
 
 
 
 
 
a1b7f41
ac25f3a
 
 
 
 
 
 
 
 
 
 
 
 
9fd5dac
 
ac25f3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
license: apache-2.0
language:
- en
library_name: pytorch
pipeline_tag: image-to-image
tags:
- super-resolution
- image-restoration
- image-to-image
- sisr
- real-world-restoration
- pytorch
- onnx
- tensorrt
- spandrel
- chainner
- transformer
- attention
datasets:
- Phips/lucid-cc0-v2-hc-512
---

# HEART

**Hybrid Efficient Attention with Rank-factorized bias Transformer**

A super-resolution architecture that is simple to train and fast to run.
Released models are **verified** with **traiNNer-redux** (training) and
**ONNX Runtime** (inference); TensorRT and other runtimes are expected to work
(same standard op set) but are not tested here.

Part of the **BODY** suite by **Philip Hofmann** β€” networks built to be
real-world usable first, not benchmark-chasing. See
[Relation to NERVE](#relation-to-nerve) for which one to pick.

All HEART models were trained on a single consumer GPU (**RTX 3060, 12 GB**) β€” this network trains and runs on ordinary consumer hardware, no server-class GPU required.

**Links:** [NERVE](https://huggingface.co/Phips/NERVE) Β· [HEART](https://huggingface.co/Phips/HEART) Β· [NERVE demo](https://huggingface.co/spaces/Phips/nerve) Β· [HEART demo](https://huggingface.co/spaces/Phips/heart) Β· [convert a checkpoint](https://huggingface.co/spaces/Phips/nerve-convert) Β· [local app](https://github.com/Phhofm/nerve-app) Β· [train NERVE](https://github.com/Phhofm/nerve-train) Β· [train HEART](https://github.com/Phhofm/heart-train)

> Short version: copy `heart_arch.py` into traiNNer-redux, train with
> `lq_size: 96`, export to ONNX, run it (ONNX Runtime verified). Details below.

![HEART 4x on anime β€” input | bicubic | release | OTF GAN](examples/ani_compare.png)
*Input (as-is) | bicubic x4 | HEART 4x release | HEART 4x OTF GAN.*

![HEART 4x on a noisy image](examples/noisy_compare.png)
![HEART 4x on a Stable-Diffusion image](examples/stablediffusion_compare.png)

---

## Why HEART?

There are strong and established SR networks already. This is the honest case
for when HEART is worth your time β€” and when it is not.

This project comes out of a trainer's frustration rather than a benchmark goal.
After training hundreds of SISR models (see
[Phhofm/models](https://github.com/Phhofm/models)), the recurring annoyance was
networks that felt like *paper* networks: they chase leaderboard metrics with
techniques that leave you juggling an unfused and a fused checkpoint, or whose
dynamic-ONNX conversion is fragile. This network is the opposite of that β€” built
so a trainer can pick it up, train it, and convert it to dynamic ONNX without
headaches.

**Strengths of HEART**
- **Real-world usability over metric chasing.** OTF models are trained on the
  full Real-ESRGAN degradation chain and restore real photos, not just clean
  bicubic downsamples.
- **Competitive quality at lower cost.** In our tests it matches HAT_M on PSNR
  (25.02 vs 25.05 dB, within noise) while using ~half the training VRAM
  (1.44 vs 2.84 GB), and at bf16 inference it is ~1.2x faster with ~2.7x less
  VRAM.
- **One file, exports cleanly.** Single-file pure PyTorch; dynamic ONNX on the
  first try; no fusion step or fused/unfused checkpoint pairs.
- **Stable, simple training.** i-LN keeps bf16 AMP stable without fp32
  gymnastics.
- **FlashAttention / SDPA friendly.** No attention masks, no position-bias
  table lookups β€” RIB replaces them with a dot product.

**When to pick something else.** For maximum benchmark PSNR or a small mobile
model, use NERVE or one of the community networks below. HEART is the desktop /
server quality tier.

Full architecture details, design rationale and ablation results:
[`docs/EXPERIMENTS.md`](docs/EXPERIMENTS.md).

**Credit where it is due.** None of this exists in a vacuum. SISR has many
strong architectures and generous community members, for example HAT, ATD and
DAT2, the community network work of [umzi](https://github.com/umzi2) and
[Kim2091](https://github.com/Kim2091), fdat by
[sharekhan](https://github.com/stinkybread), and
[the-database](https://github.com/the-database) (traiNNer-redux, its benchmarks
and its models). This list is far from complete β€” apologies to anyone not named;
the SISR community has a great many people contributing a great deal.

---

## Quickstart

### As a trainer (want to train your own model)

HEART is a normal [traiNNer-redux](https://github.com/the-database/traiNNer-redux)
architecture β€” no special steps.

```bash
# 1. Add the architecture (auto-registers: traiNNer scans *_arch.py)
cp heart_arch.py <traiNNer-redux>/traiNNer/archs/

# 2. Copy a training config and point it at your data
cp configs/4x_HEART_release.yml <traiNNer-redux>/options/

# 3. Train
cd <traiNNer-redux>
python train.py -opt options/4x_HEART_release.yml --auto_resume

# 4. Export to optimized dynamic ONNX (official converter)
cp configs/4x_HEART_onnx.yml <traiNNer-redux>/options/
python convert_to_onnx.py -opt options/4x_HEART_onnx.yml
```

Warm-start from the released pretrains by setting `path.pretrain_network_g` in
your training config (e.g. `models/heart_4x_pretrain.safetensors` for a 4x run).

**Rules for good results** (learned the hard way β€” all HEART models follow these):
- `lq_size` **must be a multiple of 32** (the window size). Use **96**. Other
  sizes (e.g. 80) teach the model a fake "edges are mirrored" response β†’
  visible bright band + tile grid, worse with GAN loss.
- Mild noise: `noise_range: [1,15]`, `noise_range2: [1,10]`.
- Batch 2 is fine on a 12 GB card at lq96.
- `--auto_resume` is recommended when running `train.py`.

### As a user (want to upscale images)

- **ONNX Runtime** (verified): run the `onnx/*.onnx` files (dynamic H/W).
  TensorRT should also work (same ops) but is untested here.
- **chaiNNer**: load the `onnx/*.onnx` files directly. Loading the
  `.safetensors` via spandrel needs HEART registered in your chaiNNer/spandrel
  build (not upstream yet).

- Input: RGB, any size (dynamic H/W).
- Scale: 4x (`heart_4x_*`), 2x (`heart_2x`).

---

## Models

| File | Role | Best for |
|---|---|---|
| `models/heart_4x_pretrain.safetensors` | **4x official pretrain** | starting any 4x training |
| `models/heart_2x.safetensors` | **2x official pretrain** | starting 2x training |
| `models/heart_4x_otf_v2.safetensors` | 4x OTF fidelity | **the pretrain for OTF GAN finetunes**; clean real-world |
| `models/heart_4x_otf_gan.safetensors` | 4x OTF GAN | sharpest real-world output |
| `onnx/heart_4x_pretrain_fp32_op17.onnx` | 4x pretrain, dynamic ONNX | inference |
| `onnx/heart_2x_fp32_op17.onnx` | 2x, dynamic ONNX | inference |
| `onnx/heart_4x_otf_v2_fp32_op17.onnx` | OTF fidelity, dynamic ONNX | inference |
| `onnx/heart_4x_otf_gan_fp32_op17.onnx` | OTF GAN, dynamic ONNX | inference |

All ONNX files are **dynamic-shape fp32, opset 17, onnxslim-optimized**.

**Pretrain chain** (how the models relate):

```
4x pretrain ──→ OTF v2 (fidelity) ──→ OTF GAN
     └──→ 2x finetune
```

---

## Visual examples

`examples/` has full-resolution comparisons on the Real-ESRGAN test set (input
fed as-is), each showing: **input** vs **HEART 4x release** vs **HEART 4x OTF
fidelity** vs **HEART 4x OTF GAN**.

- `examples/00003_compare.png` (skyline)
- `examples/ADE_val_00000114_compare.png` (scene)
- `examples/OST_009_compare.png`
- `examples/wolf_gray_compare.png`

`examples/inputs/` also contains the original Real-ESRGAN test images, so you
can inspect full-size inputs and run the models yourself.

A second set (`realphoto`, `ani`, `manga`, `text`, `noisy`, `stablediffusion`)
feeds real images in **as-is** (not downscaled):
- `examples/*_compare.png` β€” whole image: **input** | **bicubic x4** |
  **HEART 4x release** | **HEART 4x OTF GAN**.
- `examples/*_1to1.png` β€” a **1:1 native-pixel center crop** (detail comparison).
The source inputs are in `examples/inputs/`.

---

## What we actually tested

Every design choice came from a measured experiment, not a guess. The full log
(including dead ends, benchmarks and the "considered and decided" register) is
in [`docs/EXPERIMENTS.md`](docs/EXPERIMENTS.md). Highlights:

- **HEART vs HAT_M** (30k, identical settings): effectively a tie on quality
  (25.02 vs 25.05 dB, within noise) at **~half the peak VRAM** (1.44 vs
  2.84 GB), and **~1.16-1.24x faster + ~2.7x less VRAM at inference in bf16**
  (the deployment-relevant precision).
- **ATD**: OOM'd at iter 1 on a 12 GB card on the same config β€” a real
  deployment result.
- **torch.compile**: ~1.5x training speed, quality-neutral (validated).
- **i-LN** (norm choice) for stable bf16 AMP with no fp32 gymnastics.
- **RIB** replaces HAT's relative-position-bias table + OCAB with a
  FlashAttention-friendly dot product.
- **Shifted-window bug** found and fixed during ablations (it was quietly
  costing quality).
- **Runtime validation**: every released `.safetensors` loads (strict) and runs;
  every `.onnx` loads in ONNX Runtime.
- **Not tested here** (community welcome): TensorRT / DirectML *speed* and
  fixed-shape static exports, CoreML, and on-device benchmarks.
- **OTF GAN lq96 window alignment**: diagnosed the bright band + tile grid and
  fixed it via the `lq_size` multiple-of-32 rule.

---

## Training data & license

All released HEART models were trained **only** on
[`Phips/lucid-cc0-v2-hc-512`](https://huggingface.co/datasets/Phips/lucid-cc0-v2-hc-512),
a **CC0** dataset β€” the released weights are clean to use commercially.

HEART ties HAT on quality at a much lower cost, but we did **not** chase the
benchmark leaderboard. If higher PSNR/LPIPS is your goal, training on larger
academic datasets (DIV2K, Flickr2K, LSDIR, ...) is expected to help.

---

## Relation to NERVE

HEART and NERVE are the two members of the **BODY** suite (by Philip Hofmann):
same philosophy β€” real-world usability over benchmark scores β€” different tiers.

| | **HEART** | **NERVE** |
|---|---|---|
| size | ~16.7M params | ~1.8M params |
| design | attention (FlashAttention-friendly) | pure convolution |
| use when | you want the **best quality** and have the compute | you want **speed, small size, and dead-simple deployment** (mobile/edge/web) |
| cost | heavier to run | a fraction of the cost |

Rule of thumb: **HEART for maximum quality, NERVE for maximum practicality.**
Both export to dynamic ONNX; NERVE additionally converts to ncnn cleanly
because it has no attention ops.

Side-by-side comparisons on the same inputs (`input | bicubic | both release
models | both OTF GAN models`), each with a 1:1 crop:
`examples/OST_009_nerve_vs_heart_*` and `examples/00003_nerve_vs_heart_*`
(Real-ESRGAN test set), plus `examples/realphoto_nerve_vs_heart_*` and
`examples/noisy_nerve_vs_heart_*`.


---

## Repository layout

```
heart_arch.py            the architecture (single file)
configs/                 example training configs (release, OTF, OTF GAN, ONNX export)
models/                  pretrained checkpoints (see table above)
onnx/                    dynamic fp32 ONNX (opset 17, onnxslim-slimmed)
examples/                visual comparisons on the Real-ESRGAN test set
docs/EXPERIMENTS.md      everything that shaped the architecture
scripts/export_heart.py  checkpoint β†’ dynamic ONNX exporter
```

---

## Help wanted (community welcome)

The goal here is **real-world usability** β€” easy training, hassle-free dynamic
ONNX, simple and robust to maintain β€” **not** leaderboard chasing. There's
plenty worth measuring. Pick anything below and open an issue/PR/discussion
with your numbers.

**Benchmarks**
- Full-length training on an academic dataset (DIV2K / Flickr2K / LSDIR) and a
  head-to-head against HAT / HAT-iLN and other transformers.
- Perceptual metrics (LPIPS / TopIQ / MUSIQ) on real-world degraded images.

**Speed & deployment** (not benchmarked here)
- TensorRT / DirectML latency + peak VRAM on the dynamic ONNX.
- Static-shape ONNX (e.g. `1x3x256x256`) vs dynamic: speed + memory.
- fp16 vs fp32 ONNX.
- CoreML conversion + on-device test.

**Models & code**
- More finetunes (illustration, manga, denoise).
- Architecture experiments β€” see [`docs/EXPERIMENTS.md`](docs/EXPERIMENTS.md)
  for what we already tested (and rejected).

Small, measured contributions are welcome.

---

## License & credits

Apache-2.0 (this repository's code and pretrained weights).

HEART is a re-implementation/simplification inspired by:
- **HAT** (arXiv:2205.04437) and **HAT-iLN** (arXiv:2504.06629) β€” architecture
  family and i-LN.
- **SST / RIB** (arXiv:2603.06738) β€” Rank-factorized Implicit Bias, replacing
  HAT's relative-position-bias table + OCAB with a FlashAttention-friendly
  dot-product position bias.

The architecture file is original code; verify attribution/compatibility
against those papers before commercial redistribution.

## Update β€” 2026-09-30: speed benchmark (HEART vs HAT_M)

Measured on an **RTX 3060**, input `1x3x256x256` (a typical SR tile), fp16,
warmup 5, mean of 20 runs. ONNX is static-shape; TensorRT uses the same builder
options as NERVE's published config (`builderOptimizationLevel=5` + CUDA graphs).

| model | params | PyTorch fp16 | ONNX fp16 | **TensorRT fp16** | peak VRAM |
|---|---|---|---|---|---|
| **HEART 4x** | 16.7M | 816 ms | 745 ms | **266 ms** (208 FPS) | **679 MB** |
| HEART 4x OTF GAN | 16.7M | 809 ms | 753 ms | **270 ms** | 679 MB |
| HAT_M 4x | 20.8M | 1485 ms | 849 ms | 598 ms (107 FPS) | 2428 MB |

![HEART vs HAT_M β€” PyTorch / ONNX / TensorRT speed and VRAM](docs/heart_speed.png)

**Verdict.** At a 256px tile HEART is **~1.8x faster than HAT_M** in PyTorch and
uses **~3.6x less VRAM** (679 MB vs 2.4 GB) β€” a bigger margin than our earlier
30k-iteration note suggested. Under **TensorRT** both get dramatically faster,
and HEART keeps a **~2.2x** lead (266 ms vs 598 ms).

**TensorRT is a big win for HEART** (3.1x over PyTorch), much larger than for
NERVE (1.05x) β€” a bigger model gives the builder more to optimize. Use it:

```python
import onnxruntime as ort

sess = ort.InferenceSession(
    "4x_HEART_fp16.onnx",               # static shape, e.g. 1x3x256x256
    providers=[
        ("TensorrtExecutionProvider", {
            "trt_fp16_enable": True,
            "trt_builder_optimization_level": 5,
            "trt_cuda_graph_enable": True,
            "trt_engine_cache_enable": True,
            "trt_engine_cache_path": "trt_cache",
            "trt_timing_cache_enable": True,
        }),
        "CUDAExecutionProvider",
    ],
)
```

Caveats: this is a **single size (256px)** on one GPU, and ONNX/TensorRT need a
**static shape** β€” export one per size you use. HEART is **ONNX-only** (no ncnn;
its attention ops don't convert cleanly).