Machine Learning: الانحدار الخطي

آخر تحديث: 2026-08-26

الانحدار الخطي هو "Hello World" في ML — بسيط وقابل للتفسير ومفاجئ في فعاليته. إنه نقطة البداية لكل مهمة انحدار.

1. ما ستتعلمه


2. قصة حقيقية من التجارة الإلكترونية

(1) نقطة الألم: لا فكرة عن كيفية تقسيم ميزانية الإعلان

لدى Bob ميزانية إعلانية شهرية قدرها 200 ألف دولار لتوزيعها على Google Ads و Facebook و Email. حدسه يقول "أنفق أكثر، بيع أكثر"، ولكن الشهر الماضي، 50 ألف دولار إضافية على Google Ads لم تجلب سوى 30 ألف دولار من الإيرادات الإضافية — عوائد متناقصة، لكنه ليس لديه أي فكرة عن أين نقطة التحول.

(2) حل الانحدار الخطي

يمكن للانحدار الخطي تحديد مساهمة كل قناة في الإيرادات وإيجاد تخصيص الميزانية الأمثل.

PYTHON
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$

▶ مثال: الانحدار الخطي من الصفر

PYTHON
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:

TEXT 📖 للعرض فقط
# Function defined successfully

(2) تصور الانحدار التدريجي

100%
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

▶ مثال: مقارنة بين حلالين

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully
البُعد LinearRegression (المعادلة العادية) SGDRegressor
الحلال حل تحليلي $(X^TX)^{-1}X^Ty$ انحدار تدريجي تكراري
التعقيد O(n³) في عدد الميزات O(n) لكل خطوة
حجم البيانات المناسب صغير إلى متوسط (n < 100k) كبير (n > 100k)
الدقة حل دقيق حل تقريبي
يتطلب توحيد القياس لا نعم

(2) التنظيم: Ridge/Lasso/ElasticNet

▶ مثال: مقارنة التنظيم

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully
التنظيم عقوبة المعاملات التأثير حالة الاستخدام
Ridge (L2) $\alpha \sum w_i^2$ تقلص المعاملات نحو الصفر العديد من الميزات ذات الصلة
Lasso (L1) $\alpha \sum |w_i|$ تبعثر، يقود بعض المعاملات إلى الصفر اختيار الميزات
ElasticNet L1 + L2 هجين متفرق + مستقر مجموعات من الميزات المترابطة

5. الانحدار المتعدد وتفسير الميزات

▶ مثال: تحليل عائد Bob الإعلاني

PYTHON
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:

TEXT 📖 للعرض فقط
Feature Importance (standardized coefficients):

الخلاصة الرئيسية: يعكس حجم المعاملات الموحدة مباشرة أهمية الميزة. معامل Email البالغ 2.5 أكبر بكثير من معامل Google البالغ 0.8، مما يعني أن كل 1 ألف دولار مستثمر في Email يُرجع 3 أضعاف نفس الاستثمار في Google Ads.


6. تشخيص الانحدار

(1) تحليل البواقي

▶ مثال: لوحة تشخيص الانحدار بأربعة أجزاء

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully

(2) الازدواج الخطي و VIF

▶ مثال: اكتشاف VIF

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully
التشخيص طريقة التحقق المعايير الطبيعية حل الانتهاكات
الخطية رسم البواقي مقابل المتنبأ به تشتت عشوائي أضف حدودًا متعددة الحدود
الطبيعية رسم Q-Q خط مستقيم تقريبًا تحويل لوغاريتمي
تجانس التباين رسم Scale-Location شريط أفقي انحدار موزون
الازدواج الخطي VIF VIF < 10 إزالة أو دمج الميزات

❓ أسئلة شائعة

س هل يتطلب LinearRegression توحيد الميزات؟
ج يمكنه التدريب بدون توحيد، لكن المعاملات لن تكون قابلة للمقارنة (وحدات مختلفة). بعد التوحيد، يعكس حجم المعاملات مباشرة أهمية الميزة. يتطلب Ridge/Lasso/SGD التوحيد.
س هل أختار Ridge أم Lasso؟
ج العديد من الميزات التي قد تساهم جميعها → Ridge. تحتاج إلى اختيار تلقائي للميزات → Lasso. مجموعات من الميزات شديدة الترابط → ElasticNet. عمليًا، جرب الثلاثة واستخدم التحقق المتقاطع لاختيار الأفضل.
س كيف أختار معامل alpha؟
ج استخدم GridSearchCV أو RidgeCV/LassoCV للاختيار التلقائي. يعني alpha الأكبر تنظيمًا أقوى، ويدفع المعاملات أقرب إلى الصفر. ابحث عادةً على مقياس لوغاريتمي: [0.001, 0.01, 0.1, 1, 10, 100].
س كيف أفسر معامل انحدار سالب؟
ج يعني المعامل السالب أن الهدف ينخفض مع زيادة تلك الميزة. على سبيل المثال، إذا كان معامل "معدل الإرجاع" هو -5، فهذا يعني أن الإيرادات تنخفض بمقدار 5 آلاف دولار لكل زيادة بنسبة 1% في معدل الإرجاع.
س ماذا لو كان VIF > 10؟
ج أزل الميزة ذات VIF الأعلى وأعد الحساب. أو بدلاً من ذلك، استخدم PCA لإزالة الترابط الخطي. أو استخدم انحدار Ridge (التنظيم L2 يقاوم بشكل طبيعي الازدواج الخطي).
س ماذا لو لم تكن البواقي موزعة طبيعيًا؟
ج جرب تحويل لوغاريتمي على y (log(y)). إذا أظهرت البواقي شكل قمع (عدم تجانس التباين)، استخدم المربعات الصغرى الموزونة أو طبق تحويل Box-Cox على y.

📖 ملخص

📝 تمارين

  1. أساسي (الصعوبة ⭐): استخدم LinearRegression من sklearn للتنبؤ بمجموعة بيانات California Housing. أخرج R² وجميع معاملات الميزات. تلميح: fetch_california_housing() + model.coef_.
  2. متوسط (الصعوبة ⭐⭐): قارن Ridge (alpha=0.1/1/10/100) على نفس البيانات باستخدام R² المتحقق متقاطعًا وأحجام المعاملات. لاحظ كيف تؤثر قوة التنظيم على المعاملات. تلميح: استخدم Pipeline(StandardScaler+Ridge) + GridSearchCV.
  3. تحدي (الصعوبة ⭐⭐⭐): نفّذ خط أنابيب تشخيص انحدار كامل — بعد التدريب، ارسم رسوم التشخيص ذات الأربعة أجزاء، واحسب VIF، وحدد ما إذا كانت افتراضات الانحدار الخطي صامدة، واقترح تحسينات إذا لم تكن كذلك. تلميح: راجع كود التشخيص في القسم 6.

← الدرس السابق: تمرين شامل — مشروع المبتدئين | الدرس التالي: الانحدار اللوجستي →

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%