فصل ۷: یادگیری گروهی و جنگلهای تصادفی
فرض کنید یک سؤال پیچیده را از هزاران نفر بهطور تصادفی بپرسید و سپس پاسخهای آنان را تجمیع کنید. در بسیاری از موارد پاسخ جمعی از پاسخ یک متخصص بهتر خواهد بود؛ پدیدهای که به آن خرد جمعی گفته میشود. در یادگیری ماشین نیز اگر پیشبینی گروهی از مدلها ــ مانند طبقهبندها یا رگرسورها ــ با یکدیگر ترکیب شود، نتیجه اغلب از بهترین مدل منفرد بهتر است. مجموعهای از پیشبینیکنندهها را Ensemble مینامند؛ بنابراین این رویکرد یادگیری گروهی و الگوریتم آن یک روش Ensemble است.
یک نمونهٔ ساده این است که چند درخت تصمیم را هرکدام روی زیرمجموعهای تصادفی و متفاوت از دادهٔ آموزشی آموزش دهید. سپس پیشبینی همهٔ درختها را بگیرید و کلاسی را که بیشترین رأی را دارد بهعنوان خروجی نهایی انتخاب کنید. چنین مجموعهای از درختهای تصمیم جنگل تصادفی نامیده میشود و با وجود سادگی، یکی از قدرتمندترین الگوریتمهای یادگیری ماشین است.
روشهای Ensemble معمولاً در مراحل پایانی پروژه مفیدند؛ زمانی که چند مدل خوب ساختهاید و میخواهید با ترکیب آنها مدل قویتری بسازید. بسیاری از راهحلهای برنده در رقابتهای یادگیری ماشین نیز چند روش Ensemble را با هم ترکیب کردهاند. در این فصل روشهای رایجی مانند Voting، Bagging، Pasting، Random Forest، Boosting و Stacking بررسی میشوند.
طبقهبندهای رأیگیری
فرض کنید چند طبقهبند مختلف دارید که هر کدام حدود ۸۰٪ دقت دارند؛ مثلاً Logistic Regression، SVM، Random Forest و K-Nearest Neighbors. شکل ۷-۱ نمونهای از چنین گروه متنوعی را نشان میدهد.
شکل 7-1. آموزش طبقهبندهای متنوع
راه بسیار ساده برای ساخت طبقهبندی بهتر این است که پیشبینی همهٔ مدلها را تجمیع کنیم و کلاسی را که بیشترین رأی را گرفته انتخاب کنیم. این روش رأیگیری سخت یا Hard Voting نام دارد.
شکل 7-2. پیشبینی با رأیگیری سخت
شاید عجیب به نظر برسد، اما طبقهبند رأیگیری در بسیاری از موارد از بهترین عضو Ensemble دقت بالاتری پیدا میکند. حتی اگر هر عضو یک یادگیرندهٔ ضعیف باشد ــ یعنی فقط اندکی بهتر از حدس تصادفی کار کند ــ مجموعه میتواند به یک یادگیرندهٔ قوی تبدیل شود، به شرط آنکه تعداد اعضا کافی و خطاهای آنها به اندازهٔ لازم متنوع و مستقل باشد.
برای درک این موضوع سکهای را در نظر بگیرید که احتمال شیر آمدن آن ۵۱٪ و خط ۴۹٪ است. اگر هزار بار آن را پرتاب کنید، معمولاً حدود ۵۱۰ بار شیر و ۴۹۰ بار خط میآید و رأی اکثریت شیر خواهد بود. احتمال اینکه در هزار پرتاب اکثریت نتیجه شیر باشد تقریباً ۷۵٪ است؛ با دههزار پرتاب این احتمال از ۹۷٪ نیز بیشتر میشود. این نتیجه از قانون اعداد بزرگ ناشی میشود: با افزایش تعداد آزمایشها، نسبت مشاهدهشده به احتمال واقعی یعنی ۵۱٪ نزدیکتر میشود.
شکل 7-3. قانون اعداد بزرگ
بهطور مشابه اگر هزار طبقهبند داشته باشیم که هرکدام فقط ۵۱٪ مواقع درست پیشبینی کنند، در حالت ایدهآل میتوان با رأی اکثریت به دقتی در حدود ۷۵٪ رسید. البته این نتیجه زمانی برقرار است که خطاهای طبقهبندها مستقل و نامرتبط باشند. در عمل مدلها روی همان دادهها آموزش میبینند و ممکن است خطاهای مشابهی داشته باشند؛ در نتیجه رأیهای اشتباه نیز همبسته میشوند و دقت نهایی کمتر از حالت ایدهآل خواهد شد.
روشهای Ensemble زمانی بهترین نتیجه را دارند که پیشبینیکنندهها تا حد امکان مستقل و متنوع باشند. استفاده از الگوریتمهای آموزشی متفاوت یکی از روشهای ایجاد این تنوع است، زیرا احتمال خطاهای متفاوت را بیشتر میکند.
VotingClassifier در Scikit-Learn
کلاس VotingClassifier رابط سادهای دارد. کافی است فهرستی از زوجهای نام و مدل به آن بدهید و سپس مانند یک طبقهبند معمولی از آن استفاده کنید. در مثال زیر روی دادهٔ moons سه مدل متفاوت ترکیب میشوند:
from sklearn.datasets import make_moons
from sklearn.ensemble import RandomForestClassifier, VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
X, y = make_moons(n_samples=500, noise=0.30, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
X, y, random_state=42)
voting_clf = VotingClassifier(
estimators=[
('lr', LogisticRegression(random_state=42)),
('rf', RandomForestClassifier(random_state=42)),
('svc', SVC(random_state=42))
]
)
voting_clf.fit(X_train, y_train)
هنگام fit()، VotingClassifier از هر Estimator یک Clone میسازد و همان Cloneها را آموزش میدهد. مدلهای اصلی در estimators و نسخههای آموزشدیده در estimators_ موجودند. نسخههای نامگذاریشده نیز از طریق named_estimators و named_estimators_ در دسترس هستند.
>>> for name, clf in voting_clf.named_estimators_.items():
... print(name, "=", clf.score(X_test, y_test))
...
lr = 0.864
rf = 0.896
svc = 0.896
متد predict() بهطور پیشفرض رأیگیری سخت انجام میدهد. برای نخستین نمونهٔ آزمون، دو مدل از سه مدل کلاس ۱ را انتخاب میکنند:
>>> voting_clf.predict(X_test[:1])
array([1])
>>> [clf.predict(X_test[:1]) for clf in voting_clf.estimators_]
[array([1]), array([1]), array([0])]
دقت Ensemble برابر ۹۱٫۲٪ است و از هر سه مدل منفرد بهتر عمل میکند:
>>> voting_clf.score(X_test, y_test)
0.912
رأیگیری نرم
اگر همهٔ طبقهبندها بتوانند احتمال کلاسها را با predict_proba() برآورد کنند، میتوان بهجای رأی کلاسها، احتمالهای برآوردی را میانگین گرفت و کلاسی را انتخاب کرد که بیشترین احتمال میانگین را دارد. این روش رأیگیری نرم یا Soft Voting نام دارد و اغلب از Hard Voting بهتر است، زیرا به رأیهای دارای اطمینان بیشتر وزن مؤثرتری میدهد.
برای استفاده از آن، voting="soft" را تنظیم میکنیم. SVC بهطور پیشفرض احتمال کلاس نمیدهد، بنابراین باید probability=True فعال شود؛ این کار برای برآورد احتمال از Cross-Validation استفاده میکند و آموزش را کندتر میسازد:
>>> voting_clf.voting = "soft"
>>> voting_clf.named_estimators["svc"].probability = True
>>> voting_clf.fit(X_train, y_train)
>>> voting_clf.score(X_test, y_test)
0.92
فقط با تغییر به Soft Voting دقت به ۹۲٪ میرسد.
Bagging و Pasting
تنوع اعضای Ensemble را میتوان با الگوریتمهای متفاوت ایجاد کرد، اما روش دیگری نیز وجود دارد: همهٔ پیشبینیکنندهها از یک الگوریتم استفاده کنند، ولی هر کدام روی زیرمجموعهای تصادفی و متفاوت از دادهٔ آموزشی آموزش ببینند.
اگر نمونهگیری با جایگذاری انجام شود، روش Bagging نام دارد که کوتاهشدهٔ Bootstrap Aggregating است. اگر نمونهگیری بدون جایگذاری باشد، روش Pasting نامیده میشود. در هر دو روش ممکن است یک نمونه در زیرمجموعههای مربوط به چند پیشبینیکننده دیده شود، اما فقط در Bagging امکان دارد یک نمونه چند بار در زیرمجموعهٔ همان پیشبینیکننده انتخاب شود.
شکل 7-4. Bagging و Pasting؛ آموزش مدلها روی نمونههای تصادفی متفاوت
بعد از آموزش همهٔ مدلها، پیشبینی نهایی با تجمیع خروجیها ساخته میشود. برای طبقهبندی معمولاً پرتکرارترین کلاس ــ شبیه Hard Voting ــ و برای رگرسیون میانگین پیشبینیها انتخاب میشود. هر پیشبینیکنندهٔ منفرد، چون روی بخشی از داده آموزش دیده، معمولاً Bias بیشتری از مدلی دارد که کل داده را دیده است؛ اما تجمیع میتواند هم Bias و هم Variance را کاهش دهد. در مجموع معمولاً Ensemble نسبت به یک مدل منفرد Bias مشابه و Variance کمتری دارد.
پیشبینیکنندههای Bagging و Pasting مستقل از هم آموزش داده میشوند، پس آموزش آنها میتواند روی هستههای مختلف CPU یا حتی سرورهای متفاوت بهصورت موازی انجام شود. پیشبینی نیز قابل موازیسازی است و همین موضوع این روشها را بسیار مقیاسپذیر میکند.
Bagging و Pasting در Scikit-Learn
Scikit-Learn کلاس BaggingClassifier و برای رگرسیون BaggingRegressor را ارائه میکند. کد زیر ۵۰۰ درخت تصمیم میسازد که هرکدام روی ۱۰۰ نمونهٔ تصادفی با جایگذاری آموزش میبینند. اگر bootstrap=False شود، نمونهگیری بدون جایگذاری و در نتیجه Pasting انجام خواهد شد. مقدار n_jobs=-1 نیز تمام هستههای CPU موجود را به کار میگیرد:
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
bag_clf = BaggingClassifier(
DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
n_jobs=-1,
random_state=42
)
bag_clf.fit(X_train, y_train)
اگر طبقهبند پایه متد predict_proba() داشته باشد، BaggingClassifier بهطور خودکار از Soft Voting استفاده میکند. درخت تصمیم چنین متدی دارد.
شکل ۷-۵ مرز تصمیم یک درخت منفرد را با Ensemble شامل ۵۰۰ درخت مقایسه میکند. هر دو روی دادهٔ moons آموزش دیدهاند. مرز تصمیم Ensemble بسیار منظمتر است و احتمال تعمیم بهتر آن بالاتر است. Bias دو مدل تقریباً مشابه است، اما Ensemble Variance کمتری دارد.
شکل 7-5. درخت منفرد در برابر Bagging با ۵۰۰ درخت
Bagging بهعلت نمونهگیری با جایگذاری، تنوع بیشتری نسبت به Pasting ایجاد میکند. این تنوع میتواند Bias را کمی افزایش دهد، ولی همبستگی میان مدلها را کمتر میکند و Variance را پایین میآورد. در عمل Bagging اغلب نتیجهٔ بهتری میدهد، اما اگر زمان و توان محاسباتی کافی دارید، میتوان هر دو روش را با Cross-Validation مقایسه کرد.
ارزیابی Out-of-Bag
در Bagging بعضی نمونهها برای یک پیشبینیکننده چند بار انتخاب میشوند و بعضی دیگر اصلاً انتخاب نمیشوند. وقتی bootstrap=True باشد، بهطور پیشفرض به تعداد m نمونه از مجموعهای با اندازهٔ m و با جایگذاری نمونهگیری میشود. از نظر ریاضی، بهطور متوسط فقط حدود ۶۳٪ نمونههای اصلی حداقل یک بار برای هر پیشبینیکننده انتخاب میشوند. حدود ۳۷٪ باقیمانده برای آن پیشبینیکننده Out-of-Bag یا OOB هستند. این ۳۷٪ برای مدلهای مختلف یکسان نیست.
نمونههای OOB امکان میدهند بدون ساخت مجموعهٔ Validation جداگانه، Ensemble را ارزیابی کنیم. اگر تعداد Estimatorها کافی باشد، هر نمونهٔ آموزشی برای چند مدل مختلف OOB خواهد بود؛ بنابراین همان مدلها میتوانند پیشبینی منصفانه و خارج از نمونه برای آن نمونه بسازند. پس از بهدستآوردن پیشبینی OOB برای همهٔ نمونهها میتوان Accuracy یا هر معیار دیگری را محاسبه کرد.
در Scikit-Learn کافی است هنگام ساخت BaggingClassifier مقدار oob_score=True را بدهید. نتیجه در ویژگی oob_score_ قرار میگیرد:
>>> bag_clf = BaggingClassifier(
... DecisionTreeClassifier(), n_estimators=500,
... oob_score=True, n_jobs=-1, random_state=42)
...
>>> bag_clf.fit(X_train, y_train)
>>> bag_clf.oob_score_
0.896