65.9K
CodeProject 正在变化。 阅读更多。
Home

人工智能: 虚拟鱼的遗传进化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (26投票s)

2016年1月28日

CPOL

4分钟阅读

viewsIcon

27443

downloadIcon

1489

神经网络的基因突变, 以在鱼类等虚拟生物中产生更好的后代

引言

又一个进化实验。我使用了来自 nuget 的 Parallel Neural Networks 库,用于其用于强化学习的 BackPropagationNetwork 类。 基因突变、强化学习和适当的选择使鱼学会如何寻找食物。 在这里,Fish 是一个具有神经网络、颜色、大小、空气动力学效率和传感器的实体,传感器通过其角度和距离将食物的位置馈送到相应的神经输入。 鱼的属性包含在 FishChromosomes 中,该染色体可以与另一个染色体交配以产生突变的后代。

计算神经网络的输出,并将其用于计算鱼的航向角和速度,从而完成循环,并且鱼在我们的环境中“生活”。 输入神经元的数量对应于鱼传感器上的分辨率。 所有这些都发生在 Fish 类中。

每个 FishChromzomes 都有物理基因和精神基因,物理基因是大小、空气动力学效率(控制速度)和颜色。 精神基因只是运行鱼的神经网络的 NetworkData。 空气动力学效率高的鱼移动速度快,而较大的鱼需要更多的食物才能生存,颜色不会影响鱼的性能。

强化学习

鱼有一个短期记忆,记录了到目前为止它所采取的先前 10 个步骤,该记忆用于在鱼成功喂食后训练鱼。 当鱼成功到达食物时,队列中存储的先前 10 个步骤用于训练鱼上的神经网络,以使鱼更擅长以这种方式寻找食物。 因此,鱼会边走边学。 所有这些都在 FishLearn 类中实现。 鱼采取的每个步骤都会添加到队列中,以便在鱼喂食时使用。

    public class FishLearn
    {
        public Fish Fish;
        public Queue<neuralnetworks.dataset> LearnQueue;
        public BackgroundWorker worker;

        public FishLearn(Fish fish)
        {
            Fish = fish;
            LearnQueue = new Queue<neuralnetworks.dataset>(15);

            worker = new BackgroundWorker();

            worker.DoWork += ((object e, DoWorkEventArgs w) =>
            {
                Fish.FishNeural.BatchBackPropogate(LearnQueue.ToArray(),
                                                  (int)w.Argument, 0.1, 0.1, worker);
            });
        }

        public void AddStep(IEnumerable<double> neuralInputs, IEnumerable<double> neuralOutputs)
        {
            NeuralNetworks.DataSet fishio = new NeuralNetworks.DataSet() {
                                                    Inputs = neuralInputs.ToArray(),
                                                    Outputs = neuralOutputs.ToArray()
                                            };
            try
            {
                LearnQueue.Enqueue(fishio);
            }
            catch
            {
                LearnQueue.Dequeue();
                LearnQueue.Enqueue(fishio);
            }
        }

        public void LearnPreviousSteps(int iterations)
        {
            worker.WorkerReportsProgress = true;
            if(!worker.IsBusy)
                worker.RunWorkerAsync(iterations);
        }
    } 

强化学习导致模拟中每条鱼的神经网络不断变化,学习鱼喂食时的先前步骤。

遗传算法

当两个实体的属性发生基因突变时,它们的属性会混合在一起,以产生具有不同特征的新实体。 当我们模拟中的鱼正在喂食时,每个鱼都会维护喂食计数,并且当计时器重置时,会选择最好的鱼并进行杂交以产生新的个体。 由于每条鱼的神经网络都不同,因此后代将拥有自己独特的神经网络,因此具有不同的行为。 在我们的应用程序中,鱼的物理基因以以下方式混合。

精神基因

一个交叉点,用于选择以启用两个神经网络的合并。 NetworkData 对象用于此合并。

    if (frontback)
    {
        foreach (ConnectionData c in guppiedata.Connections.Where
		(r => r.From.Layer == guppiedata.InputLayerId))
            c.Weight = one.Connections.Find(r => (r.From.Layer == c.From.Layer) 
                                              && (r.From.Node == c.From.Node)
                                              && (r.To.Layer == c.To.Layer)
                                              && (r.To.Node == c.To.Node)).Weight;

        foreach (ConnectionData c in guppiedata.Connections.Where
		(r => r.To.Layer == guppiedata.OutputLayerId
                                                                    && r.To.Node < crossoverpoint1))
            c.Weight = one.Connections.Find(r => (r.From.Layer == c.From.Layer)
                                              && (r.From.Node == c.From.Node) 
                                              && (r.To.Layer == c.To.Layer) 
                                              && (r.To.Node == c.To.Node)).Weight;

        foreach (ConnectionData c in guppiedata.Connections.Where(r => 
		r.To.Layer == guppiedata.OutputLayerId
                                              && r.To.Node >= crossoverpoint1))
            c.Weight = two.Connections.Find(r => (r.From.Layer == c.From.Layer) 
                                              && (r.From.Node == c.From.Node) 
                                              && (r.To.Layer == c.To.Layer) 
                                              && (r.To.Node == c.To.Node)).Weight;
    }

