Search code examples
pythonartificial-intelligence

Why is my configuration for NEAT not functioning


I'm trying to use NEAT to AI generate songs using music theory. I have the base code laid out here and I am currently planning to expand some of the features. My code is shown below, along with my config file: However, whenever I try to use my config file, it either returns a runtime error saying Missing configuration item: num_inputs, or if I try to use the default genome config, it returns Attribute Error: module 'neat' has no attribute 'DefaultGenomeConfig'. Any ideas would be amazing. Thanks so much in advance

import neat
import random
import numpy as np
import os
from midiutil import MIDIFile

config_path = "config.txt"
num_generations = 10
num_measures = 16
beats_per_measure = 4
num_tracks = 1
beat_duration = 0.25
output_file = "generated_music.mid"

def evaluate_genome(genome, config):
    net = neat.nn.FeedForwardNetwork.create(genome, config)
    melody = []
    for _ in range(num_measures * beats_per_measure):
        inputs = [random.random()]
        output = net.activate(inputs)
        note = int(output[0] * 127)
        melody.append(note)
    fitness = calculate_fitness(melody)
    return fitness

def calculate_fitness(melody):
    melodic_fitness = calculate_melodic_fitness(melody)
    harmonic_fitness = calculate_harmonic_fitness(melody)
    rhythmic_fitness = calculate_rhythmic_fitness(melody)
    overall_fitness = melodic_fitness + harmonic_fitness + rhythmic_fitness
    return overall_fitness

def calculate_melodic_fitness(melody):
    melodic_fitness = 0.0  # Placeholder value
    return melodic_fitness

def calculate_harmonic_fitness(melody):
    harmonic_fitness = 0.0
    return harmonic_fitness

def calculate_rhythmic_fitness(melody):
    rhythmic_fitness = 0.0  # Placeholder value
    return rhythmic_fitness

def run_neat():
    local_dir = os.path.dirname(__file__)
    config_path = os.path.join(local_dir, "config.txt")
    config = neat.Config(
        neat.DefaultGenome,
        neat.DefaultReproduction,
        neat.DefaultSpeciesSet,
        neat.DefaultStagnation,
        neat.DefaultGenomeConfig,
        config_path,
    )
    population = neat.Population(config)
    reporter = neat.StdOutReporter(True)
    population.add_reporter(reporter)

    winner = population.run(evaluate_genome, num_generations)

    best_genome = winner
    best_net = neat.nn.FeedForwardNetwork.create(best_genome, config)

    melody = []
    for _ in range(num_measures * beats_per_measure):
        inputs = [random.random()]
        output = best_net.activate(inputs)
        note = int(output[0] * 127)
        melody.append(note)

    midi_file = MIDIFile(num_tracks)
    track = 0
    time = 0
    for note in melody:
        midi_file.addNote(track, 0, note, time, beat_duration, 100)
        time += beat_duration

    with open(output_file, "wb") as file:
        midi_file.writeFile(file)

    print("Generated music saved as", output_file)

run_neat()
fitness_criterion = max
fitness_threshold = 1000
pop_size = 100
reset_on_extinction = False

My configuration file:

[DefaultGenome]
# Node activation options: sigmoid, tanh, relu, softmax, identity
activation_default = sigmoid
activation_mutate_rate = 0.1
activation_options = sigmoid

# Node aggregation options: sum, product, min, max, mean
aggregation_default = sum
aggregation_mutate_rate = 0.0
aggregation_options = sum

# Node bias options: enabled, uniform, gauss
bias_init_type = gauss
bias_init_mean = 0.0
bias_init_stdev = 1.0
bias_max_value = 30.0
bias_min_value = -30.0
bias_mutate_power = 0.5
bias_mutate_rate = 0.7
bias_replace_rate = 0.1

# Connection weight options: enabled, uniform, gauss, adaptive, xavier
weight_init_type = gauss
weight_init_mean = 0.0
weight_init_stdev = 1.0
weight_max_value = 30.0
weight_min_value = -30.0
weight_mutate_power = 0.5
weight_mutate_rate = 0.8
weight_replace_rate = 0.1

[DefaultReproduction]
# Probability of asexual reproduction (without crossover)
asexual_reproduction_probability = 0.25

[DefaultSpeciesSet]
compatibility_threshold = 3.0

[DefaultStagnation]
species_fitness_func = max
max_stagnation = 20
species_elitism = 2

[DefaultGenomeConfig]
num_inputs = 1
num_outputs = 1

Solution

  • Move network configuration (num_inputs, num_outputs) to the [DefaultGenome] section (not DefaultGenomeConig).

    For example:

    [NEAT]
    fitness_criterion     = max
    fitness_threshold     = 3.9
    
    [DefaultGenome]
    # network parameters
    num_hidden              = 0
    num_inputs              = 2
    num_outputs             = 1
    

    Python-NEAT needs to find those parameters in the right section. DefaultGenomeConfig is the name of the configuration class that coresponds to the DefaultGenome section. I believe this is the source of confusion.

    Thre's a decent documentation: https://neat-python.readthedocs.io/en/latest/config_file.html