12. حفظ النموذج#

ملخص لطرق حفظ النموذج#

طريقة الحفظ

الإيجابيات

المخاطر / السلبيات

ONNX

  • تقديم النماذج بدون بيئة Python

  • تقديم بيئات التدريب بشكل مستقل عن بعضها البعض

  • الخيار الأكثر أمانًا

  • لا يتم دعم جميع نماذج scikit-learn

  • تتطلب المقدرات المخصصة مزيدًا من العمل لدعمها

  • يتم فقد كائن Python الأصلي ولا يمكن إعادة بنائه

skops.io

  • أكثر أمانًا من التنسيقات القائمة على pickle

  • يمكن التحقق من صحة المحتويات جزئيًا دون تحميل

  • ليس سريعًا مثل التنسيقات القائمة على pickle

  • يدعم أنواعًا أقل من التنسيقات القائمة على pickle

  • يتطلب نفس بيئة التدريب

pickle

  • أصلي لبايثون

  • يمكن تسلسل معظم كائنات Python

  • استخدام فعال للذاكرة مع protocol=5

  • يمكن أن يؤدي التحميل إلى تنفيذ كود عشوائي

  • يتطلب نفس بيئة التدريب

joblib

  • استخدام فعال للذاكرة

  • يدعم تعيين الذاكرة

  • اختصارات سهلة للضغط وفك الضغط

  • تنسيق قائم على Pickle

  • يمكن أن يؤدي التحميل إلى تنفيذ كود عشوائي

  • يتطلب نفس بيئة التدريب

cloudpickle

  • يمكن تسلسل كود Python المخصص غير المعبأ

  • كفاءة تحميل مماثلة لـ pickle مع protocol=5

  • تنسيق قائم على Pickle

  • يمكن أن يؤدي التحميل إلى تنفيذ كود عشوائي

  • لا توجد ضمانات للتوافق الأمامي

  • يتطلب نفس بيئة التدريب

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

  1. هل تحتاج إلى كائن Python بعد الحفظ، أم أنك تحتاج فقط إلى الحفظ من أجل تقديم النموذج والحصول على تنبؤات منه؟

إذا كنت تحتاج فقط إلى تقديم النموذج ولا يلزم إجراء مزيد من التحقيق في كائن Python نفسه، فقد يكون ONNX هو الأنسب لك. لاحظ أنه لا يتم دعم جميع النماذج بواسطة ONNX.

في حالة عدم ملاءمة ONNX لحالة الاستخدام الخاصة بك، فإن السؤال التالي هو:

  1. هل تثق تمامًا في مصدر النموذج، أم أن هناك أي مخاوف أمنية تتعلق بمصدر النموذج المحفوظ؟

إذا كانت لديك مخاوف أمنية، فيجب عليك التفكير في استخدام skops.io الذي يعيد لك كائن Python، ولكن على عكس حلول الحفظ القائمة على pickle، فإن تحميل النموذج المحفوظ لا يسمح تلقائيًا بتنفيذ كود عشوائي. لاحظ أن هذا يتطلب فحصًا يدويًا للملف المحفوظ، وهو ما يسمح لك skops.io بفعله.

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

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

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

  1. هل جربت pickle أو joblib ووجدت أنه لا يمكن حفظ النموذج؟

يمكن أن يحدث ذلك على سبيل المثال عندما يكون لديك وظائف محددة من قبل المستخدم في نموذجك.

إذا كانت الإجابة بنعم، فيمكنك استخدام cloudpickle الذي يمكنه تسلسل كائنات معينة لا يمكن تسلسلها بواسطة pickle أو joblib.

12.1. نظرة عامة على سير العمل#

في سير عمل نموذجي، تتمثل الخطوة الأولى في تدريب النموذج باستخدام scikit-learn والمكتبات المتوافقة مع scikit-learn. لاحظ أن دعم مقدرات scikit-learn والطرف الثالث يختلف عبر طرق الحفظ المختلفة.

