EduFlow

الوحدة 2 · الانحدار

الـ Regularization باستخدام Ridge وLasso

20 دقراءة 4 دتمرين Python

الدرس في لمحة

تحكّم في الـ overfitting بمعاقبة المعاملات الكبيرة باستخدام Ridge وLasso، ووحّد مقاييس الـ features لتكون العقوبة عادلة، واستخدم أصفار Lasso اختيارًا تلقائيًا للـ features.

  1. 1عقوبة تُضاف إلى دالة الخسارة
  2. 2وحّد المقاييس أولًا، ثم عاقِب
  3. 3Lasso يختار الـ features نيابةً عنك
  4. 4ترويض المعاملات المترابطة
  5. 5Ridge أم Lasso؟

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

في الدرس السابق، أعطت الـ polynomial features من الدرجة 2 نموذجَ السكري 65 عمودًا، فانخفضت نتيجة الاختبار من 0.49 إلى 0.42. كان بإمكانك التخلّص من الأعمدة الإضافية. لكن الـ regularization (التنظيم) يقدّم ما هو أفضل: احتفظ بها كلها، لكن اجعل النموذج يدفع ثمن كل وحدة من المعاملات يستخدمها. الأعمدة التي تستحق مكانها تنال وزنًا؛ والبقية تتقلّص.

عقوبة تُضاف إلى دالة الخسارة

تصغّر المربعات الصغرى العادية الخطأ المربّع ولا شيء غيره. أما الانحدار المنظَّم فيصغّر الخطأ المربّع مضافًا إليه عقوبةٌ تكبر مع حجم المعاملات:

  • Ridge يضيف alpha * sum(coef ** 2)، وهي عقوبة L2.
  • Lasso يضيف alpha * sum(abs(coef))، وهي عقوبة L1 (كما أن scikit-learn يعاير حدّ الخطأ بطريقة مختلفة قليلًا، ولهذا تختلف قيم alpha الجيدة بين النموذجين).

يحدّد alpha سعر الصرف. عند 0 تعود إلى المربعات الصغرى. ومع ازدياده، يجب على المعامل الكبير أن يشتري خفضًا كبيرًا في الخطأ كي يستحق وجوده. والمعاملات التي لا تلائم إلا الضجيج لا تستطيع ذلك، فتتقلّص. القيم المربّعة تجعل Ridge يقلّص كل شيء بسلاسة؛ والقيم المطلقة تجعل Lasso يدفع المعاملات الضعيفة حتى الصفر.

وحّد المقاييس أولًا، ثم عاقِب

تجمع العقوبة المعاملات كما لو كانت قابلة للمقارنة. وهي ليست كذلك إلا إذا اشتركت الـ features في مقياس واحد. قِس الإنفاق بالسنتات بدل الدولارات وسيصغر معامله مئة مرة، فلا تكاد العقوبة تلحظه. ضع دائمًا StandardScaler قبل أي نموذج معاقَب.

Python
from sklearn.datasets import load_diabetes
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler

X, y = load_diabetes(return_X_y=True, as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)

def degree2(model):
    return make_pipeline(PolynomialFeatures(degree=2, include_bias=False), StandardScaler(), model)

print("no penalty:", round(degree2(LinearRegression()).fit(X_train, y_train).score(X_test, y_test), 3))
for alpha in [1, 10, 100, 1000]:
    m = degree2(Ridge(alpha=alpha)).fit(X_train, y_train)
    print(f"Ridge alpha={alpha:<5} train {m.score(X_train, y_train):.3f}  test {m.score(X_test, y_test):.3f}")

مع ارتفاع alpha تنخفض نتيجة التدريب باطّراد (إذ يُمنح النموذج حرية أقل)، بينما تصعد نتيجة الاختبار من 0.42 إلى 0.50 عند alpha=100، وهذا أفضل من النموذج العادي ذي الـ features العشرة. ادفعها إلى 1,000 فتنخفض النتيجتان معًا: صار النموذج الآن في الـ underfitting. يحوّل الـ regularization المفاضلةَ بين الـ overfitting والـ underfitting إلى مقبض تديره.

Lasso يختار الـ features نيابةً عنك

أجرِ التجربة نفسها مع Lasso وعُدّ المعاملات التي تنتهي إلى الصفر بالضبط.

Python
from sklearn.datasets import load_diabetes
from sklearn.linear_model import Lasso
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler

X, y = load_diabetes(return_X_y=True, as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)

for alpha in [0.1, 1, 2, 5]:
    m = make_pipeline(PolynomialFeatures(degree=2, include_bias=False), StandardScaler(),
                      Lasso(alpha=alpha)).fit(X_train, y_train)
    zeros = (m[-1].coef_ == 0).sum()
    print(f"Lasso alpha={alpha:<4} test R2 {m.score(X_test, y_test):.3f}  zero coefficients: {zeros} of 65")

names = m[0].get_feature_names_out()
kept = [n for n, c in zip(names, m[-1].coef_) if c != 0]
print("kept at alpha=5:", kept)

عند alpha=2 يبلغ Lasso قيمة R² على الاختبار تساوي 0.53، وهي أفضل نتيجة انحدار في هذا القسم، مع تجاهله 39 عمودًا من أصل 65. وعند alpha=5 يحتفظ بـ 11 عمودًا: مؤشر كتلة الجسم وضغط الدم وs5 من النماذج السابقة، إضافة إلى بضعة تفاعلات مثل bmi bp. يصل m[-1] إلى آخر خطوة في الـ pipeline، ويُعطي m[0].get_feature_names_out() أسماء الأعمدة المولّدة.

ترويض المعاملات المترابطة

