[ENH] speed up RandomShapeletTransform #3574
Conversation
Thank you for contributing to
|
patrickzib
left a comment
There was a problem hiding this comment.
Not a review, just a comment. Maybe calling if on every value is not super efficient? normalization once outside the loops or using / std=1.0 might be faster?
Stage 1 of the rotation forest speed up plan. Extracts the code duplicated between RotationForestClassifier and RotationForestRegressor into a new BaseRotationForest in aeon/base/_estimators/sklearn, following the BaseIntervalForest pattern of switching task-specific behaviour on is_classifier. The public classes are now thin subclasses keeping their docstrings, signatures and public methods. Outputs are bit-identical to the previous implementations for fit/predict/ predict_proba/fit_predict on both estimators, with one deliberate fix: the regressor previously ignored its pca_solver parameter and always used the 'full' SVD solver. It now honours the parameter like the classifier, so the expected values in test_rotf_output (which passes pca_solver='randomized') have been regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n forest (#3630) Optimisation stage 1 of the rotation forest speed up plan. Attribute groups are only a few columns wide, so the sklearn PCA object per group is almost all validation and wrapper overhead (measured 0.43ms vs 0.05ms per group). Each group rotation is now a minimal _GroupPCA computing an exact eigendecomposition of the group covariance matrix, storing only the mean and components. The components match the sklearn 'full' SVD solver bit-for-bit, including its sign convention, except for rank-deficient inputs where trailing components are mathematically arbitrary. Predictions on the default settings are unchanged; the docstring examples and downstream STC/HC2/FreshPRINCE outputs are identical. The pca_solver parameter is deprecated and has no effect, so the pinned values in the two test_rotf_output tests (which passed pca_solver='randomized') have been regenerated. Old pickles still predict correctly since stored sklearn PCA objects are used via the same transform interface. Measured against the previous commit (500 cases, single thread unless noted): - predict: 3.2-4.4x faster across shapes - fit: regressor 1.4x, classifier 1.0-1.1x (tree building dominated) - fit with n_jobs: scaling plateau lifted from 2x to 4.2x on 8 threads (500x400, 40 trees: 6.0s -> 2.2s) - fit_predict_proba: 1.6x Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t trees (#3630) Optimisation stages 2 and 4 of the rotation forest speed up plan. When base_estimator is the default, trees are now constructed directly instead of via clone, and fit/predict on them pass check_input=False since the rotated X_t is always a finite float32 array built internally. The default tree is seeded with an int drawn from the ensemble rng, consuming exactly one draw as sklearn's _set_random_states did on the clone path, so all outputs remain bit-identical. User-supplied base estimators keep the fully validated path. The out-of-bag index computation in fit_predict previously scanned the subsample array once per case (O(n^2)); it now uses a boolean mask (O(n)). Measured at n=20,000: 0.12s -> 0.15ms per tree, saving ~24s across a 200-tree fit_predict. The OOB result scatter is also vectorised. Measured overall: fit ~8% faster single-threaded (500x800, 20 trees: 11.1s -> 10.2s min-of-2 interleaved); predict unchanged (tree traversal dominated); fit_predict unchanged at small n, large savings at large n. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…3630) Optimisation stage 3a. The per-estimator rotation previously built a list of per-group transformed arrays, concatenated them, cast to float32 and always ran nan_to_num (three full-array scans for nan/+inf/-inf). It now writes each group's rotation straight into a preallocated float32 array (the cast happens on assignment) and runs the full nan_to_num replacement only when a single np.isfinite scan finds a non-finite value, which cannot happen for the normalised inputs and finite _GroupPCA components in the common path. Output is bit-identical. Shared by predict and the fit tail via _transform_for_estimator. Measured 1.5x on predict (500x800, 50 trees: 0.33s -> 0.22s, interleaved min-of-6). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ion skip, n_jobs=1 path (#3630) A batch of small, bit-identical optimisations from the speed up plan: - Cache is_classifier(self) once as self._is_classifier instead of calling it in the per-group and per-estimator inner loops. - Extend check_input=False to any sklearn BaseDecisionTree base estimator (self._skip_tree_checks), not just the None default, so user-supplied plain decision trees (as in the tests and FreshPRINCE-style usage) also skip redundant input validation. Non-tree estimators keep the validated path. - Build the classifier PCA sampling matrix with a single np.concatenate over the selected class blocks rather than growing it one class at a time. - Skip joblib dispatch entirely when n_jobs == 1 via a small _parallel helper, used by fit, predict and fit_predict. Random draw order is preserved, so results are unchanged. All paths remain bit-identical to before (verified against a saved reference for fit/predict/predict_proba/fit_predict on both estimators, and n_jobs=1 equals n_jobs=4). Individually these are within measurement noise (~1-3%); the main value is removing the base_estimator-is-None coupling and joblib overhead. All 175 sklearn tests and doctests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uctors (#3630) Adds criterion, splitter, max_features, max_depth, max_leaf_nodes and min_samples_leaf to both RotationForestClassifier and RotationForestRegressor (and the shared base). Each defaults to the value previously hard-coded for the default tree, so behaviour is bit-identical unless a user sets them: - criterion: 'entropy' (classifier) / 'squared_error' (regressor) - splitter: 'best', max_features: None, max_depth: None, max_leaf_nodes: None, min_samples_leaf: 1 They are only used when base_estimator is None (a user-supplied estimator carries its own parameters). Tree construction is factored into a single _new_default_tree helper used by both the fit template and the per-estimator _make_tree path. These let users trade accuracy for speed without swapping in a full base_estimator, e.g. criterion='gini' (~1.5x fit), splitter='random' (~5x fit) or max_features='sqrt' (~7.5x fit) measured on 500x400/30 trees. Defaults verified bit-identical against the saved reference for both estimators; all 175 sklearn tests and doctests pass; get_params/clone round-trip the new parameters. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address recurring test-review points on the rotation forest tests:
- Fix docstrings that did not match the test: test_rotf_output claimed
'contracting and train estimate' but only checks predict output;
test_contracted_rotf claimed a train estimate it never computes.
- Derive the expected probability width from the number of classes in the
data instead of hard-coding 2.
- Replace exact error-string assertions with short, intention-revealing
substring matches ("not a time series", "same value").
- Drop the deprecated pca_solver argument from the output tests (its no-op
behaviour is covered by test_rotf_pca_solver_is_noop).
- Add test_rotf_tree_parameters to both estimators, checking the newly
exposed criterion/splitter/max_depth/min_samples_leaf reach the fitted
default trees and that the defaults are unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sion (#3630) The regressor output test pinned 15 exact continuous predictions, which vary with the scikit-learn version: 1.8 changed decision-tree handling of almost-constant features (already noted in the class docstring), shifting individual predictions by up to ~0.005 and failing the test on 1.8 while passing on 1.7. The aggregate error is stable across versions (MSE differs by ~1e-5), so test_rotf_output now asserts the test-set MSE instead of exact predictions, over the full test set (removing the magic 15-case slice). test_contracted_rotf pinned its MSE at 0.002 (decimal=4) while the observed value is 0.00213, leaving it one version bump from failing; re-centred on the observed value so the tolerance has room for cross-version drift. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
back to this draft properly. Priorities
|

Not dependent on #3631, but obviously STC sill be dependent on both that and this,
Speed up RST without changing outcome. May also redesign and change threading
Stage 1: _online_shapelet_distance scalar rewrite
Replace traverse, sums, and sums2 list state with scalar left/right state
compute the normalised value on demand, avoid unnecessary slicing. Move unchanging values out of loop
Stage 2: code restructure
I feel it could benefit from a redesign,. _process_fit_batch has too many responsibilities, fixed and contracted has duplication, _class_dictionary is populated but never read etc
Stage 3: optimisations
threading at the moment makes things worse. n_jobs=2/4 is slower than n_jobs=1 for both fit and transform, consistent
with per-candidate Joblib overhead and Numba entry points not being configured to release the GIL