예제 #1
0
        /// <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;
        }
예제 #11
0
 /// <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);
 }
예제 #12
0
        /**
           * 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--;
        }
예제 #13
0
        /**
         * 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);
        }
예제 #14
0
 /// <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);
 }
예제 #15
0
        /// <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;
        }
예제 #16
0
        /// <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;
        }