Implementierung des stochastischen Gradientenabfalls in Python

7

Ich versuche, einen grundlegenden stochastischen Gradientenabstiegsalgorithmus für eine lineare 2D-Regression in Python zu implementieren. Ich habe einen Boilerplate-Code für Vanilla GD erhalten und versucht, ihn für SGD zu konvertieren.

Insbesondere - ich bin mir ein wenig unsicher, ob ich die Verlustfunktion und partielle Ableitungen korrekt implementiert habe, da ich mit Regressionen im Allgemeinen noch nicht vertraut bin.

Ich sehe, dass die Fehler wie erwartet zu "Zick-Zack" neigen. Sieht das Folgende wie eine korrekte Implementierung aus oder habe ich Fehler gemacht?

#sample data
data  = [(1,1),(2,3),(4,3),(3,2),(5,5)]


def compute_error_for_line_given_points(b, m, points):
    totalError = 0
    x = points[0]
    y = points[1]
    return float(totalError + (y - (m * x + b)) ** 2)

def step_gradient(b_current, m_current, points, learningRate):
    N = float(1)
    for i in range(0, 1):
        x = points[0]
        y = points[1]
        b_gradient = -(2/N) * (y - ((m_current * x) + b_current)) #this is the part I am unsure
        m_gradient = -(2/N) * x * (y - ((m_current * x) + b_current)) #here as well
    new_b = b_current - (learningRate * b_gradient)
    new_m = m_current - (learningRate * m_gradient)
    return [new_b, new_m]

err_log = []
coef_log = []
b = 0 #initial intercept
m = 0 #initial slope

iterations = 4
for i in range(iterations): #epochs
    for point in data: #one point at a time for SGD
        err = compute_error_for_line_given_points(b,m, point)
        err_log.append(err)
        b,m = step_gradient(b,m,point,.01)
        coef_log.append((b,m))
Foobarbaz
quelle

Antworten:

4

Es gibt nur einen kleinen Unterschied zwischen Gradientenabstieg und stochastischem Gradientenabstieg. Der Gradientenabstieg berechnet den Gradienten basierend auf der Verlustfunktion, die über alle Trainingsinstanzen berechnet wurde, während der stochastische Gradientenabstieg den Gradienten basierend auf dem Verlust in Chargen berechnet. Beide Techniken werden verwendet, um optimale Parameter für ein Modell zu finden.

Versuchen wir, SGD in diesem 2D-Datensatz zu implementieren.

Geben Sie hier die Bildbeschreibung ein

Der Algorithmus

Der Datensatz verfügt über zwei Funktionen. Wir möchten jedoch einen Bias-Term hinzufügen, damit wir eine Spalte mit Einsen an das Ende der Datenmatrix anhängen.

shape = x.shape 
x = np.insert(x, 0, 1, axis=1)

Dann initialisieren wir unsere Gewichte, es gibt viele Strategien, um dies zu tun. Der Einfachheit halber werde ich sie alle auf 1 setzen, aber das zufällige Einstellen der Anfangsgewichte ist wahrscheinlich besser, um mehrere Neustarts verwenden zu können.

w = np.ones((shape[1]+1,))

Unsere erste Zeile sieht so aus

Geben Sie hier die Bildbeschreibung ein

Jetzt werden wir die Gewichte des Modells iterativ aktualisieren, wenn es fälschlicherweise ein Beispiel klassifiziert.

for ix, i in enumerate(x):
   pred = np.dot(i,w)
   if pred > 0: pred = 1
   elif pred < 0: pred = -1
   if pred != y[ix]:
      w = w - learning_rate * pred * i

Diese Linie ist das Gewichtsupdate w = w - learning_rate * pred * i.

Wir können sehen, dass ein kontinuierlicher Prozess zu Konvergenz führen wird.

Nach 10 Epochen

Geben Sie hier die Bildbeschreibung ein

Nach 20 Epochen

Geben Sie hier die Bildbeschreibung ein

Nach 50 Epochen

Geben Sie hier die Bildbeschreibung ein

Nach 100 Epochen

