我是一个编程新手,尤其是在编写AI程序方面。如果我的问题显得愚蠢或者容易解决,请原谅我。我创建了一个原始的Flappy Bird游戏,并使用神经网络来控制它。一切进展顺利,直到所有的鸟都死掉。所有的鸟死掉后,我会选择最佳的鸟,创建一个新种群的数组,将新鸟的“大脑”设置为与最佳鸟的“大脑”相同,最后我会对所有新种群鸟的大脑进行微小的变异以确保它们不完全相同。我尝试使用概率进行变异,对所有新鸟的大脑权重进行变异,并设置if语句以确保权重不会低于1.0或高于-1.0。然而,结果是一样的,所有下一代(第二代)的鸟行为表现得像是拥有相同的大脑。以下是我认为值得检查的一些代码。我可以提供所有代码,但代码量很大。
重新填充种群
for (int i = 0; i < population; i++) { birds.add(new Bird()); birds.get(i).brain=lastbird.brain; birds.get(i).brain.mutate(0.1); }
变异函数
public void mutate(double eta) { Random dice = new Random(); for (int layer = 1; layer < NETWORK_SIZE; layer++) { for (int neuron = 0; neuron < NETWORK_LAYER_SIZES[layer]; neuron++) { if (dice.nextDouble() < eta) { bias[layer][neuron] += dice.nextGaussian()/2; } for (int prevNeuron = 0; prevNeuron < NETWORK_LAYER_SIZES[layer - 1]; prevNeuron++) { if (dice.nextDouble() < eta) { weights[layer][neuron][prevNeuron] += dice.nextGaussian()/2; } } } } }
网络(脑)变量和构造函数
public class Network {private double[][] output;private double[][][] weights;private double[][] bias;private double[][] error_signal;private double[][] output_derivative;public final int[] NETWORK_LAYER_SIZES;public final int INPUT_SIZE;public final int OUTPUT_SIZE;public final int NETWORK_SIZE;public Network(int... NETWORK_LAYER_SIZES) { this.NETWORK_LAYER_SIZES = NETWORK_LAYER_SIZES; this.INPUT_SIZE = NETWORK_LAYER_SIZES[0]; this.NETWORK_SIZE = NETWORK_LAYER_SIZES.length; this.OUTPUT_SIZE = NETWORK_LAYER_SIZES[NETWORK_SIZE - 1]; this.output = new double[NETWORK_SIZE][]; this.weights = new double[NETWORK_SIZE][][]; this.bias = new double[NETWORK_SIZE][]; this.error_signal = new double[NETWORK_SIZE][]; this.output_derivative = new double[NETWORK_SIZE][]; for (int i = 0; i < NETWORK_SIZE; i++) { this.output[i] = new double[NETWORK_LAYER_SIZES[i]]; this.error_signal[i] = new double[NETWORK_LAYER_SIZES[i]]; this.output_derivative[i] = new double[NETWORK_LAYER_SIZES[i]]; this.bias[i] = NetworkTools.createRandomArray(NETWORK_LAYER_SIZES[i], -0.5, 0.7); if (i > 0) { weights[i] = NetworkTools.createRandomArray(NETWORK_LAYER_SIZES[i], NETWORK_LAYER_SIZES[i - 1], -1, 1); } }}
回答:
当你将第i只鸟的大脑分配给lastbird.brain
,即在birds.get(i).brain=lastbird.brain
中对所有新一代的鸟进行设置时,你实际上是将所有新鸟的大脑引用设置为指向同一个大脑对象。也就是说,任何鸟的大脑引用都指向同一个大脑对象。因此,当你改变(即变异)一只鸟的大脑时,变异是在所有引用指向的共同对象上进行的,同时也会反映到所有鸟身上。
你需要复制大脑对象的内容,而不是指向同一个对象。你可以通过克隆或使用复制构造函数来实现这一点。复制构造函数比克隆更受欢迎。你需要将birds.get(i).brain=lastbird.brain
替换为
birds.get(i).brain = new Brain(lastbird.brain);
由于你没有提供Brain对象的代码,我无法提供复制构造函数的实现。你可以在构造函数中使用=号来分配基本类型(如int、String等)。但对于所有自定义对象,你也需要为这些对象创建复制构造函数。
你可以在这里找到更多信息 如何在Java中复制对象?。
编辑: 在提供了Network类之后,添加了实现。
public Network( Network other ) { this.output = copy2d( other.output ); this.weights = copy3d( other.weights ); this.bias = copy2d( other.bias ); this.error_signal = copy2d( other.error_signal ); this.output_derivative = copy2d( other.output_derivative ); this.NETWORK_LAYER_SIZES = copy1dInt(other.NETWORK_LAYER_SIZES); this.INPUT_SIZE = other.INPUT_SIZE; this.OUTPUT_SIZE = other.OUTPUT_SIZE; this.NETWORK_SIZE = other.NETWORK_SIZE; } private static double[][][] copy3d( double[][][] original ) { double[][][] copy = new double[original.length][][]; for( int i = 0; i < original.length; i++ ) { copy[i] = copy2d( original[i] ); } return copy; } private static double[][] copy2d( double[][] original ) { double[][] copy = new double[original.length][]; for( int i = 0; i < original.length; i++ ) { copy[i] = copy1d( original[i] ); } return copy; } private static double[] copy1d( double[] original ) { int length = original.length; double[] copy = new double[length]; System.arraycopy( original, 0, copy, 0, length ); return copy; } private static int[] copy1dInt( int[] original ) { int length = original.length; int[] copy = new int[length]; System.arraycopy( original, 0, copy, 0, length ); return copy; }