Added Huber loss function class - #2281
Conversation
|
Fixes #2281 |
|
Hi @msaca-okse I just added the license header and made some edits to ensure the documentation renders well:
I wonder whether c should be included in the above equation?
We will need to add unit tests before merging into CIL. Would you be interested in working on this or are you happy for the CIL developer team to add these? |
lauramurgatroyd
left a comment
There was a problem hiding this comment.
Hi @msaca-okse, thanks for your contribution. I am now working on getting this in CIL. I am about to push some unit tests which I have developed.
Signed-off-by: Laura Murgatroyd <60604372+lauramurgatroyd@users.noreply.github.com>
Signed-off-by: Laura Murgatroyd <60604372+lauramurgatroyd@users.noreply.github.com>
MargaretDuff
left a comment
There was a problem hiding this comment.
A really picky review - apologies.
However, I am happy with the code, the gradient and Lipscitz calculations!
| self._L = np.abs(self.c) * (self.A.norm() ** 2) | ||
| except AttributeError: | ||
| if self.A.is_linear(): | ||
| Anorm = LinearOperator.PowerMethod(self.A, 10)[0] | ||
| self._L = np.abs(self.c) * (Anorm * Anorm) |
There was a problem hiding this comment.
I know that this is copied from Least squares but for a linear operator, calling A.norm() either accesses a stashed value or calls the power method itself. I don't understand in what cases the if statement on 157 will be triggered.
| if self.A.is_linear(): | ||
| Anorm = LinearOperator.PowerMethod(self.A, 10)[0] | ||
| self._L = np.abs(self.c) * (Anorm * Anorm) | ||
| else: |
There was a problem hiding this comment.
Similarly, not sure what this else will pick up. If the operator is not linear and calculate_norm is not defined, the user will get a NotImplementedError. Perhaps we catch and return that with a bit more of an explanation?
| D = DiagonalOperator(self.weight) | ||
| self._weight_norm = D.norm() |
There was a problem hiding this comment.
This would do the same thing without building a new operator - I see arguments both ways
| D = DiagonalOperator(self.weight) | |
| self._weight_norm = D.norm() | |
| self._weight_norm = self.weight.abs().max() |
Co-authored-by: Margaret Duff <43645617+MargaretDuff@users.noreply.github.com> Signed-off-by: Laura Murgatroyd <60604372+lauramurgatroyd@users.noreply.github.com>



Description
The Huber loss function behaves like the L2 loss close to 0, and like L1 loss for large values. The loss function is differentiable and can thus be used in GD or FISTA. It's an alternative to L2 loss that is more robust to extreme values. The implementation mirrors that of "cil.optimisation.functions.LeastSquares". The Huber loss has a parameter that selects when the cutoff between L2 and L1 loss happens.
Example Usage
Contribution Notes