/** * Recursively descends through the tree, removing * the supplied node and any descendants from * the internal node list. * * @param node The root node of the subtree to remove. */ public void recursiveRemoveSubtree(DecisionTreeNode node) { if (node == null) { return; } // First, recursively remove all the node's children. // (This loop doesn't run if the node is a leaf node) for (int i = 0; i < node.getArcLabelCount(); i++) { if (node.getChild(i) != null) { recursiveRemoveSubtree(node.getChild(i)); } } // Remove this node from the vector. Nodes.Remove(node); // If the node was a leaf, then we have to update // the classification statistics. if (node.isLeaf()) { TrainingCorrect -= node.getTrainingEgsCorrectClassUsingBestTrainingIndex(); TestingCorrect -= node.getTestingEgsCorrectClassUsingBestTrainingIndex(); } else { InternalNodes--; } }
/// <summary> /// Search through the current tree and return the first /// node without a complete set of children. The arc /// number for the first missing child is returned in /// position 0 of the arcNum array. /// </summary> /// <param name="node">The node at which to begin the search.</param> /// <param name="arcNum">An integer array of size 1. The arc /// number for the first missing child is /// returned in arcNum[0].</param> /// <returns>A reference to the first incomplete node /// in the tree (farthest left). The method /// returns null if the tree is already complete, /// or if the tree is empty.</returns> public DecisionTreeNode findIncompleteNode(DecisionTreeNode node, int[] arcNum) { // The search is recursive - at some point, we // may want to change this to a non-recursive // algorithm (if we start dealing with extremely // large trees?) // Base cases. if (node == null || node.isLeaf()) { return(null); } // Recursive case. This node is not a leaf - so descend. for (int i = 0; i < node.getArcLabelCount(); i++) { DecisionTreeNode nextNode; if ((nextNode = findIncompleteNode(node.getChild(i), arcNum)) != null) { return(nextNode); } } if ((arcNum[0] = node.getFirstMissingChild()) >= 0) { // We found a node with a missing child, which // is already stored in arcNum - so return this // node. return(node); } // We searched all the subtrees attached to this // node, and didn't find anything, so return null. return(null); }
/** * An implementation of the recursive decision tree * pessimistic pruning algorithm. Given a parent * node, the method will prune all the branches * below the node. * * @param node The root node of the tree to prune. * * @param error A <code>double</code> array of size 1. The * array is used to store the current error value. * * @return <code>true</code> if an entire subtree was successfully * pruned, or <code>false</code> otherwise. */ public bool prunePessimisticDT(DecisionTreeNode node, double[] error) { // Post-order walk through the tree, marking // our path as we go along. if (node.isLeaf()) { if (node.getTrainingEgsAtNode() == 0) { error[0] = 0; return true; } else { // We do the error calculation in two steps - // Here we multiply the error value by the number // of examples that reach the node. When the method // is called recursively, this value will be divided // by the number of examples that reach the parent // node (thus weighting the error from each child). int errors1 = (int)node.getTrainingEgsAtNode() - node.getTrainingEgsCorrectClassUsingBestTrainingIndex(); double p1 = (double)(errors1 + 1.0) / (node.getTrainingEgsAtNode() + 2.0); error[0] = node.getTrainingEgsAtNode() * errorBar(p1, node.getTrainingEgsAtNode()) + errors1; return true; } } // We're at an internal node, so compute the error // of the children and use the result to determine // if we prune or not. double errorSum = 0; for (int i = 0; i < node.getArcLabelCount(); i++) { // Mark our current path. Tree.flagNode(node, i); if (!prunePessimisticDT(node.getChild(i), error)) { Tree.flagNode(node, -2); return false; } errorSum += error[0]; } // Mark the node as our current target. Tree.flagNode(node, -1); // Get the worst-case performance of this node. double errorWorst; if (node.getTrainingEgsAtNode() == 0) { error[0] = 0; return true; } int errors = (int)node.getTrainingEgsAtNode() - node.getTrainingEgsCorrectClassUsingBestTrainingIndex(); double p = (double)(errors + 1.0) / (node.getTrainingEgsAtNode() + 2.0); errorWorst = (double)node.getTrainingEgsAtNode() * errorBar(p, node.getTrainingEgsAtNode()) + errors; DecisionTreeNode newNode = node; if (errorWorst < errorSum) { // We need to "prune" this node to a leaf. DecisionTreeNode parent = node.getParent(); int arcNum = -1; if (parent != null) { arcNum = parent.getChildPosition(node); } Tree.pruneSubtree(node); // Figure out the label for the new leaf. String label = null; try { label = DatasetUse.getTargetAttribute().getAttributeValueByNum(node.getTrainingBestTarget()); } catch (Exception e) { // Should never happen. //e.printStackTrace(); } node.getMask().mask(0, node.getTrainingBestTarget()); newNode = Tree.addLeafNode(parent, arcNum, label, node.getMask(), node.getTrainingEgsAtNode(), node.getTrainingBestTarget(), node.getTrainingEgsCorrectClassUsingBestTrainingIndex(), node.getTestingEgsCorrectClassUsingBestTrainingIndex(), node.getTestingEgsAtNode(), node.getTestingBestTarget(), node.getTestingEgsCorrectClassUsingBestTestingIndex(), node.getTrainingEgsCorrectClassUsingBestTestingIndex()); } // Update the count. if (newNode.isLeaf()) { error[0] = errorWorst; } else { error[0] = errorSum; } // All finished, unmark the node if it still exists. Tree.flagNode(node, -2); return true; }
/** * An implementation of the recursive decision tree * reduced error pruning algorithm. Given a parent * node, the method will prune all the branches * below the node. * * @param node The root node of the tree to prune. * * @param error A <code>double</code> array of size 1. The * array is used to store the current error value. * * @return <code>true</code> if an entire subtree was successfully * pruned, or <code>false</code> otherwise. */ public bool pruneReducedErrorDT(DecisionTreeNode node, double[] error) { if (node.isLeaf()) { error[0] = node.getTestingEgsAtNode() - node.getTestingEgsCorrectClassUsingBestTrainingIndex(); return true; } // We're at an internal node, so compute the error // of the children and use the result to determine // if we prune or not. double errorSum = 0; for (int i = 0; i < node.getArcLabelCount(); i++) { // Mark our current path. Tree.flagNode(node, i); if (!pruneReducedErrorDT(node.getChild(i), error)) { Tree.flagNode(node, -2); return false; } errorSum += error[0]; } // Mark the node as our current target. Tree.flagNode(node, -1); // Get the best-case performance of this node. double errorBest = node.getTestingEgsAtNode() - node.getTestingEgsCorrectClassUsingBestTestingIndex(); DecisionTreeNode newNode = node; if (errorBest < errorSum) { // We need to "prune" this node to a leaf. DecisionTreeNode parent = node.getParent(); int arcNum = -1; if (parent != null) { arcNum = parent.getChildPosition(node); } Tree.pruneSubtree(node); // Figure out the label for the new leaf. String label = null; try { label = DatasetUse.getTargetAttribute().getAttributeValueByNum(node.getTestingBestTarget()); } catch (Exception e) { // Should never happen. //e.printStackTrace(); } node.getMask().mask(0, node.getTestingBestTarget()); newNode = Tree.addLeafNode(parent, arcNum, label, node.getMask(), node.getTrainingEgsAtNode(), node.getTestingBestTarget(), node.getTrainingEgsCorrectClassUsingBestTestingIndex(), node.getTestingEgsCorrectClassUsingBestTestingIndex(), node.getTestingEgsAtNode(), node.getTestingBestTarget(), node.getTestingEgsCorrectClassUsingBestTestingIndex(), node.getTrainingEgsCorrectClassUsingBestTestingIndex()); } // Update the count. if (newNode.isLeaf()) { error[0] = errorBest; } else { error[0] = errorSum; } // All finished, unmark the node if it still exists. Tree.flagNode(node, -2); return true; }
/** * Recursively descends through the tree, removing * the supplied node and any descendants from * the internal node list. * * @param node The root node of the subtree to remove. */ public void recursiveRemoveSubtree(DecisionTreeNode node) { if (node == null) return; // First, recursively remove all the node's children. // (This loop doesn't run if the node is a leaf node) for (int i = 0; i < node.getArcLabelCount(); i++) if (node.getChild(i) != null) recursiveRemoveSubtree(node.getChild(i)); // Remove this node from the vector. Nodes.Remove(node); // If the node was a leaf, then we have to update // the classification statistics. if (node.isLeaf()) { TrainingCorrect -= node.getTrainingEgsCorrectClassUsingBestTrainingIndex(); TestingCorrect -= node.getTestingEgsCorrectClassUsingBestTrainingIndex(); } else InternalNodes--; }
/// <summary> /// Search through the current tree and return the first /// node without a complete set of children. The arc /// number for the first missing child is returned in /// position 0 of the arcNum array. /// </summary> /// <param name="node">The node at which to begin the search.</param> /// <param name="arcNum">An integer array of size 1. The arc /// number for the first missing child is /// returned in arcNum[0].</param> /// <returns>A reference to the first incomplete node /// in the tree (farthest left). The method /// returns null if the tree is already complete, /// or if the tree is empty.</returns> public DecisionTreeNode findIncompleteNode(DecisionTreeNode node, int[] arcNum) { // The search is recursive - at some point, we // may want to change this to a non-recursive // algorithm (if we start dealing with extremely // large trees?) // Base cases. if (node == null || node.isLeaf()) { return null; } // Recursive case. This node is not a leaf - so descend. for (int i = 0; i < node.getArcLabelCount(); i++) { DecisionTreeNode nextNode; if ((nextNode = findIncompleteNode(node.getChild(i), arcNum)) != null) { return nextNode; } } if ((arcNum[0] = node.getFirstMissingChild()) >= 0) { // We found a node with a missing child, which // is already stored in arcNum - so return this // node. return node; } // We searched all the subtrees attached to this // node, and didn't find anything, so return null. return null; }