فاز 2 · درس 18

انتخاب ویژگی در یادگیری ماشین: از فیلتر تا RFE و L1

نوع
ساختنی
زمان
حدود 75 دقیقه
زبان‌ها
Python
پیش‌نیاز
یادگیری ماشین چیست؟ مفاهیم پایه، انواع و پیاده‌سازی از صفر، رگرسیون خطی از صفر تا پیاده‌سازی با scikit-learn، رگرسیون لجستیک و دسته‌بندی از صفر، درخت تصمیم و جنگل تصادفی: پیاده‌سازی از صفر و تئوری ریاضی، ماشین‌های بردار پشتیبان (SVM)؛ از فرمول‌بندی ریاضی تا پیاده‌سازی، الگوریتم K-نزدیک‌ترین همسایه (KNN) و معیارهای فاصله، یادگیری نظارت‌نشده و الگوریتم‌های خوشه‌بندی، مهندسی و انتخاب ویژگی در یادگیری ماشین، ارزیابی مدل در یادگیری ماشین: از صفر تا پیاده‌سازی و تحلیل، مهندسی و انتخاب ویژگی در یادگیری ماشین
یک صافی فلزی مکانیکی که سنگ‌ریزه‌ها و نخاله‌ها را جدا کرده و تنها دانه‌های براق طلا را عبور می‌دهد
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفاهیم پایه
  4. سه دسته‌بندی اصلی انتخاب ویژگی
  5. حد آستانه‌ی واریانس (Variance Threshold)
  6. اطلاعات متقابل (Mutual Information)
  7. حذف بازگشتی ویژگی (RFE)
  8. منظم‌سازی L1 (Lasso)
  9. اهمیت ویژگی مبتنی بر درخت (Tree-Based Feature Importance)
  10. اهمیت جایگشتی (Permutation Importance)
  11. جدول مقایسه
  12. نمودار تصمیم‌گیری
  13. پیاده‌سازی
  14. گام 1: تولید داده‌های مصنوعی با ساختار مشخص ویژگی‌ها
  15. گام 2: حد آستانه‌ی واریانس
  16. گام 3: اطلاعات متقابل (گسسته)
  17. گام 4: حذف بازگشتی ویژگی (RFE)
  18. گام 5: انتخاب ویژگی با L1
  19. گام 6: اهمیت مبتنی بر درخت (درخت تصمیم ساده)
  20. گام 7: اجرای تمام روش‌ها و مقایسه
  21. استفاده‌ی کاربردی
  22. تحویل پروژه
  23. تمرین‌ها
  24. اصطلاحات کلیدی
  25. برای مطالعه‌ی بیشتر

در پروژه‌های داده، ساختن صدها ستون و ویژگی جدید کار چندان سختی نیست؛ اما نگه‌داشتن همه‌ی آن‌ها مدل را سنگین، کند و مستعد بیش‌برازش (overfitting) می‌کند. افزودن ویژگی‌های نویز نه تنها کمکی به یادگیری الگوهای واقعی نمی‌کند، بلکه فضای ابعادی داده‌ها را متورم کرده و مدل را به دام «نفرین ابعاد» می‌اندازد.

انتخاب ویژگی (Feature Selection) فرآیند غربال کردن داده‌هاست تا تنها متغیرهایی که بیشترین سیگنال را درباره‌ی هدف دارند باقی بمانند. با این کار، مدل سریع‌تر آموزش می‌بیند، حافظه‌ی کمتری مصرف می‌کند، تعمیم‌پذیری بهتری روی داده‌های دیده‌نشده نشان می‌دهد و مهم‌تر از همه، تصمیم‌هایش برای انسان قابل درک و تفسیرپذیر می‌شود.

در این درس، سه خانواده‌ی اصلی انتخاب ویژگی یعنی روش‌های فیلتر (مانند حد آستانه‌ی واریانس و اطلاعات متقابل)، روش‌های بسته‌بندی (مانند حذف بازگشتی ویژگی یا RFE) و روش‌های توکار (مانند منظم‌سازی L1 و اهمیت درختی) را هم از پایه پیاده‌سازی می‌کنیم و هم کاربرد عملی آن‌ها را در scikit-learn یاد می‌گیریم.

ویژگی‌های بیشتر لزوماً بهتر نیست؛ ویژگی‌های درست مهم‌ترند.

اهداف یادگیری

  • پیاده‌سازی روش‌های فیلتر (حد آستانه‌ی واریانس، اطلاعات متقابل)، یک روش بسته‌بندی یا رپر (حذف بازگشتی ویژگی یا RFE) و روش‌های توکار (L1، اهمیت درختی) از صفر، و سپس افزودن انتخاب رو به جلو (forward selection) در تمرین‌ها
  • درک این‌که چرا اطلاعات متقابل، برعکس همبستگی، روابط غیرخطی میان ویژگی و متغیر هدف را کشف می‌کند
  • مقایسه‌ی منظم‌سازی L1 (انتخاب توکار) با RFE (انتخاب بسته‌بندی) و ارزیابی توازن هزینه‌ی محاسباتی آن‌ها
  • ساخت پایپ‌لاین انتخاب ویژگی با ترکیب چند روش و اثبات بهبود تعمیم‌پذیری مدل روی داده‌های دیده‌نشده

