转向感知器
2022-08-29 本文已影响0人
大龙10
书名:代码本色:用编程模拟自然系统
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
第10章目录
10.5 转向感知器
1、转向感知器
- 将感知器的概念(多个输入,单个输出)应用到转向行为中,顺便展示增强式学习策略。
- 用一种创新的方式诠释概念。这么做能够涵盖基础知识,又能避开一些复杂烦琐的工作。主要目的是让示例程序看起来更有趣、更贴近大脑的工作方式,并不关心人工智能教科书介绍的规则。
- 前面的Vehicle类。
Vehicle对象有自己的位置、速度和加速度,能根据一系列转向规则在屏幕中移动。它有一个遵循牛顿运动定律的applyForce()函数。
如果我们在Vehicle类中再加入一个Perceptron对象。
class Vehicle {
Perceptron brain; 给小车加上大脑
PVector location;
PVector velocity;
PVector acceleration;
//……
2、场景
-
我们要研究的场景:Sketch中有一系列目标(放在ArrayList中)和一辆小车。
-
小车的目的是寻找图中的所有目标。按照第6章提出的方法,我们要实现一个seek()函数,用它计算到每个目标的转向力,再将这些转向力作用在物体上。
假设这些目标是由PVector对象组成的ArrayList,那么整体实现如下所示:
void seek(ArrayList<PVector> targets) {
for (PVector target : targets) {
PVector force = seek(targets.get(i)); 物体对每个目标都有转向力
applyForce(force);
}
}
- 在第6章,为了创建更加动态的模拟效果,我们还为每个转向力分别指定了权重。比如根据距离确定权重:目标越远,转向力越强。
我们可以在这里引入感知器,把所有转向力都当做输入,再用感知器的输入权重对它们进行处理,最后产生输出转向力。也就是:
void seek(ArrayList<PVector> targets) {
PVector[] forces = new PVector[targets.size()]; 为brain对象创建一系列输入
for (int i = 0; i < forces.length; i++) {
forces[i] = seek(targets.get(i)); 计算每个目标对应的转向力,填充转向力数组
}
PVector output = brain.process(forces); 从brain对象中获取输出结果,将转向力作用在物体上
applyForce(output);
}
- 换句话说,vehicle对象不再做转向力加权运算,它把这部分工作转给brain对象。brain对象加权求和后的输出结果就是最后的转向力。这个改变能引入很多新的实现:小车可以按照自己的意愿做出转向决定,学习自身产生的错误,并对环境的刺激做出反应。
3、具体实现
-
我们可以把直线分类感知器用做基础模型,但需要进行一些修改:感知器的输入从数字变为向量!
下表对比了两种情况下feedforward()函数的实现。
这两个函数实现的几乎是同一个算法,但有两点区别。
- 1.向量求和
每个输入不再是单个数字,而是一个向量,因此我们必须用PVector的运算方式将它们加权求和。 - 2.没有激励函数
在这里,我们想要的结果是转向力。因此,我们不需要用一个布尔值对结果进行分类,只需直接返回结果向量。
4、增强式学习
- 将最终转向力作用于小车之后,我们下一步要做的就是给大脑施加反馈,也就是所谓的增强式学习。
本次转向决策是有利的还是有害的?假如系统中同时存在捕食者(吃掉小车)和食物(提高小车的生命值),这时候,神经网络的权重调整规则就应该是:躲避捕食者,靠近食物。 -
让我们来看一个更简单的例子,在这个例子中,小车想要朝着窗口的中心运动。我们用以下方式训练大脑对象:
PVector desired = new PVector(width/2,height/2);
PVector error = PVector.sub(desired, location);
brain.train(forces,error);
- 在这里,我们向brain对象传入所有输入的副本(后续要用它纠正误差),还传入了环境的观察值:一个由当前位置指向目标位置的PVector对象,这个向量就是小车的误差:误差越大,小车的表现越差;反之,表现越好。
- 之后,大脑可以根据这个“误差”向量(有两个误差值:x坐标误差和y坐标误差)调整权重,这和之前直线分类器的训练是一样的。
- 由于小车的误差是已知的,因此我们只需要将这个误差当做参数传入。注意权重的调整需要进行两次计算:一次调整x坐标,另一次调整y坐标。
weights[i] += c*error.x*forces[i].x;
weights[i] += c*error.y*forces[i].y;
- 从Vehicle类的整体实现,我们可以看到转向函数如何用感知器控制转向行为。
5、示例
示例代码10-2 感知器转向
class Vehicle {
Perceptron brain; 小车有了大脑
PVector location; 物理运动所需的变量
PVector velocity;
PVector acceleration;
float maxforce;
float maxspeed;
Vehicle(int n, float x, float y) { 在小车的构造函数中创建感知器对象,传入输入数量和学习brain = new Perceptron(n,0.001);
acceleration = new PVector(0,0);
velocity = new PVector(0,0);
location = new PVector(x,y);
maxspeed = 4;
maxforce = 0.1;
}
void update() { update()函数和之前一样
velocity.add(acceleration);
velocity.limit(maxspeed);
location.add(velocity);
acceleration.mult(0);
}
void applyForce(PVector force) { applyForce()函数和之前一样
acceleration.add(force);
}
void steer(ArrayList<PVector> targets) {
PVector[] forces = new PVector[targets.size()];
for (int i = 0; i < forces.length; i++) {
forces[i] = seek(targets.get(i));
}
PVector result = brain.feedforward(forces); 所有转向力都是输入
applyForce(result); 施加计算得到的结果
PVector desired = new PVector(width/2,height/2); 根据和中心之间的距离训练大脑
PVector error = PVector.sub(desired, location);
brain.train(forces,error);
}
PVector seek(PVector target) { seek()函数和之前一样
PVector desired = PVector.sub(target,location);
desired.normalize();
desired.mult(maxspeed);
PVector steer = PVector.sub(desired,velocity);
steer.limit(maxforce);
return steer;
}
}