pyswarms 中的粒子位置未正确参数化

Particle position not being parametrized properly in pyswarms

我在为 pyswarms 设计适应度函数时遇到问题,该函数实际上会遍历粒子。我的设计基于这个(有效的)示例代码:

# import modules
import numpy as np

# create a parameterized version of the classic Rosenbrock unconstrained optimzation function
def rosenbrock_with_args(x, a, b, c=0):
    f = (a - x[:, 0]) ** 2 + b * (x[:, 1] - x[:, 0] ** 2) ** 2 + c
    return f

from pyswarms.single.global_best import GlobalBestPSO

# instatiate the optimizer
x_max = 10 * np.ones(2)
x_min = -1 * x_max
bounds = (x_min, x_max)
options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9}
optimizer = GlobalBestPSO(n_particles=10, dimensions=2, options=options, bounds=bounds)

# now run the optimization, pass a=1 and b=100 as a tuple assigned to args

cost, pos = optimizer.optimize(rosenbrock_with_args, 1000, a=1, b=100, c=0)

kwargs={"a": 1.0, "b": 100.0, 'c':0}

似乎通过写x[:, 0]x[:, 1],这不知何故参数化了优化函数的粒子位置矩阵。例如在调试器中执行x[:, 0] returns:

array([ 9.19955426, -5.31471451, -2.28507312, -2.53652044, -6.29916204, -8.44170591, 7.80464884, -6.42048159, 9.77440842, -9.06991295])

现在,跳转到我的代码(摘自),我有这个:

def optimize_eps_and_mp(x):

    clusterer = DBSCAN(eps=x[:, 0], min_samples=x[:, 1], metric="precomputed")
    clusterer.fit(distance_matrix)
    clusters = pd.DataFrame.from_dict({index_to_gid[i[0]]: [i[1]] for i in enumerate(clusterer.labels_)},
                                      orient="index", columns=["cluster"])
    settlements_clustered = settlements.join(clusters)
    cluster_pops = settlements_clustered.loc[settlements_clustered["cluster"] >= 0].groupby(["cluster"]).sum()["pop_sum"].to_list()
    print()

    return 1


options = {'c1': 0.5, 'c2': 0.3, 'w':0.9}
max_bound = [1000, 10]
min_bound = [1, 2]
bounds = (min_bound, max_bound)
n_particles = 10

optimizer = ps.single.GlobalBestPSO(n_particles=n_particles, dimensions=2, options=options, bounds=bounds)
cost, pos = optimizer.optimize(optimize_eps_and_mp, iters=1000)

(变量 distance_matrixsettlements 在代码的前面定义了,但是它在行 clusterer = DBSCAN(eps=x[:, 0], min_samples=x[:, 1], metric="precomputed") 上失败了,所以它们不相关。另外,我知道它总是返回 1,我只是想在完成函数之前将它无误地返回 运行)

当我在调试器中执行 x[:, 0] 时,它 returns:

array([-4.54925788, 3.94338766, 0.97085618, 9.44128746, -2.1932764 , 9.24640763, 9.18286758, -8.91052863, 0.637599 , -2.28228841])

因此,在结构方面与工作示例相同。但它在行 clusterer = DBSCAN(eps=x[:, 0], min_samples=x[:, 1], metric="precomputed") 上失败了,因为它将 x[:, 0] 的全部内容传递给 DBSCAN 函数,而不是像工作示例中那样对其进行参数化。

这些例子之间有什么我没有看到的区别吗?

我还尝试将工作示例 (rosenbrock_with_args) 中的适应度函数粘贴到我的代码中并对其进行优化,以消除我的实现设置不正确的任何可能性。然后解决方案正常收敛,所以我完全不知道为什么它不适用于我的函数 (optimize_eps_and_mp)

我得到的确切堆栈跟踪是指 dbscan 算法中的一个错误,我假设是因为它以某种方式传递了整组粒子群值而不是单个值:

pyswarms.single.global_best:   0%|          |0/1000Traceback (most recent call last):
  File "C:/FILES/boates/work_local/_code/warping-pso-dbscan/optimize_eps_and_mp.py", line 63, in <module>
    cost, pos = optimizer.optimize(optimize_eps_and_mp, iters=1000)
  File "C:\FILES\boates\Anaconda\envs\warping_pso_dbscan\lib\site-packages\pyswarms\single\global_best.py", line 184, in optimize
    self.swarm.current_cost = compute_objective_function(self.swarm, objective_func, pool=pool, **kwargs)
  File "C:\FILES\boates\Anaconda\envs\warping_pso_dbscan\lib\site-packages\pyswarms\backend\operators.py", line 239, in compute_objective_function
    return objective_func(swarm.position, **kwargs)
  File "C:/FILES/boates/work_local/_code/warping-pso-dbscan/optimize_eps_and_mp.py", line 38, in optimize_eps_and_mp
    clusterer.fit(distance_matrix)
  File "C:\FILES\boates\Anaconda\envs\warping_pso_dbscan\lib\site-packages\sklearn\cluster\dbscan_.py", line 351, in fit
    **self.get_params())
  File "C:\FILES\boates\Anaconda\envs\warping_pso_dbscan\lib\site-packages\sklearn\cluster\dbscan_.py", line 139, in dbscan
    if not eps > 0.0:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
