Giter VIP home page Giter VIP logo

aangelopoulos / im2im-uq Goto Github PK

View Code? Open in Web Editor NEW
43.0 4.0 7.0 5.36 MB

Image-to-image regression with uncertainty quantification in PyTorch. Take any dataset and train a model to regress images to images with rigorous, distribution-free uncertainty quantification.

License: MIT License

Python 100.00%
unet uncertainty uncertainty-quantification uncertainty-estimation quantile-regression image-to-image-translation biomedical-image-processing biomedical-data-science computational-biology computational-imaging mri machine-learning microscopy pytorch

im2im-uq's Introduction

im2im-uq

A platform for image-to-image regression with rigorous, distribution-free uncertainty quantification.


An algorithmic MRI reconstruction with uncertainty. A rapidly acquired but undersampled MR image of a knee (A) is fed into a model that predicts a sharp reconstruction (B) along with a calibrated notion of uncertainty (C). In (C), red means high uncertainty and blue means low uncertainty. Wherever the reconstruction contains hallucinations, the uncertainty is high; see the hallucination in the image patch (E), which has high uncertainty in (F), and does not exist in the ground truth (G).

Summary

This repository provides a convenient way to train deep-learning models in PyTorch for image-to-image regression---any task where the input and output are both images---along with rigorous uncertainty quantification. The uncertainty quantification takes the form of an interval for each pixel which is guaranteed to contain most true pixel values with high-probability no matter the choice of model or the dataset used (it is a risk-controlling prediction set). The training pipeline is already built to handle more than one GPU and all training/calibration should run automatically.

The basic workflow is

  • Define your dataset in core/datasets/.
  • Create a folder for your experiment experiments/new_experiment, along with a file experiments/new_experiment/config.yml defining the model architecture, hyperparameters, and method of uncertainty quantification. You can use experiments/fastmri_test/config.yml as a template.
  • Edit core/scripts/router.py to point to your data directory.
  • From the root folder, run wandb sweep experiments/new_experiment/config.yml, and run the resulting sweep.
  • After the sweep is complete, models will be saved in experiments/new_experiment/checkpoints, the metrics will be printed to the terminal, and outputs will be in experiments/new_experiment/output/. See experiments/fastmri_test/plot.py for an example of how to make plots from the raw outputs.

Following this procedure will train one or more models (depending on config.yml) that perform image-to-image regression with rigorous uncertainty quantification.

There are two pre-baked examples that you can run on your own after downloading the open-source data: experiments/fastmri_test/config.yml and experiments/temca_test/config.yml. The third pre-baked example, experiments/bsbcm_test/config.yml, reiles on data collected at Berkeley that has not yet been publicly released (but will be soon).

Paper

Image-to-Image Regression with Distribution-Free Uncertainty Quantification and Applications in Imaging

@article{angelopoulos2022image,
  title={Image-to-Image Regression with Distribution-Free Uncertainty Quantification and Applications in Imaging},
  author={Angelopoulos, Anastasios N and Kohli, Amit P and Bates, Stephen and Jordan, Michael I and Malik, Jitendra and Alshaabi, Thayer and Upadhyayula, Srigokul and Romano, Yaniv},
  journal={arXiv preprint arXiv:2202.05265},
  year={2022}
}

Installation

You will need to execute

conda env create -f environment.yml
conda activate im2im-uq

You will also need to go through the Weights and Biases setup process that initiates when you run your first sweep. You may need to make an account on their website.

Reproducing the results

FastMRI dataset

  • Download the FastMRI dataset to your machine and unzip it. We worked with the knee_singlecoil_train dataset.
  • Edit Line 71 of core/scripts/router to point to the your local dataset.
  • From the root folder, run wandb sweep experiments/fastmri_test/config.yml
  • After the run is complete, run cd experiments/fastmri_test/plot.py to plot the results.

TEMCA2 dataset

  • Download the TEMCA2 dataset to your machine and unzip it. We worked with sections 3501 through 3839.
  • Edit Line 78 of core/scripts/router to point to the your local dataset.
  • From the root folder, run wandb sweep experiments/temca_test/config.yml
  • After the run is complete, run cd experiments/temca_test/plot.py to plot the results.

Adding a new experiment

