Cách đọc từ bộ dữ liệu IO cao trong pytorch phát triển từ epoch sang epoch


8

Tôi sử dụng Tensorflow, nhưng tôi đang viết tài liệu cho người dùng thường sẽ thay đổi theo các khung học tập sâu .

Khi làm việc với các bộ dữ liệu không phù hợp với hệ thống tệp cục bộ (TB +), tôi lấy mẫu dữ liệu từ kho lưu trữ dữ liệu từ xa và ghi mẫu cục bộ sang tfrecordsđịnh dạng chuẩn Tensorflow .

Trong kỷ nguyên đào tạo đầu tiên tôi sẽ chỉ lấy mẫu một vài giá trị, do đó, một kỷ nguyên dữ liệu cục bộ là rất nhỏ, tôi huấn luyện về nó. Trong epoch 2, tôi kiểm tra lại những tập tin dữ liệu nào đã được tạo ra bởi các quy trình lấy mẫu của tôi (bây giờ nhiều hơn) và huấn luyện về tập hợp mở rộng của các tệp dữ liệu cục bộ cho kỷ nguyên tiếp theo. Lặp lại quá trình mỗi kỷ nguyên. Theo cách này, tôi xây dựng bộ đệm mẫu cục bộ và có thể đuổi các mẫu cũ hơn khi tôi lấp đầy bộ nhớ cục bộ. Bộ đệm mẫu cục bộ phát triển vào khoảng thời gian mô hình cần phương sai nhiều nhất (hướng tới phần sau của đào tạo).

Trong Python / Tensorflow, điều quan trọng là tôi không khử lưu lượng dữ liệu trong quy trình vòng lặp đào tạo Python vì Python GIL không thể hỗ trợ tốc độ truyền dữ liệu (300-600 MB / giây, dữ liệu không thể nén được về mặt khoa học) và do đó hiệu suất của GPU chịu đựng khi Python GIL không thể phục vụ vòng lặp đào tạo nhanh.

Việc viết các mẫu vào một tfrecordstệp từ các quy trình con (đa xử lý python) cho phép nguồn gốc của tenorflow thực hiện quá trình khử lưu huỳnh TFRecordsDatasetbên ngoài Python và do đó chúng tôi vượt qua các vấn đề GIL của Python và tôi có thể bão hòa GPU với tốc độ dữ liệu IO cao.

Tôi muốn biết làm thế nào tôi sẽ giải quyết vấn đề này ở Pytorch. Tôi đang viết về chiến lược lấy mẫu đang được sử dụng và muốn cung cấp các đề xuất cụ thể cho người dùng của cả Tensorflow và PyTorch, nhưng tôi không biết rõ về hệ sinh thái tiền xử lý PyTorch đủ để viết đủ chi tiết.

Lưu ý bên lề: giải pháp hoàn toàn dựa trên Python để hỗ trợ các tốc độ truyền dữ liệu này có thể có trong Python 3.8 với bộ nhớ chia sẻ và xử lý đa hệ thống V, nhưng tôi chưa thử vì hỗ trợ cho nó không đủ (sẽ sớm thôi ). Các giải pháp đa xử lý hiện tại không đủ vì chúng yêu cầu khử lưu huỳnh trong quy trình vòng lặp đào tạo và do đó khóa GIL trong quá trình khử lưu huỳnh ở tốc độ IO cao.


2
Làm thế nào để bạn biết tốc độ truyền dữ liệu bị ảnh hưởng bởi Python GIL? Theo hiểu biết tốt nhất của tôi, đó là hoạt động ràng buộc CPU bị ảnh hưởng bởi GIL trong hầu hết các trường hợp, không phải hoạt động ràng buộc I / O.
bom

Trong thử nghiệm của tôi, chỉ cần thực hiện khử lưu huỳnh giữa các quy trình Python với tốc độ dữ liệu nhanh nhất tôi có thể đạt được sẽ giữ quá trình mục tiêu ở mức sử dụng CPU 100%. Tôi đã thử nhiều cách tiếp cận, asyncio, đa xử lý, thậm chí đọc ổ cắm trực tiếp. Trong trường hợp ổ cắm trực tiếp đọc, tôi có thể nhận được 4GB / giây qua các quy trình và ngay khi tôi cố gắng tham gia chuỗi nhị phân tôi giảm xuống còn 2GB / giây, và bất cứ điều gì phức tạp hơn đều giảm tôi xuống tốc độ tối đa khoảng 1GB / giây. Đó là tất cả với quy trình mục tiêu sử dụng hoàn toàn lõi và do đó khóa GIL.
David park

Lưu ý rằng đây thực sự không phải là vấn đề với các bộ dữ liệu lớn phổ biến như fantenet vì IO cần để di chuyển JPEG nén trên các mạng thần kinh lớn là nhỏ so với những gì đào tạo dữ liệu khoa học không nén trên các mạng nhỏ.
David park

1
một chuỗi nối được phân loại thành một hoạt động ràng buộc CPU và nó có thể dễ dàng yêu cầu 100% dung lượng CPU mà không cần sử dụng dung lượng I / O của máy. Vì vậy, đó không phải là bằng chứng cho thấy GIL hạn chế thông lượng I / O.
bom

2
Các hoạt động tầm thường đó không yêu cầu GIL của quy trình chính nếu dữ liệu được tải theo DataLoadernhư trong câu trả lời của tôi.
bom

Câu trả lời:


7

