التوازي وإدارة الموارد والتهيئة =================================================== .. _parallelism: التوازي ----------- يقوم بعض المقدرين والمرافقين في scikit-learn بتوازي العمليات المكلفة باستخدام عدة وحدات معالجة مركزية. اعتمادًا على نوع المقدر وأحيانًا على قيم معلمات الباني، يتم ذلك إما: - مع التوازي عالي المستوى عبر `joblib `_. - مع التوازي منخفض المستوى عبر OpenMP، المستخدم في كود C أو Cython. - مع التوازي منخفض المستوى عبر BLAS، المستخدم بواسطة NumPy وSciPy للعمليات العامة على المصفوفات. معلمات `n_jobs` للمقدرين تتحكم دائمًا في مقدار التوازي الذي يديره joblib (العمليات أو الخيوط اعتمادًا على backend joblib). التوازي على مستوى الخيوط الذي يديره OpenMP في كود Cython الخاص بـ scikit-learn أو بواسطة BLAS & LAPACK الذي تستخدمه مكتبات NumPy وSciPy المستخدمة في scikit-learn يتم التحكم فيه دائمًا بواسطة متغيرات البيئة أو `threadpoolctl` كما هو موضح أدناه. لاحظ أن بعض المقدرين يمكنهم الاستفادة من جميع أنواع التوازي الثلاثة في نقاط مختلفة من طرق التدريب والتنبؤ الخاصة بهم. نصف هذه الأنواع الثلاثة من التوازي في الفقرات الفرعية التالية بمزيد من التفاصيل. التوازي عالي المستوى مع joblib .................................... عندما يستخدم التنفيذ joblib، يمكن التحكم في عدد العمال (الخيوط أو العمليات) التي يتم إنشاؤها بالتوازي عبر المعلمة ``n_jobs``. .. note:: أين (وكيف) يحدث التوازي في المقدرين الذين يستخدمون joblib عن طريق تحديد `n_jobs` موثق حاليًا بشكل سيئ. يرجى مساعدتنا من خلال تحسين وثائقنا والتعامل مع `issue 14228 `_! يمكن لـ joblib دعم كل من المعالجة المتعددة والتعدد الخيطي. ما إذا كان joblib يختار إنشاء خيط أو عملية يعتمد على **backend** الذي يستخدمه. يعتمد scikit-learn بشكل عام على backend ``loky``، وهو backend الافتراضي لـ joblib. Loky هو backend متعدد العمليات. عند القيام بالمعالجة المتعددة، من أجل تجنب تكرار الذاكرة في كل عملية (الذي ليس معقولًا مع مجموعات البيانات الكبيرة)، سيقوم joblib بإنشاء `memmap `_ يمكن لجميع العمليات مشاركته، عندما تكون البيانات أكبر من 1MB. في بعض الحالات المحددة (عندما يطلق الكود الذي يتم تشغيله بالتوازي GIL)، سيشير scikit-learn إلى ``joblib`` بأن backend متعدد الخيوط يفضل. كمستخدم، يمكنك التحكم في backend الذي سيستخدمه joblib (بغض النظر عن ما يوصي به scikit-learn) باستخدام context manager:: from joblib import parallel_backend with parallel_backend('threading', n_jobs=2): # Your scikit-learn code here يرجى الرجوع إلى `joblib's docs `_ لمزيد من التفاصيل. في الممارسة العملية، ما إذا كان التوازي مفيدًا في تحسين وقت التشغيل يعتمد على العديد من العوامل. عادة ما تكون فكرة جيدة للتجربة بدلاً من افتراض أن زيادة عدد العمال هي دائمًا أمر جيد. في بعض الحالات يمكن أن يكون ضارًا للغاية بالأداء لتشغيل عدة نسخ من بعض المقدرين أو الوظائف بالتوازي (انظر الإفراط في الاشتراك أدناه). التوازي منخفض المستوى مع OpenMP ................................... يستخدم OpenMP لتوازي الكود المكتوب في Cython أو C، معتمدًا على التعدد الخيطي حصريًا. بشكل افتراضي، ستستخدم التنفيذات التي تستخدم OpenMP أكبر عدد ممكن من الخيوط، أي عدد الخيوط مثل وحدات المعالجة المركزية المنطقية. يمكنك التحكم في العدد الدقيق للخيوط المستخدمة إما: - عبر متغير البيئة ``OMP_NUM_THREADS``، على سبيل المثال عند: تشغيل نص برمجي بايثون: .. prompt:: bash $ OMP_NUM_THREADS=4 python my_script.py - أو عبر `threadpoolctl` كما هو موضح بواسطة `هذه القطعة من الوثائق `_. الروتينات الموازية NumPy وSciPy من المكتبات العددية .......................................................... يعتمد scikit-learn بشكل كبير على NumPy وSciPy، والذي يستدعي داخليًا الروتينات الخطية المتوازية (BLAS & LAPACK) المنفذة في المكتبات مثل MKL وOpenBLAS أو BLIS. يمكنك التحكم في العدد الدقيق للخيوط التي تستخدمها BLAS لكل مكتبة باستخدام متغيرات البيئة، على وجه التحديد: - ``MKL_NUM_THREADS`` يحدد عدد الخيوط التي يستخدمها MKL، - ``OPENBLAS_NUM_THREADS`` يحدد عدد الخيوط التي يستخدمها OpenBLAS - ``BLIS_NUM_THREADS`` يحدد عدد الخيوط التي يستخدمها BLIS لاحظ أن BLAS & LAPACK يمكن أن تتأثر أيضًا بـ `OMP_NUM_THREADS`. للتحقق مما إذا كان هذا هو الحال في بيئتك، يمكنك فحص كيفية تأثير عدد الخيوط المستخدمة بشكل فعال بواسطة هذه المكتبات عند تشغيل الأمر التالي في محيط bash أو zsh للقيم المختلفة لـ `OMP_NUM_THREADS`: .. prompt:: bash $ OMP_NUM_THREADS=2 python -m threadpoolctl -i numpy scipy .. note:: في وقت الكتابة (2022)، حزم NumPy وSciPy التي يتم توزيعها على pypi.org (أي تلك المثبتة عبر ``pip install``) وعلى قناة conda-forge (أي تلك المثبتة عبر ``conda install --channel conda-forge``) مرتبطة بـ OpenBLAS، بينما حزم NumPy وSciPy packages الموزعة على قناة conda الافتراضية من Anaconda.org (أي تلك المثبتة عبر ``conda install``) مرتبطة افتراضيًا بـ MKL. الإفراط في الاشتراك: إنشاء الكثير من الخيوط ........................................... من المستحسن عمومًا تجنب استخدام عدد أكبر بكثير من العمليات أو الخيوط من عدد وحدات المعالجة المركزية على الجهاز. يحدث الإفراط في الاشتراك عندما يقوم برنامج بتشغيل الكثير من الخيوط في نفس الوقت. لنفترض أن لديك جهازًا به 8 وحدات معالجة مركزية. ضع في اعتبارك حالة تقوم فيها بتشغيل :class:`~sklearn.model_selection.GridSearchCV` (موازاة مع joblib) مع ``n_jobs=8`` على :class:`~sklearn.ensemble.HistGradientBoostingClassifier` (موازاة مع OpenMP). ستقوم كل مثيل من :class:`~sklearn.ensemble.HistGradientBoostingClassifier` بإنشاء 8 خيوط (نظرًا لأن لديك 8 وحدات معالجة مركزية). هذا ما مجموعه ``8 * 8 = 64`` خيوط، مما يؤدي إلى الإفراط في الاشتراك في الموارد المادية لوحدات المعالجة المركزية وبالتالي إلى زيادة وقت الجدولة. يمكن أن يحدث الإفراط في الاشتراك بنفس الطريقة بالضبط مع الروتينات الموازية من MKL أو OpenBLAS أو BLIS المضمنة في مكالمات joblib. بدءًا من ``joblib >= 0.14``، عندما يتم استخدام backend ``loky`` (الذي هو الافتراضي)، سيخبر joblib عملياته **الفرعية** بتحديد عدد الخيوط التي يمكنهم استخدامها، وذلك لتجنب الإفراط في الاشتراك. في الممارسة الخوارزمية التي يستخدمها joblib هي إخبار العمليات باستخدام ``max_threads = n_cpus // n_jobs``، عبر متغير البيئة الخاص بهم. عودة إلى مثالنا من الأعلى، نظرًا لأن backend joblib من :class:`~sklearn.model_selection.GridSearchCV` هو ``loky``، فإن كل عملية ستتمكن فقط من استخدام خيط واحد بدلاً من 8، وبالتالي التخفيف من مشكلة الإفراط في الاشتراك. لاحظ أنه: - تعيين أحد متغيرات البيئة (``OMP_NUM_THREADS``، ``MKL_NUM_THREADS``، ``OPENBLAS_NUM_THREADS``، أو ``BLIS_NUM_THREADS``) سيكون له الأسبقية على ما يحاول joblib القيام به. سيكون العدد الإجمالي للخيوط ``n_jobs * _NUM_THREADS``. لاحظ أن تعيين هذا الحد سيؤثر أيضًا على حساباتك في العملية الرئيسية، والتي ستستخدم فقط ``_NUM_THREADS``. يعرض joblib context manager للتحكم الدقيق في عدد الخيوط في عماله (انظر وثائق joblib المرتبطة أدناه). - عندما يتم تكوين joblib لاستخدام backend ``threading``، لا توجد آلية لتجنب الإفراط في الاشتراك عند استدعاء المكتبات الأصلية الموازية في الخيوط التي يديرها joblib. - جميع المقدرين في scikit-learn الذين يعتمدون صراحة على OpenMP في كود Cython الخاص بهم يستخدمون `threadpoolctl` داخليًا لتكييف أعداد الخيوط المستخدمة بواسطة OpenMP وربما BLAS المضمنة بحيث يتم تجنب الإفراط في الاشتراك. ستجد تفاصيل إضافية حول تخفيف joblib للإفراط في الاشتراك في `وثائق joblib `_. ستجد تفاصيل إضافية حول التوازي في المكتبات بايثون العددية في `هذه الوثيقة من Thomas J. Fan `_. مفاتيح التهيئة ----------------------- Python API .......... يمكن استخدام :func:`sklearn.set_config` و :func:`sklearn.config_context` لتغيير معلمات التهيئة التي تتحكم في جانب التوازي. .. _environment_variable: متغيرات البيئة ..................... يجب تعيين متغيرات البيئة هذه قبل استيراد scikit-learn. `SKLEARN_ASSUME_FINITE` ~~~~~~~~~~~~~~~~~~~~~~~ يحدد القيمة الافتراضية لحجة `assume_finite` لـ :func:`sklearn.set_config`. `SKLEARN_WORKING_MEMORY` ~~~~~~~~~~~~~~~~~~~~~~~~ يحدد القيمة الافتراضية لحجة `working_memory` لـ :func:`sklearn.set_config`. `SKLEARN_SEED` ~~~~~~~~~~~~~~ يحدد البذرة للمولد العشوائي العالمي عند تشغيل الاختبارات، من أجل القابلية للتكرار. لاحظ أنه من المتوقع أن تعمل اختبارات scikit-learn بشكل حتمي مع الاستخدام الصريح للبذور المستقلة الخاصة بها بدلاً من الاعتماد على مولدات الأرقام العشوائية للبايثون أو مكتبة المعايير القياسية للبايثون للتأكد من أن نتائج الاختبار مستقلة عن ترتيب تنفيذ الاختبار. ومع ذلك، قد تنسى بعض الاختبارات استخدام البذر الصريح وهذه المتغير هي وسيلة للتحكم في الحالة الأولية للمولدات المذكورة أعلاه. `SKLEARN_TESTS_GLOBAL_RANDOM_SEED` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ يتحكم في بذر مولد الأرقام العشوائية المستخدم في الاختبارات التي تعتمد على الـ `global_random_seed` fixture. تعتمد جميع الاختبارات التي تستخدم هذا الـ fixture على العقد بأن تمر بشكل حتمي لأي قيمة بذرة من 0 إلى 99. في عمليات بناء CI الليلية، يتم رسم `SKLEARN_TESTS_GLOBAL_RANDOM_SEED` بيئة متغير عشوائيًا في النطاق المذكور أعلاه وتعمل جميع الاختبارات التي تستخدم هذا الـ fixture على تلك البذرة المحددة. الهدف هو التأكد من أنه، مع مرور الوقت، سيقوم CI الخاص بنا بتشغيل جميع الاختبارات مع بذور مختلفة مع الحفاظ على مدة الاختبار لتشغيل واحد من مجموعة الاختبارات الكاملة محدودة. هذا سيتحقق من أن تأكيدات الاختبارات التي تم كتابتها لاستخدام هذا الـ fixture ليست معتمدة على قيمة بذرة محددة. يقتصر نطاق قيم البذور المقبولة على [0، 99] لأنه غالبًا ما يكون من غير الممكن كتابة اختبار يمكن أن يعمل لأي بذرة ممكنة ونريد تجنب وجود اختبارات تفشل عشوائيًا في CI. القيم الصالحة لـ `SKLEARN_TESTS_GLOBAL_RANDOM_SEED`: - `SKLEARN_TESTS_GLOBAL_RANDOM_SEED="42"`: تشغيل الاختبارات مع بذرة ثابتة من 42 - `SKLEARN_TESTS_GLOBAL_RANDOM_SEED="40-42"`: تشغيل الاختبارات مع جميع البذور بين 40 و 42. - `SKLEARN_TESTS_GLOBAL_RANDOM_SEED="all"`: تشغيل الاختبارات مع جميع البذور بين 0 و 99. يمكن أن يستغرق هذا وقتًا طويلاً: استخدمه فقط لاختبارات فردية، وليس لمجموعة الاختبارات الكاملة! إذا لم يتم تعيين المتغير، فسيتم استخدام 42 كبذرة عالمية بطريقة حتمية. هذا يضمن أن مجموعة اختبارات scikit-learn تكون حتمية قدر الإمكان لتجنب إزعاج مُحسني الطرف الثالث الودودين لدينا. وبالمثل، لا ينبغي تعيين هذا المتغير في تكوين CI لسحب الطلبات للتأكد من أن مُحسنينا الودودين ليسوا أول من يواجه مشكلة حساسية البذور في اختبار غير مرتبط بتغييرات PR الخاصة بهم. فقط مُحسنو scikit-learn الذين يشاهدون نتائج عمليات البناء الليلية يتوقع أن يزعجهم هذا. عند كتابة اختبار جديد يستخدم هذا الـ fixture، يرجى استخدام الأمر التالي للتأكد من أنه يمر بشكل حتمي لجميع البذور المقبولة على جهازك المحلي: .. prompt:: bash $ SKLEARN_TESTS_GLOBAL_RANDOM_SEED="all" pytest -v -k test_your_test_name `SKLEARN_SKIP_NETWORK_TESTS` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ عندما يتم تعيين هذه البيئة إلى قيمة غير صفرية، يتم تخطي الاختبارات التي تحتاج إلى الوصول إلى الشبكة. عندما لا يتم تعيين هذه البيئة، يتم تخطي اختبارات الشبكة. `SKLEARN_RUN_FLOAT32_TESTS` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ عندما يتم تعيين هذه البيئة إلى '1'، يتم أيضًا تشغيل الاختبارات التي تستخدم `global_dtype` fixture على بيانات float32. عندما لا يتم تعيين هذه البيئة، يتم تشغيل الاختبارات فقط على بيانات float64. `SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ عندما يتم تعيين هذه البيئة إلى قيمة غير صفرية، يتم تعيين المشتق `Cython`، `boundscheck` إلى `True`. هذا مفيد للعثور على segfaults. `SKLEARN_BUILD_ENABLE_DEBUG_SYMBOLS` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ عندما يتم تعيين هذه البيئة إلى قيمة غير صفرية، سيتم تضمين رموز التصحيح في الإضافات C المجمّعة. يتم تكوين رموز التصحيح فقط لأنظمة POSIX. `SKLEARN_PAIRWISE_DIST_CHUNK_SIZE` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ يحدد هذا حجم الجزء الذي سيتم استخدامه بواسطة التطبيقات الأساسية لـ `PairwiseDistancesReductions`. القيمة الافتراضية هي `256` والتي ثبت أنها كافية على معظم الأجهزة. قد يرغب المستخدمون الذين يبحثون عن أفضل أداء في ضبط هذا المتغير باستخدام أسس 2 بحيث يحصلون على أفضل سلوك للتوازي لمعداتهم، خاصة فيما يتعلق بحجم ذاكرتهم المخبئية. `SKLEARN_WARNINGS_AS_ERRORS` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ يتم استخدام متغير البيئة هذا لتحويل التحذيرات إلى أخطاء في الاختبارات وبناء الوثائق. يحدد بعض عمليات CI (Continuous Integration) `SKLEARN_WARNINGS_AS_ERRORS=1`، على سبيل المثال للتأكد من أننا نلتقط تحذيرات الإلغاء التدريجي من التبعيات الخاصة بنا وأننا نكيف كودنا. لتشغيل مع نفس إعداد "التحذيرات كأخطاء" كما في عمليات بناء CI هذه يمكنك تعيين `SKLEARN_WARNINGS_AS_ERRORS=1`. بشكل افتراضي، لا يتم تحويل التحذيرات إلى أخطاء. هذا هو الحال إذا تم إلغاء تعيين `SKLEARN_WARNINGS_AS_ERRORS`، أو `SKLEARN_WARNINGS_AS_ERRORS=0`. يستخدم هذا متغير البيئة مرشحات تحذير محددة لتجاهل بعض التحذيرات، نظرًا لأنه في بعض الأحيان تنشأ التحذيرات من مكتبات تابعة ولا يمكننا فعل الكثير حيالها. يمكنك الاطلاع على مرشحات التحذير في الدالة `_get_warnings_filters_info_list` في `sklearn/utils/_testing.py`. لاحظ أنه بالنسبة لبناء الوثائق، يتحقق `SKLEARN_WARNING_AS_ERRORS=1` من أن بناء الوثائق، خاصة تشغيل الأمثلة، لا ينتج أي تحذيرات. هذا يختلف عن `-W` `sphinx-build` الذي يلتقط تحذيرات بناء الجملة في ملفات rst.