pyswarms.single.global_best:   0%|          |0/1000

TL;DR

粒子群优化使用批处理。 给定一批粒子,优化后的函数必须 return 一批成本。

错误信息解释

这是错误消息中有趣的部分:

  [...]
  File "C:\FILES\boates\Anaconda\envs\warping_pso_dbscan\lib\site-packages\sklearn\cluster\dbscan_.py", line 139, in dbscan
    if not eps > 0.0:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

这是一个非常常见的 Numpy 错误信息。 当您尝试使用数组作为条件时它会出现。 正如消息所解释的那样,什么是 truth value[True, False] 这样的数组。 你必须使用像 all() 这样的函数 或 any() 将您的数组转换为单个布尔值。

那么,为什么会这样呢? 因为 eps 不是数组 .

来自 DBSCAN class 的文档, 参数 epsmin_samples 是可选的整数。 在这里你传递给他们数组。

clusterer = DBSCAN(eps=x[:, 0], min_samples=x[:, 1], metric="precomputed")

示例比较

您询问了为什么您的代码与 rosenbrock_with_args 函数一起工作。 那是因为它执行的操作可以很好地处理数组。 你传递给它一个二维数组 x(一批粒子),形状为 [10, 2](10 个 2 维粒子)和 a, b, c 标量。 由此,它计算出形状为 [10] 的一维数组,这是每个粒子的成本值。

但是,您的新 optimize_eps_and_mp 函数尝试对数组执行一些不受支持的操作。 特别是,您使用数组的一维作为 DBSCANeps 参数,它需要一个标量。

要使其正常工作,您应该自己处理批处理,实例化许多 DBSCAN 个对象:

for row in x:
  clusterer = DBSCAN(eps=row[0], min_value=row[1], [...])

分布式执行

那:

the pyswarms library is supposed to run it [the objective function] many times independently (for each particle in the swarm) and evaluate their results, and it does this somehow by distributing the function to multiple sets of inputs all at once.

pyswarm 实际上可以使用 optimizen_processes 参数并行执行集群 功能。 在这种情况下,您的函数在不同进程中被多次调用,但仍以数组作为输入。 在您的情况下,有 10 个粒子、2 个维度和 n_processes 作为 None(默认值),您的 x 输入的形状为 [10, 2]。 如果您将 n_processes 设置为 2,您的 x 输入将具有 [5, 2] 的形状。 最后,如果将 n_processes 设置为 10,则 x 输入的形状将为 [1, 2]。 在任何一种情况下,您都必须 "un-roll" 您的粒子群以进行 DBSCAN 实例化。

import pyswarms as ps


def foo(x):
    print(x.shape)
    return x[:,0]


if __name__ == "__main__":
    options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9}
    max_bound = [1000, 10]
    min_bound = [1, 2]
    bounds = (min_bound, max_bound)
    n_particles = 10

    optimizer = ps.single.GlobalBestPSO(n_particles=n_particles, dimensions=2, options=options, bounds=bounds)
    for n_processes in [None, 1, 2, 10]:
        print("\nParallelizing on {} processes.".format(n_processes))
        optimizer.optimize(foo, iters=1, n_processes=n_processes)
Parallelizing on None processes.
(10, 2)

Parallelizing on 1 processes.
(10, 2)

Parallelizing on 2 processes.
(5, 2)
(5, 2)

Parallelizing on 10 processes.
(1, 2)
(1, 2)
(1, 2)
(1, 2)
(1, 2)
(1, 2)
(1, 2)
(1, 2)
(1, 2)
(1, 2)

因此,这里是关于如何在您的案例中使用 DBSCAN 的完整示例。

def optimize_eps_and_mp(x):
    num_particles = x.shape[0]
    costs = np.zeros([num_particles])
    print("Particles swarm", x)

    for idx, particle in enumerate(x):
        print("Particle", particle)
        clusterer = DBSCAN(eps=x[0], min_samples=x[1], metric="precomputed")
        clusterer.fit(distance_matrix)
        clusters = pd.DataFrame.from_dict({index_to_gid[i[0]]: [i[1]] for i in enumerate(clusterer.labels_)},
                                      orient="index", columns=["cluster"])
        settlements_clustered = settlements.join(clusters)
        cluster_pops = settlements_clustered.loc[settlements_clustered["cluster"] >= 0].groupby(["cluster"]).sum()["pop_sum"].to_list()

        cost = 1  # Update this to compute cost value of the current particle
        costs[idx] = cost

    return costs