Chia sẻ kiến thức lập trình

Giới thiệu cấu trúc dữ liệu Tensors

Khái niệm

Tensors là 1 cấu trúc dữ liệu mảng n chiều, hỗ trợ chúng ta tính toán ma trận tương tự numpy array, tuy nhiên nó có thể chạy được trên cả CPU và GPU. Tensors được tối ưu cho việc tính toán đạo hàm của AutoGrad.

import torch
import numpy as np

1. Khởi tạo Tensor

- Trực tiếp từ dữ liệu

# The data type is automatically inferred.
data = [[1, 2],[3, 4]]
tensor_data = torch.tensor(data)
tensor_data
tensor([[1, 2],
        [3, 4]])

- Từ NumPy array

np_array = np.array(data)
tensor_from_np = torch.from_numpy(np_array)
tensor_from_np
tensor([[1, 2],
        [3, 4]])

- Từ 1 tensor khác:

x_ones = torch.ones_like(tensor_data) # retains the properties of x_data
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(tensor_data, dtype=torch.float) # overrides the datatype of x_data
print(f"Random Tensor: \n {x_rand} \n")
Ones Tensor: 
 tensor([[1, 1],
        [1, 1]]) 

Random Tensor: 
 tensor([[0.8375, 0.2649],
        [0.6834, 0.9858]]) 

- Khởi tạo ngẫu nhiên hoặc hằng số

shape có kiểu tuple, biểu diễn các chiều của một tensor

shape = (2,3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
Random Tensor: 
 tensor([[0.6235, 0.6666, 0.2049],
        [0.5252, 0.4084, 0.4464]]) 

Ones Tensor: 
 tensor([[1., 1., 1.],
        [1., 1., 1.]]) 

Zeros Tensor: 
 tensor([[0., 0., 0.],
        [0., 0., 0.]])

2. Các tính chất của Tensor

Gồm có shape, datatype, và device

tensor = torch.rand(3,4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu

3. Các phép toán trên Tensors

Có rất nhiều phép toán trên tensors (>100) gồm số học, đại số, ma trận, ngẫu nhiên...
Link tham khảo: https://pytorch.org/docs/stable/torch.html.

Mỗi phép toán đều có khả năng chạy trên GPU (thường sẽ nhanh hơn CPU).

Tensors được khởi tạo mặc định trên CPU. Dùng .to(device) chuyển tensors sang GPU.

Lưu ý: chuyển lượng lớn tensors qua lại giữa các thiết bị có thể gây tốn thời gian và bộ nhớ

# We move our tensor to the GPU if available
if torch.cuda.is_available():
    tensor = tensor.to('cuda')

tensor
tensor([[0.7317, 0.5521, 0.5672, 0.1445],
        [0.6535, 0.2131, 0.7473, 0.5856],
        [0.6212, 0.6492, 0.7159, 0.5920]], device='cuda:0')

3.1. Indexing và slicing giống numpy:

tensor = torch.randint(10, (4, 4))
display(tensor)
print('First row: ', tensor[0])
print('First column: ', tensor[:, 0])
print('Last column:', tensor[:, -1])
tensor[:,1] = 0
print(tensor)
tensor([[5, 5, 4, 6],
        [5, 5, 5, 4],
        [5, 3, 7, 7],
        [4, 7, 3, 8]])

First row:  tensor([5, 5, 4, 6])
First column:  tensor([5, 5, 5, 4])
Last column: tensor([6, 4, 7, 8])
tensor([[5, 0, 4, 6],
        [5, 0, 5, 4],
        [5, 0, 7, 7],
        [4, 0, 3, 8]])

3.2. Joining tensors

t1 = torch.cat([tensor, tensor, tensor], dim=1)
display(tensor.shape)
display(t1.shape)
print(t1)
torch.Size([4, 4])

torch.Size([4, 12])

tensor([[5, 0, 4, 6, 5, 0, 4, 6, 5, 0, 4, 6],
        [5, 0, 5, 4, 5, 0, 5, 4, 5, 0, 5, 4],
        [5, 0, 7, 7, 5, 0, 7, 7, 5, 0, 7, 7],
        [4, 0, 3, 8, 4, 0, 3, 8, 4, 0, 3, 8]])

3.3. Phép toán số học

# Phép nhân ma trận. y1, y2, y3 cho ra kết quả giống nhau
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)

y3 = torch.ones_like(tensor)
torch.matmul(tensor, tensor.T, out=y3)
display(y1, y2, y3)

# Phép nhân element-wise
z1 = tensor * tensor
z2 = tensor.mul(tensor)

z3 = torch.ones_like(tensor)
torch.mul(tensor, tensor, out=z3)

display(z1, z2, z3)
tensor([[ 77,  69,  95,  80],
        [ 69,  66,  88,  67],
        [ 95,  88, 123,  97],
        [ 80,  67,  97,  89]])

tensor([[ 77,  69,  95,  80],
        [ 69,  66,  88,  67],
        [ 95,  88, 123,  97],
        [ 80,  67,  97,  89]])

tensor([[ 77,  69,  95,  80],
        [ 69,  66,  88,  67],
        [ 95,  88, 123,  97],
        [ 80,  67,  97,  89]])

tensor([[25,  0, 16, 36],
        [25,  0, 25, 16],
        [25,  0, 49, 49],
        [16,  0,  9, 64]])

tensor([[25,  0, 16, 36],
        [25,  0, 25, 16],
        [25,  0, 49, 49],
        [16,  0,  9, 64]])

tensor([[25,  0, 16, 36],
        [25,  0, 25, 16],
        [25,  0, 49, 49],
        [16,  0,  9, 64]])

3.4. Single-element tensors

Có thể chuyển đổi one-element tensor về dạng số trong Python bằng cách dùng hàm item()

agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))
63 

3.5. Các phép toán In-place

Lưu kết quả vào biến gọi hàm. Biểu thị bởi hậu tố _.

Ví dụ: x.copy_(y), x.t_() sẽ thay đổi biến x.

print(tensor, "\n")
tensor.add_(5)
print(tensor)
tensor([[5, 0, 4, 6],
        [5, 0, 5, 4],
        [5, 0, 7, 7],
        [4, 0, 3, 8]]) 

tensor([[10,  5,  9, 11],
        [10,  5, 10,  9],
        [10,  5, 12, 12],
        [ 9,  5,  8, 13]])

Chú ý

Các phép In-place giúp tiết kiệm bộ nhớ, tuy nhiên gây nhiều vấn đề khi tính đạo hàm. Việc sử dụng chúng không được khuyến khích.


4. Liên hệ với NumPy

Tensors trên CPU và NumPy arrays có thể chia sẻ cùng địa chỉ bộ nhớ, nên thay đổi cái này sẽ làm thay đổi luôn cái còn lại

4.1. Chuyển Tensor sang NumPy array

t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]

Thay đổi tensor làm thay đổi NumPy array.

t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([2., 2., 2., 2., 2.])
n: [2. 2. 2. 2. 2.]

4.2. Chuyển NumPy array sang Tensor

n = np.ones(5)
t = torch.from_numpy(n)

Thay đổi NumPy array làm thay đổi tensor.

np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
n: [2. 2. 2. 2. 2.]
Share: