File size: 4,829 Bytes
52e8264
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# 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)
```