Plotting Important Data: Visualizing the CMA Strategy

With this example we show one technique for plotting the data of an evolution. As developers of DEAP we cannot make a choice on what data is important to plot and this part is left to the user. Although, plotting would all occur the same way. First the data is gathered during the evolution and at the end the figures are created from the data. This model is the simplest possible. One could also write all data to a file and read those file again to plot the figures. This later model would be more fault tolerant as if the evolution does not terminate normally, the figures could still be plotted. But, we want to keep this example as simple as possible and thus we will present the former model.

Evolution Loop

The beginning of this example is exactly the same as the CMA-ES example. The general evolution loop of function eaGenerateUpdate() is somewhat insufficient for our purpose. We need to gather the required data on each generation. So instead of using the eaGenerateUpdate() function, we’ll develop it to get a grip on what is recorded. First, we’ll create objects to record our data. Here we want to plot, in addition to what the Logbook and HallOfFame objects contain, the step size, the axis ratio and the major axis of the covariace matrix, the best value so far, the best coordinates so far and the standard deviation of the all coordinates at each generation.

    sigma = numpy.ndarray((NGEN,1))
    axis_ratio = numpy.ndarray((NGEN,1))
    diagD = numpy.ndarray((NGEN,N))
    fbest = numpy.ndarray((NGEN,1))
    best = numpy.ndarray((NGEN,N))
    std = numpy.ndarray((NGEN,N))

Once the objects are created, the evolution loop, based on a generational stopping criterion, calls repeatedly the generate(), evaluate() and update() methods registered in the toolbox.

    for gen in range(NGEN):
        # Generate a new population
        population = toolbox.generate()
        # Evaluate the individuals
        fitnesses = toolbox.map(toolbox.evaluate, population)
        for ind, fit in zip(population, fitnesses):
            ind.fitness.values = fit
        
        # Update the strategy with the evaluated individuals
        toolbox.update(population)

Then, the previoulsy created objects start to play their role. The data is recorded in each object on each generation.

        sigma[gen] = strategy.sigma
        axis_ratio[gen] = max(strategy.diagD)**2/min(strategy.diagD)**2
        diagD[gen, :N] = strategy.diagD**2
        fbest[gen] = halloffame[0].fitness.values
        best[gen, :N] = halloffame[0]
        std[gen, :N] = numpy.std(population, axis=0)

Now that the data is recorded the only thing left to do is to plot it. We’ll use matplotlib to generate the graphics from the recorded data.

    # The x-axis will be the number of evaluations
    x = list(range(0, strategy.lambda_ * NGEN, strategy.lambda_))
    avg, max_, min_ = logbook.select("avg", "max", "min")
    plt.figure()
    plt.subplot(2, 2, 1)
    plt.semilogy(x, avg, "--b")
    plt.semilogy(x, max_, "--b")
    plt.semilogy(x, min_, "-b")
    plt.semilogy(x, fbest, "-c")
    plt.semilogy(x, sigma, "-g")
    plt.semilogy(x, axis_ratio, "-r")
    plt.grid(True)
    plt.title("blue: f-values, green: sigma, red: axis ratio")

    plt.subplot(2, 2, 2)
    plt.plot(x, best)
    plt.grid(True)
    plt.title("Object Variables")

    plt.subplot(2, 2, 3)
    plt.semilogy(x, diagD)
    plt.grid(True)
    plt.title("Scaling (All Main Axes)")

    plt.subplot(2, 2, 4)
    plt.semilogy(x, std)
    plt.grid(True)
    plt.title("Standard Deviations in All Coordinates")
    
    plt.show()

Which gives the following result.

(Source code)

(png, hires.png, pdf)

../_images/cma_plotting_01_00.png

The complete example : examples/es/cma_plotting.