صورت مسئله

فرض کنید 500 ویژگی دارید. مدل به‌کندی آموزش می‌بیند، مدام دچار بیش‌برازش (overfitting) می‌شود و هیچ‌کس سر از سازوکار یادگیری‌اش درنمی‌آورد. به امید بهبود کارایی، ویژگی‌های بیشتری اضافه می‌کنید؛ اما اوضاع بدتر می‌شود.

این همان نمود عینی «نفرین ابعاد» (curse of dimensionality) است. با افزایش تعداد ویژگی‌ها، حجم فضای ویژگی منفجر می‌شود؛ نقاط داده در فضا تنک و پراکنده می‌شوند و فاصله‌ی بین نقاط به هم میل می‌کند. در چنین وضعیتی، مدل برای پیدا کردن الگوهای واقعی به داده‌هایی به‌مراتب بیشتر به‌صورت نمایی نیاز دارد. ویژگی‌های نویز، سیگنال‌های واقعی را در خود غرق می‌کنند و بیش‌برازش به حالت پیش‌فرض تبدیل می‌شود.

انتخاب ویژگی (feature selection) پادزهر این مشکل است: نویزها را دور بریزید، ویژگی‌های تکراری و زائد را حذف کنید و فقط آن‌هایی را نگه دارید که واقعاً اطلاعاتی درباره‌ی متغیر هدف دارند. نتیجه‌ی کار، آموزش سریع‌تر، تعمیم‌پذیری بهتر و مدل‌هایی است که واقعاً می‌توانید توضیحشان دهید.

هدف این نیست که از تمام اطلاعات در دسترس استفاده کنید؛ هدف استفاده از اطلاعات درست است.

مفاهیم پایه

سه دسته‌بندی اصلی انتخاب ویژگی

تمام روش‌های انتخاب ویژگی در یکی از این سه دسته جا می‌گیرند:

روش‌های انتخاب ویژگیروش‌های فیلترروش‌های بسته‌بندیروش‌های تعبیه‌شدهآستانه واریانساطلاعات متقابلآزمون کای‌اسکوئرفیلتر کردن بر اساس همبستگیحذف بازگشتی ویژگیانتخاب رو به جلوحذف رو به عقبمنظم‌سازی L1 / لاسواهمیت مبتنی بر درختالاستیک نت

روش‌های فیلتر (Filter methods) با معیارهای آماری به هر ویژگی نمره‌ای جداگانه می‌دهند. این روش‌ها کاری به مدل ندارند؛ سریع‌اند، اما برهم‌کنش (interaction) میان ویژگی‌ها را نادیده می‌گیرند.

روش‌های بسته‌بندی (Wrapper methods) برای ارزیابی زیرمجموعه‌های ویژگی، مدلی را آموزش می‌دهند و عملکرد مدل را مبنای نمره‌دهی می‌گذارند. نتایج بهتری به دست می‌دهند، ولی چون مدل بارها باید از نو آموزش ببیند، پرهزینه‌اند.

روش‌های توکار (Embedded methods) کار انتخاب ویژگی را در حین آموزش خود مدل انجام می‌دهند. برای مثال، منظم‌سازی L1 وزن ویژگی‌های کم‌اثر را به صفر می‌رساند و درخت‌های تصمیم بر اساس مفیدترین ویژگی‌ها شاخه می‌زنند. انتخاب در دلِ فرآیند برازش رخ می‌دهد، نه در مرحله‌ای جداگانه.

حد آستانه‌ی واریانس (Variance Threshold)

ساده‌ترین روش فیلتر همین است: اگر مقادیر یک ویژگی در بین نمونه‌ها به‌ندرت تغییر کند، تقریباً هیچ اطلاعاتی با خود ندارد.

ویژگی‌ای را در نظر بگیرید که در 999 نمونه از 1000 نمونه برابر با 0.0 باشد. واریانس آن نزدیک به صفر است؛ هیچ مدلی نمی‌تواند با تکیه بر آن کلاس‌ها را از هم تشخیص دهد. آن را حذف کنید.

variance(x)=mean((x−mean(x))2)\text{variance}(x) = \text{mean}\bigl((x - \text{mean}(x))^2\bigr)

حد آستانه‌ای مشخص کنید (مثلاً 0.01) و هر ویژگی با واریانس کمتر از آن را کنار بگذارید. این کار، ویژگی‌های ثابت یا تقریباً ثابت را بدون حتی یک بار نگاه کردن به متغیر هدف حذف می‌کند.

زمان استفاده: به‌عنوان گام پیش‌پردازش قبل از روش‌های دیگر، تا ویژگی‌های کاملاً بی‌مصرف را بدون هیچ هزینه‌ی محاسباتی کنار بگذارد.

