def create(self,popParam): indType = agent.individualTypes.find(popParam.indTypeName) netdef = popParam.genomeDefinition idGenerator = networks.idGenerators.find(netdef.idGenerator) genomeFactory = indType.genomeFactory if popParam.genomeFactory is not None: genomeFactory = popParam.genomeFactory factoryParam = popParam.factoryParam size = popParam.size genomes = [] for i in range(size): netid = idGenerator.getNetworkId() net = NeuralNetwork(netid,netdef) # 创建输入神经元 self.__createLayerNeuron(net,netdef,0,netdef.neuronCounts[0],netdef.models.input) # 创建输出神经元 self.__createLayerNeuron(net,netdef,len(netdef.neuronCounts)-1,netdef.neuronCounts[-1], netdef.models.hidden) # 创建中间神经元(如果有) hiddenCount = netdef.neuronCounts[1:-1] if not collections.isEmpty(hiddenCount): for i in range(len(hiddenCount)): self.__createLayerNeuron(net, netdef, i+1, netdef.neuronCounts[i+1], netdef.models.input) genomes.append(net) # 初始化连接 if popParam.factoryParam.connectionRate <= 0: #初始无连接 continue elif popParam.factoryParam.connectionRate >= 1: #初始全连接 for i in range(len(net.neurons)-1): for j,n1 in enumerate(net.neurons[i]): # 取第i层神经元 for k,n2 in enumerate(net.neurons[i+1]): # 取第i+1层神经元 synapseid = idGenerator.getSynapseId(net,n1.id,n2.id) synapse = Synapse(synapseid,0,n1.id,n2.id,netdef.models.synapse) net.synapses.append(synapse) else: # 以一定概率进行连接 allsynapse = [] for i in range(len(net.neurons)-1): for j,n1 in enumerate(net.neurons[i]): # 取第i层神经元 for k,n2 in enumerate(net.neurons[i+1]): # 取第i+1层神经元 allsynapse.append((n1,n2)) synapsecount = int(len(allsynapse)*popParam.factoryParam.connectionRate) if synapsecount > 0 : indexes = np.random.uniform(0,len(allsynapse)-1,synapsecount) for i in indexes: n1 = allsynapse[i][0] n2 = allsynapse[i][1] synapseid = idGenerator.getSynapseId(net, n1.id, n2.id) synapse = Synapse(synapseid, 0, n1.id, n2.id, netdef.models.synapse) net.synapses.append(synapse) logging.debug('# neat工厂创建网络:'+str(net)) return genomes
def getAllCacheNeuronIds(self): ''' 取得所有缓存的神经元id :return: list 按照从小到大排序的神经元id ''' nids = [] if collections.isEmpty(self.neuronIdcaches) else list(self.neuronIdcaches.values()) nids.sort() return nids
def recordIndividuals(self): logInds = self.__getLogIndividuals() if collections.isEmpty(logInds): return kwdatas = {} for ind in logInds: kwdatas['ind' + str(ind.id)] = str(ind) self.__recordSection('重要个体', **kwdatas) if self.callback is not None: self.callback('inds.record', self)
def __do_mutate_deletenode(self, ind, session): net = ind.genome ns = net.getHiddenNeurons() if collections.isEmpty(ns): return False, '', 'deletenode', None # 随机选择 neuron = choice(ns) net.remove(neuron) return True, '', 'deletenode', neuron
def getAllCacheSynasesIds(self,net=None): ''' 取得所有缓存的突触id :param net: 所属网络,如果为None,则为所有网络的合并 :return: list 按照从小到大排序的神经元id ''' sids = [] if collections.isEmpty(self.synapseIdcaches) else list(self.synapseIdcaches.values()) sids.sort() return sids
def getPrevNeuron(self, neuronId): ''' 取得某神经元的前序连接神经元 :param neuronId: :return: ''' synapses = self.getInputSynapse(neuronId) if collections.isEmpty(synapses): return [] return list(map(lambda s: self.getNeuron(id=s.fromId), synapses))
def getNextNeuron(self, neuronId): ''' 取得某神经元的后序连接神经元 :param neuronId: :return: ''' synapses = self.getOutputSynapse(neuronId) if collections.isEmpty(synapses): return [] return list(map(lambda s: self.getNeuron(id=s.toId), synapses))
def __do_mutate_activationFunction(self, ind, session): net = ind.genome ns = net.getHiddenNeurons() if collections.isEmpty(ns): return False, '', 'modifyactivationFunction', None # 随机选择 neuron = choice(ns) activationFunction = neuron._doSelectActiovationFunction() return True, '', 'modifyactivationFunction', str( neuron) + ':' + activationFunction.nameInfo.name
def __init__(self, **configuration): ''' 普通输入模型 :param configuration: 模型缺省配置 ''' self.nameInfo = CommonInputNeurnModel.nameInfo self.configuration = configuration if not collections.isEmpty( configuration) else {} self.initStates = CommonInputNeurnModel.__initStates self.variables = CommonInputNeurnModel.__variables
def getNeuronLayerCount(self): ''' 每层神经元数量 :return: dict 每层的神经元数量,key是层id,value是数量 ''' r = {} for index, ns in enumerate(self.neurons): if collections.isEmpty(ns): continue r[ns[0].layer] = r.get(ns[0].layer, 0) + 1 return r
def __do_mutate_deleteconnection(self, ind, session): net = ind.genome synapses = net.getSynapses() if collections.isEmpty(synapses): return False, '', 'deleteconnection', None # 随机选择 s = choice(synapses) net.remove(s) session.monitor.recordDebug(NeatMutate.name, 'ind' + str(ind.id) + '删除连接', str(s.id)) return True, '', 'deleteconnection', s
def __recordSection(self, stageName, **kwdatas): contents = '事件 = '+stageName + \ ',年代 = '+('' if self.evoTask.curSession is None else str(int(self.evoTask.curSession.curTime))) + \ ',时间 = '+time.strftime('%H:%M:%S',time.localtime(time.time())) self.logger.info("") self.logger.info(contents) #keys = [] if collections.isEmpty(kwdatas) else kwdatas.keys() keys = [] if collections.isEmpty(kwdatas) else kwdatas for key in keys: self.logger.info(key + ':' + str(kwdatas[key]))
def _initVariableValue(self): ''' 根据模型配置初始号变量的值 :return: ''' if not collections.isEmpty(self.variables): for var in self.variables: if var.type is not float: continue if var.nameInfo.name in self.modelConfiguration: var.range = Range(self.modelConfiguration[var.nameInfo.name]) var.value = 0 if var.range is None else var.range.sample()
def __init__(self,id,inds,pop): ''' 物种 :param id: int 物种id :param inds: list of int or list of Individuals 属于该种群的个体 :param pop: 物种 ''' self.id = id self.pop = pop self.targetSize = 0 self.indids = [] if not collections.isEmpty(inds): self.indids = list(map(lambda ind:ind.id if isinstance(ind,Individual) else ind,inds)) self.features = {} self.__doEvaulate()
def execute(self, session): n_clusters = session.popParam.species.size method = session.popParam.species.method iter = session.popParam.species.iter alg = session.popParam.species.get('alg', 'kmean') # 取得所有个体的特征向量 idgenerator = networks.idGenerators.find( session.popParam.genomeDefinition.idGenerator) inds = session.pop.inds indVecs = np.array( list(map(lambda ind: self.__getIndVector(ind, session), inds))) if indVecs.dtype is np.dtype('O'): indVecs_new = [] for sn, indVec in enumerate(indVecs): indVec1 = indVec.astype(np.float64) indVecs_new.append(indVec1) if np.array(indVecs_new).dtype is np.dtype('O'): if len(indVec1) > len(indVecs_new[0]): indVecs_new[-1] = indVec1[:len(indVecs_new[0])] indVecs = np.array(indVecs_new) indArray = whiten(indVecs) if alg == 'kmean': centroids, distortion = kmeans(obs=indArray, k_or_guess=n_clusters, iter=iter) labels = vq(indArray, centroids) species = [] for index, center in enumerate(centroids): species_inds = [ ind for i, ind in enumerate(inds) if i in np.argwhere(labels[0] == index) ] if collections.isEmpty(species_inds): continue specieId = idgenerator.getSpeciesid( list(map(lambda ind: ind.genome, species_inds))) for ind in species_inds: ind.speciedId = specieId sp = Specie(id=specieId, inds=species_inds, pop=session.pop) species.append(sp) session.pop.species = species return session.pop.species else: raise RuntimeError('物种分类算法名称无效(popParam.species.alg):' + alg)
def neat_callback(event, monitor): # session结束的时候将精英个体的图形写入文件 if event == 'session.end': filename = 'session' + str(monitor.evoTask.curSession.taskxh) + ".ind" eliest = monitor.evoTask.curSession.pop.eliest if collections.isEmpty(eliest): return optimaInd = eliest[0] net = optimaInd.genome netviewer = NetworkView() netviewer.drawNet(net, filename=filename + str(optimaInd.id) + '.svg', view=False) #collections.foreach(eliest,lambda ind : netviewer.drawNet(filename=filename+ind.id+'.svg',view=False)) #evolutionView = EvolutionViewer() #evolutionView.drawSession(monitor,monitor.evoTask.curSession,'fitness') return True
def getNeuron(self, id=-1, layer=-1, xhInLayer=-1, coord=None): ''' 查找满足特定条件神经元(先按id,再按坐标,在按层和层内序号) :param id: 神经元id,有效则会优先查找 :param layer: 所在层 :param xhInLayer: 层内序号 :param coord: 坐标 :return: ''' if id > 0: #优先按照id查找 ns = self.getNeurons() return collections.first(ns, lambda n: n.id == id) if coord is not None: #其次按照特定坐标寻找 ns = self.getNeurons() return collections.first(ns, lambda n: n.coord == coord) #查找特定层中某个序号的神经元 ns = self.getNeurons(layer=layer) if not collections.isEmpty(ns): return None if xhInLayer >= len(ns): return None return ns[xhInLayer]
def activate(self, net, inputs): ''' 激活网络 :param net: 测试网络 :param task: 测试任务 :return: outputs ''' # 取得输入 inputNeurons = net.getInputNeurons() # 重置神经元和突触状态 collections.foreach(net.getNeurons(), lambda n: n.reset()) collections.foreach(net.getSynapses(), lambda s: s.reset()) # 设置输入 for d, v in enumerate(inputs): if d >= len(inputNeurons): break model = models.nervousModels.find( inputNeurons[d].modelConfiguration.modelid) model.execute(inputNeurons[d], net, value=v) s = net.getOutputSynapse(inputNeurons[d].id) if collections.isEmpty(s): continue collections.foreach(s, lambda x: x.getModel().execute(x, net)) # 反复执行 ns = net.getNeurons() neuronCount = net.getNeuronCount() iterCount = 0 outputNeurons = net.getOutputNeurons() #while not collections.all(outputNeurons,lambda n:'value' in n.states.keys()) and iterCount<=neuronCount: while not collections.all( outputNeurons, lambda n: 'value' in n.states) and iterCount <= neuronCount: iterCount += 1 #uncomputeNeurons = collections.findall(ns,lambda n:'value' not in n.states.keys()) uncomputeNeurons = collections.findall( ns, lambda n: 'value' not in n.states) if collections.isEmpty(uncomputeNeurons): break for n in uncomputeNeurons: model = n.getModel() synapses = net.getInputSynapse(n.id) if collections.isEmpty(synapses): continue #if not collections.all(synapses,lambda s:'value' in s.states.keys()):continue if not collections.all(synapses, lambda s: 'value' in s.states): continue model.execute(n, net) synapses = net.getOutputSynapse(n.id) if collections.isEmpty(synapses): continue collections.foreach(synapses, lambda s: s.getModel().execute(s, net)) # 将没结果的输出神经元的值设置为0 #outputNeuronsWithNoResult = collections.findall(outputNeurons,lambda n:'value' not in n.states.keys()) outputNeuronsWithNoResult = collections.findall( outputNeurons, lambda n: 'value' not in n.states) if not collections.isEmpty(outputNeuronsWithNoResult): collections.foreach(outputNeuronsWithNoResult, lambda n: exec("n['value']=0")) # 取得结果 outputs = list(map(lambda n: n['value'], outputNeurons)) if len(outputs) == 1: outputs = outputs[0] return outputs
def execute(self, session): #region 第一步:规划每个物种中应有的个体数量 # 取得物种集合,并按平均适应度排序 species = session.pop.getSpecies() if collections.isEmpty(species): raise RuntimeError('NEAT选择操作失败:物种集合为空') species.sort(key=lambda s: s['fitness']['average'], reverse=True) # 根据物种的平均适应度在所有物种中占的比重,计算每个物种的目标个体数量 specie_total_fitness = sum( list(map(lambda sp: sp['fitness']['average'], species))) totalSize = 0 for i in range(len(species)): specie = species[i] # 根据物种适应度计算目标个体数量 speicesFitness = specie['fitness']['average'] specie.targetSize = int((speicesFitness / specie_total_fitness) * len(session.pop.inds)) totalSize += specie.targetSize # 如果所有物种的目标个体数量之和仍小于种群个体数量,将不足的部分加到适应度最高的物种上(按照上面计算,不会出现大于的情况) if totalSize < len(session.pop.inds): species[0].targetSize += len(session.pop.inds) - totalSize totalSize = len(session.pop.inds) session.monitor.recordDebug( 'neat_selection', '物种的目标个体数量', reduce(lambda x, y: x + "," + y, map(lambda s: str(s.id) + "=" + str(s.targetSize), species))) #endregion #region 第二步:遍历每个物种,如果物种中实际个体数量大于前面计算的每个物种的目标个体数量,则将适应度差的个体淘汰 removeIndids = [] for i in range(len(species)): specie = species[i] # 将物种中个体按照适应度由高到低排序,其中精英个体尽管排前面 specie.indids.sort( key=lambda indid: session.pop[indid]['fitness'] + 0.000001 if session.pop[indid] in session.pop.eliest else 0, reverse=True) # 实际个体数量不多于目标个体数量,不需要淘汰 if len(specie.indids) <= specie.targetSize: continue # 删除适应度最小的个体,直到实际个体数量与目标个体数量相等(这样的删除方法,有可能会导致精英个体也被删除) while len(specie.indids) > specie.targetSize: removeIndid = specie.indids[-1] removeInd = session.pop[removeIndid] removeIndids.append(removeIndid) del specie.indids[-1] # 从物种记录中删除 session.pop.inds.remove(removeInd) # 从种群记录中删除 session.monitor.recordDebug( 'neat_selection', '删除的个体', collections.mapreduce( removeIndids, reducefunc=lambda i, j: str(i) + ',' + str(j))) # 遍历所有物种,如果有物种个体数量为0,则将该物种删除 species = [s for s in species if len(s.indids) > 0] #endregion #region 第三步:对每个物种,随机选择需要交叉操作的个体 corssmeateInds = [] for specie in species: if len(specie.indids) >= specie.targetSize: continue for i in range(specie.targetSize - len(specie.indids)): if len(specie.indids) == 1: corssmeateInds.append((specie.indids[0], specie.indids[0])) elif len(specie.indids) == 2: corssmeateInds.append((specie.indids[0], specie.indids[1])) else: indexpair = random.sample(range(len(specie.indids)), 2) corssmeateInds.append((specie.indids[indexpair[0]], specie.indids[indexpair[1]])) # 有错误:session.monitor.recordDebug('neat_selection', '交叉的个体', reduce(lambda i, j: str(list(i)[0])+"-"+str(list(i)[1]) + ',' + str(list(j)[0])+"-"+str(list(j)[1]), corssmeateInds)) # reduce(lambda i,j:str(i[0])+'-'+str(i[1])+','+str(j[0])+'-'+str(j[1]),[(0,1),(2,3)]) ---> 0-1,2-3 # reduce(lambda i,j:str(i[0])+'-'+str(i[1])+','+str(j[0])+'-'+str(j[1]),[(0,1),(2,3),(4,5)]) ---> 0--,4-5 sdebug = '' for cross in corssmeateInds: if strs.isVaild(sdebug): sdebug += ',' sdebug += str(cross[0]) + '-' + str(cross[1]) session.monitor.recordDebug('neat_selection', '交叉的个体', sdebug) #region 第四步:对所有个体按照适应度从高到低排序,随机选择其中一部分作为变异个体 metateinds = [] # 计算变异个体数量 mutateCount = int(session.runParam.mutate.propotion ) if session.runParam.mutate.propotion >= 1 else int( totalSize * session.runParam.mutate.propotion) if mutateCount <= 0: return True, '', (corssmeateInds, metateinds) # 对所有个体按照适应度从高到低排序 session.pop.inds.sort(key=lambda x: x['fitness'] + 0.000001 if x in session.pop.eliest else 0, reverse=True) # 选择候选变异个体(精英个体将被排除) candidateInds = collections.findall( session.pop.inds, lambda ind: ind not in session.pop.eliest) # 为每个个体计算一个选择概率(适应度越低的被选择的概率就高) if len(candidateInds) <= 0: max, avg, min, stdev = 0., 0., 0., 0. print('变异个体数量无效,' + str(session.pop.eliest)) return True, '选择操作完成,其中淘汰个体数量=' + str( len(removeIndids)) + ',交叉个体数量=' + str( len(corssmeateInds)) + ',变异个体数量=0', (corssmeateInds, []) else: max, avg, min, stdev = collections.rangefeature( list(map(lambda ind: ind['fitness'], candidateInds))) #fitnesssum = sum(list(map(lambda ind:ind['fitness'],candidateInds))) mutateSelProb = [ 1 - ((ind['fitness'] - min) / ((max - min) if max != min else 1)) for index, ind in enumerate(candidateInds) ] mutateSelProb = np.array(mutateSelProb) p = mutateSelProb / mutateSelProb.sum() np.random.seed(0) #p = np.array(mutateSelProb) mutateinds = np.random.choice(candidateInds, size=mutateCount, p=p.ravel()) session.monitor.recordDebug( 'neat_selection', '变异的个体', reduce(lambda i, j: i + ',' + j, map(lambda ind: str(ind.id), mutateinds))) return True, '选择操作完成,其中淘汰个体数量=' + str( len(removeIndids)) + ',交叉个体数量=' + str( len(corssmeateInds)) + ',变异个体数量=' + str(len(mutateinds)), ( corssmeateInds, list(map(lambda ind: ind.id, mutateinds)))
def readText(filename, encode='utf-8', raiseError=False): lines = readLines(filename, None, encode, raiseError) if collections.isEmpty(lines): return '' return reduce(lambda x, y: x + '\n' + y, lines)
def putneuron(self, neuron, inids=None, outinds=None, synapseModelConfig=None): ''' 添加神经元,会检查神经元id,是否重复(重复会删除旧的) :param neuron: Neuron 待添加神经元 :param inids: int or list 输入神经元id :param outinds: int or list 输出神经元id :param synapseModelConfig: 突触计算模型 :return: ''' if neuron is None: return self # 检查神经元id if neuron.id <= 0: idGenerator = idGenerators.find( self.definition.get('idGenerator', 'default')) if idGenerator is None: raise RuntimeError( "连接神经元失败(NeuralNetwork.connect(srcid,destid)):idGenerator无效" ) neuron.id = idGenerator.getNeuronId(self, neuron.coord) # 检查神经元是否已经存在 n = self.getNeuron(id=neuron.id) if n is not None: self.remove(n) # 第一次添加神经元 layerindex = -1 if len(self.neurons) <= 0: self.neurons.append([]) layerindex = 0 else: # 根据神经元所在层查找对应位置 for i, ns in enumerate(self.neurons): if collections.isEmpty(ns): del self.neurons[i] i -= 1 continue if ns[0].layer == neuron.layer: layerindex = i elif ns[0].layer > neuron.layer: layerindex = i self.neurons.insert(i, []) if layerindex >= 0: break continue # 添加神经元 if layerindex < 0: self.neurons.append([neuron]) else: self.neurons[layerindex].append(neuron) # 连接 if inids is not None and synapseModelConfig is not None: self.connect(inids, neuron.id, neuron.birth, synapseModelConfig) if outinds is not None and synapseModelConfig is not None: self.connect(neuron.id, outinds, neuron.birth, synapseModelConfig) return self
def _compute_distance(self, ind1, ind2, session): # 距离计算,这段代码参考了https://github.com/CodeReclaimers/neat-python # 取得距离计算参数 disjoint_coefficient = session.popParam.species.disjoint_coefficient weight_coefficient = session.popParam.species.weight_coefficient # 取得个体神经网络和所有神经元以及所有突触 net1 = ind1.genome net2 = ind2.genome ns1 = net1.getNeurons() ns2 = net2.getNeurons() if collections.isEmpty(ns1): ns1 = [] if collections.isEmpty(ns2): ns2 = [] sps1 = net1.getSynapses() sps2 = net2.getSynapses() if collections.isEmpty(sps1): sps1 = [] if collections.isEmpty(sps2): sps2 = [] # 计算节点差异 disjoint_nodes = 0 node_distance = 0.0 for n1 in ns1: if net2.getNeuron(n1.id) is None: disjoint_nodes += 1 for n2 in ns2: n1 = net1.getNeuron(n2.id) if n1 is None: disjoint_nodes += 1 else: d = abs( n1.getVariableValue('bias', 0.0) - n2.getVariableValue('bias', 0.0)) + abs(n1['value'] - n2['value']) if n1['activation'] != n2['activation']: d += 1.0 d = d * weight_coefficient node_distance += d max_nodes = max(len(ns1), len(ns2)) node_distance = (node_distance + disjoint_coefficient * disjoint_nodes) / max_nodes # 计算突触差异 disjoint_connections = 0 connection_distance = 0.0 for s1 in sps1: if net2.getSynapse(id=s1.id) is None: disjoint_connections += 1 for s2 in sps2: s1 = net1.getSynapse(id=s2.id) if s1 is None: disjoint_connections += 1 else: d = abs(s1['weight'] - s2['weight']) d = d * weight_coefficient connection_distance += d max_conn = max(len(sps1), len(sps2)) connection_distance = (connection_distance + disjoint_coefficient * disjoint_connections ) / max_conn if max_conn > 0 else 0.0 # 计算总距离 distance = node_distance + connection_distance return distance