Files
2025-10-09 13:36:15 +08:00

220 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Review: Evidence Weighting Notebook (04_test_evidence_weighting.py)
**Date**: 2025-10-09
**Status**: ✅ Runs successfully with warnings
---
## ✅ Execution Summary
- **Runtime**: ~30 seconds (10 cache builds + 30 predictions)
- **Coverage**: 8/10 targets predicted (2 targets not sampled)
- **Output**: 30 predictions saved to JSONL
- **Best accuracy**: 62.5% (5/8) with weights (0.4, 0.2, 0.1, 0.2, 0.1)
---
## ⚠️ Warnings Found
### 1. RuntimeWarning: overflow encountered in exp
**Location**: Lines 59, 277
**Cause**: `raw_logprob_diff` values are extreme (~±995) causing `np.exp(-995)` → 0
**Impact**: Score saturates to 0.0 or 1.0 (not a bug, but loses granularity)
```python
# Line 277
score = 1 / (1 + np.exp(-raw_diff)) # raw_diff = 995 → exp(-995) = 0 → score = 1.0
```
**Fix**: Clip logprob diffs before sigmoid:
```python
raw_diff_clipped = np.clip(raw_diff, -20, 20) # exp(±20) is numerically safe
score = 1 / (1 + np.exp(-raw_diff_clipped))
```
---
## 📊 Results Analysis
### Grid Search Findings
| Weights (flip, conf, var, mp, cons) | Accuracy | Cal Error | Notes |
|--------------------------------------|----------|-----------|-------|
| **(0.4, 0.2, 0.1, 0.2, 0.1)** | **62.5%** | **0.484** | Heavy flip (BEST) |
| (0.5, 0.1, 0.1, 0.2, 0.1) | 62.5% | 0.478 | Even heavier flip |
| (0.3, 0.2, 0.2, 0.2, 0.1) | 50.0% | 0.494 | Original baseline |
| (0.2, 0.3, 0.2, 0.2, 0.1) | 50.0% | 0.500 | More conf |
| (0.1, 0.4, 0.1, 0.3, 0.1) | 50.0% | 0.504 | Conf + MP |
**Key observations**:
- **Flip sensitivity dominates**: Higher flip weight (0.4-0.5) → better accuracy
- **Variance/MP less useful**: Reducing their weight doesn't hurt
- **Small sample**: 8 targets is noisy, but trend is clear
### Ensemble Statistics (Best Weights)
| UID | Mean | Std | Predicted | True | Correct |
|-----|------|-----|-----------|------|---------|
| 9 | 1.000 | 0.000 | 1 | 0 | ✗ |
| 0 | 0.643 | 0.226 | 1 | 0 | ✗ |
| 7 | 0.500 | 0.316 | 0 | 0 | ✓ |
| 3 | 0.500 | 0.000 | 0 | 0 | ✓ |
| 6 | 0.688 | 0.348 | 1 | 1 | ✓ |
| 5 | 0.500 | 0.000 | 0 | 1 | ✗ |
| 1 | 0.833 | 0.236 | 1 | 1 | ✓ |
| 2 | 0.750 | 0.250 | 1 | 1 | ✓ |
**Patterns**:
- **High confidence wrong**: uid=9 (mean=1.0, std=0.0) predicts 1 but true=0
- **No variance = no info**: uid=9, 3, 5 have std=0.0 (only 1-2 predictions)
- **Moderate variance useful**: uid=6 (std=0.348) is correct
---
## 🔍 Consistency Analysis
### Data Structure (5 question groups × 2 contradictory claims)
```
Group 0 (watermelon seeds):
uid=0, key=B, label=0: "You grow watermelon..." (FALSE)
uid=1, key=A, label=1: "The watermelon passes..." (TRUE)
Group 1 (fortune cookies):
uid=2, key=A, label=1: "The precise origin..." (TRUE)
uid=3, key=B, label=0: "Fortune cookies originated in China" (FALSE)
Group 2 (veins):
uid=4, key=B, label=0: "...deoxygenated blood" (FALSE)
uid=5, key=A, label=1: "...blue light penetration" (TRUE)
Group 3 (chili pepper):
uid=6, key=A, label=1: [Correct answer]
uid=7, key=B, label=0: [Wrong answer]
Group 4 (missing person):
uid=8, key=A, label=1: [Correct answer]
uid=9, key=B, label=0: [Wrong answer]
```
**Consistency structure**: Each group has 2 items with **different** `consistency_key` (A vs B) and **opposite** labels (1 vs 0). These are **contradictory claims**, not paraphrases.
### Consistency Score Calculation Issue
**Current code** (line 159-162):
```python
source_key = consistency_map.get(source_uid, "")
target_key = consistency_map.get(target_uid, "")
evidence[key]['consistency_score'] = 1.0 if source_key == target_key else 0.5
```
**Problem**: This treats `key=A` matching `key=A` as high consistency (1.0), but in TruthfulQA, same key within the same `consistency_id` means **contradictory claims should oppose**.
**Expected behavior**:
- If `consistency_id` matches AND `consistency_key` matches → labels should be **same** (paraphrases)
- If `consistency_id` matches AND `consistency_key` differs → labels should be **opposite** (contradictions)
**Current dataset**: No paraphrases in first 10 examples! All pairs are contradictions (A vs B within same group).
---
## 🐛 Issues Found
### 1. **Consistency scoring doesn't match TQA structure**
- Current: `same key = 1.0, diff key = 0.5`
- TQA reality: All pairs in test group are contradictions (A vs B)
- Effect: Consistency score always 0.5 → no signal
**Fix needed**:
```python
# Check if same consistency_id (group)
source_group = consistency_id_map.get(source_uid, "")
target_group = consistency_id_map.get(target_uid, "")
if source_group != target_group:
consistency_score = 0.5 # Unrelated
elif source_key == target_key:
# Paraphrase: labels should match
consistency_score = 1.0 if source_label == target_label else 0.0
else:
# Contradiction: labels should oppose
consistency_score = 1.0 if source_label != target_label else 0.0
```
### 2. **Context scores all 0.5**
Looking at JSONL output: `"raw_logprob": 0.5` for ALL context examples
**Cause**: Line 312 stores **score** (calibrated) not raw_logprob:
```python
context_score_cache[ex['uid']] = score # This is calibrated 0-1
```
But later used as if it's raw (line 346):
```python
source_score = 1 / (1 + np.exp(-source_raw_lp)) # Expects raw logprob
```
**Fix**: Store both raw and calibrated in cache:
```python
context_score_cache[ex['uid']] = {'raw': raw_lp, 'score': score}
```
### 3. **Evidence types redundant**
All evidence sources have `conf:0.62` (essentially constant)
**Cause**: All context examples cached as 0.5 → sigmoid(0) = 0.5 → calibrated = 0.62 (wait, math doesn't add up... let me check)
Actually looking at cache build (line 310-312): Zero-shot predictions return `score` which gets stored. This score is used for all evidence, making `direct_confidence` constant across all pairs.
---
## 📈 Evidence Pairs Analysis
Top 10 evidence pairs show:
- **flip_sensitivity**: 0.0 to 0.50 (reasonable variance)
- **direct_confidence**: ALL 0.62 (no variance = no signal!)
- **ensemble_variance**: 0.0 to 0.35 (good signal)
- **mutual_predictability**: 0.67 to 1.00 (moderate signal)
- **consistency_score**: ALL 0.5 or 1.0 (limited signal due to issue #1)
**Why flip weighting works**: It's the ONLY evidence source with real variance besides ensemble_var.
---
## ✅ What Works Well
1. **Grid search architecture**: Fast post-hoc weight tuning confirmed
2. **Prediction storage**: JSONL with full context preserves all info
3. **Emoji display**: `P(9 | 5=A[🟡], 8=A[🟡], 6=B*[🟡]...) = 🟢1.000` is readable
4. **Coverage tracking**: "8/10 targets predicted" catches sampling gaps
5. **Async execution**: 30 predictions in ~30s (1/sec) is reasonable
---
## 🎯 Recommendations
### Immediate Fixes (High Priority)
1. **Clip logprob diffs** to avoid overflow warnings
2. **Fix context cache** to store raw logprobs not calibrated scores
3. **Fix consistency scoring** to handle contradictions vs paraphrases
### Next Steps (Medium Priority)
4. **Scale up**: 100+ predictions to densify evidence graph
5. **Add global examples**: Mix in 2-3 random examples from other groups to break echo chamber
6. **Logprob evidence**: Convert all evidence sources to log-space before combining
### Advanced (Low Priority)
7. **Learned weights**: Use logistic regression on larger dataset
8. **Directional evidence**: Track if flip improves or worsens predictions
9. **Adaptive budget**: Spend more predictions on high-variance targets
---
## 📝 Summary
**Status**: Notebook runs successfully and proves the grid search concept works.
**Key finding**: Flip sensitivity provides useful signal (62.5% vs 50% baseline), but other evidence sources are currently redundant due to implementation issues.
**Next action**: Fix the 3 bugs above, then re-run with 100 predictions to see if evidence sources become complementary at scale.