Geben Sie hier die Bildbeschreibung ein

Und schlussendlich,

Geben Sie hier die Bildbeschreibung ein


Der Code

Den Datensatz für diesen Code finden Sie hier .

Die Funktion, mit der die Gewichte trainiert werden, wird in der Merkmalsmatrix übernommen x und die Ziele y. Es gibt die trainierten Gewichte zurückw und eine Liste der historischen Gewichte, die während des Trainingsprozesses angetroffen wurden.

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

def get_weights(x, y, verbose = 0):
    shape = x.shape
    x = np.insert(x, 0, 1, axis=1)
    w = np.ones((shape[1]+1,))
    weights = []

    learning_rate = 10
    iteration = 0
    loss = None
    while iteration <= 1000 and loss != 0:
        for ix, i in enumerate(x):
            pred = np.dot(i,w)
            if pred > 0: pred = 1
            elif pred < 0: pred = -1
            if pred != y[ix]:
                w = w - learning_rate * pred * i
            weights.append(w)    
            if verbose == 1:
                print('X_i = ', i, '    y = ', y[ix])
                print('Pred: ', pred )
                print('Weights', w)
                print('------------------------------------------')


        loss = np.dot(x, w)
        loss[loss<0] = -1
        loss[loss>0] = 1
        loss = np.sum(loss - y )

        if verbose == 1:
            print('------------------------------------------')
            print(np.sum(loss - y ))
            print('------------------------------------------')
        if iteration%10 == 0: learning_rate = learning_rate / 2
        iteration += 1    
    print('Weights: ', w)
    print('Loss: ', loss)
    return w, weights

Wir werden diese SGD auf unsere Daten in perpptron.csv anwenden .

df = np.loadtxt("perceptron.csv", delimiter = ',')
x = df[:,0:-1]
y = df[:,-1]

print('Dataset')
print(df, '\n')

w, all_weights = get_weights(x, y)
x = np.insert(x, 0, 1, axis=1)

pred = np.dot(x, w)
pred[pred > 0] =  1
pred[pred < 0] = -1
print('Predictions', pred)

Zeichnen wir die Entscheidungsgrenze

x1 = np.linspace(np.amin(x[:,1]),np.amax(x[:,2]),2)
x2 = np.zeros((2,))
for ix, i in enumerate(x1):
    x2[ix] = (-w[0] - w[1]*i) / w[2]

plt.scatter(x[y>0][:,1], x[y>0][:,2], marker = 'x')
plt.scatter(x[y<0][:,1], x[y<0][:,2], marker = 'o')
plt.plot(x1,x2)
plt.title('Perceptron Seperator', fontsize=20)
plt.xlabel('Feature 1 ($x_1$)', fontsize=16)
plt.ylabel('Feature 2 ($x_2$)', fontsize=16)
plt.show()

Um den Trainingsprozess zu sehen, können Sie die Gewichte drucken, während sie sich im Laufe der Epochen geändert haben.

for ix, w in enumerate(all_weights):
    if ix % 10 == 0:
        print('Weights:', w)
        x1 = np.linspace(np.amin(x[:,1]),np.amax(x[:,2]),2)
        x2 = np.zeros((2,))
        for ix, i in enumerate(x1):
            x2[ix] = (-w[0] - w[1]*i) / w[2]
        print('$0 = ' + str(-w[0]) + ' - ' + str(w[1]) + 'x_1'+ ' - ' + str(w[2]) + 'x_2$')

        plt.scatter(x[y>0][:,1], x[y>0][:,2], marker = 'x')
        plt.scatter(x[y<0][:,1], x[y<0][:,2], marker = 'o')
        plt.plot(x1,x2)
        plt.title('Perceptron Seperator', fontsize=20)
        plt.xlabel('Feature 1 ($x_1$)', fontsize=16)
        plt.ylabel('Feature 2 ($x_2$)', fontsize=16)
        plt.show()
JahKnows
quelle
Verwenden Sie die ausführliche Option, wenn Sie eine vollständige Anzeige wünschen, um zu sehen, was der Algorithmus während der Iterationen tut.
JahKnows