/// <summary> /// Attaches a new internal node to the supplied node, /// along the specified arc. /// </summary> /// <param name="parent">The node in the current tree to attach /// the internal node to. If the node is null, the /// new internal node becomes the root of the tree.</param> /// <param name="arcNum">The arc number (or attribute value /// index) along which to attach the new node.</param> /// <param name="attributePosition">The position of the /// attribute used to split at the new node, relative /// to the other attributes in the dataset.</param> /// <param name="att">The attribute used to split at the new /// node.</param> /// <returns>A reference to the new internal node.</returns> public DecisionTreeNode addInternalNode(DecisionTreeNode parent, int arcNum, int attributePosition, Attribute att, AttributeMask mask, int numTrainingExamplesReachHere, int bestTrainingTargetIndex, int numTrainingEgsCorrectClassUsingBestTrainingIndex, int numTestingEgsCorrectClassUsingBestTrainingIndex, int numTestingExamplesReachHere, int bestTestingTargetIndex, int numTestingEgsCorrectClassUsingBestTestingIndex, int numTrainingEgsCorrectClassUsingBestTestingIndex) { // Create a new internal node. DecisionTreeNode internalNode = new DecisionTreeNode(parent, att.getName(), att.getValueNames(), mask); // Set the node statistics. internalNode.setTrainingStats(numTrainingExamplesReachHere, bestTrainingTargetIndex, numTrainingEgsCorrectClassUsingBestTrainingIndex, numTestingEgsCorrectClassUsingBestTrainingIndex); internalNode.setTestingStats(numTestingExamplesReachHere, bestTestingTargetIndex, numTestingEgsCorrectClassUsingBestTestingIndex, numTrainingEgsCorrectClassUsingBestTestingIndex); // Update the tree statistics. InternalNodes++; // Now, attach the new internal node to the supplied node. if (parent != null) parent.setChild(arcNum, internalNode); // Add a reference to the new node to the node list. Nodes.Add(internalNode); return internalNode; }
private int _trainTrainingCorrectClass; // Number of training examples #endregion Fields #region Constructors public DecisionTreeNode(DecisionTreeNode parent, String label, Object[] arcLabels, AttributeMask mask) { if (label == null || mask == null) throw new Exception("Label or attribute mask is null."); Parent = parent; NodeLabel = label; ArcLabels = arcLabels; AttMask = mask; // The number of possible children for the node is // equal to the number of values for this attribute. // If the arc label array was null, then this is // a leaf node that has no children. if (arcLabels != null) Children = new DecisionTreeNode[ArcLabels.Length]; // The node is initially unflagged. NodeFlag = -2; }
/** * Attaches the supplied node at specified child position. * The method does not check to see if a node already * exists at the insertion position. * If this node is a leaf node, the method has no effect. * * @throws IndexOutOfBoundsException If the supplied * child number is less than 0 or greater * than the number of possible children (which * is equivalent to the number of attribute * values) at this node. */ public void setChild(int childNum, DecisionTreeNode node) { if (isLeaf()) return; if (childNum < 0 || childNum > (Children.Length - 1)) throw new Exception("Cannot add child at position " + childNum + "."); Children[childNum] = node; }
/** * Searches through the current node's list of children * and attempts to locate a child that matches the * supplied child. If a match is found, the reference * to the child is removed, leaving a vacant arc. */ public void removeChild(DecisionTreeNode child) { // Search through the list of children, looking for a match. for (int i = 0; i < Children.Length; i++) { if (Children[i] == child) { Children[i] = null; return; } } }
/** * Returns the position of the supplied child beneath * the parent node. If this node is a leaf, or the * supplied node is not a child of this node, the method * returns -1. * * @param child The 'potential' child of this node. * * @return The position of the supplied child beneath * this node, or -1 if this is a leaf node * or the node is not a parent of the * child. */ public int getChildPosition(DecisionTreeNode child) { if (this.isLeaf()) return -1; for (int i = 0; i < Children.Length; i++) if (Children[i] == child) return i; return -1; }
/** * 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 * learning algorithm. Given a parent node and an arc * number, the method will attach a new decision 'sub'-tree * below the parent node. * * @param parent The parent node for the new decision tree. * * @param arcNum The arc number (or path) along which the * new subtree will be attached. * * @return true if an entire subtree was successfully added, * false otherwise. */ public bool learnDT(DecisionTreeNode parent, int arcNum) { AttributeMask mask; if (parent == null) { // We have to add at the root. mask = new AttributeMask(DatasetUse.getNumAttributes()); } else { mask = new AttributeMask(parent.getMask()); // Mask off the specified arc number. try { mask.mask(DatasetUse.getAttributePosition(parent.getLabel()), arcNum); } catch (Exception e) { //e.printStackTrace(); return false; } } // Now, classify the examples at the current position. int[] conclusion = new int[8]; int result = classifyExamples(mask, conclusion, null, null, null); Attribute target = DatasetUse.getTargetAttribute(); int numTargetVals = target.getNumValues(); String label; if (result == DATASET_EMPTY) { // If no examples reach our current position // we add a leaf with the most common target // classfication for the parent node. // Save testing results. int numTestingExamplesReachHere = conclusion[5]; int bestTestingTargetIndex = conclusion[4]; int numTestingExamplesCorrectClass = conclusion[6]; int numTrainingExamplesCorrectClass = conclusion[7]; classifyExamples(parent.getMask(), conclusion, null, null, null); try { label = target.getAttributeValueByNum(conclusion[0]); } catch (Exception e) { return false; } // We have to grab the counts again for the testing data... int[] currTestingCounts = new int[target.getNumValues()]; getExampleCounts(mask, DatasetUse.getTestingExamples(), currTestingCounts, null); // Mask target value and add a leaf to the tree. mask.mask(0, conclusion[0]); DecisionTreeNode node = Tree.addLeafNode(parent, arcNum, label, mask, 0, conclusion[0], 0, currTestingCounts[conclusion[0]], numTestingExamplesReachHere, bestTestingTargetIndex, numTestingExamplesCorrectClass, numTrainingExamplesCorrectClass); return true; } if (result == DATASET_IDENT_CONCL) { // Pure result - we can add a leaf node with the // correct target attribute value. try { label = target.getAttributeValueByNum(conclusion[0]); } catch (Exception e) { //e.printStackTrace(); return false; } // Mask target value and add a leaf to the tree. mask.mask(0, conclusion[0]); DecisionTreeNode node = Tree.addLeafNode(parent, arcNum, label, mask, conclusion[1], conclusion[0], conclusion[2], conclusion[3], conclusion[5], conclusion[4], conclusion[6], conclusion[7]); return true; } // Mixed conclusion - so we have to select // an attribute to split on, and then build a // new internal node with that attribute. // First, generate statistics - this may take awhile. int[] nodeStats = new int[numTargetVals]; List<Attribute> availableAtts = generateStats(mask, nodeStats); if (availableAtts.Count == 0) { // No attributes left to split on - so use // the most common target value at the current position. try { label = target.getAttributeValueByNum(conclusion[0]); } catch (Exception e) { //e.printStackTrace(); return false; } mask.mask(0, conclusion[0]); DecisionTreeNode node = Tree.addLeafNode(parent, arcNum, label, mask, conclusion[1], conclusion[0], conclusion[2], conclusion[3], conclusion[5], conclusion[4], conclusion[6], conclusion[7]); return true; } // Choose an attribute, based on the set of // available attributes. List<double> results = new List<double>(); Attribute att = chooseAttribute(availableAtts, nodeStats, results); int attPos; try { attPos = DatasetUse.getAttributePosition(att.getName()); } catch (Exception e) { //e.printStackTrace(); return false; } DecisionTreeNode newParent = Tree.addInternalNode(parent, arcNum, attPos, att, mask, conclusion[1], conclusion[0], conclusion[2], conclusion[3], conclusion[5], conclusion[4], conclusion[6], conclusion[7]); // Now, recursively decend along each branch of the new node. for (int j = 0; j < newParent.getArcLabelCount(); j++) { // Recursive call. if (!learnDT(newParent, j)) return false; } return true; }
public void ExtractRulesFromDTTree(DecisionTreeNode currentRoot) { if (currentRoot.isLeaf()) { BackTrackRule(currentRoot); } else { DecisionTreeNode temp = currentRoot; for (int i = 0; i < temp.Children.Length; i++) { currentRoot = temp.Children[i]; ExtractRulesFromDTTree(currentRoot); } } }
private void BackTrackRule(DecisionTreeNode currentNode) { string rule; string[] Temp = {"IF", "AND","THEN"}; DecisionTreeNode tempParentNode = currentNode; DecisionTreeNode tempCurrentNode = currentNode; Rule newRule = new Rule(); // Xây dựng luật rule = Temp[2] + " \"" + DatasetUse.Attributes[0].Name + "\" = " + "\'"+ currentNode.NodeLabel +"\'"; newRule.AddAttributeTarget(DatasetUse.Attributes[0].Name, currentNode.NodeLabel); while (tempCurrentNode.Parent != null) { tempParentNode = tempCurrentNode.Parent; // Kiểm tra đúng cha con int i; for( i = 0 ; i < tempParentNode.Children.Length; i++) { if(tempParentNode.Children[i] == tempCurrentNode) { break; } } rule = Temp[1] + " \"" + tempParentNode.NodeLabel + "\" = \'" + tempParentNode.ArcLabels[i] + "\' " + rule; newRule.AddAttributeCondition(tempParentNode.NodeLabel , tempParentNode.ArcLabels[i].ToString()); tempCurrentNode = tempParentNode; } rule = rule.Substring(rule.IndexOf(Temp[1]) + Temp[1].Length); rule = Temp[0] + rule; newRule.SetRule(rule); newRule.AttributeConditions.Reverse(); newRule.AttributeConditionValues.Reverse(); newRule.AttributeTargets.Reverse(); newRule.AttributeTargetValues.Reverse(); // Thêm một luật mới vào danh sách các luật rút ra ListRules.ListRules.Add(newRule); }
/** * 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; }
/// <summary> /// Sets the given node's flag value. This method exist /// here in order to allow the tree to notify /// TreeChangeListeners that may be tracking the state of /// the tree. public void flagNode(DecisionTreeNode node, int flagValue) { node.setFlag(flagValue); }
/** * 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--; }
/** * Prunes off the subtree starting at the supplied root. * * @param pruneRoot The root of the subtree to remove. */ public void pruneSubtree(DecisionTreeNode pruneRoot) { if (pruneRoot == null) { return; } // Detach the root node of the subtree. pruneRoot.detach(); // Once a node has been removed, the tree is no longer complete. Complete = false; // Now, tell the tree to remove all the // node's children. recursiveRemoveSubtree(pruneRoot); }
/// <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; }
/// <summary> /// Attaches a new leaf node to the supplied node, along /// the specified arc. /// </summary> public DecisionTreeNode addLeafNode(DecisionTreeNode parent, int arcNum, String label, AttributeMask mask, int numTrainingExamplesReachHere, int bestTrainingTargetIndex, int numTrainingEgsCorrectClassUsingBestTrainingIndex, int numTestingEgsCorrectClassUsingBestTrainingIndex, int numTestingExamplesReachHere, int bestTestingTargetIndex, int numTestingEgsCorrectClassUsingBestTestingIndex, int numTrainingEgsCorrectClassUsingBestTestingIndex) { // Create new leaf node. DecisionTreeNode leaf = new DecisionTreeNode(parent, label, null, mask); // Set the node statistics. leaf.setTrainingStats(numTrainingExamplesReachHere, bestTrainingTargetIndex, numTrainingEgsCorrectClassUsingBestTrainingIndex, numTestingEgsCorrectClassUsingBestTrainingIndex); leaf.setTestingStats(numTestingExamplesReachHere, bestTestingTargetIndex, numTestingEgsCorrectClassUsingBestTestingIndex, numTrainingEgsCorrectClassUsingBestTestingIndex); // Update the tree statistics. TrainingCorrect += numTrainingEgsCorrectClassUsingBestTrainingIndex; TestingCorrect += numTestingEgsCorrectClassUsingBestTrainingIndex; // Now, attach the new leaf to the supplied node. if (parent != null) parent.setChild(arcNum, leaf); // Add a reference to the new node to the node list. Nodes.Add(leaf); // Determine if the tree is complete. if (findIncompleteNode((DecisionTreeNode)Nodes[0], new int[1]) == null) { Complete = true; } return leaf; }