Skip to content

Generalisation to regression#22

Merged
james-large merged 37 commits intosktime:devfrom
Withington:regressors
Feb 19, 2020
Merged

Generalisation to regression#22
james-large merged 37 commits intosktime:devfrom
Withington:regressors

Conversation

@Withington
Copy link
Copy Markdown
Collaborator

Reference Issues/PRs

Fixes #18

What does this implement/fix? Explain your changes.

Draft pull request: do not merge. Please review the structure of the new regressors and networks folders and classes. These enable regressors and classifiers to be built using the same code to define the main part of their networks.

Any other comments?

  • Implemented MLP and CNN to demonstrate the structure. See test_regressors.py for tests on the regressors and on using them for forecasting.
  • Added utils folder for common functionality, e.g. save_trained_model

I look forward to your feedback.

@Withington Withington requested review from james-large and mloning and removed request for james-large January 25, 2020 08:45
@james-large
Copy link
Copy Markdown
Contributor

Looks decent, all makes sense

The testing of forecasting via reduction is cool too

A part of me I don't like is thinking to abstract to a ...Predictor class, with fit/predict that Classifier/Regressor extend, but the rest of me says there's nothing to gain and it'd be a waste of time.

@Withington
Copy link
Copy Markdown
Collaborator Author

Like an MLPPredictor that MLPClassifier and MLPRegressor extend? Maybe it could work using an MLPPredictorMixin (like the mixin classes of sklearn). I’ll have a look, it might be a neat way to handle the network’s parameters.

@mloning
Copy link
Copy Markdown
Contributor

mloning commented Jan 27, 2020

All looks good to me, the only thing I'm not sure about is the folder structure, networks/deeplearning, we don't want users to access anything is this folder, so perhaps rename it to base/estimators/deeplearning/, that way we can also refactor the classifiers in the sktime base repo into regressors simply by adding more folders to base/estimators/, what do you think?

@Withington
Copy link
Copy Markdown
Collaborator Author

Folder structure: yes that could work. It would look like:

sktime_dl/base/estimators/deeplearning
sktime_dl/classifiers/deeplearning
sktime_dl/regressors/deeplearning

and in sktime
sktime/base/estimators
sktime/classifiers containing /distance_based and other folders
sktime/regressors add new folder /distance_based and other folders

@mloning
Copy link
Copy Markdown
Contributor

mloning commented Jan 31, 2020

Seems like a lot of folder name duplications. But perhaps still better than the alternatives. One alternative would be to follow scikit-learn and abandon the classifiers/regressor folder divide and go with a category which contains both classifiers and regressors. Not sure what a good category would be, though. Perhaps supervised or supervised_learning? We would then have files for the sub-categories (deep learning, distance-based, etc) in which both regressors and classifiers (and their shared base/parent classes) live. What do you think?

@Withington
Copy link
Copy Markdown
Collaborator Author

Yes, the sklearn folder structure might work well. In sklearn there is a file:
sklearn/base.py – contains base classes the different types of estimator:
BaseEstimator
ClassifierMixin
RegressonMixin
ClusterMixin
etc

Then there are separate folders under sklearn for each type of algorithm

  • sklearn/neighbors (for KnearestNeighbor) contains
    • _base.py – classes NeighborsBase, KNeighborsMixin, etc
    • _classification.py for class KneighborsClassifier and the classification functions
    • _regression.py for class KneighborsRegressor and the regression functions
  • sklearn/neural_network (MLP)
  • sklearn/svm
    etc

sklearn-dl could be simililarly structured with

  • sklearn-dl/base/estimators containing
    • classifier.py for class BaseDeepClassifier
    • regressor.py for class BaseDeepRegressor
    • cluster.py – add later for unsupervised learning clustering
    • other types of learning e.g. association, reinforcement

