(Однослойный) персептрон в PyTorch, плохая конвергенция - PullRequest
0 голосов
/ 05 июля 2018

Я пытаюсь разработать простой однослойный персептрон с PyTorch (v0.4.0) для классификации логических операций AND. Я хочу разработать его, используя autograd для расчета градиента весов и смещения, а затем обновлять их в SGD-манере.

Код очень прост и выглядит следующим образом:

# AND points and labels
data = torch.tensor([
    [0, 0],
    [0, 1],
    [1, 0],
    [1, 1]
    ], dtype=torch.float32)
labels = torch.tensor([0,0,0,1], dtype=torch.float32)

weights = torch.zeros(2, dtype=torch.float32, requires_grad=True)
bias = torch.zeros(1, requires_grad=True)
losses = []
epochs = 100
eta = 0.01
for epoch in range(epochs):
    total_loss = 0
    for idx in range(4):
        # take current input
        X = data[idx,:]
        y = labels[idx]

        # compute output and loss
        out = torch.add(torch.dot(weights, X), bias)
        loss = (out-y).pow(2)
        total_loss += loss.item()
        # backpropagation
        loss.backward()

        # compute accuracy and update parameters
        with torch.no_grad():
            weights -= eta * weights.grad
            bias -= eta * bias.grad
            # reset gradient to zero
            weights.grad.zero_()
            bias.grad.zero_()
    losses.append(total_loss)

Модель сходится, как видно из кривой обучения Loss over epochs но полученная плоскость: in orange the plane, the top-right plane has label 1, the others 0

с 50% точностью.

Я пробовал с разными исходными параметрами, а также с использованием оптимизатора SGD от PyTorch, но ничего не изменилось. Я знаю, что MSE - это потеря регрессии, но я не думаю, что проблема в этом.

Есть идеи?

Обновление Плоскость вычисляется с этими двумя строками кода

xr = np.linspace(0, 1, 10)
yr = (-1 / weights[1].item()) * (weights[0].item() * xr  + bias.item())
plt.plot(xr,yr,'-')

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Уравнение, которое вы используете для вычисления плоскости

yr = (-1 / weights[1].item()) * (weights[0].item() * xr  + bias.item())

выводится в случае, когда y_i = [+1, -1] и существует функция знака: она вычисляется путем поиска плоскости, которая разделяет положительные и отрицательные примеры. Это предположение больше не действует, если вы меняете цели.

Если вы нарисуете это:

x1 = np.linspace(0, 1, 10)
x2 = np.linspace(0, 1, 10)
X, Y = np.meshgrid(x1, x2)
w1, w2 = weights.detach().numpy()[0, 0], weights.detach().numpy()[1, 0]
b = bias.detach().numpy()[0]
Z = w1*X + w2*Y + b

которая является правильной плоскостью в 3D, вы получите правильное разделение plane in 3D space separating examples of different classes

Вы можете получить правильное разделение по своей формуле, если вы сместитесь на коэффициент, который зависит от среднего значения меток, например:

yr = (-1 / weights[1].item()) * (weights[0].item() * xr  + bias.item() - 0.5)

но я не могу придти к обоснованию этого формально.

0 голосов
/ 06 июля 2018

Мне удалось решить проблему двумя разными способами:

Метод 1 - Изменить метки на -1 и 1
Путем простого изменения меток с (0, 1) на (-1, 1) плоскость вычисляется правильно.

Следовательно, новые метки (те же данные):

labels = torch.tensor([-1,-1,-1,1], dtype=torch.float32)

Метод 2 - добавить сигмовидную функцию после выхода
С (0, 1) метками добавьте сигмовидную функцию сразу после вычисления следующим образом:

out = torch.add(torch.dot(weights, X), bias)
out = torch.sigmoid(out)

Я думаю, что метод 1 учитывает функцию знака персептрона, поскольку план должен различать точки на основе знака выхода.
Метод 2 адаптирует эти рассуждения для (0,1) меток, используя функцию сжатия.
Это только предварительные, частичные объяснения. Попробуйте прокомментировать ниже более точные.

...