المزالق الشائعة في تفسير معاملات النماذج الخطية#

في النماذج الخطية، يتم نمذجة القيمة المستهدفة كمجموعة خطية من الميزات (انظر قسم النماذج الخطية في دليل المستخدم لوصف مجموعة من النماذج الخطية المتاحة في scikit-learn). تمثل المعاملات في النماذج الخطية المتعددة العلاقة بين الميزة المعينة، \(X_i\) والهدف، \(y\)، بافتراض أن جميع الميزات الأخرى تظل ثابتة ( التبعية الشرطية). هذا يختلف عن رسم \(X_i\) مقابل \(y\) وملاءمة علاقة خطية: في هذه الحالة، تؤخذ جميع القيم الممكنة للميزات الأخرى في الاعتبار في التقدير (التبعية الهامشية).

سيقدم هذا المثال بعض التلميحات في تفسير المعامل في النماذج الخطية، مشيرًا إلى المشاكل التي تنشأ عندما يكون النموذج الخطي غير مناسب لوصف مجموعة البيانات، أو عندما تكون الميزات مترابطة.

ملاحظة

ضع في اعتبارك أن الميزات \(X\) والنتيجة \(y\) هي بشكل عام نتيجة عملية توليد بيانات غير معروفة لنا. يتم تدريب نماذج التعلم الآلي لتقريب الدالة الرياضية غير المرصودة التي تربط \(X\) بـ \(y\) من بيانات العينة. نتيجة لذلك، قد لا يعمم أي تفسير يتم إجراؤه حول نموذج ما بالضرورة على عملية توليد البيانات الحقيقية. هذا صحيح بشكل خاص عندما يكون النموذج ذو جودة سيئة أو عندما لا تكون بيانات العينة ممثلة للسكان.

سنستخدم بيانات من "مسح السكان الحالي" من عام 1985 للتنبؤ بالأجور كدالة لميزات مختلفة مثل الخبرة والعمر أو التعليم.

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy as sp
import seaborn as sns

مجموعة البيانات: الأجور#

نحصل على البيانات من OpenML. لاحظ أن تعيين المعلمة as_frame إلى True سيسترجع البيانات كإطار بيانات pandas.

from sklearn.datasets import fetch_openml

survey = fetch_openml(data_id=534, as_frame=True)

بعد ذلك، نحدد الميزات X والأهداف y: العمود WAGE هو متغيرنا المستهدف (أي المتغير الذي نريد التنبؤ به).

X = survey.data[survey.feature_names]
X.describe(include="all")
EDUCATION SOUTH SEX EXPERIENCE UNION AGE RACE OCCUPATION SECTOR MARR
count 534.000000 534 534 534.000000 534 534.000000 534 534 534 534
unique NaN 2 2 NaN 2 NaN 3 6 3 2
top NaN no male NaN not_member NaN White Other Other Married
freq NaN 378 289 NaN 438 NaN 440 156 411 350
mean 13.018727 NaN NaN 17.822097 NaN 36.833333 NaN NaN NaN NaN
std 2.615373 NaN NaN 12.379710 NaN 11.726573 NaN NaN NaN NaN
min 2.000000 NaN NaN 0.000000 NaN 18.000000 NaN NaN NaN NaN
25% 12.000000 NaN NaN 8.000000 NaN 28.000000 NaN NaN NaN NaN
50% 12.000000 NaN NaN 15.000000 NaN 35.000000 NaN NaN NaN NaN
75% 15.000000 NaN NaN 26.000000 NaN 44.000000 NaN NaN NaN NaN
max 18.000000 NaN NaN 55.000000 NaN 64.000000 NaN NaN NaN NaN


لاحظ أن مجموعة البيانات تحتوي على متغيرات فئوية ورقمية. سنحتاج إلى مراعاة ذلك عند معالجة مجموعة البيانات مسبقًا فيما بعد.

X.head()
EDUCATION SOUTH SEX EXPERIENCE UNION AGE RACE OCCUPATION SECTOR MARR
0 8 no female 21 not_member 35 Hispanic Other Manufacturing Married
1 9 no female 42 not_member 57 White Other Manufacturing Married
2 12 no male 1 not_member 19 White Other Manufacturing Unmarried
3 12 no male 4 not_member 22 White Other Other Unmarried
4 12 no male 17 not_member 35 White Other Other Married


هدفنا للتنبؤ: الأجر. يتم وصف الأجور كرقم فاصلة عائمة بالدولار في الساعة.

