# DeepJSCC vs. Capacity-Matched JPEG over AWGN
Train an image encoder and decoder jointly over an additive white Gaussian noise (AWGN) channel,
then compare the learned system with a capacity-matched JPEG reference at a fixed bandwidth ratio.
Use this page for a compact implementation and training check. For a research-facing comparison
with no-CSI slow fading and a bandwidth sweep, use
[DeepJSCC over slow Rayleigh fading](deepjscc_slow_rayleigh.md).
```{warning}
**Experimental tutorial evidence.** The completed result on this page uses one trained model, four
Kodak crops, and three channel-noise seeds. It is not a paper-grade population claim or a
publication-ready result. Authoritative Kodak redistribution terms have not been archived, so this
site does not publish the source crop or reconstruction gallery.
```
## What this demo measures
The two paths are:
```text
Capacity-matched JPEG: image → highest JPEG quality fitting the capacity budget
→ ideal error-free delivery → JPEG decode
DeepJSCC: image → 0.5 learned complex symbols/pixel → AWGN
→ learned reconstruction
```
The bandwidth ratio is
`κ = 0.5 complex channel uses per source pixel`. For an SNR coordinate in decibels,
`γ = 10^(SNR_dB/10)` and the ideal complex-AWGN budget is
`capacity bpp = κ × log2(1 + γ)`.
At each SNR, the JPEG reference searches qualities `1–95` and chooses the highest native bitstream
that fits this budget. If quality `1` does not fit, it records an outage and returns the benchmark's
declared gray reconstruction. This is an optimistic channel-coding assumption for one fixed JPEG
implementation, not an upper bound over digital separation or all source codecs. The comparison is
modeled on the protocol in the
[DeepJSCC paper](https://doi.org/10.1109/TCCN.2019.2919300).
PSNR, measured in decibels, and MS-SSIM, reported on an approximately `0–1` scale here, both increase
with reconstruction quality. Training minimizes image mean squared error (MSE); the benchmark
reports all three measures. This experiment varies SNR at one fixed `κ`, so it is not a
rate-distortion sweep.
## Before you start
You need:
- a Git clone of Noema, a supported Python version (`3.11`–`3.13`), and `uv`;
- network access for dependencies and the 24 hash-verified Kodak PNGs;
- port `8766` available if you choose the Workbench route;
- enough time for an 80-epoch starter-model training run and a 32-run benchmark.
The starter selects CUDA when available and otherwise uses CPU. Seeds and file hashes reproduce the
protocol, but training is not guaranteed to be bit-identical across hardware and library builds.
Your result ID and exact metrics may therefore differ from the reference result below.
Fetch the dataset once before choosing a workflow:
```bash
cd "$(git rev-parse --show-toplevel)"
uv sync --extra onnx --extra wireless
uv run --extra onnx noema data fetch kodak \
--directory .noema/datasets/kodak
```
```{caution}
The fetcher records the mirror URL and file hashes, but the repository currently records the Kodak
usage statement as an unverified third-party statement. Confirm the applicable terms before using
the data, and do not redistribute the images or their reconstructions from this tutorial.
```
## How the Noema pieces fit
```text
recipe
→ exported training bundle and interface contract
→ hash-verified data contract and train/validation split
→ paired encoder/decoder artifact
→ frozen benchmark pack
→ result bundle with metrics and provenance
```
The recipe defines the communication problem and replaceable interfaces. The exported bundle
contains the trainer-neutral contract plus a PyTorch starter. Training returns the encoder and
decoder together, and the benchmark evaluates that frozen pair without changing the declared
resource budget.
## Choose one workflow
The CLI quick start and the guided Workbench route are alternatives. Do not run both into the same
bundle directory. The exporter rejects a non-empty output directory unless overwrite is explicitly
enabled, so choose a fresh bundle path for each attempt.
### Option A — CLI training summary
Run this block from any directory inside the Git clone after fetching Kodak. It exports the bundle,
attaches the starter, validates the contract, trains and evaluates the pair, and runs the benchmark.
```bash
(
set -euo pipefail
ROOT="$(git rev-parse --show-toplevel)"
BUNDLE="$ROOT/.noema/training_exports/deepjscc_image"
cd "$ROOT"
uv run --project "$ROOT" --extra onnx --extra wireless noema differentiable export \
"$ROOT/recipes/deepjscc_kodak_awgn_train.yaml" \
--training-plan \
"$ROOT/demo_trainings/deepjscc_image_reconstruction/training_plan.yaml" \
--out "$BUNDLE"
uv run --project "$ROOT" --extra onnx --extra wireless python \
"$ROOT/demo_trainings/prepare_example.py" deepjscc-image "$BUNDLE" \
--project-root "$ROOT"
cd "$BUNDLE"
uv run --project "$ROOT" --extra onnx --extra wireless python validate_contract.py
uv run --project "$ROOT" --extra onnx --extra wireless python train_demo.py
uv run --project "$ROOT" --extra onnx --extra wireless python evaluate_demo.py
cd "$BUNDLE/reference_training"
uv run --project "$ROOT" --extra onnx --extra wireless python build_benchmark.py
cd "$ROOT"
uv run --project "$ROOT" --extra onnx --extra wireless noema benchmark validate \
"$BUNDLE/reference_training/benchmark_pack.yaml"
uv run --project "$ROOT" --extra onnx --extra wireless noema benchmark run \
"$BUNDLE/reference_training/benchmark_pack.yaml"
)
```
This example reads a file-backed dataset directly, so it does not need a tensor-capture stage.
The final command prints the result ID and paths to `result.json`, `metrics.csv`, `recipes.csv`, and
`summary.md`.
### Option B — guided Workbench workflow
#### 1. Open the template
Start Noema in Terminal 1 and leave the process running:
```bash
cd "$(git rev-parse --show-toplevel)"
uv run --extra onnx --extra wireless noema ui serve --port 8766
```
Open [http://127.0.0.1:8766](http://127.0.0.1:8766), select **Browse template recipes**, then open
**Semantic communication** > **Image reconstruction** >
**Image reconstruction — DeepJSCC over AWGN**.
The recipe declares:
- Kodak images `01–20` for the deterministic train/validation partition;
- Kodak images `21–24` for the benchmark only;
- a training SNR grid of `[-4, 0, 4, 8, 12, 16]` dB;
- unit average transmitted complex-symbol power;
- paired trainable **Sender** and **Receiver** interfaces.
#### 2. Export from Workbench
Configure Workbench:
1. Under **Operation Training Capabilities**, select **Train/replace** for **Sender** and
**Receiver**.
2. Set **Bundle directory** to `.noema/training_exports/deepjscc_image_workbench`.
3. Select **PyTorch** under **Support framework**.
4. Leave **Overwrite generated files** disabled for a new bundle. Enable it only when you
intentionally want to regenerate Noema-owned contract files in an existing bundle.
5. Select **Export training bundle**.
In Terminal 2, attach the included starter:
```bash
cd "$(git rev-parse --show-toplevel)"
uv run --extra onnx --extra wireless python demo_trainings/prepare_example.py \
deepjscc-image .noema/training_exports/deepjscc_image_workbench
```
Inspect `data_contract.yaml` to confirm the selected files, hashes, split, and crop policy.
#### 3. Train and return the endpoint pair
```bash
cd "$(git rev-parse --show-toplevel)/.noema/training_exports/deepjscc_image_workbench"
uv run --project ../../.. --extra onnx --extra wireless python validate_contract.py
uv run --project ../../.. --extra onnx --extra wireless python train_demo.py
uv run --project ../../.. --extra onnx --extra wireless python evaluate_demo.py
```
The residual convolutional model downsamples each spatial dimension by eight and emits 32 complex
feature maps, giving `32 / (8 × 8) = 0.5` complex channel uses per source pixel. Crops, flips, and
rotations augment the 16 training images while the recorded source files and hashes remain fixed.
The paired artifact contains:
- `artifacts/encoder.onnx` and `artifacts/decoder.onnx`;
- `trained_artifact.yaml`, which binds the pair to its interfaces;
- `reference_training/training_history.json`;
- `reference_training/evaluation_metrics.json`.
Workbench discovers the pair automatically. Select **Validate returned model** and continue only
after the interface validation succeeds.
#### 4. Build and run the benchmark
```bash
cd "$(git rev-parse --show-toplevel)"
cd .noema/training_exports/deepjscc_image_workbench/reference_training
uv run --project ../../../.. --extra onnx --extra wireless python build_benchmark.py
cd ../../../..
uv run --extra onnx --extra wireless noema benchmark validate \
.noema/training_exports/deepjscc_image_workbench/reference_training/benchmark_pack.yaml
uv run --extra onnx --extra wireless noema benchmark run \
.noema/training_exports/deepjscc_image_workbench/reference_training/benchmark_pack.yaml
```
### Progress and recovery
| Stage | Successful checkpoint | Safe recovery |
| --- | --- | --- |
| Dataset fetch | 24 Kodak files pass the recorded hashes | Rerun the fetch command |
| Export | `data_contract.yaml` and starter support files exist | Choose a fresh bundle, or deliberately enable overwrite |
| Contract validation | `validate_contract.py` reports a valid contract | Fix the reported file, hash, or interface error before training |
| Training/evaluation | Both ONNX files and `trained_artifact.yaml` exist | Rerun from the bundle; training does not resume an interrupted epoch sequence |
| Benchmark | The CLI prints `RESULT_ID` and result paths | Rebuild the pack only if its inputs changed; otherwise rerun the benchmark |
## Method and benchmark protocol
The benchmark grid is `[-6, -4, -2, 0, 4, 8, 12, 16]` dB. The model trains on
`[-4, 0, 4, 8, 12, 16]` dB, so `−2` dB is an unseen interpolation point and `−6` dB tests
extrapolation below the training range.
The same channel-noise seeds—`71001`, `72001`, and `73001`—are reused at every SNR. This
common-random-number design pairs the SNR comparisons; the seeds are not fresh independent draws at
each coordinate. The capacity-matched JPEG reference is deterministic and runs once per SNR.
Both methods receive a maximum budget of `0.5` complex channel uses per source pixel. DeepJSCC emits
that number directly. The JPEG model reserves the same full block even when its native bitstream
uses less of the ideal capacity or the run is in outage.
The current JPEG recipe fixes the encoder configuration, including 4:2:0 subsampling, non-progressive
output, and qualities `1–95`. The
[current recipe template](../../recipes/jpeg_capacity_matched_kodak_awgn.yaml) may evolve; the
snapshot manifest records hashes for the exact per-run recipes used by the completed result.
## Checks before interpretation
Before interpreting a new result, verify that:
1. every learned run uses the same paired-artifact identity;
2. images `21–24` do not appear in the training contract;
3. both methods are admitted under the `0.5`-use resource limit;
4. every selected JPEG bitstream is at or below its ideal capacity budget;
5. quality `0` in the operating-point table is interpreted as the declared outage sentinel.
## Completed reference result
```{warning}
This stored result is descriptive tutorial evidence. Its 95% intervals cover three channel-noise
seeds for one frozen model and the same four crops. They do not cover training initialization,
model-selection, or image-population uncertainty.
```
Reference result identity
`20260727T235925Z_semantic_comm.digital_vs_deepjscc_post_training_v3` contains 32 runs: one
deterministic capacity-matched JPEG run and three learned noise realizations at each of eight SNR
coordinates.
PSNR chart unavailable. The accessible quality table below contains the plotted means; the downloadable chart data contains interval bounds.
MS-SSIM chart unavailable. The accessible quality table below contains the plotted means; the downloadable chart data contains interval bounds.