Triển khai Stochastic Gradient Descent trong Python


7

Tôi đang cố gắng thực hiện thuật toán Stochastic Gradient Descent cơ bản cho hồi quy tuyến tính 2 chiều trong python. Tôi đã được cấp một số mã soạn sẵn cho vanilla GD và tôi đã cố gắng chuyển đổi nó để làm việc cho SGD.

Cụ thể - Tôi hơi không chắc chắn nếu tôi thực hiện đúng chức năng mất và các đạo hàm riêng, vì tôi mới nói chung về hồi quy.

Tôi thấy rằng các lỗi có xu hướng "zig zag" như mong đợi. Điều sau đây có giống như một triển khai chính xác hay tôi đã phạm sai lầm nào?

#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))

Câu trả lời:


4

Chỉ có một sự khác biệt nhỏ giữa độ dốc gốc và độ dốc dốc ngẫu nhiên. Độ dốc độ dốc tính toán độ dốc dựa trên hàm mất mát được tính trên tất cả các trường hợp đào tạo, trong khi độ dốc độ dốc ngẫu nhiên tính toán độ dốc dựa trên tổn thất theo lô. Cả hai kỹ thuật này được sử dụng để tìm các tham số tối ưu cho một mô hình.

Hãy để chúng tôi thử triển khai SGD trên bộ dữ liệu 2D này.

nhập mô tả hình ảnh ở đây

Thuật toán

Bộ dữ liệu có 2 tính năng, tuy nhiên chúng tôi sẽ muốn thêm một thuật ngữ thiên vị để chúng tôi nối một cột của các tính năng vào cuối ma trận dữ liệu.

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

Sau đó, chúng tôi khởi tạo trọng lượng của chúng tôi, có nhiều chiến lược để làm điều này. Để đơn giản, tôi sẽ đặt tất cả chúng thành 1 tuy nhiên việc đặt ngẫu nhiên các trọng số ban đầu có lẽ tốt hơn để có thể sử dụng nhiều lần khởi động lại.

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

Dòng ban đầu của chúng tôi trông như thế này

nhập mô tả hình ảnh ở đây

Bây giờ chúng tôi sẽ lặp lại cập nhật các trọng số của mô hình nếu nó phân loại nhầm một ví dụ.

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

Dòng này là cập nhật trọng lượng w = w - learning_rate * pred * i.

Chúng ta có thể thấy rằng làm quá trình này liên tục sẽ dẫn đến sự hội tụ.

Sau 10 kỷ nguyên

nhập mô tả hình ảnh ở đây

Sau 20 kỷ nguyên

nhập mô tả hình ảnh ở đây

Sau 50 kỷ nguyên

nhập mô tả hình ảnh ở đây

Sau 100 kỷ nguyên

nhập mô tả hình ảnh ở đây

Và cuối cùng,

nhập mô tả hình ảnh ở đây


Mật mã

Bộ dữ liệu cho mã này có thể được tìm thấy ở đây .

Hàm sẽ huấn luyện các trọng số trong ma trận tính năng x và các mục tiêu y. Nó trả về trọng lượng được đào tạow và một danh sách các trọng lượng lịch sử gặp phải trong suốt quá trình đào tạo.

%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

Chúng tôi sẽ áp dụng SGD này cho dữ liệu của chúng tôi trong perceptron.csv .

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)

Hãy vẽ ranh giới quyết định

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()

Để xem quy trình đào tạo, bạn có thể in các trọng số khi chúng thay đổi thông qua các kỷ nguyên.

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()

Sử dụng tùy chọn verbose nếu bạn muốn đọc toàn bộ để xem thuật toán đang làm gì trong các lần lặp.
JahKnows
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.