当前位置: 首页 > news >正文

亳州网站建设费用品牌网站建设创意新颖

亳州网站建设费用,品牌网站建设创意新颖,商品营销推广的方法有哪些,公众号自己做电影网站原文#xff1a;http://blog.csdn.net/ecitnet/article/details/1799444 游戏编程中的人工智能技术.. (连载之一)用平常语言介绍神经网络(Neural Networks in Plain English)因为我们没有很好了解大脑#xff0c;我们经常试图用最新的技术作为一种模型来解释它。在我童年…  原文http://blog.csdn.net/ecitnet/article/details/1799444 游戏编程中的人工智能技术. .  (连载之一) 用平常语言介绍神经网络(Neural Networks in Plain English) 因为我们没有很好了解大脑我们经常试图用最新的技术作为一种模型来解释它。在我童年的时候我们都坚信大脑是一部电话交换机。(否 则它还能是什么呢)我当时还看到英国著名神经学家谢林顿把大脑的工作挺有趣地比作一部电报机。更早些时候弗罗伊德经常把大脑比作一部水力发电机而莱 布尼茨则把它比作了一台磨粉机。我还听人说古希腊人把大脑功能想象为一付弹弓。显然目前要来比喻大脑的话那只可能是一台数字电子计算机了。                                                                     John R.Searle[注1] 神经网络介绍Introduction to Neural Networks   曾有很长一个时期人工神经网络对我来说是完全神秘的东西。当然有关它们我在文献中已经读过了我也能描述它们的结构和工作机理但我始终没有能“啊 哈”一声如同你头脑中一个难于理解的概念有幸突然得到理解时的感觉那样。我的头上好象一直有个榔头在敲着或者像电影Animal House(中文片名为“动物屋”)中那个在痛苦地尖叫“先生谢谢您再给我一个啊”的可怜家伙那样。我无法把数学概念转换成实际的应用。有时我甚至 想把我读过的所有神经网络的书的作者都抓起来把他们缚到一棵树上大声地向他们吼叫“不要再给我数学了快给我一点实际东西吧”。但无需说这是永远不可能发生的事情。我不得不自己来填补这个空隙...由此我做了在那种条件下唯一可以做的事情。我开始干起来了。一笑 这 样几个星期后在一个美丽的日子里当时我在苏格兰海边度假当我越过一层薄雾凝视着狭长的海湾时我的头脑突然受到一个冲击。一下子悟到了人工神经网络 是怎样工作的。我得到“啊哈”的感觉了但我此时身边只有一个帐篷和一个睡袋还有半盒子的脆玉米片没有电脑可以让我迅速写出一些代码来证实我的直 觉。Arghhhhh这时我才想到我应该买一台手提电脑。不管怎样几天后我回到家了我立刻让我的手指在键盘上飞舞起来。几个小时后我的第一人工神经 网络程序终于编成和运行了并且工作得挺好自然代码写的有点乱需要进行整理但它确实已能工作了并且更重要的是我还知道它为什么能工作我可 以告诉你那天我是一位非常得意的人。 我希望本书传递给你的就是这种“啊哈”感觉。当我们学完遗传算法时你可能已尝到了一点感觉但你希望这种感觉是美妙的话那就要等把神经网络部分整个学完。生物学的神经网络-大脑              (A Biological Neural Network–The Brain).... 你的大脑是一块灰色的、像奶冻一样的东西。它并不像电脑中的CPU那样利用单个的处理单元来进行工作。如果你有一具新鲜地保存到福尔马林中的尸体用一 把锯子小心地将它的头骨锯开搬掉头盖骨后你就能看到熟悉的脑组织皱纹。大脑的外层象一个大核桃那样全部都是起皱的[图0左]这一层组织就称皮层(Cortex)。如果你再小心地用手指把整个大脑从头颅中端出来再去拿一把外科医生用的手术刀将大脑切成片那么你将看到大脑有两层[图0右]: 灰色的外层(这就是“灰质”一词的来源但没有经过福尔马林固定的新鲜大脑实际是粉红色的。) 和白色的内层。灰色层只有几毫米厚其中紧密地压缩着几十亿个被称作neuron神经细胞、神经元的微小细胞。白色层在皮层灰质的下面占据了皮层的 大部分空间是由神经细胞相互之间的无数连接组成。皮层象核桃一样起皱这可以把一个很大的表面区域塞进到一个较小的空间里。这与光滑的皮层相比能容纳更 多的神经细胞。人的大脑大约含有1OG即100亿个这样的微小处理单元;一只蚂蚁的大脑大约也有250,OOO个。 以下表l显示了人和几种动物的神经细胞的数目。 图0-1 大脑半球像核桃图0-2 大脑皮层由灰质和白质组成 图0  大脑的外形和切片形状 表l 人和几种动物的神经细胞的数目 动 物  神经细胞的数目数量级  蜗 牛  10,000 10^4  蜜 蜂  100,000 10^5  蜂 雀  10,000,000 10^7  老 鼠  100,000,000 10^8  人 类   10,000,000,000 10^10  大 象   100,000,000,000 10^11   图1神经细胞的结构    在人的生命的最初9个月内这些细胞以每分钟25,000个的惊人速度被创建出来。神经细胞和人身上任何其他类型细胞十分不同每个神经细胞都长着一根 像电线一样的称为轴突axon的东西它的长度有时伸展到几厘米译注用来将信号传递给其他的神经细胞。神经细胞的结构如图1所 示。它由一个细胞体(soma)、一些树突(dendrite) 、和一根可以很长的轴突组成。神经细胞体是一颗星状球形物里面有一个核(nucleus)。树突由细胞体向各个方向长出本身可有分支是用来接收信号 的。轴突也有许多的分支。轴突通过分支的末梢(terminal)和其他神经细胞的树突相接触,形成所谓的突触Synapse图中未画出一个神经 细胞通过轴突和突触把产生的信号送到其他的神经细胞。 每个神经细胞通过它的树突和大约10,000个其他的神经细胞相连。这就使得你的头脑中所有神经细胞之间连接总计可能有l,000,000,000,000,000个。这比100兆个现代电话交换机的连线数目还多。所以毫不奇怪为什么我们有时会产生头疼毛病 有趣的事实        曾经有人估算过如果将一个人的大脑中所有神经细胞的轴突和树突依次连接起来并拉成一根直线可从地球连到月亮再从月亮返回地球。如果把地球上所有人脑的轴突和树突连接起来则可以伸展到离开们最近的星系 神 经细胞利用电-化学过程交换信号。输入信号来自另一些神经细胞。这些神经细胞的轴突末梢也就是终端和本神经细胞的树突相遇形成突触 synapse信号就从树突上的突触进入本细胞。信号在大脑中实际怎样传输是一个相当复杂的过程但就我们而言重要的是把它看成和现代的计算机一 样利用一系列的0和1来进行操作。就是说大脑的神经细胞也只有两种状态兴奋fire和不兴奋即抑制。发射信号的强度不变变化的仅仅是频 率。神经细胞利用一种我们还不知道的方法,把所有从树突上突触进来的信号进行相加如果全部信号的总和超过某个阀值就会激发神经细胞进入兴奋 fire状态这时就会有一个电信号通过轴突发送出去给其他神经细胞。如果信号总和没有达到阀值神经细胞就不会兴奋起来。这样的解释有点过分简单 化但已能满足我们的目的。  神经细胞利用电-化学过程交换信号。输入信号来自另一些神经细胞。这些神经细胞的轴突末梢也就是终端 和本神经细胞的树突相遇形成突触synapse信号就从树突上的突触进入本细胞。信号在大脑中实际怎样传输是一个相当复杂的过程但就我们而言重 要的是把它看成和现代的计算机一样利用一系列的0和1来进行操作。就是说大脑的神经细胞也只有两种状态兴奋fire和不兴奋即抑制。发射信 号的强度不变变化的仅仅是频率。神经细胞利用一种我们还不知道的方法,把所有从树突上突触进来的信号进行相加如果全部信号的总和超过某个阀值就会激 发神经细胞进入兴奋fire状态这时就会有一个电信号通过轴突发送出去给其他神经细胞。如果信号总和没有达到阀值神经细胞就不会兴奋起来。这样的 解释有点过分简单化但已能满足我们的目的。 正是由于数量巨大的连接使得大脑具备难以置信的能力。尽管每一个神经细胞仅仅工作于大约100Hz的频率但因各个神经细胞都以独立处理单元的形式并行工作着使人类的大脑具有下面这些非常明显的特点  能实现无监督的学习。 有 关我们的大脑的难以置信的事实之一就是它们能够自己进行学习而不需要导师的监督教导。如果一个神经细胞在一段时间内受到高频率的刺激则它和输入信号 的神经细胞之间的连接强度就会按某种过程改变使得该神经细胞下一次受到激励时更容易兴奋。这一机制是50多年以前由Donard Hebb在他写的Organination of Behavior一书中阐述的。他写道: “当神经细胞 A的一个轴突重复地或持久地激励另一个神经细胞B后则其中的一个或同时两个神经细胞就会发生一种生长过程或新陈代谢式的变化使得励 B细胞之一的A细胞它的效能会增加” 与此相反的就是如果一个神经细胞在一段时间内不受到激励那么它的连接的有效性就会慢慢地衰减。这一现象就称可塑性plasticity。 对损伤有冗余性(tolerance)。大 脑即使有很大一部分受到了损伤它仍然能够执行复杂的工作。一个著名的试验就是训练老鼠在一个迷宫中行走。然后科学家们将其大脑一部分一部分地、越来越 大地加以切除。他们发现即使老鼠的很大的一部大脑被切除后它们仍然能在迷宫中找到行走路径。这一事实证明了在大脑中知识并不是保存在一个局部地 方。另外所作的一些试验则表明如果大脑的一小部分受到损伤则神经细胞能把损伤的连接重新生长出来。 处理信息的效率极高。神 经细胞之间电-化学信号的传递与一台数字计算机中CPU的数据传输相比速度是非常慢的但因神经细胞采用了并行的工作方式使得大脑能够同时处理大量 的数据。例如大脑视觉皮层在处理通过我们的视网膜输入的一幅图象信号时大约只要100ms的时间就能完成。考虑到你的神经细胞的平均工作频率只有 100Hz100ms的时间就意味只能完成10个计算步骤想一想通过我们眼睛的数据量有多大你就可以看到这真是一个难以置信的伟大工程了。 善于归纳推广。大脑和数字计算机不同它极擅长的事情之一就是模式识别并能根据已熟悉信息进行归纳推广(generlize)。例如我们能够阅读他人所写的手稿上的文字即使我们以前从来没见过他所写的东西。 它是有意识的。意 识consciousness是神经学家和人工智能的研究者广泛而又热烈地在辩论的一个话题。有关这一论题已有大量的文献出版了但对于意识实际究竟 是什么至今尚未取得实质性的统一看法。我们甚至不能同意只有人类才有意识或者包括动物王国中人类的近亲在内才有意识。一头猩猩有意识吗你的猫有意识 吗上星期晚餐中被你吃掉的那条鱼有意识吗 因此一个人工神经网络(Artificial neural network,简称ANN)就是要在当代数字计算机现有规模的约束下来模拟这种大量的并行性,并在实现这一工作时使它能显示许多和生物学大脑相类似的特性。下面就让我们瞧瞧它们的表演吧   游戏编程中的人工智能技术. .(连载之二) 3 数字版的神经网络 (The Digital Version) 上面我们看到了生物的大脑是由许多神经细胞组成同样模拟大脑的人工神经网络ANN是由许多叫做人工神经细胞Artificial neuron也称人工神经原或人工神经元的细小结构模块组成。人工神经细胞就像真实神经细胞的一个简化版但采用了电子方式来模拟实现。一个人工神 经网络中需要使用多少个数的人工神经细胞差别可以非常大。有的神经网络只需要使用10个以内的人工神经细胞而有的神经网络可能需要使用几千个人工神经 细胞。这完全取决于这些人工神经网络准备实际用来做什么。  有趣的事实       有 一个叫 Hugo de Garis的同行曾在一个雄心勃勃的工程中创建并训练了一个包含1000,000,000个人工神经细胞的网络。这个人工神经网络被他非常巧妙地建立起 来了它采用蜂房式自动机结构目的就是为一机器客户定制一个叫做CAM BrainMachine(“CAM大脑机器”) 的机器CAM就是Cellular Automata Machine的缩写。此人曾自夸地宣称这一人工网络机器将会有一只猫的智能。许多神经网络研究人员认为他是在“登星”了但不幸的是雇用他的公司在 他的梦想尚未实现之前就破产了。此人现在犹他州是犹他州大脑工程Utah Brain Project的领导。时间将会告诉我们他的思想最终是否能变成实际有意义的东西。[译注]     我想你现在可能很想知道一个人工神经细胞究竟是一个什么样的东西但是它实际上什么东西也不像; 它只是一种抽象。还是让我们来察看一下图2吧这是表示一个人工神经细胞的一种形式。 [译注]Hugo de Garis现在为犹他州立大学教授有关他和他的CAM机器可在该校网站的一个网页上看到报道其上有真实的照片见http://www.cs.usu.edu/~degaris 图2 一个人工神经细胞 图中左边几个灰底圆中所标字母w代表浮点数称为权重weight或权值权数。进入人工神经细胞的每一个input(输入)都与一个权重w相 联系正是这些权重将决定神经网络的整体活跃性。你现在暂时可以设想所有这些权重都被设置到了-和之间的一个随机小数。因为权重可正可负故能对与它 关联的输入施加不同的影响如果权重为正就会有激发excitory作用权重为负则会有抑制inhibitory作用。当输入信号进入神经 细胞时它们的值将与它们对应的权重相乘作为图中大圆的输入。大圆的‘核’是一个函数叫激励函数(activation function)它把所有这些新的、经过权重调整后的输入全部加起来形成单个的激励值(activation value)。激励值也是一浮点数且同样可正可负。然后再根据激励值来产生函数的输出也即神经细胞的输出如果激励值超过某个阀值作为例子我们假设 阀值为1.0就会产生一个值为1的信号输出如果激励值小于阀值1.0则输出一个0。这是人工神经细胞激励函数的一种最简单的类型。在这里从激励 值产生输出值是一个阶跃函数[译注]。看一看图3后你就能猜到为什么有这样的名称。 图3 阶跃激励函数 [译注] 由图可知阶跃函数是一元的而激励函数既然能把多个输入相加应为多元故需加以区别。 如果到目前为止你对这些还没有获得很多感觉那也不必担心。窍门就是: 不要企图去感觉它暂时就随波逐流地跟我一起向前走吧。在经历本章的若干处后你最终就会开始弄清楚它们的意义。而现在就放松一点继续读下去吧。 3.1 现在需要一些数学了Now for Some Math    今后讨论中我将尽量把数学降低到绝对少量但学习一些数学记号对下面还是很有用的。我将把数学一点一点地喂给你在到达有关章节时向你介绍一些新概 念。我希望采用这样的方式能使你的头脑能更舒适地吸收所有的概念并使你在开发神经网络的每个阶段都能看到怎样把数学应用到工作中。现在首先让我们来看一 看怎样把我在此之前告诉你的所有知识用数学方式表达出来。 一个人工神经细胞(从现在开始我将把“人工神经细胞”简称它为“神经细胞”) 可以有任意n个输入n代表总数。可以用下面的数学表达式来代表所有n个输入 x1, x2, x3, x4, x5, ..., xn 同样 n 个权重可表达为: w1, w2, w3, w4, w5 ..., wn 请记住激励值就是所有输入与它们对应权重的之乘积之总和因此现在就可以写为: a w1x1 w2x2 w3x3 w4x4 w5x5 ... wnxn 以这种方式写下的求和式我在第5章“建立一个更好的遗传算法”中已提到可以用希腊字母Σ来简化 注 神经网络的各个输入以及为各个神经细胞的权重设置都可以看作一个n维的向量。你在许多技术文献中常常可以看到是以这样的方式来引用的。  下面我们来考察在程序中应该怎样实现假设输入数组和权重数组均已初始化为x[n]和w[n]则求和的代码如下: double activation 0;for(int i0; in; i) { activation x[i] * w[i]; } 图4以图形的方式表示了此方程。请别忘记如果激励值超过了阀值神经细胞就输出1; 如果激活小于阀值则神经细胞的输出为0。这和一个生物神经细胞的兴奋和抑制是等价的。我们假设一个神经细胞有5个输入他们的权重w都初始化成正负1之 间的随机值(-1 w 1) 。 表2说明了激励值的求和计算过程。 图4 神经细胞的激励函数 如果我们假定激活所需阀值1则因激励值1.1 激活阀值所以这个神经细胞将输出1。  在进一步读下去之前请你一定要确切弄懂激励函数怎样计算。   表2  神经细胞激励值的计算 输 入  权 重  输入*权重的乘积  运行后总和  1  0.5  0.5  0.5  0 -0.2  0  0.5 1  -0.3  -0.3   0.2  1  0.9  0.9   1.1  0 0.1   0  1.1  3.2  行我知道什么是神经细胞了但用它来干什么呢    大脑里的生物神经细胞和其他的神经细胞是相互连接在一起的。为了创建一个人工神经网络人工神经细胞也要以同样方式相互连接在一起。为此可以有许多不同 的连接方式其中最容易理解并且也是最广泛地使用的就是如图5所示那样把神经细胞一层一层地连结在一起。这一种类型的神经网络就叫前馈网络 feedforword network。这一名称的由来就是因为网络的每一层神经细胞的输出都向前馈送feed到了它们的下一层在图中是画在它的上面的那一层)直到 获得整个网络的输出为止。      图5 一个前馈网络 由图可知网络共有三层译注输入层不是神经细胞神经细胞只有两层。输入层中的每个输入都馈送到了隐藏层作为该层每一个神经细胞的输入然 后从隐藏层的每个神经细胞的输出都连到了它下一层即输出层的每一个神经细胞。图中仅仅画了一个隐藏层作为前馈网络一般地可以有任意多个隐藏层。 但在对付你将处理的大多数问题时一层通常是足够的。事实上有一些问题甚至根本不需要任何隐藏单元你只要把那些输入直接连结到输出神经细胞就行了。另 外我为图5选择的神经细胞的个数也是完全任意的。每一层实际都可以有任何数目的神经细胞这完全取决于要解决的问题的复杂性。但神经细胞数目愈多网络 的工作速度也就愈低由于这一缘故以及为了其他的几种原因我将在第9章作出解释网络的规模总是要求保持尽可能的小。   到此我能想象你或许已对所有这些信息感到有些茫然了。我认为在这种情况下我能做的最好的事情就是向你介绍一个神经网络在现实世界中的实际应用例子它有望使你自己的大脑神经细胞得到兴奋不错吧好的下面就来了...     你可能已听到或读到过神经网络常常用来作模式识别。这是因为它们善于把一种输入状态它所企图识别的模式映射到一种输出状态它曾被训练用来识别的模式。   下面我们来看它是怎么完成的。我们以字符识别作为例子。设想有一个由8x8个格子组成的一块面板。每一个格子里放了一个小灯每个小灯都可独立地被打开格子变亮或关闭格子变黑这样面板就可以用来显示十个数字符号。图6显示了数字“4”。 图6  用于字符显示的矩阵格点 要解决这一问题我们必需设计一个神经网络它接收面板的状态作为输入然后输出一个1或0输出1代表ANN确认已显示了数字“4”而输出0表示没 有显示“4”。因此神经网络需要有64个输入(每一个输入代表面板的一个具体格点) 和由许多神经细胞组成的一个隐藏层还有仅有一个神经细胞的输出层隐藏层的所有输出都馈送到它。我真希望你能在你的头脑中画出这个图来因为要我为你把 所有这些小圆和连线统统画出来确实不是一桩愉快的事一笑。   一旦神经网络体系创建成功后它必须接受训练来认出数字 “4”。为此可用这样一种方法来完成先把神经网的所有权重初始化为任意值。然后给它一系列的输入在本例中就是代表面板不同配置的输入。对每一种输入 配置我们检查它的输出是什么并调整相应的权重。如果我们送给网络的输入模式不是“4” 则我们知道网络应该输出一个0。因此每个非“4”字符时的网络权重应进行调节使得它的输出趋向于0。当代表“4”的模式输送给网络时则应把权重调整到 使输出趋向于1。   如果你考虑一下这个网络你就会知道要把输出增加到10是很容易的。然后通过训练就可以使网络能识别0到9 的所有数字。但为什么我们到此停止呢我们还可以进一步增加输出使网络能识别字母表中的全部字符。这本质上就是手写体识别的工作原理。对每个字符网络 都需要接受许多训练使它认识此文字的各种不同的版本。到最后网络不单能认识已经训练的笔迹还显示了它有显著的归纳和推广能力。也就是说如果所写文 字换了一种笔迹它和训练集中所有字迹都略有不同网络仍然有很大几率来认出它。正是这种归纳推广能力使得神经网络已经成为能够用于无数应用的一种无价 的工具从人脸识别、医学诊断直到跑马赛的预测另外还有电脑游戏中的bot作为游戏角色的机器人的导航或者硬件的robot真正的机器人的 导航。      这种类型的训练称作有监督的学习supervised learnig用来训练的数据称为训练集training set。调整权重可以采用许多不同的方法。对本类问题最常用的方法就是反向传播backpropagation简称backprop或BP方法。 有关反向传播问题我将会在本书的后面当你已能训练神经网络来识别鼠标走势时再来进行讨论。在本章剩余部分我将集中注意力来考察另外的一种训练方式 即根本不需要任何导师来监督的训练或称无监督学习unsupervised learnig。      这样我已向你介绍了一些基本的知识现在让我们来考察一些有趣的东西并向你介绍第一个代码工程。 游戏编程中的人工智能技术  (连载之三) 4. 聪明的扫雷机工程Smart Minesweeper Project        我要向你介绍的第一个完整例子是怎么使用神经网络来控制具有人工智能的扫雷机的行为。扫雷机工作在一个很简单的环境中那里只有扫雷机以及随机散布的许多地雷。 图7 运行中的演示程序。     尽管书上图形画成了黑白色但当你运行程序时性能最好的扫雷机将显现为红色。地雷你可能已经猜到就是那些小方形。工程的目标是创建一个网络它不需 要从我们这里得到任何帮助就能自己进行演化evolve去寻找地雷。为了实现这一功能网络的权重将被编码到基因组中并用一个遗传算法来演化它 们。         怎么样很酷吧 提示重要      如果你跳过前面的一些章节来到这里而你又不了解怎样使用遗  传算法则在进一步阅读下面的内容之前你应回到前面去补读一下有关遗传算法的内容。         首先让我解释人工神经网络(ANN)的体系结构。我们需要决定输入的数目、输出的数目、还有隐藏层和每个隐藏层中隐藏单元的数目。 4.1 选择输出Choosing the Outputs        那么人工神经网络怎样控制扫雷机的行动呢很好我们把扫雷机想象成和坦克车一样通过左右2个能转动的履带式轮轨track来行动的。见图案9.8。  图8  扫雷机的控制    扫雷机向前行进的速度以及向左、向右转弯的角度都是通过改变2个履带轮的相对速度来实现的。因此神经网络需要2个输入1个是左侧履带轮的速度另一个是右侧履带轮的速度。   啊但是...,我听见你在嘀咕了。如果网络只能输出一个或一个我们怎么能控制车轨移动的快慢呢 你是对的如果利用以前描述的阶跃函数来决定输出我们就根本无法控制扫雷机实际移动。幸好我有一套戏法让我卷起袖子来把激励函数的输出由阶跃式改 变成为在之间连续变化的形式这样就可以供扫雷机神经细胞使用了。为此有几种函数都能做到这样我们使用的是一个被称为逻辑斯蒂S形函数 logistic sigmoid function[译注1]。该函数所实现的功能本质上说就是把神经细胞原有的阶跃式输出曲线钝化为一光滑曲线后者绕y轴0.5处点对称[译注 2]如图9所示。 [译注1] logistic有’计算的’或’符号逻辑的’等意思在内和’逻辑的(logic)’意义不同。[译注2] 点对称图形绕对称点转180度后能与原图重合。若f(x)以原点为点对称,则有f(-x)-f(x) 图9 S形曲线。    当神经细胞的激励值趋于正、负无穷时S形函数分别趋于或。负的激励值对应的函数值都0.5; 正激励值对应的函数值都0.5。S形函数用数学表达式写出来则为:                     这个方程看上去可能会吓唬一些人但其实很简单。e是数学常数近似等于2.7183a是神经细胞的激励值它是函数的自变量而p是一个用来控制曲 线形状变化快慢或陡峭性的参数。p通常设定为1。当p赋以较大值时曲线就显得平坦反之就会使曲线变为陡峭。见图1O。很低的p值所生成的函数就和阶 跃函数近似。P值的大小用来控制何时使神经网络由低变高开始翻转有很大作用但是在本例子中我们将它保持为。 注“S型”的英文原名Sigmoid 或Sigmoidal 原来是根据希腊字“Sigma”得来的但非常巧它也可以说成是曲线的一种形状。   图7。10  不同的S形响应曲线。 4.2 选择输入Choosing the Inputs   上面我们已经把输出安排好了现在我们来考虑输入确定网络需要什么样的输入为此我们必须想象一下扫雷机的具体细节需要什么样的信息才能使它朝地雷前进你可能想到的第一个输入信息清单是 扫雷机的位置(x1,y1) 与扫雷机最靠近的地雷的位置(x2,y2) 代表扫雷机前进方向的向量(x3,y3)     这样一共得到6个输入。但是要网络使用这些输入工作起来就非常困难因为网络在像我们希望的那样执行工作之前必须寻找所有6个输入之间的数学关 系而这有相当工作量。可以把此作为一个练习倒是很理想的去试试如何给出最少数量的输入而仍能为网络传达解决问题所需要的全部信息。 你的网络使用的输入愈少网络所要求的神经细胞数目也愈少。而较少的神经细胞就意味更快速的训练和更少的计算有利于网络更高速度的工作。   只要作少量的额外考虑就能够把输入的个数减少为4这就是图11中所画出的两个向量的个参数。        把神经网络的所有输入进行规范化是一种好想法。这里的意思并不是说每个输入都要改变大小使它们都在01间而是说每一个输入应该受到同等重视。例如拿 我们已经讨论过的扫雷机输入为例。瞄准向量或视线向量look-at vector总是一个规范化向量即长度等于1分量x和y都在01间。但从扫雷机到达其最近地雷的向量就可能很大其中的一个分量甚至有可能和窗体 的宽度或高度一样大。如果这个数据以它的原始状态输入到网络网络对有较大值的输入将显得更灵敏由此就会使网络性能变差。因此在信息输入到神经网络中 去之前数据应预先定比scaled和标准化standardized使它们大小相似similar。在本特例中由扫雷机引到与其最接近 地雷的向量需要进行规范化normalized。这样可以使扫雷机的性能得到改良。   图11 选择输入。     小技巧    有时你把输入数据重新换算rescale一下使它以0点为中心就能从你的神经网络获得最好的性能。这一小窍门在你设计网络时永远值得一试。但我在扫雷机工程中没有采用这一方法这是因为我想使用一种更直觉的方法。   4.3 隐藏的神经细胞要多少How many Hidden Neurons?    到此我们已把输入、输出神经细胞的数目和种类确定下来了下一步是确定隐藏层的数目并确定每个隐藏层中神经细胞必须有多少但遗憾的是还没有一种确 切的规则可用来计算这些。它们的开发又需要凭个人的“感觉”了。某些书上和文章中确实给过一些提纲性的东西,告诉你如何去决定隐藏神经细胞个数但业内专 家们的一致看法是你只能把任何建议当作不可全信的东西主要还要靠自己的不断尝试和失败中获得经验。但你通常会发现你所遇到的大多数问题都只要用一个 隐藏层就能解决。所以本领的高低就在于如何为这一隐藏层确定最合适的神经细胞数目了。显然个数是愈少愈好因为我前面已经提及数目少的神经细胞能够 造就快速的网络。通常为了确定出一个最优总数我总是在隐藏层中采用不同数目的神经细胞来进行试验。我在本章所编写的神经网络工程的.    第一版本中一共使用了10个隐藏神经细胞当然我的这个数字也不一定是最好的一笑。你应围绕这个数字的附近来做游戏并观察隐藏层 神经细胞的数目对扫雷机的演化会产生什么样的影响。不管怎样理论已经够了让我们拿一个具体程序来看看吧你可以在本书所附光盘的 Chapter7/Smart Sweepers v1.0文件夹中找到本章下面几页即将描述的所有程序的源码。 游戏编程中的人工智能技术. .(连载之四)   4.4  CNeuralNet.h神经网络类的头文件   在CNeuralNet.h 文件中我们定义了人工神经细胞的结构、定义了人工神经细胞的层的结构、以及人工神经网络本身的结构。首先我们来考察人工神经细胞的结构。 4.4.1  SNeuron神经细胞的结构 这是很简单的结构。人工神经细胞的结构中必须有一个正整数来纪录它有多少个输入还需要有一个向量std:vector来表示它的权重。请记住神经细胞的每一个输入都要有一个对应的权重。 Struct SNeuron{     // 进入神经细胞的输入个数     int m_NumInputs;          // 为每一输入提供的权重     vectordouble m_vecWeight;          //构造函数     SNeuron(int NumInputs);  }; 以下就是SNeuron 结构体的构造函数形式: SNeuron::SNeuron(int NumInputs): m_NumInputs(NumInputs1) (     // 我们要为偏移值也附加一个权重因此输入数目上要 1     for (int i0; iNumInputs1; i)     {         // 把权重初始化为任意的值         m_vecWeight.push_back(RandomClamped());     } }   由上可以看出构造函数把送进神经细胞的输入数目NumInputs作为一个变元并为每个输入创建一个随机的权重。所有权重值在-1和1之间。        这是什么 我听见你在说。这里多出了一个权重不 错我很高兴看到你能注意到这一点因为这一个附加的权重十分重要。但要解释它为什么在那里我必须更多地介绍一些数学知识。回忆一下你就能记得激励值 是所有输入*权重的乘积的总和而神经细胞的输出值取决于这个激励值是否超过某个阀值(t)。这可以用如下的方程来表示: w1x1 w2x2 w3x3 ... wnxn t 上式是使细胞输出为的条件。因为网络的所有权重需要不断演化进化如果阀值的数据也能一起演化那将是非常重要的。要实现这一点不难你使用一个简单的诡计就可以让阀值变成权重的形式。从上面的方程两边各减去t得: w1x1 w2x2 w3x3 ... wnxn –t 0 这个方程可以再换用一种形式写出来如下: w1x1 w2x2 w3x3 ... wnxn t *(–1) 0 到此我希望你已能看出阀值t为什么可以想像成为始终乘以输入为 -的权重了。这个特殊的权重通常叫偏移bias这就是为什么每个神经细胞初始化时都要增加一个权重的理由。现在当你演化一个网络时你就不必再 考虑阀值问题因为它已被内建在权重向量中了。怎么样想法不错吧为了让你心中绝对敲定你所学到的新的人工神经细胞是什么样子请再参看一下图12。   图12 带偏移的人工神经细胞。 4.4.2  SNeuronLayer神经细胞层的结构   神经细胞层SNeuronLayer的结构很简单它定义了一个如图13中所示的由虚线包围的神经细胞SNeuron所组成的层。         图13 一个神经细胞层。 以下就是层的定义的源代码它应该不再需要任何进一步的解释: struct SNeuronLayer{    // 本层使用的神经细胞数目   int                     m_NumNeurons;       // 神经细胞的层   vectorSNeuron   m_vecNeurons;    SNeuronLayer(int NumNeurons, int NumInputsPerNeuron);} 4.4.3  CNeuralNet神经网络类 这是创建神经网络对象的类。让我们来通读一下这一个类的定义: class CNeuralNet{private:    int                m_NumInputs;     int                m_NumOutputs;     int                m_NumHiddenLayers;     int         m_NeuronsPerHiddenLyr;     // 为每一层包括输出层存放所有神经细胞的存储器     vectorSNeuronLayer  m_vecLayers; 所有private成员由其名称容易得到理解。需要由本类定义的就是输入的个数、输出的个数、隐藏层的数目、以及每个隐藏层中神经细胞的个数等几个参数。 public:      CNeuralNet(); 该构造函数利用ini文件来初始化所有的Private成员变量然后再调用CreateNet来创建网络。      // 由SNeurons创建网络     void    CreateNet(); 我过一会儿马上就会告诉你这个函数的代码。      // 从神经网络得到读出权重     vectordouble   GetWeights()const; 由于网络的权重需要演化所以必须创建一个方法来返回所有的权重。这些权重在网络中是以实数型向量形式表示的我们将把这些实数表示的权重编码到一个基因组中。当我开始谈论本工程的遗传算法时我将为您确切说明权重如何进行编码。     // 返回网络的权重的总数    int GetNumberOfWeights()const;     // 用新的权重代替原有的权重    void PutWeights(vectordouble weights);        这一函数所做的工作与函数GetWeights所做的正好相反。当遗传算法执行完一代时新一代的权重必须重新插入神经网络。为我们完成这一任务的是PutWeight方法。        // S形响应曲线    inline double  Sigmoid(double activation, double response);      当已知一个神经细胞的所有输入*重量的乘积之和时这一方法将它送入到S形的激励函数。      // 根据一组输入来计算输出     vectordouble Update(vectordouble inputs); 对此Update函数函数我马上就会来进行注释的。 }; // 类定义结束 4.4.3.1  CNeuralNet::CreateNet创建神经网络的方法 我在前面没有对CNeuralNet的2个方法加以注释这是因为我要为你显示它们的更完整的代码。这2个方法的第一个是网络创建方法 CreateNet。它的工作就是把由细胞层SNeuronLayers所收集的神经细胞SNeurons聚在一起来组成整个神经网络代码为:void CNeuralNet::CreateNet(){    // 创建网络的各个层    if (m_NumHiddenLayers 0)      {      //创建第一个隐藏层译注      m_vecLayers.push_back(SNeuronLayer(m_NeuronsPerHiddenLyr,                                           m_NumInputs));      for( int iO; im_NumHiddenLayers-l; i)     {        m_vecLayers.push_back(SNeuronLayer(m_NeuronsPerHiddenLyr                                                  m_NeuronsPerHiddenLyr));      } 译注如果允许有多个隐藏层则由接着for循环即能创建其余的隐藏层。      // 创建输出层      m_vecLayers.push_back(SNeuronLayer(m_NumOutput,m_NeuronsPerHiddenLyr));   } else //无隐藏层时只需创建输出层   {       // 创建输出层        m_vecLayers.push_back(SNeuronLayer(m_NumOutputs, m_NumInputs));   }} 4.4.3.2  CNeuralNet::Update神经网络的更新方法 Update函数(更新函数)称得上是神经网络的“主要劳动力”了。这里输入网络的数据input是以双精度向量std::vector的数据格式传 递进来的。Update函数通过对每个层的循环来处理输入*权重的相乘与求和再以所得的和数作为激励值通过S形函数来计算出每个神经细胞的输出正如 我们前面最后几页中所讨论的那样。Update函数返回的也是一个双精度向量std::vector它对应的就是人工神经网络的所有输出。 请你自己花两分钟或差不多的时间来熟悉一下如下的Update函数的代码这能使你正确理解我们继续要讲的其他内容: vectordouble CNeuralNet::Update(vectordouble inputs){     // 保存从每一层产生的输出     vectordouble outputs;      int cWeight 0;      // 首先检查输入的个数是否正确     if (inputs.size() ! m_NumInputs)      {          // 如果不正确就返回一个空向量          return outputs;      }      // 对每一层,...     for (int i0; im_NumHiddenLayers1; i)     {       if (iO)         {            inputs outputs;         }    outputs.clear();     cWeight 0;     // 对每个神经细胞,求输入*对应权重乘积之总和。并将总和抛给S形函数,以计算输出   for (int j0; jm_vecLayers[i].m_NumNeurons; j)        {          double netinput 0;               int NumInputs m_vecLayers[i].m_vecNeurons[j].m_NumInputs;              // 对每一个权重         for (int kO; kNumInputs-l; k)         {             // 计算权重*输入的乘积的总和。            netinput m_vecLayers[i].m_vecNeurons[j].m_vecWeight[k] *                    inputs[cWeight];         }             // 加入偏移值        netinput m_vecLayers[i].m_vecNeurons[j].m_vecWeight[NumInputs-1] *                    CParams::dBias; 别忘记每个神经细胞的权重向量的最后一个权重实际是偏移值这我们已经说明过了我们总是将它设置成为 –1的。我已经在ini文件中包含了偏移值你可以围绕它来做文章考察它对你创建的网络的功能有什么影响。不过这个值通常是不应该改变的。      // 每一层的输出当我们产生了它们后我们就要将它们保存起来。但用Σ累加在一起的     // 激励总值首先要通过S形函数的过滤才能得到输出outputs.push_back(Sigmoid(netinput,CParams::dActivationResponse)); cWeight 0:    }  }   return outputs; } 游戏编程中的人工智能技术. .(连载之五) 4.5 神经网络的编码Encoding the Network   在本书的开始几章中你已经看到过怎样用各种各样的方法为遗传算法编码。但当时我并没有向你介绍过一个用实数编码的具体例子因为我知道我要留在这里向你介绍。我曾经讲到为了设计一个前馈型神经网络编码是很容易的。我们从左到右读每一层神经细胞的权重读完第一个隐藏层再向上读它的下一层把所读到的数据依次保存到一个向量中这样就实现了网络的编码。因此如果我们有图14所示的网络则它的权重编码向量将为: 0.3, -O.8 -O.2 0.6 O.1 -0.l 0.4 0.5 在这一网络中为了简单我没有把偏移值的权重包括进去。但在实际实现编码时你必须包含偏移值这个权重否则你肯定无法获得你所需要的结果。 图14 为权重编码。 在此之前讲的事情你都懂了吗好极了那下面就让我们转来考虑怎样用遗传算法来操纵已编码的基因吧。 4.6  遗传算法The Genetic Algorithm        到此所有的权重已经象二进制编码的基因组那样形成了一个串我们就可以象本书早先讨论过的那样来应用遗传算法了。遗传算法GA是在扫雷机已被允许按照用户指定的帧数为了某种缘故, 我下面更喜欢将帧数称作滴答数英文是ticks运转后执行的。你可以在ini文件中找到这个滴答数iNumTicks的设置。下面是基因组结构体的代码。这些对于你应该是十分面熟的东西了。  Struct SGenome {  vector double   vecWeights;   double       dFitness;   SGenome():dFitness(0) {}   SGenome(vector double w, double f):vecWeights(w),dFitness(f){}   //重载的排序方法       friend bool  operator(const SGenome lhs, const SGenome rhs)         {             return (lhs.dFitness rhs.dFitness);         }  }; 从上面的代码你可看出这一SGenome结构和我们在本书所有其他地方见到的SGenome结构几乎完全一致唯一的差别就是这里的染色体是一个双精度向量std::vector。因此可以和通常一样来应用杂交操作和选择操作。但突变操作则稍微有些不同这里的权重值是用一个最大值为dMaxPerturbation的随机数来搔扰的。这一参数dMaxPerturbation在ini文件中已作了声明。另外作为浮点数遗传算法突变率也被设定得更高些。在本工程中它被设成为0.1。 下面就是扫雷机工程遗传算法类中所见到的突变函数的形式: void CGenAlg::Mutate(vectordouble chromo){  // 遍历权重向量按突变率将每一个权重进行突变  for (int i0; ichromo.size(); i)  {     // 我们要骚扰这个权重吗     if (RandFloat() m_dMutationRate)     {       // 为权重增加或减小一个小的数量       chromo[i] (RandomClamped() * CParams::dMaxPerturbatlon);     }  }} 如同以前的工程那样我已为v1.0版本的Smart Minesweepers工程保留了一个非常简单的遗传算法。这样就能给你留下许多余地可让你利用以前学到的技术来改进它。就象大多数别的工程一样v1.O版只用轮盘赌方式选精英并采用单点式杂交。 注意: 当程序运行时权重可以被演化成为任意的大小它们不受任何形式的限制。  4.7  扫雷机类The CMinesweeper Class   这一个类用来定义一个扫雷机。就象上一章描述的登月艇类一样扫雷机类中有一个包含了扫雷机位置、速度、以及如何转换方向等数据的纪录。类中还包含扫雷机的视线向量look-at vector它的2个分量被用来作为神经网络的2个输入。这是一个规范化的向量它是在每一帧中根据扫雷机本身的转动角度计算出来的它指示了扫雷机当前是朝着哪一个方向如图11所示。 下面就是扫雷机类CMinesweeper的声明: class CMinesweeper{private:     // 扫雷机的神经网络      CNeuralNet        m_ItsBrain;      // 它在世界坐标里的位置     SVector2D         m_vPosition;     // 扫雷机面对的方向     SVector2D          m_vLookAt;      // 它的旋转(surprise surprise)     double      m_dRotation;      double      m_dSpeed;      // 根据ANN保存输出     double         m_lTrack,                    m_rTrack;      m_lTrack和m_rTrack根据网络保存当前帧的输出。     这些就是用来决定扫雷机的移动速率和转动角度的数值。      // 用于度量扫雷机适应性的分数     double       m_dFitness;      每当扫雷机找到一个地雷它的适应性分数就要增加。     // 扫雷机画出来时的大小比例     double      m_dScale;     // 扫雷机最邻近地雷的下标位置     int       m_iClosestMine;     在控制器类CControl1er中有一个属于所有地雷的成员向量std::vector。    而m_iClosestMine就是代表最靠近扫雷机的那个地雷在该向量中的位置的下标。 public:     CMinesweeper();     // 利用从扫雷机环境得到的信息来更新人工神经网    bool Update(vectorSVector2D mines);     // 用来对扫雷机各个顶点进行变换以便接着可以画它出来    void  WorldTransform(vectorSPoint sweeper);    // 返回一个向量到最邻近的地雷    5Vector2D GetClosestMine(vectorSVector2D objects);     // 检查扫雷机看它是否已经发现地雷    int        CheckForMine(vectorSVector2D mines, double size);         void       Reset();     // ----------------- 定义各种供访问用的函数    SVector2D  Position()const { return m_vPosition; }    void       IncrementFitness(double val) { m_dFitness val; }    double     Fitness()const { return m_dFitness; }    void       PutWeights(vectordouble w) { m_ItsBrain.PutWeights(w); }    int        GetNumberOfWeights()const                              { return m_ItsBrain.GetNumberOfWeights(); }}; 4.7.1 The CMinesweeper::Update Function扫雷机更新函数 需要更详细地向你说明的CMinesweeper类的方法只有一个这就是Update更新函数。该函数在每一帧中都要被调用以更新扫雷机神经网络。让我们考察这函数的肚子里有些什么货色: bool CMinesweeper::Update(vectorSVector2D mines) {     //这一向量用来存放神经网络所有的输入     vectordouble inputs;      //计算从扫雷机到与其最接近的地雷2个点之间的向量     SVector2D vClosestMine GetClosestMine(mines);      //将该向量规范化     Vec2DNormalize(vClosestMine); 首先该函数计算了扫雷机到与其最靠近的地雷之间的向量然后使它规范化。记住向量规范化后它的长度等于1。但扫雷机的视线向量look-at vector这时不需要再作规范化因为它的长度已经等于1了。由于两个向量都有效地化成了同样的大小范围我们就可以认为输入已经是标准化了这我前面已讲过了。      //加入扫雷机-最近地雷之间的向量     Inputs.push_back(vClosestMine.x);     Inputs.push_back(vCIosestMine.y);      //加入扫雷机的视线向量     Inputs.push_back(m_vLookAt.x);     Inputs.push_back(m_vLookAt.y);      //更新大脑并从网络得到输出     vectordouble output m_ItsBrain.Update(inputs); 然后把视线向量以及扫雷机与它最接近的地雷之间的向量都输入到神经网络。函数CNeuralNet::Update利用这些信息来更新扫雷机网络并返回一个std::vector向量作为输出。      //保证在输出的计算中没有发生错误     if (output.size() CParams::iNumOutputs)      {         return false;      }      // 把输出赋值到扫雷机的左、右轮轨     m_lTrack output[0];     m_rTrack output[1]; 在更新神经网络时当检测到确实没有错误时程序把输出赋给m_lTrack和m_rTrack。 这些值代表施加到扫雷机左、右履带轮轨上的力。      // 计算驾驶的力     double RotForce m_lTrack - m_rTrack;      // 进行左转或右转     Clamp(RotForce, -CParams::dMaxTurnRate, CParams::dMaxTurnRate);        m_dSpeed (m_lTrack m_rTrack);   扫雷机车的转动力是利用施加到它左、右轮轨上的力之差来计算的。并规定施加到左轨道上的力减去施加到右轨道上的力就得到扫雷机车辆的转动力。然后就把此力施加给扫雷机车使它实行不超过ini文件所规定的最大转动率的转动。而扫雷机车的行进速度不过就是它的左侧轮轨速度与它的右侧轮轨速度的和。既然我们知道了扫雷机的转动力和速度它的位置和偏转角度也就都能更新了。      //更新扫雷机左右转向的角度     m_dRotation RotForce;          // 更新视线角度     m_vLookAt.x -sin(m_dRotation);     m_vLookAt.y cos(m_dRotation);      // 更新它的位置     m_vPosition (m_vLookAt* m_dSpeed);      // 如果扫雷机到达窗体四周则让它实行环绕使它不至于离开窗体而消失     If (m_vPosition.x CParams::WindowWidth) m_vPosition.x 0;     If (m_vPosition.x 0) m_vPosition.x CParams::WindowWidth;     If (m_vPosition.y CParams::WindowHeight) m_vPosition.y 0;     If (m_vPosition.y D) m_vPosition.y CParams::WindowHeight;     为了使事情尽可能简单我已让扫雷机在碰到窗体边框时就环绕折回(wrap)。采用这种方法程序就不再需要做任何碰撞-响应方面的工作。环绕一块空地打转对我们人来说是一桩非常不可思议的动作但对扫雷机这就像池塘中的鸭子。     Returen true; } 4.8  CController Class控制器类 CController类是和一切都有联系的类。图15指出了其他的各个类和CController类的关系。 下面就是这个类的定义:  class CController { private:     // 基因组群体的动态存储器一个向量      vectorSGenome   m_vecThePopulation;        图15  minesweeper工程的程序流程图     // 保存扫雷机的向量    vectorCMinesweeper  m_vecSweepers;         // 保存地雷的向量    vectorSVector2D   m_vecMines;     // 指向遗传算法对象的指针    CGenAIg*          m_pGA;     int               m_NumSweepers;     int               m_NumMines;     // 神经网络中使用的权重值的总数    int               m_NumWeightsInNN;     // 存放扫雷机形状各顶点的缓冲区    vectorSPoint    m_SweeperVB;     // 存放地雷形状各顶点的缓冲区    vectorSPoint    m_MineVB;     // 存放每一代的平均适应性分数供绘图用    vectordouble    m_vecAvFitness;     // 存放每一代的最高适应性分    vectordouble    m_vecBestFitness;     // 我们使用的各种不同类型的画笔    HPEN              m_RedPen;    HPEN              m_BluePen;    HPEN              m_GreenPen;    HPEN              m_OldPen;     // 应用程序窗口的句柄    HWND              m_hwndMain;    // 切换扫雷机程序运行的速度    bool              m_bFastRender;        // 每一代的帧数滴答数    int               m_iTicks;     // 代的计数    int               m_iGenerations;     // 窗体客户区的大小    int               cxClientcyClient;     // 本函数在运行过程中画出具有平均-和最优适应性值的图    void              PlotStats(HDC surface); public:     CController(HWND hwndMain);     ~CController();     void   Render(HDC surface);     void   WorldTransform(vectorSPoint VBuffer,           SVector2D        vPos);     bool   Update();     // 几个公用的访问方法    bool   FastRender() { return m_bFastRender; }    void   FastRender(bool arg){ m_bFastRender arg; }    void   FastRenderToggle() { m_bFastRender !m_bFastRender; }}; 当创建CController类的某个实例时会有一系列的事情发生 创建CMinesweeper对象。统计神经网络中所使用的权重的总数然后此数字即被利用来初始化遗传算法类的一个实例。从遗传算法对象中随机提取染色体(权重)并(利用细心的脑外科手术)插入到扫雷机的经网络中。创建了大量的地雷并被随机地散播到各地。为绘图函数创建了所有需要用到的GDI画笔。为扫雷机和地雷的形状创建了顶点缓冲区。  所有的一切现都已完成初始化由此Update方法就能在每一帧中被调用来对扫雷机进行演化。  4.8.1  CController::Update Method控制器的更新方法 控制器更新方法CController::Update方法或函数在每一帧中都要被调用。当调用update函数时函数的 前一半通过对所有扫雷机进行循环如发现某一扫雷机找到了地雷就update该扫雷机的适应性分数。由于m_vecThePopulation包含了所 有基因组的拷贝相关的适应性分数也要在这时进行调整。如果为完成一个代generation所需要的帧数均已通过本方法就执行一个遗传算法的时代 (epoch)来产生新一代的权重。这些权重被用来代替扫雷机神经网络中原有的旧的权重使扫雷机的每一个参数被重新设置从而为进入新一generation做好准备。     bool CController::Update() {     // 扫雷机运行总数为CParams::iNumTicks次的循环。在此循环周期中扫雷机的神经网络     // 不断利用它周围特有的环境信息进行更新。而从神经网络得到的输出使扫雷机实现所需的     // 动作。如果扫雷机遇见了一个地雷则它的适应性将相应地被更新且同样地更新了它对应     // 基因组的适应性。     if (m_iTicks CParams::iNumTicks)       {        for (int iO; im_NumSweepers; i)        {        //更新神经网络和位置          if (!m_vecSweepers[i].Update(m_vecMines))           {            //处理神经网络时出现了错误显示错误后退出             MessageBox(m_hwndMain, Wrong amount of NN inputs!,                Error, MB_OK);              return false;           }          // 检查这一扫雷机是否已经发现地雷         int GrabHit m_vecSweepers[i].CheckForMine(m_vecMines                                                     CParams::dMineScale);          if (GrabHit 0)          {            // 扫雷机已找到了地雷所以要增加它的适应性分数            m_vecSweepers[i].IncrementFitness();             // 去掉被扫雷机找到的地雷用在随机位置放置的一个新地雷来代替            m_vecMines[GrabHit] SVector2D(RandFloat() * cxClient                                          RandFloat() * cyClient);          }        // 更新基因组的适应性值        m-vecThePopulation[i].dFitness m_vecSweepers[i].Fitness();      }   }   // 一个代已被完成了。   // 进入运行遗传算法并用新的神经网络更新扫雷机的时期   else    {     // 更新用在我们状态窗口中状态     m_vecAvFitness.push_back(m_pGA-AverageFitness());     m_vecBestFitness.push_back(m_pGA-BestFitness());      // 增加代计数器的值     m_iGenerations;      // 将帧计数器复位     m_iTicks 0;      // 运行GA创建一个新的群体     m-vecThePopulation m_pGA-Epoch(m_vecThePopulation);      // 在各扫雷机中从新插入新的有希望被改进的大脑     // 并将它们的位置进行复位等     for(int iO; im_NumSweepers; i)      {m_vecSweepers[i].m_ItsBrain.PutWeights(m_vecThePopulation[i].vecWeights);        m_vecSweepers[i].Reset();      }     }  returen true;} 概括起来程序为每一世代做的工作是: l为所有扫雷机和为iNumTicks个帧组织循环调用Update函数        并根据情况增加扫雷机适应值的得分。     2从扫雷机神经网络提取权重向量。     3用遗传算法去演化出一个新的网络权重群体。     4把新的权重插入到扫雷机神经网络。  5转到第1步进行重复直到获得理想性能时为止。  最后表3列出了Smart Sweepers工程 v1.0版所有缺省参数的设置值。  表3  Smart Sweepers v1.0工程的缺省设置  神经网络 参     数 设 置 值 输入数目 4 输出数目 2 隐藏层数目 1 隐藏层神经元数目 10 激励响应 1 遗 传 算 法 参   数 设 置 值 群体大小 30 选择类型 旋转轮 杂交类型 单点 杂交率 0.7 突变率 0.1 精英设置on/off On 精英数目N/copies 4/1 总 体 特 性 参   数 设 置 值 每时代的帧数 2000 4.9  运行此程序 (Running the Program)      当你运行程序时“F”键用来切换2种不同的显示状态一种是显示扫雷机怎样学习寻找地雷一种是示在运行期中产生的最优的与平均的适当性分数的统计图表。 当显示图表时程序将会加速运行。 游戏编程中的人工智能技术. .(连载之六) 4.10  功能的两个改进 (A Couple of Improvements)  尽管扫雷机学习寻找地雷的本领十分不错这里我仍有两件事情要告诉你它们能进一步改进扫雷机的性能。 4.10.1 改进一Improvement Number One  首先单点crossover算子留下了许多可改进的余地。按照它的规定算子是沿着基因组长度任意地方切开的这样常有可能使个别神经细胞的基因组在权重的中间被一刀两段地分开。 为清楚起见我们来考察图16的权重。这是我们以前在说明基因组如何编码时看过的一个简单网络。 在这里杂交算子可以沿向量长度的任意一处切开这样就会有极大几率在某个神经细胞如第二个的权重中间断开也就是说在权重0.6和-0.1之间某处切开。这可能不会是我们想要的因为如果我们把神经细胞作为一个完整的单元来看待则它在此以前所获得的任何改良就要被骚扰了。事实上这样的杂交操作有可能非常非常象断裂性突变disruptive mutation操作所起的作用。   图16 简单的网络 与此针锋相对我已创建了另一种类型的杂交运算它只在神经细胞的边界上进行切开。在图16的例子中就是在第3、4或第6、7的两个基因之间切开如小箭头所示。 为了实现这一算法我已在CNeuralNet类中补充了另一个切割方法: CalculateSplitPoints。这一方法创建了一个用于保存所有网络权重边界的矢量它的代码如下: vectorint CNeuralNet::CalculateSplitPoints() const{   vectorint SplitPoints;     int WeightCounter 0;     // 对每一层   for (int iO; im_NumHiddenLayers 1; i)    {     // 对每一个神经细胞     for (int jO; jm_vecLayers[i].m_NumNeurons; j)   {         // 对每一个权重         for (int kO; km_vecLayers[i].m_vecNeurons[j].m_NumInputs; k)          {              WeightCounter;          }         SplitPoints.push_back(WeightCounter - 1);      }   }    return SplitPoints;} 这一方法是CController类构造函数在创建扫雷机并把断裂点向量传递给遗传算法类时调用的。它们被存储在一个名叫m_vecSplitPoints的std::vector向量中。然后遗传算法就利用这些断裂点来实现两点杂交操作其代码如下: void CGenAlg::CrossoverAtSplits(const vectordouble  mum,                                   const vectordouble  dad,                                   vectordouble         babyl,                                   vectordouble         baby2){   // 如果超过了杂交率就不再进行杂交把2个上代作为2个子代输出   // 如果2个上辈相同也把它们作为2个下辈输出   if ( (RandFloat() m_dCrossoverRate) || (mum dad))     {      baby1 mum;       baby2 dad;       return;     }    // 确定杂交的2个断裂点   int index1 RandInt(0, m_vecSplitPoints.size()-2);   int index2 RandInt(Index1, m_vecSplitPoints.size()-1);    int cp1 m_vecSplitPoints[Index1];   int cp2 m_vecSplitPoints[Index2];    // 创建子代  for (int i0; imum.size(); i)   {      if ( (icp1) || (icp2) )       {           // 如果在杂交点外保持原来的基因           babyl.push_back(mum[i]);           baby2.push_back(dad[i]);       }     else       {           // 把中间段进行交换           baby1.push_back(dad[1]);           baby2.push_back(mum[1]);       }   }    return;} 根据我的经验我已发现在进行杂交时把神经细胞当作一个不可分割的单位比在染色体长度上任意一点分裂基因组能得到更好的结果。 4.10.2 改进二Improvement Number Two 我想和你讨论的另一个性能改进是用另一种方式来观察网络的那些输入。在你已看到的例子中我们为网络使用了4个输入参数: 个用于表示扫雷机视线方向的向量,另外个用来指示扫雷机与其最靠近的地雷的方向的向量。然而有一种办法可以把这些参数的个数减少到只剩下一个。 其实你想一想就可知道扫雷机为了确定地雷的位置只要知道从它当前的位置和朝向出发需要向左或向右转动多大的一个角度这一简单的信息就够了(如果你已经考虑到了这一点那我在这里要顺便向您道贺了)。由于我们已经计算了扫雷机的视线向量和从它到最邻近地雷的向量再来计算它们之间的角度(θ)应是一件极为简单的事情 – 这就是这两个向量的点积这我们在第6章“使登陆月球容易一点”中已讨论过。见图17。 图17 计算到最邻近地雷的转动角度。 不幸的是点积仅仅给出角度的大小; 它不能指示这一角度是在扫雷机的那一侧。因此我已写了另一个向量函数返回一个向量相对于另一个向量的正负号。该函数的原型如下所示:      inline int Vec2DSign(SVector2D v1,SVector2D v2); 如果你对它的机理感兴趣你可以在文件SVector2D.h中找到它的源码。但它的基本点就是: 如果v1至v2是按顺时针方向转的则函数返回 1如果v1至v2是按逆时针方向转则函数返回 -1。   把点积和Vec2Dsign二者联合起来就能把输入的精华提纯出来使网络只需接受一个输入就行了。下面就是新的CMinesweeper::Update函数有关段落的代码形式:      // 计算到最邻近地雷的向量     SVector2D vClosestMine GetClosestMine(mines);      // 将它规范化  Vec2DNormalize(vClosestMine);   // 计算扫雷机视线向量和它到最邻近地雷的向量的点积。它给出了我们要面对  // 最邻近地雷所需转动的角度     double dot Vec2DDot(m_vLookAt, vClosestMine);      // 计算正负号     int sign Vec2DSign(m_vLookAt, vClosestMine);      Inputs.push_back(dot*sign);        运行一下光盘Chapter7/Smart Sweepers v1.1目录下的可执行程序executable你就知道经过以上个改进能为演化过程提速多少。   需要注意的一桩重要事情是带有4个输入的网络要花很长时间进行演化因为它必须在各输入数据之间找出更多的关系才能确定它应如何行动。事实上网络实际就是在学习怎么做点积并确定它的正负极性。因此当你设计自己的网络时你应仔细权衡一下是由你自己预先来计算许多输入数据好呢(它将使CPU负担增加但导致进化时间加快)还是让网络来找输入数据之间的复杂关系好(它将使演化时间变长但能使CPU减少紧张) 5 结束语last words   我希望你已享受到了你第一次攻入神经网络这一奇妙世界的快乐。我打赌你一定在为如此简单就能使用它们而感到惊讶吧对吗我想我是猜对了。   在下面几章里我将要向你介绍更多的知识告诉你一些新的训练手段和演绎神经网络结构的更多的方法。但首先请你利用本章下面的提示去玩一下游戏是有意义的。 6 练习题 (Stuff to Try)  1。 在v1.0中不用look-at向量作为输入而改用旋转角度θ作为输入由此就可以使网络的输入个数减少成为1个。请问这对神经网络的演化有什么影响你对此的看法怎样  2。 试以扫雷机的位置x1,y1、和扫雷机最接近的地雷的位置x2,y2、以及扫雷机前进方向的向量x3,y3等6个参数作为输入来设计一个神经网络使它仍然能够演化去寻找地雷。  3。 改变激励函数的响应。试用O.1 - O.3 之间的低端值它将产生和阶跃函数非常相像的一种激励函数。然后再试用高端值它将给出较为平坦的响应曲线。考察这些改变对演化进程具有什么影响  4。 改变神经网络的适应性函数使得扫雷机不是去扫除地雷而是要演化它使它能避开地雷。  5。 理一理清楚有关遗传算法的各种不同设置和运算中使你感到模糊的东西  6。 加入其他的对象类型比如人。给出一个新环境来演化扫雷机使它能避开人但照样能扫除地雷。这可能没有你想象那么容易) 神经网络编程入门 Posted on 2011-03-07 22:30 苍梧 阅读(48399) 评论(22) 编辑 收藏   本文主要内容包括 (1) 介绍神经网络基本原理(2)AForge.NET实现前向神经网络的方法(3) Matlab实现前向神经网络的方法 。   第0节、引例         本文以Fisher的Iris数据集作为神经网络程序的测试数据集。Iris数据集可以在http://en.wikipedia.org/wiki/Iris_flower_data_set  找到。这里简要介绍一下Iris数据集 有一批Iris花已知这批Iris花可分为3个品种现需要对其进行分类。不同品种的Iris花的花萼长度、花萼宽度、花瓣长度、花瓣宽度会有差异。我们现有一批已知品种的Iris花的花萼长度、花萼宽度、花瓣长度、花瓣宽度的数据。   一种解决方法是用已有的数据训练一个神经网络用作分类器。   如果你只想用C#或Matlab快速实现神经网络来解决你手头上的问题或者已经了解神经网络基本原理请直接跳到第二节——神经网络实现。   第一节、神经网络基本原理  1. 人工神经元(Artificial Neuron )模型         人工神经元是神经网络的基本元素其原理可以用下图表示 图1. 人工神经元模型          图中x1~xn是从其他神经元传来的输入信号wij表示表示从神经元j到神经元i的连接权值θ表示一个阈值 ( threshold )或称为偏置( bias )。则神经元i的输出与输入的关系表示为     图中yi表示神经元i的输出函数f称为激活函数 ( Activation Function )或转移函数 ( Transfer Function ) net称为净激活(net activation)。若将阈值看成是神经元i的一个输入x0的权重wi0则上面的式子可以简化为     若用X表示输入向量用W表示权重向量即 X [ x0 , x1 , x2 , ....... , xn ]     则神经元的输出可以表示为向量相乘的形式            若神经元的净激活net为正称该神经元处于激活状态或兴奋状态(fire)若净激活net为负则称神经元处于抑制状态。        图1中的这种“阈值加权和”的神经元模型称为M-P模型 ( McCulloch-Pitts Model )也称为神经网络的一个处理单元( PE, Processing Element )。   2. 常用激活函数         激活函数的选择是构建神经网络过程中的重要环节下面简要介绍常用的激活函数。 (1) 线性函数( Liner Function )   (2) 斜面函数( Ramp Function )   (3) 阈值函数( Threshold Function )              以上3个激活函数都属于线性函数下面介绍两个常用的非线性激活函数。 (4) S形函数( Sigmoid Function )   该函数的导函数 (5) 双极S形函数    该函数的导函数   S形函数与双极S形函数的图像如下 图3. S形函数与双极S形函数图像   双极S形函数与S形函数主要区别在于函数的值域双极S形函数值域是(-1,1)而S形函数值域是(0,1)。   由于S形函数与双极S形函数都是可导的(导函数是连续函数)因此适合用在BP神经网络中。BP算法要求激活函数可导   3. 神经网络模型         神经网络是由大量的神经元互联而构成的网络。根据网络中神经元的互联方式常见网络结构主要可以分为下面类 (1) 前馈神经网络 ( Feedforward Neural Networks )        前馈网络也称前向网络。这种网络只在训练过程会有反馈信号而在分类过程中数据只能向前传送直到到达输出层层间没有向后的反馈信号因此被称为前馈网络。感知机( perceptron)与BP神经网络就属于前馈网络。        图4 中是一个3层的前馈神经网络其中第一层是输入单元第二层称为隐含层第三层称为输出层输入单元不是神经元因此图中有2层神经元。 图4. 前馈神经网络     对于一个3层的前馈神经网络N若用X表示网络的输入向量W1~W3表示网络各层的连接权向量F1~F3表示神经网络3层的激活函数。   那么神经网络的第一层神经元的输出为 O1 F1( XW1 )   第二层的输出为 O2 F2 ( F1( XW1 ) W2 )   输出层的输出为 O3 F3( F2 ( F1( XW1 ) W2 ) W3 )        若激活函数F1~F3都选用线性函数那么神经网络的输出O3将是输入X的线性函数。因此若要做高次函数的逼近就应该选用适当的非线性函数作为激活函数。 (2) 反馈神经网络 ( Feedback Neural Networks )        反馈型神经网络是一种从输出到输入具有反馈连接的神经网络其结构比前馈网络要复杂得多。典型的反馈型神经网络有Elman网络和Hopfield网络。 图5. 反馈神经网络   (3) 自组织网络 ( SOM ,Self-Organizing Neural Networks )        自组织神经网络是一种无导师学习网络。它通过自动寻找样本中的内在规律和本质属性自组织、自适应地改变网络参数与结构。 图6. 自组织网络   4. 神经网络工作方式         神经网络运作过程分为学习和工作两种状态。 (1)神经网络的学习状态         网络的学习主要是指使用学习算法来调整神经元间的联接权使得网络输出更符合实际。学习算法分为有导师学习( Supervised Learning )与无导师学习( Unsupervised Learning )两类。        有导师学习算法将一组训练集 ( training set )送入网络根据网络的实际输出与期望输出间的差别来调整连接权。有导师学习算法的主要步骤包括 1  从样本集合中取一个样本AiBi 2  计算网络的实际输出O 3  求DBi-O 4  根据D调整权矩阵W 5 对每个样本重复上述过程直到对整个样本集来说误差不超过规定范围。   BP算法就是一种出色的有导师学习算法。        无导师学习抽取样本集合中蕴含的统计特性并以神经元之间的联接权的形式存于网络中。        Hebb学习律是一种经典的无导师学习算法。 (2) 神经网络的工作状态         神经元间的连接权不变神经网络作为分类器、预测器等使用。   下面简要介绍一下Hebb学习率与Delta学习规则 。 (3) 无导师学习算法Hebb学习率    Hebb算法核心思想是当两个神经元同时处于激发状态时两者间的连接权会被加强否则被减弱。         为了理解Hebb算法有必要简单介绍一下条件反射实验。巴甫洛夫的条件反射实验每次给狗喂食前都先响铃时间一长狗就会将铃声和食物联系起来。以后如果响铃但是不给食物狗也会流口水。 图7. 巴甫洛夫的条件反射实验      受该实验的启发Hebb的理论认为在同一时间被激发的神经元间的联系会被强化。比如铃声响时一个神经元被激发在同一时间食物的出现会激发附近的另 一个神经元那么这两个神经元间的联系就会强化从而记住这两个事物之间存在着联系。相反如果两个神经元总是不能同步激发那么它们间的联系将会越来越 弱。   Hebb学习律可表示为        其中wij表示神经元j到神经元i的连接权yi与yj为两个神经元的输出a是表示学习速度的常数。若yi与yj同时被激活即yi与yj同时为正那么Wij将增大。若yi被激活而yj处于抑制状态即yi为正yj为负那么Wij将变小。 (4) 有导师学习算法Delta学习规则   Delta学习规则是一种简单的有导师学习算法该算法根据神经元的实际输出与期望输出差别来调整连接权其数学表示如下          其中Wij表示神经元j到神经元i的连接权di是神经元i的期望输出yi是神经元i的实际输出xj表示神经元j状态若神经元j处于激活态则xj为 1若处于抑制状态则xj为0或1根据激活函数而定。a是表示学习速度的常数。假设xi为1若di比yi大那么Wij将增大若di比yi小 那么Wij将变小。        Delta规则简单讲来就是若神经元实际输出比期望输出大则减小所有输入为正的连接的权重增大所有输入为负的连接的权重。反之若神经元实际输出比 期望输出小则增大所有输入为正的连接的权重减小所有输入为负的连接的权重。这个增大或减小的幅度就根据上面的式子来计算。 (5)有导师学习算法BP算法    采用BP学习算法的前馈型神经网络通常被称为BP网络。 图8. 三层BP神经网络结构     BP网络具有很强的非线性映射能力一个3层BP神经网络能够实现对任意非线性函数进行逼近根据Kolrnogorov定理。一个典型的3层BP神经网络模型如图7所示。   BP网络的学习算法占篇幅较大我打算在下一篇文章中介绍。   第二节、神经网络实现    1. 数据预处理         在训练神经网络前一般需要对数据进行预处理一种重要的预处理手段是归一化处理。下面简要介绍归一化处理的原理与方法。 (1) 什么是归一化  数据归一化就是将数据映射到[0,1]或[-1,1]区间或更小的区间比如(0.1,0.9) 。 (2) 为什么要归一化处理  1输入数据的单位不一样有些数据的范围可能特别大导致的结果是神经网络收敛慢、训练时间长。 2数据范围大的输入在模式分类中的作用可能会偏大而数据范围小的输入作用就可能会偏小。 3 由于神经网络输出层的激活函数的值域是有限制的因此需要将网络训练的目标数据映射到激活函数的值域。例如神经网络的输出层若采用S形激活函数由于S形 函数的值域限制在(0,1)也就是说神经网络的输出只能限制在(0,1)所以训练数据的输出就要归一化到[0,1]区间。 4S形激活函数在(0,1)区间以外区域很平缓区分度太小。例如S形函数f(X)在参数a1时f(100)与f(5)只相差0.0067。 (3) 归一化算法    一种简单而快速的归一化算法是线性转换算法。线性转换算法常见有两种形式        1 y ( x - min )/( max - min )   其中min为x的最小值max为x的最大值输入向量为x归一化后的输出向量为y 。上式将数据归一化到 [ 0 , 1 ]区间当激活函数采用S形函数时值域为(0,1)时这条式子适用。        2 y 2 * ( x - min ) / ( max - min ) - 1        这条公式将数据归一化到 [ -1 , 1 ] 区间。当激活函数采用双极S形函数值域为(-1,1)时这条式子适用。 (4) Matlab数据归一化处理函数    Matlab中归一化处理数据可以采用premnmx postmnmx tramnmx 这3个函数。 1 premnmx 语法[pn,minp,maxp,tn,mint,maxt] premnmx(p,t) 参数 pn p矩阵按行归一化后的矩阵 minpmaxpp矩阵每一行的最小值最大值 tnt矩阵按行归一化后的矩阵 mintmaxtt矩阵每一行的最小值最大值 作用将矩阵pt归一化到[-1,1] 主要用于归一化处理训练数据集。 2 tramnmx 语法[pn] tramnmx(p,minp,maxp) 参数 minpmaxppremnmx函数计算的矩阵的最小最大值 pn归一化后的矩阵 作用主要用于归一化处理待分类的输入数据。 3 postmnmx 语法 [p,t] postmnmx(pn,minp,maxp,tn,mint,maxt) 参数 minpmaxppremnmx函数计算的p矩阵每行的最小值最大值 mintmaxtpremnmx函数计算的t矩阵每行的最小值最大值 作用将矩阵pntn映射回归一化处理前的范围。postmnmx函数主要用于将神经网络的输出结果映射回归一化前的数据范围。 2. 使用Matlab实现神经网络  使用Matlab建立前馈神经网络主要会使用到下面3个函数 newff 前馈网络创建函数 train训练一个神经网络 sim 使用网络进行仿真  下面简要介绍这3个函数的用法。 (1) newff函数 1newff函数语法         newff函数参数列表有很多的可选参数具体可以参考Matlab的帮助文档这里介绍newff函数的一种简单的形式。 语法net newff ( A, B, {C} ,‘trainFun’) 参数 A一个n×2的矩阵第i行元素为输入信号xi的最小值和最大值 B一个k维行向量其元素为网络中各层节点数 C一个k维字符串行向量每一分量为对应层神经元的激活函数 trainFun 为学习规则采用的训练算法。 2常用的激活函数   常用的激活函数有   a) 线性函数 (Linear transfer function) f(x) x   该函数的字符串为’purelin’。   b) 对数S形转移函数( Logarithmic sigmoid transfer function )     该函数的字符串为’logsig’。 c) 双曲正切S形函数 (Hyperbolic tangent sigmoid transfer function )   也就是上面所提到的双极S形函数。     该函数的字符串为’ tansig’。   Matlab的安装目录下的toolbox\nnet\nnet\nntransfer子目录中有所有激活函数的定义说明。 3常见的训练函数     常见的训练函数有 traingd 梯度下降BP训练函数(Gradient descentbackpropagation) traingdx 梯度下降自适应学习率训练函数 4网络配置参数 一些重要的网络配置参数如下 net.trainparam.goal  神经网络训练的目标误差 net.trainparam.show    显示中间结果的周期 net.trainparam.epochs  最大迭代次数 net.trainParam.lr    学习率 (2) train函数     网络训练学习函数。 语法[ net, tr, Y1, E ]  train( net, X, Y ) 参数 X网络实际输入 Y网络应有输出 tr训练跟踪信息 Y1网络实际输出 E误差矩阵 (3) sim函数 语法Ysim(net,X) 参数 net网络 X输入给网络的×N矩阵其中K为网络输入个数N为数据样本数 Y输出矩阵Q×N其中Q为网络输出个数 (4) Matlab BP网络实例         我将Iris数据集分为2组每组各75个样本每组中每种花各有25个样本。其中一组作为以上程序的训练样本另外一组作为检验样本。为了方便训练将3类花分别编号为123 。   使用这些数据训练一个4输入分别对应4个特征3输出分别对应该样本属于某一品种的可能性大小的前向网络。        Matlab程序如下 %读取训练数据 [f1,f2,f3,f4,class] textread(trainData.txt , %f%f%f%f%f,150); %特征值归一化 [input,minI,maxI] premnmx( [f1 , f2 , f3 , f4 ]) ;%构造输出矩阵 s length( class) ; output zeros( s , 3 ) ; for i 1 : s output( i , class( i ) ) 1 ; end %创建神经网络 net newff( minmax(input) , [103] , { logsigpurelin } , traingdx ) ; %设置训练参数 net.trainparam.show 50 ; net.trainparam.epochs 500 ; net.trainparam.goal 0.01 ; net.trainParam.lr 0.01 ; %开始训练 net train( net, input , output ) ;%读取测试数据 [t1 t2 t3 t4 c] textread(testData.txt , %f%f%f%f%f,150); %测试数据归一化 testInput tramnmx ( [t1,t2,t3,t4] , minI, maxI ) ;%仿真 Y sim( net , testInput ) %统计识别正确率 [s1 , s2] size( Y ) ; hitNum 0 ; for i 1 : s2 [m , Index] max( Y( : , i ) ) ; if( Index c(i) ) hitNum hitNum 1 ; end end sprintf(识别率是 %3.3f%%,100* hitNum / s2 )     以上程序的识别率稳定在95%左右训练100次左右达到收敛训练曲线如下图所示 图9. 训练性能表现   (5)参数设置对神经网络性能的影响         我在实验中通过调整隐含层节点数选择不通过的激活函数设定不同的学习率   1隐含层节点个数    隐含层节点的个数对于识别率的影响并不大但是节点个数过多会增加运算量使得训练较慢。   2激活函数的选择         激活函数无论对于识别率或收敛速度都有显著的影响。在逼近高次曲线时S形函数精度比线性函数要高得多但计算量也要大得多。   3学习率的选择         学习率影响着网络收敛的速度以及网络能否收敛。学习率设置偏小可以保证网络收敛但是收敛较慢。相反学习率设置偏大则有可能使网络训练不收敛影响识别效果。   3. 使用AForge.NET实现神经网络  (1) AForge.NET简介         AForge.NET是一个C#实现的面向人工智能、计算机视觉等领域的开源架构。AForge.NET源代码下的Neuro目录包含一个神经网络的类库。 AForge.NET主页http://www.aforgenet.com/ AForge.NET代码下载http://code.google.com/p/aforge/ Aforge.Neuro工程的类图如下   图10. AForge.Neuro类库类图   下面介绍图9中的几个基本的类 Neuron —神经元的抽象基类 Layer — 层的抽象基类由多个神经元组成 Network —神经网络的抽象基类由多个层Layer组成 IActivationFunction - 激活函数activation function的接口 IUnsupervisedLearning - 无导师学习unsupervised learning算法的接口ISupervisedLearning - 有导师学习supervised learning算法的接口   (2)使用Aforge建立BP神经网络         使用AForge建立BP神经网络会用到下面的几个类 1  SigmoidFunction S形神经网络   构造函数public SigmoidFunction( doublealpha )    参数alpha决定S形函数的陡峭程度。 2  ActivationNetwork 神经网络类   构造函数   public ActivationNetwork( IActivationFunction function, int inputsCount, paramsint[] neuronsCount )                          : base(inputsCount, neuronsCount.Length )   public virtual double[] Compute( double[]input )   参数意义 inputsCount输入个数 neuronsCount 表示各层神经元个数 3  BackPropagationLearningBP学习算法  构造函数 public BackPropagationLearning( ActivationNetwork network )  参数意义 network 要训练的神经网络对象 BackPropagationLearning类需要用户设置的属性有下面2个 learningRate 学习率 momentum 冲量因子 下面给出一个用AForge构建BP网络的代码。   // 创建一个多层神经网络采用S形激活函数各层分别有4,5,3个神经元 //(其中4是输入个数3是输出个数5是中间层结点个数)ActivationNetwork network new ActivationNetwork( new SigmoidFunction(2), 4, 5, 3); // 创建训练算法对象BackPropagationLearning teacher new BackPropagationLearning(network); // 设置BP算法的学习率与冲量系数teacher.LearningRate 0.1; teacher.Momentum 0; int iteration 1 ; // 迭代训练500次while( iteration 500 ) { teacher.RunEpoch( trainInput , trainOutput ) ; iteration ; } //使用训练出来的神经网络来分类t为输入数据向量network.Compute(t)[0]          改程序对Iris 数据进行分类识别率可达97%左右 。        点击下载源代码     文章来自http://www.cnblogs.com/heaad/     转载于:https://www.cnblogs.com/zhanlang96/p/3994632.html
http://www.zqtcl.cn/news/574273/

