Datasets documentation
Sử dụng với PyTorch
Sử dụng với PyTorch
Tài liệu này là phần giới thiệu nhanh về cách sử dụng datasets với PyTorch, tập trung đặc biệt vào cách lấy các đối tượng torch.Tensor từ các dataset của chúng ta, và cách sử dụng DataLoader của PyTorch cùng với Dataset của Hugging Face để đạt hiệu suất tốt nhất.
Định dạng tập dữ liệu
Mặc định, các tập dữ liệu trả về các đối tượng python thông thường: số nguyên, số thực, chuỗi, danh sách, v.v.
Để nhận các tensor PyTorch thay thế, bạn có thể đặt định dạng của dataset thành pytorch bằng cách sử dụng Dataset.with_format():
>>> from datasets import Dataset
>>> data = [[1, 2],[3, 4]]
>>> ds = Dataset.from_dict({"data": data})
>>> ds = ds.with_format("torch")
>>> ds[0]
{'data': tensor([1, 2])}
>>> ds[:2]
{'data': tensor([[1, 2],
[3, 4]])}Một đối tượng Dataset là lớp bọc của một bảng Arrow, cho phép đọc nhanh không sao chép (zero-copy) từ các mảng trong bộ dữ liệu sang các tensor PyTorch.
Để tải dữ liệu dưới dạng tensor trên GPU, hãy chỉ định đối số device:
>>> import torch
>>> device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
>>> ds = ds.with_format("torch", device=device)
>>> ds[0]
{'data': tensor([1, 2], device='cuda:0')}Mảng N chiều
Nếu tập dữ liệu của bạn bao gồm các mảng N chiều, bạn sẽ thấy rằng mặc định chúng được coi là cùng một tensor nếu hình dạng được cố định:
>>> from datasets import Dataset
>>> data = [[[1, 2],[3, 4]],[[5, 6],[7, 8]]] # fixed shape
>>> ds = Dataset.from_dict({"data": data})
>>> ds = ds.with_format("torch")
>>> ds[0]
{'data': tensor([[1, 2],
[3, 4]])}>>> from datasets import Dataset
>>> data = [[[1, 2],[3]],[[4, 5, 6],[7, 8]]] # varying shape
>>> ds = Dataset.from_dict({"data": data})
>>> ds = ds.with_format("torch")
>>> ds[0]
{'data': [tensor([1, 2]), tensor([3])]}Tuy nhiên, logic này thường đòi hỏi các phép so sánh shape và sao chép dữ liệu chậm.
Để tránh điều này, bạn phải sử dụng rõ ràng kiểu đặc trưng Array và chỉ định shape của các tensor của bạn:
>>> from datasets import Dataset, Features, Array2D
>>> data = [[[1, 2],[3, 4]],[[5, 6],[7, 8]]]
>>> features = Features({"data": Array2D(shape=(2, 2), dtype='int32')})
>>> ds = Dataset.from_dict({"data": data}, features=features)
>>> ds = ds.with_format("torch")
>>> ds[0]
{'data': tensor([[1, 2],
[3, 4]])}
>>> ds[:2]
{'data': tensor([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])}Các loại tính năng khác
Dữ liệu ClassLabel được chuyển đổi thành tensor một cách chính xác:
>>> from datasets import Dataset, Features, ClassLabel
>>> labels = [0, 0, 1]
>>> features = Features({"label": ClassLabel(names=["negative", "positive"])})
>>> ds = Dataset.from_dict({"label": labels}, features=features)
>>> ds = ds.with_format("torch")
>>> ds[:3]
{'label': tensor([0, 0, 1])}Các đối tượng chuỗi và nhị phân không thay đổi, vì PyTorch chỉ hỗ trợ các số.
Các loại tính năng Image và Audio cũng được hỗ trợ.
Để sử dụng kiểu tính năng Image, bạn cần cài đặt phần mở rộng
visionbằng lệnhpip install datasets[vision].
>>> from datasets import Dataset, Features, Audio, Image
>>> images = ["path/to/image.png"] * 10
>>> features = Features({"image": Image()})
>>> ds = Dataset.from_dict({"image": images}, features=features)
>>> ds = ds.with_format("torch")
>>> ds[0]["image"].shape
torch.Size([512, 512, 4])
>>> ds[0]
{'image': tensor([[[255, 215, 106, 255],
[255, 215, 106, 255],
...,
[255, 255, 255, 255],
[255, 255, 255, 255]]], dtype=torch.uint8)}
>>> ds[:2]["image"].shape
torch.Size([2, 512, 512, 4])
>>> ds[:2]
{'image': tensor([[[[255, 215, 106, 255],
[255, 215, 106, 255],
...,
[255, 255, 255, 255],
[255, 255, 255, 255]]]], dtype=torch.uint8)}Để sử dụng kiểu tính năng Audio, bạn cần cài đặt phần mở rộng
audiobằng lệnhpip install datasets[audio].
>>> from datasets import Dataset, Features, Audio, Image
>>> audio = ["path/to/audio.wav"] * 10
>>> features = Features({"audio": Audio()})
>>> ds = Dataset.from_dict({"audio": audio}, features=features)
>>> ds = ds.with_format("torch")
>>> ds[0]["audio"]["array"]
tensor([ 6.1035e-05, 1.5259e-05, 1.6785e-04, ..., -1.5259e-05,
-1.5259e-05, 1.5259e-05])
>>> ds[0]["audio"]["sampling_rate"]
tensor(44100)Tải dữ liệu
Tương tự như các đối tượng torch.utils.data.Dataset, một Dataset có thể được truyền trực tiếp vào một DataLoader của PyTorch:
>>> import numpy as np
>>> from datasets import Dataset
>>> from torch.utils.data import DataLoader
>>> data = np.random.rand(16)
>>> label = np.random.randint(0, 2, size=16)
>>> ds = Dataset.from_dict({"data": data, "label": label}).with_format("torch")
>>> dataloader = DataLoader(ds, batch_size=4)
>>> for batch in dataloader:
... print(batch)
{'data': tensor([0.0047, 0.4979, 0.6726, 0.8105]), 'label': tensor([0, 1, 0, 1])}
{'data': tensor([0.4832, 0.2723, 0.4259, 0.2224]), 'label': tensor([0, 0, 0, 0])}
{'data': tensor([0.5837, 0.3444, 0.4658, 0.6417]), 'label': tensor([0, 1, 0, 0])}
{'data': tensor([0.7022, 0.1225, 0.7228, 0.8259]), 'label': tensor([1, 1, 1, 1])}Tối ưu hóa việc tải dữ liệu
Có một số cách bạn có thể tăng tốc độ tải dữ liệu, giúp tiết kiệm thời gian, đặc biệt khi làm việc với các bộ dữ liệu lớn. PyTorch cung cấp việc tải dữ liệu song song hóa, lấy các lô chỉ mục thay vì từng cái một, và streaming để lặp qua bộ dữ liệu mà không cần tải xuống đĩa.
Sử dụng nhiều Worker
Bạn có thể song song hóa việc tải dữ liệu bằng đối số num_workers của DataLoader PyTorch và đạt được thông lượng cao hơn.
Bên trong, DataLoader khởi động num_workers tiến trình.
Mỗi tiến trình nạp lại bộ dữ liệu được truyền vào DataLoader và được dùng để truy vấn các ví dụ.
Việc nạp lại bộ dữ liệu bên trong một worker không làm đầy RAM của bạn, vì nó chỉ ánh xạ bộ nhớ bộ dữ liệu lại từ ổ đĩa của bạn.
>>> import numpy as np
>>> from datasets import Dataset, load_from_disk
>>> from torch.utils.data import DataLoader
>>> data = np.random.rand(10_000)
>>> Dataset.from_dict({"data": data}).save_to_disk("my_dataset")
>>> ds = load_from_disk("my_dataset").with_format("torch")
>>> dataloader = DataLoader(ds, batch_size=32, num_workers=4)Dòng dữ liệu
Dòng chảy một dataset bằng cách tải nó dưới dạng IterableDataset. Điều này cho phép bạn lặp qua một dataset từ xa một cách tuần tự mà không cần tải nó xuống đĩa, hoặc lặp qua các tệp dữ liệu cục bộ. Tìm hiểu thêm về loại dataset nào phù hợp nhất cho trường hợp sử dụng của bạn trong hướng dẫn chọn giữa dataset thông thường và dataset dạng iterable.
Một tập dữ liệu có thể lặp lại từ datasets kế thừa từ torch.utils.data.IterableDataset nên bạn có thể truyền nó vào một torch.utils.data.DataLoader:
>>> import numpy as np
>>> from datasets import Dataset, load_dataset
>>> from torch.utils.data import DataLoader
>>> data = np.random.rand(10_000)
>>> Dataset.from_dict({"data": data}).push_to_hub("<username>/my_dataset") # Upload to the Hugging Face Hub
>>> my_iterable_dataset = load_dataset("<username>/my_dataset", streaming=True, split="train")
>>> dataloader = DataLoader(my_iterable_dataset, batch_size=32)Nếu tập dữ liệu được chia thành nhiều phần (tức là tập dữ liệu bao gồm nhiều tệp dữ liệu), thì bạn có thể stream song song bằng cách sử dụng num_workers:
>>> my_iterable_dataset = load_dataset("deepmind/code_contests", streaming=True, split="train")
>>> my_iterable_dataset.num_shards
39
>>> dataloader = DataLoader(my_iterable_dataset, batch_size=32, num_workers=4)Trong trường hợp này, mỗi worker được giao một tập con của danh sách các shard để stream từ đó.
Kiểm tra điểm và tiếp tục
Nếu bạn cần một DataLoader có thể checkpoint và tiếp tục ở giữa quá trình huấn luyện, bạn có thể sử dụng StatefulDataLoader từ torchdata:
>>> from torchdata.stateful_dataloader import StatefulDataLoader
>>> my_iterable_dataset = load_dataset("deepmind/code_contests", streaming=True, split="train")
>>> dataloader = StatefulDataLoader(my_iterable_dataset, batch_size=32, num_workers=4)
>>> # save in the middle of training
>>> state_dict = dataloader.state_dict()
>>> # and resume later
>>> dataloader.load_state_dict(state_dict)Điều này có thể thực hiện được nhờ IterableDataset.state_dict() và IterableDataset.load_state_dict().
Phân tán
Để chia bộ dữ liệu của bạn trên các nút huấn luyện, bạn có thể sử dụng datasets.distributed.split_dataset_by_node():
import os
from datasets.distributed import split_dataset_by_node
ds = split_dataset_by_node(ds, rank=int(os.environ["RANK"]), world_size=int(os.environ["WORLD_SIZE"]))Điều này hoạt động cho cả dataset kiểu map và dataset kiểu iterable.
Dataset được chia cho node có rank rank trong một nhóm các node có kích thước world_size.
Đối với các bộ dữ liệu theo phong cách map:
Mỗi node được gán một phần dữ liệu, ví dụ rank 0 nhận phần đầu tiên của tập dữ liệu.
Đối với các tập dữ liệu có thể lặp lại:
Chiến lược strategy phân tách có thể là "auto", "shards" hoặc "examples":
"shards": các shard được phân bổ đều giữa các node"examples": mỗi nút giữ lại 1 ví dụ trong sốworld_size, bỏ qua các ví dụ còn lại"auto"(mặc định): sử dụng"shards"nếu số lượng shards của dataset là một ước số củaworld_size(tức là nếudataset.num_shards % world_size == 0), đây là phương án được tối ưu nhất. Ngược lại, sử dụng"examples".
Điều này cũng có thể được kết hợp với torch.utils.data.DataLoader nếu bạn muốn mỗi node sử dụng nhiều worker để tải dữ liệu.
Update on GitHubNếu bạn xáo trộn bộ dữ liệu dạng lặp (iterable dataset) trong một cấu hình phân tán, hãy đảm bảo đặt một
seedcố định trong IterableDataset.shuffle() để danh sách các shard đã xáo trộn giống nhau được sử dụng trên mọi nút, từ đó biết được nút nào nên bỏ qua những shard nào.