@@ -115,6 +115,15 @@ private:
115
115
WorkItem * _data; // array of heap elements, max element is on the left
116
116
};
117
117
118
+ // ////////////////////////////////////////////////////////////////////////////////////////
119
+ // Service structure, node split error & splitting status
120
+ // ////////////////////////////////////////////////////////////////////////////////////////
121
+ struct NodeSplitResult
122
+ {
123
+ services::Status status;
124
+ bool bSplitSucceeded;
125
+ };
126
+
118
127
// ////////////////////////////////////////////////////////////////////////////////////////
119
128
// Service structure, contains numeric tables to be calculated as result
120
129
// ////////////////////////////////////////////////////////////////////////////////////////
@@ -595,14 +604,14 @@ protected:
595
604
algorithmFPType imp);
596
605
typename DataHelper::NodeType::Leaf * makeLeaf (const IndexType * idx, size_t n, typename DataHelper::ImpurityData & imp, size_t makeLeaf);
597
606
598
- bool findBestSplit (size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity, IndexType & iBestFeature ,
599
- typename DataHelper::TSplitData & split, algorithmFPType totalWeights);
600
- bool findBestSplitSerial (size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity, IndexType & iBestFeature ,
601
- typename DataHelper::TSplitData & split, algorithmFPType totalWeights);
602
- bool findBestSplitThreaded (size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity, IndexType & iBestFeature ,
603
- typename DataHelper::TSplitData & split, algorithmFPType totalWeights);
604
- bool simpleSplit (size_t iStart, const typename DataHelper::ImpurityData & curImpurity, IndexType & iFeatureBest,
605
- typename DataHelper::TSplitData & split);
607
+ NodeSplitResult findBestSplit (size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity,
608
+ IndexType & iBestFeature, typename DataHelper::TSplitData & split, algorithmFPType totalWeights);
609
+ NodeSplitResult findBestSplitSerial (size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity,
610
+ IndexType & iBestFeature, typename DataHelper::TSplitData & split, algorithmFPType totalWeights);
611
+ NodeSplitResult findBestSplitThreaded (size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity,
612
+ IndexType & iBestFeature, typename DataHelper::TSplitData & split, algorithmFPType totalWeights);
613
+ NodeSplitResult simpleSplit (size_t iStart, const typename DataHelper::ImpurityData & curImpurity, IndexType & iFeatureBest,
614
+ typename DataHelper::TSplitData & split);
606
615
void addImpurityDecrease (IndexType iFeature, size_t n, const typename DataHelper::ImpurityData & curImpurity,
607
616
const typename DataHelper::TSplitData & split);
608
617
@@ -619,7 +628,7 @@ protected:
619
628
const size_t nGen = (!_par.memorySavingMode && !_maxLeafNodes && !_useConstFeatures) ? n : _nFeaturesPerNode;
620
629
*_numElems += n;
621
630
RNGs<IndexType, cpu> rng;
622
- rng.uniformWithoutReplacement (nGen, _aFeatureIdx.get (), _aFeatureIdx.get () + nGen, _engineImpl->getState (), 0 , n);
631
+ rng.drawKFromBufferWithoutReplacement (nGen, _aFeatureIdx.get (), _aFeatureIdx.get () + nGen, _engineImpl->getState (), n);
623
632
}
624
633
625
634
services::Status computeResults (const dtrees::internal::Tree & t);
@@ -683,16 +692,18 @@ services::Status TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, c
683
692
_aFeatureBuf.reset (_nFeatureBufs);
684
693
_aFeatureIndexBuf.reset (_nFeatureBufs);
685
694
686
- if (!_par.memorySavingMode && !_maxLeafNodes && !_useConstFeatures)
687
- {
688
- _aFeatureIdx.reset (maxFeatures * 2 ); // maxFeatures elements are used by algorithm, others are used internally by generator
689
- _aConstFeatureIdx.reset (maxFeatures * 2 ); // first maxFeatures elements are used for saving indices of constant features,
690
- // the other part are used for saving levels of this features
691
- DAAL_CHECK_MALLOC (_aConstFeatureIdx.get ());
692
- services::internal::service_memset_seq<IndexType, cpu>(_aConstFeatureIdx.get (), IndexType (0 ), maxFeatures * 2 );
693
- }
694
- else
695
- _aFeatureIdx.reset (_nFeaturesPerNode * 2 ); // _nFeaturesPerNode elements are used by algorithm, others are used internally by generator
695
+ /* first maxFeatures entries serve as a buffer of drawn samples for node splitting */
696
+ /* second maxFeatures entries contains [0, ..., maxFeatures - 1] and is used to randomly draw indices */
697
+ _aFeatureIdx.reset (maxFeatures * 2 );
698
+ _aConstFeatureIdx.reset (maxFeatures * 2 );
699
+
700
+ DAAL_CHECK_MALLOC (_aConstFeatureIdx.get ());
701
+ services::internal::service_memset_seq<IndexType, cpu>(_aConstFeatureIdx.get (), IndexType (0 ), 2 * maxFeatures);
702
+ // in order to use drawKFromBufferWithoutReplacement we need to initialize
703
+ // the buffer to contain all indices from [0, 1, ..., maxFeatures - 1]
704
+ DAAL_CHECK_MALLOC (_aFeatureIdx.get ());
705
+ services::internal::service_memset_seq<IndexType, cpu>(_aFeatureIdx.get (), IndexType (0 ), maxFeatures);
706
+ services::internal::service_memset_incrementing<IndexType, cpu>(_aFeatureIdx.get () + maxFeatures, IndexType (0 ), maxFeatures);
696
707
697
708
DAAL_CHECK_MALLOC (_aSample.get () && _helper.reset (_nSamples) && _helper.resetWeights (_nSamples) && _aFeatureBuf.get () && _aFeatureIndexBuf.get ()
698
709
&& _aFeatureIdx.get ());
@@ -798,7 +809,10 @@ typename DataHelper::NodeType::Base * TrainBatchTaskBase<algorithmFPType, BinInd
798
809
799
810
typename DataHelper::TSplitData split;
800
811
IndexType iFeature;
801
- if (findBestSplit (level, iStart, n, curImpurity, iFeature, split, totalWeights))
812
+
813
+ NodeSplitResult split_result = findBestSplit (level, iStart, n, curImpurity, iFeature, split, totalWeights);
814
+ DAAL_ASSERT (split_result.status .ok ());
815
+ if (split_result.bSplitSucceeded )
802
816
{
803
817
const size_t nLeft = split.nLeft ;
804
818
const double imp = curImpurity.var ;
@@ -844,6 +858,7 @@ typename DataHelper::NodeType::Base * TrainBatchTaskBase<algorithmFPType, BinInd
844
858
DAAL_ASSERT (split.nLeft == right->count );
845
859
return res;
846
860
}
861
+
847
862
return makeLeaf (_aSample.get () + iStart, n, curImpurity, nClasses);
848
863
}
849
864
@@ -859,7 +874,10 @@ typename DataHelper::NodeType::Base * TrainBatchTaskBase<algorithmFPType, BinInd
859
874
{
860
875
return makeLeaf (_aSample.get () + item.start , item.n , impurity, nClasses);
861
876
}
862
- else if (findBestSplit (level, item.start , item.n , impurity, iFeature, split, item.totalWeights ))
877
+
878
+ NodeSplitResult split_result = findBestSplit (level, item.start , item.n , impurity, iFeature, split, item.totalWeights );
879
+ DAAL_ASSERT (split_result.status .ok ());
880
+ if (split_result.bSplitSucceeded )
863
881
{
864
882
const double imp = impurity.var ;
865
883
const double impLeft = split.left .var ;
@@ -896,10 +914,8 @@ typename DataHelper::NodeType::Base * TrainBatchTaskBase<algorithmFPType, BinInd
896
914
return item.node ;
897
915
}
898
916
}
899
- else
900
- {
901
- return makeLeaf (_aSample.get () + item.start , item.n , impurity, nClasses);
902
- }
917
+
918
+ return makeLeaf (_aSample.get () + item.start , item.n , impurity, nClasses);
903
919
}
904
920
905
921
template <typename algorithmFPType, typename BinIndexType, typename DataHelper, CpuType cpu>
@@ -1032,37 +1048,40 @@ typename DataHelper::NodeType::Base * TrainBatchTaskBase<algorithmFPType, BinInd
1032
1048
}
1033
1049
1034
1050
template <typename algorithmFPType, typename BinIndexType, typename DataHelper, CpuType cpu>
1035
- bool TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::simpleSplit(size_t iStart,
1036
- const typename DataHelper::ImpurityData & curImpurity,
1037
- IndexType & iFeatureBest,
1038
- typename DataHelper::TSplitData & split)
1051
+ NodeSplitResult TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::simpleSplit(size_t iStart,
1052
+ const typename DataHelper::ImpurityData & curImpurity,
1053
+ IndexType & iFeatureBest,
1054
+ typename DataHelper::TSplitData & split)
1039
1055
{
1056
+ services::Status st;
1040
1057
RNGs<IndexType, cpu> rng;
1041
1058
algorithmFPType featBuf[2 ];
1042
1059
IndexType * aIdx = _aSample.get () + iStart;
1043
1060
for (size_t i = 0 ; i < _nFeaturesPerNode; ++i)
1044
1061
{
1045
1062
IndexType iFeature;
1046
1063
*_numElems += 1 ;
1047
- rng.uniform (1 , &iFeature, _engineImpl->getState (), 0 , _data->getNumberOfColumns ());
1064
+ int errorcode = rng.uniform (1 , &iFeature, _engineImpl->getState (), 0 , _data->getNumberOfColumns ());
1065
+ if (errorcode)
1066
+ {
1067
+ st = services::Status (services::ErrorNullResult);
1068
+ }
1048
1069
featureValuesToBuf (iFeature, featBuf, aIdx, 2 );
1049
1070
if (featBuf[1 ] - featBuf[0 ] <= _accuracy) // all values of the feature are the same
1050
1071
continue ;
1051
1072
_helper.simpleSplit (featBuf, aIdx, split);
1052
1073
split.featureUnordered = _featHelper.isUnordered (iFeature);
1053
1074
split.impurityDecrease = curImpurity.var ;
1054
1075
iFeatureBest = iFeature;
1055
- return true ;
1076
+ return { st, true } ;
1056
1077
}
1057
- return false ;
1078
+ return { st, false } ;
1058
1079
}
1059
1080
1060
1081
template <typename algorithmFPType, typename BinIndexType, typename DataHelper, CpuType cpu>
1061
- bool TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBestSplit(size_t level, size_t iStart, size_t n,
1062
- const typename DataHelper::ImpurityData & curImpurity,
1063
- IndexType & iFeatureBest,
1064
- typename DataHelper::TSplitData & split,
1065
- algorithmFPType totalWeights)
1082
+ NodeSplitResult TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBestSplit(
1083
+ size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity, IndexType & iFeatureBest,
1084
+ typename DataHelper::TSplitData & split, algorithmFPType totalWeights)
1066
1085
{
1067
1086
if (n == 2 )
1068
1087
{
@@ -1078,26 +1097,67 @@ bool TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBes
1078
1097
1079
1098
// find best split and put it to featureIndexBuf
1080
1099
template <typename algorithmFPType, typename BinIndexType, typename DataHelper, CpuType cpu>
1081
- bool TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBestSplitSerial(size_t level, size_t iStart, size_t n,
1082
- const typename DataHelper::ImpurityData & curImpurity,
1083
- IndexType & iBestFeature,
1084
- typename DataHelper::TSplitData & bestSplit,
1085
- algorithmFPType totalWeights)
1100
+ NodeSplitResult TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBestSplitSerial(
1101
+ size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity, IndexType & iBestFeature,
1102
+ typename DataHelper::TSplitData & bestSplit, algorithmFPType totalWeights)
1086
1103
{
1087
- chooseFeatures ();
1088
- size_t nVisitedFeature = 0 ;
1089
- const size_t maxFeatures = nFeatures ();
1090
- const float qMax = 0.02 ; // min fracture of observations to be handled as indexed feature values
1091
- IndexType * bestSplitIdx = featureIndexBuf (0 ) + iStart;
1092
- IndexType * aIdx = _aSample.get () + iStart;
1093
- int iBestSplit = -1 ;
1094
- int idxFeatureValueBestSplit = -1 ; // when sorted feature is used
1104
+ services::Status st;
1105
+
1106
+ /* counter of the number of visited features, we visit _nFeaturesPerNode
1107
+ * depending on _useConstFeatures, constant features can be skipped
1108
+ */
1109
+ size_t nVisitedFeature = 0 ;
1110
+ /* total number of features */
1111
+ const size_t maxFeatures = nFeatures ();
1112
+ /* minimum fraction of all samples per bin */
1113
+ const algorithmFPType qMax = 0.02 ;
1114
+ /* index of the best split, initialized to first index we investigate */
1115
+ IndexType * bestSplitIdx = featureIndexBuf (0 ) + iStart;
1116
+ /* sample index */
1117
+ IndexType * aIdx = _aSample.get () + iStart;
1118
+ /* zero-based index of best split */
1119
+ int64_t iBestSplit = -1 ;
1120
+ int64_t idxFeatureValueBestSplit = -1 ;
1095
1121
typename DataHelper::TSplitData split;
1096
- const float fact = float (n);
1122
+ /* RNG for sample drawing */
1123
+ RNGs<IndexType, cpu> rng;
1124
+ /* index for swapping samples in Fisher-Yates sampling */
1125
+ IndexType swapIdx;
1126
+
1097
1127
for (size_t i = 0 ; i < maxFeatures && nVisitedFeature < _nFeaturesPerNode; ++i)
1098
1128
{
1099
- const auto iFeature = _aFeatureIdx[i];
1100
- const bool bUseIndexedFeatures = (!_par.memorySavingMode ) && (fact > qMax * float (_helper.indexedFeatures ().numIndices (iFeature)));
1129
+ /* draw a random sample without replacement */
1130
+ // based on Fisher Yates sampling
1131
+ // _aFeatureIdx has length of 2 * _maxFeatures
1132
+ // first maxFeatures contain the currently selected features
1133
+ // at iteration i, we have drawn i features and written them to
1134
+ // _aFeatureIdx[0, 1, ..., i-1]
1135
+ //
1136
+ // the second half of the buffer contains all numbers from
1137
+ // [0, 1, ..., maxFeatures-1] and we randomly select one without
1138
+ // replacement based on Fisher Yates sampling
1139
+ // drawing uniformly from [0, maxFeatures-i] and swapping the indices
1140
+ // assures uniform probability of all drawn numbers
1141
+
1142
+ /* draw the i-th index of the sample */
1143
+ int errorcode = rng.uniform (1 , &swapIdx, _engineImpl->getState (), 0 , maxFeatures - i);
1144
+ if (errorcode)
1145
+ {
1146
+ st = services::Status (services::ErrorNullResult);
1147
+ }
1148
+
1149
+ /* account for buffer offset from 0 */
1150
+ swapIdx += maxFeatures;
1151
+ /* _aFeatureIdx[swapIdx] was drawn */
1152
+ _aFeatureIdx[i] = _aFeatureIdx[swapIdx];
1153
+ /* swap in number at [2 * maxFeatures - 1 - i] for next draw */
1154
+ _aFeatureIdx[swapIdx] = _aFeatureIdx[2 * maxFeatures - 1 - i];
1155
+ /* store drawn number at end of number buffer so that no number is lost */
1156
+ _aFeatureIdx[2 * maxFeatures - 1 - i] = _aFeatureIdx[i];
1157
+
1158
+ const auto iFeature = _aFeatureIdx[i];
1159
+ const bool bUseIndexedFeatures =
1160
+ (!_par.memorySavingMode ) && (algorithmFPType (n) > qMax * algorithmFPType (_helper.indexedFeatures ().numIndices (iFeature)));
1101
1161
1102
1162
if (!_maxLeafNodes && !_useConstFeatures && !_par.memorySavingMode )
1103
1163
{
@@ -1154,7 +1214,14 @@ bool TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBes
1154
1214
#endif
1155
1215
}
1156
1216
}
1157
- if (iBestSplit < 0 ) return false ; // not found
1217
+
1218
+ if (!st.ok () || iBestSplit < 0 )
1219
+ {
1220
+ // either:
1221
+ // error during splitting -> failure
1222
+ // or no split found -> not a failure but still have to return
1223
+ return { st, false };
1224
+ }
1158
1225
1159
1226
iBestFeature = _aFeatureIdx[iBestSplit];
1160
1227
bool bCopyToIdx = true ;
@@ -1193,20 +1260,18 @@ bool TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBes
1193
1260
bCopyToIdx = (iBestSplit + 1 < _nFeaturesPerNode); // if iBestSplit is the last considered feature
1194
1261
// then aIdx already contains the best split, no need to copy
1195
1262
if (bCopyToIdx) services::internal::tmemcpy<IndexType, cpu>(aIdx, bestSplitIdx, n);
1196
- return true ;
1263
+ return { st, true } ;
1197
1264
}
1198
1265
1199
1266
template <typename algorithmFPType, typename BinIndexType, typename DataHelper, CpuType cpu>
1200
- bool TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBestSplitThreaded(size_t level, size_t iStart, size_t n,
1201
- const typename DataHelper::ImpurityData & curImpurity,
1202
- IndexType & iFeatureBest,
1203
- typename DataHelper::TSplitData & split,
1204
- algorithmFPType totalWeights)
1267
+ NodeSplitResult TrainBatchTaskBase<algorithmFPType, BinIndexType, DataHelper, cpu>::findBestSplitThreaded(
1268
+ size_t level, size_t iStart, size_t n, const typename DataHelper::ImpurityData & curImpurity, IndexType & iFeatureBest,
1269
+ typename DataHelper::TSplitData & split, algorithmFPType totalWeights)
1205
1270
{
1206
1271
chooseFeatures ();
1207
1272
TArray<typename DataHelper::TSplitData, cpu> aFeatureSplit (_nFeaturesPerNode);
1208
1273
// TODO, if parallel for features
1209
- return false ;
1274
+ return { services::Status (services::ErrorMethodNotSupported), false } ;
1210
1275
}
1211
1276
1212
1277
template <typename algorithmFPType, typename BinIndexType, typename DataHelper, CpuType cpu>
0 commit comments