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

[ENH] Parameter Fitter: Basic implementation #6921

Merged
merged 26 commits into from
Nov 13, 2024

Conversation

VesnaT
Copy link
Contributor

@VesnaT VesnaT commented Oct 21, 2024

Issue

Implement a Parameter Fitter widget:

  • The initial version can only fit integer parameters.
  • to fit a parameter, the Learner has to implement a fitted_parameters method.
Includes
  • Code changes
  • Tests
  • Documentation

Copy link

codecov bot commented Oct 21, 2024

Codecov Report

Attention: Patch coverage is 95.61905% with 23 lines in your changes missing coverage. Please review.

Project coverage is 88.45%. Comparing base (52df166) to head (932fe14).
Report is 42 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6921      +/-   ##
==========================================
+ Coverage   88.37%   88.45%   +0.07%     
==========================================
  Files         329      331       +2     
  Lines       72447    73063     +616     
==========================================
+ Hits        64028    64625     +597     
- Misses       8419     8438      +19     

@markotoplak markotoplak changed the title Parameter Fitter: Basic implementation [ENH] Parameter Fitter: Basic implementation Oct 21, 2024
@markotoplak markotoplak added this to the 3.38.0 milestone Oct 21, 2024
Copy link
Contributor

@janezd janezd left a comment

Choose a reason for hiding this comment

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

I haven't really reviewed the PR - I haven't even checked it out from the repo. Here are just two things I incidentally noticed. Feel free to ignore.

Orange/base.py Outdated Show resolved Hide resolved
Orange/base.py Outdated Show resolved Hide resolved
Orange/base.py Outdated Show resolved Hide resolved
Orange/base.py Outdated Show resolved Hide resolved
Orange/classification/random_forest.py Outdated Show resolved Hide resolved
Orange/widgets/evaluate/owparameterfitter.py Outdated Show resolved Hide resolved
Orange/base.py Outdated
("type", Type),
("min", Union[int, NoneType]),
("max", Union[int, NoneType]),
]
Copy link
Contributor

Choose a reason for hiding this comment

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

What I had in mind was

class FittedParameter(NamedTuple):
    name: str
    label: str
    tick_label: str = ""
    type_: type = int
    min: Optional[int] = None
    max: Optional[int] = None

No need to change it; I find this easier on the eye, but it's a matter of taste.