محدودیت: یک ویژگی می‌تواند واریانس بالایی داشته باشد ولی سراسر نویز باشد. آستانه‌ی واریانس شرط لازم است، اما کافی نیست.

اطلاعات متقابل (Mutual Information)

اطلاعات متقابل نشان می‌دهد که دانستن مقدار ویژگی XX، چقدر از عدم قطعیت ما نسبت به متغیر هدف YY می‌کاهد.

I(X;Y)=∑x∑yp(x,y)⋅log⁡p(x,y)p(x)⋅p(y)I(X; Y) = \sum_x \sum_y p(x, y) \cdot \log \frac{p(x, y)}{p(x) \cdot p(y)}

اگر XX و YY مستقل باشند، p(x,y)=p(x)⋅p(y)p(x, y) = p(x) \cdot p(y)؛ در نتیجه عبارت لگاریتمی صفر شده و I(X;Y)=0I(X; Y) = 0 می‌شود. هرچه XX اطلاعات بیشتری از YY بدهد، اطلاعات متقابل بالاتری دارد.

برتری اصلی نسبت به همبستگی: اطلاعات متقابل روابط غیرخطی را هم می‌سنجد. یک ویژگی ممکن است همبستگی خطی صفر با هدف داشته باشد، اما چون رابطه‌شان درجه دو یا دوره‌ای است، اطلاعات متقابل بالایی نشان دهد.

برای ویژگی‌های پیوسته، ابتدا داده‌ها را در دسته‌های گسسته سطل‌بندی (binning) می‌کنند (برآورد بر پایه‌ی هیستوگرام). تعداد این دسته‌ها بر دقت برآورد اثر مستقیم دارد؛ تعداد کم باعث از دست رفتن اطلاعات می‌شود و تعداد زیاد نویز به همراه می‌آورد. انتخابی رایج: n\sqrt{n} سطل یا قاعده‌ی استرجز (1+log⁡2n1 + \log_2 n) است.

ویژگی Xگسسته‌سازی به بازه‌هامحاسبه توزیع توأم p(x,y)محاسبه MI = sum p(x,y) * log(p(x,y) /p(x)p(y))رتبه‌بندی ویژگی‌ها بر اساس امتیاز MIانتخاب K ویژگی برتر

حذف بازگشتی ویژگی (RFE)

این روش از نوع بسته‌بندی است و با تکیه بر اهمیت ویژگی‌های خودِ مدل، دست به هرس گام‌به‌گام می‌زند:

  1. مدل را با تمام ویژگی‌ها آموزش دهید.
  2. ویژگی‌ها را بر اساس اهمیت رتبه‌بندی کنید (ضرایب برای مدل‌های خطی، یا میزان کاهش ناخالصی برای درخت‌ها).
  3. کم‌اهمیت‌ترین ویژگی (یا ویژگی‌ها) را حذف کنید.
  4. این مراحل را تکرار کنید تا به تعداد ویژگی دلخواه برسید.
خیربلهشروع: تمام N ویژگیآموزش مدلرتبه‌بندی اهمیت ویژگی‌هاحذف کم‌اهمیت‌ترین ویژگیتعداد ویژگی‌ها == تعداد هدف؟بازگرداندن ویژگی‌های انتخاب‌شده

از آنجا که مدل تمام ویژگی‌های باقی‌مانده را در کنار هم می‌بیند، RFE برهم‌کنش میان ویژگی‌ها را در نظر می‌گیرد؛ چرا که حذف یک ویژگی، اهمیت سایرین را تغییر می‌دهد. به همین دلیل دقیق‌تر از روش‌های فیلتر عمل می‌کند.

هزینه‌ی کار: باید مدل را N−targetN - \text{target} بار آموزش دهید. با 500 ویژگی و هدف رسیدن به 10 ویژگی، یعنی 490 دور آموزش مدل. برای مدل‌های سنگین، این کار کند است. برای سرعت بخشیدن می‌توانید در هر مرحله چند ویژگی را با هم حذف کنید (مثلاً در هر دور، 10% از ضعیف‌ترین ویژگی‌ها را کنار بگذارید).

منظم‌سازی L1 (Lasso)

منظم‌سازی L1 قدرمطلق وزن‌ها را به تابع زیان (loss) اضافه می‌کند:

loss=prediction_error+α⋅∑∣wi∣\text{loss} = \text{prediction\_error} + \alpha \cdot \sum \lvert w_i \rvert

پارامتر آلفا (alpha) تعیین می‌کند که فرآیند هرس با چه شدتی پیش برود. هرچه آلفا بزرگ‌تر باشد، وزن ویژگی‌های بیشتری دقیقاً به صفر می‌رسد.

چرا دقیقاً صفر؟ جریمه‌ی L1 در فضای وزن‌ها ناحیه‌ی قیود لوزی‌شکلی می‌سازد. پاسخ بهینه تمایل دارد در گوشه‌های تیز این لوزی بنشیند؛ جایی که یک یا چند وزن دقیقاً صفر می‌شوند. در مقابل، منظم‌سازی L2 (ریج) قیود دایره‌ای می‌سازد که وزن‌ها را منقبض می‌کند اما به‌ندرت به صفر مطلق می‌رساند.

