4

私は次のことを知りたいです:値エンコーディングを使用して、多様性の高い染色体の初期世代を効果的に作成するにはどうすればよいですか?1つの方法はグリッドの初期化ですが、遅すぎます。

これまで、値のエンコードでランダムな値を選択するために.NETのRandomクラスを使用してきましたが、値は均一に分散されていますが、そのような染色体から計算された適応度関数の値はそうではありません。染色体初期化のコードは次のとおりです。

 public Chromosome(Random rand) 
        {
            Alele = new List<double>();
            for (int i = 0; i < ChromosomeLength; i++)
            {
                Alele.Add(rand.NextDouble() * 2000 - 1000);
            }
        }

そこで、ランダムに作成された新しい染色体(上のコード)から適応度を計算する関数を開発しました。適応度がすでに染色体のリストにある他の染色体と類似している場合、新しい染色体がランダムに作成され、彼の適応度が計算され、このプロセスが繰り返されます。彼の体力がすでにリストにあるものと十分に異ならないまで。

この部分のコードは次のとおりです。

private bool CheckSimilarFitnes(List<Chromosome> chromosome, Chromosome newCandidate) 
    {
     Boolean flag=false;
     double fitFromList, fitFromCandidate;
     double fitBigger,fitSmaller;

     foreach (var listElement in chromosome)
      {  
      fitFromList = listElement.CalculateChromosomeFitness(listElement.Alele);
      fitFromCandidate = newCandidate.CalculateChromosomeFitness(newCandidate.Alele);
      fitBigger = fitFromList >= fitFromCandidate ? fitFromList : fitFromCandidate;
      fitSmaller =  fitFromList < fitFromCandidate ? fitFromList : fitFromCandidate;

            if ((fitFromList / fitFromCandidate) < 1.5) 
                return false
      }

     else return true;

    }

しかし、私がリストに持っている染色体が多ければ多いほど、新しい染色体を追加するのに時間がかかり、すでにそこにある他の染色体とは十分に異なる適応度があります。

それで、このグリッドの初期化をより速くする方法はありますか、このような80の染色体を作るのに数日かかりますか?

4

5 に答える 5

2

ここでの基本的な問題は、ほとんどのランダムに生成された染色体が似たような適応度を持っているということですよね? それはいいです; アイデアは、最初の染色体が大きく異なる適応度を持つことではありません。染色体自体が異なるためであり、おそらくそうです。実際、まだアルゴリズムを実行していないため、最初の世代のほとんどの初期適合度はゼロに近いと予想する必要があります。

コードが非常に遅い理由は次のとおりです。最初の候補者がひどく、基本的にフィットネスがゼロだとしましょう。2 番目が 1.5 倍異なる必要がある場合、それは実際には 1.5 倍良くなければならないことを意味します。次に、次のものはそれよりも 1.5​​ 倍優れている必要があり、80 まで続きます。つまり、実際に行っているのは、完全にランダムな染色体を生成し、それらを自分の持っているものと比較することによって、ますます優れた染色体を探していることです。進行状況をログに記録すると、次の候補を見つけるのにますます時間がかかることがわかります。本当に良い染色体を見つけるのは難しいからです. しかし、より良い染色体を見つけることが GA の目的です。基本的に、あなたが行ったことは、実際に最適化する前に、いくつかの染色体を手動で最適化することです。

染色体が多様であることを確認したい場合は、内容を比較してください。適合度を比較するのではありません。適合度の比較はアルゴリズムの仕事です。

于 2012-09-22T01:02:36.470 に答える
2

ここに役立つかもしれないいくつかのコードがあります(私が書いたばかりです):1.0 間隔で10個の値を注文するためのGA。100 個の完全にランダムな対立遺伝子の集団から始まります。これはまさにコードの開始方法です。

解決するために GA に与えた目標は、値を 1.0 の間隔で昇順に並べることでした。これはEval_OrderedDistance、サンプルの各ペアの標準偏差を 1.0 から計算することにより、フィットネス関数で行われます。適合度が 0.0 に近づくにつれて、対立遺伝子が順番に表示され始めるはずです。

ジェネレーション 0 の最も適合した染色体は、残りの染色体と同様に完全にランダムでした。フィットネス値が非常に高い (つまり、悪い) ことがわかります。

