// 초기 개체를 생성하는 함수 public void Initialize() { Genome g = CreateBasicConnectedGenome(); for (int i = 0; i < Population; ++i) { AddToSpecies(g.Clone()); } }
// 적합도를 평가하고 이를 바탕으로 선택, 교차, 변이를 통해서 새로운 세대를 생성하는 함수 // // 다음 순서로 작동함 // 1) 모든 개체의 적합도를 판단함 // 2) Staleness가 높은 종을 판단하여 제거함 // 3) 적합도를 바탕으로 하여 자식을 만듦. 이때 교차와 변이 모두를 통해서 자식을 만들기도 하며, 변이만으로 자식을 생성하기도 함. // - 이때, 자식을 만들 확률은 종과 개체의 적합도에 따라서 다름. 즉 개체의 평균 적합도가 높은 종은 더 많은 자식을 만들고, 또 종 내에서 적합도가 높은 개체는 더 많은 자식을 만듦) // 4) 각 종마다 SurviveRate에 해당하는 개체만 남기고 나머지는 제거함 // 5) 새로운 자식들을 기존의 종에 추가함. public void Evaluate() { TopFitness = long.MinValue; BestGenome = null; if (Writer != null) { Writer.Record(); } // 과정 1) while (true) { Genome g = GetCurrentGenome(); g.GenerateNetwork(); // 적합도 평가 insw.Reset(); insw.Start(); g.Fitness = Agent.Evaluate(g, DataDictionary); g.Checked = true; insw.Stop(); g.ExecutionTime = insw.ElapsedMilliseconds; g.AdjustedFitness = (double)g.Fitness / Species[speciesCursor].Genomes.Count; Species[speciesCursor].AddFitness(g.AdjustedFitness); if (g.Fitness > TopFitness) { TopFitness = g.Fitness; BestGenome = g; } bool newGen = false; while (IsFitnessMeasured()) { if (NextGenome()) { newGen = true; break; } } if (newGen) { break; } } if (Generation != 0 && Generation % DisplayTop == 0) { Agent.Display(BestGenome, DataDictionary); } if (Writer != null) { Writer.Write(BestGenome, DataDictionary); if (WritePlayData) { Writer.WriteGene(BestGenome, DataDictionary); } if (WriteSpecies) { Writer.WriteSpecies(Species, DataDictionary); } } DataDictionary.Clear(); sw.Reset(); sw.Start(); // for debug Console.Write("Generation:" + Generation); Console.Write("\tHighest Fitness:" + TopFitness); Console.WriteLine("\tAmount Species:" + Species.Count); ++Generation; List <Genome> child = new List <Genome>(); int survived = 0; // 과정 2) for (int i = Species.Count - 1; i >= 0; --i) { Species s = Species[i]; Genome prevBest = s.Genomes[0]; s.Genomes.Sort((a, b) => a.Fitness.CompareTo(b.Fitness)); Genome currentBest = s.Genomes[s.Genomes.Count - 1]; // 종에서 기존의 가장 우수한 개체보다 더 적합도가 높은 개체가 등장하지 못했을 경우 if (prevBest == currentBest) { ++s.Staleness; // staleness가 일정 수준 이상이고, 전체에서 가장 우수한 개체가 속한 종이 아니라면, 종을 제거함 if (s.Staleness > Staleness && BestGenome != currentBest) { Species.RemoveAt(i); } } else // 종에서 기존의 가장 우수한 개체보다 더 적합도가 높은 개체가 등장할 경우 { s.Staleness = 0; } } // surviveRate를 바탕으로 생성할 자식 개체의 수를 계산 if (SurviveRate == 0) { survived = Species.Count; } else { foreach (Species s in Species) { survived += (int)Math.Ceiling(s.Genomes.Count * SurviveRate); } } // 과정 3) while (survived + child.Count < Population) { Genome c; // 교차 및 변이로 자식 생성 if (random.NextDouble() < BreedChance) { Species s = SelectSpecies(random); Genome g1 = SelectGenome(s, random); Genome g2 = SelectGenome(s, random); if (g1.Fitness > g2.Fitness) { c = Genome.Crossover(g1, g2, random); } else { c = Genome.Crossover(g2, g1, random); } c.Mutate(random); } else // 변이로만 자식 생성 { Species s = SelectSpecies(random); Genome g1 = SelectGenome(s, random); c = g1.Clone(); c.Mutate(random); } child.Add(c); } // 과정 4) SurviveRate에 따라서 기존의 종을 제거함 foreach (Species s in Species) { int cut = 0; if (SurviveRate == 0) { cut = s.Genomes.Count - 1; } else { cut = s.Genomes.Count - (int)Math.Ceiling(s.Genomes.Count * SurviveRate); } s.ResetFitness(); s.Genomes.RemoveRange(0, cut); s.Genomes[0].Fitness = 0; s.Genomes[0].Checked = false; // to fix; } // 과정 5) 새로 만들어진 자식 종을 추가함 foreach (Genome g in child) { AddToSpecies(g); } sw.Stop(); if (Writer != null) { Writer.Set("Breed", sw.ElapsedMilliseconds); } }