Machine Learning für Softwareentwickler. Paolo Perrotta
Чтение книги онлайн.
Читать онлайн книгу Machine Learning für Softwareentwickler - Paolo Perrotta страница 13
![Machine Learning für Softwareentwickler - Paolo Perrotta Machine Learning für Softwareentwickler - Paolo Perrotta](/cover_pre869973.jpg)
Führen weder w + lr noch w -lr zu einem geringeren Verlust als das aktuelle w, sind wir fertig, denn dann haben wir die Beispiele bestmöglich angenähert. In diesem Fall geben wir w an den Aufrufer zurück.
Anschaulich gesagt, dreht dieser Algorithmus die Gerade in beiden Richtungen und macht sie bei jedem Durchlauf ein kleines bisschen steiler oder flacher und untersucht dabei jeweils den Verlust. Je höher die Lernrate, umso schneller bewegt das System die Gerade. Das können Sie sich so ähnlich vorstellen wie einen Funker der alten Schule, der ganz langsam einen Regler dreht, um den Empfang immer ein kleines bisschen klarer zu machen, bis er schließlich so deutlich ist, wie er nur werden kann.
Iterative Algorithmen können manchmal jedoch in einer Endlosschleife hängenbleiben (man sagt dann, dass sie nicht konvergieren). Informatiker können beweisen, dass dieses Problem bei dem vorliegenden Algorithmus nicht besteht. Nach ausreichend Zeit und Iterationen wird er stets konvergieren. Allerdings kann es sein, dass ihm schon vorher die Iterationsmöglichkeiten ausgehen. In einem solchen Fall gibt train() auf und endet mit einer Ausnahme.
Sie brennen bestimmt schon darauf, den Code auszuführen. Dann also los!
Los geht’s!
Der folgende Code lädt Robertos Beispiele und übergibt sie an train(). Zur klareren Darstellung werden dabei benannte Argumente verwendet (siehe die Erklärung im Abschnitt »Schlüsselwortargumente« auf Seite 334). Nach dem Aufruf von train() werden das ermittelte Gewicht und die Vorhersage für die Anzahl von Pizzas bei 20 Reservierungen ausgegeben.
02_first/linear_regression.py
# Importiert den datensatz
X, Y = np.loadtxt("pizza.txt", skiprows=1, unpack=True)
# Trainiert das System
w = train(X, Y, iterations=10000, lr=0.01)
print("\nw=%.3f" % w)
# Sagt die Anzahl der Pizzas vorher
print("Prediction: x=%d => y=%.2f" % (20, predict(20, w)))
Beim Aufruf von train() müssen wir Werte für iterations und lr bereitstellen. Vorläufig bestimmen wir sie durch Ausprobieren. Ich habe hier 10.000 Iterationen verlangt, was nach einem guten Ausgangswert aussieht. Für lr, also die Stärke der Änderung von w bei jedem Trainingsschritt, habe ich 0,01 angegeben, was genau genug erscheint, um Pizzas zu zählen.
Wenn wir das Programm ausführen, konvergiert train() bereits nach 200 Iterationen:
Iteration 0 => Loss: 812.866667
Iteration 1 => Loss: 804.820547
Iteration 2 => Loss: 796.818187
...
Iteration 184 => Loss: 69.123947
w=1.840
Prediction: x=20 => y=36.80
Der Verlust ist bei jeder Iteration geringer geworden, bis der Algorithmus schließlich nicht mehr versucht hat, ihn noch weiter einzuschränken. An diesem Punkt beträgt das Gewicht 1,84. Das ist die Anzahl der Pizzas, die Roberto für jede Reservierung zu verkaufen erwarten darf. Bei 20 Reservierungen können wir also mit etwa 36,80 Pizzas rechnen. (Roberto verkauft zwar keine Bruchteile von Pizzas, ist aber der genaue Typ, der lieber eine Dezimalstelle zu viel als zu wenig berücksichtigt.)
Mit der Berechnung von w hat unser Code gewissermaßen eine Gerade in das Diagramm eingezeichnet wie im folgenden Graphen. (Den Code für die Diagrammausgabe finden Sie wie immer im Begleitcode zu diesem Buch.)
Das ist schon ein schönes Ergebnis. Wir können es aber noch verbessern.
Bias hinzufügen
Wenn Sie sich das vorstehende Diagramm ansehen, können Sie erkennen, dass unsere Gerade nicht die beste Annäherung an die Beispiele darstellt. Die ideale Gerade würde flacher sein und nicht durch den Ursprung gehen, sondern die Pizza-Achse etwa beim Wert 10 schneiden.
Bis jetzt haben wir dafür gesorgt, dass die Gerade durch den Ursprung verläuft, um unser Modell so einfach wie möglich zu halten. Allerdings sollten wir diese Einschränkung jetzt lieber aufgeben. Um eine Gerade zu zeichnen, die nicht durch den Ursprung geht, müssen wir unserem Modell einen weiteren Parameter hinzufügen:
ŷ = x * w + b
Das kommt Ihnen vielleicht bekannt vor. Es handelt sich hierbei um die klassische lineare Funktion, die Sie im Mathematikunterricht in der Mittelstufe kennengelernt haben, gewöhnlich in der Form y = m * x + t, wobei m als Steigung und t als y-Achsenabschnitt bezeichnet wird. Hier wollen wir jedoch die ML-Terminologie verwenden und w das Gewicht und b den Bias nennen (eigentlich die »systematische Abweichung«).
Anschaulich betrachtet ist der Bias ein Maß dafür, wie weit die Gerade nach oben oder unten verschoben wird (siehe die folgende Abbildung). Die Gerade schneidet die y-Achse beim Wert b. Bei b = 0 ergibt sich wieder unser vorheriger Fall einer Geraden, die durch den Ursprung verläuft.
Unter Verwendung des neuen Modells mit zwei Parametern sieht das komplette Programm zur linearen Regression nun wie folgt aus (wobei die kleinen Pfeile die geänderten Zeilen kennzeichnen):
02_first/linear_regression_with_bias.py
import numpy as np
def predict(X, w, b): <
return X * w + b <
def loss(X, Y, w, b): <
return np.average((predict(X, w, b) - Y) ** 2) <
def train(X, Y, iterations, lr):
w = b = 0 <
for i in range(iterations):
current_loss = loss(X, Y, w, b) <
print("Iteration %4d => Loss: %.6f" % (i, current_loss))
if loss(X, Y, w + lr, b) < current_loss: <
w += lr
elif loss(X, Y, w - lr, b) < current_loss: <
w -= lr
elif loss(X, Y, w, b + lr) < current_loss: <
b += lr <
elif loss(X, Y, w, b - lr) < current_loss: <
b -= lr <