Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
data_accessor_splitting_mem.hpp
Go to the documentation of this file.
1#ifndef FSTDATAACCESSORSPLITTINGMEM_H
2#define FSTDATAACCESSORSPLITTINGMEM_H
3 /*
27=========================================================================
28Copyright and license:
29 * FST4 software (with the exception of any bundled or externally linked
30 third-party components) is Copyright (C) 2000-present Institute of
31 Information Theory and Automation of the Czech Academy of Sciences (UTIA).
32 * FST4 is free software: you can redistribute it and/or modify it under
33 the terms of the GNU Affero General Public License as published by the
34 Free Software Foundation, version 3. The full text of the license is in
35 the file LICENSE in the distribution root and at
36 https://www.gnu.org/licenses/agpl-3.0.html.
37 * FST4 is distributed in the hope that it will be useful, but WITHOUT ANY
38 WARRANTY; without even the implied warranty of MERCHANTABILITY or
39 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
40 License for more details.
41 * Closed-source use without the AGPL's copyleft obligations requires a
42 commercial license from UTIA; in case of interest write to
43 utia@utia.cas.cz. LICENSING.md in the distribution root explains both
44 options, the contributor license agreement that every contribution
45 requires, and the contacts (technical questions and pull requests:
46 somol@utia.cas.cz, with "FST" in the subject line).
47========================================================================== */
48
49#include <memory>
50#include <cassert>
51#include <iostream>
52#include <sstream>
53#include <vector>
54
55#include "error.hpp"
56#include "global.hpp"
58#include "data_scaler.hpp"
59
60/*============== Template parameter type naming conventions ==============
61--------- Numeric types: -------------------------------------------------
62DATATYPE - data sample values - usually real numbers (but may be integers
63 in text processing etc.)
64REALTYPE - must be real numbers - for representing intermediate results of
65 calculations like mean, covariance etc.
66IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
67 extent depends on numbers of samples in data
68DIMTYPE - index values for enumeration of features (dimensions), or classes (not
69 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
70 but be aware of expressions like _classes*_features*_features !
71 in linearized representations of feature matrices for all classes
72BINTYPE - feature selection marker type - represents ca. <10 different feature
73 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
74RETURNTYPE - criterion value: real value, but may be extended in future to support
75 multiple values
76--------- Class types: ---------------------------------------------------
77SUBSET - class of class type Subset
78CLASSIFIER - class implementing interface defined in abstract class Classifier
79EVALUATOR - class implementing interface defined in abstract class Sequential_Step
80DISTANCE - class implementing interface defined in abstract class Distance
81DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
82INTERVALCONTAINER - class of class type Data_Intervaller
83CONTAINER - STL container of class type Data_Interval
84========================================================================== */
85
86namespace FST {
87
100template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
101class Data_Accessor_Splitting_Mem : public Data_Accessor_Splitting<DATATYPE,IDXTYPE,INTERVALCONTAINER>
102{
103 // NOTE: the calling convention is the same for the inner and the outer CV loop -- both call just
104 // get*Split, get*Block, get*Pattern -- and which of them a call refers to is selected beforehand
105 // with the inherited setSplittingDepth()
106 public:
108 typedef std::shared_ptr<Data_Scaler<DATATYPE> > PScaler;
109 typedef typename DAS::PPattern PPattern;
110 typedef typename DAS::PSplitters PSplitters;
113 Data_Accessor_Splitting_Mem(const std::string _filename, const PSplitters _dsp, const PScaler _dsc) : Data_Accessor_Splitting<DATATYPE,IDXTYPE,INTERVALCONTAINER>(_dsp), dsc(_dsc), filename(_filename), datasize(0), scaler_fit_train(false) {notify("Data_Accessor_Splitting_Mem constructor.");}
114 virtual ~Data_Accessor_Splitting_Mem(){notify("Data_Accessor_Splitting_Mem destructor.");}
115
125 virtual void initialize();
126
139
149 void scale_sample_inplace(DATATYPE *sample) const;
150
151 virtual bool getFirstSplit() override;
152 virtual bool getNextSplit() override;
153
154 virtual bool getFirstBlock(const DataPart ofwhat, PPattern &firstpattern, IDXTYPE &patterns, const unsigned int loopdepth=0) override;
155 virtual bool getNextBlock(const DataPart ofwhat, PPattern &firstpattern, IDXTYPE &patterns, const unsigned int loopdepth=0) override;
156
157 Data_Accessor_Splitting_Mem* clone() const override {throw fst_error("Data_Accessor_Splitting_Mem::clone() not supported, use Data_Accessor_Splitting_Mem::sharing_clone() instead.");}
159 Data_Accessor_Splitting_Mem* stateless_clone() const override {throw fst_error("Data_Accessor_Splitting_Mem::stateless_clone() not supported, use Data_Accessor_Splitting_Mem::sharing_clone() instead.");}
160
161 virtual std::ostream& print(std::ostream& os) const override;
162 protected:
165 protected:
166 // Contract for a derived (file-format) class. initialize() calls these in this order:
167 // initial_file_prepare(), then the three file_get*() queries, then initial_data_read().
168 // A hook that cannot do its job must throw fst_error; returning silently would leave the
169 // accessor half-built. None of them may be reached on a sharing_clone.
174 virtual void initial_data_read() = 0;
177 virtual void initial_file_prepare() = 0;
178 virtual unsigned int file_getNoOfClasses() const = 0;
179 virtual unsigned int file_getNoOfFeatures() const = 0;
180 virtual IDXTYPE file_getClassSize(unsigned int cls) const = 0;
181
183
186
194 std::vector<PScaler> feature_scalers;
195
196 std::string filename;
197
198 IDXTYPE datasize;
199 std::shared_ptr<DATATYPE> data;
201 std::shared_ptr<DATATYPE> data_raw;
205 std::vector<IDXTYPE> _class_offset;
206};
207
208// learns the scaler transform over the whole data set, per feature, and applies it in place;
209// off-limits in shared_clone (it mutates the shared data buffer)
210template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
212{
213 if(Clonable::is_sharing_clone()) throw fst_error("Data_Accessor_Splitting_Mem()::initial_data_scaling() called from shared_clone instance.");
214 // SCALING over values per-feature, regardless classes
215 assert(dsc);
216 {std::ostringstream sos; sos << std::endl << "Scaling data (per-feature, over all classes) using " << *dsc << "..."; syncout::print(std::cout,sos);}
217 IDXTYPE idx=0;
218 IDXTYPE _features=file_getNoOfFeatures();
219 feature_scalers.clear(); feature_scalers.reserve(_features);
220 bool keep_transforms=true;
221 for(IDXTYPE f=0;f<_features;f++) {
222 for(bool b=dsc->startFirstLoop(); b==true; b=dsc->startNextLoop())
223 {
224 idx=f;
225 for(unsigned int c=0;c<file_getNoOfClasses();c++) for(IDXTYPE p=0;p<file_getClassSize(c);p++)
226 {
227 dsc->learn(data.get()[idx]);
228 idx+=_features;
229 }
230 }
231 idx=f;
232 for(unsigned int c=0;c<file_getNoOfClasses();c++) for(IDXTYPE p=0;p<file_getClassSize(c);p++)
233 {
234 dsc->scale_inplace(data.get()[idx]);
235 idx+=_features;
236 }
237 if(keep_transforms) { // keep this feature's learned transform for scale_sample_inplace()
238 try {feature_scalers.push_back(PScaler(dsc->duplicate()));}
239 catch(fst_error&) {keep_transforms=false; feature_scalers.clear();} // a scaler without duplicate(): user vectors cannot be transformed, the data itself is unaffected
240 }
241 }
242 {std::ostringstream sos; sos << "done." << std::endl << std::endl; syncout::print(std::cout,sos);}
243}
244
245template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
247{
248 // scaler_fit_train mode: learn the transform on the current depth-0 TRAIN part only,
249 // then re-derive the whole working buffer from the pristine data (leakage-free scaling;
250 // deliberately silent — this runs on every depth-0 split change).
251 // NOTE: fitting reads the physical splits[0][*]._train intervals (absolute at depth 0),
252 // so DataPart substitution (substitute()) does not affect what the scaler learns on
253 assert(scaler_fit_train);
254 assert(dsc);
255 assert(data && data_raw);
256 const unsigned int _features=DAS::features;
257 const unsigned int _classes=(unsigned int)DAS::classes.size();
258 feature_scalers.clear(); feature_scalers.reserve(_features);
259 bool keep_transforms=true;
260 for(unsigned int f=0;f<_features;f++)
261 {
262 for(bool b=dsc->startFirstLoop(); b==true; b=dsc->startNextLoop())
263 {
264 for(unsigned int c=0;c<_classes;c++)
265 {
266 const INTERVALCONTAINER &train=*(DAS::splits[0][c]._train);
267 for(typename INTERVALCONTAINER::const_iterator iter=train.begin();iter!=train.end();++iter)
268 {
269 IDXTYPE idx=_class_offset[c]+((IDXTYPE)iter->startidx)*_features+f;
270 for(IDXTYPE p=0;p<(IDXTYPE)iter->count;p++,idx+=_features) dsc->learn(data_raw.get()[idx]);
271 }
272 }
273 }
274 // apply to ALL patterns (train and test parts alike), always from the pristine values
275 IDXTYPE idx=f;
276 for(unsigned int c=0;c<_classes;c++) for(IDXTYPE p=0;p<(IDXTYPE)DAS::classes[c];p++,idx+=_features)
277 data.get()[idx]=dsc->scale(data_raw.get()[idx]);
278 if(keep_transforms) { // this split's transform of feature f, for scale_sample_inplace()
279 try {feature_scalers.push_back(PScaler(dsc->duplicate()));}
280 catch(fst_error&) {keep_transforms=false; feature_scalers.clear();}
281 }
282 }
283}
284
285template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
287{
288 assert(sample);
289 if(feature_scalers.size()!=(std::size_t)DAS::features)
290 throw fst_error("Data_Accessor_Splitting_Mem::scale_sample_inplace(): no per-feature scaler transform available (initialize() not called yet, or the scaler does not implement duplicate())");
291 for(std::size_t f=0;f<feature_scalers.size();f++) feature_scalers[f]->scale_inplace(sample[f]);
292}
293
294template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
301
302template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
309
310template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
312{
313 // NOTE: throws I/O and memory related exceptions; off-limits in shared_clone
314 if(Clonable::is_sharing_clone()) throw fst_error("Data_Accessor_Splitting_Mem()::initialize() called from shared_clone instance.");
315
316 assert(DAS::dsp->size()>0);
317 try {
318 {std::ostringstream sos; sos << "Opening file: " << filename << std::endl; syncout::print(std::cout,sos);}
320 {
321 std::ostringstream sos;
322 sos << "Features: " << file_getNoOfFeatures() << std::endl << "Classes: " << file_getNoOfClasses() << std::endl;
323 syncout::print(std::cout,sos);
324 }
325
326 typename DAS::CLASSSIZES csizes;
327 for(unsigned int c=0;c<file_getNoOfClasses();c++)
328 {
329 {std::ostringstream sos; sos << " Class "<<c<<" size: "<<file_getClassSize(c) << std::endl; syncout::print(std::cout,sos);} // 0-based, like every class index FST4 prints or accepts (--f1-positive, --class-weights, confusion rows) and like the feature indices
330 csizes.push_back(file_getClassSize(c));
331 }
332 {
333 std::ostringstream sos;
334 sos << "Splitting depth: " << DAS::dsp->size() << std::endl;
335 syncout::print(std::cout,sos);
336 }
337
338 DAS::initialize(file_getNoOfFeatures(), csizes); // set-up memory structures to support data splitting
339
340 datasize=0;
341 _class_offset.clear();
342 for(unsigned int c=0;c<file_getNoOfClasses();c++) {
343 _class_offset.push_back(datasize); // to be set only once
345 }
346
347 data.reset(new DATATYPE[datasize], std::default_delete<DATATYPE[]>());
350 else {
351 // per-split scaling: keep the loaded data pristine (shared, read-only), work on a private
352 // copy; the transform is learned per depth-0 split in per_split_data_scaling() — before the
353 // first split is made no transform is defined, so pre-split access sees unscaled data
354 assert(dsc);
356 data.reset(new DATATYPE[datasize], std::default_delete<DATATYPE[]>());
357 std::copy(data_raw.get(),data_raw.get()+datasize,data.get());
358 {std::ostringstream sos; sos << std::endl << "Per-split scaling enabled (fit on each depth-0 TRAIN part) using " << *dsc << "." << std::endl; syncout::print(std::cout,sos);}
359 }
360
361 } catch (...) {
362 notify("Data_Accessor_Splitting_Mem::initialize() failed.");
363 throw;
364 }
365}
366
367template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
368bool Data_Accessor_Splitting_Mem<DATATYPE,IDXTYPE,INTERVALCONTAINER>::getFirstBlock(const DataPart ofwhat, PPattern &firstpattern, IDXTYPE &patterns, const unsigned int loopdepth)
369{
370 assert(data);
371 typename DAS::DATAINTERVAL tmp;
372 if(!DAS::getFirstBlock(ofwhat,tmp,loopdepth)) return false;
373 firstpattern=&data.get()[_class_offset[DAS::active_class]+tmp->startidx*DAS::features];
374 patterns=tmp->count;
375 return true;
376}
377
378template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
379bool Data_Accessor_Splitting_Mem<DATATYPE,IDXTYPE,INTERVALCONTAINER>::getNextBlock(const DataPart ofwhat, PPattern &firstpattern, IDXTYPE &patterns, const unsigned int loopdepth)
380{
381 assert(data);
382 typename DAS::DATAINTERVAL tmp;
383 if(!DAS::getNextBlock(ofwhat,tmp,loopdepth)) return false;
384 firstpattern=&data.get()[_class_offset[DAS::active_class]+tmp->startidx*DAS::features];
385 patterns=tmp->count;
386 return true;
387}
388
389template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
392 datasize(damt.datasize),
393 data(damt.data),
394 data_raw(damt.data_raw),
397{
398 if(scaler_fit_train) {
399 // per-split scaling: the clone re-learns the transform whenever it advances depth-0 splits,
400 // so it needs a private working buffer (initialized to the parent's current state — correct
401 // for any access that does not advance splits) and a private scaler instance
402 if(!damt.dsc) throw fst_error("Data_Accessor_Splitting_Mem weak cc: scaler_fit_train mode requires a scaler instance in the source.");
403 data.reset(new DATATYPE[datasize], std::default_delete<DATATYPE[]>());
404 std::copy(damt.data.get(),damt.data.get()+datasize,data.get());
405 dsc.reset(damt.dsc->duplicate());
406 } else dsc.reset();
407 filename.clear();
408 notify("Data_Accessor_Splitting_Mem weak copy-constructor.");
409}
410
411template<typename DATATYPE, typename IDXTYPE, class INTERVALCONTAINER>
413{
414 DAS::print(os);
415 os << std::endl << "Data_Accessor_Splitting_Mem(file=" << filename << ")";
416 return os;
417}
418
419} // namespace
420#endif // FSTDATAACCESSORSPLITTINGMEM_H ///:~
bool is_sharing_clone() const
true if this instance is BOTH a clone and sharing – i.e. specifically a sharing_clone() result,...
Definition clonable.hpp:127
Implements data access to data cached entirely in memory, concrete file type support is delegated to ...
Definition data_accessor_splitting_mem.hpp:102
virtual unsigned int file_getNoOfFeatures() const =0
data dimensionality as reported by the underlying file (valid only after initial_file_prepare())
Data_Accessor_Splitting_Mem * clone() const override
Definition data_accessor_splitting_mem.hpp:157
Data_Accessor_Splitting_Mem * stateless_clone() const override
Definition data_accessor_splitting_mem.hpp:159
Data_Accessor_Splitting_Mem(const Data_Accessor_Splitting_Mem &damt, int)
weak (referencing) copy-constructor to be used in sharing_clone()
Definition data_accessor_splitting_mem.hpp:390
bool scaler_fit_train
Definition data_accessor_splitting_mem.hpp:203
IDXTYPE datasize
Definition data_accessor_splitting_mem.hpp:198
virtual bool getFirstBlock(const DataPart ofwhat, PPattern &firstpattern, IDXTYPE &patterns, const unsigned int loopdepth=0) override
returns pointer to first consecutive block of data of requested DataPart type in the current split (a...
Definition data_accessor_splitting_mem.hpp:368
virtual unsigned int file_getNoOfClasses() const =0
number of classes as reported by the underlying file (valid only after initial_file_prepare())
Data_Accessor_Splitting_Mem * sharing_clone() const override=0
Data_Accessor_Splitting_Mem(const std::string _filename, const PSplitters _dsp, const PScaler _dsc)
Definition data_accessor_splitting_mem.hpp:113
DAS::PSplitters PSplitters
Definition data_accessor_splitting_mem.hpp:110
bool get_scaler_fit_train() const
see set_scaler_fit_train()
Definition data_accessor_splitting_mem.hpp:138
std::shared_ptr< DATATYPE > data_raw
Definition data_accessor_splitting_mem.hpp:201
DAS::PPattern PPattern
Definition data_accessor_splitting_mem.hpp:109
virtual std::ostream & print(std::ostream &os) const override
writes the splitter stack's print() output (or "Data_Accessor_Splitting()" if the stack is empty) to ...
Definition data_accessor_splitting_mem.hpp:412
PScaler dsc
Definition data_accessor_splitting_mem.hpp:187
void set_scaler_fit_train(const bool b)
Switches on per-split scaling: the scaler learns its transform on the current depth-0 split's TRAIN p...
Definition data_accessor_splitting_mem.hpp:137
void initial_data_scaling()
Definition data_accessor_splitting_mem.hpp:211
virtual void initial_file_prepare()=0
opens/prepares the underlying file for reading; called once by initialize() before initial_data_read(...
virtual void initialize()
to be called after object creation (for initialization of data file access etc.)
Definition data_accessor_splitting_mem.hpp:311
virtual bool getNextBlock(const DataPart ofwhat, PPattern &firstpattern, IDXTYPE &patterns, const unsigned int loopdepth=0) override
returns pointer to next consecutive block of data of requested DataPart type in the current split (ac...
Definition data_accessor_splitting_mem.hpp:379
void scale_sample_inplace(DATATYPE *sample) const
Applies the learned per-feature transform to sample, a caller-owned vector of getNoOfFeatures() value...
Definition data_accessor_splitting_mem.hpp:286
virtual bool getNextSplit() override
data access iteration (to support, e.g., loops in cross-validation)
Definition data_accessor_splitting_mem.hpp:303
std::shared_ptr< DATATYPE > data
Definition data_accessor_splitting_mem.hpp:199
virtual void initial_data_read()=0
fills the already-allocated data array with the file's samples; called once by initialize() after the...
std::string filename
Definition data_accessor_splitting_mem.hpp:196
virtual bool getFirstSplit() override
data access iteration (to support, e.g., loops in cross-validation)
Definition data_accessor_splitting_mem.hpp:295
virtual IDXTYPE file_getClassSize(unsigned int cls) const =0
number of samples of class cls as reported by the underlying file (valid only after initial_file_prep...
Data_Accessor_Splitting< DATATYPE, IDXTYPE, INTERVALLER > DAS
Definition data_accessor_splitting_mem.hpp:107
void per_split_data_scaling()
re-learns the scaler transform on the current depth-0 TRAIN part and re-scales the whole working buff...
Definition data_accessor_splitting_mem.hpp:246
std::shared_ptr< Data_Scaler< DATATYPE > > PScaler
Definition data_accessor_splitting_mem.hpp:108
std::vector< IDXTYPE > _class_offset
Definition data_accessor_splitting_mem.hpp:205
std::vector< PScaler > feature_scalers
Definition data_accessor_splitting_mem.hpp:194
PSplitters dsp
the splitter stack this accessor was constructed with, one Data_Splitter per nested splitting depth
Definition data_accessor_splitting.hpp:219
std::shared_ptr< std::vector< PSplitter > > PSplitters
shared pointer to the ordered list of splitters, one per nested splitting depth (depth 0 = outermost)
Definition data_accessor_splitting.hpp:154
std::vector< std::vector< DataSplit > > splits
one set of splitters per each splitting depth and class
Definition data_accessor_splitting.hpp:240
std::vector< unsigned int > CLASSSIZES
per-class pattern counts, indexed by class index; the format initialize() takes
Definition data_accessor_splitting.hpp:203
virtual bool getNextSplit() override
data access iteration (to support, e.g., loops in cross-validation)
Definition data_accessor_splitting.hpp:364
virtual std::ostream & print(std::ostream &os) const override
writes the splitter stack's print() output (or "Data_Accessor_Splitting()" if the stack is empty) to ...
Definition data_accessor_splitting.hpp:661
virtual bool getFirstSplit() override
data access iteration (to support, e.g., loops in cross-validation)
Definition data_accessor_splitting.hpp:330
const Data_Interval< IDXTYPE > * DATAINTERVAL
Definition data_accessor_splitting.hpp:220
virtual bool getFirstBlock(const DataPart ofwhat, PPattern &firstpattern, IDXTYPE &patterns, const unsigned int loopdepth=0) override=0
returns pointer to first consecutive block of data of requested DataPart type in the current split (a...
const DATATYPE * PPattern
Definition data_accessor_splitting.hpp:155
int active_class
denotes from which class the get*Train* get*Validate* get*Test* methods return patterns,...
Definition data_accessor_splitting.hpp:248
Data_Accessor_Splitting(const PSplitters _dsp)
Constructs an (uninitialized) accessor bound to the splitter stack _dsp; concrete subclasses must sti...
Definition data_accessor_splitting.hpp:262
virtual bool getNextBlock(const DataPart ofwhat, PPattern &firstpattern, IDXTYPE &patterns, const unsigned int loopdepth=0) override=0
returns pointer to next consecutive block of data of requested DataPart type in the current split (ac...
unsigned int features
data dimensionality, as passed to initialize()
Definition data_accessor_splitting.hpp:209
unsigned int getSplittingDepth() const
the nested splitting level last set via setSplittingDepth()
Definition data_accessor_splitting.hpp:172
CLASSSIZES classes
pattern count per class, as passed to initialize()
Definition data_accessor_splitting.hpp:208
void initialize(const unsigned int _features, const CLASSSIZES &_classes)
Definition data_accessor_splitting.hpp:503
FST4 specific error passing class.
Definition error.hpp:61
static void print(std::ostream &os, std::ostringstream &sos)
Writes sos's buffered content to os, serializing concurrent writers under FST_THREADED via a shared m...
Definition global.hpp:86
Defines support for data splitting.
Defines interface for data scaling implementations (for use in data accessors).
FST4 specific error passing class.
Global definitions.
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
DataPart
Selects which part of a split's data a block-access call reads.
Definition data_accessor.hpp:83
IDXTYPE startidx
index of the interval's first sample
Definition data_intervaller.hpp:89
IDXTYPE count
number of samples in the interval
Definition data_intervaller.hpp:90
Copyright © 2000–present ÚTIA, Institute of Information Theory and Automation of the Czech Academy of Sciences · Contacts · This site sets no cookies and uses no analytics.