این یعنی انتخاب ویژگی توکار: مدل در طول آموزش می‌آموزد کدام ویژگی‌ها را نادیده بگیرد. ویژگی‌های با وزن صفر عملاً حذف می‌شوند.

مزایا: فقط در یک دور آموزش اجرا می‌شود، ویژگی‌های همبسته را مدیریت می‌کند (یکی را نگه می‌دارد و بقیه را صفر می‌کند) و در اکثر پیاده‌سازی‌های مدل‌های خطی تعبیه شده است.

محدودیت: فقط برای مدل‌های خطی کاربرد دارد و نمی‌تواند اهمیت غیرخطی ویژگی‌ها را پیدا کند.

اهمیت ویژگی مبتنی بر درخت (Tree-Based Feature Importance)

درخت‌های تصمیم و مدل‌های گروهی مبتنی بر آن‌ها (جنگل تصادفی، گرادیان بوستینگ) به‌طور طبیعی ویژگی‌ها را رتبه‌بندی می‌کنند. هر شکست، ناخالصی را کاهش می‌دهد (جینی یا آنتروپی برای طبقه‌بندی، واریانس برای رگرسیون). ویژگی‌هایی که ناخالصی را بیشتر کاهش دهند، اهمیت بیشتری دارند.

برای یک جنگل تصادفی با TT درخت:

importance(featurej)=1T∑all trees  ∑all nodes splitting on featurej(n_samples⋅impurity_decrease)\text{importance}(\text{feature}_j) = \frac{1}{T} \sum_{\text{all trees}} \; \sum_{\text{all nodes splitting on } \text{feature}_j} (\text{n\_samples} \cdot \text{impurity\_decrease})

این فرمول برای هر ویژگی یک امتیاز نرمال‌شده به دست می‌دهد که روابط غیرخطی و برهم‌کنش ویژگی‌ها را به‌طور خودکار پوشش می‌دهد.

هشدار: معیار اهمیت درختی به سمت ویژگی‌هایی با مقادیر یکتای بالا (cardinality بالا) سوگیری دارد. برای نمونه، یک ستون شناسه‌ی تصادفی بسیار مهم به نظر می‌رسد، چون هر نمونه را بی‌نقص تفکیک می‌کند. برای اطمینان از درستی کار، از اهمیت جایگشتی (permutation importance) کمک بگیرید.

اهمیت جایگشتی (Permutation Importance)

روشی مستقل از مدل (model-agnostic):

  1. مدل را آموزش دهید و کارایی پایه‌ی آن را روی داده‌های اعتبارسنجی ثبت کنید.
  2. برای هر ویژگی: مقادیرش را تصادفی بُر بزنید و افت کارایی را اندازه بگیرید.
  3. هرچه افت عملکرد شدیدتر باشد، آن ویژگی مهم‌تر است.

اگر با بر هم زدن مقادیر یک ویژگی کارایی مدل افت نکند، مدل به آن وابسته نیست. اگر عملکرد سقوط کند، آن ویژگی حیاتی است.

اهمیت جایگشتی از سوگیری نسبت به کاردینالیتی بالا دور می‌ماند؛ اما کند است: به ازای هر ویژگی یک ارزیابی کامل نیاز دارد و برای پایداری نتایج باید چند بار تکرار شود.

جدول مقایسه

روش نوع سرعت غیرخطی برهم‌کنش ویژگی‌ها
حد آستانه‌ی واریانس فیلتر بسیار سریع خیر خیر
اطلاعات متقابل فیلتر سریع بله خیر
فیلتر همبستگی فیلتر سریع خیر خیر
RFE بسته‌بندی کند وابسته به مدل بله
L1 / Lasso توکار سریع خیر (خطی) خیر
اهمیت درختی توکار متوسط بله بله
اهمیت جایگشتی مستقل از مدل کند بله بله

نمودار تصمیم‌گیری

