|
Download CLASS_B_BIAS_MITIGATION.md from NoumanUsman/ChestSense: direct link, hf CLI and curl.
- Browser
- Download file 4.83 kB
-
https://huggingface.co/NoumanUsman/ChestSense/resolve/main/CLASS_B_BIAS_MITIGATION.md
- Command line
-
hf download hf://NoumanUsman/ChestSense/CLASS_B_BIAS_MITIGATION.md
-
curl -L -o CLASS_B_BIAS_MITIGATION.md https://huggingface.co/NoumanUsman/ChestSense/resolve/main/CLASS_B_BIAS_MITIGATION.md
4.83 kB
| # Quick Mitigation Options for Class B Bias | |
| This document outlines immediate workarounds for the Class B bias while you retrain the model. | |
| ## Option 1: Logit-Bias Correction (Inference-Time Hack) | |
| Add this function to `main.py` in the `/analyze` endpoint before softmax: | |
| ```python | |
| def apply_logit_bias_correction(logits, bias_correction=None): | |
| """ | |
| Apply bias correction to logits before softmax. | |
| This temporarily mitigates the Class B overprediction. | |
| Args: | |
| logits: Raw model output (before softmax) | |
| bias_correction: Dict with per-class bias adjustments | |
| Returns: | |
| Adjusted logits | |
| """ | |
| if bias_correction is None: | |
| # DEFAULT: Reduce Class B, boost others | |
| bias_correction = { | |
| 0: +0.3, # Adenocarcinoma: slight boost | |
| 1: -1.2, # Small Cell: heavy penalty | |
| 2: +0.3, # Large Cell: slight boost | |
| 3: +0.3, # Squamous Cell: slight boost | |
| } | |
| for class_idx, bias in bias_correction.items(): | |
| logits[class_idx] += bias | |
| return logits | |
| ``` | |
| **Usage in classification code:** | |
| ```python | |
| # in the /analyze endpoint, after model inference: | |
| with torch.no_grad(): | |
| logits = model(input_tensor) # Raw output | |
| logits_corrected = apply_logit_bias_correction(logits[0]) # Apply correction | |
| probs = torch.softmax(logits_corrected, dim=0) # Then softmax | |
| ``` | |
| ## Option 2: Confidence-Based Filtering | |
| Add confidence thresholds per-class: | |
| ```python | |
| CONFIDENCE_THRESHOLDS = { | |
| 0: 0.30, # Adenocarcinoma: accept if >= 30% | |
| 1: 0.60, # Small Cell: require higher confidence (70%+) | |
| 2: 0.25, # Large Cell: accept lower confidence | |
| 3: 0.25, # Squamous Cell: accept lower confidence | |
| } | |
| if confidence < CONFIDENCE_THRESHOLDS[class_idx]: | |
| logger.warning(f"LOW CONFIDENCE: {class_idx} at {confidence*100:.1f}% (threshold: {CONFIDENCE_THRESHOLDS[class_idx]*100}%)") | |
| # Optionally flag as uncertain | |
| ``` | |
| ## Option 3: Report Confidence Scores | |
| Return all confidence scores to the frontend and let the UI handle the ambiguity: | |
| **API Response Structure:** | |
| ```json | |
| { | |
| "success": true, | |
| "detections": [ | |
| { | |
| "tumor_id": 1, | |
| "prediction": "Small Cell (Class B)", | |
| "confidence": 75.97, | |
| "all_confidences": { | |
| "Adenocarcinoma": 3.5, | |
| "Small Cell": 75.97, | |
| "Large Cell": 6.89, | |
| "Squamous Cell": 13.64 | |
| }, | |
| "confidence_status": "UNCERTAIN", // NEW: Add this | |
| "note": "Low confidence in this prediction. Multiple classes viable." | |
| } | |
| ] | |
| } | |
| ``` | |
| Then in Flutter UI: | |
| ```dart | |
| // Show warning if confidence is borderline | |
| if (allConfidences.values.any((c) => c > 20 && c != maxConfidence)) { | |
| showConfidenceWarning("Multiple classes possible. Review with specialist."); | |
| } | |
| ``` | |
| ## Option 4: Ensemble Multiple Models | |
| If you have other trained models: | |
| ```python | |
| def ensemble_predict(input_tensor, models_list): | |
| """Average predictions from multiple models""" | |
| all_probs = [] | |
| for model in models_list: | |
| with torch.no_grad(): | |
| probs = torch.softmax(model(input_tensor), dim=1)[0] | |
| all_probs.append(probs) | |
| ensemble_probs = torch.stack(all_probs).mean(dim=0) | |
| return ensemble_probs | |
| ``` | |
| ## Comparison of Options | |
| | Option | Ease | Effectiveness | Drawbacks | | |
| |--------|------|---------------|-----------| | |
| | **Logit Bias Correction** | ⭐⭐⭐ Easy | ⭐⭐ Moderate | Doesn't fix root issue; magic numbers | | |
| | **Confidence Filtering** | ⭐⭐⭐ Easy | ⭐⭐ Moderate | Still wrong predictions, just flagged | | |
| | **Report All Scores** | ⭐⭐⭐ Easy | ⭐⭐ Moderate | Defers to human; adds complexity | | |
| | **Ensemble Models** | ⭐⭐ Medium | ⭐⭐⭐ Good | Need multiple models | | |
| | **Retrain with Weights** | ⭐ Hard | ⭐⭐⭐⭐⭐ Excellent | Takes time; need training data | | |
| ## ⚠️ Recommendation | |
| **Use Option 3 (Report All Scores) + Option 1 (Logit Correction) as SHORT-TERM fix** | |
| - Apply light logit correction to reduce Class B dominance | |
| - Return all confidence scores to frontend | |
| - Show warnings when confidence is low (< 60%) | |
| - **Plan to retrain model properly** (highest priority) | |
| **Then retrain the model** (Option 5 - see `retrain_with_balanced_weights.py`) | |
| ## Testing Your Fix | |
| After implementing mitigation: | |
| ```bash | |
| # Test with images from each class | |
| LABELS=("Class_A" "Class_B" "Class_E" "Class_E") | |
| for label in "${LABELS[@]}"; do | |
| img=$(ls test-images/${label}_*.png | head -1) | |
| echo "Testing: $img" | |
| curl -X POST -F "file=@$img" http://localhost:5001/analyze | grep -A 10 '"prediction"' | |
| done | |
| ``` | |
| Expected results after fix: | |
| ``` | |
| Class A image → Mostly Class A (not Class B) | |
| Class B image → Mostly Class B (correct) | |
| Class E image → Mix of E and others (not all B) | |
| Class G image → Mix of G and others (not all B) | |
| ``` | |