In [0]:
import torch
In [2]:
x = torch.empty(5,3) ## empty
x
Out[2]:
In [3]:
x = torch.randn(5,3) ## random initialized
x
Out[3]:
In [4]:
x = torch.zeros(5,3, dtype=torch.long)
x
Out[4]:
In [5]:
type(x)
Out[5]:
In [6]:
x = torch.ones(5,2)
x
Out[6]:
In [7]:
myarr = [[10,20.2],[30,40]] ## sample data
x = torch.tensor(myarr)
x
Out[7]:
In [8]:
## create a tensor from an existing tensor
x = torch.tensor([[1,2],[3,4]], dtype=torch.int16)
print(f"X tensor: {x}")
y = torch.tensor(x, dtype=torch.float16)
print(f"Y tensor: {y}")
In [9]:
## size of tensor
x.size()
Out[9]:
In [10]:
x = torch.randn(5,2)
x
Out[10]:
In [11]:
y = torch.ones(5,2)
y
Out[11]:
In [12]:
x + y ## sum of two tensors
Out[12]:
In [13]:
torch.add(x,y) ## alternative: sum of two tensors
Out[13]:
In [14]:
## In Place addition : Any operation that mutates a tensor in-place is post-fixed with an _. For example: x.copy_(y), x.t_(), will change x.
y.add_(x)
Out[14]:
In [15]:
### Standard numpy operations on tensors
x[:1]
Out[15]:
In [16]:
type(x[:1])
Out[16]:
In [17]:
### Resize the tensor
x = torch.randn(4,4)
x
Out[17]:
In [18]:
y = x.view(16)
y
Out[18]:
In [19]:
z = x.view([-1,8])
z
Out[19]:
In [20]:
## transpose an array
torch.transpose(x, 0,1)
Out[20]:
In [21]:
## to get the value of a tensor
x = torch.randn(1)
print(x)
print(x.item())
Numpy Bridge with Tensors
In [22]:
a = torch.ones(5)
a
Out[22]:
In [23]:
type(a)
Out[23]:
In [24]:
### convert to numpy
a.numpy()
Out[24]:
In [25]:
## converting numpy array to torch tensors
import numpy as np
a = np.ones(5)
t = torch.tensor(a, dtype=torch.int)
print(a, type(a))
print(t, type(t))
In [26]:
### if CUDA is available or not
torch.cuda.is_available()
Out[26]:
In [27]:
if torch.cuda.is_available():
device = torch.device("cuda") ## define device
x = torch.ones(5) ## normal stuff
print(x)
y = torch.ones_like(x, device=device) ### running it on gpu
print(y)
x = x.to(device) ## change the execution to device
z = x + y
print(z)
print(z.to("cpu", dtype=torch.int32)) ## change the data type of z using .to and run it on cpu
In [28]:
x = torch.ones(2,2, requires_grad=True)
x
Out[28]:
In [29]:
y = x + 2
y
Out[29]:
In [30]:
y.grad_fn ### y was created as a result of an operation, hence it has a grad_fn
Out[30]:
In [31]:
## more operation on y
z = y*y*3
out = z.mean()
print(z, out)
In [32]:
### .requires_grad_( ... ) changes an existing Tensor’s requires_grad flag in-place. The input flag defaults to False if not given.
a = torch.randn(2,2)
a = ((a*2)/(a-1))
print(a.requires_grad)
a.requires_grad_(True) ## changing the grad inplace
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
In [33]:
out ## out contains a single scalar, out.backward() is equivalent to out.backward(torch.tensor(1.)).
Out[33]:
In [0]:
out.backward()
In [35]:
print(x.grad) ### Print gradients d(out)/dx
In [36]:
## another example
t1 = torch.ones(1, requires_grad= True)
t2 = torch.ones(1, requires_grad=True)
print(t1, t2)
s = t1+t2
print(s)
In [37]:
s.grad_fn
Out[37]:
In [0]:
s.backward()
In [39]:
t1.grad
Out[39]:
Vector Jacobian Product
In [40]:
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
In [41]:
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)
print(x.grad)
In [42]:
print(x.requires_grad)
y = x.detach()
print(y.requires_grad)
print(x.eq(y).all())
A typical training procedure for a neural network is as follows:
weight = weight - learning_rate * gradient
In [0]:
import torch.nn as nn
import torch.nn.functional as F
In [52]:
X = torch.tensor(([2, 9], [5, 1], [3, 6]), dtype=torch.float) ## 3x2 tensor
y = torch.tensor(([92], [100], [89]), dtype=torch.float) ## 3x1 tensor
xPredicted = torch.tensor(([4, 8]), dtype=torch.float) # 1 X 2 tensor
print(X)
print(y)
In [59]:
X_max, X_max_ind = torch.max(X, 0) ## return max including indices and max values per col. 0 for col, 1 for row
print(X_max, X_max_ind)
xPredicted_max, _ = torch.max(xPredicted, 0)
print(xPredicted_max)
y_max = torch.max(y)
print(y_max)
## scaling
X = torch.div(X, X_max)
xPredicted = torch.div(xPredicted, xPredicted_max)
y = y/y_max
print(f"X is : {X}")
print(f"xPredicted is : {xPredicted}")
print(f"y is : {y}")
In [0]:
class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
## parameters
self.input_size = 2
self.hidden_layer = 3
self.output_layer = 1
## initializing the weights
self.W1 = torch.randn(self.input_size, self.hidden_layer) # 2x3 tensor
self.W2 = torch.randn(self.hidden_layer, self.output_layer) # 3x1 tensor
def forward(self, X):
'''
Forward propagation
'''
self.z = torch.matmul(X, self.W1)
self.z2 = torch.sigmoid(self.z)
self.z3 = torch.matmul(self.z2, self.W2)
o = torch.sigmoid(self.z3) ## final activation function
return o
def sigmoid(self, s):
return 1 / (1 + torch.exp(-s))
def sigmoidPrime(self, s):
# derivative of sigmoid
return s * (1 - s)
def backward(self, X, y, o):
'''
Backward propagation
'''
self.o_error = y - o ## calculate the difference b/w predicted and actual
self.o_delta = self.o_error * self.sigmoidPrime(o) ## derivative of sig to error
self.z2_error = torch.matmul(self.o_delta, torch.t(self.W2))
self.z2_delta = self.z2_error * self.sigmoidPrime(self.z2)
self.W1 += torch.matmul(torch.t(X), self.z2_delta)
self.W2 += torch.matmul(torch.t(self.z2), self.o_delta)
def train(self, X, y):
# forward + backward pass for training
o = self.forward(X)
self.backward(X, y, o)
def saveWeights(self, model):
# we will use the PyTorch internal storage functions
torch.save(model, "NN")
def predict(self):
print ("Predicted data based on trained weights: ")
print ("Input (scaled):" + str(xPredicted))
print ("Output:" + str(self.forward(xPredicted)))
In [73]:
NN = SimpleNN()
print(NN)
In [74]:
for i in range(10): # trains the NN 10 times
print ("#" + str(i) + " Loss: " + str(torch.mean((y - NN(X))**2).detach().item())) # mean sum squared loss
NN.train(X, y)
NN.saveWeights(NN)
NN.predict()
In [0]: