geatpy-dev / geatpy

Evolutionary algorithm toolbox and framework with high performance for Python
http://www.geatpy.com
GNU Lesser General Public License v3.0
1.96k stars 725 forks source link

【重大bug】call_back中,Population.save()与直接写成数据,完全对不上!!! #347

Closed RebelYoung closed 6 months ago

RebelYoung commented 6 months ago

Describe the bug 回调函数中我使用了ea.Population.save()函数,存下这一代的信息和在self.evalVars()函数中直接文本写出Vars,ObjV,CV完全对不上。

To Reproduce Steps to reproduce the behavior:

def call_back1(alg: ea.Algorithm, pop: ea.Population):
    pop.save(str(pop_path)) # 存在一个路径下,该函数为geatpy自带,在结束优化时,程序自身也使用了这个save()函数

以下是一个非常非常简单的测试problem,测试中,我用了Dim=2,NIND=10

class MyProblem_test(MyProblem3):
    def evalVars(self, Vars):
        with open(self.opt_path / f'Gen{self.currentGen}_PopInfo' / f'Gen{self.currentGen}_Vars.txt', 'w') as writer:
            np.savetxt(writer, Vars, delimiter=',')
        ObjV = []
        CV = []
        for i in range(Vars.shape[0]):
            n = int(Vars.shape[1] / 2)
            Au = Vars[i, 0:n]
            Al = Vars[i, n:]
            obj = [np.sum(Au), np.sum(Al)]
            cv = [-0.1] * 12
            ObjV.append(obj)
            CV.append(cv)
        with open(self.opt_path / f'Gen{self.currentGen}_PopInfo' / f'Gen{self.currentGen}_ObjV.txt', 'w') as writer:
            np.savetxt(writer, np.array(ObjV), delimiter=',')
        with open(self.opt_path / f'Gen{self.currentGen}_PopInfo' / f'Gen{self.currentGen}_CV.txt', 'w') as writer:
            np.savetxt(writer, np.array(CV), delimiter=',')
        self.currentGen += 1
        return np.array(ObjV), np.array(CV)

我在evalVars函数中直接将Vars,ObjV,CV写入了文本。 RI编码下,Phen和写入Vars应该一致。。。结果完全对不上,而且NIND越大,偏差越大。。。有近似值,但是误差无法结束,且近似值的index也不对!

这个问题造成我中间文件留下的信息,和最后优化结果无法比对!!!

我在想这是不是nsga2的问题?父代子代合并?但是中间数据也存在对不上的问题。

image

RebelYoung commented 6 months ago

另外提一下,这个测试里,第一代Gen=0的时候数据是对的。

RebelYoung commented 6 months ago

@geatpy-dev 我试了SEGA算法,结果也对不上了,这是怎么回事,,。?

geatpy-dev commented 6 months ago

单步调试即可

RebelYoung commented 6 months ago

单步调试即可

@geatpy-dev 我调试过了,就是对不上啊,我给你源码,你试试?

geatpy-dev commented 6 months ago

我这边用demo代码测试并无问题。应该涉及你的具体业务代码,需要你单步调试解决。

RebelYoung commented 6 months ago

我这边用demo代码测试并无问题。应该涉及你的具体业务代码,需要你单步调试解决。 @geatpy-dev 你有没有测试,在evalVars中写出Vars,ObjV,CV和Population.save()的csv比对,我现在测试一下demo/moea_demo2中的文件,在evalVars函数中加入np.savetxt进行校验

RebelYoung commented 6 months ago

@geatpy-dev 我调试了一遍,代码如下:只增加了outFunc,和evalVars中的写出。 10个个体,5代。每代存于Gen{i}中,比对Population.save()和np.savetxt的结果。 除了第一代以外,仍然不同。 image

import numpy as np

import geatpy as ea
from pathlib import Path

