HTML generated from Jupyter notebook: linear-regression-2.ipynb

Linear Regression using PyTorch

  • Dataset
  • Our very own linear layer
  • Built-in cost
In [1]:
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
In [2]:
N = 10 # number of data points
m = .7
c = 0
x = np.linspace(0,2*np.pi,N)
y = m*x + c + np.random.normal(0,.3,x.shape)
plt.figure()
plt.plot(x,y,'o')
plt.xlabel('x')
plt.ylabel('y')
plt.title('2D data (#data = %d)' % N)
plt.show()
In [3]:
import torch

Dataset

In [4]:
from torch.utils.data import Dataset
class MyDataset(Dataset):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __len__(self):
        return len(self.x)
    
    def __getitem__(self, idx):
        sample = {
            'feature': torch.tensor([1,self.x[idx]]), 
            'label': torch.tensor([self.y[idx]])}
        return sample
In [5]:
dataset = MyDataset(x, y)
for i in range(len(dataset)):
    sample = dataset[i]
    print(i, sample['feature'], sample['label'])
0 tensor([1., 0.]) tensor([-0.3618])
1 tensor([1.0000, 0.6981]) tensor([0.5810])
2 tensor([1.0000, 1.3963]) tensor([1.4410])
3 tensor([1.0000, 2.0944]) tensor([1.8405])
4 tensor([1.0000, 2.7925]) tensor([2.2305])
5 tensor([1.0000, 3.4907]) tensor([2.9662])
6 tensor([1.0000, 4.1888]) tensor([2.7250])
7 tensor([1.0000, 4.8869]) tensor([2.8623])
8 tensor([1.0000, 5.5851]) tensor([4.4197])
9 tensor([1.0000, 6.2832]) tensor([4.7414])

Dataloader

In [6]:
from torch.utils.data import DataLoader

dataset = MyDataset(x, y)
batch_size = 4
shuffle = True
num_workers = 4
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers)
In [7]:
import pprint as pp
for i_batch, samples in enumerate(dataloader):
    print('\nbatch# = %s' % i_batch)
    print('samples: ')
    pp.pprint(samples)
batch# = 0
samples: 
{'feature': tensor([[1.0000, 1.3963],
        [1.0000, 0.6981],
        [1.0000, 4.8869],
        [1.0000, 2.0944]]),
 'label': tensor([[1.4410],
        [0.5810],
        [2.8623],
        [1.8405]])}

batch# = 1
samples: 
{'feature': tensor([[1.0000, 0.0000],
        [1.0000, 4.1888],
        [1.0000, 3.4907],
        [1.0000, 2.7925]]),
 'label': tensor([[-0.3618],
        [ 2.7250],
        [ 2.9662],
        [ 2.2305]])}

batch# = 2
samples: 
{'feature': tensor([[1.0000, 6.2832],
        [1.0000, 5.5851]]),
 'label': tensor([[4.7414],
        [4.4197]])}

Model

