-
Notifications
You must be signed in to change notification settings - Fork 3
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] Change effects api, and use BaseObject from scikit-base #85
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #85 +/- ##
==========================================
+ Coverage 93.29% 93.35% +0.05%
==========================================
Files 25 26 +1
Lines 1044 1173 +129
==========================================
+ Hits 974 1095 +121
- Misses 70 78 +8 ☔ View full report in Codecov by Sentry. |
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.
Interesting! Interesting design, too! I will leave some comments below.
base class design
makes sense to me!
Questions:
- is it sensible to attach an
id
to each effect (the instance), rather than attach the names only in the model? I have seen a number of designs where the name is intrinsic to the estimtaor instance.sklearn
avoids that and instead assigns names extrinsically, in compositors such as pipelines.sktime
further assigns default names in compositors, e.g., just the class name if there are no duplicates of effects. - naming of methods, see below. I spot similarities to common types of methods, rename for consistency?
names of the abstract methods
I think these might map onto a generic transformer-with-parameter-estimator interface. If I were making calls on how to consistently name the methods, I would rename:
initialize
->fit
apply
->transform
?
deprecation safety
I notice some classes introduce parameters at the start or reorder them. This breaks previous positional calls, so if you want that without warning to the users, it should be a conscious choice.
I was using the ID to avoid conflicts of sample site names, but I had forgotten that it is possible to use NumPyro scopes in the model function and completely avoid the need of passing it to the effect instance! Then, it would be possible to pass effects similarly to how it is done in sktime and scikit-learn pipelines: exogenous_effects = [
("seasonality", LinearEffect(regex=starts_with(["sin", "cos"])),
...
]
Concerning the names... At first, I chose to avoid
would be a good choice. I believe that using One thing I am questioning is whether the I will update the PR today with these changes and also show an example of a composite effect that can be used in MMM applications to leverage the results of A/B tests and use them as a reference for an effect output. |
FYI @felipeffm |
So... I have an interface that I feel more comfortable with. Both "id" and "regex" are not passed to effect objects anymore. The exogenous_effects parameter of Prophetverse now is a list of tuples Children of Their responsibilities are:
The default behaviours are:
A Example: from prophetverse.sktime import Prophetverse
from prophetverse.effects.linear import LinearEffect
from prophetverse.utils import no_input_columns # Alias for None, could also a be a regex that matches nothing
from prophetverse.effects.fourier import LinearFourierSeasonality
from prophetverse.effects.log import LogEffect
from prophetverse.utils.regex import starts_with
import numpyro
exogenous_effects = [
(
"seasonality",
LinearFourierSeasonality(
freq="D",
sp_list=[7, 365.25],
fourier_terms_list=[3, 10],
prior_scale=0.1,
effect_mode="multiplicative",
),
no_input_columns,
),
(
"exog",
LogEffect(
rate_prior=dist.Gamma(2, 1),
scale_prior=dist.Gamma(2, 1),
effect_mode="additive",
),
starts_with("exog"),
),
]
model = Prophetverse(
trend="linear",
changepoint_interval=300,
changepoint_prior_scale=0.0001,
exogenous_effects=exogenous_effects,
noise_scale=0.05,
optimizer_steps=50000,
optimizer_name="Adam",
optimizer_kwargs={"step_size": 0.0001},
inference_method="map",
)
model.fit(y=y, X=X) |
One nice side effect is that the TrendModel can also be a BaseEffect object. Will address this in a future PR. |
I'll merge this PR and close #73, but will not create a new release today (just a pre-release). If you have any suggestions @fkiraly @felipeffm please feel free to re-open that issue. |
Happy with the changes, as they address my main points - I am unsure about whether these are "perfect" choices, but I suppose that's something one would glean from observing usage and using this in actuality. |
This pull request changes how effects are used in forecasters and how custom effects can be applied.
Objectives
The main objectives are:
The BaseEffect class has three arguments:
_apply
method._apply
.Children should implement optionally
_initialize
and_prepare_input_data
. The default behaviour of those methods is filtering columns of the exogenous dataframeX
according toself.regex
, and passing them to_apply
.The
_apply
method, on the other hand, must be implemented by children classes, and return ajnp.ndarray
with the computed effect.Closes #73