In the last post, we looked at the bagging process where a random sample of input is used to build each individual tree. This is one of the two types of randomness used in the algorithm. The second kind of randomness is used in the node splitting process. During the process, a random set of m attributes out of all attributes is used to compute the best split based on information gain.
If m is not specified in the algorithm, then the following logic is used to determine m.
For regression tree,
m = (int) Math.ceil(total_number_attributes / 3.0);
For classification tree,
m = (int) Math.ceil(Math.sqrt(total_number_attributes));
If you take a look at the Apache Mahout DecisionTreeBuilder, you will find the randomAttributes method below. This method selects m attributes to consider for split.
org.apache.mahout.classifier.df.builder.DecisionTreeBuilder
/** * Randomly selects m attributes to consider for split, excludes IGNORED and LABEL attributes * * @param rng random-numbers generator * @param selected attributes' state (selected or not) * @param m number of attributes to choose * @return list of selected attributes' indices, or null if all attributes have already been selected */ private static int[] randomAttributes(Random rng, boolean[] selected, int m) { int nbNonSelected = 0; // number of non selected attributes for (boolean sel : selected) { if (!sel) { nbNonSelected++; } } if (nbNonSelected == 0) { log.warn("All attributes are selected !"); return NO_ATTRIBUTES; } int[] result; if (nbNonSelected <= m) { // return all non selected attributes result = new int[nbNonSelected]; int index = 0; for (int attr = 0; attr < selected.length; attr++) { if (!selected[attr]) { result[index++] = attr; } } } else { result = new int[m]; for (int index = 0; index < m; index++) { // randomly choose a "non selected" attribute int rind; do { rind = rng.nextInt(selected.length); } while (selected[rind]); result[index] = rind; selected[rind] = true; // temporarily set the chosen attribute to be selected } // the chosen attributes are not yet selected for (int attr : result) { selected[attr] = false; } } return result; }
Given the list of chosen attributes, it loops through each one and computes the information gains for different splits generated by using different attributes and try to find the best split.
// find the best split Split best = null; for (int attr : attributes) { Split split = igSplit.computeSplit(data, attr); if (best == null || best.getIg() < split.getIg()) { best = split; } }
After getting the best split, if the information gain is less than the specified threshold, then we stop and create the leaf node with the label.
For regression, the label is the regression value which is calculated by taking the average of all the values of the sample data set. For classification, we use the majority label, the value that appears the most.
// information gain is near to zero. if (best.getIg() < EPSILON) { double label; if (data.getDataset().isNumerical(data.getDataset().getLabelId())) { label = sum / data.size(); } else { label = data.majorityLabel(rng); } log.debug("ig is near to zero Leaf({})", label); return new Leaf(label); }