コード例 #1
0
        private PartitionScore SplitOnAxis(ContinuousAxis axis, List <Episode> episodes, double[] totalWeights)
        {
            List <EvaluatedEpisode> line =
                episodes
                .AsParallel()
                .Select(episode => EvaluateEpisode(episode, axis))
                .OrderBy(x => x.Value)
                .ToList();

            int lastIndex = 0;

            double[] tacticWeightsLeft = new double[NumTactics];
            int      bestSplitPosition = -1;
            double   bestEntropy       = double.MaxValue;

            for (int i = 1; i < line.Count; ++i)
            {
                bool sameAsPrevious = i > 0 && line[i - 1].Value == line[i].Value;
                if (sameAsPrevious)
                {
                    // Cannot create a split part way through the same number
                    continue;
                }

                while (lastIndex < i)
                {
                    Episode episode = line[lastIndex++].Episode;
                    tacticWeightsLeft[(int)episode.Tactic] += episode.Weight;
                }
                double[] tacticWeightsRight = TacticEntropy.Subtract(totalWeights, tacticWeightsLeft);

                double entropy = TacticEntropy.Entropy(tacticWeightsLeft, tacticWeightsRight);

                if (entropy < bestEntropy)
                {
                    bestEntropy       = entropy;
                    bestSplitPosition = i;
                }
            }

            if (bestSplitPosition == -1)
            {
                // Unable to find split - all values must be the same
                return(null);
            }

            return(new PartitionScore {
                Partitioner = new ContinuousPartitioner(axis, line[bestSplitPosition].Value),
                LeftEpisodes = line.Take(bestSplitPosition).Select(x => x.Episode).ToList(),
                RightEpisodes = line.Skip(bestSplitPosition).Select(x => x.Episode).ToList(),
                Entropy = bestEntropy,
            });
        }
コード例 #2
0
        private DecisionNodeAccuracy OptimalLeaf(List <Episode> episodes, double[] tacticWeights)
        {
            TacticWeight bestTacticOverall =
                tacticWeights
                .Select((weight, i) => new TacticWeight {
                Tactic = (Tactic)i, Weight = weight
            })
                .MaxByOrDefault(x => x.Weight);

            if (!IsSpell(bestTacticOverall.Tactic))
            {
                return(new DecisionNodeAccuracy {
                    DecisionNode = new DecisionLeaf(bestTacticOverall.Tactic),
                    CorrectWeight = bestTacticOverall.Weight,
                });
            }

            Dictionary <HeroType, Tactic> bestTacticPerHero = new Dictionary <HeroType, Tactic>();
            double totalCorrectWeight = 0.0;

            foreach (var group in episodes.GroupBy(ep => ep.Hero.HeroType))
            {
                TacticWeight bestTacticForHero =
                    TacticEntropy.TacticFrequency(group)
                    .Select((weight, i) => new TacticWeight {
                    Tactic = (Tactic)i, Weight = weight
                })
                    .MaxByOrDefault(x => x.Weight);
                bestTacticPerHero[group.Key] = bestTacticForHero.Tactic;
                totalCorrectWeight          += bestTacticForHero.Weight;
            }

            foreach (HeroType heroType in EnumUtils.GetEnumValues <HeroType>())
            {
                if (bestTacticPerHero.GetOrDefault(heroType) == bestTacticOverall.Tactic)
                {
                    bestTacticPerHero.Remove(heroType);
                }
            }

            return(new DecisionNodeAccuracy {
                DecisionNode = new DecisionLeaf(bestTacticOverall.Tactic, bestTacticPerHero),
                CorrectWeight = totalCorrectWeight,
            });
        }
コード例 #3
0
        private static AdditionalCategory EvaluateAdditionalCategory(Enum category, IEnumerable <Episode> additionalEpisodes, double[] totalWeights, double[] rightWeights)
        {
            double[] additionalWeights = TacticEntropy.TacticFrequency(additionalEpisodes);
            double[] newRightWeights   = new double[TacticEntropy.NumTactics];
            double[] newLeftWeights    = new double[TacticEntropy.NumTactics];
            for (int i = 0; i < additionalWeights.Length; ++i)
            {
                newRightWeights[i] = rightWeights[i] + additionalWeights[i];
                newLeftWeights[i]  = totalWeights[i] - newRightWeights[i];
            }

            return(new AdditionalCategory {
                Category = category,
                AdditionalEpisodes = additionalEpisodes,
                NewRightWeights = newRightWeights,
                Entropy = TacticEntropy.Entropy(newLeftWeights, newRightWeights),
            });
        }
コード例 #4
0
        private async Task <DecisionNodeAccuracy> OptimalDecisionNode(List <Episode> episodes, double grandTotalWeight)
        {
            double[] tacticWeights = TacticEntropy.TacticFrequency(episodes);
            double   totalWeight   = tacticWeights.Sum();
            double   leafEntropy   = TacticEntropy.Entropy(tacticWeights);

            DecisionNodeAccuracy bestLeaf = OptimalLeaf(episodes, tacticWeights);

            if (bestLeaf.CorrectWeight + double.Epsilon >= totalWeight)
            {
                // We already know the perfect answer
                return(bestLeaf);
            }

            double maximumCorrectWeight = totalWeight;
            double maximumIncrease      = maximumCorrectWeight - bestLeaf.CorrectWeight;
            double requiredIncrease     = RequiredIncreaseToSplit * grandTotalWeight;

            if (maximumIncrease < requiredIncrease)
            {
                // Cannot make a big enough difference to the big picture
                return(bestLeaf);
            }



            PartitionScore partitionScore =
                _continuousLearner.Optimizers(episodes, tacticWeights)
                .Concat(_categoricalLearner.Optimizers(episodes, tacticWeights))
                .AsParallel()
                .Select(partitionEvaluator => partitionEvaluator())
                .Where(x => x != null && x.LeftEpisodes.Count > 0 && x.RightEpisodes.Count > 0)
                .MinByOrDefault(x => x.Entropy);

            if (partitionScore == null || partitionScore.Entropy >= leafEntropy)
            {
                if (partitionScore?.Entropy > leafEntropy + double.Epsilon)
                {
                    Console.WriteLine("Entropy should never increase with a split");
                }
                // No split found
                return(bestLeaf);
            }

            DecisionNodeAccuracy left = await OptimalDecisionNode(partitionScore.LeftEpisodes, grandTotalWeight);

            DecisionNodeAccuracy right = await OptimalDecisionNode(partitionScore.RightEpisodes, grandTotalWeight);

            DecisionNode splitter = new DecisionNode()
            {
                Partitioner = partitionScore.Partitioner,
                Left        = left.DecisionNode,
                Right       = right.DecisionNode,
            };
            double bestSplitCorrectWeight = left.CorrectWeight + right.CorrectWeight;
            double correctWeightIncrease  = bestSplitCorrectWeight - bestLeaf.CorrectWeight;

            if (correctWeightIncrease < requiredIncrease)
            {
                return(bestLeaf);
            }
            else
            {
                return(new DecisionNodeAccuracy {
                    DecisionNode = splitter,
                    CorrectWeight = bestSplitCorrectWeight,
                });
            }
        }