相关文章:

  • 网站如何做关键词引流网站怎么快速做收录
  • 网站建设文案网站设计要学哪些
  • 品牌网站建设gs平台搭建工具
  • 怎么把自己做的网站弄到域名上柳州游戏网站建设
  • 做衣服类网站策划书高端建设网站企业
  • 珠海网站建设公司有哪些代替做网站推广
  • 泰安的网站建设公司旅游网站建设规划报告怎么写
  • 如何建设淘宝客网站什么是网络营销常用的网络营销方法有哪些
  • 大连华南网站建设深圳网站建设公司的外文名是
  • 做招投标网站云南昆明网站建设价格
  • 越秀区网站建设公司微网站菜单
  • vs2017网站开发广州网站建设易得
  • 长沙企业网站建设价格陕西省门户网站建设政策
  • 龙华营销型网站制作wordpress最近评论
  • 嘉兴微信网站做一个招聘信息的网站_用什么做网站的软件
  • 各种购物网站大全上海市建设工程检测网
  • 网站推广沈阳php网站开发接口开发
  • 莱芜 做网站 公司官网开发
  • tomcat做网站做自媒体查找素材的网站
  • 信阳建设企业网站公司软件开发平台公司
  • 营销型网站建设营销型设计家官网视频
  • 部门网站建设目的加猛挣钱免费做网站软件
  • 洛阳制作网站哪家好wordpress是英文
  • dw里面怎么做网站轮播图网站建设分为多少模块
  • 国外互动网站wordpress设置用户头像
  • 重庆手机网站推广定做net创建网站之后怎么做
  • 网站仿静态做it的兼职网站
  • 建站用wordpress好吗hui怎么做网站
  • 从用户旅程角度做网站分析做网站还是做淘宝
  • 妇科医院网站优化服务商品牌型网站设计推荐