public int Spawn(LabeledVector <L> lvector, DistanceDelegate measure, ParallelStrategy parallelStrategy, bool spawnUsingLDA) { int nSpawnCount = 0; if (this.DoesEncloseVector(lvector, measure)) { if (parallelStrategy == ParallelStrategy.Multithreaded) { List <Task> lstSpawnThreads = new List <Task>(); foreach (SphereEx <L> child in this.Children) { lstSpawnThreads.Add(Task.Factory.StartNew(c => { nSpawnCount += ((SphereEx <L>)c).Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); }, child, TaskCreationOptions.LongRunning)); } Task.WaitAll(lstSpawnThreads.ToArray()); } else { foreach (SphereEx <L> child in this.Children) { nSpawnCount += child.Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); } } if (!spawnUsingLDA) { #region Regular Spawn if (!this.Label.Equals(lvector.Label) && !Vector.EqualsEx(this, lvector) && !this.DoesAtLeastOneChildEncloseVector(lvector, measure)) { SphereEx <L> child = new SphereEx <L>(this.Radius - measure(this, lvector), lvector); child.LDAVectors.Add(child.Label, new List <KeyValuePair <int, LabeledVector <L> > >() { new KeyValuePair <int, LabeledVector <L> >(0, lvector) }); this.AddChild(child); nSpawnCount++; } #endregion } else { #region LDA Spawn // Note that LDA Spawn doesn't work with SquaredEuclideanDistance. I may -- in the future -- do an additional check to see if the vector is contained by the hypersphere with SquaredEuclideanDistance and EuclideanDistance. If so, this will work. Until then, I'll leave it as is. if (this.Children.Any()) { // Next if-statement a duplicate of the region "Regular Spawn" // Remember, LDA is only applied at nodes with no children (leaf) if (!this.Label.Equals(lvector.Label) && !Vector.EqualsEx(this, lvector) && !this.DoesAtLeastOneChildEncloseVector(lvector, measure)) { SphereEx <L> child = new SphereEx <L>(this.Radius - measure(this, lvector), lvector); child.LDAVectors.Add(child.Label, new List <KeyValuePair <int, LabeledVector <L> > >() { new KeyValuePair <int, LabeledVector <L> >(0, lvector) }); this.AddChild(child); nSpawnCount++; } } else { bool bContains = false; List <KeyValuePair <int, LabeledVector <L> > > lst; if (!this.LDAVectors.TryGetValue(lvector.Label, out lst)) { this.LDAVectors.Add(lvector.Label, (lst = new List <KeyValuePair <int, LabeledVector <L> > >())); } else { bContains = lst.Any(kvp => Vector.EqualsEx(kvp.Value, lvector)); } if (!bContains) { lst.Add(new KeyValuePair <int, LabeledVector <L> >(this.LDAVectors.Sum(kvp => kvp.Value.Count), lvector)); if (this.LDAVectors.Keys.Count == 2) { #region If the sphere contains exactly 2 classes, do the following... bool bIsSeparable = false; DiscriminantEx <L> discriminant = null; try { bIsSeparable = LDAEx.IsCompletelySeparatedWithDiscriminant(this.LDAVectors.ElementAt(0).Value.Select(kvp => kvp.Value).ToList(), this.LDAVectors.ElementAt(1).Value.Select(kvp => kvp.Value).ToList(), out discriminant); } catch { // Just consume, leaving bIsSeparable = false } if (!bIsSeparable) { bool spawnedAtLeastOnce = false; List <LabeledVector <L> > lstCache = this.LDAVectors.SelectMany(kvp => kvp.Value).OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToList(); this.LDAVectors.Clear(); this.DiscriminantEx = null; foreach (LabeledVector <L> v in lstCache) { if (!spawnedAtLeastOnce) { int count = this.Spawn(v, measure, ParallelStrategy.SingleThreaded, false); nSpawnCount += count; spawnedAtLeastOnce = count > 0; } else { nSpawnCount += this.Spawn(v, measure, ParallelStrategy.SingleThreaded, true); } } } else { if (discriminant != null) { this.DiscriminantEx = discriminant; } } #endregion } else if (this.LDAVectors.Keys.Count > 2) { #region If the sphere contains 3 or more classes, do the following... bool spawnedAtLeastOnce = false; List <LabeledVector <L> > lstCache = this.LDAVectors.SelectMany(kvp => kvp.Value).OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToList(); this.LDAVectors.Clear(); this.DiscriminantEx = null; foreach (LabeledVector <L> v in lstCache) { if (!spawnedAtLeastOnce) { int count = this.Spawn(v, measure, ParallelStrategy.SingleThreaded, false); nSpawnCount += count; spawnedAtLeastOnce = count > 0; } else { nSpawnCount += this.Spawn(v, measure, ParallelStrategy.SingleThreaded, true); } } #endregion } // Note: If this.LDAVectors.Keys.Count == 1, don't do anything additional. } } #endregion } } return(nSpawnCount); }
public int Spawn(LabeledVector <L> lvector, DistanceDelegate measure, ParallelStrategy parallelStrategy, bool spawnUsingLDA) { int nSpawnCount = 0; if (this.DoesEncloseVector(lvector, measure)) { if (parallelStrategy == ParallelStrategy.Multithreaded) { List <Task> lstSpawnThreads = new List <Task>(); foreach (SphereEx <L> child in this.Children) { lstSpawnThreads.Add(Task.Factory.StartNew(c => { nSpawnCount += ((SphereEx <L>)c).Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); }, child, TaskCreationOptions.LongRunning)); } Task.WaitAll(lstSpawnThreads.ToArray()); } else { foreach (SphereEx <L> child in this.Children) { nSpawnCount += child.Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); } } if (!spawnUsingLDA) { #region Regular Spawn if (!this.Label.Equals(lvector.Label) && !Vector.EqualsEx(this, lvector) && !this.DoesAtLeastOneChildEncloseVector(lvector, measure)) { this.AddChild(new SphereEx <L>(this.Radius - measure(this, lvector), lvector)); nSpawnCount++; } #endregion } else { #region LDA Spawn if (!this.DoesAtLeastOneChildEncloseVector(lvector, measure)) // Don't care about label as well as location of lvector { bool bContains = false; List <LabeledVector <L> > lst; if (!this.LDAVectors.TryGetValue(lvector.Label, out lst)) { this.LDAVectors.Add(lvector.Label, (lst = new List <LabeledVector <L> >())); } else { bContains = lst.Any(v => Vector.EqualsEx(v, lvector)); } if (!bContains) { lst.Add(lvector); if (this.LDAVectors.Keys.Count == 2 && !this.Children.Any()) { #region If the sphere contains exactly 2 classes, do the following... // 1. Create another discriminant // 2. If the discrimant CANNOT separate the classes, do the following... // 2a. Remove the lvector from the list it was just added to // 2b. Create new children from the classes // 2c. Try to spawn the presented LVector in the NEW children // 2d. Clear the dictionary, LDAVectors // 2e. If you can't spawn WITHIN the NEW children, add the vector to the this.LDAVectors // 3. Else (If the discrimant CAN separate the classes), do nothing but assign the property. bool bIsSeparable = false; DiscriminantEx <L> discriminant = null; try { //bIsSeparable = LDA.IsCompletelySeparatedWithDiscriminant(this.LDAVectors.ElementAt(0).Value, this.LDAVectors.ElementAt(1).Value, this, out discriminant); bIsSeparable = LDAEx.IsCompletelySeparatedWithDiscriminant(this.LDAVectors.ElementAt(0).Value, this.LDAVectors.ElementAt(1).Value, out discriminant); } catch { // Just consume, leaving bIsSeparable = false } if (!bIsSeparable) { lst.RemoveAt(lst.Count - 1); // Faster than .Remove() as I am not linearly searching List <SphereEx <L> > lstNewChildren = new List <SphereEx <L> >(); foreach (KeyValuePair <L, List <LabeledVector <L> > > kvp in this.LDAVectors) { if (kvp.Value.Any() && !kvp.Key.Equals(this.Label)) { //Vector vectorCentroid = Vector.Centroid(kvp.Value); //if (!Vector.EqualsEx(this, vectorCentroid)) //{ // SphereEx<L> child = new SphereEx<L>(this.Radius - measure(this, vectorCentroid), kvp.Key, (IVector)vectorCentroid); // this.AddChild(child); // lstNewChildren.Add(child); // nSpawnCount++; //} } } //bool hasSpawned = false; //foreach (SphereEx<L> child in lstNewChildren) //{ // if (child.DoesEncloseVector(lvector, measure)) // { // nSpawnCount += child.Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); // hasSpawned = true; // } //} this.LDAVectors.Clear(); //if (!hasSpawned) //{ // this.LDAVectors.Add(lvector.Label, new List<LabeledVector<L>>() { lvector }); //} } else { this.DiscriminantEx = discriminant; } #endregion } else if (this.LDAVectors.Keys.Count > 2) { #region If the sphere contains 3 or more classes, do the following... // 1. Create children from the OLDER label-sets // 2. Try to spawn the presented LVector IN the NEW children just created // 3. Clear the dictionary, LDAVectors // 4. If you can't spawn WITHIN the NEW children, add the vector to this.LDAVectors List <SphereEx <L> > lstNewChildren = new List <SphereEx <L> >(); foreach (KeyValuePair <L, List <LabeledVector <L> > > kvp in this.LDAVectors) { if (!kvp.Key.Equals(lvector.Label)) { Vector vectorCentroid = Vector.Centroid(kvp.Value); if (!Vector.EqualsEx(this, vectorCentroid)) { SphereEx <L> child = new SphereEx <L>(this.Radius - measure(this, vectorCentroid), kvp.Key, (IVector)vectorCentroid); this.AddChild(child); lstNewChildren.Add(child); nSpawnCount++; } } } //bool hasSpawned = false; //foreach (SphereEx<L> child in lstNewChildren) //{ // if (child.DoesEncloseVector(lvector, measure)) // { // nSpawnCount += child.Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); // hasSpawned = true; // } //} this.LDAVectors.Clear(); //if (!hasSpawned) //{ // this.LDAVectors.Add(lvector.Label, new List<LabeledVector<L>>() { lvector }); //} #endregion } // Note: If this.LDAVectors.Keys.Count == 1, don't do anything additional. } } #endregion } } return(nSpawnCount); }
static void Main(string[] args) { ParallelStrategy strategy = ParallelStrategy.SingleThreaded; bool bIsLDA = true; RHCLib.DistanceDelegate measure = RHCLib.Vector.EuclideanDistance; int nQueuePartitions; // = 2; int nSlotsPerQueuePartition; // = 40; // If using the example, the total queue is 80 string strFile; bool bIsSTRHC1; // STRHC1 = Small dataset (40 gestures), !STRHC1 = Large dataset (300 gestures) #region Parameters if (args.Length != 5) { System.Console.WriteLine("Syntax: STRHC.EXE <Queue Partitions> <Slots Per Partition> <T|F IsLDA> <T|F IsSingledThreaded> <1 = STRHC1 (40 gestures) | 2 = STRHC2 (300 gestures)>"); return; } else { bool singleThreaded; int dataset; if (!int.TryParse(args[0], out nQueuePartitions) || !int.TryParse(args[1], out nSlotsPerQueuePartition) || !bool.TryParse(args[2], out bIsLDA) || !bool.TryParse(args[3], out singleThreaded) || !int.TryParse(args[4], out dataset) || !(new int[] { 1, 2 }.Contains(dataset))) { System.Console.WriteLine("Syntax: STRHC.EXE <Queue Partitions> <Slots Per Partition> <T|F IsLDA> <T|F IsSingledThreaded> <1 = STRHC1 (40 gestures) | 2 = STRHC2 (300 gestures)>"); return; } switch (dataset) { case 1: bIsSTRHC1 = true; strFile = @".\Data1.dat"; break; case 2: bIsSTRHC1 = false; strFile = @".\Data.dat"; break; default: throw new NotImplementedException(); } strategy = singleThreaded ? ParallelStrategy.SingleThreaded : ParallelStrategy.Multithreaded; } #endregion System.Diagnostics.Process.GetCurrentProcess().PriorityClass = System.Diagnostics.ProcessPriorityClass.High; using (System.IO.StreamWriter sw = new System.IO.StreamWriter(string.Format(@".\{0:yyyy-MM-ddTHHmmss} {1} {2}x{3} {4} {5}", DateTime.Now, bIsSTRHC1 ? "STRHC1" : "STRHC2", nQueuePartitions, nSlotsPerQueuePartition, bIsLDA ? "LDA" : "NoLDA", measure == Vector.SquaredEuclideanDistance ? "SqEuc" : "Euc"), false, Encoding.UTF8)) { Dictionary <int, Tuple <Dictionary <string, List <List <double[]> > >, Dictionary <string, List <List <double[]> > > > > dict; using (System.IO.FileStream fs = new System.IO.FileStream(strFile, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { BinaryFormatter bf = new BinaryFormatter(); dict = bf.Deserialize(fs) as Dictionary <int, Tuple <Dictionary <string, List <List <double[]> > >, Dictionary <string, List <List <double[]> > > > >; } #region Convert the dictGestures into RHCLib.LabeledVectors Dictionary <int, Tuple <List <RHCLib.LabeledVector <string>[]>, List <RHCLib.LabeledVector <string>[]> > > dictGestures = new Dictionary <int, Tuple <List <LabeledVector <string>[]>, List <LabeledVector <string>[]> > >(); foreach (KeyValuePair <int, Tuple <Dictionary <string, List <List <double[]> > >, Dictionary <string, List <List <double[]> > > > > kvp in dict) { List <LabeledVector <string>[]> lstTrain = new List <LabeledVector <string>[]>(); foreach (KeyValuePair <string, List <List <double[]> > > kvpTrain in kvp.Value.Item1) { foreach (List <double[]> gesture in kvpTrain.Value) { List <LabeledVector <string> > lst = new List <LabeledVector <string> >(); foreach (double[] frame in gesture) { lst.Add(new LabeledVector <string>(kvpTrain.Key, frame)); } lstTrain.Add(lst.ToArray()); } } List <LabeledVector <string>[]> lstTest = new List <LabeledVector <string>[]>(); foreach (KeyValuePair <string, List <List <double[]> > > kvpTest in kvp.Value.Item2) { foreach (List <double[]> gesture in kvpTest.Value) { List <LabeledVector <string> > lst = new List <LabeledVector <string> >(); foreach (double[] frame in gesture) { lst.Add(new LabeledVector <string>(kvpTest.Key, frame)); } lstTest.Add(lst.ToArray()); } } dictGestures.Add(kvp.Key, new Tuple <List <LabeledVector <string>[]>, List <LabeledVector <string>[]> >(lstTrain, lstTest)); } #endregion IList <string> classes = dict.Values.First().Item1.Keys.ToList(); int nClassCount = classes.Count(); int nFeatureSpaceDimensionality = dictGestures.Values.First().Item1.First().First().Rank + (nQueuePartitions * nClassCount); foreach (KeyValuePair <int, Tuple <List <RHCLib.LabeledVector <string>[]>, List <RHCLib.LabeledVector <string>[]> > > kvp in dictGestures) { SphereEx <string> sphere = new SphereEx <string>(Sphere <string> .CreateUnitSphere(measure, nFeatureSpaceDimensionality, string.Empty)); int nEpoch = 1; int nSpawnCount; RHCLib.LabeledVector <string> lvectorAug; Program.WriteStream(sw, string.Format("Fold: {0}", kvp.Key)); System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Start(); #region Train do { nSpawnCount = 0; foreach (RHCLib.LabeledVector <string>[] gesture in kvp.Value.Item1) { double[][] queue = Program.CreateEmptyQueue(nQueuePartitions, nSlotsPerQueuePartition, nClassCount); foreach (RHCLib.LabeledVector <string> frame in gesture) { lvectorAug = Program.CreateAugmentedVector(frame, queue, nQueuePartitions, nSlotsPerQueuePartition); double[] biases = sphere.CalculateBiases(lvectorAug, measure, classes); nSpawnCount += sphere.Spawn(lvectorAug, measure, strategy, bIsLDA); Program.AdvanceQueue(queue, biases); } } Program.WriteStream(sw, string.Format(" Epoch:\t{0}\t{1}", nEpoch++, nSpawnCount)); } while (nSpawnCount > 0); watch.Stop(); Program.WriteStream(sw, string.Format("TOTAL TIME [ms] for Fold {0}: {1}", kvp.Key, watch.ElapsedMilliseconds.ToString())); Program.WriteStream(sw, string.Format("Sphere Count: {0}", sphere.SphereCount)); Program.WriteStream(sw, string.Format("Tree Height: {0}", sphere.Height)); Program.WriteStream(sw, string.Format("Epoch Count: {0}", nEpoch - 1)); #endregion #region Test Sphere <string> sphereWinner = null; int nSphereCorrect = 0; int nSphereIncorrect = 0; int nQueueCorrect = 0; int nQueueIncorrect = 0; foreach (RHCLib.LabeledVector <string>[] gesture in kvp.Value.Item2) { #region Create Empty Queue double[][] queue = new double[nQueuePartitions * nSlotsPerQueuePartition][]; for (int i = 0; i < queue.Length; i++) { queue[i] = new double[nClassCount]; } #endregion watch.Reset(); watch.Start(); foreach (RHCLib.LabeledVector <string> frame in gesture) { lvectorAug = Program.CreateAugmentedVector(frame, queue, nQueuePartitions, nSlotsPerQueuePartition); sphereWinner = sphere.Recognize(lvectorAug, measure, strategy); double[] rgBiases = sphere.CalculateBiases(lvectorAug, measure, classes); Program.AdvanceQueue(queue, rgBiases); } watch.Stop(); Program.WriteStream(sw, string.Format("[{0} fold] Time to recognize [in ms]: {1}", kvp.Key, watch.ElapsedMilliseconds)); #region Sphere Winner Program.WriteStream(sw, string.Format("Actual: {0}\t\tSphere Recognized: {1}", gesture.First().Label, sphereWinner.Label)); if (sphereWinner.Label == gesture.First().Label) { nSphereCorrect++; } else { nSphereIncorrect++; } #endregion #region Queue Winner Dictionary <string, double> dictWinner = new Dictionary <string, double>(); foreach (string strClass in classes) { dictWinner.Add(strClass, 0.0); } // Quick and dirty Dictionary <string, double> dictQueueBreakout = new Dictionary <string, double>(); foreach (string strClass in classes) { dictQueueBreakout.Add(strClass, 0.0); } for (int i = 0; i < queue.Length; i++) { for (int j = 0; j < queue[i].Length; j++) { dictQueueBreakout[classes.ElementAt(j)] += queue[i][j]; } } string strQueueWinner = dictQueueBreakout.Aggregate((l, r) => l.Value > r.Value ? l : r).Key; Program.WriteStream(sw, string.Format("Actual: {0}\t\tQueue Recognized: {1}", gesture.First().Label, strQueueWinner)); if (strQueueWinner == gesture.First().Label) { nQueueCorrect++; } else { nQueueIncorrect++; } #endregion } Program.WriteStream(sw, string.Format("Sphere Correct: {0}\t\tSphere Incorrect: {1}\t\tSphere Percentage: {2:P}", nSphereCorrect, nSphereIncorrect, (double)nSphereCorrect / (nSphereCorrect + nSphereIncorrect))); Program.WriteStream(sw, string.Format("Queue Correct: {0}\t\tQueue Incorrect: {1}\t\tQueue Percentage: {2:P}", nQueueCorrect, nQueueIncorrect, (double)nQueueCorrect / (nQueueCorrect + nQueueIncorrect))); Program.WriteStream(sw, System.Environment.NewLine); Program.WriteStream(sw, System.Environment.NewLine); #endregion System.Console.Beep(); System.Console.Beep(); #region GC Cleanup sphere.Cleanup(); GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); // This is where you check the CLR profiler #endregion char key; do { System.Console.WriteLine("Hit 'y' to continue..."); key = System.Console.ReadKey().KeyChar; } while (key.ToString().ToUpper() != "Y"); do { System.Console.WriteLine("Hit 'y' AGAIN to continue..."); key = System.Console.ReadKey().KeyChar; } while (key.ToString().ToUpper() != "Y"); #region Serialize Sphere if (false) { using (System.IO.FileStream fs = new System.IO.FileStream(string.Format(@".\{0}-{1}-{2}-{3}.serialized", bIsSTRHC1 ? "STRHC1" : "STRHC2", nQueuePartitions, nSlotsPerQueuePartition, kvp.Key), System.IO.FileMode.Create)) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, sphere); } } #endregion } } }