[STEP] load and save in BasePkg in pytorch-forecasting v2 - #51
[STEP] load and save in BasePkg in pytorch-forecasting v2#51phoeenniixx wants to merge 14 commits into
load and save in BasePkg in pytorch-forecasting v2#51Conversation
|
Moving the converstation from the hackmd here: @fkiraly replied: @phoeenniixx replied:
Not quite! This is a wrapper around the model layer that provides interface to add tests and
Yes, the idea to create a rather generic method that can handle different D2 modules and different models as well. |
|
|
||
| ##### Example 1: Saving | ||
|
|
||
| If we pass `save_ckpt` as `True` in `model_pkg.fit()`, the methods automatically saves the model checkpoints after fitting. `ckpt_dir` is optional and defaults to `"checkpoints"`. `ckpt_kwargs` are also optional arguments passed to `ModelCheckpoint`. |
There was a problem hiding this comment.
does this mean a checkpoint is always saved, i.e., a user has no means to turn off checkpoint saving? I was assuming the default is "it is not saved". Otherwise, there is silently a file written to the hard drive on every fit call with defaults?
There was a problem hiding this comment.
no, you have to pass save_ckpt=True, it is False by default. It will be saved ONLY IF you pass this arg as True.
There was a problem hiding this comment.
Just one comment, but I don't want to overcomplicated the codebase: as I can imagine you are saving the checkpoint that leads to the lowest validation loss (as far as I remember it is related to the trainer), but in some cases, this minimum can be an 'false' minumum and what you would like to save is also the last checkpoint. I know that this would double the space of the saved weights, but sometimes you would let the network continue till the end. It is just a comment, not a mandatory change
There was a problem hiding this comment.
Ah I see at row 406 that I can eventually save the last checkpoint :-)
|
|
||
| If we pass `save_ckpt` as `True` in `model_pkg.fit()`, the methods automatically saves the model checkpoints after fitting. `ckpt_dir` is optional and defaults to `"checkpoints"`. `ckpt_kwargs` are also optional arguments passed to `ModelCheckpoint`. | ||
|
|
||
| This saves - model checkpoints and all the three cfgs (`model_cfg`, `datamodule_cfg` and `trainer_cfg`) |
There was a problem hiding this comment.
can you be more explicit on how many files and which file names this is saved to?
That would also be crucial in the docstring. A docstring should document all assumptions and guarantees, that is all preconditions that need to be satisfied, and all changes at the point of the end state it makes.
If file access or file saving is involved, then, very importantly:
- assumptions may include assumptions on files being present in a specific format or location, and the content of these files being something.
- guarantees may include files being written or changed, at specific file locations with a specific content.
|
|
||
| ##### Example 2: Loading | ||
|
|
||
| To load the model, we just pass a `ckpt_path` (no need to pass any cfgs in this case as all the cfgs are taken from the saved cfgs), But if you pass the cfgs, these configs will override the saved ones. |
There was a problem hiding this comment.
what is the reason to save configs, or to assume saved configs? For inference, at least the trainer_cfg seems like it is not needed. Similarly, could a different data_cfg not also make sense?
Imo we need to think carefully about two cases:
- save and load everything
- save and load only the weights
- any intermediate option that may make sense
There was a problem hiding this comment.
Well for data modules, we need to save the cfgs to match the exact conditions the model was trained on (like same max_encoder_length, batch_size etc). Obv the user could use their own cfgs still if they want, if they pass their own cfgs that would be given higher preference and would override the saved cfgs. But saving provides the users to not to pass the cfgs they used previously again.
About the trainer_cfg, I am a bit conflicted. I think we should add the way to save it as this would save the user from passing the cfg again if they are planning to use the same trainer they used earlier. And way to overrride it would give them complete flexibility. Although I agree saving the trainer_cfg is not something that is very important and would cause issues, but if we provide this, it would save some users (who are lazy like me) to pass the same cfg again. THey can always override it to use a new configuration for the trainer anytime.
But I think saving the cfgs for data modules are more important - it provides safeguards from the "accidents" where the user might not remember (or know) the previous cfgs for the data module and that may lead to issues
There was a problem hiding this comment.
sure, still I think saving and loading some or all should be optional (where it makes sense)
| @@ -0,0 +1,524 @@ | |||
| # How to handle `load` and `save` in `BasePkg` | |||
|
|
|||
There was a problem hiding this comment.
in an enhancement proposal, there should be a preamble. What problem are we solving, what requirements there are, what the design needs to do. Optimally, also a conceptual model and the use cases we want to solve. E.g., what are we saving? What are we loading? And why? Are there multiple use cases related to saving and loading? I think pure inference use as well as loading in order to continue training, etc, should be considered.
|
|
||
| The saving logic completely sits inside the `.fit()` method of the `Base_pkg`. When we pass `sace_ckpt=True` to `fit()`, | ||
| the method saved the model checkpoints using `ModelCheckpoint`, while other artifacts (`model_cfg`, `datamodule_cfg` and `metadata` of `datamodule`) are saved by calling `_save_artifacts`. | ||
|
|
There was a problem hiding this comment.
question: is there a case to be made for saving to be separate from fitting? E.g., is there a use case where saving does not immediately follow fitting?
There was a problem hiding this comment.
Tbh, I cant think of any case as such. I mean we would like to save the model only after it has learned something no? why would anyone would like to save random weights?
Can you please rephrase the question, I think I am not able to understand the exact question here
| for providing both training and validation data. | ||
| save_ckpt : bool, default=True | ||
| If True, save the best model checkpoint and the `datamodule_cfg`. | ||
| ckpt_dir : Union[str, Path], default="checkpoints" |
There was a problem hiding this comment.
the default is "save in checkpoints dir". However, save_ckpt and ckpt_dir feel redundant, as ckpt_dir=None could indicate that no saving is desired. This would make save_ckpt unnecessary.
There was a problem hiding this comment.
I agree, but having the save_ckpt, imo, provides a clear "feedback" to the user that only this arg is going to make the saving of models. ckpt_dir is kinda optional - only if the user wants to save the ckpts and other artifacts to somewhere specific. Usually all the checkpoints would always save in the root within a new folder- checkpoints. I dont think this ckpt_dir should decide if the user wants to save the skpts or not, for me, I would usually just pass True and would not pass anything to the ckpt_dir. ckpt_dir is just to provide more flexibility to the user.
But again this adds a little bit of redundancy. This is a question of redundancy and of being discreet. It is upto us which path we need to take.
There was a problem hiding this comment.
If you have more inclination towards the removal of redundancy I am happy to remove it completely
There was a problem hiding this comment.
ckpt_dir is kinda optional - only if the user wants to save the ckpts and other artifacts to somewhere specific.
That makes no sense to me. I can think of no scenario in which the user knows they want to save, but want no control about where it gets saved.
I still would not separate the two parameters like this.
| If True, save the best model checkpoint and the `datamodule_cfg`. | ||
| ckpt_dir : Union[str, Path], default="checkpoints" | ||
| Directory to save artifacts. | ||
| ckpt_kwargs : dict, optional |
There was a problem hiding this comment.
having arguments foo and foo_kwargs always indicates that maybe we should consider using a dataclass constructor pattern instead, and pass only foo, but it can be a newly constructed object, in this case ModelCheckpoint. Why not ask users to pass a ModelCheckpoint if they want kwargs?
There was a problem hiding this comment.
Sorry I didnt understand the question
We are not asking for foo, just foo_kwargs, we always create foo internally ourselves.
If we ask form the user to pass the ModelCheckpoint, they would themselves have to create an instance of ModelCheckpoint and I think that goes against the idea of encapsulation. If we know we are always going to use ModelCheckpoint, why ask from the user to pass it? Just ask for any specific kwargs they want to pass - which is completely optional.
This also saves the users from malicious code. If we ask the user to pass the instance of ModelCheckpoint, someone can create their own child of this class with some malicious code and pass that to the method. And as the footprint of the child is similar to ModelCheckpoint, it could cause issues - although this might be bit of a stretch, but it is possible. If the user wants so much of customization, they would bypass pkg class and use the "bare bones" pipeline instead.
I think we should always just ask for the cfgs and keep most of the things encapsulated as much as we can - to save the effort of the user to create everything themselves and also from security PoV it is better.
What do you think? @fkiraly
There was a problem hiding this comment.
hm, ok, I think that makes sense. Are there no other classes the ModelCheckpoint could be replaced by?
| ``` | ||
| **Note that the `_save_artifacts` methods doesnt save `trainer_cfg` which is a bug i think, we should save it (or always ask for this cfg from the user - both have their own pros and cons)** | ||
|
|
||
| ##### Loading |
There was a problem hiding this comment.
should there be a loose function load(ckpt_path:str)? Which recognizes what is in the path, and then loads it?
Currently, the user needs to know which model is stored in a file location, which feels cumbersome, as in the case of many models they need to keep track which model is where. Instead, a bare load would, or could, be more comfortable.
There was a problem hiding this comment.
Yeah that is the issue of the current implementation, please see Proposed design sec for more info, I have added similar method there already
| - There is no clear `load` and `save` methods - this makes adding ways to save new artifacts (like `scalers`) hard as we dont have a specific place where we can keep this logic. | ||
| - Everything is intermingled - the same method `_build_model` builds a mpdel from a config and from the checkpoints. There is no clear distinction between the responsibilities of the methods | ||
|
|
||
| ## Proposed Design |
There was a problem hiding this comment.
I think you need to think about use cases clearly. There may be distinctions as in:
- how much the user wants to save or load - e.g., weights only, configs too, and even fitted scalers etc.
- when the user wants to save or load - e.g., to checkpoint their own training process, or simply to load an inference ready checkpoint
There was a problem hiding this comment.
I think the tree inheritance takes care of this, but I think we should add some args for saving scalers, thanks I will make the updates
…'coordinator' design
|
Hi @fkiraly, @agobbifbk I think the design is ready for review |
|
|
||
| 1. The `pkg.save` calls internally `datamodule.save` and `model.save`. | ||
|
|
||
| - Here, we would also have an arg called `exclude` (present in `fit` and `pkg.save`) which expects a `list` of strings and the user can decide what to "exclude" while saving (like excluding scalers and saving only the model weights). By default, it would be empty. See the vignettes in the next section for more info. |
There was a problem hiding this comment.
Are you sure that empty is the default? In terms of usability I would expect a list of all the possible things to save: if i use the default values I can not reuse the models, this feels strange to me
There was a problem hiding this comment.
by exclude being empty, we would have an empty list - meaning NOTHING is excluded, and we save EVERYTHING.
Do you prefer it in some other way?
There was a problem hiding this comment.
My bad, it's fine like now!
There was a problem hiding this comment.
I am thinking about having an exclude or an include parameter, I suppose exclude is better? But I am not sure, often the positive specification makes sense if it is an action. Default (e.g., None) saves all, and it might be easier for a user to think about what to save as opposed to what not to save, in difference to a hard to remember full set of options?
| batch_size=32, | ||
| target_normalizer=TorchNormalizer(), | ||
| scalers=scalers | ||
| # scalers are already None by default, so we dont need to pass scalers=None |
There was a problem hiding this comment.
typo here:
# scalers are already None by default, so we dont need to pass scalers=None
| # choice is upto the user | ||
| ckpt_dir=ckpt_dir, # not None means we do the checkpointing | ||
| model_ckpt_kwargs={"monitor": "train_loss_epoch"}, # for ModelCheckpoint | ||
| exclude = ["scaler", "target_normalizer"] # dont save scalers and target_normalizers |
There was a problem hiding this comment.
This feels strange to me, I used scalers but I exclude them, so how can I suppose to run the model in inference if I lost all the information about the scalers?
There was a problem hiding this comment.
This is the case where the user just want to save the model weights. Nothing else, maybe useful in something like pretraining? I dont think it is always the case that we save (and use) the same scalers everytime (like in pretrianing)?
There was a problem hiding this comment.
I see, but the weight will work only if the data are scaled as the training dataset. We can keep this functionality for sure, but I would like to see a giant warning :-)
There was a problem hiding this comment.
well, I think it is the user who is giving instructions explicitly in this case.
Perhaps we want some better example in the vignette on including or excluding artefacts?
Seems clean to me, in DSIPTS (or in other projects) we save all the configuration in a yaml file because we build our code leveraging on HYDRA https://hydra.cc/docs/intro/. The benefit of using hydra is that when you use the code via bash you can overwrite some of the configuration directly by command line. I suggest to have a look to it and see if this is an overkill for this particular task. I try to give here an example how the code reads if all the configurations are manage through omegaconf-hydra: NOW: dataset = TimeSeries(
data=data_df, # data_df is any arbitrary dataframe
time="time_idx",
target="y",
group=["series_id"],
num=["x", "future_known_feature", "static_feature"],
cat=["category", "static_feature_cat"],
known=["future_known_feature"],
unknown=["x", "category"],
static=["static_feature", "static_feature_cat"],
)
# data module config
datamodule_cfg = dict(
max_encoder_length=30,
max_prediction_length=1,
batch_size=32,
)
# model config
model_cfg = dict(
loss=MAE(),
logging_metrics=[MAE(), SMAPE()],
optimizer="adam",
optimizer_params={"lr": 1e-3},
lr_scheduler="reduce_lr_on_plateau",
lr_scheduler_params={"mode": "min", "factor": 0.1, "patience": 10},
hidden_size=64,
num_layers=2,
attention_head_size=4,
dropout=0.1,
)
# trainer config
trainer_cfg = dict(
max_epochs=5,
accelerator="auto",
devices=1,
enable_progress_bar=True,
log_every_n_steps=10,
)
model_pkg = TFT_pkg_v2(
model_cfg=model_cfg,
trainer_cfg=trainer_cfg,
datamodule_cfg=datamodule_cfg,
)The new schema will be something like: dataset = TimeSeries(
data=data_df, # data_df is any arbitrary dataframe
**config.dataset
)
model_selected = eval(config.model)
model_pkg = model_selected(
model_cfg=config.model_cfg,
trainer_cfg=config.trainer_cfg,
datamodule_cfg=config.datamodule_cfg,
)with a yaml file paths:
root: checkpoints ## this is the main path dir
artifacts:
best_model: None ## these will be filled during the training
scalers: None
target_normalizers: None
model_cfg: None
datamodule_cfg: None
trainer_cfg: None
datamodule_metadata: None
load:
skip: [scalers] ## use after in the when
save:
opt1: other_options
dataset:
time: time_idx
target: y
group: [series_id]
num: [x, future_known_feature, static_feature]
cat: [category, static_feature_cat]
known: [future_known_feature]
unknown: [x, category]
static: [static_feature, static_feature_cat]
data_module:
max_encoder_length: 30
max_prediction_length: 1
batch_size: 32
model_cfg:
loss: : "MAE()" # somewhere after you need to call `eval `
logging_metrics: ["MAE()", "SMAPE()"]
optimizer: "adam"
optimizer_params:
lr: 1e-3
lr_scheduler: "reduce_lr_on_plateau"
lr_scheduler_params:
mode: "min"
factor: 0.1
patience: 10
## these are somehow model dependent, hydra can let you manage this, meaning that you can have, for each model, a dedicated config file with its own parameter and than hydra manage to glue together the different configs
hidden_size: 64
num_layers: 2
attention_head_size: 4
dropout: 0.1
model: TFT_pkg_v2
trainer_cfg:
max_epochs: 5
accelerator: auto
devices: 1
enable_progress_bar: True
log_every_n_steps: 10
The main idea is that the This may seems an overkill but please have a look to this: https://hydra.cc/docs/tutorials/basic/running_your_app/multi-run/ in my opinion this is a nice-to-have feature! |
|
Another option is: close this with |
|
I think hydra is a nice-to-have feat, but a feat that should be a "future" endeavour - once we have atleast the basic API stable. But I think |
|
I think that json or yaml are equivalent at this stage but yaml opens better compatibility in case we will switch to hydra in the future. Moreover, the user potentially will access to the V2 api through a configuration mechanism, hopefully not args from the main, so yaml is definitively the most reasonable choice here IMHO |
|
Thanks, I think this also resonates with how the configs ( Thanks I will update the proposal with this new change! |
| Currently, these checkpoints and cfgs are saved in this format | ||
| ``` | ||
| ckpt_dir/ | ||
| ├── best-epoch=X-step=Y.ckpt |
There was a problem hiding this comment.
what does "best model" mean here? Should it mot simply be model.ckpt for simplicity?
There was a problem hiding this comment.
i took it from the "naming" convention - mainly we save the best best model with best performance, But i agree I should add a generalised name here in the example.
But I would disagree if you mean that we save the model ckpts as model.ckpt simply. As sometimes we dont save just one model - the best or last one, but we save the models at every one (or few epochs). In that case it would be useful to have a naming that clearly define the epoch, step etc
There was a problem hiding this comment.
But I think this is more of a ModelCheckpoint responsibility then ours.
| - There is no clear `load` and `save` methods - this makes adding ways to save new artifacts (like `scalers`) hard as we dont have a specific place where we can keep this logic. | ||
| - Everything is intermingled - the same method `_build_model` builds a mpdel from a config and from the checkpoints. There is no clear distinction between the responsibilities of the methods | ||
| - Everything is saved in the same directory and there is no clear distinction between different artifacts. Optimally, in the parent directory (lets say `ckpt_dir`) should have separate sub-directories for model-checkpoints, metadata, configs and so on. | ||
| Meaning - the directory shuould be like this: |
| But currently, there are no such sub-folders (see `Example 1: Saving` section for more info). | ||
|
|
||
| ## Proposed Design | ||
| After discussion with Franz: |
There was a problem hiding this comment.
you do not need to cite me here specifically, but you can list me as a contributor at the top.
|
|
||
| 2. If the datamodule has scalers (or any other artifact) to save, it would save it and return the path of the saved artifact else, return `None`. The model saves the model weights using `ModelCheckpoint`. | ||
|
|
||
| 3. After collecting the paths where all the artifacts have been saved from the D2 and M layers, it would create a `artifacts.yaml` that would save the artifacts and the place they are saved |
There was a problem hiding this comment.
I think json and yaml should be options in the end
There was a problem hiding this comment.
Hmm, so you are suggesting we add support for both, that is a good idea. But rather than adding a new param to decide which file ext to use, would it make more sense to allow json and the method is json compatible but save would always give out a yaml file.
Meaning if the user wants they can pass their own json file, but if they are relying on pkg.save, it would always give out yaml file.
Is this a good idea or am I just oversimplifying things and it would be better to have a param to let the user decide - what save gives out - yaml or json?
I am not opposed to any of the solutions, I just want to know if this would be a good idea to make save a bit strict and keep its param set small?
|
|
||
| 4. The `pkg` class just performs the reconciliation and make sure if everything is saved in correct places or not. | ||
|
|
||
| - The user loads by specifically calling `pkg.load()` - the only endpoint for loading. The user just has to pass the `ckpt_dir` or path to `artifacts.yaml`. |
There was a problem hiding this comment.
similarly, I think json should also be ultimately supported. In load, we can disambiguate by format or file ending.
| ) | ||
| ``` | ||
| - using `pkg.save` directly | ||
| - **NOTE** if you call this method after `fit`, it will save the model checkpoints from the very LAST epoch and not the best model. |
There was a problem hiding this comment.
hm, I think this is a point of confusion!
I feel the behaviour here needs to be made much clearer as to what gets saved!
We may also want to have a config that determines what is the state of the python object after fit, e.g., best model or last epoch. If this does not exist, it would be very confusing that you can have behaviour after saving that you cannot have in-memory (e.g., a choice of "best model" vs "last epoch")
There was a problem hiding this comment.
Hmm, then I think it would be best that we leave this to ModelCheckpoint? If you pass save_last=True in that callback, it would save the model from the last epoch. Actually ModelCheckpoint can help save model ckpts in any way (can save the best model as well based on different "monitor"" etc), so should we just remove pkg.save as a endpoint and let fit call it only?
See the documentation of ModelCheckpoint here: https://lightning.ai/docs/pytorch/stable/api/lightning.pytorch.callbacks.ModelCheckpoint.html
If you call save after fit, there would be only the model from the last epoch that would be present in the memory (in most cases) and that is the only thing that save could save at that time. ANd for that we would need to add specific ways as ModelCheckpoint is a callback and can save the model via Trainer, but after fit we wouldnt be using Trainer.
I think it is not worth the effort to keep save public?
Only load stays public and save becomes private called by fit.
What do you think about this?
| And `artifacts.yaml` would look like this: | ||
| ```yaml | ||
| artifacts: | ||
| best_model : "checkpoints/model_ckpt/best_model.ckpt" |
There was a problem hiding this comment.
confusing, since best_model seems to depend on context. I would say, this should be model.ckpt, since whether the model was "best" in a sense, or just "some" model, is not an intrinsic property of the artefacts saved.
| # choice is upto the user | ||
| ckpt_dir=ckpt_dir, # not None means we do the checkpointing | ||
| model_ckpt_kwargs={"monitor": "train_loss_epoch"}, # for ModelCheckpoint | ||
| exclude = ["scaler", "target_normalizer"] # dont save scalers and target_normalizers |
There was a problem hiding this comment.
- how does the user know what options there are in
exclude? This should be clear. - in particular, I would make include/exclude strings equal to the names of files, where possible.
I think this part of the design still requires some detailed thinking to make it easy to use, but we are probably not far from a good solution.
There was a problem hiding this comment.
I think exclude is better than include as while running we would want to save everything in most cases, and added everything to a list is a waste of effort, imo. I think we should think of a rarer case where we dont save a specific thing - so we exclude it. Also, in most cases include would contain 3-4 items (model checkpoints, scalers, target_normalizers, etc) but exclude would be a smaller list.
- how does the user know what options there are in
exclude? This should be clear.
It would be ANY artifact - except the configs and datamodule.metadata. (and maybe even model chekcpoints?)
- in particular, I would make include/exclude strings equal to the names of files, where possible.
I am sorry I am not very clear what you mean by this, can you please elaborate on this?
| ) | ||
|
|
||
| model_pkg.save( | ||
| ckpt_dir=ckpt_dir, # not None means we do the checkpointing |
There was a problem hiding this comment.
these missing indentations are bothering my mental linter...
| datamodule_metadata : "checkpoints/metadata/datamodule_metadata.pkl" | ||
| ``` | ||
|
|
||
| #### Load |
There was a problem hiding this comment.
just for structure, I would show a simple save/load cycle as a first vignette pair. Only then I would present more variants of either.
The reason for this is that save and load need to match each other.
| To load the artifacts, the user has just one end-point - `pkg.load()`. They must pass a path to the directory where `artifacts.yaml` is saved. This directory must contain all the artifacts the user want to load. | ||
| If the user want to skip loading any specific artifact that is present in the `artifacts.yaml`, they 3 options: | ||
| - add that to `exclude` in `pkg.save`. | ||
| - OR add a new key `"skip"` in the yaml and add the list of artifacts that need to be skipped there |
There was a problem hiding this comment.
this is weird - is this not equivalent to simply removing the files from the manifest? Why add a skip instead?
| - add that to `exclude` in `pkg.save`. | ||
| - OR add a new key `"skip"` in the yaml and add the list of artifacts that need to be skipped there | ||
| - OR a more dangerous take - delete that entry from the yaml, but that would lead the loss of location of that artifact. | ||
| We should not have a `exclude` (or similar) param in `load` to keep `load` simple and lean - a naive laoder which loads everything it sees. The save-time `exclude` is where that decision belongs, if the user didn't want something persisted, they shouldn't have saved it. The `"skip"` key is an option for edge cases (see the end of this section for more information). |
There was a problem hiding this comment.
disagree. I think user choice of what to load should not require file manipulation!
There was a problem hiding this comment.
if the user didn't want something persisted, they shouldn't have saved it
I think this is an incorrect argument.
You need to consider the use case where someone else has saved the artefacts. Standard use case in loading weights from huggingface!
There was a problem hiding this comment.
Hmm, so I think you and @agobbifbk both agree with the alternative design (see line 835) of adding a param instead?
I would go forward with that then
fkiraly
left a comment
There was a problem hiding this comment.
Really great! Especially the idea of multiple files, defined folder structure, and manifest file (artifacts,yml).
I have two major questions or issues with the design:
- loading only parts of a saved file package should not require manipulating the
artifacts.ymlfile, see discussion above - I think the semantics vs file format of "best model" and "last model" need to be clarified - on both layers, in-memory (what is the state after
fit?), and aftersave(what is the state aftersave?). Plus, whether it makes sense to distinguish "best models" from "last models" aftersave. I currently think it makes no sense to have a distinction of the save file format by nature of extrinsic origin.
I also left some smaller comments.
|
|
||
| It will load all the artifacts that are present in `artifacts.yaml`. | ||
| If you want to skip anything you want to load, you have three options: | ||
| - add that to `exclude` in `pkg.save`. |
There was a problem hiding this comment.
I think none of the three options here make sense to me.
- "just exclude in save" - assumes the person saving and loading are the same. Cannot assume that
- the remaining two: modification of loading behaviour which as a concern sits in the python layer should not require file modifications.
There was a problem hiding this comment.
in my opinion, you already see how bad an idea the options are by how bad the (very honest and precise) docstring gets.
Noting that the docstring is "bad" since these are very unpleasant instructions, but it is also "very good" in that it captures with precision what you want the user to do. Like a very precise recipe on how to make a sandwich with canned tuna, mustard, strawberries, and woodchips.
|
|
||
| ckpt_path/ | ||
| ├── checkpoints/ | ||
| └── best-epoch=X-step=Y.ckpt |
There was a problem hiding this comment.
I think you need to think about the assumption when loading the model. Having a variable name for files in checkpoints feels like a recipe for imprecise specifications. I would set this to model.ckpt and not vary the name depending on origin. If you want to add metadata on origin, this could be in a separate file (with standardized name) but I would even not do that.
There was a problem hiding this comment.
here X and Y were just "placeholders" in reality it would be something like this: best-epoch=3-step=100.ckpt (best model from epoch 3, step 100).
But I think this is somehthing that ModelCHeckpoint should (and would) handle. we just need to pass filename as the params to it
|
Hi @fkiraly @agobbifbk
Please let me know what you think about these points |
As mentioned in the other comments, for me
We can also consider to explicity pass the parameters like: best_model = model_pkg.fit(
dataset, # a TimeSeries Object of dataset
ckpt_dir=ckpt_dir, # not None means we do the checkpointing
model_ckpt_kwargs={"monitor": "train_loss_epoch"}, # for ModelCheckpoint
save_scalers = True,
save_target_normalizer = True,
)and have the same flags in the load function? I need to raise also another option here: are we sure we want to save stuff during the fit phase? See next comment
For me it is more intuitive that the user call
I hope this can help and not add noise/confusion :-) |
Hi @agobbifbk, here we are using
What do you think about this? |
|
I don't have a strong opinion on that, I always prefer to call |
This EP proposes a design for loading and saving methods in
BasePkginpytorch-forecastingv2 - to load and save models and other artifacts (likescalers)Based on hackmd: https://hackmd.io/@Pm5-sJBvSfeR6I59oCaLOA/Bk9Xt5Txzl/edit