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

Linear Regression using PyTorch

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.4616])
1 tensor([1.0000, 0.6981]) tensor([0.6161])
2 tensor([1.0000, 1.3963]) tensor([1.1438])
3 tensor([1.0000, 2.0944]) tensor([1.1616])
4 tensor([1.0000, 2.7925]) tensor([1.9410])
5 tensor([1.0000, 3.4907]) tensor([2.0466])
6 tensor([1.0000, 4.1888]) tensor([3.1504])
7 tensor([1.0000, 4.8869]) tensor([4.1341])
8 tensor([1.0000, 5.5851]) tensor([3.8957])
9 tensor([1.0000, 6.2832]) tensor([4.7067])

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, 3.4907],
        [1.0000, 0.6981],
        [1.0000, 4.8869],
        [1.0000, 1.3963]]),
 'label': tensor([[2.0466],
        [0.6161],
        [4.1341],
        [1.1438]])}

batch# = 1
samples: 
{'feature': tensor([[1.0000, 5.5851],
        [1.0000, 0.0000],
        [1.0000, 2.7925],
        [1.0000, 4.1888]]),
 'label': tensor([[ 3.8957],
        [-0.4616],
        [ 1.9410],
        [ 3.1504]])}

batch# = 2
samples: 
{'feature': tensor([[1.0000, 2.0944],
        [1.0000, 6.2832]]),
 'label': tensor([[1.1616],
        [4.7067]])}

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.2626],
        [-0.2500],
        [-0.7019],
        [-0.7500],
        [-0.4703]], 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 = 15.819917678833008
	Batch = 1, Error = 42.4968147277832
	Batch = 2, Error = 0.42557552456855774
Epoch = 10
	Batch = 0, Error = 0.09854041039943695
	Batch = 1, Error = 0.10805653780698776
	Batch = 2, Error = 0.22046087682247162
Epoch = 20
	Batch = 0, Error = 0.17055541276931763
	Batch = 1, Error = 0.10860377550125122
	Batch = 2, Error = 0.016173765063285828
Epoch = 30
	Batch = 0, Error = 0.14505337178707123
	Batch = 1, Error = 0.045625269412994385
	Batch = 2, Error = 0.181903675198555
Epoch = 40
	Batch = 0, Error = 0.09492434561252594
	Batch = 1, Error = 0.14765948057174683
	Batch = 2, Error = 0.05652307718992233
Epoch = 50
	Batch = 0, Error = 0.052950307726860046
	Batch = 1, Error = 0.07008141279220581
	Batch = 2, Error = 0.2513729929924011
Epoch = 60
	Batch = 0, Error = 0.13909898698329926
	Batch = 1, Error = 0.0447746217250824
	Batch = 2, Error = 0.09827805310487747
Epoch = 70
	Batch = 0, Error = 0.055665023624897
	Batch = 1, Error = 0.1671134978532791
	Batch = 2, Error = 0.021786220371723175
Epoch = 80
	Batch = 0, Error = 0.0224416833370924
	Batch = 1, Error = 0.1171477660536766
	Batch = 2, Error = 0.19996628165245056
Epoch = 90
	Batch = 0, Error = 0.03901229053735733
	Batch = 1, Error = 0.08612875640392303
	Batch = 2, Error = 0.21342293918132782

Lets see how well the model has learnt the data

In [12]:
#from torch.autograd.variable import Variable

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:\n', design_matrix)
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 [13]:
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()