اگر تنظیم فراپارامترهای زیادی انجام داده باشید، عملکرد روی مجموعهٔ آزمون معمولاً کمی بدتر از چیزی است که با Cross-Validation اندازه گرفته بودید. علت این است که سامانه در پایان برای عملکرد خوب روی دادهٔ اعتبارسنجی تنظیم شده و احتمال دارد روی مجموعهدادههای ناشناخته به همان خوبی کار نکند. در این مثال چنین اتفاقی رخ نداده است، زیرا RMSE آزمون از RMSE اعتبارسنجی کمتر است؛ اما هرگاه این حالت پیش آمد باید در برابر وسوسهٔ تنظیم دوبارهٔ فراپارامترها برای بهترکردن اعداد مجموعهٔ آزمون مقاومت کنید، چون چنین بهبودهایی احتمالاً به دادهٔ جدید تعمیم پیدا نمیکنند.
مرحلهٔ پیش از راهاندازی
اکنون به مرحلهٔ پیش از راهاندازی پروژه رسیدهاید. باید راهحل خود را ارائه کنید: آنچه آموختهاید، آنچه خوب کار کرده یا نکرده، فرضهایی که انجام شدهاند و محدودیتهای سامانه را برجسته کنید. همهچیز را مستند کنید و ارائههای مناسبی با مصورسازیهای روشن و گزارههای ساده و بهیادماندنی بسازید؛ برای نمونه «درآمد میانه مهمترین پیشبین قیمت مسکن است».
در مثال مسکن کالیفرنیا، عملکرد نهایی سامانه چندان بهتر از تخمین کارشناسان نیست؛ تخمینهایی که اغلب حدود ۳۰ درصد خطا داشتند. بااینحال راهاندازی سامانه ممکن است همچنان تصمیم خوبی باشد، بهویژه اگر بخشی از زمان کارشناسان آزاد شود تا روی کارهای جالبتر و بهرهورتری تمرکز کنند.
راهاندازی، پایش و نگهداری سامانه
عالی است؛ مجوز راهاندازی را گرفتهاید. اکنون باید راهحل را برای محیط عملیاتی آماده کنید: کد را تمیز و نهایی کنید، مستندات و آزمونها را بنویسید و کارهای مشابه را انجام دهید. سپس میتوانید مدل را در محیط Production مستقر کنید.
سادهترین روش استقرار این است که بهترین مدل آموزشدیده را ذخیره کنید، فایل را به محیط عملیاتی انتقال دهید و در آنجا بارگذاری کنید. برای ذخیرهٔ مدل میتوانید از کتابخانهٔ joblib استفاده کنید:
import joblib
joblib.dump(final_model, "my_california_housing_model.pkl")
پس از انتقال مدل به محیط عملیاتی، میتوانید آن را بارگذاری و استفاده کنید. ابتدا باید هر کلاس و تابع سفارشیای را که مدل به آن متکی است import کنید - یعنی کد آنها نیز باید به محیط عملیاتی منتقل شود - سپس مدل را با joblib بارگذاری و برای پیشبینی به کار ببرید:
import joblib
[...] # import KMeans, BaseEstimator, TransformerMixin, rbf_kernel, etc.
def column_ratio(X): [...]
def ratio_name(function_transformer, feature_names_in): [...]
class ClusterSimilarity(BaseEstimator, TransformerMixin): [...]
final_model_reloaded = joblib.load("my_california_housing_model.pkl")
new_data = [...] # some new districts to make predictions for
predictions = final_model_reloaded.predict(new_data)
استقرار مدل در یک برنامهٔ وب
برای مثال ممکن است مدل در یک وبسایت استفاده شود: کاربر دادههای یک ناحیهٔ تازه را وارد میکند و دکمهٔ «برآورد قیمت» را میزند. این کار درخواستی شامل دادهها به وبسرور میفرستد؛ وبسرور درخواست را به برنامهٔ وب انتقال میدهد و کد برنامه در نهایت متد predict() مدل را فراخوانی میکند. بهتر است مدل هنگام شروع وبسرور فقط یک بار بارگذاری شود، نه هر بار که کاربر پیشبینی میخواهد.
راه دیگر این است که مدل را درون یک Web Service اختصاصی قرار دهید و برنامهٔ وب از طریق یک REST API۱۳ با آن ارتباط برقرار کند. این معماری ارتقای مدل به نسخههای تازه را بدون قطع برنامهٔ اصلی آسان میکند. همچنین Scale کردن سرویس سادهتر میشود، زیرا میتوانید هر تعداد نمونهٔ Web Service که لازم است راهاندازی کنید و درخواستهای برنامهٔ وب را میان آنها Load Balance کنید. افزون بر این، برنامهٔ وب میتواند با هر زبان برنامهنویسی نوشته شود و محدود به Python نخواهد بود.
شکل ۲-۲۰. مدل مستقرشده بهصورت Web Service و استفاده از آن در یک برنامهٔ وب
راهبرد محبوب دیگر استقرار مدل در Cloud است؛ برای نمونه در Google Vertex AI که پیشتر با نامهای Google Cloud AI Platform و Google Cloud ML Engine شناخته میشد. کافی است مدل را با joblib ذخیره و در Google Cloud Storage یا GCS بارگذاری کنید، سپس در Vertex AI یک نسخهٔ تازهٔ مدل بسازید و به فایل GCS اشاره دهید. سرویس حاصل Load Balancing و Scaling را مدیریت میکند؛ درخواستهای JSON شامل دادهٔ ورودی - مثلاً مشخصات یک ناحیه - را میگیرد و پاسخ JSON حاوی پیشبینی را برمیگرداند. این سرویس میتواند در وبسایت یا هر محیط عملیاتی دیگر استفاده شود. در فصل ۱۹ خواهید دید استقرار مدلهای TensorFlow در Vertex AI تفاوت زیادی با استقرار مدلهای Scikit-Learn ندارد.
پایش عملکرد زنده و فرسودگی مدل
استقرار پایان کار نیست. باید کد پایش بنویسید تا عملکرد زندهٔ سامانه در فواصل منظم بررسی و هنگام افت عملکرد هشدار صادر شود. افت میتواند بسیار سریع باشد؛ مثلاً یکی از مولفههای زیرساخت خراب شود. اما ممکن است بسیار آهسته رخ دهد و مدت زیادی دیده نشود. این پدیده بهدلیل Model Rot (فرسودگی مدل) رایج است: مدلی که با دادهٔ سال قبل آموزش دیده شاید دیگر با دادهٔ امروز سازگار نباشد.
بنابراین باید عملکرد زندهٔ مدل را پایش کنید. شیوهٔ انجام آن به مسئله بستگی دارد. گاهی عملکرد مدل از معیارهای پاییندستی قابل استنباط است. مثلاً اگر مدل بخشی از سامانهٔ پیشنهاددهنده باشد و محصولاتی را پیشنهاد کند که کاربران شاید به آنها علاقهمند شوند، میتوانید تعداد محصولات پیشنهادی فروختهشده در هر روز را پایش کنید. اگر این عدد نسبت به محصولات غیرپیشنهادی کاهش یابد، نخستین مظنون مدل است. ممکن است Pipeline داده خراب شده باشد یا مدل نیاز به آموزش دوباره با دادهٔ تازه داشته باشد.
در بعضی مسائل برای سنجش عملکرد به تحلیل انسانی نیاز دارید. فرض کنید یک مدل طبقهبندی تصویر آموزش دادهاید تا انواع عیب محصول را در خط تولید تشخیص دهد. چگونه پیش از آنکه هزاران محصول معیوب برای مشتری ارسال شوند از افت عملکرد مدل باخبر میشوید؟ یک راه این است که نمونهای از تمام عکسهای طبقهبندیشده - بهویژه عکسهایی که مدل دربارهٔ آنها اطمینان کمی داشته - برای ارزیابهای انسانی فرستاده شود. بسته به مسئله، ارزیابها شاید کارشناس باشند یا افراد عادی، مثلاً کارکنان یک سکوی Crowdsourcing مانند Amazon Mechanical Turk. در بعضی برنامهها حتی میتوان از کاربران خود سامانه کمک گرفت، مثلاً از طریق نظرسنجی یا استفادهٔ دوباره از CAPTCHAها.۱۴
در هر صورت باید سامانهٔ پایش، با یا بدون ارزیاب انسانی، و فرایندهای مرتبط برای تعیین اقدامات هنگام خرابی و آمادگی برای آن را پیادهسازی کنید. متأسفانه این کار میتواند بسیار زیاد باشد؛ در عمل گاهی بسیار بیشتر از ساخت و آموزش خود مدل زمان میبرد.
خودکارسازی بازآموزی و ارزیابی
اگر داده مرتب تغییر کند، باید مجموعهدادهها را بهروز کنید و مدل را منظم دوباره آموزش دهید. بهتر است تا حد امکان کل فرایند را خودکار کنید. چند مورد قابل خودکارسازی عبارتاند از:
- جمعآوری منظم دادهٔ تازه و برچسبگذاری آن، برای نمونه با ارزیابهای انسانی.
- نوشتن اسکریپتی برای آموزش مدل و تنظیم خودکار فراپارامترها. بسته به نیاز، اسکریپت میتواند روزانه یا هفتگی اجرا شود.
- نوشتن اسکریپت دیگری که مدل جدید و مدل قبلی را روی مجموعهٔ آزمون بهروزشده ارزیابی کند و اگر عملکرد افت نکرده بود مدل جدید را وارد محیط عملیاتی نماید. اگر عملکرد کاهش یافت باید علت بررسی شود. بهتر است اسکریپت عملکرد مدل را روی زیرمجموعههای مختلف مجموعهٔ آزمون نیز بسنجد، مانند ناحیههای فقیر یا ثروتمند، روستایی یا شهری و مانند آن.
پایش کیفیت دادهٔ ورودی و امکان Rollback
کیفیت دادهٔ ورودی مدل را نیز ارزیابی کنید. گاهی عملکرد بهدلیل سیگنال کمکیفیت اندکی کاهش مییابد؛ مثلاً سنسوری خراب مقدارهای تصادفی میفرستد، یا خروجی تیم دیگری که به مدل شما میرسد کهنه میشود. ممکن است مدتی طول بکشد تا عملکرد آنقدر بد شود که هشدار اصلی فعال گردد. با پایش ورودیهای مدل میتوانید مشکل را زودتر پیدا کنید؛ مثلاً اگر تعداد بیشتری از ورودیها یک ویژگی را از دست بدهند، میانگین یا انحراف معیار بیش از حد از مجموعهٔ آموزشی فاصله بگیرد، یا ویژگی دستهای شروع به دریافت دستههای تازه کند.
در پایان، از همهٔ مدلهای ساختهشده نسخهٔ پشتیبان نگه دارید و فرایند و ابزارهای لازم برای Rollback سریع به مدل قبلی را آماده کنید، چون ممکن است مدل جدید به هر دلیل شدیداً خراب شود. نسخههای پشتیبان مقایسهٔ آسان مدلهای تازه با مدلهای قبلی را نیز ممکن میکنند. به همین شکل، از هر نسخهٔ مجموعهداده هم نسخهٔ پشتیبان داشته باشید تا اگر نسخهٔ جدید خراب شد - مثلاً دادهٔ تازه پر از دادهٔ پرت بود - بتوانید به نسخهٔ قبلی برگردید. نگهداشتن نسخههای مختلف مجموعهداده همچنین اجازه میدهد هر مدل را روی هر نسخهٔ پیشین داده ارزیابی کنید.
MLOps و زیرساخت یادگیری ماشین
همانطور که میبینید، یادگیری ماشین به زیرساخت قابلتوجهی نیاز دارد. فصل ۱۹ بعضی جنبهها را بررسی میکند، اما موضوع بسیار گستردهای با نام ML Operations (MLOps) است و کتاب مستقلی میطلبد. بنابراین اگر نخستین پروژهٔ یادگیری ماشین برای ساخت و استقرار عملیاتی به زمان و تلاش زیادی نیاز داشت تعجب نکنید. خبر خوب این است که پس از آمادهشدن زیرساخت، فاصلهٔ ایده تا Production بسیار سریعتر طی خواهد شد.
حالا خودتان امتحان کنید!
امیدواریم این فصل تصویر خوبی از شکل یک پروژهٔ یادگیری ماشین و ابزارهای قابل استفاده برای آموزش سامانهای خوب ارائه کرده باشد. همانطور که دیده شد، بخش بزرگی از کار در آمادهسازی داده، ساخت ابزارهای پایش، راهاندازی Pipelineهای ارزیابی انسانی و خودکارسازی آموزش منظم مدل قرار دارد. الگوریتمهای یادگیری ماشین مهماند، اما احتمالاً بهتر است بر کل فرایند مسلط باشید و سه یا چهار الگوریتم را خوب بشناسید تا اینکه تمام وقت را صرف بررسی الگوریتمهای پیشرفته کنید.
اگر هنوز این کار را نکردهاید، اکنون زمان خوبی است لپتاپ را بردارید، مجموعهدادهای که به آن علاقه دارید انتخاب کنید و کل فرایند را از ابتدا تا انتها طی کنید. وبسایتهای مسابقه مانند Kaggle نقطهٔ شروع مناسبیاند: مجموعهدادهای برای کار دارید، هدف روشن است و افرادی هستند که تجربه را با آنها به اشتراک بگذارید. از کار لذت ببرید.
تمرینها
تمرینهای زیر بر مجموعهدادهٔ مسکن همین فصل استوارند:
- یک رگرسور ماشین بردار پشتیبان
sklearn.svm.SVR را با فراپارامترهای مختلف امتحان کنید؛ مثلاً kernel="linear" با مقادیر گوناگون فراپارامتر C یا kernel="rbf" با مقادیر متفاوت C و gamma. ماشینهای بردار پشتیبان روی مجموعهدادههای بزرگ خوب مقیاس نمیشوند؛ بنابراین بهتر است مدل را فقط روی ۵۰۰۰ نمونهٔ نخست مجموعهٔ آموزشی و با Cross-Validation سهتایی آموزش دهید، وگرنه ممکن است ساعتها طول بکشد. فعلاً نگران معنای این فراپارامترها نباشید؛ در فصل ۵ بررسی میشوند. بهترین Predictor از نوع SVR چه عملکردی دارد؟
GridSearchCV را با RandomizedSearchCV جایگزین کنید.
- Transformer از نوع
SelectFromModel را به Pipeline آمادهسازی اضافه کنید تا فقط مهمترین ویژگیها انتخاب شوند.
- یک Transformer سفارشی بسازید که در متد
fit() یک رگرسور k نزدیکترین همسایه، sklearn.neighbors.KNeighborsRegressor، را آموزش دهد و در transform() پیشبینیهای مدل را خروجی دهد. سپس این ویژگی را با ورودیهای عرض و طول جغرافیایی به Pipeline پیشپردازش اضافه کنید. ویژگی جدید تقریباً قیمت میانهٔ مسکن ناحیههای نزدیک را در اختیار مدل قرار میدهد.
- با
GridSearchCV بعضی گزینههای آمادهسازی داده را بهصورت خودکار جستوجو کنید.
- کلاس
StandardScalerClone را دوباره از صفر پیادهسازی کنید، سپس پشتیبانی از inverse_transform() را اضافه کنید؛ اجرای scaler.inverse_transform(scaler.fit_transform(X)) باید آرایهای بسیار نزدیک به X برگرداند. بعد پشتیبانی از نام ویژگیها را اضافه کنید: اگر ورودی در fit() یک DataFrame بود، feature_names_in_ را بهصورت آرایهٔ NumPy از نام ستونها تنظیم کنید. در پایان get_feature_names_out() را با آرگومان اختیاری input_features=None پیاده کنید. اگر input_features داده شده بود، طولش باید با n_features_in_ برابر باشد و در صورت تعریف feature_names_in_ با آن مطابقت کند؛ سپس همان input_features برگردانده شود. اگر input_features برابر None بود، در صورت وجود feature_names_in_ همان مقدار برگردد، و در غیر این صورت آرایهای مانند np.array(["x0", "x1", ...]) با طول n_features_in_ تولید شود.
راهحل تمرینها در انتهای Notebook همین فصل، در نشانی https://homl.info/colab3 موجود است.