Pytorch: تتسرب ذاكرة وحدة المعالجة المركزية تدريجياً عندما يكون num_workers> 0 في DataLoader

تم إنشاؤها على ٢٩ أكتوبر ٢٠١٨  ·  79تعليقات  ·  مصدر: pytorch/pytorch

ملاحظة المحرر: هناك حل بديل معروف أكثر في هذه المشكلة ، وهو عدم استخدام قوائم Python ، ولكن بدلاً من ذلك استخدام شيء آخر ، على سبيل المثال ، مصفوفة numpy أو الموتر مباشرة.

🐛 علة

ستتسرب ذاكرة وحدة المعالجة المركزية إذا كان DataLoader num_workers > 0 .

لإعادة إنتاج

قم بتشغيل المقتطف التالي:

from torch.utils.data import Dataset, DataLoader
from PIL import Image
from torchvision import transforms
import os

class DataIter(Dataset):
    def __init__(self):
        path = "path/to/data"
        self.data = []

        for cls in os.listdir(path):
            for img in os.listdir(os.path.join(path, cls)):
                self.data.append(os.path.join(path, cls, img))

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        with Image.open(self.data[idx]) as img:
            img = img.convert('RGB')
            return transforms.functional.to_tensor(img)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=18)

for i, item in enumerate(train_loader):
    if i % 200 == 0:
        print(i)

سلوك متوقع

ستبدأ ذاكرة وحدة المعالجة المركزية في الزيادة تدريجيًا ، مما يؤدي في النهاية إلى ملء ذاكرة الوصول العشوائي بالكامل. على سبيل المثال ، تبدأ العملية بحوالي 15 غيغابايت وتملأ مساحة 128 غيغابايت المتوفرة على النظام.
عندما يكون num_workers=0 ، يكون استخدام ذاكرة الوصول العشوائي ثابتًا.

بيئة

PyTorch version: 1.0.0.dev20181028
Is debug build: No
CUDA used to build PyTorch: 9.0.176

OS: Ubuntu 16.04.4 LTS
GCC version: (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
CMake version: version 3.5.1

Python version: 3.5
Is CUDA available: Yes
CUDA runtime version: 9.0.176
GPU models and configuration: 
GPU 0: GeForce GTX 1080 Ti
GPU 1: GeForce GTX 1080 Ti
GPU 2: GeForce GTX 1080 Ti

Nvidia driver version: 390.67
cuDNN version: Probably one of the following:
/usr/lib/x86_64-linux-gnu/libcudnn.so.7.1.4

Versions of relevant libraries:
[pip] Could not collect
[conda] Could not collect

PIL.__version__
'5.3.0'

معلومات إضافية

يوجد حوالي 24 مليون صورة في مجموعة البيانات ويتم تحميل جميع مسارات الصور في قائمة واحدة كما هو معروض في مقتطف الشفرة أعلاه.

لقد جربت أيضًا إصدارات متعددة من Pytorch (0.4.0 و 0.4.1) والتأثير هو نفسه.

ccezyanggchanan @ zou3519SsnL

high priority dataloader memory usage molly-guard multiprocessing triaged

التعليق الأكثر فائدة

بعد المزيد من التحقيقات ، وجدت سيناريو دقيقًا عند حدوث التسرب. ضع في اعتبارك مثال الكود أدناه:

from torch.utils.data import Dataset, DataLoader
import numpy as np
import torch


class DataIter(Dataset):
    def __init__(self):
        self.data_np = np.array([x for x in range(24000000)])
        self.data = [x for x in range(24000000)]

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        data = self.data[idx]
        data = np.array([data], dtype=np.int64)
        return torch.tensor(data)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=18)

for i, item in enumerate(train_loader):
    if i % 1000 == 0:
        print(i)

إذا استخدمنا المتغير self.data وهو قائمة بايثون قياسية من ints ، فسيحدث تسرب البيانات. ومع ذلك ، إذا تم استخدام المتغير self.data_np ، والذي يحتوي على نفس البيانات ولكن في شكل مصفوفة Numpy ، فلن يحدث التسرب.
ملاحظة أخرى هي أن التسرب يكون أقل حدة بشكل ملحوظ إذا كان shuffle=False في DataLoader .

ال 79 كومينتر

هل ترى زيادة استخدام الذاكرة عند التكرار ، أو حتى قبل أن تبدأ في التكرار؟

SsnL أثناء التكرار فقط.

عندما نصلح # 13243 ، يجب أن نتحقق مما إذا تم إصلاح هذا أيضًا.

لقد واجهت شيئًا مشابهًا حيث يرتفع استخدام الذاكرة باستمرار حتى يتم تشغيل OOM عند استخدام batch_sampler مع num_workers>0 .

لإعادة إنتاج

import math

from torch.utils.data import DataLoader


class Sampler:
    def __init__(self, n=100000, batch_size=32):
        self.n = n
        self.batch_size = batch_size

    def __len__(self):
        return math.ceil(float(self.n)/self.batch_size)

    def __iter__(self):
        batch = []
        for i in range(self.n):
            batch.append(i)
            if len(batch) == self.batch_size:
                yield batch
                batch = []
        if batch:
            yield batch


N = 100000000
train_data = list(range(N))


def ok():
    train_sampler = Sampler(len(train_data))
    train_loader = DataLoader(train_data,
                              num_workers=0,
                              batch_sampler=train_sampler)

    for i, item in enumerate(train_loader):
        if i % 10000 == 0:
            print(i)


def leaky():
    train_sampler = Sampler(len(train_data))
    train_loader = DataLoader(train_data,
                              num_workers=8,
                              batch_sampler=train_sampler)

    for i, item in enumerate(train_loader):
        if i % 10000 == 0:
            print(i)


print('Starting ok')
ok()
print('ok done, starting leaky()')
leaky()
print('leaky done')

بيئة

$ python3 collect_env.py
Collecting environment information...
PyTorch version: 0.4.0
Is debug build: No
CUDA used to build PyTorch: 9.1.85

OS: Ubuntu 16.04.5 LTS
GCC version: (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
CMake version: version 3.5.1

Python version: 3.5
Is CUDA available: Yes
CUDA runtime version: 9.1.85
GPU models and configuration: GPU 0: GeForce GTX 1050 Ti with Max-Q Design
Nvidia driver version: 390.77
cuDNN version: Probably one of the following:
/usr/lib/x86_64-linux-gnu/libcudnn.so.7.1.2
/usr/lib/x86_64-linux-gnu/libcudnn_static_v7.a

Versions of relevant libraries:
[pip] Could not collect
[conda] Could not collect

تضمين التغريدة

عندما نصلح # 13243 ، يجب أن نتحقق مما إذا تم إصلاح هذا أيضًا.

لا تزال المشكلة موجودة في 1.0.0.dev20181105 ، حيث تم إصلاح # 13243.

بعد المزيد من التحقيقات ، وجدت سيناريو دقيقًا عند حدوث التسرب. ضع في اعتبارك مثال الكود أدناه:

from torch.utils.data import Dataset, DataLoader
import numpy as np
import torch


class DataIter(Dataset):
    def __init__(self):
        self.data_np = np.array([x for x in range(24000000)])
        self.data = [x for x in range(24000000)]

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        data = self.data[idx]
        data = np.array([data], dtype=np.int64)
        return torch.tensor(data)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=18)

