plenoptic.MADCompetition#
- class plenoptic.MADCompetition(image, optimized_metric, reference_metric, minmax, metric_tradeoff_lambda=None, penalty_function=<function penalize_range>, penalty_lambda=0.1)[source]#
Synthesize a single maximally-differentiating image for two metrics.
Following the basic idea in [1], this class synthesizes a maximally-differentiating image for two given metrics, based on a given image. We start by adding noise to this image and then iteratively adjusting its pixels so as to either minimize or maximize
optimized_metricwhile holding the value ofreference_metricconstant.MADCompetiton accepts two metrics as its input. These should be callables that take two images and return a single number, and that number should be 0 if and only if the two images are identical (thus, the larger the number, the more different the two images).
Note that a full set of MAD Competition images consists of two pairs: a maximal and a minimal image for each metric. A single instantiation of
MADCompetitionwill generate one of these four images.- Parameters:
image (
Tensor) – A tensor, this is the image we use as the reference point.optimized_metric (
Module|Callable[[Tensor,Tensor],Tensor]) – The metric whose value you wish to minimize or maximize, which takes two tensors and returns a scalar.reference_metric (
Module|Callable[[Tensor,Tensor],Tensor]) – The metric whose value you wish to keep fixed, which takes two tensors and returns a scalar.minmax (
Literal['min','max']) – Whether you wish to minimize or maximizeoptimized_metric.metric_tradeoff_lambda (
float|None(default:None)) – Lambda to multiply byreference_metricloss and add tooptimized_metricloss. IfNone, we pick a value so the two initial losses are approximately equal in magnitude.penalty_function (
Callable[[Tensor],Tensor] (default:<function penalize_range at 0x7f41412b2160>)) – A function applied to the metamer during optimization, that returns a scalar penalty to be minimized. By penalizing certain properties of the image, like pixels values outside an allowed range, we can constrain those image properties. See Regularization penalty in the documentation for details and examples.penalty_lambda (
float(default:0.1)) – Weight of the penalty term. Must be non-negative.
References
Methods
get_progress(iteration[, iteration_selection])Return dictionary summarizing synthesis progress at
iteration.load(file_path[, map_location, ...])Load all relevant stuff from a .pt file.
objective_function([mad_image, image])Compute the MADCompetition synthesis loss.
save(file_path)Save all relevant variables in .pt file.
setup([initial_noise, optimizer, ...])Initialize the MAD image, optimizer, and scheduler.
synthesize([max_iter, store_progress, ...])Synthesize a MAD image.
to(*args, **kwargs)Move and/or casts the parameters and buffers.
Attributes
Optimization gradient's L2 norm over iterations.
The reference image for this MAD Competition.
Initial image for MAD Competition.
Optimization loss over iterations.
Maximally-differentiating image, the parameter we are optimizing.
Tradeoff between the two metrics in synthesis loss.
Whether we are minimizing or maximizing
optimized_metric.The metric whose value we are minimizing or maximizing.
optimized_metricloss over iterations.Torch optimizer object which updates the synthesis target.
Penalty function output over iterations.
Callable which penalizes additional properties of the synthesized image.
Magnitude of the regularization weight.
L2 norm change in pixel values over iterations.
The metric whose value we are keeping constant.
reference_metricloss over iterations.mad_image, cached over time for later examination.Learning rate scheduler which adjusts optimizer learning rate.
How often we are caching progress.
- get_progress(iteration, iteration_selection='round')[source]#
Return dictionary summarizing synthesis progress at
iteration.This returns a dictionary containing info from
losses,pixel_change_norm,gradient_norm,penalties, andsaved_mad_imagecorresponding toiteration. If synthesis was run withstore_progress=False(and so we did not cache anything insaved_mad_image), then that key will be missing. If synthesis was run withstore_progress>1, we will grab the corresponding tensor fromsaved_mad_image, with behavior determined byiteration_selection.The returned dictionary will additionally contain the keys:
"iteration": the (0-indexed positive) synthesis iteration that the values forlosses,pixel_change_norm,penaltiesandgradient_normcome from.If
self.store_progress,"store_progress_iteration": the (0-indexed positive) synthesis iteration that the value forsaved_mad_imagecomes from.
Note that for the most recent iteration (
iteration=-1oriteration=Noneoriteration==len(self.losses)-1), we do not have values forpixel_change_normorgradient_norm, since in this case we are showing the loss and value for the current MAD image.- Parameters:
iteration (
int) – Synthesis iteration to summarize. IfNone, grab the most recent. Negative values are allowed.iteration_selection (
Literal['floor','ceiling','round'] (default:'round')) –How to select the relevant iteration from
saved_mad_imagewhen the request iteration wasn’t stored.When synthesis was run with
store_progress=n(wheren>1), MAD images are only saved everyniterations. If you request an iteration where a MAD image wasn’t saved, this determines which available iteration is used instead:"floor": use the closest saved iteration before the requested one."ceiling": use the closest saved iteration after the requested one."round": use the closest saved iteration.
- Return type:
- Returns:
progress_info – Dictionary summarizing synthesis progress.
- Raises:
IndexError – If
iterationtakes an illegal value.- Warns:
UserWarning – If the iteration used for
saved_mad_imageis not the same as the argumentiteration(because e.g., you setiteration=3butself.store_progress=2).
- load(file_path, map_location=None, raise_on_checks=True, tensor_equality_atol=1e-08, tensor_equality_rtol=1e-05, **pickle_load_args)[source]#
Load all relevant stuff from a .pt file.
This must be called by a
MADCompetitionobject initialized just like the saved object.Note this operates in place and so doesn’t return anything.
Changed in version 1.2: load behavior changed in a backwards-incompatible manner in order to compatible with breaking changes in torch 2.6.
Changed in version 2.0.0: Adds
raise_on_checksargument.- Parameters:
file_path (
str) – The path to load the synthesis object from.map_location (
str|None(default:None)) – Argument to pass totorch.loadasmap_location. If you save stuff that was being run on a GPU and are loading onto a CPU, you’ll need this to make sure everything lines up properly. This should be structured like the str you would pass totorch.device.raise_on_checks (
bool(default:True)) – During load, we perform several checks to ensure that the saved object was initialized in the same way as the loading object. This is to ensure that the model, image, etc. are all the same and avoid unpleasant surprises. IfTrue, we raise aValueErrorif any of these checks fail. IfFalse, we instead raise aLoadWarning. The intended use here is if you’re loading something that was saved with an older version of plenoptic and you’re sure that you’re doing everything correctly. Note that different devices or dtypes will always result in aValueError. See raise_on_checks on the “Reproducibility and Compatibility” page of the documentation for more info. Additionally, note that, if theMADCompetitionobject itself has changed, we cannot ensure that methods are the same – proceed at your own risk.tensor_equality_atol (
float(default:1e-08)) – Absolute tolerance to use when checking for tensor equality during load, passed totorch.allclose. It may be necessary to increase if you are saving and loading on two machines with torch built by different cuda versions. Be careful when changing this! Seetorch.finfofor more details about floating point precision of different data types (especially,eps); if you have to increase this by more than 1 or 2 decades, then you are probably not dealing with a numerical issue.tensor_equality_rtol (
float(default:1e-05)) – Relative tolerance to use when checking for tensor equality during load, passed totorch.allclose. It may be necessary to increase if you are saving and loading on two machines with torch built by different cuda versions. Be careful when changing this! Seetorch.finfofor more details about floating point precision of different data types (especially,eps); if you have to increase this by more than 1 or 2 decades, then you are probably not dealing with a numerical issue.**pickle_load_args (
Any) – Any additional kwargs will be added topickle_module.loadviatorch.load, see that function’s docstring for details.
- Raises:
ValueError – If
setuporsynthesizehas been called before this call toload.ValueError – If the object saved at
file_pathis not aMADCompetitionobject.ValueError – If the saved and loading
MADCompetitionobjects have a different value for any ofimage,penalty_lambda,metric_tradeoff_lambda, orminmax.ValueError – If the behavior of
optimized_metricorreference_metricis different between the saved and loading objects.
- Warns:
UserWarning – If
setupwill need to be called afterload, to finish initializingoptimizerorscheduler.
See also
examine_saved_synthesisExamine metadata from saved object: pytorch and plenoptic versions, name of the synthesis object, shapes of tensors, etc.
Examples
>>> import plenoptic as po >>> img = po.data.einstein() >>> def ds_ssim(x, y): ... return 1 - po.metric.ssim(x, y) >>> mad = po.MADCompetition( ... img, po.metric.mse, ds_ssim, "min", metric_tradeoff_lambda=10 ... ) >>> mad.synthesize(max_iter=5, store_progress=True) >>> mad.save("mad.pt") >>> mad_copy = po.MADCompetition( ... img, po.metric.mse, ds_ssim, "min", metric_tradeoff_lambda=10 ... ) >>> mad_copy.load("mad.pt")
- objective_function(mad_image=None, image=None)[source]#
Compute the MADCompetition synthesis loss.
This computes:
\[\begin{split}t L_1(x, \hat{x}) &+ \lambda_1 [L_2(x, x+\epsilon) - L_2(x, \hat{x})]^2 \\ &+ \lambda_2 \mathcal{B}(\hat{x})\end{split}\]where \(t\) is 1 if
minmaxis'min'and -1 if it’s'max', \(L_1\) isoptimized_metric, \(L_2\) isreference_metric, \(x\) isimage, \(\hat{x}\) ismad_image, \(\epsilon\) is the initial noise, \(\mathcal{B}\) is the penalty function, \(\lambda_1\) ismetric_tradeoff_lambdaand \(\lambda_2\) ispenalty_lambda.If
setuporsynthesizehas not been called to initialize the MAD image, then this will return an empty tensor.- Parameters:
- Return type:
- Returns:
loss – 1-element tensor containing the loss on this step.
- save(file_path)[source]#
Save all relevant variables in .pt file.
Note that if
store_progressis True, this will probably be very large.See
loaddocstring for an example of use.- Parameters:
file_path (
str) – The path to save the MADCompetition object to.
- setup(initial_noise=None, optimizer=None, optimizer_kwargs=None, scheduler=None, scheduler_kwargs=None)[source]#
Initialize the MAD image, optimizer, and scheduler.
Can only be called once. If
load()has been called,initial_noisemust be None.- Parameters:
initial_noise (
float|None(default:None)) –mad_imageis initialized toself.image + initial_noise * torch.randn_like(self.image), so this gives the standard deviation of the Gaussian noise. IfNone, we use a value of 0.1.optimizer (
Optimizer|None(default:None)) – The un-initialized optimizer object to use. IfNone, we use Adam.optimizer_kwargs (
dict|None(default:None)) – The keyword arguments to pass to the optimizer on initialization. IfNone, we use{"lr": .01}and, if optimizer isNone,{"amsgrad": True}.scheduler (
LRScheduler|None(default:None)) – The un-initialized learning rate scheduler object to use. IfNone, we don’t use one.scheduler_kwargs (
dict|None(default:None)) – The keyword arguments to pass to the scheduler on initialization.
- Raises:
ValueError – If you try to set
initial_noiseafter callingload.ValueError – If
setupis called more than once or aftersynthesize.
Examples
Set initial noise:
>>> import plenoptic as po >>> img = po.data.einstein() >>> mad = po.MADCompetition( ... img, ... lambda x, y: 1 - po.metric.ssim(x, y), ... po.metric.mse, ... "min", ... metric_tradeoff_lambda=0.1, ... ) >>> mad.setup(1) >>> mad.synthesize(10)
Set optimizer:
>>> import plenoptic as po >>> img = po.data.einstein() >>> mad = po.MADCompetition( ... img, ... lambda x, y: 1 - po.metric.ssim(x, y), ... po.metric.mse, ... "min", ... metric_tradeoff_lambda=0.1, ... ) >>> mad.setup(optimizer=torch.optim.SGD, optimizer_kwargs={"lr": 0.01}) >>> mad.synthesize(10)
Use with save/load. Only the optimizer object is necessary, its kwargs and the initial noise are handled by load.
>>> import plenoptic as po >>> img = po.data.einstein() >>> mad = po.MADCompetition( ... img, ... lambda x, y: 1 - po.metric.ssim(x, y), ... po.metric.mse, ... "min", ... metric_tradeoff_lambda=0.1, ... ) >>> mad.setup(1, optimizer=torch.optim.SGD, optimizer_kwargs={"lr": 0.01}) >>> mad.synthesize(10) >>> mad.save("mad_setup.pt") >>> mad = po.MADCompetition( ... img, ... lambda x, y: 1 - po.metric.ssim(x, y), ... po.metric.mse, ... "min", ... metric_tradeoff_lambda=0.1, ... ) >>> mad.load("mad_setup.pt") >>> mad.setup(optimizer=torch.optim.SGD) >>> mad.synthesize(10)
- synthesize(max_iter=100, store_progress=False, stop_criterion=0.0001, stop_iters_to_check=50)[source]#
Synthesize a MAD image.
Update the pixels of
initial_imageto maximize or minimize (depending on the value ofminmax) the value ofoptimized_metric(image, mad_image)while keeping the value ofreference_metric(image, mad_image)constant.We run this until either we reach
max_iteror the loss changes less thanstop_criterionover the paststop_iters_to_checkiterations, whichever comes first.- Parameters:
max_iter (
int(default:100)) – The maximum number of iterations to run before we end synthesis (unless we hit the stop criterion).store_progress (
bool|int(default:False)) – Whether we should store the MAD image in progress during synthesis. IfFalse, we don’t save anything. If True, we save every iteration. If an int, we save everystore_progressiterations (note then that0is the same asFalseand1the same asTrue).stop_criterion (
float(default:0.0001)) – If the loss over the paststop_iters_to_checkhas changed less thanstop_criterion, we terminate synthesis.stop_iters_to_check (
int(default:50)) – How many iterations back to check in order to see if the loss has stopped decreasing (forstop_criterion).
- Raises:
ValueError – If we find a NaN during optimization.
- to(*args, **kwargs)[source]#
Move and/or casts the parameters and buffers.
This can be called as
to(device=None, dtype=None, non_blocking=False)
to(dtype, non_blocking=False)
to(tensor, non_blocking=False)
Its signature is similar to
torch.Tensor.to, but only accepts floating point desireddtype. In addition, this method will only cast the floating point parameters and buffers todtype(if given). The integral parameters and buffers will be moveddevice, if that is given, but with dtypes unchanged. When on_blocking` is set, it tries to convert/move asynchronously with respect to the host if possible, e.g., moving CPU Tensors with pinned memory to CUDA devices.See
torch.nn.Module.tofor examples.Note
This method modifies the module in-place.
- Parameters:
device (torch.device) – The desired device of the parameters and buffers in this module.
dtype (torch.dtype) – The desired floating point type of the floating point parameters and buffers in this module.
tensor (torch.Tensor) – Tensor whose dtype and device are the desired dtype and device for all parameters and buffers in this module.
- property initial_image: Tensor#
Initial image for MAD Competition.
This is the image whose distance to
image, the reference, we are maximizing/minimizing foroptimized_metric, while keeping constant forreference_metric.
- property losses: Tensor#
Optimization loss over iterations.
Will have
length=num_iter+1, wherenum_iteris the number of iterations of synthesis run so far.This tensor always lives on the CPU.
- property minmax: Literal['min', 'max']#
Whether we are minimizing or maximizing
optimized_metric.
- property optimized_metric: Module | Callable[[Tensor, Tensor], Tensor]#
The metric whose value we are minimizing or maximizing.
- property optimized_metric_loss: Tensor#
optimized_metricloss over iterations.That is, the value of
optimized_metric(image, mad_image). Ideally, this is either very different fromoptimized_metric(image, initial_image).This tensor always lives on the CPU, regardless of the device of the
MADCompetitionobject.
- property penalties: Tensor#
Penalty function output over iterations.
Will have
length=num_iter+1, wherenum_iteris the number of iterations of synthesis run so far.This tensor always lives on the CPU.
- property penalty_function: Callable[[Tensor], Tensor]#
Callable which penalizes additional properties of the synthesized image.
- property reference_metric: Module | Callable[[Tensor, Tensor], Tensor]#
The metric whose value we are keeping constant.
- property reference_metric_loss: Tensor#
reference_metricloss over iterations.That is, the value of
reference_metric(image, mad_image). Ideally, this is equal toreference_metric(image, initial_image).This tensor always lives on the CPU, regardless of the device of the
MADCompetitionobject.
- property saved_mad_image: Tensor#
mad_image, cached over time for later examination.How often the MAD image is cached is determined by the
store_progressargument to thesynthesizefunction.The last entry will always be the current
mad_image.If
store_progress==1, then this corresponds directly tolosses:losses[i]is the error forsaved_mad_image[i]This tensor always lives on the CPU, regardless of the device of the
MADCompetitionobject.
- property scheduler: LRScheduler | None#
Learning rate scheduler which adjusts optimizer learning rate.