3

XOR 問題を解決できる 2 つの入力ニューロン、2 つの非表示ニューロン、1 つの出力ニューロンを備えた人工ニューラル ネットワーク (ANN) を実装することになっています。ネットワークの重みは、進化的アルゴリズムを使用して最適化する必要があります。各ニューロンの活性化関数と各 ANN の適合度関数が与えられます。次の図は、問題を要約し、私が使用した変数名を紹介しています。

ここに画像の説明を入力

今、私は問題を解決するために最善を尽くしましたが、1000 ANN と 2000 世代の母集団サイズを使用する進化的アルゴリズムを使用しても、私の最高の適合度は 0.75 よりも優れていることはありません。私のコードには、ニューロン、アクティベーション、およびフィットネス関数を含む ANN クラスと、進化的アルゴリズムを含み、ANN の重みを最適化する Main クラスが含まれています。コードは次のとおりです。

各 ANN は -1 と 1 の間のランダムな重みで初期化され、変異することができます。つまり、ランダムに選択された 1 つの重みが異なる変異を返します。

public class ANN implements Comparable<ANN> {
    private Random rand = new Random();
    public double[] w = new double[6];  //weights: in1->h1, in1->h2, in2->h1, in2->h2, h1->out, h2->out

    public ANN() {
        for (int i=0; i<6; i++) //randomly initialize weights in [-1,1)
            w[i] = rand.nextDouble() * 2 - 1;
    }

    //calculates the output for input a & b
    public double ann(double a, double b) {
        double h1 = activationFunc(a*w[0] + b*w[2]);
        double h2 = activationFunc(a*w[1] + b*w[3]);
        double out = activationFunc(h1*w[4] + h2*w[5]);

        return out;
    }

    private double activationFunc(double x) {
        return 2.0 / (1 + Math.exp(-2*x)) - 1;
    }

    //calculates the fitness (divergence to the right output)
    public double fitness() {
        double sum = 0;
        //test all possible inputs (0,0; 0,1; 1,0; 1,1)
        sum += 1 - Math.abs(0 - ann(0, 0));
        sum += 1 - Math.abs(1 - ann(0, 1));
        sum += 1 - Math.abs(1 - ann(1, 0));
        sum += 1 - Math.abs(0 - ann(1, 1));
        return sum / 4.0;
    }

    //randomly change random weight and return the mutated ANN
    public ANN mutate() {
        //copy weights
        ANN mutation = new ANN();
        for (int i=0; i<6; i++)
            mutation.w[i] = w[i];

        //randomly change one
        int weight = rand.nextInt(6);
        mutation.w[weight] = rand.nextDouble() * 2 - 1;

        return mutation;
    }

    @Override
    public int compareTo(ANN arg) {
        if (this.fitness() < arg.fitness())
            return -1;
        if (this.fitness() == arg.fitness())
            return 0;
        return 1;   //this.fitness > arg.fitness
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        ANN ann = (ANN)obj;
        for (int i=0; i<w.length; i++) {    //not equal if any weight is different
            if (w[i] != ann.w[i])
                return false;
        }
        return true;
    }
}

Main クラスには進化的アルゴリズムがあり、エリート主義とランクベースの選択を使用して各母集団の次世代を作成します。つまり、100 個の最良の ANN がコピーされ、残りの 900 個は以前に成功した ANN の突然変異です。

//rank-based selection + elitism
public class Main {
    static Random rand = new Random();
    static int size = 1000;                     //population size
    static int elitists = 100;                  //number of elitists

    public static void main(String[] args) {
        int generation = 0;
        ArrayList<ANN> population = initPopulation();
        print(population, generation);

        //stop after good fitness is reached or after 2000 generations
        while(bestFitness(population) < 0.8 && generation < 2000) {
            generation++;
            population = nextGeneration(population);
            print(population, generation);
        }
    }

    public static ArrayList<ANN> initPopulation() {
        ArrayList<ANN> population = new ArrayList<ANN>();
        for (int i=0; i<size; i++) {
            ANN ann = new ANN();
            if (!population.contains(ann))  //no duplicates
                population.add(ann);
        }
        return population;
    }

    public static ArrayList<ANN> nextGeneration(ArrayList<ANN> current) {
        ArrayList<ANN> next = new ArrayList<ANN>();
        Collections.sort(current, Collections.reverseOrder());  //sort according to fitness (0=best, 999=worst)

        //copy elitists
        for (int i=0; i<elitists; i++) {
            next.add(current.get(i));
        }

        //rank-based roulette wheel
        while (next.size() < size) {                        //keep same population size
            double total = 0;
            for (int i=0; i<size; i++)
                total += 1.0 / (i + 1.0);                   //fitness = 1/(rank+1)

            double r = rand.nextDouble() * total;
            double cap = 0;
            for (int i=0; i<size; i++) {
                cap += 1.0 / (i + 1.0);                     //higher rank => higher probability
                if (r < cap) {                              //select for mutation
                    ANN mutation = current.get(i).mutate(); //no duplicates
                    if (!next.contains(mutation))
                        next.add(mutation);     
                    break;
                }
            }
        }       

        return next;
    }

    //returns best ANN in the specified population
    public static ANN best(ArrayList<ANN> population) {
        Collections.sort(population, Collections.reverseOrder());
        return population.get(0);
    }

    //returns the best fitness of the specified population
    public static double bestFitness(ArrayList<ANN> population) {
        return best(population).fitness();
    }

    //returns the average fitness of the specified population
    public static double averageFitness(ArrayList<ANN> population) {
        double totalFitness = 0;
        for (int i=0; i<size; i++)
            totalFitness += population.get(i).fitness();
        double average = totalFitness / size;
        return average;
    }

    //print population best and average fitness
    public static void print(ArrayList<ANN> population, int generation) {       
        System.out.println("Generation: " + generation + "\nBest: " + bestFitness(population) + ", average: " + averageFitness(population));
        System.out.print("Best weights: ");
        ANN best = best(population);
        for (int i=0; i<best.w.length; i++)
            System.out.print(best.w[i] + " ");
        System.out.println();
        System.out.println();
    }
}

これについてかなり考え、学んだテクニックを使用しましたが、結果は満足のいくものではありません。何らかの理由で、最適な重みが重みごとに -1 にドリフトしているように見えます。それはどのように理にかなっていますか?重みの -1 から 1 の範囲は適切ですか? 変異に加えてクロスオーバーも導入する必要がありますか? これは非常に具体的な問題であることは承知していますが、助けていただければ幸いです。

4

1 に答える 1

2

ネットワーク構造が正しくありません。各ノードのバイアスまたはしきい値がなければ、このネットワークは XOR 問題を解決できません。

一方の隠しノードは OR をエンコードし、もう一方の隠しノードは AND をエンコードする必要があります。次に、出力ノードは、XOR 問題に対して OR 隠れノードが正であり、AND 隠れノードが負であることをエンコードできます。これは、OR 隠しノードがアクティブ化され、AND 隠しノードがアクティブ化されていない場合にのみ、肯定的な結果になります。

また、重みの境界を増やし、EA がそれを自分で見つけられるようにします。ただし、それが必要かどうかは、ネットワーク構造によって異なります。

このネットワークを隠しノードとしきい値で使用する場合は、http ://www.heatonresearch.com/online/introduction-neural-networks-java-edition-2/chapter-1/page4.html を参照してください。

バイアスのある別のネットワークを使用したい場合は、http: //www.mind.ilstu.edu/curriculum/artificial_neural_net/xor_problem_and_solution.phpを参照してください。

于 2015-06-04T16:19:01.523 に答える