class MyProblem(ea.Problem):  # 继承Problem父类

    def __init__(self, M=2):
        name = 'MyProblem'  # 初始化name(函数名称,可以随意设置)
        Dim = 1  # 初始化Dim(决策变量维数)
        maxormins = [1] * M  # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)
        varTypes = [0] * Dim  # 初始化varTypes(决策变量的类型,0:实数;1:整数)
        lb = [-10] * Dim  # 决策变量下界
        ub = [10] * Dim  # 决策变量上界
        lbin = [1] * Dim  # 决策变量下边界(0表示不包含该变量的下边界,1表示包含)
        ubin = [1] * Dim  # 决策变量上边界(0表示不包含该变量的上边界,1表示包含)
        # 调用父类构造方法完成实例化
        ea.Problem.__init__(self,
                            name,
                            M,
                            maxormins,
                            Dim,
                            varTypes,
                            lb,
                            ub,
                            lbin,
                            ubin)
        self.currentGen=0
        self.opt_path=None

    def evalVars(self, Vars):  # 目标函数
        if not (self.opt_path/f'Gen{self.currentGen}').exists():
            (self.opt_path/f'Gen{self.currentGen}').mkdir(parents=True)
        with open(self.opt_path / f'Gen{self.currentGen}' / f'Gen{self.currentGen}_Vars.txt', 'w') as writer:
            np.savetxt(writer, Vars, delimiter=',')
        f1 = Vars**2
        f2 = (Vars - 2)**2
        #        # 利用罚函数法处理约束条件
        #        exIdx = np.where(Vars**2 - 2.5 * Vars + 1.5 < 0)[0] # 获取不满足约束条件的个体在种群中的下标
        #        f1[exIdx] = f1[exIdx] + np.max(f1) - np.min(f1)
        #        f2[exIdx] = f2[exIdx] + np.max(f2) - np.min(f2)
        # 利用可行性法则处理约束条件
        CV = -Vars**2 + 2.5 * Vars - 1.5
        ObjV = np.hstack([f1, f2])
        with open(self.opt_path / f'Gen{self.currentGen}' / f'Gen{self.currentGen}_ObjV.txt', 'w') as writer:
            np.savetxt(writer, ObjV, delimiter=',')
        with open(self.opt_path / f'Gen{self.currentGen}' / f'Gen{self.currentGen}_CV.txt', 'w') as writer:
            np.savetxt(writer, CV, delimiter=',')
        self.currentGen+=1
        return ObjV, CV

def call_back1(alg: ea.Algorithm, pop: ea.Population):
    if (alg.cwd/f'Gen{alg.currentGen}').exists():
        pop.save(str(alg.cwd/f'Gen{alg.currentGen}'))
# -*- coding: utf-8 -*-
"""该案例展示了一个带约束连续决策变量的最小化目标的双目标优化问题的求解。详见MyProblem.py."""
# from MyProblem import MyProblem  # 导入自定义问题接口

# import geatpy as ea  # import geatpy

if __name__ == '__main__':
    # 实例化问题对象
    problem = MyProblem()
    problem.opt_path=Path.cwd()/'debug\debug_moea'
    # 构建算法
    algorithm = ea.moea_NSGA2_templet(
        problem,
        ea.Population(Encoding='RI', NIND=10),
        MAXGEN=5,  # 最大进化代数
        outFunc=call_back1,
        logTras=0)  # 表示每隔多少代记录一次日志信息,0表示不记录。
    algorithm.cwd=problem.opt_path
    # 求解
    res = ea.optimize(algorithm,
                      verbose=False,
                      drawing=1,
                      outputMsg=True,
                      drawLog=False,
                      saveFlag=False,)
    print(res)
RebelYoung commented 6 months ago

@geatpy-dev 我调试了一遍,代码如下:只增加了outFunc,和evalVars中的写出。 10个个体,5代。每代存于Gen{i}中,比对Population.save()和np.savetxt的结果。 除了第一代以外,仍然不同。 image

import numpy as np

import geatpy as ea
from pathlib import Path