for i, item in enumerate(train_loader):
    if i % 1000 == 0:
        print(i)

إذا استخدمنا المتغير self.data وهو قائمة بايثون قياسية من ints ، فسيحدث تسرب البيانات. ومع ذلك ، إذا تم استخدام المتغير self.data_np ، والذي يحتوي على نفس البيانات ولكن في شكل مصفوفة Numpy ، فلن يحدث التسرب.
ملاحظة أخرى هي أن التسرب يكون أقل حدة بشكل ملحوظ إذا كان shuffle=False في DataLoader .

أواجه مشكلة مماثلة ، ولكن في حالتي تحدث مع مصفوفة numpy أيضًا. أنا أستخدم Python 3.7 و PyTorch للإصدار الليلي.

لا أعرف كيف تعمل المعالجة المتعددة بالفعل تحت غطاء pytorch ، لكننا ناقشنا على نطاق واسع مشكلة "تسرب الذاكرة" هذه (والتي ربما لا تكون تسربًا للذاكرة!) في منتديات fast.ai (https: // forums. fast.ai/t/runtimeerror-dataloader-worker-is-killed-by-signal/31277/55؟u=marcmuc). النتائج الأولية التي نأمل أن تضيف بعض الأفكار هنا (إذا لم ينطبق ذلك ، يرجى التعليق!):

المعالجة المتعددة لبايثون: لا توجد طريقة لتخزين كائنات بايثون عشوائية (حتى القوائم البسيطة) في الذاكرة المشتركة في بايثون دون تشغيل سلوك النسخ عند الكتابة بسبب إضافة أعداد refcounts ، في كل مرة يقرأ فيها شيء من هذه الكائنات. يتم إضافة عدد refcounts إلى صفحة ذاكرة بصفحة ذاكرة ، ولهذا السبب ينمو الاستهلاك ببطء. سينتهي الأمر بالعمليات (العمال) إلى نسخ كل / معظم الذاكرة شيئًا فشيئًا ، وهذا هو سبب حصولنا على مشكلة تجاوز الذاكرة. أفضل وصف لهذا السلوك هنا (SO).

حل ممكن:
استخدام المعالجة المتعددة كما هو الحال الآن: لكي تعمل المعالجة المتعددة بيثون بدون تأثيرات إعادة العد هذه ، يجب أن تكون الكائنات "متوافقة مع" وأن يتم تغليفها في multiprocessing.Array قبل إنشاء تجمع العملية والعاملين متشعبين. من المفترض أن يضمن هذا أن الذاكرة ستتم مشاركتها حقًا ولن يحدث نسخ عند الكتابة. يشرح هذا كيفية القيام بذلك للمصفوفات غير الدقيقة وهذا يفسر السبب وراء ذلك مرة أخرى. لا تخلط بين بعض العبارات الكاذبة حتى من قبل مؤلفي هذه الإجابات الجيدة التي تفيد بأن النسخ عند الكتابة يجعل كل هذا غير ضروري ، وهذا غير صحيح. يشير أحد التعليقات أيضًا إلى هذا:

"للإشارة فقط ، في Python fork () يعني في الواقع نسخ عند الوصول (لأن مجرد الوصول إلى الكائن سيغير عدد المرجع الخاص به)."

لست على دراية باستبدال الشعلة.

mprostock torch.multiprocessing هو ببساطة معالجة بيثون متعددة ، مع منتقي مخصص. المنتقي المخصص ، عندما يواجه torch.tensor ، سينقله تلقائيًا إلى الذاكرة المشتركة ، وبالتالي على الأقل على الكائنات torch.tensor ، لا يحدث نسخ عند الكتابة.

شكرا على الشرح! لقد جربت مثال استنساخ bfreskura وأعتقد أنه يمكنني الآن تحديد المشكلة:

أظهر مثال الاستنساخ من قبل bfreskura أعلاه الفرق بين قائمة الثعبان العادية والمصفوفة العقدية. لكن المشكلة ليست (فقط) قائمة بايثون نفسها ، نفس الشيء يحدث في مصفوفة عددية من كائن الكتابة. تخزن قوائم Python الإشارات إلى الكائنات فقط ، ويتم الاحتفاظ بالكائنات بشكل منفصل في الذاكرة. كل كائن له refcount ، لذلك كل عنصر في القائمة له refcount.

يتم تخزين المصفوفات Numpy (من أنواع np القياسية) ككتل متصلة في الذاكرة وتكون كائنًا واحدًا فقط مع عدد refcount واحد.

يتغير هذا إذا قمت بإجراء المصفوفة العقدية بشكل صريح لكائن الكتابة ، مما يجعلها تبدأ في التصرف كقائمة بايثون عادية (تخزين الإشارات فقط إلى كائنات (سلسلة)). تظهر الآن نفس "مشاكل" استهلاك الذاكرة.

هذا من شأنه أن يفسر سبب ظهور "تسرب الذاكرة" بالقوائم العادية (أو المصفوفات غير الدقيقة للكائن من النوع) ، والتي هي في الواقع مشكلة النسخ عند الوصول لعمليات الثعبان المتشعبة بسبب تغيير عمليات إعادة العد ، وليس تسرب الذاكرة.

لذلك ربما لا يكون للمشكلة (غالبًا) علاقة بالموترات أو كائنات الشعلة الفعلية ، بل تتعلق بقوائم أسماء الملفات وإملاءات الملصقات ، التي تُستخدم عمومًا في محمل البيانات / مجموعات البيانات.

لقد قمت بإنشاء جوهر دفتر ملاحظات ، إذا أراد شخص ما تجربته بسرعة.
انظر إلى استهلاك الذاكرة (ذاكرة سريعة وقذرة للنظام الكلي ، لذا فقد حاولت التأثيرات الطفيفة من العمليات الأخرى الحفاظ على نظافة النظام)

استهلاك الذاكرة بالجيجابايت مع مصفوفة سلسلة ذات طول ثابت:
image

استهلاك الذاكرة بالجيجابايت مع مجموعة الكائنات (التغيير فقط!)
image

أواجه نفس المشكلة. تملأ ذاكرة الوصول العشوائي الخاصة بي بسرعة كبيرة إذا كان عدد العمال> 0.
أقوم بحذف المتغيرات التي أشعر أنها لم تعد بحاجة إليها في الكود الخاص بي ، واستدعاء gc.collect () في كل تكرار ، ولكن لا شيء يساعد.
أي حلول؟

يساعدني التبديل من الديكت إلى الباندا ومن القوائم إلى المصفوفات المعقدة

أواجه نفس المشكلة. تملأ ذاكرة الوصول العشوائي الخاصة بي بسرعة كبيرة إذا كان عدد العمال> 0.
أقوم بحذف المتغيرات التي أشعر أنها لم تعد بحاجة إليها في الكود الخاص بي ، واستدعاء gc.collect () في كل تكرار ، ولكن لا شيء يساعد.
أي حلول؟

شكرا على الرد. سأحاول ذلك وآمل أن يعمل.

هل لي أن أسأل عن حل لهذه المشكلة؟ لقد جربت كود samgd في آخر يوم تم بناؤه pytorch ، وكان لا يزال يتسرب.

Godricly شاهد mprostock وتعليقات soumith أعلاه. هذا ليس تسريبًا حقًا ، ولكنه سلوك مؤسف لاستخدام قائمة لغة بيثون الأصلية. سيؤدي استخدام إما موتر الشعلة أو مجموعة np إلى حل مشكلة الذاكرة هذه.

mprostock هل تقصد أن النسخة التي تم إنشاؤها عن طريق النسخ عند الوصول تستهلك الذاكرة ، وليس شيئًا آخر؟ أليس إصدار النسخة بعد استخدامها؟

يحتاج شخص ما إلى تصعيد وكتابة عملية زيادة مناسبة لمجموعات بيانات الصور على الأقل. السبب الكامل لكل هذه الخدع متعددة المعالجات هو أن مجموعات بيانات الرؤية _ لديها_ لفك تشفير الصور واقتصاصها على نوى متعددة. إذا كانت هناك عملية اعتنت بفك التشفير وتحويلات الصور الهندسية (تغيير الحجم ، وتقليب المحاصيل ، والقص ، والترابط) ، وأنتجت موترات دفعة مباشرة ، فلن تكون هناك حاجة لاستخدام المعالجة المتعددة على الإطلاق ، وخطوات زيادة غير هندسية أخرى (الألوان ، التبييض / التطبيع ، الضوضاء) يمكن أن تستخدم التوازي داخل العملية لتمزيق الموتر بأكمله. يجب توخي الحذر عند تصميم مثل هذا المرجع لفضح معلمات التحويل لكل عينة في الموتر إلى الخارج ، من أجل تمكين التحويل المتوازي للتعليقات التوضيحية (المربعات المحيطة ، والأقنعة ، ونقاط المفاتيح ، إلخ).
أو الأفضل من ذلك ، اجعل هذا خادمًا ، بحيث يمكن للعمليات المتعددة (بالإضافة إلى أطر عمل DL الأخرى) استخدامه أيضًا.

mprostock شكرا لك على الشرح العظيم!

ومع ذلك ، لم يتم اقتراح أي حل حتى الآن. يبدو تخزين قوائم أسماء الملفات في كائن مجموعة البيانات عادلاً ، فكيف يمكن للمرء استخدامها؟ هل اكتشف أي شخص ذلك؟

@ 1e100 أعتقد أن fmassa تعمل على إضافة المزيد من عمليات تكبير الصور الأصلية إلى torchvision ، مما سيساعد في حل هذه المشكلة.

أي تحديث لهذه المشكلة؟

أدى استخدام الكثير من الذاكرة المشتركة إلى حل المشكلة بالنسبة لي. إليك اختراق لتعيين الذاكرة المشتركة داخل البرنامج النصي في حالة ما إذا كنت تقوم بتشغيل الكود داخل حاوية عامل إرساء وكنت غير قادر على تعيين الذاكرة المشتركة بطريقة أخرى:

os.system(f"mount -o remount,size={args.shared_memory_size} /dev/shm")

يمكن أن يكون حجم الذاكرة المشتركة على سبيل المثال نصف إجمالي ذاكرة الوصول العشوائي ، على سبيل المثال "80 جيجا" لجهاز biiig.

لقد وجدت حلاً بديلاً لطرح الخطأ unable to open shared memory object </torch_22291_1137042840> in read-write mode المرتبط بهذه المشكلة عن طريق تغيير عدد واصفات الملفات المسموح بها ، على الرغم من أن الذاكرة _ لا تزال _ تزحف إلى نقطة معينة.

للتحقق من العدد المسموح به من واصفات الملفات ، أدخل ulimit -a في bash وسيكون تحت علامة -n . لزيادة هذا الحد لصدفتك الحالية (على سبيل المثال ، إذا لم يكن لديك أذونات للخادم) ، قم بتشغيل ما يلي:
باش: ulimit -n NEW_VALUE

لتغييره للنظام بأكمله ، انظر هنا .

لذا ، إذا فهمت بشكل صحيح ، فإن العمليات العاملة تنشئ نسخة من القائمة الطويلة لمسارات الملفات في كل مرة يصلون فيها إلى القائمة؟ ولكن ، ألا تخرج هذه النسخة المؤقتة عن النطاق (وبالتالي يتم إتلافها) بمجرد عودة الوظيفة __getitem__ لهذه العملية؟ لماذا يزداد استهلاك ذاكرة الوصول العشوائي بدون حدود؟

سيكون من الجيد أن يقوم شخص ما بعمل دليل قصير مع بعض أفضل الممارسات حول كيفية تجنب هذه المشكلة. باستخدام القيم العددية ، من السهل استبدال قوائم Python بمصفوفات NumPy ، لكن ليس من الواضح تمامًا كيفية تخفيف المشكلة باستخدام السلاسل (ذات الحجم المتغير).

في حالتي ، لدي قائمة بكائنات الفئة المخصصة التي تم إنشاؤها / ملؤها في المُنشئ. يحتوي بشكل أساسي فقط على مجموعات من مسارات الملفات. ثم داخل __getitem__ أقوم بتحميل تلك الصور ، وإجراء بعض المعالجة المسبقة ، والتحويل إلى Tensors المشعل ، ثم صراحة كال del على الصور المحملة قبل العودة. تكمن المشكلة في أن إضافة بعض خطوات المعالجة المسبقة الإضافية ، والتي تبدو غير ضارة ، تقدم مشكلة استخدام الذاكرة خارج الحدود.

قد يوفر Py 3.8 mp.shared_memory حلاً لطيفًا بدرجة كافية لمشاركة العديد من الكائنات non-tensor / nparray ، على سبيل المثال ، القائمة المشتركة: https://docs.python.org/3.8/library/multiprocessing.shared_memory.html # multiprocessing.shared_memory.ShareableList. :)

إخلاء المسؤولية: لم أقرأ المستندات عن كثب.

هل هناك أي شيء عملي يمكننا القيام به هنا؟ هل لدينا ما يكفي من تحويلات الصور المدعومة في torchvision لتوثيق نقل بعض حالات الاستخدام إلى ذلك؟

فقط لتوضيح النقطة هنا: تنفيذ ما اقترحه @ 1e100 هو شيء لدينا في خارطة طريق torchvision ، لكنه ليس في الجزء العلوي من قائمتنا وربما يتطلب دعم الموتر المتداخل أولاً.

ومع ذلك ، لن يكون هذا إصلاحًا عامًا لهذه المشكلة: إنه سيتجاوز فقط الحاجة إلى المعالجة المتعددة في تحميل البيانات باستخدام نهج مختلف (على سبيل المثال ، التحويلات على وحدة معالجة الرسومات).

cccpuhrsch كما رأيت أحدهم يذكر موتر متداخل. (بالمناسبة ، cpuhrsch ، هل يمكنك عمل ملصق وحدة للموتر المتداخل وإضافة نفسك عليه على https://github.com/pytorch/pytorch/issues/24422؟)

لماذا لم يتم حل هذا الخطأ في عام؟

IMLHF راجع السطر الأول من وصف هذه المشكلة أو المناقشة أعلاه. لأن هذا ليس تسريبًا حقًا ولكنه تصميم مؤسف للثعبان ، وهو خارج أيدينا. يحاول كل من pytorch و numpy التغلب على هذا من خلال تنفيذ تسلسل مخصص لموترات و ndarrays. ومع ذلك ، لا يمكننا حساب بنية البيانات العامة حقًا. هذا مفتوح لأننا نقوم بتنفيذ المزيد من الأدوات المساعدة للمستخدمين لحل هذه المشكلة.

تساعدني إضافة torch.cuda.empty_cache() في نهاية كل تكرار في حل هذه المشكلة. يتأرجح استخدام الذاكرة بدلاً من التراكم بعد إضافة هذا.

ربما يجب علينا إضافة تحذير.

VitalyFedyunin هل لديك عرض النطاق الترددي للنظر في هذا؟ كحد أدنى ، هل يمكننا معرفة ما إذا كانت هذه هي نفس المشكلة مثل https://github.com/pytorch/pytorch/issues/17499؟

أعتقد أنني حللت هذه المشكلة باستخدام ndarray بدلاً من استخدام tensor في مشروعي.

كان الرمز الخاص بي قبل

def df2var(x):
    return (torch.LongTensor(token2id(x['Query'], max_char = max_length_char)), 
            torch.tensor(coll2id[x['Agg_Coll']], dtype = torch.long))

class Making_Dataset(Dataset):
    def __init__(self, input_dataframe):
        self.dataset = input_dataframe.apply(lambda x : df2var(x), axis = 1)

    def __len__(self):
        return len(self.dataset)

    def __getitem__(self, data_index):
        return self.dataset[data_index]

وقمت بإصلاح الكود كـ

class Making_Dataset(Dataset):
    def __init__(self, input_dataframe):
        self.text = np.array([token2id(q, max_char = max_length_char) for q in input_dataframe.Query])
        self.labels = np.array([coll2id[coll] for coll in input_dataframe.Agg_Coll])

    def __len__(self):
        return len(self.text)

    def __getitem__(self, data_index):
        return self.text[data_index], self.labels[data_index]

بعد إصلاح الكود ، اختفت مشكلة زيادة الذاكرة في كل حقبة في مشروعي.
لأنني لا أعرف بالضبط سبب هذه المشكلة ، نرحب بأي تعليقات حول هذا الموضوع!

أرى مشكلة مماثلة مع Torch 1.3.0 مع CUDA 10 على Ubuntu 18.04. لم تكن هذه مشكلة في جهاز AWS بذاكرة وصول عشوائي (RAM) بسعة 64 جيجابايت ، ولكن على جهاز محلي به ذاكرة وصول عشوائي (RAM) بسعة 128 جيجابايت ومقايضة 128 جيجابايت ، لا يمكنني حتى تجاوز 150 حقبة - يستمر استخدام الذاكرة في الزحف من بضع غيغابايت (متوقع) إلى 128 + جيجا بايت.

تحديث

كانت مشكلتي عبارة عن خطأ صغير خبيث - أثناء تسجيل إحصائيات التدريب ، كنت أقوم بحفظ معلومات التدرج بالإضافة إلى القيم الصافية ، وهي غير ضرورية وتضيف إلى بصمة الذاكرة الخاصة بك في كل فترة.

قد يوفر mp.shared_memory الخاص بـ Py 3.8 حلاً لطيفًا بدرجة كافية لمشاركة العديد من الكائنات غير الموتر / nparray ، على سبيل المثال ، القائمة المشتركة
https://github.com/pytorch/pytorch/issues/13246#issuecomment -513480017

أدى استخدام الكثير من الذاكرة المشتركة إلى حل المشكلة بالنسبة لي
https://github.com/pytorch/pytorch/issues/13246#issuecomment -487042977

مرحبًا ، لقد تأخرت قليلاً عن هذا الموضوع ، لكني أواجه نفس المشكلات ولكن مع القواميس.
هل نجح أي شخص مع هذه الاختراقات؟

هذه لا تزال قضية صالحة. هل يمكن لشخص ما تقديم قائمة بأفضل الممارسات حول كيفية استخدام عدة عاملين في DataLoaders دون التسبب في تسرب للذاكرة؟

marrrcin أعتقد أن أفضل رهان هو اعتبار التنسورات باهظة الثمن ، لذا يجب أن تكون حذرًا بشأن مقدار استخدامها ، خاصة إذا كانت هناك فرصة للحصول على معلومات التدرج.

على سبيل المثال ، ربما يكون من المفيد تخزين كل شيء على هيئة قوائم أو numpy.ndarray s حتى تحتاج إلى تنفيذ عمليات torch

AudreyBeard شكرا على الرد. في كود مجموعة البيانات الخاصة بي ، لدي كل شيء مخزّن على هيئة عدد / قوائم / سلاسل / int والجزء الوحيد الذي أستخدم فيه الموترات هو __getitem__ ولاحقًا في collate_fn (تطبيق الحشو). هل يجب علي إنشاء موترات مع تعيين requires_grad على false هناك؟ بمجرد أن ينتقل الكود الخاص بي إلى num_workers> 0 تبدأ الذاكرة في التسرب.

آسف لسوء التنسيق ، أنا على الهاتف المحمول.

marrrcin عادةً ما أقوم فقط بإلقاء بيانات الإدخال الخاصة بي (الصورة أو الإشارة أو أي شيء آخر) كـ tensor في __getitem__ . عادةً ما يتم إرجاع تصنيفاتي وكذا في شكل قوائم. لست متأكدًا من نوع البيانات التي تستخدمها أو إذا كنت تقوم بأي نوع خاص من الحشو ، لكنني عادةً ما أستخدم torchvision.transforms في __getitem__ . مقابل ما يستحق ، نادرًا ما أقوم بتطبيق collate_fn مخصص.

فكرة: لقد نشرت هنا في الأصل لأنني كنت أعاني مما اعتقدت أنه تسرب للذاكرة. اتضح أنني كنت متمسكًا بالبيانات غير الضرورية في كل حقبة ، وكان لذلك تسريب الأعراض عندما كان مجرد إدارة متغيرة دقيقة للغاية من جانبي. استغرق الأمر مني بعض الوقت لمعرفة بالضبط ما الذي يجري.

AudreyBeard حالتي لا تتعلق بالصور / torchvision ، لقد استخدمت الحشو للرموز المستخرجة من النصوص ذات الطول المتغير ، ولهذا السبب أحتاج إلى استخدام collate_fn .

class PaddingCollateFn:
    def __call__(self, batch):
        sorted_batch = sorted(batch, key=lambda x: x[0].shape[0], reverse=True)
        sequences = [x[0] for x in sorted_batch]
        sequences_padded = torch.nn.utils.rnn.pad_sequence(sequences, batch_first=True)

        attention_masks = [torch.tensor([1 for _ in x[0]]) for x in sorted_batch]

        attention_masks_padded = torch.nn.utils.rnn.pad_sequence(
            attention_masks, batch_first=True
        )
        lengths = torch.tensor([len(x) for x in sequences])
        labels = torch.tensor([x[1] for x in sorted_batch])

        return (sequences_padded, lengths, attention_masks_padded), labels

هل يجب أن أسقط موترات أصلية بعد تركها (على سبيل المثال باستخدام del )؟ اعتقدت أنه بمجرد انتهاء collate_fn ، ستكون خارج النطاق وستتم إزالتها حيث لن تكون هناك مراجع لها.

لقد قابلت هذا في الإصدار 1.3.1 من Pytorch .... عندما أقوم بتدريب ImageNet .... هل لدى أي شخص بعض الأفكار؟
في حالتي ، أستخدم عددًا من 24 عاملًا ، عادةً ما تكلف حوالي 110 جيجا بايت من الذاكرة في العصر الأول ، ولكن عندما أقوم بتدريب العصر الثاني ، سيكلف ذلك كل ذاكرتي ، وسيقتل النظام أداة تحميل البيانات ..... ر اعرف لماذا ....

بالنسبة لي ، كانت المشكلة أنني كنت أقوم بالفعل بتحويل المصفوفات غير المعقدة إلى موترات مشعلة في أداة تحميل البيانات __getitem__

يجب فقط تحويل المصفوفات Numpy إلى موترات شعلة في حلقة المدرب ، قبل إرسالها إلى النموذج مباشرة. وإلا فإن الموترات ستجعل الذاكرة المشتركة تنمو خارج الحدود.

يمكنك مراقبة الذاكرة المشتركة عن طريق تشغيل الأمر watch -n .3 df -h
الذاكرة المشتركة تتوافق مع السطر /dev/shm
يجب ألا تزيد الكمية المستخدمة بعد كل حقبة.

لدي نفس المشكلة

لم يتم حل هذا الخطأ في pytorch 1.4.0

ولدي أيضا نفس المشكلة

أنا أيضًا أواجه نفس المشكلة ، حتى بعد:
1) حذف كافة المتغيرات غير الضرورية
2) استخدام المصفوفات المعقدة بدلاً من القوائم
3) استخدام gc.collect ()

annukkaa وآخرين: مجرد استخدام np.array(list_of_paths) لا يكفي لأنه يخزن قائمة السلاسل ككائنات كثيرة. استخدم np.array(list_of_paths).astype(np.string_) لتحويل المصفوفة إلى مصفوفة بايت مربعة الشكل (وتأكد من التحويل من بايت إلى str عند استخدام السلسلة فعليًا). يجب أن يساعد. قم أيضًا بتعيين الذاكرة المشتركة إلى قيمة عالية ، على سبيل المثال ، 100 جيجابايت.

لم أره مذكورًا صراحةً في هذا الموضوع لذا اعتقدت أنني سأشارك الحل الخاص بي.
في حالتي ، كان لدي كائن فئة مخصص وقائمة سلاسل في مجموعة البيانات الخاصة بي والتي تم الوصول إليها في كل تكرار واستنفدت ذاكرة وحدة المعالجة المركزية الخاصة بي بسرعة.
من خلال التفاف الفئة والقائمة باستخدام كائن إدارة المعالجات المتعددة الذي يتعامل مع الحالات المشتركة ، تمكنت من التخلص من تسرب الذاكرة.

للربط مع الحد الأدنى من المثال ، سيبدو الرمز هكذا.

from torch.utils.data import Dataset, DataLoader
import torch
from multiprocessing import Manager


class DataIter(Dataset):
    def __init__(self):
        manager = Manager()
        self.data = manager.list([x for x in range(24000000)])

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        data = self.data[idx]
        return torch.tensor(data)


train_data = DataIter()
train_loader = DataLoader(train_data, batch_size=300,
                          shuffle=True,
                          drop_last=True,
                          pin_memory=False,
                          num_workers=18)

for i, item in enumerate(train_loader):
    if i % 1000 == 0:
        print(i)

يوجد بعض الحمل نظرًا لأن الكائنات مخللة ، ولكنها بديل جيد لتنفجر الذاكرة.

هل سيتم إصلاح هذا الشيء ؟؟؟؟

يبدو أن المشكلة لا تزال مفتوحة.

استخدام ndarrays أيضا لا يساعد. إنها تصطدم بذاكرة الوصول العشوائي الخاصة بوحدة المعالجة المركزية حوالي 4 مرات مع وجود عمال يصلون إلى الصفر.
حاولت ديل بعد أي تحسن ملحوظ.

أهلا بكم،

لقد جربت للتو حلاً لهذا وهذا يعمل مثل الجمال المطلق.

بالنسبة لي ، أقوم بتخزين بيانات الصور كمصفوفات غير مرتبة مخزنة محليًا.

لقد كتبت مجموعة البيانات المخصصة الخاصة بي كـ ---

