Repository navigation
Faster rule extraction - #24
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR increases the speed of extracting rules. The main cause was that
extract_rules.party()looked up each leaf's parent by looping over every node in the tree, which is quadratic or worse per tree. Several other methods had the same per-leaf*_find_parentpattern. Most users extract rules from every tree in an ensemble, so the work was benchmarked on that case.Changes
Speedups
extract_rules.party()now does one top-down walk per tree that carries the split expressions down and emits a rule at each leaf. ranger, randomForest and grf also skipped a row-by-row computation of fitted node ids (nrow × depth per tree) by passing a zero-row copy of the data.getTrees()call or JSON export now covers all requested trees. Previously it was repeated per tree, which made the total quadratic. Each tree is then converted with a top-down walk.ObliqueForest): the slow R6 accessors (get_means(),get_stdev(),get_fctr_info(),get_names_x()) are cached once, and each tree is a top-down walk.Output is
identical()to the old code wherever the old code could produce output.Timings
The ranger and randomForest "before" and "after" rows are not the same workload, so they understate the improvement.
Test suite timings
test-cforest.Rtest-party.RBoth files were slow because of model fitting, not rule extraction.
partykit::cforest()andctree()were fit onwa_trees, wherecountyhas 30 levels (one 3-tree cforest fit took about 170 s, while extraction took 0.01–0.03 s). A new test helper,get_wa_trees_small_data(), keeps 200 rows and the 3 most common counties and labels the restother. A 3-tree fit now takes 0.02 s. Theskip()and the randomskip_if(runif(1) <= 0.1)are removed, andtests/testthat/_snaps/cforest.mdis new because those snapshots were never recorded.API and behavior changes
treeaccepts a vector for ObliqueForest, bart, catboost, lightgbm and xgboost, matching the other ensemble methods.check_tree_type()andcheck_tree_range()are used by every method with atreeargument (including C5.0 trials, Cubist committees, cforest andactive_predictors.cforest()). The range error now states the upper limit, for example "treemust be between 1 and 3, the number of trees in the model."num_ensemble_members()is the single source for the number of trees. Internallgb_num_trees(),xgb_num_trees()andbart_num_trees()take an already-read dump so the model is not dumped a second time.rlang::check_dots_empty()is now called first in everyextract_rules()method, so typos such astrees = 2error instead of being silently ignored. As a resultdatais no longer accepted byextract_rules.C5.0().num_ensemble_members.grf()aborts clearly when the number of trees is missing, and matrices without column names no longer error.Tests
idequalspredict(type = "node").check_tree_type()andcheck_tree_range().tree = c(1, 2)error snapshots were removed because that call is now valid.data =was removed from the C5.0 tests.