GEN: fitness   (allele, ...)
  0: 375.47460 (583.640, -4.215, -78.418, 164.228, -243.982, -250.237, 354.559, 374.306, 709.859, 115.323) 

世代が進むにつれて、適合度 (1.0 からの標準偏差) は減少し、世代 100,000 でほぼ完全になります。

  100: 68.11683 (-154.818, -173.378, -170.846, -193.750, -198.722, -396.502, -464.710, -450.014, -422.194, -407.162)
  ...
10000:  6.01724 (-269.681, -267.947, -273.282, -281.582, -287.407, -293.622, -302.050, -307.582, -308.198, -308.648)
  ...
99999:  0.67262 (-294.746, -293.906, -293.114, -292.632, -292.596, -292.911, -292.808, -292.039, -291.112, -290.928)

コードの興味深い部分は、フィットネス関数です。

// try to pack the aleles together spaced apart by 1.0
// returns the standard deviation of the samples from 1.0
static float Eval_OrderedDistance(Chromosome c) {
    float sum = 0;
    int n = c.alele.Length;
    for(int i=1; i<n; i++) {
        float diff = (c.alele[i] - c.alele[i-1]) - 1.0f; 
        sum += diff*diff; // variance from 1.0
    }

    return (float)Math.Sqrt(sum/n);
}

そして突然変異。単純なクロスオーバーと「1 つの対立遺伝子を完全に変異させる」を使用しました。

Chromosome ChangeOne(Chromosome c) {
    Chromosome d = c.Clone();
    int i = rand.Next() % d.alele.Length;
    d.alele[i] = (float)(rand.NextDouble()*2000-1000);
    return d;
}

私はエリート主義を使用して、常に最高の染色体の正確なコピーを 1 つ保持しました。次に、突然変異と交差を使用して 100 個の新しい染色体を生成しました。

フィットネスの分散を計算しているように聞こえますが、これはもちろん、母集団のフィットネスがすべてほぼ同じであることを示しています。フィットネス関数をどのように定義するかが非常に重要であることがわかりました。フィットネス関数が細かくなればなるほど、染色体をより識別できるようになります。gen 0 は 68e-19 のフィットネス分散を返すため、明らかに、フィットネス関数は完全に異なる染色体に対して同様の値を返しています。

フィットネス計算を共有できますか? または、GAに解決するように依頼している問題は何ですか? それは私たちがあなたを助けるのに役立つかもしれないと思います.

[編集: 明示的なフィットネス共有/Niching の追加]

これを少し考え直し、コードを更新しました。固有の染色体を維持しようとしている場合は、その内容を比較する必要があります (他の人が述べているように)。これを行う 1 つの方法は、それらの間の標準偏差を計算することです。ある閾値よりも小さい場合は、それらを同じと見なすことができます。クラス染色体から:

// compute the population standard deviation
public float StdDev(Chromosome other) {
    float sum = 0.0f;
    for(int i=0; i<alele.Length; i++) {
        float diff = other.alele[i] - alele[i];
        sum += diff*diff;
    }
    return (float)Math.Sqrt(sum);
}

Niching はあなたが望むものを与えてくれると思います。集団内のすべての染色体を比較して類似性を判断し、それぞれに「ニッチ」値を割り当てます。次に、明示的なフィットネス共有と呼ばれる手法を使用して、染色体がニッチに属することに対して「罰せられる」. フィットネス値は、各ニッチの染色体数で割られます。したがって、ニッチ グループ A (A、A、A) に 3 つある場合、そのニッチが選択される可能性が 3 倍になるのではなく、1 つのエンティティとして扱われます。

サンプルを Explicit Fitness Sharing のオンとオフで比較しました。最大 STDDEV が 500 で Niching がオフの場合、約 18 ~ 20 のニッチがありました (つまり、基本的に 100 の母集団で各項目の 5 つの重複)。ニッチをオンにすると、約 85 のニッチがありました。それは人口の 85% の固有の染色体です。私のテストの出力では、 17000 世代後の多様性を確認できます。

ニッチコードは次のとおりです。

