Do a tutorial - pytorch basics

Ulf Hamster 4 min.
python pytorch tutorial

boilerplate

%%capture 
!pip install torch==1.1.0
# load packages
import torch
import numpy as np

# check version
print(torch.__version__)

# set GPU if available
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)

# reproducibility
np.random.seed(42)  # numpy seed
torch.manual_seed(42)  # pytorch seed
if torch.backends.cudnn.enabled:  # CuDNN deterministic mode
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False
1.1.0
cuda:0

List/Tuple <-> Torch

Most toy examples will directly instantiate torch.tensor with a python list or tuple as data.

x = (1, 2, 3, 4)
y = torch.tensor(x, device=device)
print(y)
tensor([1, 2, 3, 4], device='cuda:0')

Numpy <-> Torch

Data might have been pre-processed with numpy (or pandas) wheras the modeling occured seperatly in pytorch. The methodstorch.from_numpy converts numpy arrays to torch tensors, and the method torch.tensor.numpy() does the other way around.

x = np.array([1, 2, 3])
y = torch.from_numpy(x)
print(y, "\n", type(y))
tensor([1, 2, 3]) 
 <class 'torch.Tensor'>
z = y.numpy()
print(z, "\n", type(z))
[1 2 3] 
 <class 'numpy.ndarray'>

CPU <-> GPU

The method torch.tensor.to() can move the data between CPU memory and GPU memory.

x = torch.rand((2, 3), device=torch.device("cpu"))
print(x, "\n", x.device)
tensor([[0.8823, 0.9150, 0.3829],
        [0.9593, 0.3904, 0.6009]]) 
 cpu
if torch.cuda.is_available():
    y = x.to("cuda")
    print(y, "\n", y.device)
else:
    print("cannot run example because GPU/CUDA is NOT available")
tensor([[0.8823, 0.9150, 0.3829],
        [0.9593, 0.3904, 0.6009]], device='cuda:0') 
 cuda:0

Initialize new tensors

ROWS = 3
COLS = 4
# same behavior as torch.Tensor, i.e. no data assigned
x = torch.empty((ROWS, COLS), device=device, dtype=torch.float16)
print(x, "\n", x.device, "\n", x.dtype)
tensor([[-2.8150e+02,  1.8447e+00,  1.4248e+00,  1.8535e+00],
        [ 1.0252e-04,  1.6914e+00, -1.2341e-03,  1.8643e+00],
        [-2.4720e+03,  1.6943e+00, -6.8438e+01,  1.7744e+00]], device='cuda:0',
       dtype=torch.float16) 
 cuda:0 
 torch.float16
x = torch.zeros((ROWS, COLS), device=device, dtype=torch.uint8)
print(x, "\n", x.device, "\n", x.dtype)
tensor([[0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 0, 0, 0]], device='cuda:0', dtype=torch.uint8) 
 cuda:0 
 torch.uint8
x = torch.ones((ROWS, COLS), device=device, dtype=torch.int64)  # int64=long
print(x, "\n", x.device, "\n", x.dtype)
tensor([[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]], device='cuda:0') 
 cuda:0 
 torch.int64
x = torch.full((ROWS, COLS), 123, device=device, dtype=torch.uint8)
print(x, "\n", x.device, "\n", x.dtype)
tensor([[123, 123, 123, 123],
        [123, 123, 123, 123],
        [123, 123, 123, 123]], device='cuda:0', dtype=torch.uint8) 
 cuda:0 
 torch.uint8
torch.manual_seed(42)
x = torch.rand((ROWS, COLS), device=device, dtype=torch.float64)  # float64=double
print(x, "\n", x.device, "\n", x.dtype)
tensor([[0.7332, 0.8099, 0.8759, 0.9129],
        [0.2111, 0.7770, 0.0193, 0.9539],
        [0.1923, 0.4240, 0.3189, 0.7902]], device='cuda:0',
       dtype=torch.float64) 
 cuda:0 
 torch.float64