< 5050-500500بلهخیر - درختیخیر - سایربلهخیرشروع: انتخاب ویژگیچند ویژگی وجود دارد؟شروع با آستانه واریانس + اطلاعات متقابلآستانه واریانس، سپس L1 یا اهمیت درختآستانه واریانس، سپس فیلتر اطلاعاتمتقابل، سپس RFE روی موارد باقی‌ماندهاستفاده از مدل خطی؟منظم‌سازی L1 برای انتخاب نهاییاهمیت درختی + اهمیت جایگشتیRFE با مدل شمااعتبارسنجی: مقایسه ویژگی‌های انتخابی باتمام ویژگی‌هاکارایی بهتر شد؟استقرار با ویژگی‌های انتخاب‌شدهآزمودن روش دیگر یا نگه‌داشتن تمامویژگی‌ها
انتخاب ویژگیحفظ سیگنال، حذف نویز
FEATURE SELECTIONScore every feature by how much it tells you about the target, mutual information here, and a threshold splits signal from noise. The low bars fade out: dropping them shrinks the feature space, so distances stay meaningful, the model needs less data, and overfitting to noise columns disappears. Filter, wrapper, and embedded methods differ only in how the scores are computed.آستانه‌ی انتخاباهمیت ویژگی (مثلاً اطلاعات متقابل با متغیر هدف)10 ویژگی ورودی؛ 4 ویژگی دارای سیگنال، بقیه نویز
به هر ویژگی بر اساس میزان اطلاعاتی که درباره‌ی هدف می‌دهد امتیاز داده می‌شود (اینجا اطلاعات متقابل)، و یک آستانه سیگنال را از نویز جدا می‌کند. میله‌های کوتاه‌تر کم‌رنگ می‌شوند: حذف آن‌ها فضای ویژگی را کوچک‌تر می‌کند تا فاصله‌ها معنادار بمانند، مدل به داده‌ی کمتری نیاز داشته باشد، و بیش‌برازش روی ستون‌های نوفه از بین برود. روش‌های فیلتری، بسته‌بندی (wrapper) و تعبیه‌شده (embedded) تنها در نحوه‌ی محاسبه‌ی این امتیازها با هم تفاوت دارند.

پیاده‌سازی

گام 1: تولید داده‌های مصنوعی با ساختار مشخص ویژگی‌ها

import numpy as np


def make_feature_selection_data(n_samples=500, seed=42):
    rng = np.random.RandomState(seed)

    x1 = rng.randn(n_samples)
    x2 = rng.randn(n_samples)
    x3 = rng.randn(n_samples)
    x4 = x1 + 0.1 * rng.randn(n_samples)
    x5 = x2 + 0.1 * rng.randn(n_samples)

    informative = np.column_stack([x1, x2, x3, x4, x5])

    correlated = np.column_stack([
        x1 * 0.9 + 0.1 * rng.randn(n_samples),
        x2 * 0.8 + 0.2 * rng.randn(n_samples),
        x3 * 0.7 + 0.3 * rng.randn(n_samples),
        x1 * 0.5 + x2 * 0.5 + 0.1 * rng.randn(n_samples),
        x2 * 0.6 + x3 * 0.4 + 0.1 * rng.randn(n_samples),
    ])

    noise = rng.randn(n_samples, 10) * 0.5

    X = np.hstack([informative, correlated, noise])
    y = (2 * x1 - 1.5 * x2 + x3 + 0.5 * rng.randn(n_samples) > 0).astype(int)

    feature_names = (
        [f"info_{i}" for i in range(5)]
        + [f"corr_{i}" for i in range(5)]
        + [f"noise_{i}" for i in range(10)]
    )

    return X, y, feature_names

واقعیت بنیادین (ground truth) داده‌ها را می‌دانیم: ویژگی‌های 0 تا 4 حاوی اطلاعات‌اند (ویژگی‌های 3 و 4 کپی‌های همبسته از 0 و 1 هستند)، ویژگی‌های 5 تا 9 با ویژگی‌های اصلی همبستگی دارند، و ویژگی‌های 10 تا 19 نویز خالص‌اند. یک روش انتخاب مناسب باید ویژگی‌های 0 تا 4 را در بالاترین رتبه و 10 تا 19 را در پایین‌ترین رتبه قرار دهد.

گام 2: حد آستانه‌ی واریانس

def variance_threshold(X, threshold=0.01):
    variances = np.var(X, axis=0)
    mask = variances > threshold
    return mask, variances

گام 3: اطلاعات متقابل (گسسته)

def discretize(x, n_bins=10):
    min_val, max_val = x.min(), x.max()
    if max_val == min_val:
        return np.zeros_like(x, dtype=int)
    bin_edges = np.linspace(min_val, max_val, n_bins + 1)
    binned = np.digitize(x, bin_edges[1:-1])
    return binned


def mutual_information(X, y, n_bins=10):
    n_samples, n_features = X.shape
    mi_scores = np.zeros(n_features)

    y_vals, y_counts = np.unique(y, return_counts=True)
    p_y = y_counts / n_samples

    for f in range(n_features):
        x_binned = discretize(X[:, f], n_bins)
        x_vals, x_counts = np.unique(x_binned, return_counts=True)
        p_x = dict(zip(x_vals, x_counts / n_samples))

        mi = 0.0
        for xv in x_vals:
            for yi, yv in enumerate(y_vals):
                joint_mask = (x_binned == xv) & (y == yv)
                p_xy = np.sum(joint_mask) / n_samples
                if p_xy > 0:
                    mi += p_xy * np.log(p_xy / (p_x[xv] * p_y[yi]))
        mi_scores[f] = mi

    return mi_scores

گام 4: حذف بازگشتی ویژگی (RFE)

def simple_logistic_importance(X, y, lr=0.1, epochs=100):
    n_samples, n_features = X.shape
    w = np.zeros(n_features)
    b = 0.0

    for _ in range(epochs):
        z = X @ w + b
        pred = 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
        error = pred - y
        w -= lr * (X.T @ error) / n_samples
        b -= lr * np.mean(error)

    return w, b


