Ricerca nel sito web

Lavorare con diverse rappresentazioni di algoritmi genetici in Python


A seconda della natura del problema da ottimizzare, l'algoritmo genetico (GA) supporta due diverse rappresentazioni dei geni: binaria e decimale. L'AG binario ha solo due valori per i suoi geni, che sono 0 e 1. Questo è più facile da gestire poiché i suoi valori genetici sono limitati rispetto all'GA decimale, per il quale possiamo utilizzare formati diversi come float o intero, e limitato o illimitato. intervalli.

Questo tutorial illustra come la libreria PyGAD supporta le due rappresentazioni GA, binaria e decimale. Lo schema di questo tutorial è il seguente:

  • Iniziare con PyGAD
  • Controllare la gamma del gene nella popolazione iniziale
  • Tipo di gene (int o float)
  • Evitare di superare l'intervallo iniziale
  • Gamme geniche continue e discrete
  • Valori personalizzati per ciascun gene
  • Personalizzare alcuni geni randomizzandone altri
  • L'algoritmo genetico binario
  • Popolazione iniziale definita dall'utente

Introduzione

PyGAD è una libreria Python per implementare l'algoritmo genetico. Per installarlo e iniziare, consulta il tutorial 5 Applicazioni di algoritmi genetici che utilizzano PyGAD. Come suggerisce il nome, ti mostreremo come sviluppare cinque diverse applicazioni utilizzando la libreria.

Nella creazione di un agente di gioco per CoinTex utilizzando l'algoritmo genetico, PyGAD è stato utilizzato per creare un agente che riproduce un gioco chiamato CoinTex.

PyGAD è documentato anche in Read the Docs.

Prima di iniziare questo tutorial, assicurati di avere installato almeno la versione 2.6.0 di PyGad.

pip install pygad>=2.6.0

La sezione successiva illustra come utilizzare PyGAD per personalizzare l'intervallo di valori per i geni.

Controllare la gamma del gene nella popolazione iniziale

Come abbiamo discusso, l'AG ha due rappresentazioni per i suoi geni:

  1. Binario
  2. Decimale

Per il GA binario, ogni gene ha solo due valori: 0 o 1. D'altra parte, la rappresentazione decimale può utilizzare qualsiasi valore decimale per il gene. In questa sezione viene illustrato come limitare l'intervallo dei valori genetici.

In alcuni problemi può essere utile per un utente limitare l'intervallo di valori genetici validi. Ad esempio, supponiamo che ciascun valore di gene debba essere compreso tra 5 e 15 senza eccezioni. Come possiamo farlo?

PyGAD supporta due parametri per gestire questo scenario: init_range_low e init_range_high. Il parametro init_range_low specifica il limite inferiore dell'intervallo da cui viene creata la popolazione iniziale, mentre init_range_high specifica il limite superiore. Tieni presente che init_range_high è esclusivo. Pertanto, se init_range_low=5 e init_range_high=15, i possibili valori del gene vanno da 5 fino a 15 escluso. Questo esempio è mostrato nello snippet di codice sotto.

Oltre a questi argomenti dobbiamo anche specificare la nostra funzione fitness, fitness_func, e il numero di generazioni, num_generazioni.

Sebbene siano facoltativi, i tre parametri successivi devono essere specificati quando la popolazione iniziale viene creata in modo casuale:

  • num_parents_mating: numero di genitori da accoppiare.
  • sol_per_pop: impostato su 3, il che significa che la popolazione ha 3 soluzioni.
  • num_genes: ogni soluzione ha 4 geni.
import pygad

def fitness_function(solution, solution_idx):
    return sum(solution)

ga_instance = pygad.GA(num_generations=1,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=15)

Una volta creata l'istanza, il popolamento iniziale casuale viene preparato nell'attributo initial_population. La riga sottostante lo stampa. Poiché la popolazione ha 3 soluzioni, dove ciascuna soluzione ha 4 geni, la forma della popolazione è (3, 4). Tieni presente che ciascun gene ha un valore compreso tra 5 e 15. Per impostazione predefinita, il tipo dei geni è float.

print(ga_instance.initial_population)
print(ga_instance.initial_population.shape)
[[14.02138539 10.13561641 13.77733116 5]
 [13.28398269 14.13789428 12.6097329   7.51336248]
 [ 9.42208693  6.97035939 14.54414418  6.54276097]]

(3, 4)

