人工智能: 物理世界中的进化模拟






4.87/5 (16投票s)
使用 Farseer Physics Library 和神经网络,模拟行为进化的例子
进化
随机的零散部件不会自动组合成执行任务的机器,但在生命体中似乎并非如此。如果随机生成的特征适合环境,它们就会被传播下来。但这个创造过程如何可视化呢?要观察自然环境中的这种变化,需要观察数百万代后代的观测结果。但幸运的是,计算机可以快速完成这项工作,让我们惊叹于创造了自然界所有辉煌的这一简单过程。
问题
在这里,我们将创建一个简单的生命框架和一个提供环境供这种人造生命发展的世界。我们提供基本结构,没有意义,没有目的,我们将看到生命会找到出路。
因此,对于生命的基本结构,我们使用最简单的例子,它足够复杂,使其看起来更接近真实的细菌。
这种生命体的结构如下。
Base
一个基座包含九个节点,可以连接驱动器或传感器。神经网络通过传感器输入来控制这些驱动器。可以附加部件的节点位置如下。
最初,神经网络充满了随机权重,导致非常随机的行为,并且传感器和驱动器的位置和数量也是随机生成的。这会导致生物体产生不同的行为。
驱动器
驱动器是一个轮子,可以顺时针或逆时针旋转。它有一个输入,用于决定旋转方向。它使用 Farseer Physics 的 RevoluteJoint
和连接到它的圆形 Body
来实现这一点。
Sensor
传感器检测视野内的食物存在情况,并向连接的神经网络发送单一输入,即是否能感知到食物。颜色也相应地从红色变为灰色。
下面是一些随机生成的生物体示例,在随机位置连接了传感器或驱动器。
环境在交替的位置生成食物,当生物体到达该位置时,它会消耗食物,然后会在交替的位置重新生成。生物体必须成功地改变方向,检测到食物的存在才能在环境中生存。为了在生物体中产生这种行为,需要物理结构和神经网络的许多随机机会和突变。
下面是生成食物的位置。生物体必须成功适应才能在两个位置消耗食物。
C# 设置
在这里,我们使用基于 Box2D 的 Farseer Physics Library 来创建 2D 物理环境。对于神经网络的实现,Parallel Neural Networks Library 被使用。
首先想到的是此系统的可能架构,即为每个 Creature
和其 Food
创建一个单独的物理 World
,因为两个不同的生物体不应相互碰撞,也不应消耗彼此的食物(这将确保正确的选择)。但这会造成严重的内存浪费,即使对于少量实例也是如此。因此,使用 Box2D/Farseer Physics 的“碰撞过滤”功能,我们可以创建对象类别和组来制定碰撞规则,从而为任意数量的实例创建了一个单一的 World
模拟。这样可以运行多达 100 个并行生物体实例而不会出现故障。更多关于碰撞过滤的信息 在此。例如,生物体的基座、轮子和传感器都具有“类别组” -1
,这意味着它们永远不会相互碰撞。其余不需要的碰撞通过 CollisionCallback
事件进行过滤。
生物体的结构和神经网络的模式由 DNA 类管理,该类还包含一个随机突变结构和模式的方法。
进化实例包含一个生物体、食物以及它消耗的食物数量。
public EvolutionInstance(EvolutionWorld world, DNA dna)
{
Creature = new Creature.Creature(world.World, dna);
Food = new List<EvolutionCreature.Food>();
NumFoodConsumed = 0;
World = world;
Died = false;
FoodLocation = false;
AddItemsToCanvas();
GenerateFood();
}
生物体是根据初始随机生成的 DNA
的信息创建的。DNAElement
描述了每个节点的功能。
public class DNAElement
{
public enum ElementType { ACTUATOR, SENSOR };
public ElementType Type;
public Dictionary<string, int> Attributes;
}
DNA
由 NetworkData
(一个神经网络模式)和一个 DNAElement
列表组成。
public class DNA
{
public List<DNAElement> DNAElements { get; set; }
public NeuralNetworks.NetworkData NetworkData { get; set; }
public DNA(DNA dna, int numJoints, bool mutate)
{
DNAElements = new List<DNAElement> ();
foreach (DNAElement ele in dna.DNAElements)
{
DNAElement element = new DNAElement();
element.Attributes["Joint"] = ele.Attributes["Joint"];
element.Type = ele.Type;
DNAElements.Add(element);
}
BackPropogationNetwork network = new BackPropogationNetwork(dna.NetworkData);
NetworkData = network.GetNetworkData();
if (mutate)
Mutate(numJoints);
}
}
Creature
是根据 DNA
创建的。
public void CreateParts(World world)
{
foreach(DNAElement element in DNA.DNAElements)
{
if (element.Type == DNAElement.ElementType.SENSOR)
{
Sensor s = new Sensor(world, this, element.Attributes["Joint"]);
Shapes.Add(s.SensorBox);
}
else if (element.Type == DNAElement.ElementType.ACTUATOR)
{
Actuator a = new Actuator(world, this, element.Attributes["Joint"]);
Shapes.Add(a.Wheel);
}
}
}
DNA
中的突变是随机发生的。
public void Mutate(int maxJoints)
{
switch (RandomProvider.random.Next(5))
{
case 0:
MutateAddElement(maxJoints);
break;
case 1:
MutateNetwork();
break;
case 2:
MutateRemoveElement();
break;
case 3:
MutateExistingType();
break;
case 4:
// No change
break;
}
}
Creature
会相应地初始化,并且对象在经过适当调整后被创建并添加到 WPF Canvas
和 Farseer Physics World
中,以便我们生成由 DNA
描述的生物体。
随着模拟的进行,下一代实例根据 ReSpawn()
函数生成,在选择最佳生物体并随机突变其 DNA
后。
private void ReSpawn(DNA topDNA)
{
Instances.ForEach(r => r.Die());
Instances.Clear();
CreateWorld();
for (int i = 0; i < NumInstances; i++)
{
EvolutionInstance instance = new EvolutionInstance(this, new DNA(topDNA, 8, true));
Instances.Add(instance);
}
}
private void TimeOut()
{
EvolutionInstance bestInstance = Instances.OrderBy(r => r.NumFoodConsumed).Last();
ReSpawn(bestInstance.Creature.DNA);
}
结果
我们可以观察到,生物体的行为最初是随机的,它们要么掉下悬崖,要么停留在原地。随着模拟的进行,一代又一代的突变发生,我们期望的行为,即根据食物位置改变方向的能力,在神经网络中随机产生。
模拟中出现了许多不同类型的行为,根据我们提供的环境,能够及时消耗更多食物的生物体更有可能生存和繁衍。
在现实生活中,基本的构件来自蛋白质,它们形成复杂的形状并制造微型机械。蛋白质还充当化学信使,在有机世界中传递信号。数十亿年前,这些蛋白质通过偶然的方式结合在一起,创造了自我复制的有机结构,最终形成了我们所知的生命。