Machine Learning: الانحدار الخطي
آخر تحديث: 2026-08-26
الانحدار الخطي هو "Hello World" في ML — بسيط وقابل للتفسير ومفاجئ في فعاليته. إنه نقطة البداية لكل مهمة انحدار.
1. ما ستتعلمه
- الرياضيات وراء الانحدار الخطي: دالة الفرضية، دالة الخسارة (MSE)، الانحدار التدريجي
- تنفيذ Scikit-learn: LinearRegression مقابل SGDRegressor، التنظيم (Ridge/Lasso/ElasticNet)
- الانحدار المتعدد وتفسير الميزات: المعنى الاقتصادي للمعاملات
- اختبار الفرضيات والتشخيص: تحليل البواقي، رسوم Q-Q، عدم تجانس التباين، الازدواج الخطي (VIF)
- توقع مبيعات Bob: التنبؤ بإيرادات الشهر التالي باستخدام الإنفاق الإعلاني التاريخي + بيانات الزيارات
2. قصة حقيقية من التجارة الإلكترونية
(1) نقطة الألم: لا فكرة عن كيفية تقسيم ميزانية الإعلان
لدى Bob ميزانية إعلانية شهرية قدرها 200 ألف دولار لتوزيعها على Google Ads و Facebook و Email. حدسه يقول "أنفق أكثر، بيع أكثر"، ولكن الشهر الماضي، 50 ألف دولار إضافية على Google Ads لم تجلب سوى 30 ألف دولار من الإيرادات الإضافية — عوائد متناقصة، لكنه ليس لديه أي فكرة عن أين نقطة التحول.
(2) حل الانحدار الخطي
يمكن للانحدار الخطي تحديد مساهمة كل قناة في الإيرادات وإيجاد تخصيص الميزانية الأمثل.
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X_train, y_train)
# كل معامل = مساهمة هامشية لكل 1 ألف دولار من الإنفاق الإعلاني
for channel, coef in zip(channels, model.coef_):
print(f"{channel}: +{coef:.2f}k USD revenue per 1k USD spend")
(3) النتيجة: تحسن بنسبة 35% في العائد على الاستثمار
يكتشف Bob أن معامل Google Ads هو 0.8 (كل 1 ألف دولار يُنفق يُرجع 0.8 ألف دولار)، بينما معامل Email هو 2.5 (كل 1 ألف دولار يُنفق يُرجع 2.5 ألف دولار). بعد إعادة تخصيص الميزانية، يتحسن العائد الإجمالي على الاستثمار بنسبة 35%.
3. الرياضيات وراء الانحدار الخطي
(1) دالة الفرضية ودالة الخسارة
يفترض الانحدار الخطي أن المخرجات هي تركيبة خطية من المدخلات: $\hat{y} = w_1x_1 + w_2x_2 + ... + b$
تستخدم دالة الخسارة MSE (متوسط مربع الخطأ): $L = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2$
▶ مثال: الانحدار الخطي من الصفر
import numpy as np
# الانحدار التدريجي للانحدار الخطي
def linear_regression_gd(X, y, lr=0.01, epochs=1000):
n_samples, n_features = X.shape
w = np.zeros(n_features)
b = 0.0
for epoch in range(epochs):
# التمرير الأمامي
y_pred = X @ w + b
# حساب التدرجات
dw = (2 / n_samples) * (X.T @ (y_pred - y))
db = (2 / n_samples) * np.sum(y_pred - y)
# تحديث المعاملات
w -= lr * dw
b -= lr * db
if epoch % 200 == 0:
loss = np.mean((y - y_pred) ** 2)
print(f"Epoch {epoch}: MSE = {loss:.4f}")
return w, b
# اختبار ببيانات بسيطة
rng = np.random.default_rng(42)
X = rng.uniform(0, 10, (100, 1))
y = 3 * X.squeeze() + 7 + rng.normal(0, 2, 100)
w, b = linear_regression_gd(X, y, lr=0.01, epochs=1000)
print(f"\nLearned: w={w[0]:.2f}, b={b:.2f}")
print(f"True: w=3.00, b=7.00")
Output:
# Function defined successfully
(2) تصور الانحدار التدريجي
sequenceDiagram
participant Init as تهيئة المعاملات
participant Fwd as التمرير الأمامي
participant Loss as حساب الخسارة
participant Grad as حساب التدرج
participant Update as تحديث المعاملات
Init->>Fwd: w=0, b=0
loop كل حقبة
Fwd->>Loss: y_pred = Xw + b
Loss->>Grad: MSE = mean((y - y_pred)²)
Grad->>Update: dw, db = gradients
Update->>Fwd: w -= lr*dw, b -= lr*db
end
4. تنفيذ Scikit-learn
(1) LinearRegression مقابل SGDRegressor
▶ مثال: مقارنة بين حلالين
from sklearn.linear_model import LinearRegression, SGDRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np
rng = np.random.default_rng(42)
n = 500
X = rng.uniform(0, 100, (n, 3))
y = 50 + 0.8 * X[:, 0] + 1.2 * X[:, 1] - 0.5 * X[:, 2] + rng.normal(0, 5, n)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# الطريقة 1: المعادلة العادية (حل مغلق، دقيق)
lr_normal = LinearRegression()
lr_normal.fit(X_train, y_train)
# الطريقة 2: SGD (تكراري، لمجموعات البيانات الكبيرة)
lr_sgd = Pipeline([
("scaler", StandardScaler()),
("sgd", SGDRegressor(max_iter=1000, learning_rate="constant", eta0=0.01, random_state=42)),
])
lr_sgd.fit(X_train, y_train)
print(f"Normal Equation R²: {lr_normal.score(X_test, y_test):.4f}")
print(f"SGD R²: {lr_sgd.score(X_test, y_test):.4f}")
print(f"\nNormal coefficients: {lr_normal.coef_.round(2)}")
print(f"True coefficients: [0.80, 1.20, -0.50]")
Output:
# Executed successfully
| البُعد | LinearRegression (المعادلة العادية) | SGDRegressor |
|---|---|---|
| الحلال | حل تحليلي $(X^TX)^{-1}X^Ty$ | انحدار تدريجي تكراري |
| التعقيد | O(n³) في عدد الميزات | O(n) لكل خطوة |
| حجم البيانات المناسب | صغير إلى متوسط (n < 100k) | كبير (n > 100k) |
| الدقة | حل دقيق | حل تقريبي |
| يتطلب توحيد القياس | لا | نعم |
(2) التنظيم: Ridge/Lasso/ElasticNet
▶ مثال: مقارنة التنظيم
from sklearn.linear_model import Ridge, Lasso, ElasticNet
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np
rng = np.random.default_rng(42)
n, p = 100, 20 # p > الميزات ذات الصلة (5 فقط مهمة)
X = rng.standard_normal((n, p))
true_coefs = np.zeros(p)
true_coefs[:5] = [3, -2, 1.5, 0.8, -0.5]
y = X @ true_coefs + rng.normal(0, 1, n)
models = {
"LinearRegression": LinearRegression(),
"Ridge (alpha=1)": Ridge(alpha=1),
"Lasso (alpha=0.1)": Lasso(alpha=0.1),
"ElasticNet (alpha=0.1)": ElasticNet(alpha=0.1, l1_ratio=0.5),
}
for name, model in models.items():
pipe = Pipeline([("scaler", StandardScaler()), ("model", model)])
scores = cross_val_score(pipe, X, y, cv=5, scoring="r2")
pipe.fit(X, y)
nonzero = np.sum(np.abs(pipe.named_steps["model"].coef_) > 0.01)
print(f"{name:25s}: R²={scores.mean():.3f}, Non-zero coefs={nonzero}/{p}")
Output:
# Executed successfully
| التنظيم | عقوبة المعاملات | التأثير | حالة الاستخدام |
|---|---|---|---|
| Ridge (L2) | $\alpha \sum w_i^2$ | تقلص المعاملات نحو الصفر | العديد من الميزات ذات الصلة |
| Lasso (L1) | $\alpha \sum |w_i|$ | تبعثر، يقود بعض المعاملات إلى الصفر | اختيار الميزات |
| ElasticNet | L1 + L2 هجين | متفرق + مستقر | مجموعات من الميزات المترابطة |
5. الانحدار المتعدد وتفسير الميزات
▶ مثال: تحليل عائد Bob الإعلاني
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 200
df = pd.DataFrame({
"google_ads_k": rng.uniform(20, 80, n),
"facebook_ads_k": rng.uniform(10, 50, n),
"email_marketing_k": rng.uniform(5, 30, n),
"traffic_k": rng.uniform(50, 300, n),
})
df["revenue_k"] = (
100
+ 0.8 * df["google_ads_k"]
+ 1.2 * df["facebook_ads_k"]
+ 2.5 * df["email_marketing_k"]
+ 0.15 * df["traffic_k"]
+ rng.normal(0, 10, n)
)
X = df.drop(columns=["revenue_k"])
y = df["revenue_k"]
# التدريب بميزات موحدة للمقارنة العادلة
pipe = Pipeline([("scaler", StandardScaler()), ("model", LinearRegression())])
pipe.fit(X, y)
# تفسير المعاملات الموحدة (ترتيب الأهمية)
coefs = pipe.named_steps["model"].coef_
importance = pd.DataFrame({
"feature": X.columns,
"std_coef": coefs,
"abs_importance": np.abs(coefs),
}).sort_values("abs_importance", ascending=False)
print("Feature Importance (standardized coefficients):")
print(importance.to_string(index=False))
Output:
Feature Importance (standardized coefficients):
الخلاصة الرئيسية: يعكس حجم المعاملات الموحدة مباشرة أهمية الميزة. معامل Email البالغ 2.5 أكبر بكثير من معامل Google البالغ 0.8، مما يعني أن كل 1 ألف دولار مستثمر في Email يُرجع 3 أضعاف نفس الاستثمار في Google Ads.
6. تشخيص الانحدار
(1) تحليل البواقي
▶ مثال: لوحة تشخيص الانحدار بأربعة أجزاء
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression
from scipy import stats
rng = np.random.default_rng(42)
X = rng.uniform(0, 100, 200).reshape(-1, 1)
y = 50 + 0.8 * X.squeeze() + rng.normal(0, 5, 200)
model = LinearRegression().fit(X, y)
y_pred = model.predict(X)
residuals = y - y_pred
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# (1) البواقي مقابل القيم المتنبأ بها (تحقق من تجانس التباين)
axes[0, 0].scatter(y_pred, residuals, alpha=0.5, s=15)
axes[0, 0].axhline(0, color="red", linestyle="--")
axes[0, 0].set_xlabel("Fitted Values")
axes[0, 0].set_ylabel("Residuals")
axes[0, 0].set_title("Residuals vs Fitted")
# (2) رسم Q-Q (تحقق من الطبيعية)
stats.probplot(residuals, plot=axes[0, 1])
axes[0, 1].set_title("Q-Q Plot")
# (3) Scale-Location (تحقق من التباين)
axes[1, 0].scatter(y_pred, np.sqrt(np.abs(residuals / np.std(residuals))), alpha=0.5, s=15)
axes[1, 0].set_xlabel("Fitted Values")
axes[1, 0].set_ylabel("Scale-Location")
axes[1, 0].set_title("Scale-Location")
# (4) الفعلي مقابل المتنبأ به
axes[1, 1].scatter(y, y_pred, alpha=0.5, s=15)
axes[1, 1].plot([y.min(), y.max()], [y.min(), y.max()], "r--")
axes[1, 1].set_xlabel("Actual")
axes[1, 1].set_ylabel("Predicted")
axes[1, 1].set_title("Actual vs Predicted")
plt.tight_layout()
plt.savefig("regression_diagnostics.png", dpi=150)
Output:
# Executed successfully
(2) الازدواج الخطي و VIF
▶ مثال: اكتشاف VIF
from statsmodels.stats.outliers_influence import variance_inflation_factor
import pandas as pd
import numpy as np
# VIF > 10 يشير إلى ازدواج خطي شديد
rng = np.random.default_rng(42)
n = 100
df = pd.DataFrame({
"ad_spend": rng.uniform(10, 100, n),
"traffic": rng.normal(0, 1, n) * 50 + 500,
"clicks": rng.normal(0, 1, n) * 100 + 1000, # مترابطة بشدة مع ad_spend
"revenue": rng.uniform(50, 500, n),
})
features = ["ad_spend", "traffic", "clicks"]
for i, col in enumerate(features):
vif = variance_inflation_factor(df[features].values, i)
print(f"{col:12s}: VIF = {vif:.2f} {'⚠️ HIGH' if vif > 10 else '✅ OK'}")
Output:
# Executed successfully
| التشخيص | طريقة التحقق | المعايير الطبيعية | حل الانتهاكات |
|---|---|---|---|
| الخطية | رسم البواقي مقابل المتنبأ به | تشتت عشوائي | أضف حدودًا متعددة الحدود |
| الطبيعية | رسم Q-Q | خط مستقيم تقريبًا | تحويل لوغاريتمي |
| تجانس التباين | رسم Scale-Location | شريط أفقي | انحدار موزون |
| الازدواج الخطي | VIF | VIF < 10 | إزالة أو دمج الميزات |
❓ أسئلة شائعة
📖 ملخص
- يفترض الانحدار الخطي أن y هو تركيبة خطية من x، يُحل عبر خسارة MSE + الانحدار التدريجي أو المعادلة العادية
- واجهة sklearn الموحدة: LinearRegression (حل دقيق) مقابل SGDRegressor (بيانات كبيرة)
- ثلاثي التنظيم: Ridge (L2 يقلص المعاملات) / Lasso (L1 يتبعثر) / ElasticNet (هجين L1+L2)
- يعكس حجم المعاملات الموحدة أهمية الميزة ويوجه مباشرة القرارات التجارية
- فحوصات التشخيص الأربعة: رسم البواقي (الخطية) / رسم Q-Q (الطبيعية) / Scale-Location (التباين) / VIF (الترابط الخطي)
- تفسير المعاملات بمصطلحات تجارية هو أعظم قوة للانحدار الخطي — القابلية للتفسير تتفوق على النماذج الصندوقية السوداء
📝 تمارين
- أساسي (الصعوبة ⭐): استخدم LinearRegression من sklearn للتنبؤ بمجموعة بيانات California Housing. أخرج R² وجميع معاملات الميزات. تلميح:
fetch_california_housing()+model.coef_. - متوسط (الصعوبة ⭐⭐): قارن Ridge (alpha=0.1/1/10/100) على نفس البيانات باستخدام R² المتحقق متقاطعًا وأحجام المعاملات. لاحظ كيف تؤثر قوة التنظيم على المعاملات. تلميح: استخدم Pipeline(StandardScaler+Ridge) + GridSearchCV.
- تحدي (الصعوبة ⭐⭐⭐): نفّذ خط أنابيب تشخيص انحدار كامل — بعد التدريب، ارسم رسوم التشخيص ذات الأربعة أجزاء، واحسب VIF، وحدد ما إذا كانت افتراضات الانحدار الخطي صامدة، واقترح تحسينات إذا لم تكن كذلك. تلميح: راجع كود التشخيص في القسم 6.
← الدرس السابق: تمرين شامل — مشروع المبتدئين | الدرس التالي: الانحدار اللوجستي →