Uncertainty quantification with PnP-ULA.#

This code shows you how to use sampling algorithms to quantify uncertainty of a reconstruction from incomplete and noisy measurements.

ULA obtains samples by running the following iteration:

\[x_{k+1} = x_k + \alpha \eta \nabla \log p_{\sigma}(x_k) + \eta \nabla \log p(y|x_k) + \sqrt{2 \eta} z_k\]

where \(z_k \sim \mathcal{N}(0, I)\) is a Gaussian random variable, \(\eta\) is the step size and \(\alpha\) is a parameter controlling the regularization.

The PnP-ULA method is described in the paper Laumont et al.[1].

import deepinv as dinv
from deepinv.utils.plotting import plot
import torch
from deepinv.utils import load_example

Load image from the internet#

This example uses an image of Messi.

device = dinv.utils.get_device()

x = load_example("messi.jpg", img_size=32).to(device)
Selected GPU 0 with 24085.25 MiB free memory

Define forward operator and noise model#

This example uses inpainting as the forward operator and Gaussian noise as the noise model.

sigma = 0.1  # noise level
physics = dinv.physics.Inpainting(mask=0.5, img_size=x.shape[1:], device=device)
physics.noise_model = dinv.physics.GaussianNoise(sigma=sigma)

# Set the global random seed from pytorch to ensure reproducibility of the example.
torch.manual_seed(0)
<torch._C.Generator object at 0x7f2ceabc8d10>

Define the likelihood#

Since the noise model is Gaussian, the negative log-likelihood is the L2 loss.

\[-\log p(y|x) \propto \frac{1}{2\sigma^2} \|y-Ax\|^2\]
# load Gaussian Likelihood
likelihood = dinv.optim.data_fidelity.L2(sigma=sigma)

Define the prior#

The score a distribution can be approximated using Tweedie’s formula via the deepinv.optim.ScorePrior class.

\[\nabla \log p_{\sigma}(x) \approx \frac{1}{\sigma^2} \left(D(x,\sigma)-x\right)\]

This example uses a pretrained DnCNN model. From a Bayesian point of view, the score plays the role of the gradient of the negative log prior The hyperparameter sigma_denoiser (\(sigma\)) controls the strength of the prior.

In this example, we use a pretrained DnCNN model using the deepinv.loss.FNEJacobianSpectralNorm loss, which makes sure that the denoiser is firmly non-expansive (see Terris et al.[2]), and helps to stabilize the sampling algorithm.

sigma_denoiser = 2 / 255
prior = dinv.optim.ScorePrior(
    denoiser=dinv.models.DnCNN(pretrained="download_lipschitz")
).to(device)

Create the MCMC sampler#

Here we use the Unadjusted Langevin Algorithm (ULA) to sample from the posterior defined in deepinv.sampling.ULAIterator. The hyperparameter step_size controls the step size of the MCMC sampler, regularization controls the strength of the prior and iterations controls the number of iterations of the sampler.

regularization = 0.9
step_size = 0.01 * (sigma**2)
iterations = int(5e3) if torch.cuda.is_available() else 10
params = {
    "step_size": step_size,
    "alpha": regularization,
    "sigma": sigma_denoiser,
}
f = dinv.sampling.sampling_builder(
    "ULA",
    prior=prior,
    data_fidelity=likelihood,
    max_iter=iterations,
    params_algo=params,
    thinning=1,
    verbose=True,
)

Generate the measurement#

We apply the forward model to generate the noisy measurement.

y = physics(x)

Run sampling algorithm and plot results#

The sampling algorithm returns the posterior mean and variance. We compare the posterior mean with a simple linear reconstruction.

mean, var = f.sample(y, physics)

# compute linear inverse
x_lin = physics.A_adjoint(y)

# compute PSNR
print(f"Linear reconstruction PSNR: {dinv.metric.PSNR()(x, x_lin).item():.2f} dB")
print(f"Posterior mean PSNR: {dinv.metric.PSNR()(x, mean).item():.2f} dB")

# plot results
error = (mean - x).abs().sum(dim=1).unsqueeze(1)  # per pixel average abs. error
std = var.sum(dim=1).unsqueeze(1).sqrt()  # per pixel average standard dev.
imgs = [x_lin, x, mean, std / std.flatten().max(), error / error.flatten().max()]
plot(
    imgs,
    titles=["measurement", "ground truth", "post. mean", "post. std", "abs. error"],
)
measurement, ground truth, post. mean, post. std, abs. error
  0%|          | 0/5000 [00:00<?, ?it/s]
  3%|β–Ž         | 134/5000 [00:00<00:03, 1333.41it/s]
  6%|β–Œ         | 289/5000 [00:00<00:03, 1454.83it/s]
  9%|β–‰         | 439/5000 [00:00<00:03, 1472.25it/s]
 12%|β–ˆβ–        | 589/5000 [00:00<00:02, 1480.42it/s]
 15%|β–ˆβ–        | 739/5000 [00:00<00:02, 1484.93it/s]
 18%|β–ˆβ–Š        | 889/5000 [00:00<00:02, 1487.72it/s]
 21%|β–ˆβ–ˆ        | 1038/5000 [00:00<00:02, 1485.85it/s]
 24%|β–ˆβ–ˆβ–Ž       | 1187/5000 [00:00<00:02, 1478.10it/s]
 27%|β–ˆβ–ˆβ–‹       | 1335/5000 [00:00<00:02, 1472.99it/s]
 30%|β–ˆβ–ˆβ–‰       | 1483/5000 [00:01<00:02, 1469.54it/s]
 33%|β–ˆβ–ˆβ–ˆβ–Ž      | 1630/5000 [00:01<00:02, 1467.16it/s]
 36%|β–ˆβ–ˆβ–ˆβ–Œ      | 1777/5000 [00:01<00:02, 1465.52it/s]
 38%|β–ˆβ–ˆβ–ˆβ–Š      | 1924/5000 [00:01<00:02, 1464.40it/s]
 41%|β–ˆβ–ˆβ–ˆβ–ˆβ–     | 2071/5000 [00:01<00:02, 1463.60it/s]
 44%|β–ˆβ–ˆβ–ˆβ–ˆβ–     | 2218/5000 [00:01<00:01, 1463.05it/s]
 47%|β–ˆβ–ˆβ–ˆβ–ˆβ–‹     | 2365/5000 [00:01<00:01, 1462.67it/s]
 50%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ     | 2512/5000 [00:01<00:01, 1462.39it/s]
 53%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Ž    | 2659/5000 [00:01<00:01, 1462.21it/s]
 56%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œ    | 2806/5000 [00:01<00:01, 1462.07it/s]
 59%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‰    | 2953/5000 [00:02<00:01, 1461.99it/s]
 62%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–   | 3100/5000 [00:02<00:01, 1461.96it/s]
 65%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–   | 3247/5000 [00:02<00:01, 1461.90it/s]
 68%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Š   | 3394/5000 [00:02<00:01, 1461.88it/s]
 71%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   | 3541/5000 [00:02<00:00, 1461.84it/s]
 74%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–  | 3688/5000 [00:02<00:00, 1461.83it/s]
 77%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‹  | 3835/5000 [00:02<00:00, 1461.83it/s]
 80%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‰  | 3982/5000 [00:02<00:00, 1461.79it/s]
 83%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Ž | 4129/5000 [00:02<00:00, 1461.76it/s]
 86%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œ | 4276/5000 [00:02<00:00, 1461.73it/s]
 88%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Š | 4423/5000 [00:03<00:00, 1461.79it/s]
 91%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–| 4570/5000 [00:03<00:00, 1461.78it/s]
 94%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–| 4717/5000 [00:03<00:00, 1461.77it/s]
 97%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‹| 4864/5000 [00:03<00:00, 1461.78it/s]
100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 5000/5000 [00:03<00:00, 1464.54it/s]
Iteration 4999, current converge crit. = 1.42E-05, objective = 1.00E-03
Iteration 4999, current converge crit. = 3.42E-04, objective = 1.00E-03
Linear reconstruction PSNR: 8.55 dB
Posterior mean PSNR: 22.32 dB
References:

Total running time of the script: (0 minutes 3.571 seconds)

Gallery generated by Sphinx-Gallery