and then the architectures go in folders like

  • sklearn-dl/deeplearning/cnn
    • _base.pyclass CNNMixin to build the core CNN network, minus the final output layer
    • _classifier.pyclass CNNClassifier to build_model(), to add the final layer to the CNN, and fit()
    • _regressor.pyclass CNNRegressor to build_model() and fit()
    • _cluster.py – add later
    • other types of learning e.g. association, reinforcement
  • sklearn-dl/deeplearning/mlp
    • _base.py class MLPMixin
    • _classifier.py
    • _regressor.py
    • _cluster.py – add later

This would keep all CNN code in one place, not split over 3+ folders.

In fact, the deeplearning level could be dropped, since sktime-dl implies deeplearning, leaving

  • sklearn-dl/base/estimators (for classifier, regressor, cluster, etc)
  • sklearn-dl/cnn
  • sklearn-dl/mlp
  • folders for other architectures – resnet, fcn, etc

The structure above is like sklearn but is less like the sktime structure (where the distance_based folders live).

Let me know what you think.

@mloning
Copy link
Copy Markdown
Contributor

mloning commented Jan 31, 2020

Yes, for sktime-dl this works well, happy if you push this on this PR!

For sktime we have to think about it again, as we have additional stuff that is shared across classification and regression modules (composition, tuning, etc) but giving up the classification and regression categories does not seem right.

…base classifier to base/estimators/_classifier.py. Tests pass.
…ase regressor to base/estimators/_regressor.py. Tests pass.
…e (e.g. a network without its final layer) into deeplearning/[architecture name]. Moved base network to base/estimators/_network.py. Tests pass.
@Withington
Copy link
Copy Markdown
Collaborator Author

Work in progress.....

@mloning
Copy link
Copy Markdown
Contributor

mloning commented Feb 7, 2020

I've thought about the folder structure for base sktime, let me know what you think: #229

@Withington
Copy link
Copy Markdown
Collaborator Author

Thanks for the invitation to look at #229 . It seems there are strong arguments for sticking with the current folders for classifiers, regressors, etc. Therefore, you could put them as they are in an estimators folder.

The other shared code that you mention is specific to tasks so could go in a tasks folder. Does this task code need to add functionality to each estimator? It might be appropriate to use the Decorator design pattern.

sktime 
└───estimators
|   |───base  	(e.g.for code shared by XXClassifier and XXRegressor)
|   |───classifiers
|   |───regressors
└───tasks  (common to different algorithms)
    └───event_modeller
    └───forecaster
    │   │───base.py
    │   │───composer.py
    │   │───model_selector.py
    └───predictor
    └───segmentor
    └───transformer

…t data moved to adjust_parameters(X) and extra adjustment added to handle the short series that might be used for forecasting. Modified slice_data() to handle y that can be regression values or int class labels and where y can be 1D array (regression) or 2D array (classification). Modified pre_processing() to handle y that can be 1D array or 2D array.
@Withington
Copy link
Copy Markdown
Collaborator Author

Failing on TF 1.15 with "tensorflow.python.framework.errors_impl.InvalidArgumentError: Tensor input_8:0, specified in either feed_devices or fetch_devices was not found in the Graph"

@mloning
Copy link
Copy Markdown
Contributor

mloning commented Feb 9, 2020

Thanks for your comments!

