الانحدار اللوجستي (Logistic Regression): التنبؤ بالنتائج الثنائية (مثل: مريض/سليم)
الانحدار اللوجستي (Logistic Regression): التنبؤ بالنتائج الثنائية (مثل: مريض/سليم)
يُعد Logistic Regression من أهم خوارزميات التصنيف في مجال مقدمة في تعلم الآلة (Machine Learning): الفرق بين التعلم الخاضع وغير الخاضع للإشراف. وعلى الرغم من اسمه، فهو لا يُستخدم لتوقع قيم مستمرة مثل Linear Regression، بل لتقدير احتمال انتماء السجل إلى فئة ثنائية مثل مريض/سليم، احتيالي/سليم، أو سيشتري/لن يشتري.
تكمن قوته في بساطته التفسيرية وسرعته وقابليته العالية للتوسع داخل بيئات البيانات الحديثة، سواء على عينات صغيرة باستخدام Pandas وscikit-learn، أو على بيانات ضخمة باستخدام Apache Spark. ولهذا فهو حاضر بقوة في الطب، والتمويل، وتحليل المخاطر، وأنظمة التوصية، وكشف الاحتيال.
ما هو الانحدار اللوجستي ولماذا يُستخدم؟
الفكرة الأساسية هي تحويل المجموع الخطي للمدخلات إلى احتمال بين 0 و1 عبر دالة لوجستية تسمى Sigmoid. إذا اقترب الاحتمال من 1 فهذا يعني أن النموذج يرجّح الفئة الإيجابية، وإذا اقترب من 0 فهو يميل إلى الفئة السلبية.
رياضياً، يبدأ النموذج من معادلة خطية تضم الميزات مثل العمر، الضغط، والسكر، ثم يطبق عليها دالة Sigmoid لإخراج احتمال الإصابة. بعد ذلك نحدد عتبة قرار مثل 0.5 لتحويل الاحتمال إلى تصنيف نهائي.
متى يكون مناسباً؟
- عندما تكون المتغيرات المستهدفة ثنائية.
- عندما نحتاج إلى نموذج سريع وسهل التفسير.
- عندما يكون فهم تأثير كل متغير مهماً في القرار.
- عند بناء خط أساس قوي قبل الانتقال إلى نماذج أكثر تعقيداً.
في البيئات الطبية والمالية، لا تكفي دقة النموذج وحدها. الميزة المهمة في
Logistic Regressionهي أنه يوفّر احتمالات قابلة للتفسير، ما يسمح ببناء سياسات تشغيلية مثل تصعيد الحالات عالية الخطورة أو إرسالها إلى مراجعة بشرية قبل اتخاذ القرار النهائي.
تجهيز البيانات قبل التدريب
نجاح النموذج يبدأ من جودة البيانات، لا من الخوارزمية وحدها. لذلك يجب المرور بمراحل التنظيف والتحويل بعناية، خاصة إذا كانت البيانات قادمة من مصادر متعددة داخل ETL Pipeline. ويمكن الاستفادة هنا من خطوات تنظيف البيانات (Data Cleaning): اكتشاف ومعالجة القيم المفقودة (Missing Values) ومعالجة البيانات المكررة والمشوهة (Duplicates & Outliers) باستخدام بايثون.
أهم خطوات المعالجة
- فحص القيم المفقودة ومعالجتها.
- استبعاد السجلات غير المنطقية أو القيم الشاذة المدمرة للتدريب.
- ترميز المتغيرات الفئوية باستخدام
One-Hot Encodingعند الحاجة. - تحجيم الميزات العددية عند اختلاف المقاييس، كما في إعداد البيانات للتدريب (Data Preprocessing): تحجيم البيانات (Scaling & Normalization).
- إنشاء ميزات مشتقة ذات دلالة باستخدام هندسة الميزات (Feature Engineering): كيف تستخرج بيانات جديدة من البيانات الحالية؟.
- تقسيم البيانات إلى تدريب واختبار وفق مبادئ تقسيم البيانات (Train/Test Split): لماذا يجب أن نختبر النموذج على بيانات لم يرها من قبل؟.
بناء نموذج عملي باستخدام بايثون
في البيئات التحليلية اليومية، يبدأ كثير من علماء البيانات باستخدام Pandas وNumPy لمعالجة البيانات، كما تم شرحه في مكتبة Pandas (1): قراءة واستدعاء البيانات من ملفات CSV و Excel برمجياً ومكتبة NumPy: القوة الضاربة في معالجة المصفوفات والعمليات الرياضية المعقدة.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (
accuracy_score,
precision_score,
recall_score,
f1_score,
roc_auc_score,
confusion_matrix,
classification_report
)
# قراءة البيانات
df = pd.read_csv("patients_data.csv")
# اختيار الأعمدة
features = ["age", "blood_pressure", "glucose", "bmi", "cholesterol"]
target = "has_disease"
# معالجة القيم المفقودة
df[features] = df[features].fillna(df[features].median())
# فصل المدخلات والمخرجات
X = df[features]
y = df[target]
# تقسيم البيانات
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# التحجيم
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# تدريب النموذج
model = LogisticRegression(max_iter=1000)
model.fit(X_train_scaled, y_train)
# التنبؤ
y_pred = model.predict(X_test_scaled)
y_prob = model.predict_proba(X_test_scaled)[:, 1]
# التقييم
print("Accuracy:", accuracy_score(y_test, y_pred))
print("Precision:", precision_score(y_test, y_pred))
print("Recall:", recall_score(y_test, y_pred))
print("F1 Score:", f1_score(y_test, y_pred))
print("ROC AUC:", roc_auc_score(y_test, y_prob))
print("Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
print("\nClassification Report:\n", classification_report(y_test, y_pred))
في هذا المثال، يقوم النموذج بتقدير احتمال إصابة المريض بناءً على عدة مؤشرات صحية. ثم تُستخدم دوال مثل predict وpredict_proba لإخراج التصنيف النهائي والاحتمال المرتبط به.
كيف نفسّر نتائج النموذج؟
من الأخطاء الشائعة الاكتفاء بمؤشر Accuracy. في التشخيص الطبي مثلاً قد تكون البيانات غير متوازنة، حيث عدد الأصحاء أكبر بكثير من عدد المرضى. في هذه الحالة قد يحقق النموذج دقة مرتفعة ظاهرياً، لكنه يفشل في اكتشاف المرضى الحقيقيين.
مقاييس مهمة
Precision: نسبة الحالات التي تنبأ النموذج بأنها إيجابية وكانت فعلاً إيجابية.Recall: قدرة النموذج على اكتشاف كل الحالات الإيجابية الحقيقية.F1 Score: توازن بينPrecisionوRecall.ROC AUC: يقيس قدرة النموذج على التمييز بين الفئتين عبر عتبات مختلفة.
لفهم البنية الإحصائية العميقة وراء هذه المقاييس، يفيد الرجوع إلى الإحصاء الوصفي والاستدلالي: مفاهيم لا غنى عنها لكل عالم بيانات، لأن جودة التفسير لا تقل أهمية عن جودة التدريب.
في أنظمة القرار الحساسة، لا تجعل عتبة التصنيف ثابتة دائماً عند
0.5. الأفضل اختيار العتبة حسب تكلفة الخطأ: هل تفويت مريض أخطر من إنذار خاطئ؟ هذا القرار جزء من معمارية المنتج التحليلي، وليس مجرد تفصيل برمجي.
تطبيق الانحدار اللوجستي على البيانات الضخمة باستخدام Spark
عندما يتجاوز حجم البيانات قدرة الذاكرة المحلية، ننتقل إلى المعالجة الموزعة باستخدام Apache Spark. هنا يصبح النموذج جزءاً من Pipeline إنتاجي لمعالجة ملايين السجلات القادمة من أنظمة تشغيلية أو مستودعات بيانات.
from pyspark.sql import SparkSession
from pyspark.ml.feature import VectorAssembler, StandardScaler
from pyspark.ml.classification import LogisticRegression
from pyspark.ml import Pipeline
from pyspark.ml.evaluation import BinaryClassificationEvaluator
spark = SparkSession.builder.appName("LogisticRegressionMedical").getOrCreate()
df = spark.read.csv("patients_bigdata.csv", header=True, inferSchema=True)
feature_cols = ["age", "blood_pressure", "glucose", "bmi", "cholesterol"]
assembler = VectorAssembler(inputCols=feature_cols, outputCol="features_raw")
scaler = StandardScaler(inputCol="features_raw", outputCol="features", withStd=True, withMean=True)
lr = LogisticRegression(featuresCol="features", labelCol="has_disease", maxIter=50)
pipeline = Pipeline(stages=[assembler, scaler, lr])
train_df, test_df = df.randomSplit([0.8, 0.2], seed=42)
model = pipeline.fit(train_df)
predictions = model.transform(test_df)
evaluator = BinaryClassificationEvaluator(
labelCol="has_disease",
rawPredictionCol="rawPrediction",
metricName="areaUnderROC"
)
auc = evaluator.evaluate(predictions)
print("AUC:", auc)
هذا النهج يتيح تشغيل التدريب فوق بنية موزعة، مع الاستفادة من تقسيم البيانات تلقائياً على عدة Executors. كما يساعد على دمج خطوات التحويل والتدريب في مسار موحد قابل لإعادة الاستخدام والتشغيل المجدول.
تحسين الأداء ومعمارية البيانات
في مشاريع الإنتاج الفعلية، لا يُفصل النموذج عن هندسة البيانات. يجب تتبع مصدر كل عمود، وإصدار نسخة واضحة من الميزات، وحفظ قواعد التحويل نفسها بين التدريب والإنتاج. كما أن اختيار الميزات يجب أن يستند إلى تحليل ارتباطات منطقي مثل ما ورد في الارتباط (Correlation): كيف تكتشف العلاقة الخفية بين المتغيرات (مثل السعر والطلب)؟.
لرفع الأداء في بيئات
Big Data، احرص على تقليل عمليات السحب المتكرر من التخزين، واستخدم أعمدة قليلة وضرورية فقط، وطبّق منطق التنظيف والتحويل مبكراً داخل طبقةETLبدلاً من تأجيله إلى لحظة التدريب. هذا يقلل الكلفة الحسابية ويرفع استقرار النتائج.
متى لا يكون الانحدار اللوجستي كافياً؟
إذا كانت العلاقات بين المتغيرات شديدة التعقيد أو غير خطية بدرجة كبيرة، فقد لا يكون Logistic Regression الخيار الأمثل منفرداً. لكنه يبقى نموذجاً مرجعياً ممتازاً للمقارنة، ووسيلة قوية لفهم البيانات قبل الانتقال إلى نماذج مثل الأشجار، والغابات العشوائية، أو Gradient Boosting.
الانحدار اللوجستي ليس مجرد خوارزمية مدرسية بسيطة، بل أداة عملية فعالة لبناء أنظمة تصنيف موثوقة وقابلة للتفسير. وعندما يقترن بتنظيف بيانات جيد، وهندسة ميزات مدروسة، وخطوط معالجة محكمة، فإنه يقدم قيمة حقيقية في التنبؤ بالنتائج الثنائية، خصوصاً في المجالات التي تتطلب دقة تشغيلية وشفافية تحليلية في الوقت نفسه.