@janezd janezd self-assigned this Oct 25, 2024
@@ -54,8 +55,8 @@ def _search(
data: Table,
learner: Learner,
fitted_parameter_props: Learner.FittedParameter,
initial_parameters: dict,
steps: Sized,
initial_parameters: dict[str, Any],
Copy link
Contributor

Choose a reason for hiding this comment

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

Currently, we'd have dict[str, int], but eventually we'd have dict[str, Union[int, float]]. On the other hand, what if the learner provides a list of string arguments? So I put dict[str, Any]. We can put a humble int there, though, for the time being.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd put an int just to emphasize that other types are not supported and should be carefully reconsidered.

Orange/widgets/evaluate/owparameterfitter.py Show resolved Hide resolved
Orange/widgets/evaluate/owparameterfitter.py Show resolved Hide resolved
Orange/widgets/evaluate/owparameterfitter.py Show resolved Hide resolved
Orange/widgets/evaluate/owparameterfitter.py Show resolved Hide resolved
Orange/widgets/evaluate/owparameterfitter.py Show resolved Hide resolved

@property
def initial_parameters(self) -> dict:
if not self._learner or not self._data:
if not self._learner:
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above.

Orange/widgets/evaluate/owparameterfitter.py Show resolved Hide resolved
Orange/widgets/evaluate/owparameterfitter.py Show resolved Hide resolved
Orange/widgets/evaluate/owparameterfitter.py Outdated Show resolved Hide resolved
@janezd
Copy link
Contributor

janezd commented Oct 25, 2024

I focused on the gui layout and changed a few minor things I encountered.

In the future, we probably want to support:

  • optimization of multiple arguments,
  • non-int arguments (at least floats),
  • outputting a learner with internal fitting.

So, I thought about making some other changes, but decided not to, because they would probably be changed again in that future.

Copy link
Contributor Author

@VesnaT VesnaT left a comment

Choose a reason for hiding this comment

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

Two more things I'd suggest changing:

  • Steps preview does not refresh when key down/up in spin box

  • If I select Manual and there is no range in the line edit, the behaviour is incorrect.
    image

@@ -54,8 +55,8 @@ def _search(
data: Table,
learner: Learner,
fitted_parameter_props: Learner.FittedParameter,
initial_parameters: dict,
steps: Sized,
initial_parameters: dict[str, Any],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd put an int just to emphasize that other types are not supported and should be carefully reconsidered.

initial_parameters: dict,
steps: Sized,
initial_parameters: dict[str, Any],
steps: Collection[Any],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also an int

initial_parameters: dict,
steps: Sized,
initial_parameters: dict[str, Any],
steps: Collection[Any],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Any => int

Orange/widgets/evaluate/owparameterfitter.py Outdated Show resolved Hide resolved
rect.adjust(style.pixelMetric(style.PM_IndicatorWidth)
+ style.pixelMetric(style.PM_CheckBoxLabelSpacing), 0, 0, 0)

last_text = f", {self.__steps[-1]}"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

image
I'd remove the , for a single value.

@janezd
Copy link
Contributor

janezd commented Oct 30, 2024

I think I've addressed all comments except for

Steps preview does not refresh when key down/up in spin box

This looks like a problem in gui.spin. Open an arbitrary widget, e.g. Tree Learner and key up/down on some spin. It doesn't recompute.

@janezd janezd force-pushed the summary_of_fit branch 2 times, most recently from d4a1145 to ae4ee9e Compare November 4, 2024 22:24
@markotoplak
Copy link
Member

You likely noticed this already, but this PR is not Python 3.9 compatible. Or are we planning to drop support for 3.9? Numpy already did according to https://numpy.org/neps/nep-0029-deprecation_policy.html.

@janezd janezd force-pushed the summary_of_fit branch 7 times, most recently from c702f4d to 0ca5f2a Compare November 6, 2024 21:13
@janezd
Copy link
Contributor

janezd commented Nov 7, 2024

I added rudimentary documentation. The widget is bound to significantly change in the future, so it's not worth spending too much time on this.

Copy link
Contributor

@lanzagar lanzagar left a comment

Choose a reason for hiding this comment

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

  • The widget crashes when given a dataset without a class.
  • I think the controls in Parameter Fitter should be tied to the Learner input. One should be able to select a parameter to fit and the range of values before connecting the data input (since that already starts the fitting and I would like it to use the correct settings). Also, refreshing the data or even inputting a completely different dataset should not reset the settings in Parameter Fitter. I think it is acceptable if they are reset when the Learner input is refreshed, but ideally it would even try to keep the settings when e.g. I just change the name of the learner or some of the other learner parameters (maybe we don't reset to default if the class of the learner stays the same?).

Orange/widgets/evaluate/owparameterfitter.py Outdated Show resolved Hide resolved
Orange/regression/pls.py Outdated Show resolved Hide resolved
@VesnaT VesnaT self-assigned this Nov 8, 2024
@VesnaT VesnaT force-pushed the summary_of_fit branch 4 times, most recently from 25bd989 to 24638c0 Compare November 12, 2024 07:05
def set_learner(self, learner: Optional[Learner]):
if self._learner:
self.__initialize_settings = \
not isinstance(self._learner, type(learner))
Copy link
Member

Choose a reason for hiding this comment

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

If you need an exact type, do not use isinstance (this also allows for subclasses).

I would do it differently, though, by just comparing if these two leartners have the same fitted_parameters.

@lanzagar lanzagar merged commit 2869a55 into biolab:master Nov 13, 2024
29 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants