Skip to content
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

Extend docstrings of loss functions #64

Merged

Conversation

beichensinn
Copy link
Contributor

@beichensinn beichensinn commented Apr 20, 2021

Summary

Added docstrings.
Closes #49.

@woodsp-ibm woodsp-ibm added the type: documentation 📖 Issues related to documentation label Apr 20, 2021
This class computes the L1 loss: sum |target - predict|
"""

def evaluate(self, predict: np.ndarray, target: np.ndarray):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When overridding a base class, where we have (and should have) documentation on base class method, there is no need to duplicate/repeat it on derived classes. The base class documentation is inherited via Sphinx which means it can be updated/managed in one place rather than having to deal with copies of it all over the place in derived classes. I think we can say in the base class that it Raises: QiskitMachineLearingError if the shape is incorrect or if used for a classification it does not support, e.g it can only handle binary but is given multiclass

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

return self.evaluate(predict, target)

@abstractmethod
def evaluate(self, predict, target):
""" evaluate """
def evaluate(self, predict: np.ndarray, target: np.ndarray):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see in the derived classes they document this returning a float and the meaning of the float. In which case I think the signature here should be updated to return a float ie def evaluate(self, predict: np.ndarray, target: np.ndarray) -> float: and document here the meaning of that return.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For both evaluate and gradient we expect them to return a float right so the caller of a concrete sub-class knows what to expect. Hence in the abstract methods here the signature should show the float return typehint and the Returns in the docstring document the meaning of the float. The docstrings here would then be complete meaning no docstring should be needed on the derived classes - which at present all have a copy/paste of the same Returns text when they should all just simply have no docstring i.e inherit the one from the base class.

raise NotImplementedError

@abstractmethod
def gradient(self, predict, target):
""" gradient """
def gradient(self, predict: np.ndarray, target: np.ndarray):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment on return value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@adekusar-drl
Copy link
Collaborator

@woodsp-ibm if you could take a look again at the PR.

return self.evaluate(predict, target)

@abstractmethod
def evaluate(self, predict, target):
"""evaluate"""
def evaluate(self, predict: np.ndarray, target: np.ndarray):
Copy link
Member

@woodsp-ibm woodsp-ibm May 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def evaluate(self, predict: np.ndarray, target: np.ndarray):
def evaluate(self, predict: np.ndarray, target: np.ndarray) -> float:

The typehint should show it returns a float

Also there should be a Returns: entry in the docstring that states the meaning of the return value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

raise NotImplementedError

@abstractmethod
def gradient(self, predict, target):
"""gradient"""
def gradient(self, predict: np.ndarray, target: np.ndarray):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def gradient(self, predict: np.ndarray, target: np.ndarray):
def gradient(self, predict: np.ndarray, target: np.ndarray) -> float:

The typehint should show the return type

And likewise there should be a Returns: entry in the docstring that states the meaning of the return value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

raise NotImplementedError

@staticmethod
def _validate(predict, target):
def _validate(predict: np.ndarray, target: np.ndarray):
Copy link
Member

@woodsp-ibm woodsp-ibm May 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _validate(predict: np.ndarray, target: np.ndarray):
def _validate(predict: np.ndarray, target: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:

The typehint should have the return type. The docstring has a Returns entry so thats good. Tuple will need importing from typing though for this.

I must admit this method is kinda confusing. If it just needs to validate the shapes why does it need to return them since the caller already has them? Now I see it makes a numpy array of them, but then the typehint says it takes a numpy array and not something like a list where I could see it adjusting the types as needed as well as doing some validation, with the (possibly) adjusted values returned,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment on lines 92 to 93
L1Loss:
This class computes the L1 loss: sum |target - predict|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicating the name of the class into the docstring is not necessary; It should be a simple sentence summary, as you have there already, followed by further paragraph(s) of text describing things for more complicated classes that need it = which I do not think is the case here.

Suggested change
L1Loss:
This class computes the L1 loss: sum |target - predict|
This class computes the L1 loss: sum |target - predict|

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment on lines 116 to 117
L2Loss:
This class computes the L2 loss: sum (target - predict)^2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
L2Loss:
This class computes the L2 loss: sum (target - predict)^2
This class computes the L2 loss: sum (target - predict)^2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@adekusar-drl adekusar-drl requested a review from woodsp-ibm May 28, 2021 11:38
@adekusar-drl adekusar-drl changed the title Extend loss docstring #49 Extend docstrings of loss functions Jun 1, 2021
stefan-woerner
stefan-woerner previously approved these changes Jun 2, 2021
Copy link
Contributor

@stefan-woerner stefan-woerner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

"""

def __call__(self, predict, target):
def __call__(self, predict: Union[int, np.ndarray], target: Union[int, np.ndarray]) -> float:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is now either an integer or an numpy array. The args below mention just the numpy array not a single value. I see the single value is reformed to a numpy array. Does that mean the numpy arrays are arrays of ints too? I.e the predicted and target values are all ints. Maybe the Args docstring can be improved.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is now either an integer or an numpy array. The args below mention just the numpy array not a single value. I see the single value is reformed to a numpy array. Does that mean the numpy arrays are arrays of ints too? I.e the predicted and target values are all ints. Maybe the Args docstring can be improved.

No more ints, just numpy arrays.

Args:
predict: a numpy array of predicted values using the model
target: a numpy array of the true values

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have a Returns here to state what the float return is

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have a Returns here to state what the float return is

Added a Returns section.

Args:
predict: a numpy array of predicted values using the model
target: a numpy array of the true values

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again it would be good to have a Returns statement here for the float return

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again it would be good to have a Returns statement here for the float return

Same here, added Returns.

def _validate(predict, target):
def _validate(
predict: Union[int, np.ndarray], target: Union[int, np.ndarray]
) -> Tuple[np.ndarray, np.ndarray]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if I pass in an 'int' and an 'int' as predict and target, I will not get back an Tuple of ints rather a Tuple of arrays? I guess its doing a type cast as well as validate that the other methods rely on. Not sure if it would be helpful to expose the duality in the name say _cast_validate or _to_array_validate - its a private method so maybe its less important.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if I pass in an 'int' and an 'int' as predict and target, I will not get back an Tuple of ints rather a Tuple of arrays? I guess its doing a type cast as well as validate that the other methods rely on. Not sure if it would be helpful to expose the duality in the name say _cast_validate or _to_array_validate - its a private method so maybe its less important.

Now only numpy arrays, renamed to _validate_shapes. I hope it is good enough now.

)
return predict, target


class L1Loss(Loss):
"""L1Loss"""
"""
L1Loss:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of the class will be in the docs from the class itself, it does not need to be put into the docstring as it will not really add any value. All it needs is the description of what this class is about ie. the L1Loss: bit can simply be removed. Same in the other classes in this module.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of the class will be in the docs from the class itself, it does not need to be put into the docstring as it will not really add any value. All it needs is the description of what this class is about ie. the L1Loss: bit can simply be removed. Same in the other classes in this module.

Agree, docstrings are updated.

@adekusar-drl adekusar-drl merged commit df5d9f8 into qiskit-community:main Jun 16, 2021
gentinettagian pushed a commit to gentinettagian/qiskit-machine-learning that referenced this pull request Dec 14, 2021
This PR revises documentation of the loss functions.

Co-authored-by: Manoel Marques <Manoel.Marques@ibm.com>
Co-authored-by: Anton Dekusar <62334182+adekusar-drl@users.noreply.github.com>
Co-authored-by: Anton Dekusar <adekusar@ie.ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation 📖 Issues related to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Extend loss function doc strings
5 participants