Trên thực tế, bạn có thể dễ dàng khử lưu lượng dữ liệu trong một quy trình con bằng cách sử dụng torch.utils.data.DataLoader. Bằng cách đặt num_workersđối số thành 1 hoặc giá trị lớn hơn, bạn có thể sinh ra các quy trình con với trình thông dịch python và GIL của riêng chúng.

loader = torch.utils.data.DataLoader(your_dataset, num_workers=n, **kwargs)
for epoch in range(epochs):
    for batch_idx, data in enumerate(loader):
         # loader in the main process does not claim GIL at this point

A Dataloaderyêu cầu a torch.utils.data.Datasetđể có được dữ liệu từ. Nó có thể không phải là một công việc tầm thường để thực hiện một lớp con thích hợp trong trường hợp của bạn. Trong trường hợp bạn cần tạo lại một Datasetthể hiện cho mỗi kỷ nguyên, bạn có thể làm một cái gì đó như thế này.

for epcoh in range(epochs):
    dset = get_new_dataset()
    loader = torch.utils.data.DataLoader(dset, num_workers=n, **kwargs)
    for batch_idx, data in enumerate(loader):
        # Do training

hoặc thậm chí tốt hơn

dset = get_new_dataset()
loader = torch.utils.data.DataLoader(dset, num_workers=n, **kwargs)

for epcoh in range(epochs):
    last_batch_idx =  (len(dset)-1) // loader.batch_size
    for batch_idx, data in enumerate(loader):
        # Prepare next loader in advance to avoid blocking
        if batch_idx == last_batch_idx:
            dset = get_new_dataset()
            loader = torch.utils.data.DataLoader(dset, num_workers=n, **kwargs)
        # Do training

Một lưu ý phụ, xin lưu ý rằng hoạt động bị ràng buộc bởi CPU bị ảnh hưởng bởi GIL trong hầu hết các trường hợp, chứ không phải hoạt động ràng buộc I / O, nghĩa là threadingsẽ thực hiện cho mọi hoạt động nặng nề của I / O và bạn thậm chí không cần subprocess. Để biết thêm thông tin xin vui lòng tham khảo câu hỏi này và bài viết trên wikipedia này .


Chỉ cần xác nhận, liệu có torch.utils.data.DataLoaderđặt dữ liệu trên GPU từ các quy trình con hay nó đang cố gắng sử dụng đa xử lý của python để chuyển nó sang quy trình vòng lặp đào tạo? Tôi đã thấy rằng chỉ cần khử lưu lượng từ quy trình này sang quy trình khác với tốc độ dữ liệu đạt tới 1GB / giây là> 1 lõi công việc, do đó, các vấn đề GIL tôi gặp phải khi thử phương pháp này trong TF. Nhưng nếu việc torch.utils.data.DataLoaderdi chuyển dữ liệu lên GPU theo cách không yêu cầu khử tuần hoàn Python thì tất cả đều ổn. Tôi chỉ muốn xác nhận sự hiểu biết đó.
David park

@DavidParks Bạn sử dụng chức năng cụ thể nào khi bạn đang thử nghiệm khử lưu huỳnh từ quy trình này sang quy trình khác? có vẻ như quá trình khử lưu huỳnh liên quan đến hoạt động ràng buộc của CPU, do đó các vấn đề về GIL.
bom

Tôi đã thử đa xử lý (rất chậm), ống (tốt hơn) và ổ cắm thô đọc (tốt nhất). Không có cái nào trong số này hoạt động khi tốc độ IO là một phần đáng kể của GB / giây, chỉ cần di chuyển nhiều dữ liệu đó cần nhiều hơn 1 lõi, và do đó các giải pháp Python (trước bộ nhớ chia sẻ 3,8 và System V) bị tách ra đối với tôi trong Tensorflow. Đó là lý do tại sao tôi viết vào các tệp tfrecords và để cho tenorflow thực hiện khử lưu huỳnh bên ngoài Python. TF không khóa Python GIL và song song hóa các hoạt động, vì vậy quy trình chính của tôi sử dụng 600% CPU trong khi Python GIL không hoạt động và miễn phí để phục vụ vòng lặp đào tạo.
David park

@DavidParks Ý tôi là, bạn sử dụng loại chức năng khử thư viện hoặc thư viện nào? (không phải thư viện truyền thông liên tiến trình). torch.utils.data.DataLoadercó thể dễ dàng sử dụng 600% CPU trở lên và quy trình chính không cần nhiều năng lượng CPU trong hầu hết các trường hợp khi đào tạo chủ yếu là tính toán GPU (Khi đào tạo chủ yếu là tính toán CPU, vẫn không có vấn đề gì vì hoạt động ma trận của pytorch có thể dễ dàng sử dụng nhiều CPU).
bom

Chỉ cần sử dụng dưa chua để khử lưu huỳnh trên các quy trình của python, sau đó là chức năng tạo python để đưa các mẫu vào hệ sinh thái TensorFlow. Đó là cách tiếp cận thất bại đối với tôi. Sau khi dữ liệu được đưa vào hệ sinh thái TensorFlow, nó sẽ được đặt trên GPU và đào tạo là một câu chuyện khác. TF không cung cấp cách cho các quy trình con python cung cấp dữ liệu cho TF, bạn chỉ có một vài tùy chọn và dữ liệu được định dạng tfrecords (định dạng Bộ đệm giao thức) là hợp lý nhất. Nghe có vẻ dễ dàng hơn trong PyTorch, vì vậy tôi sẽ có một số người dùng PyTorch ở đây xác thực nó.
David park
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.