Há apenas uma pequena diferença entre a descida do gradiente e a descida do gradiente estocástico. A descida do gradiente calcula o gradiente com base na função de perda calculada em todas as instâncias de treinamento, enquanto a descida do gradiente estocástico calcula o gradiente com base na perda em lotes. Ambas as técnicas são usadas para encontrar parâmetros ideais para um modelo.
Vamos tentar implementar o SGD neste conjunto de dados 2D.
O algoritmo
O conjunto de dados possui 2 recursos, no entanto, queremos adicionar um termo de viés para anexar uma coluna deles ao final da matriz de dados.
shape = x.shape
x = np.insert(x, 0, 1, axis=1)
Depois inicializamos nossos pesos, existem muitas estratégias para fazer isso. Para simplificar, definirei todos eles como 1, no entanto, definir os pesos iniciais aleatoriamente provavelmente é melhor para poder usar várias reinicializações.
w = np.ones((shape[1]+1,))
Nossa linha inicial é assim
Agora, atualizaremos iterativamente os pesos do modelo se ele classificar um exemplo por engano.
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
Esta linha é a atualização de peso w = w - learning_rate * pred * i
.
Podemos ver que fazer esse processo continuamente levará à convergência.
Após 10 épocas
Após 20 épocas
Após 50 épocas
Após 100 épocas
E finalmente,
O código
O conjunto de dados para este código pode ser encontrado aqui .
A função que treinará os pesos leva na matriz de feixes e os alvosxy. Retorna os pesos treinadosW e uma lista de pesos históricos encontrados ao longo do processo de treinamento.
%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
Aplicaremos esse SGD aos nossos dados em 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)
Vamos traçar o limite de decisão
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()
Para ver o processo de treinamento, você pode imprimir os pesos à medida que eles mudavam nas épocas.
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()