Chào mừng các bạn đến với bài viết thứ tám trong loạt bài về Lập Trình Neural Network Với Pytorch. Trong bài viết này, chúng ta sẽ xem xét kỹ cách tạo tensors pytorch và sự khác biệt giữa các cách để chuyển đổi dữ liệu thành tensors trong PyTorch. Đến cuối bài viết chúng ta sẽ biết sự khác biệt giữa các tùy chọn cũng như tùy chọn nào nên được sử dụng và sử dụng khi nào.
Bắt đầu thôi!
Các tensors PyTorch như chúng ta đã thấy là các đối tượng của lớp torch.Tensor. Sự khác biệt giữa khái niệm về tensor và tensor PyTorch đó là tensor PyTorch cung cấp cho chúng ta một triển khai cụ thể mà chúng ta có thể làm việc với code.
Trong bài viết trước, chúng ta đã biết cách tạo tensor trong PyTorch bằng cách sử dụng dữ liệu như list Python, chuỗi và ndarrays NumPy. Với một numpy.ndarray, chúng ta thấy rằng có bốn cách để tạo một đối tượng torch.Tensor.Như dưới đây:
> data = np.array([1,2,3]) > type(data) numpy.ndarray > o1 = torch.Tensor(data) > o2 = torch.tensor(data) > o3 = torch.as_tensor(data) > o4 = torch.from_numpy(data)
> print(o1) tensor([1., 2., 3.])
> print(o2) tensor([1, 2, 3], dtype=torch.int32)
> print(o3) tensor([1, 2, 3], dtype=torch.int32)
> print(o4) tensor([1, 2, 3], dtype=torch.int32)
Nhiệm vụ của chúng ta trong bài này là khám phá sự khác biệt giữa 4 cách này và đề xuất một cách tốt nhất cho nhu cầu tạo tensor của chúng ta.
Numpy dtype trên các hệ thống khác nhau
Numpy đặt kiểu dtype mặc định của nó dựa trên việc nó đang chạy trên hệ thống 32 bit hay 64 bit và hành vi cũng khác nhau trên các hệ thống Windows khác nhau.
Liên kết này cung cấp thêm thông tin về sự khác biệt được thấy trên hệ thống Windows. Các phương thức bị ảnh hưởng là: tensor, as_tensor và from_numpy.
Sự khác nhau giữa các tùy chọn tạo tensor là gì?
Chữ hoa/chữ thường (Uppercase/lowercase): torch.Tensor() và torch.tensor()
Để ý thấy tùy chọn đầu tiên torch.Tensor() có chữ T viết hoa trong khi tùy chọn thứ hai torch.tensor() có chữ t viết thường. Vậy sự khác biệt ở đây là gì?
Tùy chọn đầu tiên với chữ T hoa là constructor của lớp torch.Tensor, và tùy chọn thứ hai là tùy chọn mà chúng ta gọi là một factory function có chức năng xây dựng các đối tượng torch.Tensor và trả chúng cho người gọi.
Bạn có thể nghĩ về hàm torch.tensor() như một nhà máy tạo ra các tensor với một số tham số đầu vào. Factory functions là một mẫu thiết kế phần mềm để tạo các đối tượng. Nếu bạn muốn đọc thêm về nó, hãy kiểm tra ở đây.
Đó là sự khác biệt giữa chữ T hoa và chữ t viết thường, nhưng cách nào tốt hơn giữa hai chữ này? Câu trả lời là bạn có thể sử dụng một trong hai. Tuy nhiên, hàm factory function torch.tensor() có tài liệu hướng dẫn tốt hơn và nhiều tùy chọn cấu hình hơn, vì vậy nó được khuyên dùng nhiều hơn.
Default dtype Và Inferred dtype
Trước khi chúng ta loại bỏ hàm torch.Tensor() khỏi danh sách các tùy chọn ưu tiên sử dụng, hãy xem sự khác biệt mà chúng ta quan sát được trong các đầu ra của tensor.
Sự khác biệt là ở loại dtype của mỗi tensor:
> print(o1.dtype) torch.float32 > print(o2.dtype) torch.int32 > print(o3.dtype) torch.int32 > print(o4.dtype) torch.int32
Sự khác biệt ở đây là hàm tạo torch.Tensor() sử dụng loại dtype mặc định khi xây dựng tensor. Chúng ta có thể xác minh dtype mặc định bằng phương thức torch.get_default_dtype().
> torch.get_default_dtype() torch.float32
Để xác minh bằng code, chúng ta có thể làm như sau:
> o1.dtype == torch.get_default_dtype() True
Các tùy chọn khác chọn dtype dựa trên dữ liệu đến. Đây được gọi là type inference, là loại dtype được suy ra dựa trên dữ liệu đến.Lưu ý rằng dtype cũng có thể được chỉ định rõ ràng bằng cách chỉ định loại dtype làm đối số:
> torch.tensor(data, dtype=torch.float32) > torch.as_tensor(data, dtype=torch.float32)
Với torch.Tensor(), chúng ta không thể thay đổi dtype mặc định. Đây là một ví dụ cho thấy hàm torch.Tensor() không linh động trong việc tùy chọn cấu hình, đó cũng là một trong những lý do chúng ta nên sử dụng factory function torch.tensor() để tạo tensor trong pytorch.
Sharing Memory For Performance: Copy Vs Share
Để thấy sự khác nhau, chúng ta sẽ thực hiện thay đổi đối với dữ liệu đầu vào ban đầu trong numpy.ndarray sau khi sử dụng ndarray để tạo tensor.
> print('old:', data) old: [1 2 3] > data[0] = 0 > print('new:', data) new: [0 2 3] > print(o1) tensor([1., 2., 3.]) > print(o2) tensor([1, 2, 3], dtype=torch.int32) > print(o3) tensor([0, 2, 3], dtype=torch.int32) > print(o4) tensor([0, 2, 3], dtype=torch.int32)
Có thể thấy, ban đầu chúng ta có data[0]=1, lưu ý rằng chúng ta chỉ thay đổi dữ liệu trong numpy.ndarray ban đầu. Chứ không thực hiện bất kỳ thay đổi nào đối với các tensors của chúng ta (o1, o2, o3, o4).
Tuy nhiên, sau khi thiết lập data[0]=0 ta có thể thấy một số tensor đã có sự thay đổi. o1 và o2 vẫn có giá trị ban đầu là 1 cho index = 0, trong khi hai đó o3 và o4 có giá trị mới là 0 cho index = 0.
Điều này xảy ra vì torch.Tensor() và torch.tensor() sao chép dữ liệu đầu vào của chúng trong khi torch.as_tensor() và torch.from_numpy() lại chia sẻ dữ liệu đầu vào của chúng trong bộ nhớ với đối tượng đầu vào ban đầu.
Share Data | Copy Data |
torch.as_tensor() | torch.tensor() |
torch.from_numpy() | torch.Tensor() |
Việc chia sẻ (share data) này có nghĩa là dữ liệu thực tế trong bộ nhớ tồn tại ở một nơi duy nhất. Do đó, bất kỳ thay đổi nào xảy ra trong dữ liệu sẽ được thay đổi trong cả hai đối tượng, torch.Tensor và numpy.ndarray.
Chia sẻ dữ liệu hiệu quả hơn và sử dụng ít bộ nhớ hơn so với sao chép dữ liệu vì dữ liệu không được ghi vào hai vị trí trong bộ nhớ.
Nếu chúng ta có một torch.Tensor và chúng ta muốn chuyển nó thành numpy.ndarray, chúng ta làm như sau:
> print(o3.numpy()) [0 2 3] > print(o4.numpy()) [0 2 3]
Kiểm tra:
> print(type(o3.numpy())) <class 'numpy.ndarray'> > print(type(o4.numpy())) <class 'numpy.ndarray'>
Điều này chỉ ra rằng torch.as_tensor() và torch.from_numpy() đều chia sẻ bộ nhớ với dữ liệu đầu vào của chúng. Tuy nhiên, chúng ta nên sử dụng cái nào, và chúng khác nhau như thế nào?
Hàm torch.from_numpy() chỉ chấp nhận numpy.ndarrays, trong khi hàm torch.as_tensor() chấp nhận nhiều đối tượng giống mảng bao gồm các tensors PyTorch khác. Vì lý do này, torch.as_tensor() là lựa chọn thích hợp nhất để sử dụng.
Các tùy chọn tốt nhất để tạo tensor Pytorch.
Với tất cả những gì đã trình bày ở trên, chúng ta có thể kết luận các tùy chọn sau đây là tốt nhất để sử dụng:
- torch.tensor()
- torch.as_tensor()
Một số điều cần lưu ý về chia sẻ bộ nhớ:
Vì các đối tượng numpy.ndarray được phân bổ trên CPU, nên hàm as_tensor() phải sao chép dữ liệu từ CPU sang GPU khi GPU đang được sử dụng.
Chia sẻ bộ nhớ của as_tensor() không hoạt động với các cấu trúc dữ liệu Python tích hợp sẵn như list.
Lệnh gọi as_tensor() yêu cầu nhà phát triển phải có kiến thức về tính năng chia sẻ. Điều này là cần thiết để chúng ta không vô tình thực hiện một thay đổi không mong muốn đối với dữ liệu mà không nhận ra rằng thay đổi này sẽ ảnh hưởng đến nhiều đối tượng.
Việc cải thiện hiệu suất as_tensor() sẽ lớn hơn nếu có nhiều hoạt động qua lại giữa các đối tượng numpy.ndarray và đối tượng tensor. Tuy nhiên, nếu chỉ có một hoạt động tải duy nhất, thì sẽ không có nhiều tác động từ góc độ hiệu suất.
Kết Luận
Bài viết này đã giúp chúng ta hiểu rõ hơn về các tùy chọn tạo tensor pytorch. Chúng ta đã tìm hiểu về factory functions, đã thấy cách chia sẻ bộ nhớ và sao chép có thể tác động đến hiệu suất của chương trình. Hi vọng bạn thấy thích bài viết này. Hẹn gặp bạn trong các bài viết tiếp theo!