def rfe(X, y, n_features_to_select=5, lr=0.1, epochs=100):
    n_total = X.shape[1]
    remaining = list(range(n_total))
    rankings = np.ones(n_total, dtype=int)
    rank = n_total

    while len(remaining) > n_features_to_select:
        X_subset = X[:, remaining]
        w, _ = simple_logistic_importance(X_subset, y, lr, epochs)
        importances = np.abs(w)

        least_idx = np.argmin(importances)
        original_idx = remaining[least_idx]
        rankings[original_idx] = rank
        rank -= 1
        remaining.pop(least_idx)

    for idx in remaining:
        rankings[idx] = 1

    selected_mask = rankings == 1
    return selected_mask, rankings

گام 5: انتخاب ویژگی با L1

def soft_threshold(w, alpha):
    return np.sign(w) * np.maximum(np.abs(w) - alpha, 0)


def l1_feature_selection(X, y, alpha=0.1, lr=0.01, epochs=500):
    n_samples, n_features = X.shape
    w = np.zeros(n_features)
    b = 0.0

    for _ in range(epochs):
        z = X @ w + b
        pred = 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
        error = pred - y

        gradient_w = (X.T @ error) / n_samples
        gradient_b = np.mean(error)

        w -= lr * gradient_w
        w = soft_threshold(w, lr * alpha)
        b -= lr * gradient_b

    selected_mask = np.abs(w) > 1e-6
    return selected_mask, w

گام 6: اهمیت مبتنی بر درخت (درخت تصمیم ساده)

def gini_impurity(y):
    if len(y) == 0:
        return 0.0
    classes, counts = np.unique(y, return_counts=True)
    probs = counts / len(y)
    return 1.0 - np.sum(probs ** 2)


def best_split(X, y, feature_idx):
    values = np.unique(X[:, feature_idx])
    if len(values) <= 1:
        return None, -1.0

    best_threshold = None
    best_gain = -1.0
    parent_gini = gini_impurity(y)
    n = len(y)

    for i in range(len(values) - 1):
        threshold = (values[i] + values[i + 1]) / 2.0
        left_mask = X[:, feature_idx] <= threshold
        right_mask = ~left_mask

        n_left = np.sum(left_mask)
        n_right = np.sum(right_mask)

        if n_left == 0 or n_right == 0:
            continue

        gain = parent_gini - (n_left / n) * gini_impurity(y[left_mask]) - (n_right / n) * gini_impurity(y[right_mask])

        if gain > best_gain:
            best_gain = gain
            best_threshold = threshold

    return best_threshold, best_gain


def tree_importance(X, y, n_trees=50, max_depth=5, seed=42):
    rng = np.random.RandomState(seed)
    n_samples, n_features = X.shape
    importances = np.zeros(n_features)

    for _ in range(n_trees):
        sample_idx = rng.choice(n_samples, size=n_samples, replace=True)
        feature_subset = rng.choice(n_features, size=max(1, int(np.sqrt(n_features))), replace=False)

        X_boot = X[sample_idx]
        y_boot = y[sample_idx]

        tree_imp = _build_tree_importance(X_boot, y_boot, feature_subset, max_depth)
        importances += tree_imp

    total = importances.sum()
    if total > 0:
        importances /= total

    return importances


def _build_tree_importance(X, y, feature_subset, max_depth, depth=0):
    n_features = X.shape[1]
    importances = np.zeros(n_features)

    if depth >= max_depth or len(np.unique(y)) <= 1 or len(y) < 4:
        return importances

    best_feature = None
    best_threshold = None
    best_gain = -1.0

    for f in feature_subset:
        threshold, gain = best_split(X, y, f)
        if gain > best_gain:
            best_gain = gain
            best_feature = f
            best_threshold = threshold

    if best_feature is None or best_gain <= 0:
        return importances

    importances[best_feature] += best_gain * len(y)

    left_mask = X[:, best_feature] <= best_threshold
    right_mask = ~left_mask

    importances += _build_tree_importance(X[left_mask], y[left_mask], feature_subset, max_depth, depth + 1)
    importances += _build_tree_importance(X[right_mask], y[right_mask], feature_subset, max_depth, depth + 1)

    return importances

گام 7: اجرای تمام روش‌ها و مقایسه

این فایل کد، تمام 5 روش را روی مجموعه‌داده‌ی مصنوعی یکسانی اجرا می‌کند و با چاپ جدولی مقایسه‌ای نشان می‌دهد که هر روش کدام ویژگی‌ها را برگزیده است.

استفاده‌ی کاربردی

در scikit-learn، انتخاب ویژگی مستقیماً در پایپ‌لاین تعبیه شده است:

from sklearn.feature_selection import (
    VarianceThreshold,
    mutual_info_classif,
    RFE,
    SelectFromModel,
)
from sklearn.linear_model import Lasso, LogisticRegression
from sklearn.ensemble import RandomForestClassifier