12.1.1. تدريب النموذج وحفظه#

يعتمد إنشاء نموذج مناسب على حالة الاستخدام الخاصة بك. على سبيل المثال، نقوم هنا بتدريب sklearn.ensemble.HistGradientBoostingClassifier على مجموعة بيانات iris:

>>> from sklearn import ensemble
>>> from sklearn import datasets
>>> clf = ensemble.HistGradientBoostingClassifier()
>>> X, y = datasets.load_iris(return_X_y=True)
>>> clf.fit(X, y)
HistGradientBoostingClassifier()

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

  • ONNX: أنت بحاجة إلى وقت تشغيل ONNX وبيئة مثبتة بها تبعيات مناسبة لتحميل النموذج واستخدام وقت التشغيل للحصول على تنبؤات. يمكن أن تكون هذه البيئة ضئيلة ولا تتطلب بالضرورة حتى تثبيت Python لتحميل النموذج وحساب التنبؤات. لاحظ أيضًا أن onnxruntime يتطلب عادةً ذاكرة وصول عشوائي أقل بكثير من Python لحساب التنبؤات من النماذج الصغيرة.

  • skops.io، pickle، joblib، cloudpickle: أنت بحاجة إلى بيئة Python مثبتة بها التبعيات المناسبة لتحميل النموذج والحصول على تنبؤات منه. يجب أن تحتوي هذه البيئة على نفس الحزم ونفس الإصدارات مثل البيئة التي تم تدريب النموذج فيها. لاحظ أن أيا من هذه الطرق لا تدعم تحميل نموذج تم تدريبه باستخدام إصدار مختلف من scikit-learn، وربما إصدارات مختلفة من التبعيات الأخرى مثل numpy و scipy. سيكون مصدر قلق آخر هو تشغيل النموذج المحفوظ على أجهزة مختلفة، وفي معظم الحالات يجب أن تكون قادرًا على تحميل النموذج المحفوظ على أجهزة مختلفة.

12.2. ONNX#

ONNX، أو تنسيق Open Neural Network Exchange هو الأنسب في حالات الاستخدام حيث يحتاج المرء إلى حفظ النموذج ثم استخدام المصنوعات اليدوية المحفوظة للحصول على تنبؤات دون الحاجة إلى تحميل كائن Python نفسه. كما أنه مفيد في الحالات التي تحتاج فيها بيئة التقديم إلى أن تكون خفيفة وبسيطة، لأن وقت تشغيل ONNX لا يتطلب python.

ONNX هو تسلسل ثنائي للنموذج. تم تطويره لتحسين قابلية استخدام التمثيل القابل للتشغيل البيني لنماذج البيانات. يهدف إلى تسهيل تحويل نماذج البيانات بين أطر عمل التعلم الآلي المختلفة، وتحسين قابلية نقلها على بنى الحوسبة المختلفة. المزيد من التفاصيل متاحة من ONNX tutorial. لتحويل نموذج scikit-learn إلى ONNX sklearn-onnx تم تطويره. ومع ذلك، لا يتم دعم جميع نماذج scikit-learn، وهي تقتصر على scikit-learn الأساسي ولا تدعم معظم مقدرات الطرف الثالث. يمكن للمرء كتابة محول مخصص لمقدرات الطرف الثالث أو المخصصة، لكن الوثائق للقيام بذلك متفرقة وقد يكون من الصعب القيام بذلك.

استخدام ONNX#

لتحويل النموذج إلى تنسيق ONNX، تحتاج إلى إعطاء المحول بعض المعلومات حول الإدخال أيضًا، والتي يمكنك قراءة المزيد عنها هنا:

from skl2onnx import to_onnx
onx = to_onnx(clf, X[:1].astype(numpy.float32), target_opset=12)
with open("filename.onnx", "wb") as f:
    f.write(onx.SerializeToString())

يمكنك تحميل النموذج في Python واستخدام وقت تشغيل ONNX للحصول على تنبؤات:

from onnxruntime import InferenceSession
with open("filename.onnx", "rb") as f:
    onx = f.read()
sess = InferenceSession(onx, providers=["CPUExecutionProvider"])
pred_ort = sess.run(None, {"X": X_test.astype(numpy.float32)})[0]

12.3. skops.io#

يتجنب skops.io استخدام pickle ويحمل فقط الملفات التي تحتوي على أنواع وإشارات إلى وظائف موثوق بها إما افتراضيًا أو بواسطة المستخدم. لذلك، فإنه يوفر تنسيقًا أكثر أمانًا من pickle و joblib و cloudpickle.

استخدام skops#

تشبه واجهة برمجة التطبيقات إلى حد كبير pickle، ويمكنك حفظ نماذجك كما هو موضح في التوثيق باستخدام skops.io.dump و skops.io.dumps:

import skops.io as sio
obj = sio.dump(clf, "filename.skops")

ويمكنك تحميلها مرة أخرى باستخدام skops.io.load و skops.io.loads. ومع ذلك، تحتاج إلى تحديد الأنواع التي تثق بها. يمكنك الحصول على أنواع غير معروفة موجودة في كائن / ملف تم تفريغه باستخدام skops.io.get_untrusted_types، وبعد التحقق من محتوياته، قم بتمريره إلى دالة التحميل:

unknown_types = sio.get_untrusted_types(file="filename.skops")
# تحقق من محتويات unknown_types، وقم بالتحميل فقط إذا كنت تثق
# كل ما تراه.
clf = sio.load("filename.skops", trusted=unknown_types)

يرجى الإبلاغ عن المشكلات وطلبات الميزات المتعلقة بهذا التنسيق على متعقب مشكلات skops.

12.4. pickle و joblib و cloudpickle#

تستخدم هذه الوحدات / الحزم الثلاثة بروتوكول pickle تحت الغطاء، ولكنها تأتي مع اختلافات طفيفة:

  • pickle هي وحدة من مكتبة Python القياسية. يمكنه تسلسل وإلغاء تسلسل أي كائن Python، بما في ذلك فئات وكائنات Python المخصصة.

  • joblib أكثر كفاءة من pickle عند العمل مع نماذج التعلم الآلي الكبيرة أو مصفوفات numpy الكبيرة.

  • يمكن لـ cloudpickle تسلسل كائنات معينة لا يمكن تسلسلها بواسطة pickle أو joblib، مثل الوظائف المحددة من قبل المستخدم ووظائف lambda. يمكن أن يحدث ذلك على سبيل المثال، عند استخدام FunctionTransformer واستخدام دالة مخصصة لتحويل البيانات.

استخدام pickle أو joblib أو cloudpickle#

اعتمادًا على حالة الاستخدام الخاصة بك، يمكنك اختيار إحدى هذه الطرق الثلاث لحفظ وتحميل نموذج scikit-learn الخاص بك، وتتبع جميعها نفس واجهة برمجة التطبيقات:

# هنا يمكنك استبدال pickle بـ joblib أو cloudpickle
from pickle import dump
with open("filename.pkl", "wb") as f:
    dump(clf, f, protocol=5)

يوصى باستخدام protocol=5 لتقليل استخدام الذاكرة وجعلها أسرع لتخزين وتحميل أي مصفوفة NumPy كبيرة مخزنة كسمة مناسبة في النموذج. يمكنك بدلاً من ذلك تمرير protocol=pickle.HIGHEST_PROTOCOL وهو ما يعادل protocol=5 في Python 3.8 والإصدارات الأحدث (في وقت كتابة هذا التقرير).

ولاحقًا عند الحاجة، يمكنك تحميل نفس الكائن من الملف المحفوظ:

# هنا يمكنك استبدال pickle بـ joblib أو cloudpickle
from pickle import load
with open("filename.pkl", "rb") as f:
    clf = load(f)