If you want to extend this code to a new experiment, you will need to write some code compatible with our infrastructure. If adding a new dataset, you will need to write a valid PyTorch dataset object; you need to add a new model architecture, you will need to specify it; and so on. Usually, you will want to start by creating a folder experiments/new_experiment along with a config file experiments/new_experiment/config.yml. The easiest way is to start from an existing config, like experiments/fastmri_test/config.yml.

Adding new datasets

To add a new dataset, use the following procedure.

  • Download the dataset to your machine.
  • In core/datasets, make a new folder for your dataset core/datasets/new_dataset.
  • Make a valid PyTorch Dataset class for your new dataset. The most critical part is writing a __get_item__ method that returns an image-image pair in CxHxW order; see core/datasets/bsbcm/BSBCMDataset.py for a simple example.
  • Make a file core/datasets/new_dataset/__init__.py and export your dataset by adding the line from .NewDataset.py import NewDatasetClass (substituting in your filename and classname appropriately).
  • Edit core/scripts/router.py to load your new dataset, near Line 64, following the pattern therein. You will also need to import your dataset object.
  • Populate your new config file experiments/new_experiment/config.yml with the correct directories and experiment name.
  • Execute wandb sweep experiments/new_experiment/config.yml and proceed as normal!

Adding new models

In our system, there are two parts to a model---the base architecture, which we call a trunk (e.g. a U-Net), and the final layer. Defining a trunk is as simple as writing a regular PyTorch nn.module and adding it near Line 87 of core/scripts/router.py (you will also need to import it); see core/models/trunks/unet.py for an example.

The process for adding a final layer is a bit more involved. The final layer is simply a Pytorch nn.module, but it also must come with two functions: a loss function and a nested prediction set function. See core/models/finallayers/quantile_layer.py for an example. The steps are:

  • Create a final layer nn.module object. The final layer should also have a heuristic notion of uncertainty built in, like quantile outputs.
  • Specify the loss function is used to train a network with this final layer.
  • Specify a nested prediction set function that uses output of the final layer to form a prediction set. The prediction set should scale up and down with a free factor lam, which will later be calibrated. The function should have the same prototype as that on Line 34 of core/models/finallayers/quantile_layer.py for an example.
  • After creating the new final layer and related functions, add it to core/models/add_uncertainty.py as in Line 59.
  • Edit wandb sweep experiments/new_experiment/config.yml to include your new final layer, and run the sweep as normal!

im2im-uq's People

Contributors

aangelopoulos avatar apsk14 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

im2im-uq's Issues

Error in Hoeffding-Bentkus bound

Hello,

I think there might be an error in the computation of the Hoeffding-Bentkus bound:
In bounds.py in the function hoeffding_plus(mu, x, n), line 11, shouldn't the np.maximum be replaced by np.minimum? As it is know, if mu>=x, Hoeffding_plus returns 0. Therefore, whenever mu>muhat in line 19, hoeffding_mu will be zero. But this seems odd, as mu should be the upper confidence bound and in general be larger than muhat, right? Also in your paper https://arxiv.org/pdf/2101.02703.pdf it seems the other way around in Propositions 3,4.

Best,
Andi

Reproducing Results

Hi @aangelopoulos and @apsk14 ,

Thanks for your great work, I am trying to reproduce the paper results, because I would love to add the img2img conformal method to the Lightning-UQ-Box.

I have run your code as instructed in the README.md via the sweep for the FastMRI Dataset (singlecoil_train), but the data normalization is doing some strange things. The normalization parameters being computed (taken from the cached norm_params.pickle file) are as follows:

{
    'input_max': 30.512170791625977, 
    'input_min': -0.604563295841217, 
    'input_mean': 0.06673449277877808, 
    'input_std': 0.8941464991811945, 
    'output_max': 7.970857620239258, 
    'output_min': -0.0070489561185240746, 
    'output_mean': 0.1550026535987854, 
    'output_std': 0.2108842432498932
}

However, these values are quiet large given the data and have the effect that the inputs and targets have some strange statistics after being normalized (both with standard and min_max) when I check them during the loss or eval computation

GET THE METRICS INCLUDING SPATIAL MISCOVERAGE
dataset is map-style (not resettable)
Validation output 0
mean: -0.07451456785202026
std: 9.658681665314361e-05
min: -0.07463472336530685
max: -0.07418996095657349
model targets
mean: 0.0008978385012596846
std: 1.147075727203628e-05
min: 0.0008843542891554534
max: 0.0009457280393689871