vt = VarianceThreshold(threshold=0.01)
X_filtered = vt.fit_transform(X)

mi_scores = mutual_info_classif(X, y)
top_k = np.argsort(mi_scores)[-10:]

rfe_selector = RFE(LogisticRegression(), n_features_to_select=10)
rfe_selector.fit(X, y)
X_rfe = rfe_selector.transform(X)

lasso_selector = SelectFromModel(Lasso(alpha=0.01))
lasso_selector.fit(X, y)
X_lasso = lasso_selector.transform(X)

rf = RandomForestClassifier(n_estimators=100)
rf.fit(X, y)
importances = rf.feature_importances_

پیاده‌سازی از صفر این روش‌ها دقیقاً نشان می‌دهد درون هر روش چه می‌گذرد. حد آستانه‌ی واریانس صرفاً محاسبه‌ی var(X, axis=0) و اعمال یک ماسک است. اطلاعات متقابل چیزی جز شمارش بسامدهای توأم و حاشیه‌ای در یک جدول توافقی نیست. RFE حلقه‌ای است که آموزش می‌دهد، رتبه می‌زند و هرس می‌کند. L1 همان گرادیان کاهشی به‌همراه گام آستانه‌گذاری نرم (soft-thresholding) است. اهمیت درختی نیز تجمیع کاهش ناخالصی‌ها روی نقاط شکست است. خبری از جادو نیست؛ فقط پای آمار و حلقه‌ها در میان است.

نسخه‌های scikit-learn ویژگی‌های بیشتری می‌افزایند: استحکام بالاتر (مثلاً mutual_info_classif به‌جای سطل‌بندی از برآورد چگالی k-NN استفاده می‌کند)، سرعت بیشتر (پیاده‌سازی با C) و یکپارچگی با پایپ‌لاین.

تحویل پروژه

خروجی این درس:

  • outputs/skill-feature-selector.md -- یک درخت تصمیم‌گیری مرجع و سریع برای انتخاب مناسب‌ترین روش انتخاب ویژگی

تمرین‌ها

  1. انتخاب رو به جلو (Forward selection): نقطه‌ی مقابل RFE را پیاده کنید. کار را با صفر ویژگی آغاز کنید. در هر گام، ویژگی‌ای را اضافه کنید که عملکرد مدل را بیشتر از همه بهبود می‌دهد. وقتی افزودن ویژگی کمکی نکرد متوقف شوید. ویژگی‌های انتخاب‌شده را با نتایج RFE مقایسه کنید. کدام سریع‌تر است؟ کدام خروجی بهتری می‌دهد؟

  2. انتخاب پایدار (Stability selection): انتخاب ویژگی با L1 را 50 بار، هر بار روی یک زیرنمونه‌ی تصادفی 80 درصدی از داده‌ها و با مقادیر کمی متفاوت آلفا اجرا کنید. بشمارید هر ویژگی چند بار انتخاب می‌شود. ویژگی‌هایی که در بیش از 80% دورها انتخاب شوند «پایدار» هستند. ویژگی‌های پایدار را با یک‌بار اجرای L1 مقایسه کنید. کدام قابل اعتمادتر است؟

  3. تشخیص هم‌خطی چندگانه (Multicollinearity): ماتریس همبستگی همه‌ی ویژگی‌ها را حساب کنید. تابعی بنویسید که با دریافت یک آستانه‌ی همبستگی (مثلاً 0.9)، از هر جفت‌ویژگی بسیار همبسته یکی را حذف کند (ویژگی‌ای که اطلاعات متقابل بیشتری با متغیر هدف دارد حفظ شود). آن را روی داده‌های مصنوعی بسنجید و بررسی کنید که آیا ویژگی‌های تکراری و همبسته را حذف می‌کند یا خیر.

  4. پایپ‌لاین انتخاب ویژگی: حد آستانه‌ی واریانس، فیلتر اطلاعات متقابل و RFE را درون یک پایپ‌لاین زنجیر کنید. ابتدا ویژگی‌های با واریانس نزدیک به صفر را حذف کنید، سپس 50% برتر بر اساس اطلاعات متقابل را نگه دارید و در نهایت روی بازمانده‌ها RFE را اجرا کنید. این پایپ‌لاین را با اجرای تکی RFE روی همه‌ی ویژگی‌ها مقایسه کنید. آیا سریع‌تر است؟ آیا دقت برابری دارد؟

  5. پیاده‌سازی اهمیت جایگشتی از صفر: اهمیت جایگشتی را پیاده‌سازی کنید. برای هر ویژگی، مقادیرش را 10 بار بُر بزنید و میانگین افت امتیاز F1 را حساب کنید. رتبه‌بندی نهایی را با اهمیت درختی مقایسه کنید. مواردی را که این دو هم‌نظر نیستند پیدا کنید و دلیلش را شرح دهید (راهنمایی: ویژگی‌های همبسته).

اصطلاحات کلیدی

