Symbolic Regression Dies ist eine Fortsetzung der Einführung von Python Evolution Calculation Library Deap, die zuletzt veröffentlicht wurde. Dieses Mal werden wir uns die symbolische Regression im Beispiel der genetischen Programmierung (GP) ansehen. Mit GP können Sie den Genotyp des genetischen Algorithmus (GA) mit einer Baumstruktur oder einer Diagrammstruktur behandeln, und die symbolische Regression ist ein typisches Problem von GP. Der spezifische Inhalt ist das Problem der Identifizierung der folgenden quaternären Funktionen im Bereich von $ [-1, 1] $.
f(x)=x^4+x^3+x^2+x
Die Bewertungsfunktion wird durch den Fehler zwischen der geschätzten Gleichung $ prog (x) $ und der wahren Gleichung $ f (x) $ an 20 Punkten zwischen -1 und 1 ausgedrückt.
\sum_{i=1}^{20} |prog(x_i) - f(x_i)|^2
Schauen wir uns den Inhalt des Beispiels an. Importieren Sie zunächst das Modul.
import operator
import math
import random
import numpy
from deap import algorithms
from deap import base
from deap import creator
from deap import tools
from deap import gp
Erstellen Sie als Nächstes eine Reihe von Grundelementen, die jeder Knoten der Baumstruktur sind. Hier sind grundlegende arithmetische Operationen, Dreiecksfunktionen und temporäre Zufallszahlen in der Menge enthalten.
# Define new functions
def safeDiv(left, right):
try:
return left / right
except ZeroDivisionError:
return 0
pset = gp.PrimitiveSet("MAIN", 1)
pset.addPrimitive(operator.add, 2)
pset.addPrimitive(operator.sub, 2)
pset.addPrimitive(operator.mul, 2)
pset.addPrimitive(safeDiv, 2)
pset.addPrimitive(operator.neg, 1)
pset.addPrimitive(math.cos, 1)
pset.addPrimitive(math.sin, 1)
pset.addEphemeralConstant("rand101", lambda: random.randint(-1,1))
pset.renameArguments(ARG0='x')
Um den Fehler aufgrund der Nullteilung zu vermeiden, wird die Teilung neu definiert und dem Grundelement hinzugefügt, und die anderen verwenden die Funktionen des Operatormoduls und des Mathematikmoduls von Python. Das zweite Argument von addPrimitive gibt die Anzahl der Argumente für die primitive Funktion an. addEphemeralConstant wird verwendet, wenn das Ende eines Knotens einen Wert verwendet, der aus einer Funktion wie einer Zufallszahl anstelle einer Konstante generiert wurde. Hier können Sie sehen, dass wir eine Zufallszahl definieren, die eine von -1,0,1 generiert. Ich denke nicht, dass es in dieser Ausgabe sehr viel gewählt wird, aber ... Das zweite Argument des PrimitiveSet gibt die Anzahl der Programmeingaben an. Dieses Mal gibt es nur eine, sie heißt standardmäßig "ARG0", wurde jedoch in renameArguments in "x" umbenannt.
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMin)
Stellen Sie als Nächstes das Minimierungsproblem und den einzelnen Typ mit ** Creator ** wie im vorherigen GA-Beispiel ein.
toolbox = base.Toolbox()
toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=2)
toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.expr)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("compile", gp.compile, pset=pset)
def evalSymbReg(individual, points):
#Konvertierung von Baumdarstellung in Funktion
func = toolbox.compile(expr=individual)
#Berechnung des durchschnittlichen quadratischen Fehlers zwischen der geschätzten Formel und der wahren Formel
sqerrors = ((func(x) - x**4 - x**3 - x**2 - x)**2 for x in points)
return math.fsum(sqerrors) / len(points),
toolbox.register("evaluate", evalSymbReg, points=[x/10. for x in range(-10,10)])
toolbox.register("select", tools.selTournament, tournsize=3)
toolbox.register("mate", gp.cxOnePoint)
toolbox.register("expr_mut", gp.genFull, min_=0, max_=2)
toolbox.register("mutate", gp.mutUniform, expr=toolbox.expr_mut, pset=pset)
Als Nächstes erstellen wir mithilfe der ** Toolbox ** Methoden zur Generierung von Personen und Populationen, Bewertungsfunktionen, Schnittpunkte, Mutationen, Auswahlmethoden usw. gp.genHalfAndHalf ist eine Funktion, die Bäume erstellt, aber min_ und max_ geben die minimale und maximale Tiefe des Baums und genGrow an (Generierung von Bäumen, bei denen die Tiefe jedes Blattknotens unterschiedlich sein kann) Es wurde entwickelt, um genFull (Erzeugung von Bäumen mit der gleichen Tiefe jedes Blattknotens) in zwei Hälften zu machen. gp.compile erstellt eine Funktion, die tatsächlich von einer Person ausgeführt werden kann. Am Ende wird die Mutation durch Hinzufügen eines neuen Teilbaums zum Knoten angegeben.
def main():
random.seed(318)
pop = toolbox.population(n=300)
hof = tools.HallOfFame(1)
stats_fit = tools.Statistics(lambda ind: ind.fitness.values)
stats_size = tools.Statistics(len)
mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size)
mstats.register("avg", numpy.mean)
mstats.register("std", numpy.std)
mstats.register("min", numpy.min)
mstats.register("max", numpy.max)
pop, log = algorithms.eaSimple(pop, toolbox, 0.5, 0.1, 40, stats=mstats,
halloffame=hof, verbose=True)
#Protokoll anzeigen
return pop, log, hof
if __name__ == "__main__":
main()
Erstellen Sie als Nächstes die Hauptfunktion und führen Sie sie aus. Legen Sie zunächst die statistischen Informationen fest, die Sie berechnen möchten, um die statistischen Informationen zu erhalten. Generieren Sie dann die anfängliche Grundgesamtheit und führen Sie die Evolutionsberechnung mit eaSimple durch. Bei der Ausführung werden die statistischen Informationen für jede Generation wie unten gezeigt ausgegeben.
fitness size
--------------------------------------- -------------------------------
gen nevals avg max min std avg max min std
0 300 2.36554 59.2093 0.165572 4.63581 3.69667 7 2 1.61389
1 146 1.07596 10.05 0.165572 0.820853 3.85333 13 1 1.79401
2 169 0.894383 6.3679 0.165572 0.712427 4.2 13 1 2.06398
3 167 0.843668 9.6327 0.165572 0.860971 4.73333 13 1 2.36549
4 158 0.790841 16.4823 0.165572 1.32922 5.02 13 1 2.37338
5 157 0.836829 67.637 0.165572 3.97761 5.59667 13 1 2.20771
6 179 0.475982 3.53043 0.150643 0.505782 6.01 14 1 2.02564
7 176 0.404081 2.54124 0.150643 0.431554 6.42 13 1 2.05352
8 164 0.39734 2.99872 0.150643 0.424689 6.60333 14 3 2.10063
9 161 0.402689 13.5996 0.150643 0.860105 6.61333 13 3 2.05519
10 148 0.392393 2.9829 0.103868 0.445793 6.74333 15 1 2.39669
11 155 0.39596 7.28126 0.0416322 0.578673 7.26333 15 1 2.53784
12 163 0.484725 9.45796 0.00925063 0.733674 8.16 17 1 2.74489
13 155 0.478033 11.0636 0.0158757 0.835315 8.72333 21 1 3.04633
14 184 0.46447 2.65913 0.0158757 0.562739 9.84333 23 1 3.17681
15 161 0.446362 5.74933 0.0158757 0.700987 10.5367 23 2 3.29676
16 187 0.514291 19.6728 0.013838 1.44413 11.8067 25 1 4.25237
17 178 0.357693 3.69339 0.00925063 0.456012 12.7767 29 1 5.3672
18 163 0.377407 14.2468 0.00925063 0.949661 13.4733 35 1 5.67885
19 155 0.280784 9.55288 0.00925063 0.691295 15.2333 36 2 5.7884
20 165 0.247941 2.89093 0.00925063 0.416445 15.63 31 3 5.66508
21 160 0.229175 2.9329 0.00182347 0.406363 16.5033 37 2 6.44593
22 165 0.183025 3.07225 0.00182347 0.333659 16.99 32 2 5.77205
23 156 0.22139 2.49124 0.00182347 0.407663 17.5 42 1 6.7382
24 167 0.168575 1.98247 5.12297e-33 0.272764 17.48 43 3 6.21902
25 166 0.177509 2.3179 5.12297e-33 0.330852 16.9433 36 1 6.6908
26 152 0.187417 2.75742 5.12297e-33 0.382165 17.2267 47 3 6.21734
27 169 0.216263 3.37474 5.12297e-33 0.419851 17.1967 47 3 6.00483
28 176 0.183346 2.75742 5.12297e-33 0.295578 16.7733 42 3 5.85052
29 159 0.167077 2.90958 5.12297e-33 0.333926 16.59 45 2 5.75169
30 171 0.192057 2.75742 5.12297e-33 0.341461 16.2267 53 3 5.48895
31 209 0.279078 3.04517 5.12297e-33 0.455884 15.84 32 3 5.60188
32 161 0.291415 19.9536 5.12297e-33 1.22461 16.0267 35 3 5.45276
33 157 0.16533 2.54124 5.12297e-33 0.316613 15.5033 33 2 5.08232
34 159 0.164494 2.99681 5.12297e-33 0.316094 15.4567 33 1 4.99347
35 157 0.158183 2.9829 5.12297e-33 0.309021 15.3133 34 2 4.54113
36 172 0.203184 3.00665 5.12297e-33 0.380584 15.12 29 1 4.70804
37 181 0.251027 4.66987 5.12297e-33 0.483365 15.18 36 1 5.6587
38 141 0.249193 12.8906 5.12297e-33 0.848809 15.1633 36 1 5.38114
39 158 0.188209 2.33071 5.12297e-33 0.346345 15.1967 32 1 4.82887
40 165 0.220244 2.3179 5.12297e-33 0.381864 15.44 33 2 5.70787
expr = toolbox.individual()
nodes, edges, labels = gp.graph(expr)
import matplotlib.pyplot as plt
import networkx as nx
g = nx.Graph()
g.add_nodes_from(nodes)
g.add_edges_from(edges)
pos = nx.graphviz_layout(g, prog="dot")
nx.draw_networkx_nodes(g, pos)
nx.draw_networkx_edges(g, pos)
nx.draw_networkx_labels(g, pos, labels)
plt.show()
Lassen Sie uns abschließend versuchen, den endgültig optimierten Baum zu zeichnen. Networkx und matplotlib werden zum Zeichnen verwendet. Unten sehen Sie die Struktur des Baums, die durch diese Optimierung erhalten wurde.
Lassen Sie uns ein Diagramm erstellen, um zu sehen, ob eine Funktion, die der tatsächlichen nahe kommt, tatsächlich erhalten wird. Rot ist die wahre Funktion und Grün ist das Schätzergebnis, aber es ist nicht gut. Es kann durch Hinzufügen anderer Baumtiefen und Grundelemente verbessert werden, aber lassen wir es vorerst hier.
Beispiel für eine symbolische Regression: http://deap.gel.ulaval.ca/doc/dev/examples/gp_symbreg.html
Recommended Posts