技術共有

差分進化アルゴリズム

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. はじめに

差分進化 (DE) は、多数のランダムな個体を作成し、それらを事前定義された評価指標と比較し、最良の個体を保持し、残りの個体の特性を混合して新しい個体を作成し、このプロセスを繰り返すことで機能するアルゴリズムです。グローバル最適化の問題。次のコーディングでは、このプロセスを誰にでも丁寧に説明します。

2. コード

# 创建一个对象。
from typing import List, Dict, Tuple, Union

class Individual:
    def __init__(
            self,
            parameters: Dict[str, float],
            score: Union[float, None] = None
    ) -> None:
        super().__init__()
        self.parameter_values = parameters
        self.score = score
   # 该对象保存单个个体的参数和分数。具有参数的个体x=2, y=3     
parameter_value = {"x":2, "y":3}
score = 13
# 评分
def scoring(ind: Individual) -> float:
    return ind.parameter_values["x"] ** 2 + ind.parameter_values["y"] ** 2
# 创造一个群体并进化它们。
class Population:
    # 最大化方向常量
    MAXIMIZE = 1
    # 最小化方向常量
    MINIMIZE = -1

    def __init__(
            self,
            pop_size,  # 种群大小
            direction: int,  # 优化方向(1 表示最大化,-1 表示最小化)
            bounds: Dict[str, Tuple[float, float]],  # 参数的边界范围字典
            parameters: List[str]  # 参数名称列表
    ) -> None:
        """
        初始化 Population 类的实例

        参数:
        pop_size (int):种群的大小
        direction (int):优化的方向,1 表示最大化,-1 表示最小化
        bounds (Dict[str, Tuple[float, float]]):包含每个参数的边界范围的字典
        parameters (List[str]):参数的名称列表
        """
        self.pop_size = pop_size
        self.direction = direction
        self.bounds = bounds
        self.parameters = parameters
        self.individuals: List[Individual] = []  # 存储个体的列表

    def _create_individual(self) -> Individual:
        """
        内部方法,用于创建一个个体

        返回:
        Individual:创建的个体
        """
        dict_parameters = {}  # 用于存储个体的参数值
        for parameter in self.parameters:
            # 在参数的边界范围内生成随机值
            param_value = random.uniform(self.bounds[parameter][0], self.bounds[parameter][1])
            dict_parameters[parameter] = param_value
        individual = Individual(parameters=dict_parameters)  # 创建个体对象
        individual.score = scoring(individual)  # 计算个体的得分
        return individual

    def initiate(self):
        """
        初始化种群,创建指定数量的个体并添加到 individuals 列表中
        """
        for i in range(self.pop_size):
            individual = self._create_individual()
            self.individuals.append(individual)

    def get_best_individual(self) -> Individual:
        """
        获取种群中得分最优的个体

        返回:
        Individual:得分最优的个体
        """
        self.best_individual = max(self.individuals, key=lambda x: x.score * self.direction)
        return self.best_individual

    def get_worst_individual(self) -> Individual:
        """
        获取种群中得分最差的个体

        返回:
        Individual:得分最差的个体
        """
        self.best_individual = min(self.individuals, key=lambda x: x.score * self.direction)
        return self.best_individual

    def get_mean_score(self) -> float:
        """
        计算种群中个体得分的平均值

        返回:
        float:个体得分的平均值
        """
        self.mean_score = sum([ind.score for ind in self.individuals]) / len(self.individuals)
        return self.mean_score

    def get_std_score(self) -> float:
        """
        计算种群中个体得分的标准差

        返回:
        float:个体得分的标准差
        """
        self.std_score = sum([(ind.score - self.mean_score) ** 2 for ind in self.individuals]) / len(self.individuals)
        return self.std_score
        class Optimization:
    def __init__(self, f: float, cp: float, population: Population):
        """
        初始化 Optimization 类的实例

        参数:
        f (float):某个相关的浮点数参数
        cp (float):某个相关的浮点数参数
        population (Population):种群对象
        """
        self.f = f
        self.cp = cp
        self.population = population

    @staticmethod
    def _mutant_choose(population: Population) -> Tuple[Individual, Individual, Individual]:
        """
        静态方法,从种群中选择 3 个随机个体

        参数:
        population (Population):要选择个体的种群

        返回:
        Tuple[Individual, Individual, Individual]:随机选择的 3 个个体
        """
        # 从种群的个体列表中随机抽取 3 个个体
        individuals = population.individuals
        random_individuals = random.sample(individuals, 3)
        return tuple(random_individuals)

    @staticmethod
    def _handle_boundaries(parameter_value: float, bounds: Tuple[float, float]) -> float:
        """
        静态方法,处理参数值超出边界的情况

        参数:
        parameter_value (float):要检查的参数值
        bounds (Tuple[float, float]):参数的边界范围

        返回:
        float:处理后的参数值
        """
        # 如果参数值大于上界,将其设为上界
        if parameter_value > bounds[1]:
            parameter_value = bounds[1]
        # 如果参数值小于下界,将其设为下界
        elif parameter_value < bounds[0]:
            parameter_value = bounds[0]
        return parameter_value

    @staticmethod
    def _calculate_parameter(
            semi_parents: Tuple[Individual, Individual, Individual],
            parameter_name: str,
            bounds: Tuple[float, float],
            f: float
    ) -> float:
        """
        静态方法,根据半亲代计算试验个体的参数

        参数:
        semi_parents (Tuple[Individual, Individual, Individual]):三个半亲代个体
        parameter_name (str):参数的名称
        bounds (Tuple[float, float]):参数的边界范围
        f (float):某个相关的浮点数参数

        返回:
        float:计算得到的试验个体的参数值
        """
        # 根据半亲代计算试验个体的参数
        trial_parameter = semi_parents[0].parameter_values[parameter_name] + 
                          f * (semi_parents[1].parameter_values[parameter_name] -
                               semi_parents[2].parameter_values[parameter_name])
        # 处理参数值超出边界的情况
        trial_parameter = Optimization._handle_boundaries(trial_parameter, bounds)
        return trial_parameter

    def _mutation(self, population: Population) -> Individual:
        """
        执行变异操作,创建试验个体

        参数:
        population (Population):要操作的种群

        返回:
        Individual:创建的试验个体
        """
        # 选择半亲代
        semi_parents = Optimization._mutant_choose(population)
        trial_parameters = {}
        # 对于每个参数名称
        for parameter in population.parameters:
            # 计算试验个体的参数
            trial_parameter = self._calculate_parameter(
                semi_parents, parameter,
                population.bounds[parameter],
                self.f
            )
            trial_parameters[parameter] = trial_parameter
        # 创建试验个体
        trial_individual = Individual(parameters=trial_parameters)
        return trial_individual

