PyTorch Tensor Basics
This is an introduction to PyTorch's Tensor class, which is reasonably analogous to Numpy's ndarray, and which forms the basis for building neural networks in PyTorch.
Now that we know WTF a tensor is, and saw how Numpy's ndarray
can be used to represent them, let's switch gears and see how they are represented in PyTorch.
PyTorch has made an impressive dent on the machine learning scene since Facebook opensourced it in early 2017. It may not have the widespread adoption that TensorFlow has  which was initially released well over a year prior, enjoys the backing of Google, and had the luxury of establishing itself as the gold standard as a new wave of neural networking tools was being ushered in  but the attention that PyTorch receives in the research community especially is quite real. Much of this attention comes both from its relationship to Torch proper, and its dynamic computation graph.
Image source
As excited as I have recently been by turning my own attention to PyTorch, this is not really a PyTorch tutorial; it's more of an introduction to PyTorch's Tensor
class, which is reasonably analogous to Numpy's ndarray
.
Tensor (Very) Basics
So let's take a look at some of PyTorch's tensor basics, starting with creating a tensor (using the Tensor
class):
import torch # Create a Torch tensor t = torch.Tensor([[1, 2, 3], [4, 5, 6]]) t
tensor([[ 1., 2., 3.], [ 4., 5., 6.]])
You can transpose a tensor in one of 2 ways:
# Transpose t.t() # Transpose (via permute) t.permute(1,0)
Both result in the following output:
tensor([[ 1., 4.], [ 2., 5.], [ 3., 6.]])
Note that neither result in a change to the original.
Reshape a tensor with view:
# Reshape via view t.view(3,2)
tensor([[ 1., 2.], [ 3., 4.], [ 5., 6.]])
And another example:
# View again... t.view(6,1)
tensor([[ 1.], [ 2.], [ 3.], [ 4.], [ 5.], [ 6.]])
It should be obvious that mathematical conventions which are followed by Numpy carry over to PyTorch Tensors (specifically I'm referring to row and column notation).
Create a tensor and fill it with zeros (you can accomplish something similar with ones()
):
# Create tensor of zeros t = torch.zeros(3, 3) t
tensor([[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]])
Create a tensor with randoms pulled from the normal distribution:
# Create tensor from normal distribution randoms t = torch.randn(3, 3) t
tensor([[ 1.0274, 1.3727, 0.2196], [0.7258, 2.1236, 0.8512], [ 0.0392, 1.2392, 0.5460]])
Shape, dimensions, and datatype of a tensor object:
# Some tensor info print('Tensor shape:', t.shape) # t.size() gives the same print('Number of dimensions:', t.dim()) print('Tensor type:', t.type()) # there are other types
Tensor shape: torch.Size([3, 3]) Number of dimensions: 2 Tensor type: torch.FloatTensor
It should also be obvious that, beyond mathematical concepts, a number of programmatic and instantiation similarities between ndarray
and Tensor
implementations exist.
You can slice PyTorch tensors the same way you slice ndarrays
, which should be familiar to anyone who uses other Python structures:
# Slicing t = torch.Tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Every row, only the last column print(t[:, 1]) # First 2 rows, all columns print(t[:2, :]) # Lower right most corner print(t[1:, 1:])
tensor([ 3., 6., 9.]) tensor([[ 1., 2., 3.], [ 4., 5., 6.]]) tensor([[ 9.]])
PyTorch Tensor
To and From Numpy ndarray
You can easily create a tensors from an ndarray
and vice versa. These operations are fast, since the data of both structures will share the same memory space, and so no copying is involved. This is obviously an efficient approach.
# Numpy ndarray <> PyTorch tensor import numpy as np # ndarray to tensor a = np.random.randn(3, 5) t = torch.from_numpy(a) print(a) print(t) print(type(a)) print(type(t))
[[0.52192738 1.11579634 1.26925835 0.10449378 1.02894372] [0.78707263 0.05350072 0.65815075 0.18810677 0.52795765] [0.41677548 0.82031861 2.46699201 0.60320375 1.69778546]] tensor([[0.5219, 1.1158, 1.2693, 0.1045, 1.0289], [0.7871, 0.0535, 0.6582, 0.1881, 0.5280], [0.4168, 0.8203, 2.4670, 0.6032, 1.6978]], dtype=torch.float64) <class 'numpy.ndarray'> <class 'torch.Tensor'>
# tensor to ndarray t = torch.randn(3, 5) a = t.numpy() print(t) print(a) print(type(t)) print(type(a))
tensor([[0.1746, 2.4118, 0.4688, 0.0517, 0.2706], [0.8402, 0.3289, 0.4170, 1.9131, 0.8601], [0.6688, 0.2069, 0.8106, 0.8582, 0.0450]]) [[0.17455131 2.4117854 0.4688457 0.05168453 0.2706456 ] [0.8402392 0.3289494 0.41703534 1.9130518 0.86014426] [0.6688193 0.20693372 0.8105542 0.8581988 0.04502954]] <class 'torch.Tensor'> <class 'numpy.ndarray'>
Basic Tensor Operations
Here are a few tensor operations, which you can compare with Numpy implementations for fun. First up is the cross product:
# Compute cross product t1 = torch.randn(4, 3) t2 = torch.randn(4, 3) t1.cross(t2)
tensor([[ 2.6594, 0.5765, 1.4313], [ 0.4710, 0.3725, 2.1783], [0.9134, 1.6253, 0.7398], [0.4959, 0.4198, 1.1338]])
Next is the matrix product:
# Compute matrix product t = (torch.Tensor([[2, 4], [5, 10]]).mm(torch.Tensor([[10], [20]]))) t
tensor([[ 100.], [ 250.]])
And finally, elementwise multiplication:
# Elementwise multiplication t = torch.Tensor([[1, 2], [3, 4]]) t.mul(t)
tensor([[ 1., 4.], [ 9., 16.]])
A Word About GPUs
PyTorch tensors have inherent GPU support. Specifying to use the GPU memory and CUDA cores for storing and performing tensor calculations is easy; the cuda
package can help determine whether GPUs are available, and the package's cuda()
method assigns a tensor to the GPU.
# Is CUDA GPU available? torch.cuda.is_available() # How many CUDA devices? torch.cuda.is_available() # Move to GPU t.cuda()
Related:
 WTF is a Tensor?!?
 Getting Started with PyTorch Part 1: Understanding How Automatic Differentiation Works
 A Simple Starter Guide to Build a Neural Network
Top Stories Past 30 Days