12.5. قيود الأمان والصيانة#

picklejoblib و clouldpickle بالتمديد)، لديه العديد من نقاط الضعف الأمنية الموثقة عن طريق التصميم ويجب استخدامه فقط إذا كانت المصنوعات اليدوية، أي ملف pickle، قادمة من مصدر موثوق به وموثق. يجب ألا تقوم أبدًا بتحميل ملف pickle من مصدر غير موثوق به، على غرار الطريقة التي يجب ألا تقوم بها أبدًا بتنفيذ كود من مصدر غير موثوق به.

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

لاحظ أيضًا أنه لا توجد طرق مدعومة لتحميل نموذج تم تدريبه باستخدام إصدار مختلف من scikit-learn. أثناء استخدام skops.io أو joblib أو pickle أو cloudpickle، قد يتم تحميل النماذج المحفوظة باستخدام إصدار واحد من scikit-learn في إصدارات أخرى، ومع ذلك، فإن هذا غير مدعوم تمامًا وغير مستحسن. يجب أيضًا أن يؤخذ في الاعتبار أن العمليات التي يتم إجراؤها على هذه البيانات يمكن أن تعطي نتائج مختلفة وغير متوقعة، أو حتى تعطل عملية Python الخاصة بك.

من أجل إعادة بناء نموذج مشابه مع الإصدارات المستقبلية من scikit-learn، يجب حفظ بيانات وصفية إضافية جنبًا إلى جنب مع النموذج المخلل:

  • بيانات التدريب، على سبيل المثال مرجع لقطة غير قابلة للتغيير

  • كود مصدر Python المستخدم لإنشاء النموذج

  • إصدارات scikit-learn وتبعياتها

  • درجة التحقق المتبادل التي تم الحصول عليها على بيانات التدريب

يجب أن يجعل ذلك من الممكن التحقق من أن درجة التحقق المتبادل تقع في نفس النطاق كما كان من قبل.

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

إذا كنت تريد معرفة المزيد عن هذه المشكلات، فيرجى الرجوع إلى هذه المحادثات:

12.5.1. تكرار بيئة التدريب في الإنتاج#

إذا كانت إصدارات التبعيات المستخدمة قد تختلف من التدريب إلى الإنتاج، فقد يؤدي ذلك إلى سلوك غير متوقع وأخطاء أثناء استخدام النموذج المدرب. لمنع مثل هذه المواقف، يوصى باستخدام نفس التبعيات والإصدارات في كل من بيئة التدريب والإنتاج. يمكن تثبيت هذه التبعيات المتعدية بمساعدة أدوات إدارة الحزم مثل pip و mamba و conda و poetry و conda-lock و pixi وما إلى ذلك.

ليس من الممكن دائمًا تحميل نموذج تم تدريبه باستخدام إصدارات أقدم من مكتبة scikit-learn وتبعياتها في بيئة برامج محدثة. بدلاً من ذلك، قد تحتاج إلى إعادة تدريب النموذج باستخدام الإصدارات الجديدة من جميع المكتبات. لذلك عند تدريب نموذج، من المهم تسجيل وصفة التدريب (على سبيل المثال، نص Python) ومعلومات مجموعة التدريب، والبيانات الوصفية حول جميع التبعيات لتكون قادرًا على إعادة بناء نفس بيئة التدريب تلقائيًا للبرامج المحدثة.

InconsistentVersionWarning#

عندما يتم تحميل مقدر باستخدام إصدار scikit-learn غير متوافق مع الإصدار الذي تم تخليل المقدر به، يتم رفع InconsistentVersionWarning. يمكن التقاط هذا التحذير للحصول على الإصدار الأصلي الذي تم تخليل المقدر به:

from sklearn.exceptions import InconsistentVersionWarning
warnings.simplefilter("error", InconsistentVersionWarning)

try:
    with open("model_from_prevision_version.pickle", "rb") as f:
        est = pickle.load(f)