`استيراد الشعلة
من torch.utils استيراد البيانات
استيراد numpy كـ np

فئة DataSetBuilder (data.Dataset):
"" "مجموعة بيانات TinyImagenet." ""

def __init__(self, rootpath, train=True, transform=None):
    """
    Args:
        rootpath: Path to the pytorch file with annotations.
        root_dir (string): Directory with all the images.
        transform (callable, optional): Optional transform to be applied
            on a sample.
    """
    self.path = rootpath
    self.transform = transform
    self.train = train
    # Load input data
    if self.train:
        self.X_ = np.load(self.path +'x_train.npy')
    else:
        self.X_ = np.load(self.path +'x_test.npy')
    # Load target data
    if self.train:
        self.y_ = np.load(self.path +'y_train.npy')
    else:
        self.y_ = np.load(self.path +'y_test.npy')

def __len__(self):
    if self.train:
        dataFile = self.path + 'x_train.npy'
    else:
        dataFile = self.path + 'x_test.npy'

    data = np.load(dataFile)
    return data.shape[0]

def __getitem__(self, idx):
    if torch.is_tensor(idx):
        idx = idx.tolist()
    X = self.X_[idx, :, :, :]
    y = self.y_[idx]
    if self.transform is not None:
        X = self.transform(X)
    return X, torch.from_numpy(y).type(torch.LongTensor)`

بدلاً من تحميل البيانات في __getitem__ ، أقوم بتحميلها في وقت بناء الكائن مما يعني أنه لا يتم تحميلها في كل مرة في نفس المصفوفات غير الدقيقة في الذاكرة بدلاً من تحميلها مرة واحدة في وقت إنشاء الكائن.

أتمنى أن يساعدك هذا!

اترك تعليقًا إذا كان هذا يناسبك ... :-)

