شعار زيفيرنت

اختبار فعال لتعلم الآلة

التاريخ:

By إدواردو بلانكاس، المؤسس المشارك @ Ploomber.io

قدمنا ​​نسخة أقصر من سلسلة المدونات هذه في باي داتا العالمية 2021.

انقر هنا للجزء الثانيهنا للجزء الثالث.

اختبار فعال لتعلم الآلة


 

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

التحديات عند اختبار مشاريع ML

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

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

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

أجزاء من خط أنابيب التعلم الآلي

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

  1. خط أنابيب توليد الميزات. سلسلة من العمليات الحسابية للمعالجة مسودة بيانات وقم بتعيين كل نقطة بيانات إلى ناقل المعالم. لاحظ أننا نستخدم هذا المكون في التدريب ووقت الخدمة.
  2. مهمة التدريب. يأخذ مجموعة تدريب وينتج ملف نموذجي.
  3. ملف النموذج. ناتج مهمة التدريب. إنه ملف واحد يحتوي على نموذج مع معلمات تم تعلمها. بالإضافة إلى ذلك ، قد يتضمن معالجات مسبقة مثل التحجيم أو ترميز واحد ساخن.
  4. خط أنابيب التدريب. تلخص منطق التدريب: الحصول على البيانات الأولية ، وإنشاء الميزات ، وتدريب النماذج.
  5. خط أنابيب الخدمة. (المعروف أيضًا باسم خط أنابيب الاستدلال) يلخص منطق العرض: يحصل على ملاحظة جديدة ، ويولد الميزات ، ويمرر الميزات عبر النموذج ، ويعيد توقعًا.

اختبار فعال لتعلم الآلة

ما يمكن أن تذهب الخطأ؟

 
لتحفيز إستراتيجية الاختبار الخاصة بنا ، دعنا نعدد ما يمكن أن يحدث خطأ في كل جزء:
 

خط أنابيب توليد الميزات

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

مهمة التدريب

  1. لا يمكن تدريب نموذج (على سبيل المثال ، التبعيات المفقودة ، التعليمات البرمجية المعطلة).
  2. يؤدي تشغيل مهمة التدريب ببيانات عالية الجودة إلى إنتاج نماذج منخفضة الجودة.

ملف النموذج

  1. يتميز النموذج الذي تم إنشاؤه بجودة أقل من نموذجنا الحالي في الإنتاج.
  2. لا يتكامل ملف النموذج بشكل صحيح مع خط أنابيب التقديم.

خط أنابيب الخدمة

  1. لا يمكن تقديم التنبؤات (على سبيل المثال ، التبعيات المفقودة ، التعليمات البرمجية المعطلة).
  2. عدم التوافق بين المعالجة المسبقة في التدريب ووقت الخدمة (ويعرف أيضًا باسم انحراف خدمة التدريب).
  3. يخرج توقعًا عند تمرير بيانات أولية غير صالحة.
  4. تعطل عند تمرير بيانات صحيحة.

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

استراتيجية الاختبار

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

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

مستويات الاختبار

  1. اختبار الدخان. نحن نضمن أن الكود الخاص بنا يعمل عن طريق تشغيله على كل منهما git push.
  2. اختبار التكامل واختبار الوحدة. اختبار مخرجات المهمة وتحويلات البيانات.
  3. تغييرات التوزيع وخدمة خط الأنابيب. اختبار التغييرات في توزيعات البيانات واختبار يمكننا تحميل ملف نموذج والتنبؤ.
  4. انحراف خدمة التدريب. اختبر أن منطق التدريب والخدمة متسق.
  5. جودة النموذج. جودة نموذج الاختبار.

مقدمة سريعة للاختبار بـ pytest

 
إذا كنت قد استخدمت pytest من قبل ، يمكنك تخطي هذا القسم.

الاختبارات عبارة عن برامج قصيرة تتحقق مما إذا كانت التعليمات البرمجية الخاصة بنا تعمل أم لا. فمثلا:

# test_math.py
from my_math_project import add, subtract def test_add(): assert add(1, 1) == 2 def test_subtract(): assert subtract(43, 1) == 42

الاختبار هو وظيفة تقوم بتشغيل بعض التعليمات البرمجية ، و يؤكد ناتجها. على سبيل المثال ، يحتوي الملف السابق على اختبارين: test_add و  test_substract، منظم في ملف يسمى test_math.py؛ من المعتاد أن يكون لديك ملف واحد لكل وحدة (على سبيل المثال ، test_math.py يختبر جميع الوظائف في أ math.py وحدة). عادةً ما تخضع ملفات الاختبار لامتداد tests/ دليل:

tests/ test_math.py test_stuff.py ... test_other.py

اختبار الأطر مثل بيتيست تسمح لك بجمع جميع اختباراتك وتنفيذها والإبلاغ عن أي منها فشل وأي منها ينجح:

# collect rests, run them, and report results
pytest

يبدو هيكل المشروع النموذجي كما يلي:

src/
exploratory/
tests/

src/ يحتوي على مهام خط أنابيب مشروعك ووظائف المرافق الأخرى. exploratory/ يتضمن دفاتر ملاحظات استكشافية وتنتقل اختباراتك إلى tests/ الدليل. الكود في src/ يجب أن يكون قابلاً للاستيراد من المجلدين الآخرين. أسهل طريقة لتحقيق ذلك هي حزم مشروعك. خلاف ذلك ، عليك أن تتلاعب sys.path or PYTHONPATH.

كيفية التنقل في نموذج التعليمات البرمجية

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

ينفذ المشروع خط الأنابيب باستخدام بلومبر ، إطار عملنا مفتوح المصدر. ومن ثم ، يمكنك رؤية مواصفات خط الأنابيب في ملف pipeline.yaml ملف. لمعرفة الأوامر التي نستخدمها لاختبار خط الأنابيب ، افتح .github/workflows/ci.yml، هذا ملف تكوين إجراءات GitHub يخبر GitHub بتشغيل أوامر معينة على كل منها git push.

رغم أنه ليس ضروريًا تمامًا ، فقد ترغب في التحقق من موقعنا البرنامج التعليمي التمهيدي بلومبر لفهم المفاهيم الأساسية.

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

المستوى 1: اختبار الدخان

 
رمز عينة متاح هنا.

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

توثيق التبعيات

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

ploomber
scikit-learn
pandas

بعد إنشاء البيئة الافتراضية الخاصة بك ، قم بإنشاء ملف آخر (requirements.lock.txt) لتسجيل الإصدارات المثبتة لجميع التبعيات. يمكنك القيام بذلك باستخدام pip freeze > requirements.lock.txt الأمر (قم بتنفيذه بعد التشغيل pip install -r requirements.txt) ، والذي يولد شيئًا مثل هذا:

ploomber==0.13
scikit-learn==0.24.2
pandas==1.2.4
# more packages required by your dependencies...

يضمن تسجيل إصدارات تبعية محددة أن التغييرات من أي من هذه الحزم لا تؤدي إلى تعطيل مشروعك.

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

اختبار خطوط أنابيب توليد الميزات

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

بمجرد حصولك على هذا ، حان الوقت لتنفيذ اختبارنا الأول ؛ تشغيل خط الأنابيب مع عينة من البيانات الخام (على سبيل المثال ، 1٪). الهدف هو جعل هذا الاختبار يعمل بسرعة (ليس أكثر من بضع دقائق). سيبدو اختبارك كالتالي:

from my_project import generate_features_and_label def test_generate_training_set(): my_project.generate_features_and_label(sample=True)

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

اختبار مهمة التدريب

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

from my_project import generate_features_and_label, train_model def test_train_model(): # test we can generate features X, y = my_project.generate_features_and_label(sample=True) # test we can train a model model = train_model(X, y)

في مخزن العينات ، نستخدم Ploomber ، لذلك نقوم باختبار خط أنابيب الميزة ومهمة التدريب عن طريق الاتصال ploomber build، والذي ينفذ جميع المهام في خط أنابيبنا.

المستوى 2: اختبار التكامل واختبار الوحدة

 
رمز عينة متاح هنا.

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

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

دعونا نناقش الهدف الأول.

اختبار التكامل

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

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

اختبار فعال لتعلم الآلة

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

def clean(df) # clean data frame with raw data # ... # ... # integration test: check age column has a minimum value of 0 assert df.age.min() > 0

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

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

هذا هو التنفيذ لاختبار التكامل في مستودع العينات لدينا.

اختبار وحدة

 
ضمن كل مهمة في خط الأنابيب الخاص بك (على سبيل المثال ، في الداخل clean) ، من المحتمل أن يكون لديك إجراءات أصغر ؛ يجب كتابة هذه الأجزاء من التعليمات البرمجية كوظائف منفصلة ووحدة مُختبرة (على سبيل المثال ، إضافة اختبارات في ملف tests/ الدليل).

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

import transform def clean(df): # some data cleaning code... # ... df['chest_pain_type'] = transform.chest_pain_type(df.chest_pain_type) # ... # more data cleaning code...

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

def test_transform_chest_pain_type(): # sample input series = pd.Series([0, 1, 2, 3]) # expected output expected = pd.Series([ 'typical angina', 'atypical angina', 'non-anginal pain', 'asymptomatic', ]) # test assert transform.chest_pain_type(series).equals(expected)

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

هذا هو التنفيذ اختبار الوحدة في مستودع العينة.

التالي

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

إذا كنت ترغب في معرفة موعد خروج الجزء الثاني ، فقم بالاشتراك في نشرتنا الإخبارية، اتبعنا تويتر or لينكدين:.

 
 
إدواردو بلانكاس هو أحد مؤسسي Ploomber. Ploomber هي شركة مدعومة من Y Combinator تساعد علماء البيانات في بناء خطوط أنابيب البيانات بسرعة. نقوم بذلك من خلال السماح لهم بتطوير خطوط أنابيب معيارية ونشرها في أي مكان. قبل ذلك ، كان عالم بيانات في Fidelity Investments ، حيث نشر أول نموذج تعلم آلي يواجه العملاء لإدارة الأصول. يحمل إدواردو ماجستير في علوم البيانات من جامعة كولومبيا وبكالوريوس في هندسة الميكاترونكس من Tecnológico de Monterrey.

بقعة_صورة

أحدث المعلومات الاستخباراتية

بقعة_صورة