// returns: total number of niches in this population
// max_stddev -- any two chromosomes with population stddev less than this max
//               will be grouped together
int ComputeNiches(float max_stddev) {
    List<int> niches = new List<int>();

    // clear niches
    foreach(var c in population) {
        c.niche = -1;
    }

    // calculate niches
    for(int i=0; i<population.Count; i++) {
        var c = population[i];
        if( c.niche != -1) continue; // niche already set

        // compute the niche by finding the stddev between the two chromosomes 
        c.niche = niches.Count;
        int count_in_niche = 1; // includes the curent Chromosome
        for(int j=i+1; j<population.Count; j++) {
            var d = population[j];
            float stddev = c.StdDev(d);
            if(stddev < max_stddev) {
                d.niche = c.niche; // same niche
                ++count_in_niche;
            }
        }
        niches.Add(count_in_niche);
    }

    // penalize Chromosomes by their niche size
    foreach(var c in population) {
        c.niche_scaled_fitness = c.scaled_fitness / niches[c.niche];
    }

    return niches.Count;
}

[編集: アントンのコードの事後分析と更新]

これはおそらく宿題の問題に対処するための適切なフォーラムではないことはわかっていますが、これを知る前に努力をしたし、それをするのがとても楽しかったので、それはアントンにとってのみ役立つと思います.

Genotip.csKromosom.csKromoMain.cs

このコードは良好な多様性を維持しており、1回の実行で「生のフィットネス」を47まで下げることができました。これは、あなたの場合、平均二乗誤差です。それはかなり近かったです!

私のコメントで述べたように、宿題を手伝うだけでなく、プログラミングを手伝いたいと思います。あなたの仕事のこれらの分析を読んでください。

  1. 予想通り、最初から「より多様な」集団を作る必要はありませんでした。完全にランダムな染色体を生成するだけです。

  2. あなたの突然変異とクロスオーバーは非常に破壊的であり、それらのいくつかしかありませんでした. この問題によりうまく機能すると思われるいくつかの新しい演算子を追加しました。

  3. あなたは最善の解決策を捨てていました。Tournament Selection のみでコードを実行したところ、他のすべてよりも 99% 優れた Kromo が 1 つありました。トーナメントの選択では、その最高の価値は忘れられる可能性が非常に高かった. 次世代のためにその価値のコピーを保持する「エリート主義」を少し追加しました。

  4. オブジェクト指向の手法を検討してください。私があなたに送った書き直したコードと私の元のコードを比較してください。

  5. コードを複製しないでください。2 つの異なるクラスにサンプリング パラメーターがありました。

  6. コードをきれいに保ちます。コードの未使用部分がいくつかありました。特に SO に質問を送信する場合は、質問を絞り込み、未使用のコードを削除し、クリーンアップを行ってください。

  7. コードにコメントしてください!私は再作業を大幅にコメントしました。それがセルビア語であることは知っていますが、ほんの少しのコメントでも、他の人があなたが何をしていて、何をしようとしているのかを理解するのに役立ちます.

  8. 全体として、Tournament Selection などのより洗練された機能の実装は素晴らしい仕事でした

  9. List ではなく double[] 配列を優先します。オーバーヘッドが少なくなります。また、List temp 変数のいくつかは必要ありませんでした。あなたの構造

    リスト一時 = 新しいリスト(); for(...) { temp.add(値); } for(temp の各値) { sum += value } average = sum / temp.Count

次のように簡単に記述できます。

sum = 0
for(...) {
    sum += value;
}
average = sum / count;
  1. いくつかの場所で、ループ変数を初期化するのを忘れていたため、問題が簡単に追加された可能性があります。このようなことは重大な問題を引き起こす可能性があり、他の 1 つまたは 2 つの場所と一緒にフィットネスコードに含まれていました。

    ダブルフィット = 0; for(各染色体) { // 初期化する必要があります ここでループに適合 for(各対立遺伝子) { 適合 += ...; フィット/=カウント; }

頑張れプログラミング!

于 2012-09-22T14:16:44.537 に答える
1

私はこれについて簡単に説明しますが、Isaac はほとんど正しいです。GA にその仕事をさせる必要があります。個人の世代 (染色体など) があり、それらはフィットネスのスケール全体にあります (または、すべて同一である可能性があります)。

良いものを選んで (それ自体で) 変異させ、(互いに) クロスオーバーさせます。上位 10% を使用して別の完全な母集団を生成し、下位 90% を捨てることもできます。たぶん、あなたはいつもトップの男をそばに置いています(エリート主義)。

個人はすべて非常に似ているため、GA の改善が止まるまで、これをしばらく繰り返します。母集団の多様性がほとんどなくなってしまいました。

1) 突然変異をより効果的にする、2) 突然変異させる個体を選択するためのより良い方法を見つけることが、あなたを助けるかもしれません. 私のコメントでは、ゲーム プログラマーのための AI テクニックをお勧めしました。それは素晴らしい本です。非常に読みやすい。