Il codice seguente imposta init_range_low su 1 e init_range_high su 3, per vedere come cambia la gamma di geni.

ga_instance = pygad.GA(num_generations=1,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=1,
                       init_range_high=3)

print(ga_instance.initial_population)

Come indicato di seguito, la popolazione creata casualmente ha tutti i geni compresi tra 1 e 3. Nota che è possibile avere un gene con valore 1 ma è impossibile avere un valore 3.

[[1.00631559 2.91140666 1.30055502 2.10605866]
 [2.23160212 2.32108812 1.90731624 1]
 [2.23293791 1.9496456  1.25106388 2.46866602]]

Tieni presente che i parametri init_range_low e init_range_high limitano semplicemente la gamma di geni nella popolazione iniziale. E se le soluzioni si evolvessero in un certo numero di generazioni? Ciò potrebbe far sì che i geni superino l'intervallo iniziale.

Per fare un esperimento, il parametro num_generazioni è impostato su 10 e il metodo run() viene chiamato per far evolvere le soluzioni attraverso le 10 generazioni.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=1,
                       init_range_high=3)

ga_instance.run()

Una volta completato il metodo run(), il codice successivo stampa quanto segue:

  • La popolazione iniziale utilizzando l'attributo initial_population.
  • La popolazione finale utilizzando l'attributo population.
print(ga_instance.initial_population)
print(ga_instance.population)
[[1.08808272 1.16951518 1.30742402 1.40566555]
 [2.88777068 2.49699173 2.47277427 2.36010308]
 [1.94598736 2.10177613 1.57860387 1.45981019]]

[[3.7134492  1.9735615  3.39366783 2.21956642]
 [3.7134492  2.49699173 2.47277427 2.36010308]
 [2.94450144 1.9735615  3.39366783 2.36010308]]

Per la popolazione iniziale, tutti i geni sono compresi tra 1 e 3. Per la popolazione finale, alcuni geni hanno superato l'intervallo come il primo e il terzo gene nella prima soluzione. Come forzare i geni di una popolazione a rientrare nell'intervallo? Questo è discusso nella sezione Evita di superare l'intervallo.

Qualcos'altro da notare è che il tipo del gene è in virgola mobile. Alcuni problemi potrebbero funzionare solo con valori interi. La sezione successiva discute come specificare il tipo di geni utilizzando il parametro gene_type.

Tipo di gene (int o float)

Per impostazione predefinita, PyGAD assegna valori casuali a virgola mobile alla popolazione iniziale. Nel caso in cui l'utente desideri che i valori siano interi, il parametro gene_type è disponibile a questo scopo. È supportato in PyGAD 2.6.0 e versioni successive.

Supporta 2 valori:

  1. float: è il valore predefinito. Ciò significa che i geni sono numeri in virgola mobile.
  2. int: i geni vengono convertiti da numeri a virgola mobile a numeri interi.

Il codice successivo imposta il parametro gene_type su int per forzare la popolazione iniziale casuale ad avere geni interi.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=1,
                       init_range_high=3,

                       gene_type=int)

print(ga_instance.initial_population)

La popolazione iniziale casuale è stampata di seguito. Si noti che l'intervallo del gene va da 1 a 3 esclusivo. Ciò significa che 1 e 2 sono gli unici numeri interi. Pertanto, la popolazione ha solo i valori 1 e 2.

[[1 1 2 1]
 [1 2 2 1]
 [1 2 1 2]]

Quando l'intervallo cambia da 5 a 10, i possibili valori del gene sono 5, 6, 7, 8 e 9.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=10,

                       gene_type=int)

print(ga_instance.initial_population)
[[5 9 7 8]
 [5 7 9 8]
 [5 5 6 7]]

Tieni presente che l'impostazione del parametro gene_type su int o float non impedisce ai geni di superare l'intervallo specificato utilizzando init_range_low e init_range_high. Questo è discusso nella sezione successiva.

Evita di superare l'intervallo iniziale

La popolazione iniziale creata casualmente ha i suoi geni all'interno dell'intervallo specificato dai 2 parametri init_range_low e init_range_high. Ma questo non garantisce che i suoi geni siano sempre all'interno di questo intervallo. Il motivo è che i valori dei geni cambiano a causa dell’operazione di mutazione.

Per impostazione predefinita, l'operazione di mutazione di tipo random viene applicata a tutti i geni. Ciò provoca alcuni cambiamenti casuali nei geni che fanno sì che i loro valori superino l'intervallo iniziale. In base al tipo di problema da risolvere, il superamento dell'intervallo può rappresentare o meno un problema.

Se il problema deve avere i suoi geni all'interno di un intervallo, allora ci sono diverse opzioni per forzare tutti i geni di tutte le generazioni a rientrare nell'intervallo. Queste opzioni sono riassunte come segue:

  1. Non utilizzare la mutazione casuale.
  2. Disabilitare l'operazione di mutazione.
  3. Utilizza il parametro mutation_by_replacement. Questa è l'opzione più pratica.

Discutiamo ciascuna di queste opzioni.

Non utilizzare la mutazione random

Il tipo dell'operazione di mutazione utilizzata viene specificato utilizzando il parametro mutation_type. I tipi supportati sono:

  1. Casuale: mutation_type=random
  2. Scambia: mutation_type=swap
  3. Inversione: mutation_type=inversione
  4. Codifica: mutation_type=codifica

Di questi 4 tipi, solo la mutazione random può modificare i valori del gene al di fuori dell'intervallo. Quindi, un modo per forzare i geni a rientrare nell'intervallo iniziale è utilizzare un altro tipo di mutazione rispetto alla mutazione random.

Il codice successivo utilizza la mutazione swap. Anche dopo l'esecuzione del metodo run(), i valori dei geni sono ancora all'interno dell'intervallo iniziale.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=10,

                       mutation_type="swap",

                       gene_type=int)

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[6 9 8 7]
 [5 5 8 8]
 [9 8 5 6]]

[[8 9 6 9]
 [8 9 6 9]
 [8 9 6 9]]

Questa opzione potrebbe non essere fattibile in molte situazioni perché gli altri tipi mantengono i valori genetici originali cambiando solo il loro ordine. Non ci sono cambiamenti introdotti nei geni.

Disabilita l'operazione di mutazione

PyGAD può disabilitare l'operazione di mutazione impostando il parametro mutation_type su None. Anche se preserva i valori genetici entro l’intervallo iniziale, disabilita una delle opzioni principali per l’evoluzione delle soluzioni.

Il codice successivo disabilita l'operazione di mutazione. Dopo 10 generazioni, i geni rientrano ancora nell'intervallo specificato.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=10,

                       mutation_type=None,

                       gene_type=int)

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[7 9 5 9]
 [5 6 6 8]
 [8 5 6 6]]

[[7 9 5 9]
 [7 9 6 6]
 [7 9 5 6]]

Utilizza il parametro mutation_by_replacement

Le 2 opzioni precedenti scarificano tramite la mutazione random o tramite la mutazione stessa per mantenere il gene nell'intervallo iniziale. L'opzione più fattibile che supporta l'uso della mutazione random pur mantenendo i geni entro l'intervallo specificato è il parametro booleano mutation_by_replacement.

Normalmente, la mutazione casuale genera un valore casuale. Questo valore viene quindi aggiunto al valore del gene corrente. Supponiamo che esista un gene con valore 2,5 e che l'intervallo specificato sia compreso tra 1 e 3, escluso. Se il valore casuale è 0,7, aggiungendolo al valore del gene corrente si ottiene 2,5+0,7=3,2 che è fuori dall'intervallo.

Quando il parametro mutation_by_replacement è True, sostituisce (non aggiunge) il valore del gene con il valore casuale. Quindi, quando il valore casuale è 0,7, il nuovo valore del gene sarà 0,7. Se gene_type è impostato su int, il risultato sarà 1.0.

L'utente può controllare l'intervallo da cui viene generato il valore casuale utilizzando i 2 parametri random_mutation_min_val e random_mutation_max_val che specificano rispettivamente i limiti inferiore e superiore.

Per mantenere i geni entro l'intervallo, ciascuno di questi parametri deve soddisfare la seguente condizione:

init_range_low <= param <= init_range_high

Per un'esperienza ottimale, imposta random_mutation_min_val=init_range_low e  random_mutation_max_val=init_range_high.

Il codice successivo fornisce un esempio dell'utilizzo dei 3 parametri discussi in questa sottosezione (mutation_by_replacement, random_mutation_min_val e random_mutation_max_val).

ga_instance = pygad.GA(num_generations=1000,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=10,

                       random_mutation_min_val=5,
                       random_mutation_max_val=10,

                       mutation_by_replacement=True,

                       gene_type=int)

ga_instance.run()

Per qualsiasi numero di generazioni, i geni non supereranno l'intervallo. Il codice successivo stampa le popolazioni iniziali e finali. I geni nella popolazione finale non superano l'intervallo.

print(ga_instance.initial_population)
print(ga_instance.population)
[[5 8 8 5]
 [9 8 8 9]
 [5 9 8 9]]

[[9 9 9 9]
 [9 9 9 9]
 [9 9 8 9]]

Utilizzando i 3 parametri mutation_by_replacement, è possibile fare in modo che GA funzioni solo con geni binari (cioè geni con valori 0 e 1). Questo avviene nel modo seguente:

  • Imposta init_range_low=random_mutation_min_val=0.
  • Imposta init_range_high=random_mutation_max_val=2.
  • Imposta mutation_by_replacement=True.
  • Imposta gene_type=int.
ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=0,
                       init_range_high=2,

                       random_mutation_min_val=0,
                       random_mutation_max_val=2,

                       mutation_by_replacement=True,

                       gene_type=int)

ga_instance.run()

Ecco le popolazioni iniziali e finali in cui tutti i geni sono 0 o 1.

print(ga_instance.initial_population)
print(ga_instance.population)
[[1 1 0 1]
 [0 1 0 0]
 [0 1 1 1]]

[[0 1 0 1]
 [0 1 0 0]
 [0 1 1 0]]

Tieni presente che questo non è l'unico modo per supportare GA binario. Usando il parametro gene_space, è anche possibile supportare il GA binario. Questo parametro viene introdotto nella sezione successiva.

Gamme geniche continue e discrete

La discussione precedente presuppone che l'intervallo da cui vengono campionati i geni sia continuo. Pertanto, se l'intervallo inizia da 1 a 5, tutti i valori all'interno di questo intervallo (1, 2, 3 e 4) sono accettabili. Cosa succede se alcuni valori all'interno di un intervallo non sono consentiti o i valori non seguono un intervallo continuo (ad esempio -2, 18, 43 e 78)? A tale scopo, PyGAD supporta un parametro denominato gene_space per specificare lo spazio dei valori dei geni.

Il parametro gene_space consente all'utente di elencare tutti i possibili valori dei geni. Accetta un elenco o una tupla in cui sono elencati tutti i possibili valori dei geni.

Il codice successivo utilizza il parametro gene_space per elencare i possibili valori per tutti i geni. Di conseguenza, tutti i geni vengono campionati dai 4 valori elencati.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[-2, 18, 43, 78])

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[78 43 78 -2]
 [18 -2 78 78]
 [43 43 18 -2]]

[[-2 -2 18 78]
 [18 -2 78 78]
 [-2 -2 18 78]]

Si noti che tutti i geni sono campionati dagli stessi valori. In altre parole, i valori nel parametro gene_space sono globali per tutti i geni. E se ogni gene avesse valori distinti? La sezione successiva illustra come specificare valori personalizzati per ciascun gene.

Valori personalizzati per ciascun gene

Quando il parametro gene_space accetta una lista/tupla non nidificata, i valori in questa lista/tupla vengono utilizzati per campionare i valori di tutti i geni. Può succedere che alcuni geni abbiano valori propri distinti. Il parametro gene_space accetta i valori di ciascun gene separatamente. Questo avviene creando una lista/tupla nidificata in cui ogni elemento contiene i possibili valori per il suo gene corrispondente.

Supponiamo che ci siano 4 geni e che ogni gene abbia il proprio spazio di valori. Un elenco dei possibili valori per ciascun gene viene preparato come indicato di seguito. Si noti che nessun gene ha i suoi valori seguendo una sequenza. Ogni gene potrebbe avere un numero diverso di valori.

  1. Gene 1: [-4, 2]
  2. Gene 2: [0, 5, 7, 22, 84]
  3. Gene 3: [-8, -3, 0, 4]
  4. Gene 4: [1, 6, 16, 18]

Tutti e 4 gli elenchi vengono aggiunti come elementi nel parametro gene_space come indicato di seguito.

gene_space = [[-4, 2],
              [0, 5, 7, 22, 84],
              [-8, -3, 0, 4],
              [1, 6, 16, 18] ]

Il codice successivo crea un'istanza della classe pygad.GA che utilizza l'attributo gene_space. La popolazione iniziale e finale stampata mostra come ciascun gene viene campionato dal proprio spazio. Ad esempio, i valori del primo gene per tutte le soluzioni sono -4 e 2.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[[-4, 2],
                                   [0, 5, 7, 22, 84],
                                   [-8, -3, 0, 4],
                                   [1, 6, 16, 18] ])

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[-4. 84.  0. 18.]
 [ 2.  7.  4.  1.]
 [ 2.  0. -8.  6.]]

