神经网络深入(连载4)拓扑扩张
来源:互联网 发布:java面向对象关键字 编辑:程序博客网 时间:2024/04/28 15:48
11.4 NEAT(拓扑扩张的神经演化)
NEAT是Neuro Evolution of AugmentingTopologies(扩张拓扑的神经演化)的简称。这是得克萨斯州大学Kenneth Stanley Owen 和 Risto Miikkulainen 合作开发的一种网络。 它采用基于节点的编码来描述网络的结构和连接权重。当创建新的节点和链接时,它借助于所产生的历史数据,避免了竞争约定问题,所以这是一种非常漂亮的方法。NEAT也企图把它所生成的网络的尺寸减到最小。为此,在演化开始时,它让群体中每个神经网络都有最小的拓扑结构,然后在演化进行中,始终都是一个一个地在网络中添加神经细胞和连接。由于这种过程和自然界所有的生物机体的生长过程一样- 随着时间的推移,不断发育长大、不断增加复杂性 - 因而,这是一种非常吸引人的解决方法,而这也是我在本章中要特别着重介绍这种技术的部分原因。
为了使这一思想得到实现,需要利拥相当多的代码。当我描述NEAT演化规则的每一部分时我都会把有关代码列出来。我将按照程序固有的顺序来进行讲解,这样做,源程序代码本身就能帮助你加强对注释文本的理解、帮助你快速掌握概念。本章的所有源程序均可在光盘的Chapter11/NEAT Sweepers文件夹下找到。
首先让我描述网络怎么编码。
11.4.1 NEAT基因组(NEATGenome)
NEAT基因组的结构包括一张神经细胞基因(neuron gene [译注2])表和一张链接基因(link gene)表。一个链接基因的信息包括:它所连接的2个神经细胞、与此连接相关联的权重、用来说明链接是否已启用(enabled)的一个标帜、用来说明链接是否为返回(recurrent)的一个标帜,此外还有一个创新数(innovation number,有关它我们马上就会作更多说明)。神经细胞基因描述了该神经细胞在网络内的功能,它可以是一个输入神经细胞[译注1],或者是一个输出神经细胞,或者一个隐藏神经细胞,再或者是一个偏移神经细胞[译注1]。每个神经细胞基因也拥有一个唯一的ID(标识号)。
图11.12显示了一个简单网络的基因组的2种基因表。
11.4.1.1SLinkGene(链接基因的结构)
链接基因的结构称作SlinkGene,它放在文件genes.h中。它的定义如下:
StructSlinkGene
{
//该链接(link)所连结的2个神经细胞的标识(ID)
int FromNeuron,
ToNeuron;
double dWeight;
[译注1] "输入神经细胞"和"偏移神经细胞"都是NEAT的演化对象,在演化结果中都不代表神经细胞。
[译注2] 由注1可知,如果把‘神经细胞基因’改称‘节点基因’就可以更确切些。
图11.12 为 NEAT网络编码
//指明本link当前是否已Enabled的标志(flag)
bool bEnabled;
//指明本link是否Recurrent的标志
bool bRecurrent;
//我下面就会告诉有关这一个值的所有一切
int InnovationID;
SLinkGene(){}
SlinkGene(int in,
int out,
bool enable,
int tag,
double w,
bool rec= false):bEnabled(enable), InnovationID(tag),
FromNeuron(in),
ToNeuron(out),
dWeight(w),
bRecurrent(rec)
{}
// 重载’<’运算符用于排序(我们以创新标识ID作为应用范畴)
friend bool operator<(constSLinkGene& lhs, const SLinkGene& rhs)
{
return (lhs.InnovationID < rhs.InnovationID);
}
};
11.4.1.2 SNeuronGene(神经细胞基因结构)
神经细胞基因的结构体记作SNeuronGene,其代码可在genes.h中找到。下面是它的定义;
structSNeuronGene
{
//它的标识号码
int iID;
//它的类型
neuron_type NeuronType;
这是一个枚举类型。其值为input、hidden、bias、output 和none。有关none类型如何使用,下一节讨论innovations(创新)时你就会知道。
//它是Recurrent的吗?
bool bRecurrent;
在NEAT中,一个自返神经细胞(recurrent neuron)定义为存在一个从自己连向自己的链接的神经细胞,如图 11.13所示。
//设置S形函数的曲率(弯曲性)
double dActivationResponse;
在本实现中,每个神经细胞的S形函数的激励响应也要分别地进行演化。
//在网格中的位置
double dSplitY, dSplitX;
图11.13 带有2个进入链接的神经细胞:
它有一个向外的链接和一个环形自返链接
如果你想在一个2D格点上布置(laid out)一个神经网络,就必须了解每个神经细胞在格子上的坐标。利用这一信息,我们还可以在显示器上画出这个网络,这能为用户提供视觉上的帮助。
当一基因组最初建立时,所有神经细胞都指定了一个平面坐标(SplitX,SplitY)。我现在暂时只讨论它们的Y坐标值 SplitY,但X坐标值SplitX也可以用类似方法进行计算。每个输入神经细胞的SplitY值指定为0,每个输出神经细胞的SplitY值指定1。当一个神经细胞加入到输入输出之间时,就要有效地断裂开一个链接,新的神经细胞的SplitY值取为它上下2个邻近接点的中点值。图11.14 应能帮助阐明这一点。
图 11.14 计算SplitY深度值的几步例子
这一信息除了可用在网络绘图程序中计算显示坐标外,在计算整个网络的深度(depth)
以及确定一个新加入的连接是否返回连接也是极有用的。
SNeuronGene(neuron_type type,
int id,
double y,
double x,
bool r= false ):iID(id),
NeuronType(type),
bRecurrent(r),
pReuronMarker(NULL),
dSplitV(y),
dSplitX(x)
{}
};
11.4.1.3 CGenome(基因组类)
下面是基因组类CGenome的定义。其中包含的某些方法和成员你可能还无法理解,现在只需要快速浏览一下这个类,然后就转到下一节去。
(请注意,为了简单起见,我已删去了那些访问方法)。
classCGenome
{
private:
//它的标识(ID)
int m_GenomeID;
//组成此基因组的所有神经细胞
vector<SNeuronGene> m_vecNeurons;
//所有的链接
vector<SLinkGene> m_vecLinks;
//指向它的表现型的指针
CNeuralNet* m_pPhenotype;
//它的原始适应性分数
double m_dFitness;
//当它已被放置进一物种并已作了相应调整后的适当性分数
double m_dAdjustedFitness;
//要求孵化的下一代的子孙数目
double m_dAmountToSpawn;
//分别用来保存输入和输出数目的2个纪录
int m_iNumlnputs,
m_iNumOutputs;
//保存该基因组进入的物种的轨迹(仅用于显示目的)
int m_iSpecies;
//返回true,如果指定的链接已是基因组的一个部分
bool DuplicateLink(int NeuronIn, intNeuronOut);
//给定一个神经细胞ID时,本函数就能找到它在m_vecNeurons中的位置
int GetElementPos(int neuron_id);
//测试传入的ID是否与已经存在的某个神经细胞ID相同。
//这一测试在AddNeuron中需要使用
bool AlreadyHaveThisNeuronID(const int ID);
publlc:
CGenome();
//本构造函数创建了一个最小的基因,它有输出与输入神经细胞,但每个输入神
//经细胞都连结到每个输出神经细胞
CGenome(int id, int inputs, int outputs);
//本构造函数利用一个SLinkGenes向量、一个SneuronGenes向量、
//和一个标识号ID来创建一个基因组
CGenome(int id,
vector<SNeuronGene> neurons,
vector<SLinkGene> genes,
int inputs,
int outputs);
//析构函数
~CGenome();
//复制构造函数
CGenome(const CGenome& g);
//为operator赋值
CGenome& operator = (const CGenome& g);
//由基因组创建神经网络
CNeuralNet* CreatePhenotype(int depth);
//删除神经网络
int DeletePhenotype();
//按突变率在基因组中增加一个链接
void AddLink(double MutationRate,
double ChanceOfRecurrent,
CInnovation &innovation,
int NumTrysToFindLoop,
int NumTrysToAddLink);
//和一个神经细胞
void AddNeuron(double MutationRate,
CInnovation &innovation,
int NumTrysToFindOldLink);
//这一函数对连接权量实行突变
void MutateWeights(doublemut_rate,
double prob_new_mut,
double dMaxPertubation);
//骚扰神经细胞的激励响应
void MutateActivationResponse(double mut_rate,
doubleMaxPertubation);
//计算本基因组和其他基因组之间的兼容性分
double GetCompatibilityScore(const CGenome &genome);
void SortGenes();
//重载 ’<’ 运算符用于排序。从最合适到最不合适的顺序来排
friend booloperator<(const CGenome& lhs, const CGenome& rhs)
{
return(lhs.m_dFitness > rhs.m_dFitness);
}
};
- 神经网络深入(连载4)拓扑扩张
- 神经网络深入(连载1)神经网络的拓扑
- 神经网络深入(连载9)神经网络类
- 神经网络深入(连载2)直接编码
- 神经网络深入(连载3)间接编码
- 神经网络深入(连载6)物种形成
- 神经网络深入(连载7)更换时代
- 神经网络深入(连载5)算子与创新
- 神经网络深入(连载8)基因变成表现形
- 神经网络深入(连载10)一个demo程序
- 神经网络入门--连载4
- 神经网络(拓扑排序)
- NOIP2013 神经网络(拓扑)
- 神经网络入门(连载之一)
- 神经网络进阶(连载4)鼠标手势的识别
- 神经网络 拓扑
- 神经网络入门介绍(连载一)
- 神经网络入门(连载之二)
- 【设计模式小结】—-结构型模式
- 2016.3 idea 自动格式化代码插件
- android读书复习笔记一 IPC
- 第二章 IPC机制
- Android面试题整理
- 神经网络深入(连载4)拓扑扩张
- 第三章 View的事件体系
- perl 和curl 发送json数据例子
- 欢迎使用CSDN-markdown编辑器
- C++性能优化实践
- 水仙花数
- Android Studio使用git插件上传代码
- 洛谷P1192 台阶问题
- 第四章 View的工作原理