ادامهٔ Gradient Boosting
پس از آموزش نخستین DecisionTreeRegressor، درخت دوم را روی خطاهای باقیماندهٔ مدل اول آموزش میدهیم:
y2 = y - tree_reg1.predict(X)
tree_reg2 = DecisionTreeRegressor(max_depth=2, random_state=43)
tree_reg2.fit(X, y2)
سپس درخت سوم روی خطاهای باقیماندهٔ مدل دوم آموزش داده میشود:
y3 = y2 - tree_reg2.predict(X)
tree_reg3 = DecisionTreeRegressor(max_depth=2, random_state=44)
tree_reg3.fit(X, y3)
اکنون Ensemble سه درخت دارد. برای پیشبینی کافی است خروجی هر سه درخت جمع شود:
>>> X_new = np.array([[-0.4], [0.], [0.5]])
>>> sum(tree.predict(X_new)
... for tree in (tree_reg1, tree_reg2, tree_reg3))
array([0.49484029, 0.04021166, 0.75026781])
شکل ۷-۹ پیشبینی سه درخت را در ستون چپ و پیشبینی Ensemble را در ستون راست نشان میدهد. در سطر نخست فقط یک درخت وجود دارد و خروجی Ensemble با آن یکی است. در سطر دوم درخت تازه روی Residualهای مدل اول آموزش دیده و خروجی Ensemble جمع پیشبینی دو درخت است. در سطر سوم نیز درخت دیگری خطاهای قبلی را اصلاح میکند. با افزودن درختها، پیشبینی Ensemble بهتدریج بهتر میشود.
شکل 7-9. Gradient Boosting؛ هر مدل روی residual مدل قبل آموزش میبیند
برای ساخت GBRT نیازی نیست درختها و Residualها را دستی مدیریت کنیم. کلاس GradientBoostingRegressor این فرایند را انجام میدهد و برای طبقهبندی نیز GradientBoostingClassifier وجود دارد. مانند Random Forest میتوان فراپارامترهای رشد درخت، مثل max_depth و min_samples_leaf، و همچنین فراپارامترهای خود Ensemble مانند n_estimators را تنظیم کرد. کد زیر همان Ensemble سهدرختی را میسازد:
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(
max_depth=2,
n_estimators=3,
learning_rate=1.0,
random_state=42
)
gbrt.fit(X, y)
Learning Rate و Shrinkage
فراپارامتر learning_rate سهم هر درخت در خروجی نهایی را مقیاس میکند. اگر مقدار کوچکی مانند ۰٫۰۵ انتخاب شود، برای برازش مجموعهٔ آموزشی به درختهای بیشتری نیاز است، اما مدل معمولاً بهتر تعمیم پیدا میکند. این روش منظمسازی Shrinkage نام دارد.
شکل ۷-۱۰ دو GBRT با تنظیمهای متفاوت را نشان میدهد. مدل سمت چپ تعداد کافی درخت ندارد و کمبرازش است. مدل سمت راست تقریباً تعداد مناسبی دارد. اگر به مدل دوم درختهای بیشتری اضافه شود، احتمالاً بیشبرازش آغاز خواهد شد.
شکل 7-10. GBRT با تعداد ناکافی درخت و تعداد مناسب
Early Stopping برای انتخاب تعداد درختها
برای پیدا کردن تعداد مناسب درختها میتوان از Cross-Validation همراه GridSearchCV یا RandomizedSearchCV استفاده کرد، اما راه سادهتری نیز وجود دارد. اگر n_iter_no_change مثلاً برابر ۱۰ شود، GradientBoostingRegressor وقتی ببیند افزودن ۱۰ درخت آخر بهبود معناداری ایجاد نکرده است، آموزش را خودکار متوقف میکند. این همان Early Stopping فصل ۴ است، با اندکی صبر برای چند مرحلهٔ بدون پیشرفت.
gbrt_best = GradientBoostingRegressor(
max_depth=2,
learning_rate=0.05,
n_estimators=500,
n_iter_no_change=10,
random_state=42
)
gbrt_best.fit(X, y)
اگر n_iter_no_change بیش از حد کوچک باشد، آموزش ممکن است زود متوقف شود و مدل کمبرازش بماند. اگر خیلی بزرگ باشد، خطر بیشبرازش بیشتر میشود. با وجود اینکه در مثال حداکثر ۵۰۰ Estimator تعیین شده، Early Stopping خیلی زودتر متوقف میشود:
>>> gbrt_best.n_estimators_
92
هنگامی که n_iter_no_change تنظیم شده باشد، fit() بهطور خودکار بخشی از دادهٔ آموزشی را برای Validation کنار میگذارد تا پس از افزودن هر درخت عملکرد مدل را بسنجد. اندازهٔ این بخش با validation_fraction کنترل میشود و مقدار پیشفرض آن ۱۰٪ است. فراپارامتر tol بیشترین بهبودی را مشخص میکند که هنوز «ناچیز» محسوب میشود و مقدار پیشفرض آن ۰٫۰۰۰۱ است.
Stochastic Gradient Boosting
GradientBoostingRegressor فراپارامتر subsample را نیز دارد که سهم نمونههای آموزشی استفادهشده برای هر درخت را تعیین میکند. مثلاً با subsample=0.25 هر درخت فقط روی ۲۵٪ نمونهها که تصادفی انتخاب شدهاند آموزش میبیند. این روش در برابر Bias بیشتر، Variance را کاهش میدهد و آموزش را نیز سریعتر میکند. به آن Stochastic Gradient Boosting گفته میشود.
Gradient Boosting مبتنی بر Histogram
Scikit-Learn پیادهسازی دیگری از GBRT برای مجموعهدادههای بزرگ دارد: Histogram-Based Gradient Boosting یا HGB. در این روش ویژگیهای ورودی به بازهها یا Binها تقسیم و با اعداد صحیح جایگزین میشوند. تعداد Binها با max_bins کنترل میشود؛ مقدار پیشفرض ۲۵۵ است و بالاتر از آن قابل تنظیم نیست.
Bin کردن تعداد آستانههای احتمالی را که الگوریتم باید بررسی کند بهشدت کاهش میدهد. همچنین کار با اعداد صحیح اجازه میدهد ساختارهای داده سریعتر و کمحافظهتری استفاده شوند و روش ساخت Binها نیاز به مرتبسازی ویژگیها در هنگام آموزش هر درخت را از بین میبرد.
در نتیجه پیچیدگی محاسباتی HGB تقریباً O(b × m) است، در حالی که GBRT معمولی حدود O(n × m × log(m)) هزینه دارد. در این نمادگذاری b تعداد Binها، m تعداد نمونهها و n تعداد ویژگیها است. در عمل HGB روی مجموعههای بزرگ میتواند صدها برابر سریعتر از GBRT معمولی باشد.
البته Bin کردن مقداری از دقت عددی را از بین میبرد. این کاهش دقت نقش یک منظمساز را دارد؛ بسته به داده ممکن است بیشبرازش را کم کند یا برعکس باعث کمبرازش شود.
Scikit-Learn دو کلاس HistGradientBoostingRegressor و HistGradientBoostingClassifier را ارائه میکند. تفاوتهای مهم آنها با پیادهسازی معمولی عبارتاند از:
- اگر تعداد نمونهها بیش از ۱۰٬۰۰۰ باشد، Early Stopping بهطور خودکار فعال میشود. با
early_stopping=True یا False میتوان آن را همیشه فعال یا غیرفعال کرد.
- Subsampling پشتیبانی نمیشود.
n_estimators به max_iter تغییر نام داده است.
- فقط چند فراپارامتر درخت قابل تنظیماند:
max_leaf_nodes، min_samples_leaf و max_depth.
کلاسهای HGB دو مزیت مهم دیگر هم دارند: ویژگیهای دستهای و مقادیر گمشده را مستقیماً پشتیبانی میکنند و به این ترتیب Preprocessing سادهتر میشود. ویژگیهای دستهای باید بهصورت اعداد صحیح از صفر تا مقداری کمتر از max_bins نمایش داده شوند؛ برای این کار میتوان OrdinalEncoder را به کار برد.
Pipeline زیر مدل کاملی برای دادهٔ مسکن کالیفرنیا میسازد:
from sklearn.pipeline import make_pipeline
from sklearn.compose import make_column_transformer
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.preprocessing import OrdinalEncoder
hgb_reg = make_pipeline(
make_column_transformer(
(OrdinalEncoder(), ["ocean_proximity"]),
remainder="passthrough"
),
HistGradientBoostingRegressor(
categorical_features=[0],
random_state=42
)
)
hgb_reg.fit(housing, housing_labels)
در این Pipeline به Imputer، Scaler یا One-Hot Encoder نیازی نیست. categorical_features باید شاخص ستونهای دستهای یا آرایهای Boolean باشد. بدون تنظیم خاص فراپارامترها، RMSE این مدل حدود ۴۷٬۶۰۰ است.
در زیستبوم Python پیادهسازیهای بهینهٔ دیگری از Gradient Boosting نیز وجود دارند؛ از جمله XGBoost، CatBoost و LightGBM که قابلیتهایی مانند شتابدهی GPU را ارائه میکنند. کتاب همچنین به TensorFlow Random Forests اشاره میکند که پیادهسازیهای بهینهای از Random Forest، Extra-Trees، GBRT و روشهای دیگر دارد.
Stacking
آخرین روش Ensemble این فصل Stacking یا Stacked Generalization است. ایده بسیار ساده است: بهجای استفاده از یک تابع ثابت مانند رأی اکثریت برای ترکیب خروجی مدلها، خودِ عملیات ترکیب را به یک مدل دیگر یاد میدهیم.
در شکل ۷-۱۱ سه پیشبینیکنندهٔ پایه برای یک نمونهٔ جدید مقادیر ۳٫۱، ۲٫۷ و ۲٫۹ را پیشبینی میکنند. مدل نهایی که Blender یا Meta-Learner نام دارد این سه مقدار را بهعنوان ورودی میگیرد و خروجی نهایی ۳٫۰ را تولید میکند.
شکل 7-11. تجمیع پیشبینیها با Blender
آموزش Blender
برای آموزش Blender ابتدا باید مجموعهٔ آموزشی مخصوص ترکیب ساخته شود. برای هر مدل پایه میتوان cross_val_predict() را اجرا کرد تا برای هر نمونهٔ مجموعهٔ آموزشی اصلی، پیشبینی خارج از نمونه ایجاد شود. این پیشبینیها ویژگیهای ورودی مجموعهٔ آموزشی Blender هستند و Target همان Target اصلی است.
تعداد ویژگیهای مجموعهٔ Blender مستقل از تعداد ویژگیهای دادهٔ اصلی است و برابر تعداد پیشبینیکنندههای پایه خواهد بود. اگر سه مدل پایه داشته باشیم، برای هر نمونه سه ویژگی ــ یعنی سه پیشبینی ــ داریم. پس از آموزش Blender، مدلهای پایه یک بار دیگر روی کل مجموعهٔ آموزشی اصلی آموزش داده میشوند.
شکل 7-12. آموزش Blender در Stacking
میتوان چند Blender متفاوت ساخت؛ مثلاً یکی Linear Regression و دیگری Random Forest Regression. سپس خروجی این Blenderها به یک لایهٔ بالاتر داده شود تا یک Blender دیگر نتیجهٔ نهایی را بسازد. شکل ۷-۱۳ چنین Stacking چندلایهای را نشان میدهد. شاید این ساختار کمی عملکرد را بهتر کند، اما هزینهٔ آموزش و پیچیدگی سامانه نیز افزایش مییابد.
شکل 7-13. پیشبینی در Stacking چندلایه
Stacking در Scikit-Learn
Scikit-Learn دو کلاس StackingClassifier و StackingRegressor دارد. نمونهٔ زیر VotingClassifier فصل را با StackingClassifier جایگزین میکند:
from sklearn.ensemble import StackingClassifier
stacking_clf = StackingClassifier(
estimators=[
('lr', LogisticRegression(random_state=42)),
('rf', RandomForestClassifier(random_state=42)),
('svc', SVC(probability=True, random_state=42))
],
final_estimator=RandomForestClassifier(random_state=43),
cv=5 # number of cross-validation folds
)
stacking_clf.fit(X_train, y_train)
برای هر پیشبینیکننده، Stacking ابتدا تلاش میکند predict_proba() را صدا بزند؛ اگر موجود نباشد از decision_function() و در آخر از predict() استفاده میکند. اگر Final Estimator مشخص نشود، StackingClassifier بهطور پیشفرض LogisticRegression و StackingRegressor از RidgeCV استفاده خواهد کرد.
در مثال کتاب، این Stacking روی مجموعهٔ آزمون به دقت ۹۲٫۸٪ میرسد که کمی بهتر از Soft Voting با دقت ۹۲٪ است.
جمعبندی فصل ۷
روشهای Ensemble انعطافپذیر، قدرتمند و در عین حال نسبتاً سادهاند. Random Forest، AdaBoost و GBRT از مدلهایی هستند که برای بسیاری از مسائل یادگیری ماشین ارزش دارد در مراحل اولیهٔ آزمایش شوند، بهخصوص روی دادههای جدولی ناهمگون. این مدلها معمولاً Preprocessing کمی لازم دارند و برای ساخت سریع نمونهٔ اولیه مناسباند. روشهایی مانند Voting و Stacking نیز میتوانند برای فشار دادن عملکرد سامانه تا مرزهای بالاتر مفید باشند.
تمرینهای فصل ۷
- اگر پنج مدل متفاوت روی دقیقاً یک مجموعهٔ آموزشی ساخته شدهاند و همه Precision برابر ۹۵٪ دارند، آیا ترکیب آنها میتواند نتیجهٔ بهتری بدهد؟ اگر بله چگونه و اگر نه چرا؟
- تفاوت طبقهبندهای Hard Voting و Soft Voting چیست؟
- آیا میتوان آموزش Bagging را میان چند سرور توزیع کرد و سریعتر نمود؟ Pasting، Boosting، Random Forest و Stacking چطور؟
- مزیت ارزیابی Out-of-Bag چیست؟
- چه چیزی Extra-Trees را از Random Forest معمولی تصادفیتر میکند؟ این تصادفیبودن چه کمکی میکند و Extra-Trees معمولاً سریعتر است یا کندتر؟
- اگر AdaBoost روی دادهٔ آموزشی کمبرازش دارد، کدام فراپارامترها را باید تغییر دهید و در چه جهتی؟
- اگر Gradient Boosting بیشبرازش دارد، Learning Rate را باید افزایش دهید یا کاهش؟
- MNIST را به مجموعههای آموزش، Validation و آزمون تقسیم کنید؛ مثلاً ۵۰٬۰۰۰ نمونه آموزش، ۱۰٬۰۰۰ Validation و ۱۰٬۰۰۰ آزمون. سپس مدلهایی مانند Random Forest، Extra-Trees و SVM را آموزش دهید. با Hard Voting یا Soft Voting آنها را به Ensembleای تبدیل کنید که روی Validation از تکتک مدلها بهتر باشد. سپس آن را روی آزمون ارزیابی و میزان بهبود را اندازه بگیرید.
- با مدلهای تمرین قبل روی Validation پیشبینی انجام دهید و مجموعهٔ آموزشی جدیدی بسازید که ویژگیهای هر نمونه، پیشبینی همهٔ مدلها و Target، کلاس واقعی تصویر باشد. روی این داده یک طبقهبند آموزش دهید؛ این مدل Blender است و همراه مدلهای پایه یک Stacking Ensemble میسازد. آن را روی آزمون ارزیابی کنید: برای هر تصویر ابتدا پیشبینی مدلهای پایه را به دست آورید و سپس به Blender بدهید. نتیجه را با Voting مقایسه کنید. در پایان همین آزمایش را با
StackingClassifier انجام دهید و بررسی کنید آیا عملکرد بهتر میشود و چرا.
راهحل تمرینها در Notebook تکمیلی همین فصل در منبع کتاب ارائه شده است.