1#ifndef FSTCLASSIFIERRF_H
2#define FSTCLASSIFIERRF_H
110template<
class RETURNTYPE,
typename DATATYPE,
typename REALTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
111class Classifier_Random_Forest :
public Clonable_Impl<Classifier_Random_Forest<RETURNTYPE,DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>,Classifier<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR> > {
143 virtual std::ostream&
print(std::ostream& os)
const override
173 std::vector<REALTYPE>
_X;
174 std::vector<DIMTYPE>
_y;
178 std::uint64_t
rng_next() {std::uint64_t &s=
_rng_state; s^=s>>12; s^=s<<25; s^=s>>27;
return s*2685821657736338717ULL;}
179 IDXTYPE
rng_below(
const IDXTYPE n) {assert(n>0);
return (IDXTYPE)(
rng_next()%(std::uint64_t)n);}
187 void grow_tree(std::vector<Node> &tree, std::vector<IDXTYPE> &samples,
const DIMTYPE mtry);
193template<
class RETURNTYPE,
typename DATATYPE,
typename REALTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
196 notify(
"Classifier_Random_Forest::initialize().");
198 assert(da->getNoOfFeatures()>0);
199 if(da->getNoOfClasses()<2)
throw fst_error(
"Classifier_Random_Forest requires at least 2 classes");
207template<
class RETURNTYPE,
typename DATATYPE,
typename REALTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
210 notify(
"Classifier_Random_Forest::train().");
213 assert(
_classes==da->getNoOfClasses());
214 assert(
_features==da->getNoOfFeatures());
215 assert(sub->get_n_raw()==da->getNoOfFeatures());
225 const DIMTYPE stride=da->getNoOfFeatures();
226 _X.clear();
_y.clear();
227 typename DATAACCESSOR::PPattern p;
232 for(
const auto &blk : da->blocks(
TRAIN))
for(i=0,p=blk.data,s=blk.size;i<s;i++)
234 const auto *pat=&p[(std::size_t)i*stride];
235 for(std::size_t f=0;f<d;f++)
_X.push_back((REALTYPE)pat[
_index[f]]);
239 const IDXTYPE N=(IDXTYPE)
_y.size();
240 if(N==0)
return false;
243 std::uint64_t h=14695981039346656037ULL;
244 const std::uint64_t prime=1099511628211ULL;
245 auto mix=[&h](std::uint64_t v) {
for(
int b=0;b<8;b++) {h^=(v&0xffu); h*=prime; v>>=8;}};
247 mix((std::uint64_t)da->getSplitIndex());
248 for(std::size_t f=0;f<d;f++) mix((std::uint64_t)
_index[f]);
249 if(h==0) h=0x9E3779B97F4A7C15ULL;
254 if(mtry==0) mtry=(DIMTYPE)std::floor(std::sqrt((
double)d));
256 if(mtry>(DIMTYPE)d) mtry=(DIMTYPE)d;
260 std::vector<IDXTYPE> samples(N);
263 for(IDXTYPE n=0;n<N;n++) samples[n]=
rng_below(N);
269template<
class RETURNTYPE,
typename DATATYPE,
typename REALTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
274 struct Work {
long node; IDXTYPE lo, hi;
unsigned int depth;};
275 std::vector<Work> stack;
276 std::vector<IDXTYPE> cnt(K), cntL(K);
277 std::vector<DIMTYPE> feats(d);
278 std::vector<IDXTYPE> sortbuf;
281 tree.push_back(
Node());
282 stack.push_back(Work{0,0,(IDXTYPE)samples.size(),0});
283 while(!stack.empty())
285 const Work w=stack.back(); stack.pop_back();
286 const IDXTYPE n=w.hi-w.lo;
290 std::fill(cnt.begin(),cnt.end(),(IDXTYPE)0);
291 for(IDXTYPE ii=w.lo;ii<w.hi;ii++) cnt[
_y[samples[ii]]]++;
293 for(DIMTYPE c=1;c<K;c++)
if(cnt[c]>cnt[major]) major=c;
294 const bool pure=(cnt[major]==n);
299 DIMTYPE best_f=0; REALTYPE best_thr=0.0, best_gain=(REALTYPE)-1.0;
303 for(std::size_t f=0;f<d;f++) feats[f]=(DIMTYPE)f;
304 for(DIMTYPE k=0;k<mtry;k++) {
const IDXTYPE j=k+
rng_below((IDXTYPE)(d-k)); std::swap(feats[k],feats[j]);}
306 for(DIMTYPE k=0;k<mtry;k++)
308 const DIMTYPE f=feats[k];
309 sortbuf.assign(samples.begin()+w.lo,samples.begin()+w.hi);
311 std::sort(sortbuf.begin(),sortbuf.end(),[
this,d,f](
const IDXTYPE a,
const IDXTYPE b)
312 {const REALTYPE va=_X[(std::size_t)a*d+f], vb=_X[(std::size_t)b*d+f]; return va<vb || (va==vb && a<b);});
317 std::fill(cntL.begin(),cntL.end(),(IDXTYPE)0);
318 for(IDXTYPE ii=0;ii+1<n;ii++)
320 cntL[
_y[sortbuf[ii]]]++;
321 const REALTYPE v0=
_X[(std::size_t)sortbuf[ii]*d+f], v1=
_X[(std::size_t)sortbuf[ii+1]*d+f];
322 if(!(v0<v1))
continue;
323 const IDXTYPE nL=ii+1, nR=n-nL;
325 REALTYPE sL=0.0, sR=0.0;
326 for(DIMTYPE c=0;c<K;c++) {
327 const REALTYPE l=(REALTYPE)cntL[c], r=(REALTYPE)(cnt[c]-cntL[c]);
330 const REALTYPE gain=sL/(REALTYPE)nL+sR/(REALTYPE)nR;
332 REALTYPE thr=(v0+v1)/(REALTYPE)2.0;
336 if(!(thr<v1)) thr=v0;
337 best_gain=gain; best_f=f; best_thr=thr;
341 if(best_gain<(REALTYPE)0.0) make_leaf=
true;
346 tree[w.node].left=-1; tree[w.node].right=-1;
347 tree[w.node].feature=0; tree[w.node].threshold=0.0;
348 tree[w.node].leaf_class=major;
353 auto mid=std::stable_partition(samples.begin()+w.lo,samples.begin()+w.hi,
354 [
this,d,best_f,best_thr](
const IDXTYPE a){return _X[(std::size_t)a*d+best_f]<=best_thr;});
355 const IDXTYPE cut=(IDXTYPE)(mid-samples.begin());
356 assert(cut>w.lo && cut<w.hi);
360 const long left=(long)tree.size(); tree.push_back(
Node());
361 const long right=(long)tree.size(); tree.push_back(
Node());
362 tree[w.node].feature=best_f; tree[w.node].threshold=best_thr;
363 tree[w.node].left=left; tree[w.node].right=right; tree[w.node].leaf_class=0;
364 stack.push_back(Work{right,cut,w.hi,w.depth+1});
365 stack.push_back(Work{left,w.lo,cut,w.depth+1});
369template<
class RETURNTYPE,
typename DATATYPE,
typename REALTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
373 while(tree[node].left>=0)
375 const Node &nd=tree[node];
378 return tree[node].leaf_class;
381template<
class RETURNTYPE,
typename DATATYPE,
typename REALTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
392template<
class RETURNTYPE,
typename DATATYPE,
typename REALTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
396 scores.assign(
_classes,(RETURNTYPE)0.0);
Dependency-free Random Forest classifier (CART trees, Gini impurity, bagging, per-node feature subsam...
Definition classifier_rf.hpp:111
unsigned int _max_depth
tree depth cap; 0 = unlimited; set by set_max_depth()
Definition classifier_rf.hpp:160
virtual bool classify(DIMTYPE &cls, const PPattern &pattern) override
Definition classifier_rf.hpp:382
void initialize(const PDataAccessor da)
records data shape, allocates the index buffer
Definition classifier_rf.hpp:194
std::vector< IDXTYPE > _votes
classify() vote scratch (member to avoid per-pattern allocation)
Definition classifier_rf.hpp:169
IDXTYPE rng_below(const IDXTYPE n)
a value in [0,n) via modulo of rng_next(). The modulo bias is of order n/2^64 - unmeasurable for any ...
Definition classifier_rf.hpp:179
void set_seed_salt(const unsigned int s)
salts the subset-content hash the training RNG is seeded from; default 1 (the same value the unseeded...
Definition classifier_rf.hpp:126
void grow_tree(std::vector< Node > &tree, std::vector< IDXTYPE > &samples, const DIMTYPE mtry)
Grows one CART tree (Gini-impurity best split, mtry random candidate features per node) into tree,...
Definition classifier_rf.hpp:270
std::uint64_t _rng_state
xorshift64* generator state; seeded per train() call (see train())
Definition classifier_rf.hpp:177
void set_mtry(const DIMTYPE m)
features tried per node; 0 = floor(sqrt(subset size))
Definition classifier_rf.hpp:123
IDXTYPE _min_leaf
minimum samples in each child after a split; set by set_min_leaf()
Definition classifier_rf.hpp:161
std::uint64_t rng_next()
advances the xorshift64* generator, returns its next 64-bit output
Definition classifier_rf.hpp:178
void set_tree_count(const unsigned int t)
number of trees to grow (must be >0); default 100
Definition classifier_rf.hpp:122
unsigned int _seed_salt
salts the per-evaluation subset-content hash; set by set_seed_salt(), default 1
Definition classifier_rf.hpp:162
DIMTYPE _mtry
features tried per split node; 0 = auto (floor(sqrt(subset size))); set by set_mtry()
Definition classifier_rf.hpp:159
DIMTYPE _subfeatures
d = current subset size
Definition classifier_rf.hpp:167
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the object; base default writes nothing
Definition classifier_rf.hpp:143
unsigned int _tree_count
number of trees to grow; set by set_tree_count()
Definition classifier_rf.hpp:158
std::vector< REALTYPE > _X
N x d row-major selected training values.
Definition classifier_rf.hpp:173
void set_min_leaf(const IDXTYPE ml)
minimum samples in each child after a split (must be >0); default 1
Definition classifier_rf.hpp:125
DIMTYPE tree_vote(const std::vector< Node > &tree, const PPattern &pattern) const
Walks tree from its root to a leaf for pattern (via _index to map each node's subset-position feature...
Definition classifier_rf.hpp:370
std::vector< DIMTYPE > _index
subset position -> raw feature index (first d entries valid)
Definition classifier_rf.hpp:168
std::vector< DIMTYPE > _y
N training labels.
Definition classifier_rf.hpp:174
DIMTYPE _classes
number of classes, as recorded by initialize()
Definition classifier_rf.hpp:165
DIMTYPE _features
raw (full) feature count, as recorded by initialize()
Definition classifier_rf.hpp:166
Classifier< RETURNTYPE, DIMTYPE, SUBSET, DATAACCESSOR > parent
the abstract base type
Definition classifier_rf.hpp:113
virtual bool classify_scores(std::vector< RETURNTYPE > &scores, const PPattern &pattern) override
Definition classifier_rf.hpp:393
virtual bool train(const PDataAccessor da, const PSubset sub) override
Grows the forest on the designated training part of da, restricted to sub's selected features....
Definition classifier_rf.hpp:208
Clonable_Impl< Classifier_Random_Forest, parent > parent_impl
implements clone() via the (implicit, member-wise) copy-constructor - all members are value-semantic
Definition classifier_rf.hpp:114
DATAACCESSOR::PPattern PPattern
one data sample as exposed by DATAACCESSOR (a raw pointer to its feature vector)
Definition classifier_rf.hpp:117
std::shared_ptr< DATAACCESSOR > PDataAccessor
shared pointer to the data accessor supplying train/test blocks
Definition classifier_rf.hpp:115
std::vector< std::vector< Node > > _forest
the trained forest; outer index = tree, inner = that tree's node vector (root at index 0)
Definition classifier_rf.hpp:170
void set_max_depth(const unsigned int dp)
0 = unlimited
Definition classifier_rf.hpp:124
std::shared_ptr< SUBSET > PSubset
shared pointer to the feature subset being trained/classified on
Definition classifier_rf.hpp:116
abstract class, defines interface for classifier implementations (mainly to be used in wrappers)
Definition classifier.hpp:97
CRTP mixin implementing the Clonable protocol for a concrete class.
Definition clonable.hpp:184
FST4 specific error passing class.
Definition error.hpp:61
Defines interface for classifier implementations.
Defines interface for classes that may need to get cloned.
FST4 specific error passing class.
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
void notify(const std::string msg)
Prints msg followed by a newline via syncout::print(); a no-op unless the preprocessor macro DEBUG is...
Definition global.hpp:112
@ TRAIN
the split's training part
Definition data_accessor.hpp:85
Definition classifier_rf.hpp:149
DIMTYPE leaf_class
predicted class (meaningful only when left<0, i.e. at a leaf)
Definition classifier_rf.hpp:154
long left
left-child index into the tree's node vector; <0 marks this node a leaf
Definition classifier_rf.hpp:152
REALTYPE threshold
split threshold; a pattern routes to left when its value is <=threshold
Definition classifier_rf.hpp:151
DIMTYPE feature
split feature, given as a subset position (see _index for the raw feature index)
Definition classifier_rf.hpp:150
long right
right-child index into the tree's node vector (meaningful only when left>=0)
Definition classifier_rf.hpp:153