# 3. 基础操作 本章节将引导您运行第一个 EvoX 优化任务,介绍如何**启动 EvoX **并**初始化优化流程**,如何**配置一个 EvoX 项目**(选择算法和问题并将其组装起来),以及常用的**基本命令**(或方法)来控制优化过程。通过一个简单示例,您将了解到 EvoX 的基本用法。 ## 启动与初始化 安装验证完成后,您可以开始使用 EvoX 编写优化脚本。可以在任意 Python 环境(如终端、Jupyter Notebook、IDE 等)中导入 EvoX。 首先,让我们导入 EvoX 及其相关模块,并初始化一个简单的优化任务。例如,我们使用粒子群优化算法(PSO)来优化经典的 Ackley 函数。Ackley函数是一个常见的基准测试函数,其全局最优解已知在 $(0,0,\dots,0)$ 处,适合作为示例。 以下是一个最小的 EvoX 示例代码,演示如何启动并运行优化: ```python import torch from evox.algorithms import PSO # 导入PSO算法 from evox.problems.numerical import Ackley # 导入Ackley优化问题 from evox.workflows import StdWorkflow, EvalMonitor # 导入标准工作流和监控器 # 1. 定义优化算法和问题 algorithm = PSO( pop_size=50, # 种群规模为50 lb=-32 * torch.ones(2), # 决策变量下界:二维向量,每维-32 ub= 32 * torch.ones(2) # 决策变量上界:二维向量,每维32 ) problem = Ackley() # 优化问题:Ackley函数(默认维度与算法匹配) # 2. 组合工作流(Workflow),并添加监控器用于跟踪结果 monitor = EvalMonitor() workflow = StdWorkflow(algorithm, problem, monitor) # 3. 初始化工作流 workflow.init_step() # 初始化算法和问题内部状态 # 4. 执行优化迭代 for i in range(100): workflow.step() # 推进优化一步 # 5. 获取结果(例如打印最优值) best_fitness = monitor.get_best_fitness() # 从监控器获取当前迭代的最佳适应度值 print("迭代完成,当前找到的最优适应度值:", float(best_fitness)) ``` 上述代码包含以下步骤: - 首先设置了PSO算法的参数:种群规模50,搜索空间为二维,范围在[-32, 32]之间。 - 然后定义了 Ackley 问题(Ackley函数默认定义为二维)。 - 我们创建了一个标准工作流 `StdWorkflow`,将算法和问题**组装**起来,并传入了一个监控器 `EvalMonitor` 用于记录优化过程数据。 - 接着,通过 `workflow.init_step()` 完成初始化操作,这一步会自动初始化种群、随机种子等内部状态。 - 然后,我们运行一个循环,通过 `workflow.step()` 连续执行100次迭代。每调用一次 `step()`,算法会生成新解并评估其适应度,不断逼近最优。 - 最后,我们使用监控器提供的方法 `get_min_fitness()` 获取迭代过程中的最佳适应度值并打印出来。 执行该脚本,您将看到优化迭代的输出,例如: ```text 迭代完成,当前找到的最优适应度值: 9.5367431640625e-07 ``` 由于我们没有在循环中显式打印,每一步的中间结果不会显示,但通过最终的适应度值可以判断算法是否收敛。例如,Ackley函数的最优值是0,若输出接近0则说明PSO找到了接近全局最优的解。您也可以调用 `print(monitor.history)` 查看监控器记录的历史数据,或使用 `monitor.plot()` 绘制收敛曲线(需要安装可视化支持,如plotly)。 ```{note} `StdWorkflow` 是 EvoX 提供的**标准优化流程**封装,它内部实现了传统进化算法中的“初始化-迭代更新”逻辑,封装了算法与问题的交互。对于大多数简单应用,直接使用 `StdWorkflow` 即可满足需求。而 `EvalMonitor` 则是一个监控器,实现了 `get_best_fitness()`、`plot()` 等方法,用于收集和展示优化过程中的性能指标。初学者可以暂时将其理解为一个记录簿,记录每次迭代的最好结果等信息,以便事后分析。 ``` 在上面的示例中,我们已经初步配置了一个 EvoX 项目,包括选择算法、定义问题以及组建工作流。一般来说,配置一个 EvoX 项目需要以下几个步骤: 1. **选择/定义优化问题**:明确您要解决的优化问题是什么。例如,若要优化某个数学函数, EvoX 的 `evox.problems` 模块下提供了大量**内置问题**(如 Sphere, Rastrigin, Ackley 等经典函数)可直接使用。如果内置库没有涵盖您的问题,您可以自定义问题(在后续开发章节详述)。配置问题时,通常需要知道**决策变量的维度**、**取值范围**。 2. **选择/配置优化算法**:根据问题类型选择合适的演化算法。EvoX 在 `evox.algorithms` 下提供了丰富的算法,包括单目标算法(如 PSO、GA、CMA-ES 等)和多目标算法(如 NSGA-II、RVEA 等)。选择算法后,您一般需要设置算法参数,例如种群规模(`pop_size`)、算法特有参数(比如 GA 的交叉概率、变异概率)等。大部分算法需要定义**变量范围**(下界 lb 和上界 ub )以及问题维度以便初始化群体。如果使用多目标算法,还需指定目标数量(`n_objs`)等参数。EvoX 的算法实现通常已经为常见超参数提供默认值,但初学者应根据具体任务酌情调整这些参数以取得更好效果。 3. **组装工作流**:有了算法和问题实例后,需要将二者“组装”到一个工作流中。工作流表示完整的优化过程控制。在 EvoX 中通常使用 `StdWorkflow` 来组合算法和问题。如果需要监控优化进展,可以在 Workflow 中加入一个监控器(如 `EvalMonitor`)。监控器不是必须的,但在调试和分析时,它可以提供有用的信息。组装工作流的代码一般是一行,如:`workflow = StdWorkflow(algo, prob, monitor)`。 4. **初始化**:调用工作流的初始化方法以开始优化。新版 EvoX 提供了便捷的 `Stdworkflow.init_step()` 方法,一次性完成初始化操作。 5. **运行迭代**:通过循环反复调用 `workflow.step()` 来推进演化过程。每次调用会进行一次迭代,包括算法内部的“生成新解->评估->选择”等步骤。在迭代过程中,可以通过监控器查看实时结果,例如每若干代打印一次当前最优适应度。迭代的终止条件可根据需求设置:常见的是固定代数(如运行100代),或者监控指标在若干代内收敛(改进不足阈值)时停止。 6. **获取结果**:迭代结束后,需要从算法中提取最终结果,比如找到的最优解和对应的目标值。使用 EvoX 时,典型地通过监控器获取这些信息。如上例使用 `EvalMonitor.get_best_fitness()` 得到最优适应度值。若想获取最优解向量,一种方法是让问题对象对候选解进行评估时保存了谁最好,或者监控器提供相应接口。在 EvoX 的标准实现中,`EvalMonitor` 会记录每一代的最优个体以及适应度,可以通过其属性获取。假设 `monitor.history` 保存了历代信息,我们可以获取最后一代的最优解。当然,也可以不使用`EvalMonitor`,而是在循环结束后直接从算法对象中查询——具体取决于算法实现。如果自定义算法实现了 `get_best()` 方法或在其 state 中存储了最优个体,那也可直接提取。但由于 EvoX 强调纯函数和模块化,通常通过监控模块提供结果。 通过上述步骤配置项目,您可以清晰地组织起优化任务的代码结构。对于初学者来说,重点在于理解**算法-问题-工作流**三者如何协同:算法负责如何生成和改进解,问题负责评价解的好坏,工作流则把二者串联起来形成循环。 接下来,我们将介绍一些 EvoX 中可用的基本命令和功能调用,加深您对操控优化过程的理解。 ## 基本命令介绍 在使用 EvoX 过程中,有一些**常用的方法和函数**相当于“命令”,需要熟悉: ### 工作流相关方法 - **`StdWorkflow.init_step()`**:初始化。这是启动优化流程的快捷命令,常用于脚本开始处。它会调用算法和问题的初始化逻辑,生成初始种群并评估适应度。执行该命令后,Workflow 内部就有了初始状态,可以进入循环迭代。 - **`StdWorkflow.step()`**:推进优化一步。每次调用会让算法根据当前种群状态生成新的候选解并评估,然后选择生成下一个种群。用户通常在一个循环中多次调用它来反复迭代。`step()` 的返回值通常为空(内部状态在Workflow中更新),或者旧版接口可能返回新的 state。对于初学者,直接调用无需理会返回值即可。 ### 监控器相关方法 以 `EvalMonitor`为例,常用方法包括: - `EvalMonitor.get_best_fitness()`: 获取迭代以来记录到的最小适应度(对于最小化问题)或最大适应度(对于最大化问题,一般监控器有区分)。这用于了解当前最佳结果。 - `EvalMonitor.get_history()` 或 `monitor.history`: 获取完整的历史记录,比如每代的最优值等。可用于分析收敛趋势。 - `EvalMonitor.plot()`: 绘制收敛曲线或指标曲线,需要环境支持图形界面或在Notebook中运行。通常监控器基于 plotly 绘图,绘制结果能帮助我们直观判断算法性能。 监控器在内部通过记录每代调用问题评估的次数和结果等实现上述功能,一般不需要用户干预,只需在合适时机提取数据。 ### 算法相关方法 - `Algorithm.__init__()`方法:算法的初始化方法。其中,算法的变量一般使用`evoox.core.Mutable()`进行包装,超参数使用`evox.core.Parameter()`进行包装。 - `Algorithm.step()`方法:特殊场景或自定义算法/问题时,您可能直接调用算法的`step()`方法,通常该方法内包含该算法全部的计算迭代过程。 - `Algorithm.init_step()`方法:`init_step()`内含一个算法的第一次迭代过程,在不进行重写的情况下,`init_step()`方法会直接调用`step()`方法。一般对常规场景来说,算法的第一次迭代与其他迭代并无不同,这些算法可能不需要也没有重写`init_step()`,但如果某些算法需要进行超参数优化,那么可能除了要在`__init__()`中,设置超参数,还需要在`init_step()`方法中将超参数或者受超参数影响的变量进行更新。 ### 设备与并行控制 - `.to(device)` 方法:如果需要在程序中切换计算设备,可以使用 PyTorch 的 `.to(device)` 方法将张量(`torch.Tensor`)迁移到GPU/CPU(实际上 Pytorch 的一些方法(如`torch.randn`)也需要您指定计算设备)。一般来说,只要您使用`torch.set_default_device()`指定计算设备为`cuda:0`(前提是您的设备支持且正确安装了 EvoX 及其依赖,您可以使用`torch.cuda.is_available()`验证这一点),那么 EvoX 的大部分高性能并行计算都会在GPU上自动进行。如果您自定义算法、问题、监控器等任何类的对象时,产生了新的张量或者使用了需要指定程序计算设备的 Pytorch 方法,建议您指定`device`为` cuda:0`或`torch.get_default_device()`,以免计算分到不同计算设备上进行而大大降低性能。 对于初学者,上述基本方法足以完成常规优化任务。简而言之:**初始化问题/算法——搭建监控器——组装工作流——运行和输出**,就是 EvoX 最常用的操作流程。掌握这些后,您已经可以运用 EvoX 解决简单的问题了。 在进入下一章前,建议您多尝试修改示例中的算法或问题,比如将 PSO 换成不同算法,将 Ackley 换成其他测试函数,或使用监控器获得更多信息等,以体会 EvoX 项目配置的灵活性。