اصطلاح در گفتار عامیانه معنای دقیق فنی
روش فیلتر (Filter method) «نمره‌دهی مستقل به هر ویژگی» رویکردی در انتخاب ویژگی که با معیاری آماری و بدون نیاز به آموزش مدل، هر ویژگی را به‌تنهایی ارزیابی و رتبه‌بندی می‌کند
روش بسته‌بندی (Wrapper method) «سپردن انتخاب ویژگی به مدل» روشی در انتخاب ویژگی که زیرمجموعه‌های ویژگی را با آموزش دادن یک مدل و استفاده از عملکرد آن ارزیابی می‌کند
روش توکار (Embedded method) «انتخاب ویژگی توسط خود مدل حین آموزش» فرآیند انتخاب ویژگی در دل برازش مدل؛ مثل منظم‌سازی L1 که وزن‌ها را به صفر می‌رساند
اطلاعات متقابل (Mutual information) «میزان اطلاعاتی که یک متغیر از متغیر دیگر می‌دهد» معیاری از کاهش عدم قطعیت درباره‌ی YY در صورت دانستن XX، که وابستگی‌های خطی و غیرخطی را ثبت می‌کند
حذف بازگشتی ویژگی (RFE) «آموزش، رتبه‌بندی، هرس، تکرار» یک روش بسته‌بندی تکرارشونده که مدلی را آموزش می‌دهد، کم‌اهمیت‌ترین ویژگی(ها) را برمی‌دارد و این روند را تا رسیدن به تعداد ویژگی مطلوب ادامه می‌دهد
منظم‌سازی L1 / Lasso «جریمه‌ای که ویژگی‌ها را حذف می‌کند» افزودن مجموع قدرمطلق وزن‌ها به تابع زیان (loss)، که وزن ویژگی‌های بی‌اهمیت را دقیقاً به صفر می‌رساند
حد آستانه‌ی واریانس (Variance threshold) «حذف ویژگی‌های ثابت» حذف ویژگی‌هایی که واریانس مقادیرشان در بین نمونه‌ها از آستانه‌ای مشخص کمتر است؛ برای حذف ویژگی‌های بدون اطلاعات
اهمیت ویژگی (Feature importance) «کدام ویژگی‌ها بیشترین اهمیت را دارند» نمره‌ای که نشان می‌دهد هر ویژگی چقدر در پیش‌بینی‌های مدل سهم دارد؛ محاسبه‌شده از کاهش ناخالصی (درخت‌ها) یا بزرگی ضرایب (خطی)
اهمیت جایگشتی (Permutation importance) «بُر بزن و خسارت را بسنج» سنجش اهمیت ویژگی با بر هم زدن تصادفی مقادیر آن و اندازه‌گیری افت عملکرد مدل بر اثر آن
نفرین ابعاد (Curse of dimensionality) «ویژگی زیاد، داده‌ی کم» پدیده‌ای که در آن افزودن ویژگی‌ها حجم فضای ویژگی را به‌صورت نمایی رشد می‌دهد، داده‌ها را تنک و فاصله‌ها را بی‌معنا می‌کند

برای مطالعه‌ی بیشتر

سؤالات متداول

تفاوت اصلی بین روش‌های فیلتر، بسته‌بندی و توکار در انتخاب ویژگی چیست؟

روش‌های فیلتر بدون درگیر کردن مدل و بر اساس آزمون‌های آماری تک‌تک ویژگی‌ها را می‌سنجند و بسیار سریع‌اند. روش‌های بسته‌بندی (Wrapper) مانند RFE عملکرد یک مدل یادگیری ماشین را روی زیرمجموعه‌های مختلف بررسی می‌کنند و پرهزینه‌ترند. روش‌های توکار (Embedded) مانند منظم‌سازی L1 یا درخت‌ها، کار انتخاب ویژگی را حین خودِ فرآیند آموزش مدل انجام می‌دهند.

چرا اطلاعات متقابل (Mutual Information) بر همبستگی خطی برتری دارد؟

ضریب همبستگی پیرسون تنها روابط خطی مستقیم را ردیابی می‌کند؛ اگر رابطه‌ای غیرخطی، درجه دو یا دوره‌ای باشد همبستگی آن ممکن است صفر شود. در مقابل، اطلاعات متقابل بر پایه‌ی کاهش عدم قطعیت (آنتروپی) است و پیوندهای پیچیده‌ی غیرخطی را به‌خوبی آشکار می‌کند.

چرا منظم‌سازی L1 ضرایب را دقیقاً صفر می‌کند اما L2 این کار را نمی‌کند؟

جریمه‌ی قدرمطلق در منظم‌سازی L1 ناحیه‌ی قیود لوزی‌شکلی در فضای وزن‌ها می‌سازد که گوشه‌های تیز دارد؛ نقاط بهینه‌ی تابع زیان اغلب با این گوشه‌ها برخورد می‌کنند که مختصات آن‌ها صفر است. جریمه‌ی L2 قید کروی یا دایره‌ای می‌سازد که وزن‌ها را کوچک می‌کند اما به‌ندرت دقیقاً به صفر مطلق می‌رساند.

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.