You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

DEAP遗传算法参数越界与类型异常问题求助

Fixing DEAP Mixed Integer-Float Parameter Bounds & Type Issues

Hey there! I see you're new to DEAP and ran into two key issues with your optimization script: integer parameters turning into floats after mutation, and parameters drifting outside your defined MIN/MAX bounds. Let's fix these problems with targeted changes to your code.

The Root Causes

  • Type Loss: Using mutGaussian on the entire individual affects your integer parameter—Gaussian mutation adds floating-point values, turning your int into a float.
  • Boundary Drift: DEAP's default operators don't enforce bounds post-initialization, so crossover/mutation can push parameters outside your MIN_X/MAX_X ranges.

Step-by-Step Fixes

1. Custom Mutation Function

We need to handle integer and float parameters separately to preserve types and bounds. Create a custom mutation function that uses integer-specific mutation for your int parameter, and constrained Gaussian mutation for your float parameter:

def custom_mutate(individual):
    # Mutate integer parameter (first element) within its bounds
    tools.mutUniformInt(individual, low=MIN_2, up=MAX_2, indpb=0.2)
    # Mutate float parameter (second element)
    tools.mutGaussian(individual, mu=0, sigma=1, indpb=0.2)
    
    # Enforce integer type and bounds for the first parameter
    individual[0] = max(MIN_2, min(MAX_2, int(round(individual[0]))))
    # Enforce bounds for the float parameter
    individual[1] = max(MIN_1, min(MAX_1, individual[1]))
    
    return individual,

2. Update Toolbox Registration

Replace your default mutation registration with our custom function:

# Replace this line:
# toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.2)
# With this:
toolbox.register("mutate", custom_mutate)

3. (Optional) Add Post-Crossover Bound Check

While cxTwoPoint won't change types here (since it swaps like-positioned elements), you can add a quick check to ensure bounds are maintained after crossover if needed. Add this inside your crossover loop:

if random.random() < CXPB:
    toolbox.mate(child1, child2)
    # Post-crossover bound enforcement
    child1[0] = max(MIN_2, min(MAX_2, child1[0]))
    child1[1] = max(MIN_1, min(MAX_1, child1[1]))
    child2[0] = max(MIN_2, min(MAX_2, child2[0]))
    child2[1] = max(MIN_1, min(MAX_1, child2[1]))
    del child1.fitness.values
    del child2.fitness.values

Full Modified Script

Here's your complete script with all fixes applied:

import random
from deap import base
from deap import creator
from deap import tools

CXPB, MUTPB = 0.2, 0.2
IND_SIZE = 1
POP_SIZE = 10
GEN_SIZE = 50
MIN_1, MAX_1 = 7.5, 8.5
MIN_2, MAX_2 = 20, 60

creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0))
creator.create("Individual", list, fitness=creator.FitnessMin)

toolbox = base.Toolbox()
toolbox.register("attr_float", random.uniform, MIN_1, MAX_1)
toolbox.register("attr_int" , random.randint, MIN_2, MAX_2)
toolbox.register("individual", tools.initCycle, creator.Individual,(toolbox.attr_int,toolbox.attr_float), IND_SIZE)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

toolbox.register("mate", tools.cxTwoPoint)

# Custom mutation function to handle mixed types and bounds
def custom_mutate(individual):
    tools.mutUniformInt(individual, low=MIN_2, up=MAX_2, indpb=0.2)
    tools.mutGaussian(individual, mu=0, sigma=1, indpb=0.2)
    
    # Enforce integer type and bounds
    individual[0] = max(MIN_2, min(MAX_2, int(round(individual[0]))))
    # Enforce float bounds
    individual[1] = max(MIN_1, min(MAX_1, individual[1]))
    
    return individual,

toolbox.register("mutate", custom_mutate)
toolbox.register("select", tools.selTournament, tournsize=3)

def evaluate(individual):
    print ('evaluate:',individual)
    a = sum(individual)
    b = len(individual)
    return a, 1. / b

toolbox.register("evaluate", evaluate)

pop = toolbox.population(n=POP_SIZE)

for g in range(GEN_SIZE):
    print ('generation',g)
    # Select the next generation individuals
    offspring = toolbox.select(pop, len(pop))
    # Clone the selected individuals (convert map to list for Python 3 compatibility)
    offspring = list(map(toolbox.clone, offspring))
    # Apply crossover on the offspring
    for child1, child2 in zip(offspring[::2], offspring[1::2]):
        if random.random() < CXPB:
            toolbox.mate(child1, child2)
            # Optional post-crossover bound check
            child1[0] = max(MIN_2, min(MAX_2, child1[0]))
            child1[1] = max(MIN_1, min(MAX_1, child1[1]))
            child2[0] = max(MIN_2, min(MAX_2, child2[0]))
            child2[1] = max(MIN_1, min(MAX_1, child2[1]))
            del child1.fitness.values
            del child2.fitness.values
    # Apply mutation on the offspring
    for mutant in offspring:
        if random.random() < MUTPB:
            toolbox.mutate(mutant)
            del mutant.fitness.values
    # Evaluate the individuals with an invalid fitness
    invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
    fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
    for ind, fit in zip(invalid_ind, fitnesses):
        ind.fitness.values = fit
    # The population is entirely replaced by the offspring
    pop[:] = offspring

Key Notes

  • Integer Preservation: Using mutUniformInt for the integer parameter ensures it stays within bounds and maintains integer type. We also add a round and int conversion as a safety net.
  • Boundary Enforcement: The max(MIN, min(MAX, value)) pattern clamps parameters to your defined ranges—no more values outside 7.5-8.5 or 20-60.
  • Python 3 Compatibility: Converted the map result to a list to avoid iteration issues in Python 3.

内容的提问来源于stack exchange,提问作者user3884301

火山引擎 最新活动