مرحبًا @ varinder-singh ،
جميل أنك وجدت حلاً. لا أرى كيف يختلف هذا عن المثال غير المقيد الذي قدمه bfreskura سابقًا ؟ يقوم __getitem__ أيضًا بتقطيع البيانات من مصفوفة متراكمة.
ربما أقرأ الشفرة بشكل خاطئ ، هل تمانع في توضيح سبب تأثيرها على استهلاك الذاكرة بشكل مختلف؟

بعد مواجهة هذه المشكلة في مشروعي الحالي ، والقراءة من خلال هذا الموضوع ، أشعر أنه قد يكون من المفيد إضافة أفكاري وتقديم حل متعدد الاستخدامات إلى حد ما.

اهم الاشياء اولا:

1) بالنظر إلى كل ما يمكنني ملاحظته هنا فإن تشخيص mprostock صحيح. وفر لي عملك الكثير من الوقت في الحفر بنفسي.
2) بالطبع ردsoumith صحيح أيضًا ، لكنه لا ينطبق في هذه الحالة بسبب الأسباب التي ذكرها منشورmprostock لاحقًا على مصفوفات الكائن.

هذه ليست مشكلة PyTorch. إنها مشكلة بايثون ، وبالتالي يجب حلها هناك. ولكن نظرًا لأن المشكلة ناتجة عن حساب المرجع ، وهو جزء لا يتجزأ من إدارة ذاكرة Python ، فقد لا يحدث هذا في أي وقت قريب. بعض الحلول المقترحة أعلاه مثيرة للاهتمام ، ولكن لماذا تذهب إلى مثل هذه الأطوال؟ بافتراض أن المهمة هي الوصول المشترك إلى عدد من التسلسلات متغيرة الطول مثل أسماء الملفات ، فلن تحتاج إلى ابتكار أي شيء جديد. ببساطة استخدم numpy لحزم التسلسلات وإجراء بحث غير مباشر. لفهم ما أعنيه ، راجع الكود أدناه ، والذي يتجنب تمامًا المشكلة التي تمت مناقشتها في هذا الموضوع.

سلاسل mprostock و smolendawid هي في الأساس مجرد سلاسل من الأعداد الصحيحة ، وهو نوع يمكن التعامل معه بسهولة في numpy. تم تصميم المثال أدناه لمشاركة أي قائمة من السلاسل (مثل أسماء ملفات الصور) بين برامج تحميل البيانات المتعددة.
marrrcin لقد طلبت أفضل الممارسات. هذا قوي ويعمل مع أي قائمة تسلسل متغير الطول. في مشروعي الحالي ، أستخدم متغيرًا أكثر تعقيدًا قليلاً من هذا للبيانات متعددة الأبعاد حيث يكون لكل بُعد طول متغير.
SsnL يحل هذا ضمنيًا المشكلة التي تناقشها مع zhiweifang في / issues / 20433 بدون استخدام بنيات Python 3.8 الفاخرة.

import numpy as np
import torch
from typing import Union

# --- UTILITY FUNCTIONS ---
def string_to_sequence(s: str, dtype=np.int32) -> np.ndarray:
    return np.array([ord(c) for c in s], dtype=dtype)

def sequence_to_string(seq: np.ndarray) -> str:
    return ''.join([chr(c) for c in seq])

def pack_sequences(seqs: Union[np.ndarray, list]) -> (np.ndarray, np.ndarray):
    values = np.concatenate(seqs, axis=0)
    offsets = np.cumsum([len(s) for s in seqs])
    return values, offsets

def unpack_sequence(values: np.ndarray, offsets: np.ndarray, index: int) -> np.ndarray:
    off1 = offsets[index]
    if index > 0:
        off0 = offsets[index - 1]
    elif index == 0:
        off0 = 0
    else:
        raise ValueError(index)
    return values[off0:off1]


# --- OUR DATASET CODE STARTS HERE ---
class MyDataset(torch.utils.data.Dataset):

    def __init__(self):
        strings = [
            'I like', # You can use np.int8 for ASCII strings.
            'chocolate',
            '我喜欢', # If you use anything that is not standard ASCII,
            '巧克力', # need to use np.int16, or even np.int32.
        ]

        # Convert each string to sequence of codepoints (integer),
        # and then pack them into a numpy array.
        seqs = [string_to_sequence(s) for s in strings]
        self.strings_v, self.strings_o = pack_sequences(seqs)

    def __len__(self): return 4

    def __getitem__(self, i):
        # Use indirect lookup to fetch the i-th sequence. This only uses integer numpy
        # array lookups, which avoids that the objects are subsequently replicated by
        # child processes.
        seq = unpack_sequence(self.strings_v, self.strings_o, i)
        string = sequence_to_string(seq)
        # ACTION NEEDED: You probably do not want to return the string itself ;-).
        return string


m = MyDataset()
for i in range(len(m)):
    print(i, '=', m[i])

# Output
# -------
# 0 = I like
# 1 = chocolate
# 2 = 我喜欢
# 3 = 巧克力

لقد تمكنت من حل هذه المشكلة وأرغب في مشاركة سنتي. لقد اتبعت بشكل أساسي الأفكار التي أشار إليها harpone وآخرين بأن السلاسل هي المشكلة. كان لدي حجتان إشكاليتان في صفي لمجموعة البيانات:

  1. مصفوفة عددية من السلاسل (لم يساعد الإرسال باستخدام .astype (str))
  2. قاموس من السلاسل إلى نواقل numpy

اضطررت إلى إصلاح كل من 1 و 2 لإيقاف تسرب الذاكرة. بالنسبة إلى 1 ، فإن السلاسل النصية الخاصة بي هي في الواقع تجزئة للوصول إلى المتجهات المعقدة في القاموس ، لذلك قمت بتحويل جميع السلاسل إلى أعداد صحيحة منذ أن كان لدي قاموس بحجم ثابت.

بالنسبة إلى 2 ، قمت بتحويل القاموس لاستخدام مفاتيح الأعداد الصحيحة ، لكن تسرب الذاكرة لا يزال مستمراً. ما نجح في الواقع هو عدم تمرير القاموس إلى فئة Dataset على الإطلاق ، ولكن فقط إعادة مفتاح interger في __getitem___ والقيام بفهرسة القاموس / pormotion إلى Pytorch tensor / الترقية إلى GPU في حلقة القطار الخاصة بي.

هل هناك أي طريقة لجعل عمليات أداة تحميل البيانات تعيد تهيئة نفسها كل حقبة وتنظيف كل تلك الذاكرة المسربة؟

Pozimek لقد أعادوا بالفعل تهيئة كل حقبة.

إذن ما هي أفضل ممارسة الآن؟

wangchust : الحل الذي اقترحه bashimao يعمل بشكل جميل بالنسبة لي ، حتى في مجموعات البيانات الكبيرة نسبيًا (25 مليون + تسلسل نصي).

wangchust : الحل الذي اقترحه bashimao يعمل بشكل جميل بالنسبة لي ، حتى في مجموعات البيانات الكبيرة نسبيًا (25 مليون + تسلسل نصي).

أنا أيضا. يعمل حل bashimao بشكل جيد جدًا.