y = survey.target.values.ravel()
survey.target.head()
0    5.10
1    4.95
2    6.67
3    4.00
4    7.50
Name: WAGE, dtype: float64

نقسم العينة إلى مجموعة بيانات تدريب ومجموعة بيانات اختبار. سيتم استخدام مجموعة بيانات التدريب فقط في التحليل الاستكشافي التالي. هذه طريقة لمحاكاة موقف حقيقي حيث يتم إجراء التنبؤات على هدف غير معروف، ولا نريد أن يكون تحليلنا وقراراتنا متحيزة بمعرفتنا ببيانات الاختبار.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

أولاً، دعنا نحصل على بعض الأفكار من خلال النظر إلى توزيعات المتغيرات و في العلاقات الزوجية بينها. سيتم استخدام المتغيرات الرقمية فقط. في الرسم التخطيطي التالي، تمثل كل نقطة عينة.

train_dataset = X_train.copy()
train_dataset.insert(0, "WAGE", y_train)
_ = sns.pairplot(train_dataset, kind="reg", diag_kind="kde")
plot linear model coefficient interpretation

يكشف النظر عن كثب إلى توزيع WAGE أنه يحتوي على ذيل طويل. لهذا السبب، يجب أن نأخذ لوغاريتمه لتحويله تقريبًا إلى توزيع طبيعي (تعمل النماذج الخطية مثل ridge أو lasso بشكل أفضل لتوزيع طبيعي للخطأ).

يزداد WAGE عندما يزداد EDUCATION. لاحظ أن التبعية بين WAGE و EDUCATION الممثلة هنا هي تبعية هامشية، أي أنها تصف سلوك متغير معين دون إبقاء المتغيرات الأخرى ثابتة.

أيضًا، يرتبط EXPERIENCE و AGE ارتباطًا خطيًا قويًا.

خط أنابيب التعلم الآلي#

لتصميم خط أنابيب التعلم الآلي لدينا، نقوم أولاً يدويًا بالتحقق من نوع البيانات التي نتعامل معها:

survey.data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 534 entries, 0 to 533
Data columns (total 10 columns):
 #   Column      Non-Null Count  Dtype
---  ------      --------------  -----
 0   EDUCATION   534 non-null    int64
 1   SOUTH       534 non-null    category
 2   SEX         534 non-null    category
 3   EXPERIENCE  534 non-null    int64
 4   UNION       534 non-null    category
 5   AGE         534 non-null    int64
 6   RACE        534 non-null    category
 7   OCCUPATION  534 non-null    category
 8   SECTOR      534 non-null    category
 9   MARR        534 non-null    category
dtypes: category(7), int64(3)
memory usage: 17.2 KB

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

  • يقوم بتشفير الأعمدة الفئوية بنظام واحد ساخن (أي، إنشاء عمود لكل فئة)، فقط للمتغيرات الفئوية غير الثنائية؛

  • كنهج أول (سنرى بعد ذلك كيف سيؤثر تطبيع القيم الرقمية على مناقشتنا)، احتفظ بالقيم الرقمية كما هي.

from sklearn.compose import make_column_transformer
from sklearn.preprocessing import OneHotEncoder

categorical_columns = ["RACE", "OCCUPATION", "SECTOR", "MARR", "UNION", "SEX", "SOUTH"]
numerical_columns = ["EDUCATION", "EXPERIENCE", "AGE"]

preprocessor = make_column_transformer(
    (OneHotEncoder(drop="if_binary"), categorical_columns),
    remainder="passthrough",
    verbose_feature_names_out=False,  # avoid to prepend the preprocessor names
)

لوصف مجموعة البيانات كنموذج خطي، نستخدم انحدار ريدج بتنظيم صغير جدًا ونمذجة لوغاريتم WAGE.

from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline

model = make_pipeline(
    preprocessor,
    TransformedTargetRegressor(
        regressor=Ridge(alpha=1e-10), func=np.log10, inverse_func=sp.special.exp10
    ),
)

معالجة مجموعة البيانات#

أولاً، نقوم بملاءمة النموذج.