torch.manual_seed(42)
x = torch.randn((ROWS, COLS), device=device, dtype=torch.float32)  # float32=float
print(x, "\n", x.device, "\n", x.dtype)
tensor([[ 0.6226,  0.8774,  1.1547,  1.3588],
        [-0.8027,  0.7622, -2.0681,  1.6839],
        [-0.8694, -0.1916, -0.4707,  0.8071]], device='cuda:0') 
 cuda:0 
 torch.float32

y=..._like(x) – New tensors with the same dimensions

The ...like(x) methods automagically adopt the dimensions of an existing tensor. Thus, you don't need to deal with dimensions explicitly.

x = torch.empty((2, 3), device=device)
print(x)
tensor([[ 0.6226,  0.8774,  1.1547],
        [ 1.3588, -0.8027,  0.7622]], device='cuda:0')
y = torch.empty_like(x, device=device, dtype=torch.float16)
print(y)
tensor([[ 9.0350e+02,  1.7803e+00, -6.3820e-03],
        [ 1.8438e+00, -1.6422e+01,  1.8936e+00]], device='cuda:0',
       dtype=torch.float16)
y = torch.zeros_like(x, device=device, dtype=torch.uint8)
print(y)
tensor([[0, 0, 0],
        [0, 0, 0]], device='cuda:0', dtype=torch.uint8)
y = torch.ones_like(x, device=device, dtype=torch.uint8)
print(y)
tensor([[1, 1, 1],
        [1, 1, 1]], device='cuda:0', dtype=torch.uint8)
y = torch.full_like(x, 4.2, device=device, dtype=torch.float32)
print(y)
tensor([[4.2000, 4.2000, 4.2000],
        [4.2000, 4.2000, 4.2000]], device='cuda:0')
torch.manual_seed(42)
y = torch.rand_like(x, device=device, dtype=torch.float32)
print(y)
tensor([[0.7332, 0.8099, 0.8759],
        [0.9129, 0.2111, 0.7770]], device='cuda:0')
torch.manual_seed(42)
y = torch.randn_like(x, device=device, dtype=torch.float32)
print(y)
tensor([[ 0.6226,  0.8774,  1.1547],
        [ 1.3588, -0.8027,  0.7622]], device='cuda:0')

x=x.new_...(r,c) – Replace tensor with a new tensor

The .new_... method is just another way to overwrite an existing tensor with a new tensor.

(rand and randn doesn't have a new_ method)

x = torch.empty((4, 3), device=device)
print(x)
tensor([[ 0.6226,  0.8774,  1.1547],
        [ 1.3588, -0.8027,  0.7622],
        [-2.0681,  1.6839, -0.8694],
        [-0.1916, -0.4707,  0.8071]], device='cuda:0')
x = x.new_empty((6, 2), device=device, dtype=torch.float64)
print(x)
tensor([[ 2.0287e-03,  5.8445e-02],
        [ 5.8329e-04,  3.6780e-01],
        [-9.3889e-09,  9.3403e-04],
        [ 1.9314e-02,  9.5390e-01],
        [ 1.9232e-01,  4.2404e-01],
        [ 3.1893e-01,  7.9018e-01]], device='cuda:0', dtype=torch.float64)
x = x.new_zeros((2, 5), device=device, dtype=torch.float16)
print(x)
tensor([[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]], device='cuda:0', dtype=torch.float16)
x = x.new_ones((3, 2), device=device, dtype=torch.float16)
print(x)
tensor([[1., 1.],
        [1., 1.],
        [1., 1.]], device='cuda:0', dtype=torch.float16)
x = x.new_full((2, 4), 123, device=device, dtype=torch.int64)
print(x)
tensor([[123, 123, 123, 123],
        [123, 123, 123, 123]], device='cuda:0')

A brief explanation. The following two examples produce the same results. Also note that all settings are lost no matter if you assign x=x.new_ or x=torch...., e.g. requires_grad will be reset.

x = torch.empty((3, 4), requires_grad=True)
x = x.new_ones((2, 3))
print(x)
tensor([[1., 1., 1.],
        [1., 1., 1.]])
x = torch.empty((3, 4), requires_grad=True)
x = torch.ones((2, 3))
print(x)
tensor([[1., 1., 1.],
        [1., 1., 1.]])

Links