In [8]:
import torch.nn as nn
from torch.nn.parameter import Parameter
class MyModel(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(MyModel, self).__init__()
        
        self.weight = Parameter(torch.Tensor(output_dim, input_dim))
        self.bias = Parameter(torch.Tensor(output_dim, 1))
        
        stdv = 1.
        self.weight.data.uniform_(-stdv, stdv)
        self.bias.data.uniform_(-stdv, stdv)
        
    def forward(self, x):
        weight_and_bias = torch.cat((self.weight, self.bias), 1)
        #print(weight_and_bias)
        #print(weight_and_bias.t().shape)
        #print(x.shape)
        #print(self.weight.size())
        
        out = x.matmul(weight_and_bias.t())
        return out

Setting a model for our problem

In [9]:
input_dim = 1
output_dim = 1

model = MyModel(input_dim, output_dim)

model(torch.rand([5,2]))
Out[9]:
tensor([[-0.1100],
        [-0.1870],
        [-0.4243],
        [-0.5530],
        [-0.3281]], grad_fn=<MmBackward>)

Cost function

Often called loss or error

In [10]:
cost = nn.MSELoss()

Minimizing the cost function

In other words training (or learning from data)

In [11]:
num_epochs = 100  # How many times the entire training data is seen?
l_rate = 0.01
optimiser = torch.optim.SGD(model.parameters(), lr = l_rate) 

dataset = MyDataset(x, y)
batch_size = 4
shuffle = True
num_workers = 4
training_sample_generator = DataLoader(dataset, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers)

for epoch in range(num_epochs):
    if epoch % 10 == 0:
        print('Epoch = %s' % epoch)
    for batch_i, samples in enumerate(training_sample_generator):
        predictions = model(samples['feature'])
        error = cost(predictions, samples['label'])
        if epoch % 10 == 0:
            print('\tBatch = %s, Error = %s' % (batch_i, error.item()))
        
        # Before the backward pass, use the optimizer object to zero all of the
        # gradients for the variables it will update (which are the learnable
        # weights of the model). This is because by default, gradients are
        # accumulated in buffers( i.e, not overwritten) whenever .backward()
        # is called. Checkout docs of torch.autograd.backward for more details.
        optimiser.zero_grad()
        
        # Backward pass: compute gradient of the loss with respect to model
        # parameters
        error.backward()
        
        # Calling the step function on an Optimizer makes an update to its
        # parameters
        optimiser.step()
Epoch = 0
	Batch = 0, Error = 3.6194071769714355
	Batch = 1, Error = 1.6688085794448853
	Batch = 2, Error = 1.7244681119918823
Epoch = 10
	Batch = 0, Error = 0.3371277451515198
	Batch = 1, Error = 0.2850171625614166
	Batch = 2, Error = 0.020265253260731697
Epoch = 20
	Batch = 0, Error = 0.29061824083328247
	Batch = 1, Error = 0.15898209810256958
	Batch = 2, Error = 0.19518862664699554
Epoch = 30
	Batch = 0, Error = 0.1601485162973404
	Batch = 1, Error = 0.29877153038978577
	Batch = 2, Error = 0.11672575026750565
Epoch = 40
	Batch = 0, Error = 0.13720978796482086
	Batch = 1, Error = 0.06203906983137131
	Batch = 2, Error = 0.521589994430542
Epoch = 50
	Batch = 0, Error = 0.28696396946907043
	Batch = 1, Error = 0.0987117663025856
	Batch = 2, Error = 0.0626242533326149
Epoch = 60
	Batch = 0, Error = 0.12105162441730499
	Batch = 1, Error = 0.21212919056415558
	Batch = 2, Error = 0.1273290514945984
Epoch = 70
	Batch = 0, Error = 0.1331830471754074
	Batch = 1, Error = 0.09055991470813751
	Batch = 2, Error = 0.3479709029197693
Epoch = 80
	Batch = 0, Error = 0.08263953775167465
	Batch = 1, Error = 0.21871107816696167
	Batch = 2, Error = 0.16919730603694916
Epoch = 90
	Batch = 0, Error = 0.28425654768943787
	Batch = 1, Error = 0.05836670845746994
	Batch = 2, Error = 0.1187400221824646

Lets see how well the model has learnt the data

In [14]:
x_for_plotting = np.linspace(0, 2*np.pi, 1000)
design_matrix = torch.tensor(np.vstack([np.ones(x_for_plotting.shape), x_for_plotting]).T, dtype=torch.float32)
print('Design matrix shape:', design_matrix.shape)

y_for_plotting = model.forward(design_matrix)
print('y_for_plotting shape:', y_for_plotting.shape)
Design matrix shape: torch.Size([1000, 2])
y_for_plotting shape: torch.Size([1000, 1])
In [15]:
plt.figure()
plt.plot(x,y,'o')
plt.plot(x_for_plotting, y_for_plotting.data.numpy(), 'r-')
plt.xlabel('x')
plt.ylabel('y')
plt.title('2D data (#data = %d)' % N)
plt.show()