[[-4.  7.  4.  1.]
 [ 2.  7.  4.  1.]
 [-4.  7.  4.  6.]]

I valori dei 4 geni precedenti non seguivano una sequenza. Può succedere che i valori di alcuni geni seguano una sequenza. I valori dei 4 geni sono elencati di seguito. I valori del primo gene iniziano da 0 a 5 (esclusivo) e i valori del secondo gene iniziano da 16 a 27 (esclusivo). I valori del terzo e del quarto gene sono gli stessi di prima.

  1. Gene 1: [0, 1, 2, 3, 4]
  2. Gene 2: [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
  3. Gene 3: [-8, -3, 0, 4]
  4. Gene 4: [1, 6, 16, 18]

Il nuovo valore del parametro gene_space è riportato di seguito.

gene_space = [ [0, 1, 2, 3, 4],
               [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],
               [-8, -3, 0, 4],
               [1, 6, 16, 18] ]

Cosa succede se un gene ha una sequenza di, ad esempio, 1.000 valori? Dobbiamo elencarne i singoli elementi? Fortunatamente, PyGAD consente di specificare lo spazio di un singolo gene utilizzando la funzione range(). Se lo spazio dei valori del primo gene inizia da 0 fino a 5 escluso, allora può essere modellato utilizzando range(0, 5). Per il secondo gene che inizia da 16 fino a 26 escluso, il suo spazio di valori è rappresentato utilizzando range(16, 27).

Dopo aver utilizzato la funzione range(), il nuovo valore del parametro gene_space viene fornito di seguito.

gene_space = [ range(5), range(16, 27), [-8, -3, 0, 4], [1, 6, 16, 18] ]

Ecco il codice che utilizza il gene_space aggiornato.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[range(5),
                                   range(16, 27),
                                   [-8, -3, 0, 4],
                                   [1, 6, 16, 18] ])

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[ 0. 19. -8. 18.]
 [ 2. 26.  4.  6.]
 [ 3. 18. -3.  1.]]

[[ 3. 25.  0.  6.]
 [ 0. 26.  4.  18.]
 [ 3. 22.  0.  6.]]

È possibile fissare un gene a un singolo valore. Questo avviene assegnando il suo elemento nel parametro gene_space a quel singolo valore. Ecco un esempio in cui il primo gene è impostato su 4 e il terzo gene su 5. Questi 2 geni non cambieranno mai i loro valori.

gene_space = [4,
              range(16, 27),
              5,
              [1, 6, 16, 18] ]

Ecco il codice che utilizza l'ultimo valore gene_space. Nella popolazione iniziale e finale, il primo e il terzo gene non cambiano mai.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[4,
                                   range(16, 27),
                                   5,
                                   [1, 6, 16, 18] ])

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[ 4. 21.  5. 16.]
 [ 4. 18.  5. 16.]
 [ 4. 24.  5. 16.]]

[[ 4. 18.  5.  1.]
 [ 4. 18.  5. 16.]
 [ 4. 18.  5. 18.]]

Personalizza alcuni geni randomizzandone altri

Secondo la discussione precedente del parametro gene_space, ogni gene ha il proprio spazio genetico specificato codificando i singoli valori o utilizzando la funzione range().

In alcuni casi, l'utente potrebbe dover forzare la limitazione di alcuni geni ad alcuni valori, ma altri geni potrebbero essere randomizzati. Ad esempio, se il cromosoma ha un gene che deve essere -1 o 1, gli altri geni possono avere qualsiasi valore casuale. Come farlo?

Affinché il gene venga randomizzato, assegna il suo elemento nel parametro gene_space a None. Ciò significa che il valore di questo gene sarà randomizzato. La riga successiva assegna l'elenco [-1, 1] al primo gene e None ai restanti 3 geni. Gli ultimi 3 geni avranno valori casuali.

gene_space = [[-1, 1], None, None, None]

Il codice successivo utilizza l'ultimo valore gene_space. Nota come il primo gene viene campionato dall'elenco [-1, 1] mentre gli altri geni hanno valori casuali.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[[-1, 1], None, None, None])
ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[ 1.          0.28682682  1.39230915  1.12768838]
 [-1.         -1.05781089  1.71296713  2.56994039]
 [ 1.          3.78611876 -3.80634854  2.15975074]]

[[-1.         -1.05781089  1.88097581  2.56994039]
 [-1.         -1.05781089  1.71296713  2.56994039]
 [-1.         -1.05781089  1.3061504   2.56994039]]

Tieni presente che i geni casuali vengono inizializzati in modo casuale da valori all'interno dell'intervallo specificato dai 2 parametri init_range_low e init_range_high. Se il tipo di mutazione è casuale, allora il valore casuale aggiunto al gene viene campionato dall'intervallo specificato dai 2 parametri random_mutation_min_val e random_mutation_max_val. Inoltre, il tipo del valore casuale è determinato in base al parametro gene_type. Infine, se mutation_by_replacement è impostato su True, il valore casuale non verrà aggiunto ma sostituirà il gene. Tieni presente che questi parametri influenzano solo i geni il cui spazio è impostato su None.

Il codice successivo forza il valore iniziale del gene tra 10 e 20, esclusi. L'intervallo casuale della mutazione va da 30 a 40, esclusivo. Il gene_type è impostato su int.

ga_instance = pygad.GA(num_generations=1000,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=10,
                       init_range_high=20,

                       random_mutation_min_val=30,
                       random_mutation_max_val=40,

                       gene_space=[[-1, 1], None, None, None],

                       gene_type=int)

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[ 1. 16. 14. 10.]
 [-1. 12. 16. 14.]
 [-1. 17. 19. 13.]]

[[-1. 12. 16. 48.]
 [-1. 15. 26. 14.]
 [-1. 12. 16. 14.]]

Algoritmo genetico binario

Nella sezione Utilizza il parametromutation_by_replacement, PyGAD ha supportato l'algoritmo genetico binario utilizzando i seguenti parametri.

  • init_range_low=random_mutation_min_val=0.
  • init_range_high=random_mutation_max_val=2.
  • mutation_by_replacement=Vero.
  • gene_type=int.

È anche possibile supportare il GA binario utilizzando il parametro gene_space. Questo avviene impostando questo parametro sullo spazio globale [0, 1]. Ciò significa che tutti i geni hanno i loro valori 0 o 1.

Il codice successivo imposta il parametro gene_space su [0, 1]. Ciò forza i valori di tutti i geni ad essere 0 o 1.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[0, 1])
ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[1 1 1 0]
 [0 1 0 0]
 [1 1 1 0]]

[[0 1 1 0]
 [0 1 0 1]
 [0 1 0 0]]

Popolazione iniziale definita dall'utente

A volte l'utente potrebbe voler iniziare con una popolazione iniziale personalizzata senza alcuna randomizzazione. PyGAD supporta un parametro denominato initial_population che consente all'utente di specificare un popolamento iniziale personalizzato.

Il codice successivo prepara e assegna un elenco nidificato al parametro initial_population in cui sono presenti 3 soluzioni in cui ciascuna soluzione ha 4 geni. In questo caso i parametri num_genes e sol_per_pop non sono necessari poiché verranno dedotti dal valore assegnato al parametro initial_population.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       fitness_func=fitness_function,

                       initial_population=[[34, 32, 24, -2],
                                           [3, 7, 2, 7],
                                           [-2, -4, -6, 1]])
ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[34 32 24 -2]
 [ 3  7  2  7]
 [-2 -4 -6  1]]

[[3 7 2 6]
 [3 7 2 7]
 [3 7 2 7]]

Conclusione

Questo tutorial ha utilizzato la libreria PyGAD per lavorare sia con le rappresentazioni binarie che decimali dell'algoritmo genetico. Il tutorial ha discusso i diversi parametri in PyGAD per consentire all'utente di controllare il modo in cui viene creata la popolazione iniziale oltre a controllare l'operazione di mutazione.

Utilizzando il parametro gene_type, i valori dei geni possono essere float o numeri interi. Il parametro mutation_by_replacement viene utilizzato per mantenere i geni nel loro intervallo iniziale. Il parametro initial_population accetta un popolamento iniziale definito dall'utente.

Il parametro gene_space aiuta nel caso in cui i valori dei geni non seguano una sequenza. In questo caso, i valori genetici discreti vengono forniti come elenco. Questo parametro accetta uno spazio di valori personalizzato per ciascun gene. Inoltre, consente di campionare alcuni geni da uno spazio definito e di selezionarne altri in modo casuale.

Articoli correlati: