PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // KFoldSplitter.cc 00004 // 00005 // Copyright (C) 1998 Pascal Vincent 00006 // Copyright (C) 1999,2000 Pascal Vincent, Yoshua Bengio and University of Montreal 00007 // Copyright (C) 2002 Frederic Morin 00008 // Copyright (C) 2008 Jerome Louradour 00009 // 00010 // Redistribution and use in source and binary forms, with or without 00011 // modification, are permitted provided that the following conditions are met: 00012 // 00013 // 1. Redistributions of source code must retain the above copyright 00014 // notice, this list of conditions and the following disclaimer. 00015 // 00016 // 2. Redistributions in binary form must reproduce the above copyright 00017 // notice, this list of conditions and the following disclaimer in the 00018 // documentation and/or other materials provided with the distribution. 00019 // 00020 // 3. The name of the authors may not be used to endorse or promote 00021 // products derived from this software without specific prior written 00022 // permission. 00023 // 00024 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 00025 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00026 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 00027 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00028 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00029 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00030 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00031 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00032 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00033 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00034 // 00035 // This file is part of the PLearn library. For more information on the PLearn 00036 // library, go to the PLearn Web site at www.plearn.org 00037 00038 /* ******************************************************* 00039 * $Id: KFoldSplitter.cc 9229 2008-07-10 18:55:38Z saintmlx $ 00040 ******************************************************* */ 00041 00044 #include "KFoldSplitter.h" 00045 #include "VMat_basic_stats.h" 00046 #include <plearn/vmat/ConcatRowsVMatrix.h> 00047 #include <plearn/vmat/SubVMatrix.h> 00048 #include <plearn/vmat/ClassSubsetVMatrix.h> 00049 00050 namespace PLearn { 00051 using namespace std; 00052 00054 // KFoldSplitter // 00056 KFoldSplitter::KFoldSplitter(int k) 00057 : append_non_constant_test(false), 00058 append_train(false), 00059 include_test_in_train(false), 00060 balance_classes(false), 00061 K(k) 00062 { 00063 // Default cross-validation range is the whole dataset. 00064 cross_range.first = 0; 00065 cross_range.second = 1; 00066 } 00067 00068 PLEARN_IMPLEMENT_OBJECT(KFoldSplitter, 00069 "K-fold cross-validation splitter.", 00070 "KFoldSplitter implements K splits of the dataset into a training-set and a test-set.\n" 00071 "To perform leave-one-out cross-validation, K must be set to -1 (or, obviously, to the\n" 00072 "exact number of examples).\n" 00073 "The cross-validation may be performed only on a subset of the source data, using the option\n" 00074 "'cross_range', that will define a range of samples on which to perform cross-validation.\n" 00075 "All samples before this range will systematically be added to the train set, while all samples\n" 00076 "after this range will be added to the test set.\n" 00077 ); 00078 00080 // declareOptions // 00082 void KFoldSplitter::declareOptions(OptionList& ol) 00083 { 00084 declareOption(ol, "K", &KFoldSplitter::K, OptionBase::buildoption, 00085 "Split dataset in K parts (you can use K = -1 to perform leave-one-out CV)."); 00086 00087 declareOption(ol, "append_train", &KFoldSplitter::append_train, OptionBase::buildoption, 00088 "If set to 1, the trainset will be appended after in the returned sets."); 00089 00090 declareOption(ol, "append_non_constant_test", 00091 &KFoldSplitter::append_non_constant_test, 00092 OptionBase::buildoption, 00093 "If true, the non-constant part of the test set will be appended\n" 00094 "in the returned sets. This mostly makes sense when 'cross_range'\n" 00095 "is not (0:1).", 00096 OptionBase::advanced_level); 00097 00098 declareOption(ol, "include_test_in_train", &KFoldSplitter::include_test_in_train, OptionBase::buildoption, 00099 "If set to 1, the test set will be included in the train set."); 00100 00101 declareOption(ol, "cross_range", &KFoldSplitter::cross_range, 00102 OptionBase::buildoption, 00103 "The range on which cross-validation is applied (similar to the\n" 00104 "FractionSplitter ranges).", 00105 OptionBase::advanced_level); 00106 00107 declareOption(ol, "balance_classes", &KFoldSplitter::balance_classes, 00108 OptionBase::buildoption, 00109 "Should we balance classes inside the splits to obtain the same\n" 00110 "class frequencies. This corresponds to concatenating the results\n" 00111 "of a K-Fold performed on the subsets of examples from each class.\n" 00112 "Note that it currently does not support leave-one-out, and that\n" 00113 "you might obtain strange results if K > number of samples in one\n" 00114 "class.\n" 00115 "Note also that for this option to work, you have to label your\n" 00116 "classes from 0 to (n_classes-1), and all classes must be present\n" 00117 "in the source VMat."); 00118 00119 inherited::declareOptions(ol); 00120 } 00121 00123 // build_ // 00125 void KFoldSplitter::build_() 00126 { 00127 PLASSERT( K > 0 || K == -1 ); 00128 } 00129 00131 // build // 00133 void KFoldSplitter::build() 00134 { 00135 inherited::build(); 00136 build_(); 00137 } 00138 00140 // nsplits // 00142 int KFoldSplitter::nsplits() const 00143 { 00144 return K > 0 ? K 00145 : dataset ? dataset->length() 00146 : -1; 00147 } 00148 00150 // nSetsPerSplit // 00152 int KFoldSplitter::nSetsPerSplit() const 00153 { 00154 int nsets = 2; 00155 if (append_train) 00156 nsets++; 00157 if (append_non_constant_test) 00158 nsets++; 00159 return nsets; 00160 } 00161 00163 // getSplit // 00165 TVec<VMat> KFoldSplitter::getSplit(int k) 00166 { 00167 if (k >= nsplits()) 00168 PLERROR("KFoldSplitter::getSplit() - k (%d) cannot be greater than " 00169 " the number of splits (%d)", k, nsplits()); 00170 00171 real start = cross_range.first; 00172 real end = cross_range.second; 00173 int n_data = dataset->length(); 00174 PLASSERT_MSG(start >= 0 && end >= 0 && end > start && start < n_data && end < n_data, 00175 string("start=")+tostring(start)+", end="+tostring(end)+", n_data="+tostring(n_data)); 00176 int i_start = 0; 00177 if (start > 0) 00178 i_start = start >= 1 ? int(round(start)) : int(round(n_data * start)); 00179 int i_end = n_data; 00180 if (!fast_exact_is_equal(end, 1)) 00181 i_end = end >= 1 ? int(round(end)) : int(round(n_data * end)); 00182 // The cross validation will be done only on examples i_start, ..., i_end - 1. 00183 int n_cross_data = i_end - i_start; 00184 bool do_partial_cross = (n_cross_data != n_data); 00185 if (K > 0 && K > n_data) 00186 PLERROR("In KFoldSplitter::getSplit - The number of splits (%d) cannot" 00187 " be greater than the number of samples in the dataset (%d). " 00188 "If you want to perform leave-one-out cross-validation, please" 00189 "set K = -1", K, n_data); 00190 real test_fraction = K > 0 ? (n_cross_data/(real)K) : 0; 00191 if ((int)(test_fraction) < 1) 00192 test_fraction = 1; // leave-one-out cross-validation 00193 00194 TVec<VMat> split_(2); 00195 VMat non_constant_test; 00196 if (do_partial_cross) { 00197 if (balance_classes) 00198 PLERROR("balance_classes not implemented with partial_cross"); 00199 VMat sub_data = new SubVMatrix(dataset, i_start, 0, n_cross_data, dataset->width()); 00200 split(sub_data, test_fraction, split_[0], split_[1], k, true); 00201 non_constant_test = split_[1]; 00202 if (i_start > 0) { 00203 VMat constant_train = new SubVMatrix(dataset, 0, 0, i_start, dataset->width()); 00204 split_[0] = new ConcatRowsVMatrix(constant_train, split_[0]); 00205 } 00206 if (i_end < n_data) { 00207 VMat constant_test = new SubVMatrix(dataset, i_end, 0, n_data - i_end, dataset->width()); 00208 split_[1] = new ConcatRowsVMatrix(split_[1], constant_test); 00209 } 00210 } 00211 else { 00212 if (balance_classes) 00213 { 00214 PLASSERT( dataset->targetsize() > 0 ); 00215 TVec<VMat> tmp_split_(2); 00216 int i_class = 0; 00217 if (test_fraction > 1.0) 00218 test_fraction /= n_cross_data; 00219 else 00220 PLERROR("In KFoldSplitter::getSplit - Leave-one-out not implemented"); 00221 while (true) { // break point below 00222 VMat dataset_class = new ClassSubsetVMatrix(dataset, i_class); 00223 int length = dataset_class->length(); 00224 if (length == 0 ) break; 00225 if (length < K) 00226 PLWARNING("In KFoldSplitter::getSplit - There are less " 00227 "samples from class %d (N = %d) than splits " 00228 "(K = %d): you may get weird results", 00229 i_class, length, K); 00230 split(dataset_class, test_fraction, tmp_split_[0], tmp_split_[1], k, true); 00231 if (i_class == 0) { 00232 split_ = tmp_split_.copy(); 00233 } else { 00234 split_[0] = new ConcatRowsVMatrix(split_[0], tmp_split_[0]); 00235 split_[1] = new ConcatRowsVMatrix(split_[1], tmp_split_[1]); 00236 } 00237 i_class++; 00238 } 00239 } 00240 else 00241 split(dataset, test_fraction, split_[0], split_[1], k, true); 00242 non_constant_test = split_[1]; 00243 } 00244 if (include_test_in_train) 00245 split_[0] = new ConcatRowsVMatrix(split_[0], split_[1]); 00246 if (append_train) 00247 split_.append(split_[0]); 00248 if (append_non_constant_test) { 00249 PLCHECK_MSG(!balance_classes, "Not implemented"); 00250 split_.append(non_constant_test); 00251 } 00252 return split_; 00253 } 00254 00255 } // end of namespace PLearn 00256 00257 00258 /* 00259 Local Variables: 00260 mode:c++ 00261 c-basic-offset:4 00262 c-file-style:"stroustrup" 00263 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00264 indent-tabs-mode:nil 00265 fill-column:79 00266 End: 00267 */ 00268 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :