Bài 1: Tensor | Deep Learning cơ bản

Chúng tôi rất vui mừng được chia sẻ kiến thức sâu sắc về từ khóa Tensor la gi để tối ưu hóa nội dung trang web và tiếp thị trực tuyến. Bài viết cung cấp phương pháp tìm kiếm, phân tích từ khóa và chiến lược hiệu quả. Cảm ơn sự quan tâm và hãy tiếp tục theo dõi để cập nhật kiến thức mới.

Tài liệu trình diễn dưới dạng số thực

Thông thường các thuật toán Machine Learning (ML), Deep Learning (DL) chỉ xử lý được tài liệu dạng số thực nên các tài liệu đưa vào mô hình thường được chuyển về dạng số thực.

Bạn Đang Xem: Bài 1: Tensor | Deep Learning cơ bản

Ảnh màu (rgb) được trình diễn dưới dạng 1 tensor 3D

Hay tài liệu dạng chữ (tôi, yêu, hoa,..) cũng được chuyển về dạng vector trước lúc cho vào các mô hình, ví dụ như mô hình word2vec.

Với tài liệu nguồn vào dạng số thì những mô hình ML hay DL sẽ thực hiện các phép tính toán, chuyển đổi để cho ra được output của mô hình. Vậy nên trình diễn tài liệu dạng số thực và các phép tính toán trên số thực đó đó là nền tảng cơ bản cho những mô hình AI.

Numpy arrays vs PyTorch tensors.

Numpy là thư viện Python giúp lưu trữ và xử lý các phép tính với tài liệu dạng số thực. Tuy nhiên, Numpy được viết bằng C/C++ nên tốc độ xử lý và tính toán rất nhanh.

PyTorch tensors có chức năng và mục tiêu tương tự Numpy arrays nhưng có một vài ưu điểm hơn:

  • Thực hiện tính toán nhanh trên GPU, vì các mô hình DL muốn tăng tốc thì hãy xử lý qua các GPU nên tensors tương trợ tính toán nhanh trên GPU rất cấp thiết.
  • Các Pytorch tensors có thể lưu lại được đồ thị tính toán nên có thể tính đạo hàm nhanh chóng, phục vụ cho thuật toán backpropagation trong Deep Learning, bài sau học về autograd mình sẽ nói kĩ.

Có bài này so sánh về tốc độ giữa Numpy arrays và Torch tensors, mọi người tham khảo thêm.

Torch Tensors

Số thực là tài liệu 0D, vector 1D, ma trận 2D còn tài liệu từ 3D trở đi được gọi là tensor, rõ ràng và cụ thể mọi người xem ở đây.

Vector

Để truy cập đến thành phần của vector và sửa thành phần của vector ta dùng chỉ số index. Index sẽ tiến hành đánh khởi đầu từ 0 đến thành phần cuối cùng của vector.

Tuy nhiên để truy cập thành phần cuối của vector mình phải dùng

x[x.shape[0] – 1] # x.shape[0] trả về số thành phần trong vector

Khá kềnh càng, nên để tiện cho mọi người cần lấy thành phần từ thời điểm cuối lại, Tensors tương trợ đánh index từ thời điểm cuối lại, -Một là phần từ thời điểm cuối cùng, -2 là thành phần thứ hai từ thời điểm cuối lại,….

x[-1] # Tương đương với x[x.shape[0] – 1], lấy thành phần cuối cùng

Vậy có cách nào lấy được một số thành phần trong vector, ví dụ 3 thành phần cuối cùng, các thành phần index chẵn,..? Có, slicing sẽ giúp mọi người.

Cú pháp sliding là: start:stop:step

Nếu mọi người không truyền gì thì mặc định start=0, stop=x.shape[0]step=1. Ý tưởng slicing là sẽ lấy từ thành phần index start đến index (stop – 1) với bước nhảy là step.

x = x[:] = x[::] = x[0:x.shape[0]:1] # lấy tất các thành phần trong x