物理基因

随机获取一个交叉点,列表的长度作为最大值。 两个基因列表被复制到交叉点的两侧。

    public static List<gene> MixGenesUp(List<gene> one, List<gene> two)
    {
        int crossoverpoint = RandomProvider.Random.Next(one.Count);
        List<gene> genes = new List<gene>();
        for (int i = 0; i < one.Count; i++)
        {
            if (i <= crossoverpoint)
                genes.Add(new Gene() { Name = one[i].Name, Value = one[i].Value });
            else
                genes.Add(new Gene() { Name = two[i].Name, Value = two[i].Value });
        }
        return genes;
    }

模拟

模拟中的食物在每次消耗时都会自动生成。 鱼看不到彼此,但在任何给定时间都可以看到所有食物颗粒的位置。 计算传感器值并将其应用于神经网络,读取输出,并计算并应用鱼的航向角和前进速度。

    public void Live(FoodGenerator foodGen, IEnumerable<fish> fishes)
    {
        Sensor.UpdateSensors(foodGen.FoodParticles);
        FishNeural.ApplyInput(Sensor.FoodSensors);
        FishNeural.CalculateOutput();

        CheckFood(foodGen);

        Age += 0.005;

        if (Age > 10)
        {
            IsDead = true;
            if (Dead != null)
                Dead(this, new DeadEventArgs() { });
        }

        IEnumerable<double> Outputs = FishNeural.ReadOutput();

        FishLearn.AddStep(Sensor.FoodSensors, Outputs);
        HeadingAngle = (Math.Atan(Outputs.ElementAt(1)/Outputs.ElementAt(2))-(Math.PI/4));
        MoveForward(Outputs.ElementAt(0));
    }

神经网络库中的 Backpropagation 类直接用于此目的。

选择程序

仅选择一两条鱼来重新生成整个下一代可能会导致所需特征的丢失,因此从前 3 条鱼中选择两条鱼并进行交配以产生后代。

    public void NextGeneration()
    {
        IEnumerable<fish> topFishes = fishes.OrderByDescending(r => r.NumFoodEaten).Take(5);
        int totalfood = 0;
        generation ++;
        fishes.ForEach(r => totalfood += r.NumFoodEaten);
        status_text_block.Text = "Generation: " +
                                 generation.ToString() + 
                                 ", Total Food: " + 
                                 totalfood.ToString();

        graphWindow.AddDataPoint(generation, totalfood);

        for (int i = 0; i < fishes.Count; i++)
        {
            if(i < fishes.Count/3)
                fishes[i] = new Fish(FishChromozomes.Mate
                (topFishes.ElementAt(0).Chromozomes, topFishes.ElementAt(1).Chromozomes));
            else if(i < fishes.Count*2/3)
                fishes[i] = new Fish(FishChromozomes.Mate
                (topFishes.ElementAt(0).Chromozomes, topFishes.ElementAt(2).Chromozomes));
            else
                fishes[i] = new Fish(FishChromozomes.Mate
                (topFishes.ElementAt(1).Chromozomes, topFishes.ElementAt(2).Chromozomes));
        }
    }

结果

最初,鱼几乎不知道如何到达食物,有些鱼像培养皿中的小斑马鱼一样不停地绕圈。 逐渐地,一代又一代,鱼获得了使它们能够消耗更多食物的特征。 结果是可见的,因为经过几代之后,鱼直接去寻找食物,获得了一种群集行为。 总体的种群适应度只不过是鱼消耗的总食物量绘制在图形窗口中,并且可以看到一致的增长,一代又一代。 这证明模拟是成功的,并且基因突变成功地产生了更健康的个体。 出现的另一个特征是射向两个食物颗粒的中点。 实验表明,当一只青蛙被展示两个相距一定距离的昆虫时,青蛙最初会朝着食物来源的中间方向移动,然后在达到阈值距离后,它会选择一侧。 在几代之后,我们的鱼也看到了相同的行为。

© . All rights reserved.