神经网络进阶(连载2)为机器人提供一存储器

来源:互联网 发布:一键重装软件 编辑:程序博客网 时间:2024/05/21 17:39

游戏编程中的人工智能技术
 

   

 (连载之二)

为机器人提供记忆器

(Giving Your Bot a Memory)


8.2 为Bot提供记忆器(Give Your Bot a Memory)

       Bot的记忆器(memory)用来记忆它所在的那个环境,所以可以用代表环境的一个简单的数据结构来表示。在本例子中,我们把环境划分成许多同等大小的cell(小方格或小房间) ,如图8.5所示。每个方格存放一个整数,而所有方格中的数据可以在一个2维向量std::vector中保存。

       这一2维向量就可以用来作为保存相关信息的一张记忆地图(memory map)。在本例中被记录的是扫雷机在每一方格访问的滴答(tick)数。利用这个2维向量,扫雷机就能知道,在每一个小格,在此以前它是否曾经到过那里,或曾经呆过了多少时间。为了探索环境,扫雷机必须演化神经网络,去优先访问那些没有访问过的小方格。

       记忆地图由称作CMapper的类来实现。下面就是它的定义:

 

     class CMapper

     {

    private:

 

    //记忆小方格的2d向量

    vector<vector<SCell> > m_2DvecCells;

 

       其中SCell是一个很简单的结构体,它保存一个记录着小方格坐标的矩形结构RECT,一个纪录了在那里花去多少时间的整数iTicksSpentHere。 SCell 结构体中也包含用来增加和清除iTicksSpentHere内容的方法。

 

intm_NumCellsX;

intm_NumCellsY;

intm_iTotalCells;

//每个方格的大小尺寸

doublem_dCellSize; 

    public:

     CMapper():m_NumCellsX(0),

               m_NumCellsY(O),

    m_iTotalCells(0)

     { } 

     //以下方法在该类的一个实例被创建之后调用。它为所有方格设置了坐标。

void  Init(int MaxRangeX, int MaxRangeY); 

     //下面的Update方法在每一帧中都要被调用,用来更新指定位置方格的访问时间

void  Update(double xPos, double yPos); 

     //返回化在此位置(方格)的时间的长短(滴答数)

int   TicksLingered(double xPos, double yPos)const; 

     //返回被访问过的方格总数

int   NumCellsVisited()const; 

     //返回在给定位置的方格是否已经被访问或未被访问过

bool  BeenVisited(double xPos, double yPos)const; 

     //这种方法把访问的任何方格画成为红色。被访问的次数也愈多,红色也愈深

     void    Render(HDCsurface);

     void    Reset();

     int     NumCells(){returnm_iTotalCells;}

};

 

       扫雷机有了记忆器,就需要有一种利用记忆器来记住它们曾经在哪里的办法。由于扫雷机的前一版本中我们已经对触觉器的端点位置作过了计算,所以这一步骤的实现是很简单的。这些触觉器的末端可以在记忆地图上到处进行“探索”并从其中取得信息。这和一个昆虫使用其“天线”进行探索的方法完全类似。见图8.6。

 

      这些触觉器得到的读数保存在一个叫做m_vecFeelers的std::vector向量中,然后与触觉器原有的坐标参数一起被送入神经网络。


图8.6  扫雷机长出了天线

 

       完成本工作的代码可以在CMinesweeper::TestSensors方法中找到。下面是新增加的几行代码的形式:

 

//检查扫雷机在当前位置的小格中已经访问过多少时间

intHowOften = m_htemoryNap.TicksLingered(m_tranSensors[SP].X,

       m_tranSensors[SP].y); 

if(HowOften == 0)