مرحبا بالجميع ، أنا هنا مرة أخرى. هل يلبي أي شخص "OverflowError: لا يمكن إجراء تسلسل لكائن بايت أكبر من 4 جيجا بايت" عند تفرع عمال تحميل البيانات من العملية الرئيسية؟

مرحبا بالجميع ، أنا هنا مرة أخرى. هل يلبي أي شخص "OverflowError: لا يمكن إجراء تسلسل لكائن بايت أكبر من 4 جيجا بايت" عند تفرع عمال تحميل البيانات من العملية الرئيسية؟

wangchust إذا كنت تقوم بإجراء تسلسل ، فمن المحتمل أنك تفعل شيئًا خاطئًا. ستقوم كل عملية بإلغاء تسلسل 4 أو مهما كان حجم العنصر الخاص بك بالغيغابايت وإعادة بناء الكائنات المتسلسلة. وبالتالي ، سوف تقوم بتكرار الذاكرة وستنفد منها في النهاية إذا كان هناك العديد من العمليات المتوازية. الهدف الكامل للإجراءات التي اقترحها أنا والآخرون في هذا الموضوع هو تجنب تكرار الذاكرة. كما قلت في جملتي الأولى ، أعتقد أنك تفعل شيئًا خاطئًا ، ربما على مستوى أساسي جدًا.

يبدو أن مصفوفة السلسلة المدعومة من الموتر المخصص تساعد https://gist.github.com/vadimkantorov/86c3a46bf25bed3ad45d043ae86fff57 :

import torch

class TensorBackedImmutableStringArray:
    def __init__(self, strings, encoding = 'utf-8'):
        encoded = [torch.ByteTensor(torch.ByteStorage.from_buffer(s.encode(encoding))) for s in strings]
        self.cumlen = torch.cat((torch.zeros(1, dtype = torch.int64), torch.as_tensor(list(map(len, encoded)), dtype = torch.int64).cumsum(dim = 0)))
        self.data = torch.cat(encoded)
        self.encoding = encoding

    def __getitem__(self, i):
        return bytes(self.data[self.cumlen[i] : self.cumlen[i + 1]]).decode(self.encoding)

    def __len__(self):
        return len(self.cumlen) - 1

    def __list__(self):
        return [self[i] for i in range(len(self))]

ربما يستحق شيء مثل هذا تضمينه في PyTorch الأساسية

أي شخص لديه أي حظ في الحصول على القواميس للعمل وعدم تسرب؟
لقد رأيت هذا المنشور أعلاه ، لكنني أرغب في الوصول إلى نوع من جدول التجزئة داخل العامل بدلاً من القيام بالعمل خارجه كما يوحي هذا التعليق.

أفكر في أحد الأمور التالية:

  • مدير المعالجة المتعددة ديكت
  • الذاكرة المشتركة أو mmap
  • جدول تجزئة محلي قائم على العقد بدون أي كائنات بيولوجية.

تبدو الذاكرة المشتركة وكأنها خيار واعد ومتأصل في Python. أنا فضول لماذا تستخدم ديكت؟ النمط الشائع هنا هو الحصول على قائمة بالعناصر (سلاسل عادةً) وفهرستها.

بالنسبة لي ، كانت في الأصل قائمة بالإملاءات (قائمة أمثلة على البيانات الوصفية ، كل مثال كان عبارة عن إملاء)

فهمتك. بشكل عام ، تجعل الإملاء الأمر أكثر صعوبة ، لأن نمط الوصول إلى الذاكرة ليس تسلسليًا. أفكر في إضافة دعم هياكل البيانات الآمنة من Fork (لست متأكدًا مما إذا كان مدمجًا في الريبو أم منفصلًا).

تبدو الذاكرة المشتركة وكأنها خيار واعد ومتأصل في Python. أنا فضول لماذا تستخدم ديكت؟ النمط الشائع هنا هو الحصول على قائمة بالعناصر (سلاسل عادةً) وفهرستها.

VitalyFedyunin شكرا للنصيحة. قد أحاول الذاكرة المشتركة أولاً بعد ذلك.
سبب الإملاء الآن هو البحث عن عناصر O (1) لوظيفة أخذ العينات العشوائية في خطوة إنشاء البيانات. وبشكل أكثر تحديدًا ، "التعدين الثلاثي" حيث يتم وضع مفتاح الإختصار على user_id ، والقيم عبارة عن قائمة بالأمثلة الإيجابية المرتبطة بهذا المستخدم. انظر هنا للحصول على مثال.

marrrcin عادةً ما أقوم فقط بإلقاء بيانات الإدخال الخاصة بي (الصورة أو الإشارة أو أي شيء آخر) كـ tensor في __getitem__ . عادةً ما يتم إرجاع تصنيفاتي وكذا في شكل قوائم. لست متأكدًا من نوع البيانات التي تستخدمها أو إذا كنت تقوم بأي نوع خاص من الحشو ، لكنني عادةً ما أستخدم torchvision.transforms في __getitem__ . مقابل ما يستحق ، نادرًا ما أقوم بتطبيق collate_fn مخصص.

فكرة: لقد نشرت هنا في الأصل لأنني كنت أعاني مما اعتقدت أنه تسرب للذاكرة. اتضح أنني كنت متمسكًا بالبيانات غير الضرورية في كل حقبة ، وكان لذلك تسريب الأعراض عندما كان مجرد إدارة متغيرة دقيقة للغاية من جانبي. استغرق الأمر مني بعض الوقت لمعرفة بالضبط ما الذي يجري.

تضمين التغريدة كان هذا مفيدًا وحل مشكلتي.

الشيء الذي يثير فضولي هو (1) لماذا يؤثر الخلط على استهلاك الذاكرة كثيرًا و (2) لماذا يبدو أن إجمالي استخدام الذاكرة أكبر بكثير من عدد العمليات * حجم سمة البيانات.

في مثال bfreskura ، حجم self.data هو 24e7 عددًا صحيحًا ، أي ما يقرب من 1.83 جيجابايت. إذا قللنا ذلك إلى 24e5 (بحيث يمكن تشغيل البرنامج النصي بسرعة حتى الاكتمال) ، فسيكون حجم كائن البيانات 18.92 ميجابايت تقريبًا.

في حالة قائمة Python ، إذا تم ضبط shuffle = False ، فأنا أقيس أن العملية تستهلك 298.17 ميجابايت. ثم أقوم بتعيين خلط ورق اللعب = صحيح ، أقيس أن العملية تستهلك 1.44 جيجابايت.

لذلك ، أكثر من 18 عاملاً + عملية رئيسية رئيسية واحدة ، حتى لو تم نسخ جميع البيانات إلى كل عملية ، يجب ألا يتجاوز ذلك 359.48 ميغابايت إضافية من ذاكرة الوصول العشوائي. كيف يتم ذلك عندما أحصل على ما يقرب من 4 أضعاف هذا المبلغ عند التبديل = صحيح؟ أتخيل أنه يجب أن يكون له علاقة بالوصول المتسلسل مقابل الذاكرة العشوائية وأخطاء الصفحة الناتجة ، لكنني أشعر بالفضول إذا كان بإمكان أي شخص أن يصف بدقة أكبر ما يحدث هنا.

