mirror of
https://github.com/wassname/Unsupervised-Elicitation.git
synced 2026-09-10 11:50:22 +08:00
220 lines
7.7 KiB
Markdown
220 lines
7.7 KiB
Markdown
# 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.
|