A simple nonlinear dataset: XOR

[1]:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

cmap = plt.get_cmap('tab10')
colors = [cmap(i) for i in range(cmap.N)]

mpl.rcParams["font.size"] = 24
mpl.rcParams["lines.linewidth"] = 2

XOR Dataset

[2]:
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
Y = np.array([0, 1, 1, 0])
[3]:
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
plt.scatter(X[:, 0], X[:, 1], c=[colors[Y[i]] for i in range(len(Y))], s=500)
plt.xlim([-0.2, 1.2])
plt.ylim([-0.2, 1.2])
fig.tight_layout()
../../_images/examples_nn_Reference_ch5_Part_2_xor_4_0.png

PyTorch implementation

[4]:
import torch
class LinearRegresion(torch.nn.Module):
    def __init__(self, indim):
        super(LinearRegresion, self).__init__()
        self.linear = torch.nn.Linear(indim, 1)
    def forward(self, x):
        x = self.linear(x)
        return x

Logistic Regression

[5]:
x = np.linspace(-10, 10, 1000)
y = 1/(1+np.exp(-x))

fig, ax = plt.subplots(1, 1, figsize=(5, 5))
plt.plot(x, y, "k-", linewidth=3)
[5]:
[<matplotlib.lines.Line2D at 0x7dda427cf550>]
../../_images/examples_nn_Reference_ch5_Part_2_xor_8_1.png
[6]:
model = LinearRegresion(2)
criterion = torch.nn.BCELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

train_loss = []
data = torch.from_numpy(X).float()
y_true = torch.from_numpy(Y).float()

for epoch in range(500):
    optimizer.zero_grad()

    output = model(data)
    y_pred = torch.sigmoid(output)

    loss = criterion(y_pred, y_true.reshape(y_pred.shape))

    train_loss.append(loss.item())
    loss.backward()
    optimizer.step()
[7]:
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
plt.plot(train_loss)
fig.tight_layout()
../../_images/examples_nn_Reference_ch5_Part_2_xor_10_0.png
[8]:
x1 = np.linspace(0, 1, 100)
x2 = np.linspace(0, 1, 100)
xx1, xx2 = np.meshgrid(x1, x2)
x_grid = np.hstack([xx1.ravel().reshape(-1, 1), xx2.ravel().reshape(-1, 1)])
x_grid = torch.from_numpy(x_grid).float()
output = model(x_grid)
y_grid_pred = torch.sigmoid(output)
y_grid_pred = y_grid_pred.reshape(xx1.shape)


# plot space separation
fig, ax = plt.subplots(1, 1, figsize=(5, 5))

custom_cmap = ListedColormap([cmap(0), cmap(1)])
plt.pcolormesh(xx1, xx2, y_grid_pred>=0.5, cmap=custom_cmap, alpha=0.5)

plt.scatter(X[:, 0], X[:, 1], c=[colors[Y[i]] for i in range(len(Y))], s=500)
plt.xlim([-0.2, 1.2])
plt.ylim([-0.2, 1.2])
fig.tight_layout()
../../_images/examples_nn_Reference_ch5_Part_2_xor_11_0.png

Multiple layer Perceptron

[9]:
import torch
import torch.nn as nn
import torch.nn.functional as F

class MLPRegresion(torch.nn.Module):
    def __init__(self, indim, hidden_dims):
        assert isinstance(hidden_dims, list), f"Hidden dimensions {hidden_dims} should be a list"
        super(MLPRegresion, self).__init__()
        hidden_dims = [indim] + hidden_dims
        layers = []
        for i in range(len(hidden_dims)-1):
            layers.append(torch.nn.Linear(hidden_dims[i], hidden_dims[i+1]))

        layers.append(torch.nn.Linear(hidden_dims[-1], 1))

        self.layers = nn.ModuleList(layers)

    def forward( self, x):
        for layer in self.layers[:-1]:
            x = layer(x)
            x = nn.ReLU()(x)
        x = self.layers[-1](x)
        return x
[10]:
model = MLPRegresion(2, [3, 2])
criterion = torch.nn.BCELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

train_loss = []
data = torch.from_numpy(X).float()
y_true = torch.from_numpy(Y).float()

for epoch in range(5000):
    optimizer.zero_grad()

    output = model(data)
    y_pred = torch.sigmoid(output)

    loss = criterion(y_pred, y_true.reshape(y_pred.shape))

    train_loss.append(loss.item())
    loss.backward()
    optimizer.step()
[11]:
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
plt.plot(train_loss)
fig.tight_layout()
../../_images/examples_nn_Reference_ch5_Part_2_xor_15_0.png
[12]:
x1 = np.linspace(0, 1, 100)
x2 = np.linspace(0, 1, 100)
xx1, xx2 = np.meshgrid(x1, x2)
x_grid = np.hstack([xx1.ravel().reshape(-1, 1), xx2.ravel().reshape(-1, 1)])
x_grid = torch.from_numpy(x_grid).float()

with torch.no_grad():
    output = model(x_grid)

y_grid_pred = torch.sigmoid(output)
y_grid_pred = y_grid_pred.reshape(xx1.shape)


# plot space separation
fig, ax = plt.subplots(1, 1, figsize=(5, 5))

custom_cmap = ListedColormap([cmap(0), cmap(1)])
plt.pcolormesh(xx1, xx2, y_grid_pred>=0.5, cmap=custom_cmap, alpha=0.5)

plt.scatter(X[:, 0], X[:, 1], c=[colors[Y[i]] for i in range(len(Y))], s=500)
plt.xlim([-0.2, 1.2])
plt.ylim([-0.2, 1.2])
fig.tight_layout()
../../_images/examples_nn_Reference_ch5_Part_2_xor_16_0.png
[12]: