0Pricing
Machine Learning Academy · درس

استراتيجيات الحد من التحيز: المعالجة المسبقة وأثناء المعالجة وما بعدها

سيطبّق المتعلمون إعادة الترجيح (المعالجة المسبقة)، ويضيفون قيدًا للإنصاف إلى التدريب (أثناء المعالجة)، ويعايرون عتبات القرار لكل مجموعة (المعالجة اللاحقة)، مع مقارنة المفاضلات.

استراتيجيات الحد من التحيز: المعالجة المسبقة وأثناء المعالجة وما بعدها درس مجاني في Machine Learning Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Machine Learning Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Machine Learning Academy 4 دروس في المجموع.

بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.

Three Stages of Bias Mitigation

Bias mitigation strategies fall into three categories based on where in the ML pipeline they intervene. Pre-processing methods modify the training data before any model is trained. In-processing methods modify the learning algorithm itself to enforce fairness during training. Post-processing methods adjust the model's outputs after training without changing the model. Each approach has different trade-offs in accuracy, flexibility, and computational cost.

Pre-processing: Reweighing

Reweighing assigns different sample weights to training examples so that the weighted distribution is fair with respect to the protected attribute. Examples from under-represented (label, group) combinations receive higher weight; over-represented combinations receive lower weight. The model is then trained with these weights using the sample_weight parameter, which most scikit-learn estimators accept. No changes to the algorithm are needed.

from fairlearn.preprocessing import CorrelationRemover
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

# Reweighing: compute weights to balance (group, label) combinations
def compute_reweighing_weights(y, sensitive):
    n = len(y)
    weights = np.ones(n)
    for label in np.unique(y):
        for group in np.unique(sensitive):
            mask = (y == label) & (sensitive == group)
            expected = (y == label).mean() * (sensitive == group).mean()
            actual = mask.mean()
            if actual > 0:
                weights[mask] = expected / actual
    return weights

# Usage: model.fit(X_train, y_train, sample_weight=weights)

Pre-processing: Removing Proxy Features

Even without the protected attribute, proxy features like zip code or surname can encode demographic information. Correlation removal projects features to reduce their correlation with the sensitive attribute. fairlearn.preprocessing.CorrelationRemover implements this by subtracting the projection of each feature onto the sensitive attribute, producing decorrelated features. This reduces the model's ability to learn discriminatory patterns through proxies.

from fairlearn.preprocessing import CorrelationRemover
import numpy as np

X = np.random.randn(500, 5)
sensitive = np.random.choice([0, 1], 500)

# Project out correlation with sensitive attribute
cr = CorrelationRemover(sensitive_feature_ids=[0])  # column 0 is sensitive
X_fair = cr.fit_transform(X)

print('Original correlation with sensitive:', np.corrcoef(X[:, 0], sensitive)[0, 1])
print('After removal:', np.corrcoef(X_fair[:, 0], sensitive)[0, 1])

In-processing: Exponentiated Gradient

The Exponentiated Gradient algorithm from fairlearn is a meta-algorithm that wraps any scikit-learn classifier and iteratively trains it with adjusted sample weights to satisfy a fairness constraint (e.g., equalized odds). It solves a constrained optimisation problem: minimise classification error subject to the fairness constraint staying within a tolerance epsilon. The result is a randomised classifier ensemble that balances accuracy and fairness.

from fairlearn.reductions import ExponentiatedGradient, EqualizedOdds
from sklearn.linear_model import LogisticRegression
import numpy as np

np.random.seed(42)
X = np.random.randn(600, 4)
sensitive = np.random.choice(['A', 'B'], 600)
y = (X[:, 0] + np.random.randn(600) * 0.5 > 0).astype(int)

base_estimator = LogisticRegression(max_iter=1000)
mitigator = ExponentiatedGradient(
    base_estimator,
    constraints=EqualizedOdds(),
    eps=0.05  # allowed violation
)
mitigator.fit(X, y, sensitive_features=sensitive)
y_pred_fair = mitigator.predict(X)
print('Predictions generated:', y_pred_fair[:10])

In-processing: GridSearch Fairness

fairlearn.reductions.GridSearch is a simpler alternative that performs a grid search over Lagrange multipliers for the fairness constraint, training a separate model for each multiplier value. The result is a set of (accuracy, fairness) trade-off points that form a Pareto frontier. You can then select the model that meets your minimum fairness threshold at maximum accuracy. It is fully compatible with any sklearn estimator.

from fairlearn.reductions import GridSearch, DemographicParity
from sklearn.tree import DecisionTreeClassifier

gs = GridSearch(
    DecisionTreeClassifier(max_depth=4),
    constraints=DemographicParity(),
    grid_size=10
)
gs.fit(X, y, sensitive_features=sensitive)

# Inspect trade-off frontier
for pred in gs.predictors_:
    y_hat = pred.predict(X)
    acc = (y_hat == y).mean()
    dp_diff = abs(
        y_hat[sensitive == 'A'].mean() - y_hat[sensitive == 'B'].mean()
    )
    print(f'Accuracy={acc:.3f} | DP difference={dp_diff:.3f}')

Post-processing: Threshold Calibration per Group

Threshold calibration applies different decision thresholds to each demographic group so that a chosen fairness criterion is satisfied. For example, to achieve equal opportunity you lower the threshold for a disadvantaged group until its true positive rate matches the advantaged group. This is the simplest post-processing approach and requires no retraining, making it easy to apply to any deployed model.

import numpy as np
from sklearn.metrics import recall_score

# Assume model outputs probabilities: proba_A, proba_B for each group
np.random.seed(0)
proba = np.random.beta(2, 5, 600)  # model probability scores
y_true = (proba > 0.3 + np.random.randn(600) * 0.1).astype(int)

mask_A = sensitive == 'A'
mask_B = sensitive == 'B'

# Find threshold for group B to match group A's TPR at threshold 0.5
base_tpr = recall_score(y_true[mask_A], (proba[mask_A] >= 0.5).astype(int))
for thresh in np.arange(0.2, 0.7, 0.01):
    tpr_B = recall_score(y_true[mask_B], (proba[mask_B] >= thresh).astype(int))
    if abs(tpr_B - base_tpr) < 0.02:
        print(f'Equal-opportunity threshold for group B: {thresh:.2f} (TPR={tpr_B:.3f})')
        break

Post-processing: ThresholdOptimizer in fairlearn

fairlearn.postprocessing.ThresholdOptimizer automates per-group threshold calibration. It takes a fitted estimator, wraps it, and finds the group-specific thresholds that minimise a chosen objective (e.g., balanced accuracy) subject to a fairness constraint. At prediction time it routes each sample to the appropriate threshold based on its group membership — transparent and auditable.

from fairlearn.postprocessing import ThresholdOptimizer
from fairlearn.metrics import equalized_odds_difference
from sklearn.linear_model import LogisticRegression

base_model = LogisticRegression(max_iter=1000)
base_model.fit(X, y)

to = ThresholdOptimizer(
    estimator=base_model,
    constraints='equalized_odds',
    objective='balanced_accuracy_score',
    predict_method='predict_proba'
)
to.fit(X, y, sensitive_features=sensitive)
y_pred_to = to.predict(X, sensitive_features=sensitive)

print('EO difference before:', equalized_odds_difference(y, base_model.predict(X), sensitive_features=sensitive))
print('EO difference after:', equalized_odds_difference(y, y_pred_to, sensitive_features=sensitive))

Comparing the Three Approaches

Each stage of intervention has practical implications. Pre-processing is model-agnostic and can be applied once for all downstream models, but may lose information. In-processing integrates fairness into the optimisation objective, often achieving the best accuracy-fairness balance, but requires retraining. Post-processing is the easiest to retrofit to a deployed model without retraining, but may be less effective when probability scores are poorly calibrated across groups.

comparison = {
    'Pre-processing (reweighing/removal)': {
        'requires_retraining': True,
        'model_agnostic': True,
        'typical_accuracy_cost': 'Low to medium'
    },
    'In-processing (Exp. Gradient)': {
        'requires_retraining': True,
        'model_agnostic': True,
        'typical_accuracy_cost': 'Medium'
    },
    'Post-processing (threshold optimizer)': {
        'requires_retraining': False,
        'model_agnostic': True,
        'typical_accuracy_cost': 'Low (if scores are calibrated)'
    }
}
for method, props in comparison.items():
    print(method, '->', props)

Measuring Mitigation Effectiveness

After applying any mitigation strategy, re-run the full set of fairness metrics to confirm improvement. Also measure the accuracy cost: the difference in overall accuracy between the unmitigated and mitigated models. Plot the fairness-accuracy trade-off curve across different mitigation strengths to help stakeholders choose an operating point. Document the chosen point and the rationale in the model card.

from fairlearn.metrics import MetricFrame, demographic_parity_difference
from sklearn.metrics import accuracy_score
import numpy as np

results = []
for threshold_offset in [0.0, -0.05, -0.10, -0.15]:
    y_hat = np.where(
        sensitive == 'B',
        (proba >= 0.5 + threshold_offset).astype(int),
        (proba >= 0.5).astype(int)
    )
    acc = accuracy_score(y_true, y_hat)
    dp = demographic_parity_difference(y_true, y_hat, sensitive_features=sensitive)
    results.append({'offset': threshold_offset, 'accuracy': acc, 'dp_diff': abs(dp)})
    print(f'offset={threshold_offset:.2f}: accuracy={acc:.3f}, dp_diff={abs(dp):.3f}')

Fairness Beyond Technical Metrics

Technical mitigation is necessary but not sufficient for responsible AI. Affected communities must be involved in defining what fairness means for their context. Deployment teams should establish ongoing monitoring for fairness drift as population distributions change. Regular third-party audits, clear appeal mechanisms for affected individuals, and transparent disclosure of the mitigation approach are all part of a complete responsible AI practice.

# Non-technical checklist for responsible deployment
checklist = [
    'Affected community consulted on fairness definition',
    'Model card documents protected attributes, metrics, and mitigation',
    'Fairness metrics monitored in production alongside accuracy',
    'Individual appeal process exists for adverse decisions',
    'Third-party audit scheduled before high-stakes deployment',
    'Data provenance documented to detect historical bias sources'
]
for item in checklist:
    print('[x]', item)

End-to-End Fairness Pipeline

A complete fairness-aware ML pipeline combines all stages. Start with pre-processing (remove proxy features, apply reweighing), train with an in-processing constraint to bake fairness into the objective, and apply post-processing threshold calibration if residual disparity remains. After deployment, integrate monitoring to detect when distribution shifts cause fairness metrics to drift, and trigger retraining when thresholds are exceeded.

# Sketch of a full fairness-aware pipeline
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from fairlearn.reductions import ExponentiatedGradient, EqualizedOdds
from sklearn.linear_model import LogisticRegression

# Step 1: pre-process (reweighing done externally via sample_weight)
preprocessor = StandardScaler()
X_scaled = preprocessor.fit_transform(X)

# Step 2: in-processing with fairness constraint
mitigator = ExponentiatedGradient(
    LogisticRegression(max_iter=1000),
    constraints=EqualizedOdds(),
    eps=0.05
)
mitigator.fit(X_scaled, y, sensitive_features=sensitive)

# Step 3: evaluate and document
y_fair = mitigator.predict(X_scaled)
print('Fair model accuracy:', (y_fair == y).mean().round(3))

Quick Check

Test your understanding of Machine Learning with Python concepts from this lesson.

Lesson Recap

In this lesson you learned: pre-processing methods (reweighing, correlation removal) modify training data before model fitting, in-processing methods (Exponentiated Gradient, GridSearch) bake fairness constraints into the learning objective, and post-processing methods (ThresholdOptimizer) calibrate thresholds per group after training. Together these tools let you systematically reduce bias measured by demographic parity, equal opportunity, and equalized odds. Next up we begin the capstone project by scoping a real end-to-end ML problem.

الأسئلة الشائعة

هل درس «استراتيجيات الحد من التحيز: المعالجة المسبقة وأثناء المعالجة وما بعدها» مجاني؟

نعم — نص درس «استراتيجيات الحد من التحيز: المعالجة المسبقة وأثناء المعالجة وما بعدها» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Machine Learning Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Machine Learning Academy 4 دروس في المجموع.

ماذا ستتعلم في «استراتيجيات الحد من التحيز: المعالجة المسبقة وأثناء المعالجة وما بعدها»؟

سيطبّق المتعلمون إعادة الترجيح (المعالجة المسبقة)، ويضيفون قيدًا للإنصاف إلى التدريب (أثناء المعالجة)، ويعايرون عتبات القرار لكل مجموعة (المعالجة اللاحقة)، مع مقارنة المفاضلات. تتمرن على Machine Learning Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Machine Learning Academy؟

لا تُشترط خبرة سابقة. Machine Learning Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «استراتيجيات الحد من التحيز: المعالجة المسبقة وأثناء المعالجة وما بعدها»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Machine Learning Academy هذا؟

نعم. كل درس في Machine Learning Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. قيم SHAP: الأهمية العامة والمحلية للميزات
  2. LIME: تفسيرات محلية قابلة للفهم ومستقلة عن النموذج
  3. مقاييس الإنصاف: التكافؤ الديموغرافي وتكافؤ الفرص
  4. استراتيجيات الحد من التحيز: المعالجة المسبقة وأثناء المعالجة وما بعدها
← العودة إلى Machine Learning Academy