Validation output 1
mean: -0.0745149627327919
std: 9.722328832140192e-05
min: -0.07463472336530685
max: -0.07417760789394379
model targets
mean: 0.0008978385012596846
std: 1.147075727203628e-05
min: 0.0008843542891554534
max: 0.0009457280393689871

Validation output 2
mean: -0.07451476156711578
std: 9.685881377663463e-05
min: -0.07463475316762924
max: -0.07417760789394379
model targets
mean: 0.0008978385012596846
std: 1.147075727203628e-05
min: 0.0008843542891554534
max: 0.0009457280393689871

Essentially, the inputs and output get normalized to basically constant values, with no variation in pixel values.

I went down this path because, I was a bit astound by the very low mse value and interval size reported in the paper, but also in the metrics that are being computed:

# IMG2IMG Results:
# Risk: 0.0847138
# Mean size: 0.001457
# Spearman: 0.231718
# Strat Risk: [0.086, 0.0839, 0.0839, 0.0851]
# MSE: 7.1185ee-06
# Spatial Miscoverage: mu 0.08471, sigma: 0.271718, min: 0.0, max: 1.0

I have a script with which I compute normalization statistics for datasets and get statistics that are quiet different and orders of magnitude lower:

{
    "input_min": 1.325229959547869e-06,
    "input_max": 0.0003247384447604418,
    "input_mean": 4.692213955797128e-05,
    "input_std": 7.761315646348521e-05,
    "output_min": 2.350711156395846e-06,
    "output_max": 0.00033284907112829387,
    "output_mean": 4.8656625481538975e-05,
    "output_std": 7.794056000420824e-05
}

However, when using those, the image statistics after normalization seem more reasonable (more pixel variance):

Validation output 0
mean: -0.4299814701080322
std: 0.14215296506881714
min: -0.6043229699134827
max: 0.26245826482772827
model targets
mean: 0.04337747395038605
std: 0.03413794934749603
min: -0.00697715999558568
max: 0.32631415128707886

Validation output 1
mean: -0.4297109842300415
std: 0.14215296506881714
min: -0.6041231155395508
max: 0.2342386245727539
model targets
mean: 0.04337747395038605
std: 0.03413794934749603
min: -0.00697715999558568
max: 0.32631415128707886

Validation output 2
mean: -0.4299924373626709
std: 0.14188271760940552
min: -0.6043405532836914
max: 0.2342386245727539
model targets
mean: 0.04337747395038605
std: 0.03413794934749603
min: -0.00697715999558568
max: 0.32631415128707886

I haven't found the exact issue for why I am observing the normalized images I am seeing with your script (although I am suspecting it in the RunningStats object` at the moment), but was hoping that I might have missed something obvious, before diving down deeper into the rabbit hole. I was hoping that you might be able to confirm my observations on your end or have any other pointers. Thanks in advance :)

UCB value in algorithm 2

Hi Anastasios,
I was wondering if there is an error in the paper, or rather I didn't understand the algorithm section.
The UCB value in algorithm 2 gets a value of 1 and immediately after that being evaluated against alpha, which when alpha is 0.1 means the inside of the while loop will never gets executed.
I believe the value should be 0 and UCB gets bigger with every iteration.
Thanks.
Liron.

Typo in evaluate_from_loss_table

Hey there,

I think in the function evaluate_from_loss_table in calibrate_model.py line 66 should be
idx_lambda = (RhatPlus <= alpha).nonzero()[0]
instead of
idx_lambda = (RhatPlus <= delta).nonzero()[0]

Maybe I am wrong, but from the paper I think you want RhatPlus to be less than alpha and then delta is the according confidence bound, right?

Best,
Andi

Prediction set size calculation

Hey,
Thank you for publishing the paper and for providing such an extensive code base.
I have a question regarding the calculation of the prediction set size. In the function get_rcps_metrics_from_outputs it seems the calculation of the size of the set is done based on random pixels (as seen below), is this indeed the case?
If this is indeed the case, could you please explain why would we prefer this to taking the average size across pixels?

sets_full = (sets[2]-sets[0]).flatten(start_dim=1).detach().cpu().numpy()
size_random_idxs = np.random.choice(sets_full.shape[1],size=sets_full.shape[0])  
size_samples = sets_full[range(sets_full.shape[0]),size_random_idxs]  
sizes = sizes + [torch.tensor(size_samples),]  

Thanks,
Eliahu

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.