{

     m_vecFeelers.push_back(-l);    

     continue;

if(HowOften < 10)

{

     m_vecFeelers.push_back(0);

     continue;

}    

if(HowOften < 20)

{

 m_vecFeelers.push_back(0.2);    

     continue;

if(HowOften < 30)

{

m_vecFeelers.push_back(O.4); 

     continue;

}

if(HoNOften < 50)

{

m_vecFeelers.push_back(0.6); 

     continue;

}

If(HowOften < 80)

(

m_vecFeelers.push_back(0.8); 

     continue;

}    

m_vecFeelers.push_back(1);

 

        由于把输入标准化后再送入网络是一种好的办法,所以我们把加入m_vecFeelers的任何值v都定在-1< v < 1的范围之间。如果一个小方格以前从来没有被扫雷机访问过,则触觉器由此小方格读出的数值为 -1。如果小方格已被访问过,则触觉器由此小方格读出的数值将在0和1之间。在小方格中呆过的时间愈长,从它得到的读数v也愈大。

        你可能想知道,这种具有滑动(slide)范围的记分法有什么意义?如果让触觉器为所有未访问的单元返回-1、为所有已访问单元,不分访问次数多少,统统返回1,不行吗?为此,我们下面来考察2个图,通过它们的对照就能得到最好的解释。我们先假设后一种情况为真,也就是触觉器仅仅返回-1或1两种读数。现考察图8.7。

 

图8.7  哦哦!我出不去了 

        图中的数字显示了扫雷机在每一方格所经历的时间的多少。没有标明数字的方格代表从未访问的小方格。这一幅图对于一个扫雷机来说是一个非常普通的场景图(scenario)。现由于触觉器只能为每一访问过的小方格读出1,这样,图中的扫雷机就会由于无法获得寻找出路的线索而被绝望地粘在这个位置。它的触觉器无论触到哪里,所得到的读数都是相同的一个值:1。 

       但是当触觉器读数采用在一个范围内可以滑动变化的数据时,则你能看到,神经网络通过学习就有可能指导扫雷机朝着数值较少的方格走并最终找到出路。见图8.8。 

   我也已把其他的输入包含到进神经网络,这就是成员变量m_bCollided。这一变量能够明析告诉扫雷机它目前是否已和一个障碍物相撞,并对其如何完成操作有帮助。(当你利用本程序来玩游戏时,你不妨删除这一输入,考察网络的演化时间有多长。)

 

图8.8  探索到达自由的路

 

图8.9  递归网络


小技巧

        某些工作采用少量的短期记忆(short-term memory)是有益的。短期记忆可以通过把神经网络的输出直接连接到输入的办法来实现。例如,对于扫雷机,你要创建一个网络,它带有2个附加的输入端,然后把前一个代的输出(m_ltrack和m_rtrack)作为2个附加输入端的输入,如图8.9所示。这种类型的网络叫递归网络(recurrent network)。

        这一想法还可以推广,把前一代的任意多个输出实行反馈。但是,这将要大大减少网络的处理速度,因而要尽量避免这样做。在一个游戏中,你希望你的网络总是要尽可能快的。(我想你多少已经知道一点!)


 8.2.1适应性函数(The Fitness Function)  
 

          我们可以把2.1版的适应性函数,与记忆在小方格中的分数结合在一起,来构作新的适应性函数。但这样做并不好,我们完全可以仅仅考察小方格中的分数,这样还能更快地得到结果。为了尽可能多地访问小方格,扫雷机将会自动地去学习怎样避免与障碍物碰撞和避免绕圈子打转,因为要完成这两种动作的任何一个,都会减慢扫雷机的速度,从而产生较低的分数!哈哈,真是聪明吧?

       现在已到了运行版本2.2 可执行文件、并观察会出现什么的好时光了。你可以看到,当遗传算法执行100-150个代之后,扫雷机的性能就已经非常好了。 

       表8.2显示了本工程中我所用的缺省值设置。

 

表8.2  Smart Sweepers v2.2工程的缺省设置

神经网络

参     数

设 置 值

输入数目

11

输出数目

2

隐藏层数目

1

隐藏层神经元数目

10

激励响应

1

 

遗 传 算 法

参   数

设 置 值

群体大小

45

选择类型

锦标赛式

竞赛参与者数目

5

杂交类型

2点

杂交率

0.7

突变率

0.1

精英设置(on/off)

On

精英数目(N/copies)

4/1

 

总 体 特 性

参   数

设 置 值

触觉器数目

5

触觉器长度(pixel数)

25

每一代(epoch)的帧数目

2000

8.3本章小结(Summary)

       我现在希望你自己头脑中神经细胞已像国庆节晚上的焰火表演一样在点火了。如果我做的工作没有错的话,你到晚上应该已很难入睡,因为所有的时兴想法都会绕着你的头顶飞舞起来。且你对技术愈是精通,你能看到的它的应用领域也愈多。需要说明的是,利用神经网络来解决的问题不一定都要是大事情,如用来控制FPS游戏[译注1]中bot(机器人)的每一个动作那样。要构造一个网络来解决这样复杂的控制,说实在,有点太乐观主义了(尽管也有人已经在试验这样的工程了)。你只要用它们来处理游戏代理的人工智能的一部分的工作就行了。 

      按照作者本人的观点,神经网络最好是以模块的方式来加以应用。例如,你可以为游戏中诸如追击(pursuit)、逃跑(flee)、侦察(explore)、聚会(gather),等特殊种类的行为分别进行训练,然后用另一个神经网络作为一状态机,来选择在每一个给定的时间内与哪一种行为相关。你甚至也可以设计一些网络,然后使用一台简单的有限状态机在每一个给定的时间来选择一种合适的网络。 

       一个出色的应用就是利用神经网络来为Quake型[译注2]第一人称射击类游戏中的机器人(bot)计算射击瞄准。凡是玩过目前已有的Bot游戏的人几乎总能得出这样的一个结论:瞄准的人工智能需要大大加以改进。当bot们采用较高一级水平游戏时,它们发出的30% 的射击几乎是荒谬地不像样:它们正是太精确了。简直就可以和电影A Fistful of Dollars!中的Client Eastwood[译注3]进行对赛了!但你很容易利用神经网络来对它进行改进,你可以设计一个带有可见度等级(明亮/模糊/黑暗)、可见目标的数量、到达目标的距离、以及当前选择的武器等参数作为输入,一个确定与目标中心的距离半径作为输出的网络。这一神经网络经过训练后就能给出更为实际的瞄准行为。 

        那是否有可能在一个汽车赛游戏中用神经网络来控制汽车呢?当然行!事实上人们已经做成功了。在Colin McRae Rally的第2版[译注4]中,赛车就是由一个神经网络来驾驶的,它已被训练到在赛道上奔跑。 

       但我提到过的有些思想利用遗传算法去演化网络的权重是很难实现的。有时采用监督式的训练是解决问题的最好方法,这就是我在下一个章中要谈论的一种方法。

 

[译注1] FPS为First-PersonShooter的速写,FPS游戏即“第一人称射击类游戏”。

[译注2] Quake型 First-Person Shooter中译为“雷神型”第一人称射击类游戏。

[译注3] A Fistful ofDollars原为一美国电影,中文译为“反恐精英”。Client Eastwood 在片中扮演了男主角。

[译注4] Colin McRae Rally 为一车赛游戏,中译为“科林麦克雷拉力赛车游戏”,目前已出5版。


8.4 练习题(Stuff to Try)

l. 把工程的一些参数设置加入到基因组中,使它们能被遗传算法所演化。例如,使能

对触觉器的长度的个数等参数进行演化。

2. 在游戏世界中再加入几项东西让扫雷机去寻找。

3. 演化扫雷机,使它能避免与其他扫雷机相互碰撞。

4. 创建一个神经网络,为第6章中的登月者实现导航。

5. 演化神经网络,根据Tron[译注],来玩光圈游戏(light circle game)。

这一游戏如果你是第一次碰到,要实现决不容易。事实上,你几乎注定要失败。但请你相信,尽管你没有能够完成这一练习,失败的教训也是很有价值的。你能解决一个困难在你工作开始之前的问题吗?

6. 把可视化加入程序,使你能实时看到神经网络和权重的变化。

  

 

 [译注] Tron是一个赛车类游戏,中文译为“横冲直撞赛车类游戏”。可以在网上找到免费下载的网站,现在的最高版为6.2。


  以上两个两连载为《游戏编程中的人工智能技术》第8章的内容,其完整程序我将放到zzwu的下载中,读者可去那里下载

0 0