-
Notifications
You must be signed in to change notification settings - Fork 541
Optim wip - Move & restructure loss objectives #527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@NarineK I left the loss objective functionality the exact same, so that @greentfrapp and I can change them as we tackle issues like: #496 (comment), and allowing for non mean & penalty losses. This PR is ready for review as I will likely not be making any major changes to it for the moment. |
* https://pytorch.org/docs/stable/generated/torch.nn.ParameterList.html Previously PyTorch had issues accessing the parameters inside LaplacianImage.
Fix LaplacianImage & add init option to NaturalImage
Handle input images
* Add missing type hints. * Add decorrelate_init & squash_func parameters to NaturalImage.
* Because why not
096a138
to
d49757b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for moving the losses into a separate file and extending from the Loss ABC, @ProGamerGov! It looks like, there are a couple of losses that assume NCHW input shape - we could group them together or leave as is. It's probably fine to leave them as is right now.
In general I think that it would be great if we could document and explain the losses a bit more in detail and point out to the related papers.
I also added a couple inline comments. Let me know what you think.
captum/optim/_core/loss.py
Outdated
pass | ||
|
||
def get_neuron_pos( | ||
self, H: int, W: int, x: Optional[int] = None, y: Optional[int] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this a specific logic for images ? Perhaps we can keep this function in _utils
package ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's specific logic meant to be used along with activations in the form of NCHW, and I've moved it to a new _utils/images.py
file.
captum/optim/_core/loss.py
Outdated
H_vec, W_vec = self.direction.size(2), self.direction.size(3) | ||
H_activ, W_activ = activations.size(2), activations.size(3) | ||
|
||
H = (H_activ - W_vec) // 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: H_vec ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had it as H_vec
because of the input variable name vec
, but I've changed it to be H_direction
now as that seems more readable.
captum/optim/_core/loss.py
Outdated
|
||
class TensorDirection(Loss): | ||
""" | ||
Visualize a tensor direction. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Do you mind adding some description here about this loss w.r.t. cosine similarity, etc. ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a citation and link to the distil paper that the direction objectives come from.
self.direction = vec.reshape((1, -1, 1, 1)) | ||
|
||
def __call__(self, targets_to_values: ModuleOutputMapping) -> torch.Tensor: | ||
activations = targets_to_values[self.target] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think that it would be good to assert the sizes of self.direction and activations before computing cosine similarity on them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added an assert for the channel size of self.direction & activations. @greentfrapp may be able to add more assertion tests in his upcoming PR.
activations = targets_to_values[self.target] | ||
x_diff = activations[..., 1:, :] - activations[..., :-1, :] | ||
y_diff = activations[..., :, 1:] - activations[..., :, :-1] | ||
return torch.sum(torch.abs(x_diff)) + torch.sum(torch.abs(y_diff)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this L1-norm w.r.t. total variation of height and width ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the sum of the absolute differences for neighboring values in the activations or image. TensorFlow's version (that Lucid uses) links to this Wikipedia page: https://en.wikipedia.org/wiki/Total_variation_denoising
My neural-style-pt project uses basically the same algorithm, but it's origins trace back to this research article: https://arxiv.org/abs/1412.0035
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chris also says that TensorFlow's total variation algorithm comes from the Understanding Deep Image Representations by Inverting Them paper: https://arxiv.org/abs/1412.0035
captum/optim/_core/loss.py
Outdated
|
||
class ActivationInterpolation(Loss): | ||
""" | ||
Interpolate between two different layers & channels |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a paper related to this loss. It would be good to document it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a link and citiation for Activation Interpolation!
(activ_a - activ_b) ** 2 | ||
).mean() / self.decay_ratio ** float(d) | ||
|
||
return sum_tensor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: because we are optimizing negative loss do we want to return sum_tensor
? I'm asking that because the losses are returning the opposite sign here for other losses as well:
https://github.com/greentfrapp/lucent/blob/master/lucent/optvis/objectives.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently the InputOptimization class doesn't support losses like Alignment. So, I've left them as they are for @greentfrapp to change, as he's currently working on the optimization system.
@NarineK I'll have to think about the ordering of the objectives, but for the moment we can leave them in their current order. As for the losses, I'll work on writing more in-depth descriptions with @greentfrapp, but in the meantime we can leave the descriptions as they are so that @greentfrapp can continue his work on the optimization stuff. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for working on this PR, @ProGamerGov !
I moved all loss objectives from
captum.optim._core.objectives
tocaptum.optim._core.loss
as per: #500 (comment)I changed the loss functions to classes and made it so that they were building off an abstract loss class. The new classes work with the existing objective optimization code, so no changes to that were needed.
The tutorials were also updated to reflect the loss changes.
The loss changes should be compatible with / not be in conflict with: Optim-WIP: Preliminary transform unit tests & more #521
Added a ton of missing type hints.
I added the 3 Direction objectives that we were missing. They've been tested and work with the current objective system.
Added Channel Interpolation objective, and made it possible to use without selecting specific channels.
Added layer objective.
Added alignment objective.
Added weight objective (all 3 weight objectives merged into a single objective class).
I got LaplacianImage working. It seems to work pretty good for feature visualization!
I added an init parameter to FFTImage, LaplacianImage, and NaturalImage.
FFTImage, NaturalImage, PixelImage, LaplacianImage, InputOptimization, and ToRGB all support batch sizes now!
Added
decorrelate_init
&squash_func
parameters to NaturalImage.Added size property / function to ImageTensor, as size is more PyTorch-like than shape I think.
Fixed major bug where errors / interrupts to the InputOptimization class' optimize function resulted in activations being None in subsequent runs.
Custom input images now also work correctly!