model.fit(X_train, y_train)
Pipeline(steps=[('columntransformer',
                 ColumnTransformer(remainder='passthrough',
                                   transformers=[('onehotencoder',
                                                  OneHotEncoder(drop='if_binary'),
                                                  ['RACE', 'OCCUPATION',
                                                   'SECTOR', 'MARR', 'UNION',
                                                   'SEX', 'SOUTH'])],
                                   verbose_feature_names_out=False)),
                ('transformedtargetregressor',
                 TransformedTargetRegressor(func=<ufunc 'log10'>,
                                            inverse_func=<ufunc 'exp10'>,
                                            regressor=Ridge(alpha=1e-10)))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


ثم نتحقق من أداء النموذج المحسوب برسم تنبؤاته على مجموعة الاختبار وحساب، على سبيل المثال، متوسط الخطأ المطلق للنموذج.

from sklearn.metrics import PredictionErrorDisplay, median_absolute_error

mae_train = median_absolute_error(y_train, model.predict(X_train))
y_pred = model.predict(X_test)
mae_test = median_absolute_error(y_test, y_pred)
scores = {
    "MedAE on training set": f"{mae_train:.2f} $/hour",
    "MedAE on testing set": f"{mae_test:.2f} $/hour",
}
_, ax = plt.subplots(figsize=(5, 5))
display = PredictionErrorDisplay.from_predictions(
    y_test, y_pred, kind="actual_vs_predicted", ax=ax, scatter_kwargs={"alpha": 0.5}
)
ax.set_title("Ridge model, small regularization")
for name, score in scores.items():
    ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.tight_layout()
Ridge model, small regularization

النموذج المتعلم بعيد عن كونه نموذجًا جيدًا يقوم بعمل تنبؤات دقيقة: هذا واضح عند النظر إلى الرسم التخطيطي أعلاه، حيث يجب أن تقع التنبؤات الجيدة على الخط المتقطع الأسود.

في القسم التالي، سوف نفسر معاملات النموذج. أثناء قيامنا بذلك، يجب أن نضع في اعتبارنا أن أي استنتاج نستخلصه هو حول النموذج الذي نبنيه، وليس حول عملية التوليد الحقيقية (العالم الحقيقي) للبيانات.

تفسير المعاملات: المقياس مهم#

بادئ ذي بدء، يمكننا إلقاء نظرة على قيم معاملات الانحدار الذي قمنا بملاءمته.

feature_names = model[:-1].get_feature_names_out()

coefs = pd.DataFrame(
    model[-1].regressor_.coef_,
    columns=["Coefficients"],
    index=feature_names,
)

coefs
Coefficients
RACE_Hispanic -0.013555
RACE_Other -0.009111
RACE_White 0.022558
OCCUPATION_Clerical 0.000056
OCCUPATION_Management 0.090538
OCCUPATION_Other -0.025091
OCCUPATION_Professional 0.071974
OCCUPATION_Sales -0.046625
OCCUPATION_Service -0.091042
SECTOR_Construction -0.000176
SECTOR_Manufacturing 0.031277
SECTOR_Other -0.031004
MARR_Unmarried -0.032405
UNION_not_member -0.117154
SEX_male 0.090808
SOUTH_yes -0.033823
EDUCATION 0.054699
EXPERIENCE 0.035005
AGE -0.030867


يتم التعبير عن معامل AGE بالـ "دولار/ساعة لكل سنة معيشة" بينما يتم التعبير عن معامل EDUCATION بالـ "دولار/ساعة لكل سنة تعليم". هذا التمثيل للمعاملات له فائدة جعل التنبؤات العملية للنموذج واضحة: زيادة بمقدار \(1\) سنة في AGE تعني انخفاضًا بمقدار \(0.030867\) دولار/ساعة، بينما زيادة بمقدار \(1\) سنة في EDUCATION تعني زيادة بمقدار \(0.054699\) دولار/ساعة. من ناحية أخرى، المتغيرات الفئوية (مثل UNION أو SEX) هي أرقام بلا أبعاد تأخذ إما القيمة 0 أو 1. معاملاتها معبر عنها بالدولار/ساعة. بعد ذلك، لا يمكننا مقارنة حجم المعاملات المختلفة لأن الميزات لها مقاييس طبيعية مختلفة، وبالتالي نطاقات قيم مختلفة، بسبب اختلاف وحدات القياس الخاصة بها. هذا أكثر وضوحًا إذا قمنا برسم المعاملات.

coefs.plot.barh(figsize=(9, 7))
plt.title("Ridge model, small regularization")
plt.axvline(x=0, color=".5")
plt.xlabel("Raw coefficient values")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization

في الواقع، من الرسم البياني أعلاه، يبدو أن العامل الأكثر أهمية في تحديد WAGE هو المتغير UNION، حتى لو أخبرتنا حدسنا أن المتغيرات مثل EXPERIENCE يجب أن يكون لها تأثير أكبر.

قد يكون النظر إلى مخطط المعامل لقياس أهمية الميزة مضللًا حيث يختلف بعضها على نطاق صغير، بينما يختلف البعض الآخر، مثل AGE، أكثر من ذلك بكثير، عدة عقود.

هذا واضح إذا قارنا الانحرافات المعيارية لمختلف الميزات.

X_train_preprocessed = pd.DataFrame(
    model[:-1].transform(X_train), columns=feature_names
)

X_train_preprocessed.std(axis=0).plot.barh(figsize=(9, 7))
plt.title("Feature ranges")
plt.xlabel("Std. dev. of feature values")
plt.subplots_adjust(left=0.3)
Feature ranges

سيؤدي ضرب المعاملات بالانحراف المعياري للميزة ذات الصلة إلى تقليل جميع المعاملات إلى نفس وحدة القياس. كما سنرى لاحقًا هذا يعادل تطبيع المتغيرات الرقمية إلى انحرافها المعياري، مثل \(y = \sum{coef_i \times X_i} = \sum{(coef_i \times std_i) \times (X_i / std_i)}\).

وبهذه الطريقة، نؤكد على أنه كلما زاد تباين الميزة، زاد وزن المعامل المقابل على المخرجات، مع تساوي كل شيء آخر.

coefs = pd.DataFrame(
    model[-1].regressor_.coef_ * X_train_preprocessed.std(axis=0),
    columns=["Coefficient importance"],
    index=feature_names,
)
coefs.plot(kind="barh", figsize=(9, 7))
plt.xlabel("Coefficient values corrected by the feature's std. dev.")
plt.title("Ridge model, small regularization")
plt.axvline(x=0, color=".5")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization

الآن بعد أن تم قياس المعاملات، يمكننا مقارنتها بأمان.

تحذير

لماذا يشير الرسم البياني أعلاه إلى أن الزيادة في العمر تؤدي إلى انخفاض في الأجر؟ لماذا الرسم التخطيطي المزدوج الأولي يخبرنا بالعكس؟

يخبرنا الرسم البياني أعلاه عن التبعيات بين ميزة محددة و الهدف عندما تظل جميع الميزات الأخرى ثابتة، أي التبعيات الشرطية. ستؤدي زيادة AGE إلى انخفاض WAGE عندما تظل جميع الميزات الأخرى ثابتة. على العكس من ذلك، ستؤدي زيادة EXPERIENCE إلى زيادة WAGE عندما تظل جميع الميزات الأخرى ثابتة. أيضًا، AGE و EXPERIENCE و EDUCATION هي المتغيرات الثلاثة التي تؤثر أكثر على النموذج.

تفسير المعاملات: توخي الحذر بشأن السببية#

النماذج الخطية هي أداة رائعة لقياس الارتباط الإحصائي، لكن يجب أن نتوخى الحذر عند إصدار بيانات حول السببية، فبعد كل شيء لا يعني الارتباط دائمًا السببية. هذا صعب بشكل خاص في العلوم الاجتماعية لأن المتغيرات التي نلاحظها تعمل فقط كوكلاء لعملية السببية الأساسية.

في حالتنا الخاصة، يمكننا التفكير في EDUCATION للفرد على أنه وكيل لكفاءته المهنية، وهو المتغير الحقيقي الذي نهتم به ولكن لا يمكننا ملاحظته. نود بالتأكيد أن نعتقد أن البقاء في المدرسة لمدة أطول سيزيد من الكفاءة الفنية، ولكن من الممكن أيضًا تمامًا أن تسير السببية في الاتجاه الآخر أيضًا. أي أن أولئك الذين يتمتعون بكفاءة فنية يميلون إلى البقاء في المدرسة لفترة أطول.

من غير المرجح أن يهتم صاحب العمل بأي حالة هي (أو إذا كانت مزيجًا من الاثنين)، طالما ظل مقتنعًا بأن الشخص الذي لديه المزيد من EDUCATION هو الأنسب للوظيفة، فسيكون سعيدًا بدفع WAGE أعلى.

يصبح هذا الخلط بين الآثار مشكلة عند التفكير في شكل من أشكال التدخل، على سبيل المثال، الإعانات الحكومية للشهادات الجامعية أو المواد الترويجية التي تشجع الأفراد على متابعة التعليم العالي. يمكن أن ينتهي الأمر بالمبالغة في تقدير فائدة هذه التدابير، خاصة إذا كانت درجة الخلط قوية. يتوقع نموذجنا زيادة قدرها \(0.054699\) في الأجر بالساعة لكل سنة تعليم. قد يكون التأثير السببي الفعلي أقل بسبب هذا الخلط.

التحقق من تباين المعاملات#

يمكننا التحقق من تباين المعامل من خلال التحقق المتقاطع: إنه شكل من أشكال اضطراب البيانات (يتعلق بـ إعادة التشكيل).

إذا تغيرت المعاملات بشكل كبير عند تغيير مجموعة بيانات الإدخال، فإن متانتها غير مضمونة، وربما يجب تفسيرها بحذر.

from sklearn.model_selection import RepeatedKFold, cross_validate

cv = RepeatedKFold(n_splits=5, n_repeats=5, random_state=0)
cv_model = cross_validate(
    model,
    X,
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)

coefs = pd.DataFrame(
    [
        est[-1].regressor_.coef_ * est[:-1].transform(X.iloc[train_idx]).std(axis=0)
        for est, (train_idx, _) in zip(cv_model["estimator"], cv.split(X, y))
    ],
    columns=feature_names,
)
plt.figure(figsize=(9, 7))
sns.stripplot(data=coefs, orient="h", palette="dark:k", alpha=0.5)
sns.boxplot(data=coefs, orient="h", color="cyan", saturation=0.5, whis=10)
plt.axvline(x=0, color=".5")
plt.xlabel("Coefficient importance")
plt.title("Coefficient importance and its variability")
plt.suptitle("Ridge model, small regularization")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization, Coefficient importance and its variability

مشكلة المتغيرات المترابطة#

تتأثر معاملات AGE و EXPERIENCE بتباين قوي قد يكون بسبب الترابط الخطي بين الميزتين: نظرًا لأن AGE و EXPERIENCE يختلفان معًا في البيانات، فمن الصعب فصل تأثيرهما.

للتحقق من هذا التفسير، نرسم تباين معامل AGE و EXPERIENCE.

plt.ylabel("Age coefficient")
plt.xlabel("Experience coefficient")
plt.grid(True)
plt.xlim(-0.4, 0.5)
plt.ylim(-0.4, 0.5)
plt.scatter(coefs["AGE"], coefs["EXPERIENCE"])
_ = plt.title("Co-variations of coefficients for AGE and EXPERIENCE across folds")
Co-variations of coefficients for AGE and EXPERIENCE across folds

يتم ملء منطقتين: عندما يكون معامل EXPERIENCE موجبًا، يكون معامل AGE سالبًا والعكس صحيح.

للمضي قدمًا، نزيل إحدى الميزتين ونتحقق من تأثير ذلك على استقرار النموذج.

column_to_drop = ["AGE"]

cv_model = cross_validate(
    model,
    X.drop(columns=column_to_drop),
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)

coefs = pd.DataFrame(
    [
        est[-1].regressor_.coef_
        * est[:-1].transform(X.drop(columns=column_to_drop).iloc[train_idx]).std(axis=0)
        for est, (train_idx, _) in zip(cv_model["estimator"], cv.split(X, y))
    ],
    columns=feature_names[:-1],
)
plt.figure(figsize=(9, 7))
sns.stripplot(data=coefs, orient="h", palette="dark:k", alpha=0.5)
sns.boxplot(data=coefs, orient="h", color="cyan", saturation=0.5)
plt.axvline(x=0, color=".5")
plt.title("Coefficient importance and its variability")
plt.xlabel("Coefficient importance")
plt.suptitle("Ridge model, small regularization, AGE dropped")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization, AGE dropped, Coefficient importance and its variability

يُظهر تقدير معامل EXPERIENCE الآن تباينًا أقل بكثير. يظل EXPERIENCE مهمًا لجميع النماذج المدربة أثناء التحقق المتقاطع.

المعالجة المسبقة للمتغيرات الرقمية#

كما ذكر أعلاه (انظر "خط أنابيب التعلم الآلي")، يمكننا أيضًا اختيار قياس القيم الرقمية قبل تدريب النموذج. يمكن أن يكون هذا مفيدًا عندما نطبق قدرًا متشابهًا من التنظيم على كل منهم في ridge. تتم إعادة تعريف المعالج المسبق من أجل طرح المتوسط وقياس المتغيرات إلى تباين الوحدة.

from sklearn.preprocessing import StandardScaler

preprocessor = make_column_transformer(
    (OneHotEncoder(drop="if_binary"), categorical_columns),
    (StandardScaler(), numerical_columns),
)

سيبقى النموذج دون تغيير.

model = make_pipeline(
    preprocessor,
    TransformedTargetRegressor(
        regressor=Ridge(alpha=1e-10), func=np.log10, inverse_func=sp.special.exp10
    ),
)
model.fit(X_train, y_train)
Pipeline(steps=[('columntransformer',
                 ColumnTransformer(transformers=[('onehotencoder',
                                                  OneHotEncoder(drop='if_binary'),
                                                  ['RACE', 'OCCUPATION',
                                                   'SECTOR', 'MARR', 'UNION',
                                                   'SEX', 'SOUTH']),
                                                 ('standardscaler',
                                                  StandardScaler(),
                                                  ['EDUCATION', 'EXPERIENCE',
                                                   'AGE'])])),
                ('transformedtargetregressor',
                 TransformedTargetRegressor(func=<ufunc 'log10'>,
                                            inverse_func=<ufunc 'exp10'>,
                                            regressor=Ridge(alpha=1e-10)))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


مرة أخرى، نتحقق من أداء النموذج المحسوب باستخدام، على سبيل المثال، متوسط الخطأ المطلق للنموذج ومعامل R التربيعي.

mae_train = median_absolute_error(y_train, model.predict(X_train))
y_pred = model.predict(X_test)
mae_test = median_absolute_error(y_test, y_pred)
scores = {
    "MedAE on training set": f"{mae_train:.2f} $/hour",
    "MedAE on testing set": f"{mae_test:.2f} $/hour",
}

_, ax = plt.subplots(figsize=(5, 5))
display = PredictionErrorDisplay.from_predictions(
    y_test, y_pred, kind="actual_vs_predicted", ax=ax, scatter_kwargs={"alpha": 0.5}
)
ax.set_title("Ridge model, small regularization")
for name, score in scores.items():
    ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.tight_layout()
Ridge model, small regularization

لتحليل المعامل، لا يلزم القياس هذه المرة لأنه تم إجراؤه أثناء خطوة المعالجة المسبقة.

coefs = pd.DataFrame(
    model[-1].regressor_.coef_,
    columns=["Coefficients importance"],
    index=feature_names,
)
coefs.plot.barh(figsize=(9, 7))
plt.title("Ridge model, small regularization, normalized variables")
plt.xlabel("Raw coefficient values")
plt.axvline(x=0, color=".5")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization, normalized variables

نقوم الآن بفحص المعاملات عبر عدة طيات تحقق متقاطع. كما في المثال أعلاه، لا نحتاج إلى قياس المعاملات بواسطة الانحراف المعياري لقيم الميزة حيث تم بالفعل إجراء هذا القياس في خطوة المعالجة المسبقة لخط الأنابيب.

cv_model = cross_validate(
    model,
    X,
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)
coefs = pd.DataFrame(
    [est[-1].regressor_.coef_ for est in cv_model["estimator"]], columns=feature_names
)
plt.figure(figsize=(9, 7))
sns.stripplot(data=coefs, orient="h", palette="dark:k", alpha=0.5)
sns.boxplot(data=coefs, orient="h", color="cyan", saturation=0.5, whis=10)
plt.axvline(x=0, color=".5")
plt.title("Coefficient variability")
plt.subplots_adjust(left=0.3)
Coefficient variability

النتيجة مشابهة تمامًا للحالة غير الطبيعية.

النماذج الخطية مع التنظيم#

في ممارسة التعلم الآلي، غالبًا ما يُستخدم انحدار ريدج مع تنظيم غير ضئيل.

أعلاه، قمنا بتحديد هذا التنظيم إلى حد ضئيل جدًا. التنظيم يُحسِّن حالة المشكلة ويقلل من تباين التقديرات. RidgeCV تطبق التحقق المتقاطع من أجل تحديد قيمة معامل التنظيم (alpha) الأنسب للتنبؤ.

from sklearn.linear_model import RidgeCV

alphas = np.logspace(-10, 10, 21)  # alpha values to be chosen from by cross-validation
model = make_pipeline(
    preprocessor,
    TransformedTargetRegressor(
        regressor=RidgeCV(alphas=alphas),
        func=np.log10,
        inverse_func=sp.special.exp10,
    ),
)
model.fit(X_train, y_train)
Pipeline(steps=[('columntransformer',
                 ColumnTransformer(transformers=[('onehotencoder',
                                                  OneHotEncoder(drop='if_binary'),
                                                  ['RACE', 'OCCUPATION',
                                                   'SECTOR', 'MARR', 'UNION',
                                                   'SEX', 'SOUTH']),
                                                 ('standardscaler',
                                                  StandardScaler(),
                                                  ['EDUCATION', 'EXPERIENCE',
                                                   'AGE'])])),
                ('transformedtargetregressor',
                 TransformedTargetRegressor(func=<ufunc 'log10'>,
                                            inverse_func=<ufunc 'exp10'>,
                                            regressor=RidgeCV(alphas=array([1.e-10, 1.e-09, 1.e-08, 1.e-07, 1.e-06, 1.e-05, 1.e-04, 1.e-03,
       1.e-02, 1.e-01, 1.e+00, 1.e+01, 1.e+02, 1.e+03, 1.e+04, 1.e+05,
       1.e+06, 1.e+07, 1.e+08, 1.e+09, 1.e+10]))))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


أولاً نتحقق من قيمة \(\alpha\) التي تم تحديدها.

model[-1].regressor_.alpha_
np.float64(10.0)

ثم نتحقق من جودة التنبؤات.

mae_train = median_absolute_error(y_train, model.predict(X_train))
y_pred = model.predict(X_test)
mae_test = median_absolute_error(y_test, y_pred)
scores = {
    "MedAE on training set": f"{mae_train:.2f} $/hour",
    "MedAE on testing set": f"{mae_test:.2f} $/hour",
}

_, ax = plt.subplots(figsize=(5, 5))
display = PredictionErrorDisplay.from_predictions(
    y_test, y_pred, kind="actual_vs_predicted", ax=ax, scatter_kwargs={"alpha": 0.5}
)
ax.set_title("Ridge model, optimum regularization")
for name, score in scores.items():
    ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.tight_layout()
Ridge model, optimum regularization

قدرة النموذج المنظم على إعادة إنتاج البيانات مشابهة لقدرة النموذج غير المنظم.

coefs = pd.DataFrame(
    model[-1].regressor_.coef_,
    columns=["Coefficients importance"],
    index=feature_names,
)
coefs.plot.barh(figsize=(9, 7))
plt.title("Ridge model, with regularization, normalized variables")
plt.xlabel("Raw coefficient values")
plt.axvline(x=0, color=".5")
plt.subplots_adjust(left=0.3)
Ridge model, with regularization, normalized variables

المعاملات مختلفة بشكل كبير. معاملات AGE و EXPERIENCE كلاهما موجبان لكنهما الآن لهما تأثير أقل على التنبؤ.

يقلل التنظيم من تأثير المتغيرات المترابطة على النموذج لأن الوزن مشترك بين متغيري التنبؤ، لذلك لن يكون لأي منهما أوزان قوية بمفرده.

من ناحية أخرى، فإن الأوزان التي تم الحصول عليها مع التنظيم أكثر استقرارًا (انظر قسم انحدار ريدج والتصنيف في دليل المستخدم). هذا الاستقرار المتزايد واضح من الرسم التخطيطي، الذي تم الحصول عليه من اضطرابات البيانات، في تحقق متقاطع. يمكن مقارنة هذا الرسم التخطيطي بـ الرسم التخطيطي السابق.

cv_model = cross_validate(
    model,
    X,
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)
coefs = pd.DataFrame(
    [est[-1].regressor_.coef_ for est in cv_model["estimator"]], columns=feature_names
)
plt.ylabel("Age coefficient")
plt.xlabel("Experience coefficient")
plt.grid(True)
plt.xlim(-0.4, 0.5)
plt.ylim(-0.4, 0.5)
plt.scatter(coefs["AGE"], coefs["EXPERIENCE"])
_ = plt.title("Co-variations of coefficients for AGE and EXPERIENCE across folds")
Co-variations of coefficients for AGE and EXPERIENCE across folds

النماذج الخطية ذات المعاملات المتفرقة#

هناك إمكانية أخرى لمراعاة المتغيرات المترابطة في مجموعة البيانات، وهي تقدير المعاملات المتفرقة. بطريقة ما فعلنا ذلك بالفعل يدويًا عندما أسقطنا عمود AGE في تقدير ريدج سابق.

تُقدِّر نماذج لاسو (انظر قسم Lasso في دليل المستخدم) المعاملات المتفرقة. LassoCV تطبق التحقق المتقاطع من أجل تحديد قيمة معامل التنظيم (alpha) الأنسب لتقدير النموذج.

from sklearn.linear_model import LassoCV

alphas = np.logspace(-10, 10, 21)  # alpha values to be chosen from by cross-validation
model = make_pipeline(
    preprocessor,
    TransformedTargetRegressor(
        regressor=LassoCV(alphas=alphas, max_iter=100_000),
        func=np.log10,
        inverse_func=sp.special.exp10,
    ),
)

_ = model.fit(X_train, y_train)

أولاً، نتحقق من قيمة \(\alpha\) التي تم تحديدها.

model[-1].regressor_.alpha_
np.float64(0.001)

ثم نتحقق من جودة التنبؤات.

mae_train = median_absolute_error(y_train, model.predict(X_train))
y_pred = model.predict(X_test)
mae_test = median_absolute_error(y_test, y_pred)
scores = {
    "MedAE on training set": f"{mae_train:.2f} $/hour",
    "MedAE on testing set": f"{mae_test:.2f} $/hour",
}

_, ax = plt.subplots(figsize=(6, 6))
display = PredictionErrorDisplay.from_predictions(
    y_test, y_pred, kind="actual_vs_predicted", ax=ax, scatter_kwargs={"alpha": 0.5}
)
ax.set_title("Lasso model, optimum regularization")
for name, score in scores.items():
    ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.tight_layout()
Lasso model, optimum regularization

بالنسبة لمجموعة البيانات لدينا، مرة أخرى، النموذج ليس تنبئيًا للغاية.

coefs = pd.DataFrame(
    model[-1].regressor_.coef_,
    columns=["Coefficients importance"],
    index=feature_names,
)
coefs.plot(kind="barh", figsize=(9, 7))
plt.title("Lasso model, optimum regularization, normalized variables")
plt.axvline(x=0, color=".5")
plt.subplots_adjust(left=0.3)
Lasso model, optimum regularization, normalized variables

يُحدد نموذج لاسو الارتباط بين AGE و EXPERIENCE ويقمع أحدهما من أجل التنبؤ.

من المهم أن تضع في اعتبارك أن المعاملات التي تم إسقاطها قد تظل مرتبطة بالنتيجة بنفسها: اختار النموذج قمعها لأنها لا تجلب سوى القليل من المعلومات الإضافية أو لا تجلب أي معلومات إضافية على رأس الميزات الأخرى. بالإضافة إلى ذلك، يكون هذا الاختيار غير مستقر للميزات المترابطة، ويجب تفسيره بـ حذر.

في الواقع، يمكننا التحقق من تباين المعاملات عبر الطيات.

cv_model = cross_validate(
    model,
    X,
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)
coefs = pd.DataFrame(
    [est[-1].regressor_.coef_ for est in cv_model["estimator"]], columns=feature_names
)
plt.figure(figsize=(9, 7))
sns.stripplot(data=coefs, orient="h", palette="dark:k", alpha=0.5)
sns.boxplot(data=coefs, orient="h", color="cyan", saturation=0.5, whis=100)
plt.axvline(x=0, color=".5")
plt.title("Coefficient variability")
plt.subplots_adjust(left=0.3)
Coefficient variability

نلاحظ أن معاملات AGE و EXPERIENCE تختلف كثيرًا اعتمادًا على الطية.

التفسير السببي الخاطئ#

قد يرغب صانعو السياسات في معرفة تأثير التعليم على الأجور لتقييم ما إذا كانت سياسة معينة مصممة لجذب الناس لمتابعة المزيد من التعليم ستكون منطقية من الناحية الاقتصادية أم لا. بينما تُعد نماذج التعلم الآلي رائعة لقياس الارتباطات الإحصائية، إلا أنها غير قادرة بشكل عام على استنتاج التأثيرات السببية.

قد يكون من المغري النظر إلى معامل التعليم على الأجور من نموذجنا الأخير (أو أي نموذج في هذا الشأن) والاستنتاج بأنه يلتقط التأثير الحقيقي لتغيير في متغير التعليم المعياري على الأجور.

لسوء الحظ، من المحتمل أن تكون هناك متغيرات مرب

Total running time of the script: (0 minutes 16.063 seconds)

Related examples

فشل التعلم الآلي في استنتاج الآثار السببية

فشل التعلم الآلي في استنتاج الآثار السببية

معاملات Ridge كدالة لتنظيم L2

معاملات Ridge كدالة لتنظيم L2

رسم معاملات Ridge كدالة للتنظيم

رسم معاملات Ridge كدالة للتنظيم

هندسة الميزات ذات الصلة بالوقت

هندسة الميزات ذات الصلة بالوقت

Gallery generated by Sphinx-Gallery