-
Notifications
You must be signed in to change notification settings - Fork 126
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
Update robust loss #502
Update robust loss #502
Conversation
Modification of Robust Loss. Original one function on the whole norm square, while the new one function on individual residuals. And the radius is changed from exp() to square()
Because I do not want to mess up the current environment of Theseus, it would be more convenient to test the new Robust Loss Class defined in the Notebook
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.
Thanks a lot for your contribution! I left some comments that need to be addressed.
Also CI is failing due to some lint errors. Can you please install pip install -r requirements/dev.txt
and also pre-commit hooks, so that they take care of lint errors automatically.
Thanks!
@@ -143,4 +186,4 @@ def _supports_masking(self) -> bool: | |||
@_supports_masking.setter | |||
def _supports_masking(self, val: bool): | |||
self.cost_function._supports_masking = val | |||
self.__supports_masking__ = val | |||
self.__supports_masking__ = val |
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.
Missing end line at end of file.
@@ -52,6 +52,7 @@ def __init__( | |||
cost_function: CostFunction, | |||
loss_cls: Type[RobustLoss], | |||
log_loss_radius: Variable, | |||
RobustSum: bool = False, |
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.
Let's keep python
naming conventions using lower_case
for variables instead of RobustSum
. Not sure what a good name for this should be; let's start with flatten_dims: bool = False
, which defaults to the old version, and True
means the new version.
@@ -70,6 +71,7 @@ def __init__( | |||
self.log_loss_radius = log_loss_radius | |||
self.register_aux_var("log_loss_radius") | |||
self.loss = loss_cls() | |||
self.RobustSum = RobustSum |
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.
self.flatten_dims = flatten_dims
error_loss = self.loss.evaluate(squared_norm, self.log_loss_radius.tensor) | ||
|
||
|
||
if self.RobustSum: |
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 think this can be done much more simply. If I'm not mistaken, you can keep all of the old code and just add the following lines at the beginning of weighted_error()
if self.flatten_dims:
weighted_error = weighted_error.view(-1, 1)
and then reshape the output back to (-1, self.dim())
before returning.
A similar procedure can be followed for weighted_jacobians
.
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.
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 a bit hard to explain, but it works, I just tested it. I'll put another PR soon.
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.
Please take a look at #503. The code for the jacobians part was a bit tricker than I suggested above, but I was still able to mostly use all of the old computation. Also look at the unit test I added and let me know if covers the expected behavior. Thanks!
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 a bit hard to explain, but it works, I just tested it. I'll put another PR soon.
Oh! I see what you mean! You treat every element of the original cost as the original "square norm"
return ( | ||
(error_loss + RobustCostFunction._EPS).sqrt() | ||
) | ||
# Inside will compare squared_norm with exp(radius) |
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.
Please remove any debug comments.
@classmethod | ||
def evaluate(cls, x: torch.Tensor, log_radius: torch.Tensor) -> torch.Tensor: | ||
return cls._evaluate_impl(x, log_radius.exp()) | ||
|
||
return cls._evaluate_impl(x, log_radius.square()) |
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.
We should keep this as exp()
.
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.
Sure. Could I know the initiative behind 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.
Since square() is not monotonous, I personally prefer 'exp()', which is more useful when we try to learn a robust loss radius.
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.
Oh! That's great! Because at a very early time, I tried to let the system automatically learn the Radius, and I failed at that time (performance was not good). But with the new version, the training outcome may change.
@@ -49,4 +50,4 @@ def _evaluate_impl(x: torch.Tensor, radius: torch.Tensor) -> torch.Tensor: | |||
|
|||
@staticmethod | |||
def _linearize_impl(x: torch.Tensor, radius: torch.Tensor) -> torch.Tensor: | |||
return torch.sqrt(radius / torch.max(x, radius) + _LOSS_EPS) | |||
return torch.sqrt(radius / torch.max(x, radius) + _LOSS_EPS) |
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.
Missing line at end of file.
@@ -0,0 +1,731 @@ | |||
{ |
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.
For the unit test, the right place to do it is this file. There is no need to write a full example. We only need two versions of a small objective and compare that the both versions lead to the same error and jacobians tensors. We can remove this notebook.
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 use notebook to test to avoid the old version will mess up with the new one. Because I have installed Theseus 0.1.4 and do not want to make any changes to it. And any of the function that call th.xx will call the original Theseus function. Do you have any good ideas? Or I can just replace my new script file with the one in the environment site-package?
return [ | ||
rescale.view(-1, 1, 1) * jacobian for jacobian in weighted_jacobians | ||
], rescale * weighted_error | ||
#squared_norm = torch.sum(weighted_error**2, dim=1, keepdim=True) |
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.
See comment above on a suggestion for implementing this more easily.
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.
Add some more comments in #503.
@classmethod | ||
def evaluate(cls, x: torch.Tensor, log_radius: torch.Tensor) -> torch.Tensor: | ||
return cls._evaluate_impl(x, log_radius.exp()) | ||
|
||
return cls._evaluate_impl(x, log_radius.square()) |
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.
Since square() is not monotonous, I personally prefer 'exp()', which is more useful when we try to learn a robust loss radius.
Motivation and Context
Current Robust Loss cannot deal with outlinear existing in a optimization problem
How Has This Been Tested
Types of changes
Checklist