Machine Learning: الانحدار اللوجستي
آخر تحديث: 2026-08-26
الانحدار اللوجستي ليس حقًا "انحدارًا" — إنه مصنف قوي يُخرج الاحتمالات، وليس الأرقام الخام.
1. ما ستتعلمه
- من الخطي إلى اللوجستي: دالة السيني، مخرجات الاحتمالات، وحدود القرار
- دالة الخسارة: خسارة الانتروبيا المتقاطعة والبديهة وراء تقدير الاحتمال الأقصى
- امتدادات متعددة الفئات: One-vs-Rest و Softmax (Multinomial)
- مقاييس التقييم: Accuracy/Precision/Recall/F1/ROC-AUC وكيفية الاختيار لسيناريو عملك
- تنبؤ Alice بتخلي المستخدمين: تحديد العملاء عالي المخاطر في بيئة SaaS
2. قصة حقيقية من عمليات SaaS
(1) نقطة الألم: اكتشاف التخلي متأخرًا جدًا، ومكلف استعادتهم
تدير Alice منصة SaaS تضم 50 ألف مستخدم نشط بمعدل تخلي شهري 8% — أي 4 آلاف مستخدم يُفقدون كل شهر. مع قيمة كل مستخدم حوالي 2 ألف دولار سنويًا، الخسارة الشهرية تقريبًا 670 ألف دولار. المشكلة: بحلول وقت إلغاء المستخدم للاشتراك، يكون الأوان قد فات — معدل نجاح الاستعادة أقل من 5%.
(2) حل الانحدار اللوجستي
يمكن للانحدار اللوجستي التنبؤ باحتمالية التخلي بمجرد أن يُظهر سلوك المستخدم علامات تحذير، مما يوفر إنذارًا مبكرًا يتراوح بين 2-4 أسابيع ويرفع معدل نجاح الاستعادة إلى 30%.
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
model = LogisticRegression(class_weight="balanced")
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred, target_names=["Retained", "Churned"]))
(3) النتيجة: كل عميل مُستحق قيمته 2 ألف دولار
باستخدام الانحدار اللوجستي، تحدد Alice المستخدمين عالي مخاطر التخلي مسبقًا وتوفر 1.2 ألف مستخدم شهريًا، مما يضيف ما يقرب من 2.4 مليون دولار في الإيرادات السنوية. استعادة العميل تكلف حوالي 50 دولارًا — أقل بكثير من تكلفة 500 دولار لاكتساب عميل جديد.
3. من الخطي إلى اللوجستي
(1) دالة السيني
يستخدم الانحدار اللوجستي دالة السيني لضغط المخرجات الخطية في النطاق [0,1] كاحتمالية: $\sigma(z) = \frac{1}{1+e^{-z}}$
graph LR
INPUT[ميزات الإدخال x] --> LINEAR[تركيبة خطية<br/>z = w·x + b]
LINEAR --> SIGMOID[دالة السيني<br/>σ = 1/(1+e^(-z))]
SIGMOID --> PROB[P(y=1) = σ]
PROB --> DECISION{P ≥ 0.5?}
DECISION -->|نعم| CLASS1[الفئة 1<br/>مثلًا: متخلي]
DECISION -->|لا| CLASS0[الفئة 0<br/>مثلًا: محتفظ]
▶ مثال: تصور دالة السيني
import numpy as np
import matplotlib.pyplot as plt
z = np.linspace(-8, 8, 100)
sigmoid = 1 / (1 + np.exp(-z))
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(z, sigmoid, linewidth=2, color="#2196F3")
ax.axhline(0.5, color="red", linestyle="--", alpha=0.5, label="Decision boundary")
ax.axvline(0, color="gray", linestyle="--", alpha=0.3)
ax.set_xlabel("z (linear combination)")
ax.set_ylabel("σ(z) = P(y=1)")
ax.set_title("Sigmoid Function")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("sigmoid.png", dpi=150)
Output:
# Executed successfully
(2) خسارة الانتروبيا المتقاطعة
دالة الخسارة للانحدار اللوجستي هي الانتروبيا المتقاطعة (Log Loss):
$L = -\frac{1}{n}\sum[y\log(\hat{y}) + (1-y)\log(1-\hat{y})]$
| التسمية الحقيقية | الاحتمالية المتنبأ بها | الخسارة | البديهة |
|---|---|---|---|
| y=1 | ŷ=0.99 | 0.01 | عقوبة شبه معدومة |
| y=1 | ŷ=0.5 | 0.69 | عقوبة معتدلة |
| y=1 | ŷ=0.01 | 4.60 | عقوبة ضخمة |
| y=0 | ŷ=0.01 | 0.01 | عقوبة شبه معدومة |
| y=0 | ŷ=0.99 | 4.60 | عقوبة ضخمة |
4. الانحدار اللوجستي مع Scikit-learn
▶ مثال: تصنيف ثنائي — التنبؤ بنية شراء المستخدم
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
import numpy as np
rng = np.random.default_rng(42)
n = 1000
X = rng.standard_normal((n, 4)) # [وقت_التصفح, عدد_الصفحات, قيمة_السلة, عدد_الزيارات]
y = (X[:, 0] * 0.5 + X[:, 1] * 1.2 + X[:, 2] * 2.0 + rng.normal(0, 0.5, n) > 1.5).astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
pipe = Pipeline([
("scaler", StandardScaler()),
("model", LogisticRegression(random_state=42)),
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
y_prob = pipe.predict_proba(X_test)[:, 1] # احتمالية الفئة 1
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"\nConfusion Matrix:\n{confusion_matrix(y_test, y_pred)}")
print(f"\nClassification Report:\n{classification_report(y_test, y_pred)}")
print(f"\nSample probabilities: {y_prob[:5].round(3)}")
Output:
# Executed successfully
▶ مثال: تنبؤ Alice بتخلي مستخدمي SaaS
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, precision_recall_fscore_support
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 5000
df = pd.DataFrame({
"tenure_months": rng.integers(1, 60, n),
"monthly_usage_hours": rng.exponential(20, n),
"support_tickets": rng.poisson(2, n),
"payment_delay_days": rng.integers(0, 30, n),
"plan_tier": rng.choice([1, 2, 3], n, p=[0.5, 0.35, 0.15]),
})
# منطق التخلي: مدة قصيرة + استخدام قليل + تذاكر كثيرة + تأخيرات
churn_score = (
-0.05 * df["tenure_months"]
- 0.1 * df["monthly_usage_hours"]
+ 0.5 * df["support_tickets"]
+ 0.1 * df["payment_delay_days"]
+ rng.normal(0, 1, n)
)
df["churned"] = (churn_score > 2).astype(int)
print(f"Churn rate: {df['churned'].mean():.1%}")
# التدريب بـ class_weight="balanced" للبيانات غير المتوازنة
X = df.drop(columns=["churned"])
y = df["churned"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
model = LogisticRegression(class_weight="balanced", max_iter=500, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_prob)
precision, recall, f1, _ = precision_recall_fscore_support(y_test, y_pred, average="binary")
print(f"AUC-ROC: {auc:.4f}")
print(f"Precision: {precision:.4f}, Recall: {recall:.4f}, F1: {f1:.4f}")
Output:
# Executed successfully
5. امتدادات متعددة الفئات
(1) One-vs-Rest مقابل Softmax
▶ مثال: متعدد الفئات — التصنيف التلقائي لفئات المنتجات
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score
X, y = load_iris(return_X_y=True)
# One-vs-Rest (افتراضي)
ovr_model = LogisticRegression(multi_class="ovr", max_iter=200)
ovr_scores = cross_val_score(ovr_model, X, y, cv=5, scoring="accuracy")
# Softmax (Multinomial)
softmax_model = LogisticRegression(multi_class="multinomial", max_iter=200)
softmax_scores = cross_val_score(softmax_model, X, y, cv=5, scoring="accuracy")
print(f"One-vs-Rest: {ovr_scores.mean():.4f} +/- {ovr_scores.std():.4f}")
print(f"Softmax: {softmax_scores.mean():.4f} +/- {softmax_scores.std():.4f}")
Output:
# Executed successfully
| البُعد | One-vs-Rest | Softmax (Multinomial) |
|---|---|---|
| المبدأ | K من المصنفات الثنائية | مصنف واحد متعدد الفئات |
| معايرة الاحتمالات | لا ضمان بأن مجموعها 1 | مجموعها 1 بدقة |
| الكفاءة الحسابية | قابل للتوازي | يتطلب تحسينًا مشتركًا |
| الأفضل لـ | فئات كثيرة | فئات قليلة، تحتاج احتمالات |
6. مقاييس التصنيف والخيارات التجارية
(1) مصفوفة الارتباك والمقاييس المشتقة
▶ مثال: منحنى ROC ومنحنى PR
from sklearn.metrics import roc_curve, auc, precision_recall_curve
import matplotlib.pyplot as plt
import numpy as np
# افترض y_test و y_prob من المثال السابق
rng = np.random.default_rng(42)
y_test = rng.choice([0, 1], 500, p=[0.85, 0.15])
y_prob = np.clip(y_test * 0.8 + rng.normal(0, 0.3, 500), 0, 1)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# منحنى ROC
fpr, tpr, _ = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
axes[0].plot(fpr, tpr, color="#2196F3", lw=2, label=f"AUC = {roc_auc:.3f}")
axes[0].plot([0, 1], [0, 1], "r--", alpha=0.5)
axes[0].set_xlabel("False Positive Rate")
axes[0].set_ylabel("True Positive Rate")
axes[0].set_title("ROC Curve")
axes[0].legend()
# منحنى PR
precision_vals, recall_vals, _ = precision_recall_curve(y_test, y_prob)
axes[1].plot(recall_vals, precision_vals, color="#4CAF50", lw=2)
axes[1].set_xlabel("Recall")
axes[1].set_ylabel("Precision")
axes[1].set_title("Precision-Recall Curve")
plt.tight_layout()
plt.savefig("roc_pr_curves.png", dpi=150)
Output:
# Executed successfully
(2) اختيار المقاييس لسيناريو عملك
| السيناريو | المقياس ذو الأولوية | السبب | استراتيجية العتبة |
|---|---|---|---|
| تنبؤ تخلي المستخدمين | Recall | تفويت عميل متخلٍ مكلف | خفض العتبة (0.3) |
| تصفية البريد المزعج | Precision | تصنيف بريد شرعي بشكل خاطئ غير مقبول | رفع العتبة (0.7) |
| توصية المنتج | F1 | التوازن بين الدقة والتغطية | الافتراضي (0.5) |
| كشف الاحتيال | Recall | تفويت الاحتيال مكلف للغاية | خفض العتبة (0.2) |
❓ أسئلة شائعة
PolynomialFeatures) ليتعلم حدود قرار غير خطية — essentially doing linear classification in a higher-dimensional space.LogisticRegression(penalty="l1", solver="saga") لتمكين L1.📖 ملخص
- يستخدم الانحدار اللوجستي السيني لتحويل المخرج الخطي إلى احتمالات، مع 0.5 كحد قرار افتراضي
- تعاقب خسارة الانتروبيا المتقاطعة بشدة التنبؤات الواثقة الخاطئة
- يتعامل class_weight="balanced" مع عدم توازن الفئات — ضروري لتنبؤ التخلي
- متعدد الفئات: One-vs-Rest (مصنفات ثنائية متعددة) مقابل Softmax (احتمالات موحدة)؛ الأخير يعاير الاحتمالات بشكل أفضل
- اختيار المقاييس التجارية: التخلي → Recall، البريد المزعج → Precision، التوصية → F1
- ضبط عتبة القرار يمكن أن يحسن القيمة التجارية — لا ترتبط بالافتراضي 0.5
📝 تمارين
- أساسي (الصعوبة ⭐): استخدم الانحدار اللوجستي للتصنيف متعدد الفئات على مجموعة بيانات Iris، واطبع الدقة و classification_report. تلميح:
LogisticRegression(max_iter=200)+classification_report. - متوسط (الصعوبة ⭐⭐): محاكاة مجموعة بيانات غير متوازنة (95:5) وقارن الاستدعاء للانحدار اللوجستي الافتراضي مقابل class_weight="balanced". تلميح: استخدم
make_classification(weights=[0.95, 0.05]). - تحدي (الصعوبة ⭐⭐⭐): نفّذ خط أنابيب تنبؤ التخلي الكامل لـ Alice — ولّد بيانات مستخدمي SaaS المحاكاة، ودرب نموذج الانحدار اللوجستي، وارسم منحنيات ROC/PR، واحسب Recall/Precision/F1 عند عتبات مختلفة، واعثر على العتبة المثلى تجاريًا. تلميح:
roc_curve+precision_recall_curve+ تكرار على العتبات.
← الدرس السابق: الانحدار الخطي | الدرس التالي: أشجار القرار والغابات العشوائية →