للإشارة إلى التعديلات التي أجريتها (Fire CLI + تقرير استهلاك الذاكرة) على البرنامج النصي bfreskura ، هنا:

https://gist.github.com/Erotemic/3f017de31529dc64c1a54948f37da1d5

سيؤدي الوصول العشوائي إلى إجبار Python على إعادة كتابة عدادات الكائنات في الذاكرة ، مما يتسبب في نسخ إطارات الذاكرة عند الكتابة. يمكن تحسين الوصول التسلسلي من خلال عدم كتابة عداد غير متغير (يعتمد على الأرجح على دورات GC). كما أنه من الأكثر أمانًا (حتى الآن) التقدير حسب ذروة الاستخدام وهو عدد العمال * (جميع أحجام الكائنات + عدد العناصر * مؤشر كائن بيثون + حجم العداد). نحن نعمل حاليًا على حل لمنع نسخ الذاكرة الكاملة ، لكنه يتطلب إعادة هندسة كبيرة وسيستغرق وقتًا.

VitalyFedyunin شكرًا على التفسير ، لكنني أخشى أنني لم أفهم الأمر تمامًا بعد: ابتسم:

لقد تمكنت من حل المشكلة المذكورة أعلاه باستخدام مصفوفة numpy بدلاً من قائمة ، على سبيل المثال np.string_ نوع مصفوفة بايت مربعة الشكل ، لكنني الآن أواجه مشكلة مشابهة على ما يبدو مع webdataset (https: / /github.com/tmbdev/webdataset/issues/24#issuecomment-709101119). يبدو أنني لا أنفد من shm ، ولكن كما أوضحتtmbdev سابقًا في خيط webdataset ، يمكن أن تكون المشكلة _number_ مقاطع الذاكرة المشتركة ...

هل لديك أي نصائح حول كيفية تصحيح هذه المشكلة و / أو بعض الاختراقات المؤقتة حولها؟ لقد جربت ipcs ولكن هذا لا يظهر أي شيء مفيد بالنسبة لي (على ما أعتقد). يعرض lsof /dev/shm بعض المعلومات عن أشياء وأحجام shm ، لكنني لست متأكدًا تمامًا مما تعنيه ...

بالنسبة لي ، فإن قياس proportional set size (psutil) ساعد في قياس حجم المشكلة. لقد عملت حولها من خلال فصول StringArray و DictArray المخصصة: https://gist.github.com/vadimkantorov/86c3a46bf25bed3ad45d043ae86fff57 التي تحزم السلاسل / الإملاء في موترات مسطحة (لم يتم استخدام NumPy)

wangchust : الحل الذي اقترحه bashimao يعمل بشكل جميل بالنسبة لي ، حتى في مجموعات البيانات الكبيرة نسبيًا (25 مليون + تسلسل نصي).

آسف ، ربما أفتقد شيئًا ما حول استخدام جيثب لكني لا أرى أي حل من bashimao في هذا الموضوع ، مجرد تعليق. هل يمكن لأي شخص أن يشير لي إليه من فضلك؟

أبسط بكثير من الإرسال إلى np.string_ (ملاحظة NOT str إلخ). قل لديك

strings = ['hello', 'world']

ثم افعل

strings_byte = np.array(strings).astype(np.string_)

ستكون النتيجة بعد ذلك مصفوفة واحدة مربعة البايت (لاحظ النوع dtype):

array([b'hello', b'world'], dtype='|S5')

يجب عليك بعد ذلك إعادة الترميز إلى السلسلة عند اختيار سلسلة من ذلك ، على سبيل المثال str(strings_byte[0], encoding='utf-8') .

لاحظ أن هذا لن يعمل:

strings_byte = np.array(strings).astype(str)

لاحظ النوع:

array(['hello', 'world'], dtype='<U5')

هذه ليست مجموعة بايتات مربعة أي ليس كائنًا واحدًا.

سيكون من المفيد بالنظر إلى استمرار هذه المشكلة وعدد المرات التي اصطدمت فيها أنا أو زملائي بهذه المشكلة إذا كان بإمكاننا الحصول على وصفة لتحديد ما إذا كان هذا هو السبب أم لا. بعد قراءة هذا الموضوع بدقة ، يبدو أن هناك اقتراحات جيدة لتخفيف المشكلة (https://github.com/pytorch/pytorch/issues/13246#issuecomment-436632186 ، https://github.com/pytorch/pytorch/issues / 13246 # issuecomment-612396143) ، ولكن أيضًا بعض السلوك المربك (https://github.com/pytorch/pytorch/issues/13246#issuecomment-708067670).

  1. هل يكفي تشغيل أداة تحميل البيانات بمفردها في فترة True loop ومراقبة استخدام الذاكرة لاستبعاد هذه المشكلة؟ أفترض أنه إذا نمت الذاكرة أثناء تشغيل الحلقة ، فيمكننا أن نستنتج أن كائن مجموعة البيانات لدينا لديه بعض السلوك المرضي الذي يراكم الكائنات أو أننا نواجه هذه المشكلة؟
  2. الشيء الذي لا يمكنني فهمه حقًا من هذا الموضوع هو سبب كون هذه مشكلة إذا كان لديك فئات مجموعة بيانات لا تحتوي إلا على بضعة ميغا بايت من البيانات. إذا فهمت بشكل صحيح ، فإن المشكلة الموضحة هنا هي أن أي كائنات من نوع python يتم الوصول إليها بواسطة مجموعة البيانات سيتم نسخها في النهاية إلى ذاكرة مؤشر ترابط عامل. إذا كان لدي فئة مجموعة بيانات بسيطة تقوم بتحميل مقاطع الفيديو من قائمة المسارات المخزنة كحقل في فئة مجموعة البيانات ، فلماذا تكون هذه مشكلة؟ البيانات المشتركة صغيرة جدًا بحيث لا تكاد تذكر. لماذا يؤدي استخدام shuffle=True في أداة تحميل البيانات إلى زيادة استخدام الذاكرة كما هو موضح في https://github.com/pytorch/pytorch/issues/13246#issuecomment -708067670؟

حل يناسبني - https://t.me/snakers4/2577

حل يناسبني - https://t.me/snakers4/2577

هذا لطيف! أعتقد أن الميزة الوحيدة لأسلوبي في https://gist.github.com/vadimkantorov/86c3a46bf25bed3ad45d043ae86fff57 هي أنه يمكن مشاركة الكائنات المعبأة بالموتّر بين عمال DDP مع بدائل DDP (أي أننا نقرأ كائن مجموعة بيانات عملاق في سلسلة واحدة فقط ، ثم نثر كائن مجموعة البيانات المعبأ بالموتّر إلى رتب DDP الأخرى). وبنفس الطريقة ، يمكن لعامل DDP الرئيسي جمع بعض مصفوفات السلاسل المحشوة بالموتّر من صفوف DDP.

حدوث آخر في العالم الحقيقي لهذا الخطأ: https://github.com/NVIDIA/NeMo/issues/1467

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات