(Single Layer) Perceptron in PyTorch, bad convergence
I'm trying to develop a simple single layer perceptron with PyTorch (v0.4.0) to classify AND boolean operation.
I want to develop it by using autograd to calculate gradient of weights and bias and then update them in a SGD manner.

The code is very simple and is the following:

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


The model converges, as you can see from the learning curve 

but the resulting plane is:


with 50% of accuracy.

I tried with different inital parameters and also by using the SGD optimizer from PyTorch but nothing changed.
I know that MSE is a regression loss but I don't think the problem is there.

Any ideas?

Update
The plane is computed with these 2 lines of code

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


The equation you use to compute the plane

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


is derived in the case where y_i = [+1, -1] and there is a sign function: it's computed by looking for the plane that separates positive and negative examples. This assumption is not valid anymore if you change targets. 

If you draw this:

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


which is the correct plane in 3D, you get the correct separation

You can get a correct separation with your formula if you offset by a factor that depends on the average of the labels, like:

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


but I can't come around at justifying it formally.
