예제 #1
0
        /// <summary>
        /// This version starts with a SOM, then potentially splits the largest node and/or gathers the smallest nodes into a single
        /// </summary>
        /// <returns></returns>
        public static SOMResult Train(ISOMInput[] inputs, SOMRules rules, bool isDisplay2D)
        {
            SOMResult result = SelfOrganizingMaps.TrainSOM(inputs, rules, isDisplay2D);

            if (result.Nodes.Length == 0)
            {
                return(result);
            }
            else if (result.Nodes.Length == 1)
            {
                #region kmeans single node

                if (inputs.Length < 20)
                {
                    return(result);
                }

                return(SelfOrganizingMaps.TrainKMeans(inputs, 5, true));

                #endregion
            }

            var categorized = GetSOM_SplitMerge_Categorize(result);

            List <SOMNode>     nodes     = new List <SOMNode>();
            List <ISOMInput[]> newInputs = new List <ISOMInput[]>();

            foreach (NodeCombo set in UtilityCore.Iterate(categorized.kmeans, categorized.keep))        // UtilityCore.Iterate gracefully skips nulls
            {
                nodes.Add(set.Node);
                newInputs.Add(set.Inputs);
            }

            if (categorized.remaining != null)
            {
                nodes.Add(new SOMNode()
                {
                    Position = MathND.GetCenter(categorized.remaining.Select(o => o.Node.Position)),
                    Weights  = MathND.GetCenter(categorized.remaining.Select(o => o.Node.Weights)),
                });

                newInputs.Add(categorized.remaining.
                              SelectMany(o => o.Inputs).
                              ToArray());
            }

            return(new SOMResult(nodes.ToArray(), newInputs.ToArray(), false));
        }
예제 #2
0
        /// <summary>
        /// This overload does an initial training, then recurses on any node that has too wide of a range of values
        /// </summary>
        /// <remarks>
        /// This method is a bit of a failure.  Sometimes it works, but other times it just runs without fixing anything
        /// </remarks>
        /// <param name="maxSpreadPercent">
        /// Spread is an input's distance from the center of all inputs.  The percent is a node's max distance divided by all node's max distance.
        /// .65 to .75 is a good value to use (smaller values will chop up into more nodes)
        /// </param>
        public static SOMResult TrainSOM(ISOMInput[] inputs, SOMRules rules, double maxSpreadPercent, bool isDisplay2D, bool returnEmptyNodes = false)
        {
            const int MININPUTSFORSPLIT = 4;

            // Get the initial result
            SOMResult result = TrainSOM(inputs, rules, isDisplay2D, returnEmptyNodes);

            #region Divide large nodes

            double totalSpread = GetTotalSpread(inputs.Select(o => o.Weights));

            int infiniteLoop = 0;

            while (infiniteLoop < 50)     // if it exceeds this, just use whatever is there
            {
                // Split up nodes that have too much variation (image's distance from average)
                var reduced = Enumerable.Range(0, result.Nodes.Length).
                              AsParallel().
                              Select(o => SplitNode(o, result, MININPUTSFORSPLIT, maxSpreadPercent, totalSpread, rules)).
                              ToArray();

                if (reduced.All(o => !o.Item1))
                {
                    // No changes were needed this pass
                    break;
                }

                SOMNode[] reducedNodes = reduced.
                                         SelectMany(o => o.Item2).
                                         ToArray();

                // Rebuild result
                ISOMInput[][] imagesByNode = SelfOrganizingMaps.GetInputsByNode(reducedNodes, inputs);
                result = new SOMResult(reducedNodes, imagesByNode, false);

                result = SelfOrganizingMaps.RemoveZeroNodes(result);

                infiniteLoop++;
            }

            #endregion

            // Inject positions into the nodes
            InjectNodePositions2D(result.Nodes);        //TODO: Look at isDisplay2D
            result = ArrangeNodes_LikesAttract(result);

            return(result);
        }
예제 #3
0
        private static (NodeCombo[] kmeans, NodeCombo[] keep, NodeCombo[] remaining) GetSOM_SplitMerge_Categorize(SOMResult result)
        {
            NodeCombo[] nodes = Enumerable.Range(0, result.Nodes.Length).
                                Select(o => new NodeCombo()
            {
                Node = result.Nodes[o], Inputs = result.InputsByNode[o]
            }).
                                OrderByDescending(o => o.Inputs.Length).
                                ToArray();

            // First node is a potential kmeans split
            NodeCombo kmeans    = null;
            int       keepStart = 0;

            if (nodes[0].Inputs.Length.ToDouble() / nodes[1].Inputs.Length.ToDouble() > 10)
            {
                kmeans    = nodes[0];
                keepStart = 1;
            }

            NodeCombo[] kmeansSplit = null;
            if (kmeans != null)
            {
                SOMResult result2 = SelfOrganizingMaps.TrainKMeans(kmeans.Inputs, 4, true);

                kmeansSplit = Enumerable.Range(0, result2.Nodes.Length).
                              Select(o => new NodeCombo()
                {
                    Node = result2.Nodes[o], Inputs = result2.InputsByNode[o]
                }).
                              ToArray();
            }

            // Next nodes are the ones to leave alone
            var keep     = new List <NodeCombo>();
            int?keepStop = null;

            keep.Add(nodes[keepStart]);

            for (int cntr = keepStart + 1; cntr < nodes.Length; cntr++)
            {
                if (nodes[keepStart].Inputs.Length.ToDouble() / nodes[cntr].Inputs.Length.ToDouble() > 10)
                {
                    keepStop = cntr;
                    break;
                }

                keep.Add(nodes[cntr]);
            }

            // Everything else gets merged into a single node
            NodeCombo[] remaining = null;
            if (keepStop != null)
            {
                remaining = Enumerable.Range(keepStop.Value, result.Nodes.Length - keepStop.Value).
                            Select(o => nodes[o]).
                            ToArray();
            }

            if (remaining == null && keep.Count > 0 && kmeans != null)
            {
                int sumKeep = keep.Sum(o => o.Inputs.Length);

                int smallestKmeans = kmeansSplit.
                                     Select(o => o.Inputs.Length).
                                     OrderBy(o => o).
                                     First();

                if (smallestKmeans.ToDouble() / sumKeep.ToDouble() > 10)
                {
                    remaining = keep.ToArray();
                    keep.Clear();
                }
            }

            return(kmeansSplit, keep.ToArray(), remaining);
        }