except InconsistentVersionWarning as w:
    print(w.original_sklearn_version)

12.5.2. تقديم قطعة أثرية النموذج#

الخطوة الأخيرة بعد تدريب نموذج scikit-learn هي تقديم النموذج. بمجرد تحميل النموذج المدرب بنجاح، يمكن تقديمه لإدارة طلبات التنبؤ المختلفة. قد يتضمن ذلك نشر النموذج كخدمة ويب باستخدام الحاويات، أو استراتيجيات نشر النماذج الأخرى، وفقًا للمواصفات.

12.6. تلخيص النقاط الرئيسية#

بناءً على الأساليب المختلفة لحفظ النموذج، يمكن تلخيص النقاط الرئيسية لكل نهج على النحو التالي:

  • ONNX: يوفر تنسيقًا موحدًا لحفظ أي نموذج للتعلم الآلي أو التعلم العميق (بخلاف scikit-learn) وهو مفيد للاستدلال على النموذج (التنبؤات). ومع ذلك، يمكن أن يؤدي إلى مشكلات توافق مع أطر عمل مختلفة.

  • skops.io: يمكن مشاركة نماذج scikit-learn المدربة بسهولة ووضعها في الإنتاج باستخدام skops.io. إنه أكثر أمانًا مقارنة بالطرق البديلة القائمة على pickle لأنه لا يقوم بتحميل كود عشوائي إلا إذا طلب المستخدم ذلك صراحةً. يجب تعبئة هذا الرمز واستيراده في بيئة Python المستهدفة.

  • joblib: تجعله تقنيات تعيين الذاكرة الفعالة أسرع عند استخدام نفس النموذج المحفوظ في عمليات Python متعددة عند استخدام mmap_mode="r". كما أنه يوفر اختصارات سهلة لضغط وفك ضغط الكائن المحفوظ دون الحاجة إلى رمز إضافي. ومع ذلك، فقد يؤدي إلى تنفيذ كود ضار عند تحميل نموذج من مصدر غير موثوق به مثل أي آلية حفظية أخرى تعتمد على pickle.

  • pickle: إنه أصلي لبايثون ويمكن تسلسل معظم كائنات Python وإلغاء تسلسلها باستخدام pickle، بما في ذلك فئات ووظائف Python المخصصة طالما تم تعريفها في حزمة يمكن استيرادها في البيئة المستهدفة. بينما يمكن استخدام pickle لحفظ وتحميل نماذج scikit-learn بسهولة، فقد يؤدي إلى تنفيذ كود ضار أثناء تحميل نموذج من مصدر غير موثوق به. يمكن أيضًا أن يكون pickle فعالًا جدًا من حيث الذاكرة إذا تم حفظ النموذج باستخدام protocol=5 ولكنه لا يدعم تعيين الذاكرة.

  • cloudpickle: لديه كفاءة تحميل مماثلة لـ pickle و joblib (بدون تعيين الذاكرة)، ولكنه يوفر مرونة إضافية لتسلسل كود Python المخصص مثل تعبيرات lambda والوظائف والفئات المحددة بشكل تفاعلي. قد يكون الملاذ الأخير لحفظ خطوط الأنابيب مع مكونات Python المخصصة مثل sklearn.preprocessing.FunctionTransformer التي تغلف دالة محددة في نص التدريب نفسه أو بشكل عام خارج أي حزمة Python قابلة للاستيراد. لاحظ أن cloudpickle لا يقدم أي ضمانات للتوافق الأمامي وقد تحتاج إلى نفس إصدار cloudpickle لتحميل النموذج المحفوظ جنبًا إلى جنب مع نفس إصدار جميع المكتبات المستخدمة لتعريف النموذج. مثل آليات الحفظ الأخرى القائمة على pickle، فقد يؤدي إلى تنفيذ كود ضار أثناء تحميل نموذج من مصدر غير موثوق به.