Như ví dụ x[1:5:2] ở trên thì mình sẽ lấy thành phần đầu ở index 1, sau đó lấy thành phần ở index 3, tuy nhiên sẽ không còn lấy thành phần ở index 5 vì tôi chỉ lấy từ index start đến (stop – 1) hay từ là 1 -> 4.

x[1:5:2] # output: [2, 4]

Torch tensors không tương trợ step âm như python list.

Ma trận

Khác với vector là 1D, ma trận 2D, trình diễn dưới dạng hàng và cột, kích thước ma trận được quy định là số hàng * số cột, ví dụ ma trận ở dưới displaystyle Ainmathbb{R}^{3times 2}. Ta có thể dùng tính chất shape để lấy ra kích thước của A

A.shape # torch.Size([3, 2]) A.shape[0] # 3

Để truy cập đến thành phần trong ma trận mình phải chỉ rõ thành phần đấy ở hàng nào và cột nào. Mình sẽ truyền chỉ số hàng trước, chỉ số cột sau. Chỉ số của hàng và cột cũng đánh khởi đầu từ 0, và cũng xuất hiện quy tắc -1 như ở phần vector.

Ở ma trận cũng tương trợ slicing, tuy nhiên ma trận 2 chiều nên những lúc slicing thì hãy chỉ rõ slice những hàng nào và slide những cột nào.

# A[1:, :1] # Tương đương A[1:A.shape[0]:1, 0:1:1]

Mình nói là slicing cũng truyền hàng trước, cột sau. Phần hàng truyền 1: nên sẽ lấy từ hàng 1 đến hết, còn phần cột truyền :1 nên sẽ hiểu là 0:1 và chỉ lấy cột 0.

A[:, 1] # tensor([2, 4, 6])

Mình nói là slicing cũng truyền hàng trước, cột sau. Phần hàng truyền “:” nên hiểu là “0:A.shape[0]:1” ý là lấy tất các hàng, còn phần cột truyền 1 nên sẽ lấy cột 1. Do đó A[:, 1] lấy ra cột index 1.

Tensor 3D

Với tensor 3D thì tính chất shape sẽ cho ra 3 giá trị, tương ứng độ sâu (depth), số hàng, số cột. Để truy cập thành phần thì tôi cũng phải chỉ rõ index của depth, hàng và cột. Tương tự để slicing thì tôi cũng phải slicing trên cả 3D.

Xem Thêm : Tiền pháp định (Fiat) là gì? Mối liên quan giữa Fiat và tiền mã hóa

Ý tưởng với tensor n dimension tương tự.

Torch Properties

Dtype

Torch tensors chỉ chứa tài liệu kiểu số và kiểu bool (True/False). Mỗi torch tensor thuộc 1 kiểu tài liệu, ở tính chất dtype. Đây là list các kiểu tài liệu torch tensors có thể chứa:

  • torch.float32 or torch.float: 32-bit floating-point
  • torch.float64 or torch.double: 64-bit, double-precision floating-point
  • torch.float16 or torch.half: 16-bit, half-precision floating-point
  • torch.int8: signed 8-bit integers
  • torch.uint8: unsigned 8-bit integers
  • torch.int16 or torch.short: signed 16-bit integers
  • torch.int32 or torch.int: signed 32-bit integers
  • torch.int64 or torch.long: signed 64-bit integers
  • torch.bool: Boolean

Thường ngày khi chúng ta gán giá trị cho tensor thì torch sẽ tự động hóa gán dtype bằng dtype của giá trị có kiểu rộng hơn trong tensor. Ví dụ: các giá trị trong tensor có cả int, float thì dtype của tensor sẽ là float.

points = torch.tensor([7, 8, 10, 6.5]) print(points.dtype) # output: torch.float32

Tuy nhiên bạn cũng xuất hiện thể khởi tạo kiểu tài liệu cho tensor.