class MyProblem(ea.Problem):  # 继承Problem父类

    def __init__(self, M=2):
        name = 'MyProblem'  # 初始化name(函数名称,可以随意设置)
        Dim = 1  # 初始化Dim(决策变量维数)
        maxormins = [1] * M  # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)
        varTypes = [0] * Dim  # 初始化varTypes(决策变量的类型,0:实数;1:整数)
        lb = [-10] * Dim  # 决策变量下界
        ub = [10] * Dim  # 决策变量上界
        lbin = [1] * Dim  # 决策变量下边界(0表示不包含该变量的下边界,1表示包含)
        ubin = [1] * Dim  # 决策变量上边界(0表示不包含该变量的上边界,1表示包含)
        # 调用父类构造方法完成实例化
        ea.Problem.__init__(self,
                            name,
                            M,
                            maxormins,
                            Dim,
                            varTypes,
                            lb,
                            ub,
                            lbin,
                            ubin)
        self.currentGen=0
        self.opt_path=None

    def evalVars(self, Vars):  # 目标函数
        if not (self.opt_path/f'Gen{self.currentGen}').exists():
            (self.opt_path/f'Gen{self.currentGen}').mkdir(parents=True)
        with open(self.opt_path / f'Gen{self.currentGen}' / f'Gen{self.currentGen}_Vars.txt', 'w') as writer:
            np.savetxt(writer, Vars, delimiter=',')
        f1 = Vars**2
        f2 = (Vars - 2)**2
        #        # 利用罚函数法处理约束条件
        #        exIdx = np.where(Vars**2 - 2.5 * Vars + 1.5 < 0)[0] # 获取不满足约束条件的个体在种群中的下标
        #        f1[exIdx] = f1[exIdx] + np.max(f1) - np.min(f1)
        #        f2[exIdx] = f2[exIdx] + np.max(f2) - np.min(f2)
        # 利用可行性法则处理约束条件
        CV = -Vars**2 + 2.5 * Vars - 1.5
        ObjV = np.hstack([f1, f2])
        with open(self.opt_path / f'Gen{self.currentGen}' / f'Gen{self.currentGen}_ObjV.txt', 'w') as writer:
            np.savetxt(writer, ObjV, delimiter=',')
        with open(self.opt_path / f'Gen{self.currentGen}' / f'Gen{self.currentGen}_CV.txt', 'w') as writer:
            np.savetxt(writer, CV, delimiter=',')
        self.currentGen+=1
        return ObjV, CV

def call_back1(alg: ea.Algorithm, pop: ea.Population):
    if (alg.cwd/f'Gen{alg.currentGen}').exists():
        pop.save(str(alg.cwd/f'Gen{alg.currentGen}'))
# -*- coding: utf-8 -*-
"""该案例展示了一个带约束连续决策变量的最小化目标的双目标优化问题的求解。详见MyProblem.py."""
# from MyProblem import MyProblem  # 导入自定义问题接口

# import geatpy as ea  # import geatpy

if __name__ == '__main__':
    # 实例化问题对象
    problem = MyProblem()
    problem.opt_path=Path.cwd()/'debug\debug_moea'
    # 构建算法
    algorithm = ea.moea_NSGA2_templet(
        problem,
        ea.Population(Encoding='RI', NIND=10),
        MAXGEN=5,  # 最大进化代数
        outFunc=call_back1,
        logTras=0)  # 表示每隔多少代记录一次日志信息,0表示不记录。
    algorithm.cwd=problem.opt_path
    # 求解
    res = ea.optimize(algorithm,
                      verbose=False,
                      drawing=1,
                      outputMsg=True,
                      drawLog=False,
                      saveFlag=False,)
    print(res)

@geatpy-dev 你直接运行试一下,比对f'Gen{i}/Gen{i}_ObjV.txt'f'Gen{i}/ObjV.csv'

RebelYoung commented 6 months ago

代码其余部分没有动过,是demo/moea_demo2 @geatpy-dev

RebelYoung commented 6 months ago

@geatpy-dev ,有没有测试一下

geatpy-dev commented 6 months ago

Geatpy内置并无call_back方法,你需要自己单步调试检查你的代码。