أتذكر s1 عند -918 وs2 عند +508 في الانحدار العادي، قياسَي دم مترابطَين يشدّ أحدهما عكس الآخر؟ مع الـ features الموحّدة المقاييس تظهر القصة نفسها على نطاق أصغر، والعقوبة تحسمها.

معاملا s1 وs2 على features موحّدة المقاييس. المربعات الصغرى: s1 سالب 44، وs2 موجب 25. Ridge مع alpha تساوي 10: s1 سالب 13، وs2 قرب الصفر. Lasso مع alpha تساوي 1: s1 سالب 8، وs2 صفر بالضبط. 0 المربعات الصغرى s1 −44 s2 +25 Ridge, alpha=10 s1 −13 s2 +0.5 Lasso, alpha=1 s1 −8 s2 0 (محذوف)
ما الذي يحدث لمعاملين مترابطين

يوزّع Ridge الوزن بين الـ features المترابطة بشكل معقول بدل أن يتركها يلغي بعضها بعضًا؛ بينما يميل Lasso إلى الاحتفاظ بأحدها وحذف الآخر. وفي الحالتين تصبح المعاملات مستقرة بما يكفي كي لا ينقلب اتجاهها مع تغيير صغير في بيانات التدريب.

Ridge أم Lasso؟

كلاهما يعالج الداء نفسه، فالاختيار يرجع إلى ما تريده من النموذج النهائي. يفوز Ridge عادةً حين تحمل features كثيرة شيئًا قليلًا من الإشارة لكلٍّ منها، وهذا شائع مع القياسات المتداخلة، مثل قراءات الدم الست هنا. ويفوز Lasso حين تهمّ features قليلة والبقية ضجيج، ويسلّمك قائمة مشتريات أقصر من المدخلات، وهذا مهم حين يجب جمع كل feature وتنظيفه ومراقبته في بيئة الإنتاج. وحين تأتي الـ features في مجموعات مترابطة، قد تكون عادة Lasso في الاحتفاظ بعضو واحد، باعتباطية إلى حدّ ما، غير مستقرة من تدريب إلى آخر. ويمزج ElasticNet العقوبتين لهذه الحالة بالضبط. وعلى مجموعة بيانات بهذا الحجم، الإجابة الأمينة هي أن تجرّب الاثنين على بيانات تحقق وتحتفظ بالفائز، ثم تفضّل الأبسط حين يتعادلان.

بهذا يُختتم الانحدار. وفكرة العقوبة نفسها تعود مباشرة في القسم القادم: LogisticRegression منظَّم افتراضيًا، بمعامل C يعمل كمقلوب alpha. يعيد القسم 3 الهدفَ إلى انقطاع العملاء، نعم أو لا.

أهم النقاط

  • يضيف الـ regularization إلى دالة الخسارة عقوبةً على حجم المعاملات، فيتنازل النموذج عن قليل من التوافق مع التدريب مقابل معاملات أبسط وأكثر استقرارًا.
  • يعاقب Ridge مربعات المعاملات فيقلّصها جميعًا؛ ويعاقب Lasso قيمها المطلقة فيجعل بعضها صفرًا بالضبط.
  • alpha هو قوة العقوبة: 0 يعني المربعات الصغرى العادية، والقيم الكبيرة جدًا تسطّح النموذج حتى الـ underfitting.
  • وحّد مقاييس الـ features قبل النموذج المعاقَب، وإلا عاقبت العقوبةُ الـ features على وحداتها لا على فائدتها.
  • اختر alpha على بيانات تحقق أو بالتحقق المتقاطع، بالطريقة نفسها التي تختار بها أي hyperparameter.

قراءات إضافية

تحقّق من فهمك

3 أسئلة

السؤال 1 من 3

رفعت قيمة alpha في Ridge من 1 إلى 100,000. ماذا يحدث للنموذج؟

السؤال 2 من 3

لماذا نضع StandardScaler قبل Lasso في الـ pipeline؟

السؤال 3 من 3

تحتاج إلى نموذج انقطاع يستخدم أقل عدد ممكن من 60 feature مرشّحًا. ما الخيار الأول الطبيعي؟

تدريب

تمرين Python

دع Lasso يختار الـ features

الـ pipeline في الكود المبدئي ينسى شيئًا مهمًا. أصلحه، ثم افحص ما فعله Lasso.

  1. lasso_model: PolynomialFeatures(include_bias=False) من الدرجة 2، ثم StandardScaler، ثم Lasso(alpha=1)، مدرَّبًا على بيانات التدريب.
  2. lasso_r2: قيمة R² على بيانات الاختبار. وn_zero: عدد معاملاته الـ 65 التي تساوي صفرًا بالضبط.
  3. kept: قائمة بأسماء الأعمدة المولّدة التي معاملها ليس صفرًا.
from sklearn.datasets import load_diabetes
from sklearn.linear_model import Lasso, Ridge
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler

X, y = load_diabetes(return_X_y=True, as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)

# 1. Pipeline: degree-2 PolynomialFeatures (include_bias=False) -> StandardScaler -> Lasso(alpha=1)
lasso_model = make_pipeline(PolynomialFeatures(degree=2, include_bias=False), Lasso(alpha=1))
lasso_model.fit(X_train, y_train)

# 2. Test R2 and the number of coefficients that are exactly zero
lasso_r2 = None
n_zero = None

# 3. Names of the generated columns Lasso kept (non-zero coefficient), as a list of strings
kept = None
جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

(لا مخرجات)

الفحوص

  • الـ pipeline هو PolynomialFeatures ← StandardScaler ← Lasso(alpha=1)
  • lasso_r2 هو قيمة R² على بيانات الاختبار
  • n_zero يعدّ المعاملات الصفرية
  • kept يسرد أسماء الأعمدة غير الصفرية