def _crossover(self, parent: Individual, trial: Individual, parameters):
    """
    执行交叉操作,生成子代个体

    参数:
    parent (Individual):父代个体
    trial (Individual):试验个体
    parameters (List[str]):参数列表

    返回:
    Individual:生成的子代个体
    """
    child_parameters = {}
    # 对于每个参数
    for parameter in parameters:
        # 生成随机概率
        prob = random.random()
        # 根据概率决定子代参数取值来自父代还是试验个体
        if prob < self.cp:
            child_parameters[parameter] = parent.parameter_values[parameter]
        else:
            child_parameters[parameter] = trial.parameter_values[parameter]
    # 创建子代个体
    child = Individual(parameters=child_parameters)
    return child

@staticmethod
def _selection(child: Individual, parent: Individual, direction: int) -> bool:
    """
    执行选择操作,比较子代和父代个体的得分,并根据方向决定是否选择子代

    参数:
    child (Individual):子代个体
    parent (Individual):父代个体
    direction (int):优化方向(1 表示最大化,-1 表示最小化)

    返回:
    bool:如果子代更优(或得分相等且倾向于选择子代),则返回 True,否则返回 False
    """
    child.score = scoring(child)
    # 在得分相等的情况下倾向于选择子代
    if direction == Population.MAXIMIZE:
        return child.score >= parent.score
    else:
        return child.score <= parent.score


def main(self, generations: int):
    """
    主函数,执行多代的优化过程

    参数:
    generations (int):要执行的代数
    """
    population = self.population

    for gen in range(generations):
        new_individuals = []

        for i in range(population.pop_size):
            # 变异操作
            trial_individual = self._mutation(population)

            # 交叉操作
            parent = population.individuals[i]
            child = self._crossover(parent, trial_individual, population.parameters)

            # 选择操作
            if self._selection(child, parent, self.population.direction):
                new_individuals.append(child)
            else:
                new_individuals.append(parent)

        # 用新的个体更新种群
        population.individuals = new_individuals

        # 在每一代结束时更新统计信息或执行其他必要任务
        best_individual = population.get_best_individual()
        worst_individual = population.get_worst_individual()
        mean_score = population.get_mean_score()
        std_score = population.get_std_score()

        # 打印或存储关于这一代的相关信息
        print(
            f"Generation {gen + 1}: Best Score - {best_individual.score}, Worst Score - {worst_individual.score}, Mean Score - {mean_score}, Std Score - {std_score}")

    # 完成所有代后,可以返回或执行任何最终操作
    final_best_individual = population.get_best_individual()
    print(
        f"Optimization complete. Best individual: {final_best_individual.parameter_values}, Score: {final_best_individual.score}")



population = Population(
    pop_size=50,
    direction=Population.MINIMIZE,
    bounds={"x": (-100, 100), "y": (-100, 100)},
    parameters=["x", "y"]
)
population.initiate()

optimization = Optimization(
    f=0.5,
    cp=0.7,
    population=population
)

optimization.main(20)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321

3. 制限事項

微分進化アルゴリズムは多用途ですが、欠点がないわけではありません。これらの制限の中で最も明らかなのは、多くのメタヒューリスティック アルゴリズムが直面する局所最適性の問題です。 DE アルゴリズムも遺伝的アルゴリズムも、見つけた最適なパラメーターが最適なソリューションであることを保証しません。深い谷を想像してみてください。私たちの住民はそこを越えようとしています。人々は地面を低くしたいと考えています。そのため、全員が谷に入り込み、そこに閉じ込められるかもしれません。一方、どこか別の場所には、より深い谷があります。パラメーターを変更してパラメーターをより多様にし、解空間内でより大きなステップを実行すると効果がありますが、パフォーマンスが犠牲になります。