use of org.knime.base.node.mine.treeensemble2.learner.SplitCandidate in project knime-core by knime.
the class TreeNominalColumnDataTest method testCalcBestSplitRegressionBinary.
/**
* Tests the method
* {@link TreeNominalColumnData#calcBestSplitRegression(DataMemberships, RegressionPriors, TreeTargetNumericColumnData, RandomData)}
* using binary splits
*
* @throws Exception
*/
@Test
public void testCalcBestSplitRegressionBinary() throws Exception {
TreeEnsembleLearnerConfiguration config = new TreeEnsembleLearnerConfiguration(true);
Pair<TreeNominalColumnData, TreeTargetNumericColumnData> tennisDataRegression = tennisDataRegression(config);
TreeNominalColumnData columnData = tennisDataRegression.getFirst();
TreeTargetNumericColumnData targetData = tennisDataRegression.getSecond();
TreeData treeData = createTreeDataRegression(tennisDataRegression);
double[] rowWeights = new double[SMALL_COLUMN_DATA.length];
Arrays.fill(rowWeights, 1.0);
IDataIndexManager indexManager = new DefaultDataIndexManager(treeData);
DataMemberships dataMemberships = new RootDataMemberships(rowWeights, treeData, indexManager);
RegressionPriors priors = targetData.getPriors(rowWeights, config);
SplitCandidate splitCandidate = columnData.calcBestSplitRegression(dataMemberships, priors, targetData, null);
assertNotNull(splitCandidate);
assertThat(splitCandidate, instanceOf(NominalBinarySplitCandidate.class));
assertTrue(splitCandidate.canColumnBeSplitFurther());
assertEquals(32.9143, splitCandidate.getGainValue(), 0.0001);
NominalBinarySplitCandidate binarySplitCandidate = (NominalBinarySplitCandidate) splitCandidate;
TreeNodeNominalBinaryCondition[] childConditions = binarySplitCandidate.getChildConditions();
assertEquals(2, childConditions.length);
assertArrayEquals(new String[] { "R" }, childConditions[0].getValues());
assertArrayEquals(new String[] { "R" }, childConditions[1].getValues());
assertEquals(SetLogic.IS_NOT_IN, childConditions[0].getSetLogic());
assertEquals(SetLogic.IS_IN, childConditions[1].getSetLogic());
}
use of org.knime.base.node.mine.treeensemble2.learner.SplitCandidate in project knime-core by knime.
the class TreeNominalColumnDataTest method testCalcBestSplitClassificationBinary.
/**
* Tests the method
* {@link TreeNominalColumnData#calcBestSplitClassification(DataMemberships, ClassificationPriors, TreeTargetNominalColumnData, RandomData)}
* using binary splits.
*
* @throws Exception
*/
@Test
public void testCalcBestSplitClassificationBinary() throws Exception {
final TreeEnsembleLearnerConfiguration config = createConfig(false);
Pair<TreeNominalColumnData, TreeTargetNominalColumnData> tennisData = tennisData(config);
TreeNominalColumnData columnData = tennisData.getFirst();
TreeTargetNominalColumnData targetData = tennisData.getSecond();
assertEquals(SplitCriterion.Gini, config.getSplitCriterion());
double[] rowWeights = new double[SMALL_COLUMN_DATA.length];
Arrays.fill(rowWeights, 1.0);
TreeData tennisTreeData = tennisTreeData(config);
IDataIndexManager indexManager = new DefaultDataIndexManager(tennisTreeData);
DataMemberships dataMemberships = new RootDataMemberships(rowWeights, tennisTreeData, indexManager);
ClassificationPriors priors = targetData.getDistribution(rowWeights, config);
SplitCandidate splitCandidate = columnData.calcBestSplitClassification(dataMemberships, priors, targetData, null);
assertNotNull(splitCandidate);
assertThat(splitCandidate, instanceOf(NominalBinarySplitCandidate.class));
assertTrue(splitCandidate.canColumnBeSplitFurther());
// manually via libre office calc
assertEquals(0.0689342404, splitCandidate.getGainValue(), 0.00001);
NominalBinarySplitCandidate binSplitCandidate = (NominalBinarySplitCandidate) splitCandidate;
TreeNodeNominalBinaryCondition[] childConditions = binSplitCandidate.getChildConditions();
assertEquals(2, childConditions.length);
assertArrayEquals(new String[] { "R" }, childConditions[0].getValues());
assertArrayEquals(new String[] { "R" }, childConditions[1].getValues());
assertEquals(SetLogic.IS_NOT_IN, childConditions[0].getSetLogic());
assertEquals(SetLogic.IS_IN, childConditions[1].getSetLogic());
BitSet inChild = columnData.updateChildMemberships(childConditions[0], dataMemberships);
DataMemberships child1Memberships = dataMemberships.createChildMemberships(inChild);
ClassificationPriors childTargetPriors = targetData.getDistribution(child1Memberships, config);
SplitCandidate splitCandidateChild = columnData.calcBestSplitClassification(child1Memberships, childTargetPriors, targetData, null);
assertNotNull(splitCandidateChild);
assertThat(splitCandidateChild, instanceOf(NominalBinarySplitCandidate.class));
// manually via libre office calc
assertEquals(0.0086419753, splitCandidateChild.getGainValue(), 0.00001);
inChild = columnData.updateChildMemberships(childConditions[1], dataMemberships);
DataMemberships child2Memberships = dataMemberships.createChildMemberships(inChild);
childTargetPriors = targetData.getDistribution(child2Memberships, config);
splitCandidateChild = columnData.calcBestSplitClassification(child2Memberships, childTargetPriors, targetData, null);
assertNull(splitCandidateChild);
}
use of org.knime.base.node.mine.treeensemble2.learner.SplitCandidate in project knime-core by knime.
the class TreeNominalColumnDataTest method testCalcBestSplitClassificationMultiwayXGBoostMissingValueHandling.
/**
* This method tests the XGBoost missing value handling for classification in case of multiway splits.
*
* @throws Exception
*/
@Test
public void testCalcBestSplitClassificationMultiwayXGBoostMissingValueHandling() throws Exception {
final TreeEnsembleLearnerConfiguration config = createConfig(false);
config.setUseBinaryNominalSplits(false);
config.setMissingValueHandling(MissingValueHandling.XGBoost);
final TestDataGenerator dataGen = new TestDataGenerator(config);
final RandomData rd = config.createRandomData();
// test the case that there are no missing values in the training data
final String noMissingCSV = "a, a, a, b, b, b, b, c, c";
final String noMissingTarget = "A, B, B, C, C, C, B, A, B";
TreeNominalColumnData dataCol = dataGen.createNominalAttributeColumn(noMissingCSV, "noMissings", 0);
TreeTargetNominalColumnData targetCol = TestDataGenerator.createNominalTargetColumn(noMissingTarget);
DataMemberships dataMem = createMockDataMemberships(targetCol.getNrRows());
SplitCandidate split = dataCol.calcBestSplitClassification(dataMem, targetCol.getDistribution(dataMem, config), targetCol, rd);
assertNotNull("There is a possible split.", split);
assertEquals("Incorrect gain.", 0.216, split.getGainValue(), 1e-3);
assertThat(split, instanceOf(NominalMultiwaySplitCandidate.class));
NominalMultiwaySplitCandidate nomSplit = (NominalMultiwaySplitCandidate) split;
assertTrue("No missing values in the column.", nomSplit.getMissedRows().isEmpty());
TreeNodeNominalCondition[] conditions = nomSplit.getChildConditions();
assertEquals("Wrong number of child conditions.", 3, conditions.length);
assertEquals("Wrong value in child condition.", "a", conditions[0].getValue());
assertEquals("Wrong value in child condition.", "b", conditions[1].getValue());
assertEquals("Wrong value in child condition.", "c", conditions[2].getValue());
assertFalse("Missing values should be sent to the majority child (i.e. b)", conditions[0].acceptsMissings());
assertTrue("Missing values should be sent to the majority child (i.e. b)", conditions[1].acceptsMissings());
assertFalse("Missing values should be sent to the majority child (i.e. b)", conditions[2].acceptsMissings());
// test the case that there are missing values in the training data
final String missingCSV = "a, a, a, b, b, b, b, c, c, ?";
final String missingTarget = "A, B, B, C, C, C, B, A, B, C";
dataCol = dataGen.createNominalAttributeColumn(missingCSV, "missings", 0);
targetCol = TestDataGenerator.createNominalTargetColumn(missingTarget);
dataMem = createMockDataMemberships(targetCol.getNrRows());
split = dataCol.calcBestSplitClassification(dataMem, targetCol.getDistribution(dataMem, config), targetCol, rd);
assertNotNull("There is a possible split.", split);
assertEquals("Incorrect gain.", 0.2467, split.getGainValue(), 1e-3);
assertThat(split, instanceOf(NominalMultiwaySplitCandidate.class));
nomSplit = (NominalMultiwaySplitCandidate) split;
assertTrue("Split should handle missing values.", nomSplit.getMissedRows().isEmpty());
conditions = nomSplit.getChildConditions();
assertEquals("Wrong number of child conditions.", 3, conditions.length);
assertEquals("Wrong value in child condition.", "a", conditions[0].getValue());
assertEquals("Wrong value in child condition.", "b", conditions[1].getValue());
assertEquals("Wrong value in child condition.", "c", conditions[2].getValue());
assertFalse("Missing values should be sent to b", conditions[0].acceptsMissings());
assertTrue("Missing values should be sent to b", conditions[1].acceptsMissings());
assertFalse("Missing values should be sent to b", conditions[2].acceptsMissings());
}
use of org.knime.base.node.mine.treeensemble2.learner.SplitCandidate in project knime-core by knime.
the class TreeNominalColumnDataTest method testCalcBestSplitClassificationBinaryPCA.
/**
* Tests the method
* {@link TreeNominalColumnData#calcBestSplitClassification(DataMemberships, ClassificationPriors, TreeTargetNominalColumnData, RandomData)}
* using binary splits. In this test case the data has more than two classes and the used algorithm is therefore PCA
* based.
*
* @throws Exception
*/
@Test
public void testCalcBestSplitClassificationBinaryPCA() throws Exception {
TreeEnsembleLearnerConfiguration config = createConfig(false);
Pair<TreeNominalColumnData, TreeTargetNominalColumnData> pcaData = createPCATestData(config);
TreeNominalColumnData columnData = pcaData.getFirst();
TreeTargetNominalColumnData targetData = pcaData.getSecond();
TreeData treeData = createTreeData(pcaData);
assertEquals(SplitCriterion.Gini, config.getSplitCriterion());
double[] rowWeights = new double[targetData.getNrRows()];
Arrays.fill(rowWeights, 1.0);
IDataIndexManager indexManager = new DefaultDataIndexManager(treeData);
DataMemberships dataMemberships = new RootDataMemberships(rowWeights, treeData, indexManager);
ClassificationPriors priors = targetData.getDistribution(rowWeights, config);
SplitCandidate splitCandidate = columnData.calcBestSplitClassification(dataMemberships, priors, targetData, null);
assertNotNull(splitCandidate);
assertThat(splitCandidate, instanceOf(NominalBinarySplitCandidate.class));
assertTrue(splitCandidate.canColumnBeSplitFurther());
assertEquals(0.0659, splitCandidate.getGainValue(), 0.0001);
NominalBinarySplitCandidate binarySplitCandidate = (NominalBinarySplitCandidate) splitCandidate;
TreeNodeNominalBinaryCondition[] childConditions = binarySplitCandidate.getChildConditions();
assertEquals(2, childConditions.length);
assertArrayEquals(new String[] { "E" }, childConditions[0].getValues());
assertArrayEquals(new String[] { "E" }, childConditions[1].getValues());
assertEquals(SetLogic.IS_NOT_IN, childConditions[0].getSetLogic());
assertEquals(SetLogic.IS_IN, childConditions[1].getSetLogic());
}
use of org.knime.base.node.mine.treeensemble2.learner.SplitCandidate in project knime-core by knime.
the class TreeNumericColumnData method calcBestSplitRegression.
@Override
public SplitCandidate calcBestSplitRegression(final DataMemberships dataMemberships, final RegressionPriors targetPriors, final TreeTargetNumericColumnData targetColumn, final RandomData rd) {
final TreeEnsembleLearnerConfiguration config = getConfiguration();
final boolean useAverageSplitPoints = config.isUseAverageSplitPoints();
final int minChildNodeSize = config.getMinChildSize();
// get columnMemberships
final ColumnMemberships columnMemberships = dataMemberships.getColumnMemberships(getMetaData().getAttributeIndex());
final int lengthNonMissing = getLengthNonMissing();
// missing value handling
final boolean useXGBoostMissingValueHandling = config.getMissingValueHandling() == MissingValueHandling.XGBoost;
// are there missing values in this column (complete column)
boolean branchContainsMissingValues = containsMissingValues();
boolean missingsGoLeft = true;
double missingWeight = 0.0;
double missingY = 0.0;
// check if there are missing values in this rowsample
if (branchContainsMissingValues) {
columnMemberships.goToLast();
while (columnMemberships.getIndexInColumn() >= lengthNonMissing) {
missingWeight += columnMemberships.getRowWeight();
missingY += targetColumn.getValueFor(columnMemberships.getOriginalIndex());
if (!columnMemberships.previous()) {
break;
}
}
columnMemberships.reset();
branchContainsMissingValues = missingWeight > 0.0;
}
final double ySumTotal = targetPriors.getYSum() - missingY;
final double nrRecordsTotal = targetPriors.getNrRecords() - missingWeight;
final double criterionTotal = useXGBoostMissingValueHandling ? (ySumTotal + missingY) * (ySumTotal + missingY) / (nrRecordsTotal + missingWeight) : ySumTotal * ySumTotal / nrRecordsTotal;
double ySumLeft = 0.0;
double nrRecordsLeft = 0.0;
double ySumRight = ySumTotal;
double nrRecordsRight = nrRecordsTotal;
// all values in the current branch are missing
if (nrRecordsRight == 0) {
// it is impossible to determine a split
return null;
}
double bestSplit = Double.NEGATIVE_INFINITY;
double bestImprovement = 0.0;
double lastSeenY = Double.NaN;
double lastSeenValue = Double.NEGATIVE_INFINITY;
double lastSeenWeight = -1.0;
// compute the gain, keep the one that maximizes the split
while (columnMemberships.next()) {
final double weight = columnMemberships.getRowWeight();
if (weight < EPSILON) {
// ignore record: not in current branch or not in sample
continue;
} else if (Math.floor(weight) != weight) {
throw new UnsupportedOperationException("weighted records (missing values?) not supported, " + "weight is " + weight);
}
final double value = getSorted(columnMemberships.getIndexInColumn());
if (lastSeenWeight > 0.0) {
ySumLeft += lastSeenWeight * lastSeenY;
ySumRight -= lastSeenWeight * lastSeenY;
nrRecordsLeft += lastSeenWeight;
nrRecordsRight -= lastSeenWeight;
if (nrRecordsLeft >= minChildNodeSize && nrRecordsRight >= minChildNodeSize && lastSeenValue < value) {
boolean tempMissingsGoLeft = true;
double childrenSquaredSum;
if (branchContainsMissingValues && useXGBoostMissingValueHandling) {
final double[] tempChildrenSquaredSum = new double[2];
tempChildrenSquaredSum[0] = ((ySumLeft + missingY) * (ySumLeft + missingY) / (nrRecordsLeft + missingWeight)) + (ySumRight * ySumRight / nrRecordsRight);
tempChildrenSquaredSum[1] = (ySumLeft * ySumLeft / nrRecordsLeft) + ((ySumRight + missingY) * (ySumRight + missingY) / (nrRecordsRight + missingWeight));
if (tempChildrenSquaredSum[0] >= tempChildrenSquaredSum[1]) {
childrenSquaredSum = tempChildrenSquaredSum[0];
tempMissingsGoLeft = true;
} else {
childrenSquaredSum = tempChildrenSquaredSum[1];
tempMissingsGoLeft = false;
}
} else {
childrenSquaredSum = (ySumLeft * ySumLeft / nrRecordsLeft) + (ySumRight * ySumRight / nrRecordsRight);
}
double criterion = childrenSquaredSum - criterionTotal;
boolean randomTieBreaker = criterion == bestImprovement ? rd.nextInt(0, 1) == 1 : false;
if (criterion > bestImprovement || randomTieBreaker) {
bestImprovement = criterion;
bestSplit = useAverageSplitPoints ? getCenter(lastSeenValue, value) : lastSeenValue;
// if there are no missing values go with majority
missingsGoLeft = branchContainsMissingValues ? tempMissingsGoLeft : nrRecordsLeft >= nrRecordsRight;
}
}
}
lastSeenY = targetColumn.getValueFor(columnMemberships.getOriginalIndex());
lastSeenValue = value;
lastSeenWeight = weight;
}
// + " but was " + lastSeenY * lastSeenWeight;
if (bestImprovement > 0.0) {
if (useXGBoostMissingValueHandling) {
// return new NumericMissingSplitCandidate(this, bestSplit, bestImprovement, missingsGoLeft);
return new NumericSplitCandidate(this, bestSplit, bestImprovement, new BitSet(), missingsGoLeft ? NumericSplitCandidate.MISSINGS_GO_LEFT : NumericSplitCandidate.MISSINGS_GO_RIGHT);
}
return new NumericSplitCandidate(this, bestSplit, bestImprovement, getMissedRows(columnMemberships), NumericSplitCandidate.NO_MISSINGS);
} else {
return null;
}
}
Aggregations