この本の見出しをいくつか挙げると、探しているものは次のとおりです。

Roulette Selection (on stackoveflow) ( on wikipedia ) やStochastic Universal Samplingなどの選択手法は、個人の選択方法を制御します。私はいつもルーレットの選択が好きです。個人が選択される確率を設定します。単純なホワイト ノイズのランダム サンプリングではありません。

ローマ字からランダムに4文字を選択するために、GAの外でこれを使用しました。各文字に 0.0 から 1.0 の値を割り当てました。ユーザー (子供) が文字を正しく選択するたびに、その値を 0.1 ずつ下げます。これにより、他の文字が選択される可能性が高くなります。10 回後にユーザーが正しい文字を選択した場合、値は 0.0 になり、その文字が再度表示される可能性は (ほとんど) ありません。

ランク スケーリング、シグマ スケーリング、ボルツマン スケーリング (pdf on ftp!!!)などのフィットネス スケーリング手法により、生のフィットネス値を変更して、調整されたフィットネス値を得ることができます。これらのいくつかは、時間の経過とともに変化する「圧力」または「温度」を設定できるボルツマン スケーリングなどの動的なものです。「圧力」が高まるということは、より適した個人が選択されることを意味します。圧力の低下は、集団内の任意の個人を選択できることを意味します。

私はこれを次のように考えています。多次元空間で解決策を探しているのです。あなたは「ピーク」に達し、そこに向かって進みます。適合しなければならないというプレッシャーは非常に高いです。あなたはその極大値にぴったりと合っています。これで、フィットネスは変更できません。あなたの突然変異はあなたをピークから抜け出させません。それで、プレッシャーを減らし始めて、ああ、ランダムにアイテムを選択します。フィットネス レベルが低下し始めますが、しばらくは問題ありません。その後、再び圧力を上げ始め、驚きます。あなたは局所的な最大値をスキップして、登るべき素敵な新しい局所的な最大値を見つけました。再び圧力を上げてください!

Niching (私は使ったことはありませんが、似たような個人をグループ化する方法のようです)。かなり優秀な 2 人の個人がいるとしますが、それらは大きく異なります。彼らは選ばれ続けています。それらはわずかに変異し続け、あまり良くなりません。これで、母集団の半分が A のマイナー バリアントであり、母集団の半分が B のマイナー バリアントです。これは、グループ A 全体の平均適応度はどのくらいかを示す方法のように思えます。そしてBは?そして、あなたが持っている他のすべてのニッチについてはどうですか。次に、各ニッチの平均適合度に基づいて選択を行います。ニッチを選択し、そのニッチからランダムに個人を選択します。たぶん私は結局これを使い始めるでしょう。それはいいですね!

その一部が役立つことを願っています!

于 2012-09-22T02:21:43.113 に答える
0

アプリケーションに真の乱数が必要な場合は、 Random.orgをチェックすることをお勧めします。無料の HTTP API と、ほぼすべての言語のクライアントがあります。

ランダム性は、多くの目的で、コンピューター プログラムで通常使用される疑似乱数アルゴリズムよりも優れている大気ノイズに由来します。

(私は Random.org とは関係ありませんが、PHP クライアントには貢献しました)。

于 2012-09-22T00:31:55.113 に答える
0

あなたの問題は、ランダム値がどのように機能するかではなく、フィットネスがどのように機能し、候補者をどのように選択するかにあると思います。フィルタリングが厳しすぎるため、十分な要素を受け入れることさえできない可能性があります。

サンプル

  • 値: ランダムな float 0 ~ 10000。
  • フィットネス関数平方根(n)
  • フィットネスの目的の分布 - 距離が少なくとも 1 の線形。

このフィットネス関数を使用すると、幅 1 の「スポット」のほとんどを (最大 100 の場所があるため) すぐに取得できるため、次のスポットごとに時間がかかります。ある時点で、いくつかの小さな範囲が残り、ほとんどの結果が単純に拒否されます。さらに悪いことに、約 50 桁の数字を取得すると、次の数字が収まらない可能性が高くなります。

于 2012-09-22T01:09:40.307 に答える