Forecasters are estimators like classifiers or regressors, and so would be segmentors or event modellers (i.e. probabilistic forecasters). While any regressor can be used to solve a forecasting task (via reduction), there are also specific forecasting algorithms which we want to include (e.g. exponential smoothing). So decorating regressors with forecasting functionality wouldn't be enough, no? Currently, we're encapsulating reduction from forecasting to regression as a meta-forecaster that works with any regressors (see this #218 for work in progress).

You suggest to group by function (i.e. estimation, model selection, etc.), rather than task/setting, so if we include forecasters in estimators, this could look like this:

sktime 
└───estimators
|   |───base  	(e.g.for code shared by estimators)
|   |───classifiers
|   |───regressors
|   |───forecaster
|   └───...

But then I'm not sure how to organise model selection and other composition methods specific to the task/setting (e.g. a SlidingWindowSplitter used when evaluating forecasting models but not useful for evaluating other performance on other tasks).

I also don't like the idea of giving up the classifier and regressors folder on the top level and putting it inside some much more obscure "series_as_features" module, but can't think of anything better at the moment.

@james-large
Copy link
Copy Markdown
Contributor

Scatter brain/rambling/not fully formed thoughts ahead, sorry

Ultimately there's going to be redundancy somewhere in the structure most likely, question is what redundancy do you want to accept.

In what ways can these things reasonably be grouped?

  1. Input/Output
    1a. series -> series transformers
    1b. series -> vector transformers
    1c. series -> double/vector predictors
    1d. model -> model meta processes (composition, tuning, etc)
    1e. a -> b general/other utilities
    1f. etc.

1.2 Output only?

  1. High level data tasks
    2a. prediction (classification, regression, forecasting, etc.), aka predictors, estimators, models etc.
    2b. composition
    2c. selection
    2d. tuning
    2e. representation/transformation
    2f. evaluation
    2g. visualisation?
    etcetc.

  2. Prediction tasks (basically the members of 2a)
    3a. classification
    3b. regression
    etc.

  3. Conceptual algorithm type
    3a. shapelet vs interval vs dictionary etc
    3b. classical vs modern?

Others?

4 is certainly the weakest (or maybe most niche?) grouping IMO, but parties want it in. I'd argue for 2 being the strongest. There's the question of whether prediction (or whatever name it could be) should be an encapsulating folder, or instead if classification,regression... should be their own top level folders, but yeah you need somewhere to put base.py for estimators so it makes sense for the prediction containing folder to be there really.

Then when it comes to which redundancy to accept in relation to e.g. specific evaluation methods as per example above, you probably repeat the classification,regression... categories in each of composition, selection etc. as needed

sktime/
    prediction/
        base.py
        classification/
             base.py
             dictionary_based/
                  boss.py
             ...
        regression/
            base.py
            dictionary_based/
                ...
            ...
         ...
    evaluation/
         base.py 
         ...
         classification/ 
              classification_evaluation_methods.py  # only if needed beyond base/shared methods
         forecasting/
              sliding_window_splitter.py
              ...         
         ...
    composition/
         base.py 
         ...
         classification/ 
              classification_composition_methods.py  # only if needed beyond base/shared methods
         ...

If you argue for 3 being the strongest grouping over 2, you flip the priority of classification,regression etc and put that on top level, while duplicating prediction,evaluation,composition directories

In terms of usage, the imports for some experiment you want to run would then be something like

from sktime.prediction.forecasting import FancyForecaster
from sktime.evaluation import SomeGenericEvalMetric
from sktime.evaluation.forecasting import SlidingWindowSplitter 
...

and so on. From the point of view of a user, the redundancy might not even be that unattractive really, makes it obvious what methods can be shared between prediction tasks and what are specific.

So long as you ignore any code that might be useful for more than one but not all prediction tasks...

@mloning
Copy link
Copy Markdown
Contributor

mloning commented Feb 11, 2020

Thanks, I like that structure too! One issue is that classification and regression (and to some extent clustering) share composition and tuning functionality, so it doesn't make much sense to put this into composition/classification, so we need some series_as_features folder, no? I don't like the idea of giving up classification/regression grouping either but can't think of any better grouping.

Anyway, before we go on here, would you mind posting that into the other issue thread? @Withington please feel free to comment on the other issue as well!

@Withington Withington marked this pull request as ready for review February 13, 2020 17:07
@Withington
Copy link
Copy Markdown
Collaborator Author

This seems like a good time to merge this pull request. Seven regressors have been created. The outstanding three are MCDCNN, MCNN and TWIESN. I can work on these but they require a bit more attention:

MCDCNN

  • investigate the intermittent failure. “ValueError: Error when checking target: expected dense_9 to have shape (1,) but got array with shape (2,)
  • Enable multi-variate use. Add multi-variate tests before making code changes to create the regressor.

MCNN

  • add a test on a dataset where dl-4-tsc achieves good accuracy. On ItalyPowerDemand only 0.5 is achived by both dl-4-tsc and sktime-dl.

TWIESN

  • investigate why sktime-dl accuracy on ItalyPowerDemand is only 0.49 while dl-4-tsc achieved 0.88.
  • add a test on a dataset where sktime-dl acheives good accuracy.

Could you please review the current pull request and consider merging it to dev?

There are now a lot of changes so feel free to ask me about it. I kept it fairly clean by moving entire blocks of code from name/_classifier.py to name/_base.py.

Copy link
Copy Markdown
Contributor

@mloning mloning left a comment

Choose a reason for hiding this comment

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

Great work! I've left a few minor comments, but overall looks good to me!



class BaseDeepRegressor(BaseRegressor, RegressorMixin):
model_save_directory = None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we want the model_save_directory as a class attribute?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

BaseDeepClassifier already had model_save_directory as a class attribute so it is included in BaseDeepRegressor too.

self.save_trained_model() is always called at the end of fit(X, y) and the model is saved if model_save_directory is not None.

It is also used occasionally in callbacks, e.g. ResNet and TLeNet keras.callbacks.ModelCheckpoint

I wouldn't put model_save_directory in BaseDeepNetwork because a network is incomplete, it's not a full model.

Does that answer your question or did you have something else in mind?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure why it's defined on the class and not the instance level?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good question. Perhaps all BaseDeepRegressor and BaseDeepClassifier class attributes need to be moved to the instance level? Currently we have:

class BaseDeepClassifier(BaseClassifier):
    classes_ = None
    nb_classes = None
    model_save_directory = None
    model = None
    model_name = None

Any reason why it would need to stay like this? Memory considerations? @james-large ?

I'll try it locally and test it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Class attributes moved to instance attributes and __init__ calls updated accordingly. What do you make of it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, happy to merge once @james-large has reviewed it! :-)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This makes more sense for sure, problem of poor python-esque code understanding back then on my part probably

…ance because BaseDeepRegressor already inherits it.
@Withington
Copy link
Copy Markdown
Collaborator Author

Comments above. Please hit the "Resolve conversation" button if my replies cover your points satisfactorily.

@james-large
Copy link
Copy Markdown
Contributor

I'm happy. Nice work @Withington. Added some comments to the individual queries above, I reckon everything's fine as is. Shall we get this into dev and then look into the dev branch TS problems if they still persist?

Will leave this up for another couple hours or so while I head home in case of any sudden changes of heart, and merge it in

@james-large james-large merged commit 4403590 into sktime:dev Feb 19, 2020
@Withington
Copy link
Copy Markdown
Collaborator Author

Thanks for the merge! 😃

What are the "dev branch TS problems" @james-large ? I'm not sure what TS is.

@Withington Withington deleted the regressors branch February 20, 2020 15:45
@james-large james-large mentioned this pull request Feb 20, 2020
@james-large
Copy link
Copy Markdown
Contributor

Ah meant tf, tensorflow. There was some problem with the tf2.x version of the travis checks iirc? No matter, looks fixed, maybe it was long ago or I imagined it. Brain's still recovering

@Withington
Copy link
Copy Markdown
Collaborator Author

TF 2.0 was fine, that is in dev branch now.
TF 2.1 was a problem:

  • TensorFlow 2.1 and scikit-learn 0.22 - build fails with "It seems that scikit-learn has not been built correctly."
  • TensorFlow 2.1 and scikit-learn 0.21.3 works fine, the build passes.
    I'll try again when there have been some updates.

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.

3 participants