points = torch.tensor([7, 8, 10, 6]) print(points.dtype) # output: torch.int64 # Gán kiểu tài liệu cho tensor points = torch.tensor([7, 8, 10, 6], dtype=torch.short) print(points.dtype) # output: torch.int16

Hoặc mình cũng xuất hiện thể chuyển kiểu tài liệu của tensor đã được khai báo.

points = torch.tensor([7, 8, 10, 6]).short() points = torch.tensor([7, 8, 10, 6]).to(dtype=torch.short)

Hàm to(dtype=…) sẽ kiểm tra kiểu tài liệu của tensor và chuyển sang kiểu tài liệu mới nếu cấp thiết. Phần dưới mình sẽ dùng hàm to() để chuyển tensor từ CPU sang GPU.

Torch transpose

Hàm torch.transpose(input, dim0, dim1): Nhận input tensor và sẽ đổi chỗ dim0 và dim1 với nhau.

Ví dụ: với ma trận phép tính transpose sẽ chuyển hàng và cột, cụ thể hàng thứ i của A sẽ thành cột thứ i của A^T và cột thứ j của A sẽ thành hàng thứ j của A^T, do đó Ainmathbb{R}^{3times 2} Rightarrow A^Tinmathbb{R}^{2times 3}

Cùng thử nhìn transpose với tensor 3d nhé.

Mọi người thấy mình transpose chiều sâu và chiều hàng, chiều cột giữ nguyên (số cột giữ nguyên). Vì số cột giữ nguyên, nên mọi người thấy các vector hàng ở A và A^T không thay đổi, chỉ đổi vị trí. Và từng cột ở mỗi ma trận trong A được tách ra thành các thành phần cho chiều sâu.

Ngoài ra torch còn tương trợ rất nhiều phép tính toán liên quan đến tensor nữa, rõ ràng và cụ thể mọi người xem ở đây.

Torch Storage

Phần này cùng xem thực sự Torch lưu trữ tensor ra làm sao.

Storage

Thực ra các giá trị trong tensor sẽ tiến hành lưu trên 1 vùng nhớ liên tục trên bộ nhớ, được quản lý bởi torch.Storage. Storage là một trong mảng 1 chiều gồm các số có cùng kiểu tài liệu (ở trên mình biết các giá trị trong một tensor cùng kiểu tài liệu).

Ví dụ mình tạo 1 vector với torch, kiểu tài liệu mặc định với số nguyên sẽ là torch.int64, hay mỗi thành phần cần 8 bytes để lưu trữ.

x sẽ trỏ đến thành phần trước hết, và để lấy thành phần x[i] thì mình sẽ truy cập đến vị trị (x + i * 8). Đây là một phần lý do vì sao index mọi người thấy hay khởi đầu từ 0, tại x đã trỏ đến thành phần trước hết x[0] rồi, còn x[i] sẽ tiện lấy địa chỉ của thành phần (i+1), thêm nữa mọi người xem ở đây.

Storage 1 chiều thì lưu tài liệu Torch tensor 2 chiều dạng ma trận ra làm sao? Storage xếp hết tài liệu thành 1 chiều, nối các hàng từ trên xuống dưới tuần tự với nhau cho tới hết.

x trỏ đến thành phần hàng 0, cột 0 (x[0][0]). Thành phần x[i][j] sẽ ở ô nhớ (x+i*col+j), trong đó col là số cột của ma trận, hay x[i][j] = storage[i*col+j]

Ví dụ ma trận trên có 2 hàng, 3 cột, thì thành phần x[1][2] (=6) sẽ tại khu vực x+1*3+2 = x+5, để truy cập giá trị x[1][2] qua storage mình dùng storage[5].

x = torch.tensor([[1,2,3],[4,5,6]]) x.storage() # output: 1,2,3,4,5,6 x[1][2] == x.storage()[5] # output: True

Tensor metadata: Size, offset, and stride

Để tensor lấy giá thấp trị từ storage thì mình cần 1 vài thông tin: size, offset và stride.

  • Offset là vị trí khai mạc lưu giá trị của tensor trong storage.
  • Size là kích thước của tensor.
  • Stride có số chiều bằng số chiều của Size, ý tức là cần nhảy bao nhiêu thành phần trong storage để được thành phần tiếp theo trong chiều đấy.

Như trong ví dụ dưới thì size hay shape, đó là kích thước ma trận (3×3). Offset = 1, tức là giá trị của tensor này lưu từ index 1 của storage thay vì index 0 như các ví dụ ở trên.

Stride = (3,1) ý là:

  • để lấy giá trị ở cột đấy nhưng ở hàng phía dưới, cần nhảy 3 thành phần trên storage, ví dụ: x[1][1] (=3) lưu ở index 5 trên storage, thì x[2][1] (=3) lưu ở vị trí 5 + 3 = 8 trên storage.
  • để lấy giá trị ở hàng đấy nhưng ở cột phụ cận, cần nhảy một phần tử trên storage , ví dụ: x[1][1] (=3) lưu ở index 5 trên storage, thì x[1][2] (=2) lưu ở vị trí 5 + 1 = 6 trên storage.

Rõ ràng có một storage và biết được những chỉ số size, offset, stride sẽ lấy lấy được những thành phần trong tensor.

Thành phần x[i][j] sẽ tương ứng với storage[offset +stride[0] * i + stride[1] * j].

Vì sao cần nhiều thông tin như vậy? Tưởng ở trên chỉ có mỗi số cột là lấy được hết các giá trị của tensor. Lời giải đáp là để sở hữu thể lưu nhiều tensor cùng trên 1 storage. Cùng xem ví dụ về transpose tensor ở dưới.

Transposing tensor

Torch tensor x và x_t (transpose) sẽ dùng chung 1 storage thay vì phải copy ra 1 vùng nhớ khác.

x = torch.tensor([[3, 1, 2], [4, 1, 7]]) x_t = x.t() # Viết gọn cho x.transpose(0, 1) id(x.storage()) == id(x_t.storage()) # output: True. Hàm id trả về địa chỉ của x.storage(), mình thấy là x và x_t có cùng storage.

Ví dụ trên mình thấy là x và x_t dùng chung 1 storage. Tính chất offset cả hai đều bằng 0, size thì khác nhau, displaystyle Ainmathbb{R}^{2times 3}, A^Tinmathbb{R}^{3times 2}

Xem Thêm : Nyongtory Là Gì ? 50 Điều Về Nyongtory (I)

x.stride() # (3,1) x_t.stride() # (1,3)

Và stride khác nhau, ở x thì mình cần nhảy 3 thành phần trong storage để đến vị trí cột đấy nhưng ở hàng dưới, x[0][0] = storage[0] = 3, x[1][0] = storage[3] = 4. Tuy nhiên, ở x_t thì mình chỉ có nhảy một phần tử trong storage để đến vị trí cột đấy nhưng ở hàng dưới, x_t[0][0] = storage[0] = 3, x_t[1][0] = storage[1] = 1.

Mình thực hiện phép tính transpose nhưng vẫn dùng chung storage. Ngoài ra, ví dụ như khi mọi người slicing chẳng hạn, thì để dùng chung storage mình sẽ cần thay đổi offset, size, stride.

Contiguous tensors

Một vài phép tính trong Torch tensors chỉ chạy trên contigous tensors, ví dụ view. Để kiểm tra xem tensor có contiguous không mình dùng hàm is_contiguous().

x.is_contiguous() # output: True x_t.is_contiguous() # output: False x.view(1, -1) # [3, 1, 2, 4, 1, 7] x_t.view(1, -1) # RuntimeError

Khi mình khởi tạo 1 tensor x thường nhật, thì những giá trị x sẽ tiến hành lữu trữ liên tục (theo từng hàng, hết hàng xuống hàng dưới) và x[i,j] sẽ tương ứng storage[offset+i*col+j] do đó x sẽ là contiguous tensor, còn khi mình thực hiện transpose thì x_t dùng chung storage với x nên trật tự index không còn được như mặc định, do đó x_t không phải contiguous tensor.

Mình có thể chuyển 1 tensor không phải contiguous tensor sang contigous tensor bằng hàm contiguous().

x_t_con = x_t.contiguous() x_t_con.is_contiguous() # Trả về True x_t_con.storage() # 3 4 1 1 2 7

Mình thấy là giá trị x_t_con và x_t là như nhau, tuy nhiên vùng storage khác nhau và stride sẽ khác nhau.

Torch GPU

Phần trước mình có nói về storage thì mặc định sẽ lưu ở CPU, tuy nhiên Torch được cho phép tensor lưu ở GPU để tính toán song song cũng như tăng tốc độ xử lý.

Nếu 1 tensor được lưu ở GPU, thì những phép tính toán sẽ tiến hành thực hiện ở GPU.

Để khởi tạo 1 tensor và lưu trên gpu thì mình dùng tính chất device.

x_gpu = torch.tensor([[4.0, 1.0], [5.0, 3.0], [2.0, 1.0]], device=’cuda’)

Hoặc mình có thể copy 1 tensor từ CPU sang GPU

x = torch.tensor([[4.0, 1.0], [5.0, 3.0], [2.0, 1.0]]) x_gpu = x.to(device=’cuda’)

Mỗi tensor chỉ được lưu trên 1 GPU nhất định nên nếu có nhiều GPU thì phải chỉ rõ lưu trên GPU nào, index GPU cũng khởi đầu từ 0.

x_gpu = x.to(device=’cuda:0′) # hoặc x_gpu = x.cuda(0) x_gpu = x_gpu + 4 # Thực hiện phép tính trên GPU

Để chuyển trái lại từ GPU về CPU thì mình dùng

x_cpu = x_gpu.to(device=’cpu’) # hoặc x_cpu = x_gpu.cpu()

Vậy là tôi đã đi qua tri thức cơ bản của Torch tensors, những bài sau mình sẽ dùng tensors để xây các mô hình neural network, CNN,…

Torch Tensor to Numpy Array

Torch được cho phép chuyển tensor sang Numpy array. Các tính chất về size, shape sẽ tiến hành giữ nguyên, type sẽ chuyển từ Torch sang Numpy.

x = torch.tensor([1,2,3]) x_np = x.numpy()

Nếu tensor được lưu trên CPU, Torch tensor và Numpy array sẽ dùng chung vùng nhớ, nên thay đổi giá trị tại 1 biến thì giá trị biến còn sót lại cũng thay đổi.

x[1] = 0 print(x) # output: [1, 0, 3] print(x_np) # output: [1, 0, 3]

Nếu tensor được lưu trên GPU thì mọi người sẽ không còn thể chuyển trực tiếp tensor sang Numpy array được, mà mình cần copy nội dung của tensor sang CPU trước rồi mới chuyển sang Numpy array. Do đó 2 biến trên gpu và np không dùng chung vùng nhớ và sửa 1 biến không tác động ảnh hưởng biến còn sót lại.

x_gpu = torch.tensor([1, 2, 3], device=’cuda’) x_np = x_gpu.numpy() # Error x_np = x_gpu.cpu().numpy() # ok x_gpu[1] = 0 print(x_gpu) # output: [1, 0, 3] print(x_np) # output: [1, 2, 3]

Tương tự, mình có thể chuyển Numpy array sang Torch tensor. Torch tensor sẽ lưu ở CPU và 2 biến trên np và cpu sẽ dùng chung vùng nhớ.

x_np = np.array([1, 2, 3]) x_cpu = torch.from_numpy(x_np)

Vậy là bài này tôi đã học các tri thức cơ bản của Torch Tensors, bài sau mình sẽ học về autograd trong tensors.

Code bài này mọi người lấy ở đây.

Tags: contiguous tensortensortensor gputensor metadatatorchtorch storagetorch tensortranspose

You May Also Like

About the Author: v1000