add: ADR-003 Future-as-Label demo — detailed implementation plan with research validation
Browse files- docs/ADR-003-future-as-label.md +445 -0
docs/ADR-003-future-as-label.md
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ADR-003: Future-as-Label Demo on Olist
|
| 2 |
+
|
| 3 |
+
**Status:** Proposed
|
| 4 |
+
**Date:** 2026-05-02
|
| 5 |
+
**Author:** Rafael Ferraz
|
| 6 |
+
**Context:** V4.2 concluded. Next experiment: implement the Future-as-Label paradigm (arXiv 2601.06336) as a proof-of-concept on the Olist e-commerce dataset to demonstrate that GRPO with a proper scoring rule reward produces calibrated probability estimates.
|
| 7 |
+
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
## 1. Context
|
| 11 |
+
|
| 12 |
+
### What is Future-as-Label?
|
| 13 |
+
|
| 14 |
+
The **Foresight Learning** paradigm (Turtel et al., 2601.06336) trains LLMs to produce calibrated probability estimates by:
|
| 15 |
+
|
| 16 |
+
1. **Temporal masking:** At prediction time `t`, the model only sees information timestamped ≤ `t` (even when training offline on resolved events)
|
| 17 |
+
2. **Terminal reward via proper scoring rule:** GRPO reward is assigned only after binary outcome resolution — no intermediate rewards
|
| 18 |
+
3. **Calibration pressure:** A proper scoring rule penalizes overconfidence more than appropriate uncertainty, creating an intrinsic pressure toward calibrated outputs that SFT cannot replicate
|
| 19 |
+
|
| 20 |
+
**Paper results (Qwen3-32B, 160 GRPO steps):**
|
| 21 |
+
- Brier score: 0.2472 → **0.1793** (−27%)
|
| 22 |
+
- ECE: 0.2175 → **0.1042** (halved)
|
| 23 |
+
- Outperforms Qwen3-235B base (7× larger) on calibration
|
| 24 |
+
|
| 25 |
+
### Why this matters for Tucano2-Commerce
|
| 26 |
+
|
| 27 |
+
The V4.2 project showed GRPO can teach format-following (extraction +29.5%) but struggles with reasoning tasks (SQL −15.6%). Probability calibration is fundamentally different:
|
| 28 |
+
- The model doesn't need to "know" new facts — it needs to express uncertainty correctly
|
| 29 |
+
- The reward signal is dense (every sample gets a continuous score based on the probability)
|
| 30 |
+
- Brier/log score rewards are mathematically guaranteed to be optimized by the true probability
|
| 31 |
+
|
| 32 |
+
This tests whether GRPO on a 0.5B model can learn **calibration** — a capability orthogonal to format-following.
|
| 33 |
+
|
| 34 |
+
### Infrastructure continuity
|
| 35 |
+
|
| 36 |
+
| Component | V4.2 | FAL Demo | Change? |
|
| 37 |
+
|---|---|---|---|
|
| 38 |
+
| Model | Tucano2-qwen-0.5B-Instruct | Same | No |
|
| 39 |
+
| Framework | Unsloth + TRL 0.24.0 | Same | No |
|
| 40 |
+
| Hardware | L4 (24GB) | Same | No |
|
| 41 |
+
| Data source | 1,480 custom prompts | Olist HF dataset | New |
|
| 42 |
+
| Reward | 4 task-specific functions | Single Brier/log score | Simpler |
|
| 43 |
+
| Output format | JSON/text/SQL/notification | Single probability decimal | Simpler |
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
## 2. Decisions
|
| 48 |
+
|
| 49 |
+
### Decision 1: Use Brier Score reward (not log score)
|
| 50 |
+
|
| 51 |
+
**Paper uses:** Log score: `y·log(p) + (1−y)·log(1−p)`
|
| 52 |
+
**We use:** Brier score: `1 − (p − y)²`
|
| 53 |
+
|
| 54 |
+
**Rationale:**
|
| 55 |
+
- Both are proper scoring rules — uniquely minimized by the true probability
|
| 56 |
+
- Brier score is bounded [0, 1] — no risk of −∞ gradient explosions near p=0 or p=1
|
| 57 |
+
- Log score requires careful clamping (paper uses [0.001, 0.999]) and can still produce extreme gradients
|
| 58 |
+
- For a 0.5B model that may output poorly calibrated probabilities early in training, bounded rewards are safer
|
| 59 |
+
- V4.2 validated that bounded reward functions work well with our GRPO setup
|
| 60 |
+
|
| 61 |
+
**Consequence:** Slightly weaker calibration pressure at extremes (Brier penalizes p=0.99, y=0 as 0.98², log score penalizes as −4.6). Acceptable for a PoC where the goal is "does it improve at all."
|
| 62 |
+
|
| 63 |
+
**Alternative considered:** Use log score with aggressive clamping. Rejected because it adds a failure mode (gradient explosion) with minimal upside for a demo.
|
| 64 |
+
|
| 65 |
+
### Decision 2: Three prediction tasks (repurchase, review, delivery)
|
| 66 |
+
|
| 67 |
+
**Rationale:**
|
| 68 |
+
- Tests whether calibration transfers across different prediction domains
|
| 69 |
+
- Ensures the model learns general probability estimation, not just one base rate
|
| 70 |
+
- Each task has different characteristics:
|
| 71 |
+
- **Repurchase (Task A):** Low base rate (~15-20%), model must learn to be appropriately pessimistic
|
| 72 |
+
- **Review (Task B):** High base rate (~77%), model must learn to be appropriately optimistic
|
| 73 |
+
- **Delivery (Task C):** Medium-high base rate (~85%), model must distinguish nuanced factors
|
| 74 |
+
- Stratified split ensures eval has balanced representation
|
| 75 |
+
|
| 76 |
+
**Consequence:** Smaller per-task sample size in eval (~67 samples each). Sufficient for Brier/ECE computation but may not reach statistical significance on Wilcoxon per-task.
|
| 77 |
+
|
| 78 |
+
### Decision 3: Temporal split (not random split)
|
| 79 |
+
|
| 80 |
+
**Rationale:**
|
| 81 |
+
- Core requirement of Future-as-Label: no temporal leakage
|
| 82 |
+
- Train on orders before 2018-06-01, eval on 2018-06-01 to 2018-08-31
|
| 83 |
+
- This simulates real deployment: model trained on historical data, evaluated on "future" data it hasn't seen
|
| 84 |
+
- Random split would allow the model to memorize patterns from concurrent orders
|
| 85 |
+
|
| 86 |
+
**Consequence:** Distribution shift between train and eval (seasonality, seller changes). This is REALISTIC — calibration should be robust to temporal shift. If it isn't, that's a valid negative result.
|
| 87 |
+
|
| 88 |
+
### Decision 4: Short completion length (32-64 tokens)
|
| 89 |
+
|
| 90 |
+
**Rationale:**
|
| 91 |
+
- Output is just "PROB: 0.73" (≈10 tokens including reasoning)
|
| 92 |
+
- With MAX_COMPLETION_LENGTH=32, each generation is fast → step time drops dramatically
|
| 93 |
+
- V4.2 ran at 33s/step with 512-token completions; at 32 tokens expect ~5-8s/step
|
| 94 |
+
- 800 steps × 8s = ~1.8 hours (vs. 22h for V4.2)
|
| 95 |
+
- No thinking/reasoning required — just a number
|
| 96 |
+
|
| 97 |
+
**Consequence:** Model can't "show its work." It outputs only the probability. This is intentional — the paper shows that even without chain-of-thought, proper scoring rule rewards improve calibration. We're testing the minimal case.
|
| 98 |
+
|
| 99 |
+
**Risk:** If the model needs reasoning tokens to produce meaningful probabilities, 32 tokens won't be enough. Mitigation: if parse rate is <50% at 32 tokens, increase to 64 or 128.
|
| 100 |
+
|
| 101 |
+
### Decision 5: NUM_GENERATIONS=16 (not paper's K=4)
|
| 102 |
+
|
| 103 |
+
**Rationale:**
|
| 104 |
+
- Paper uses K=4 trajectories per event on 32B model
|
| 105 |
+
- Our 0.5B model + short completions has massive VRAM headroom
|
| 106 |
+
- V4.2 validated G=16 on this exact hardware and model
|
| 107 |
+
- More rollouts = more reward variance per prompt = stronger GRPO signal
|
| 108 |
+
- At 32-token completions, 16 rollouts use negligible memory
|
| 109 |
+
|
| 110 |
+
**Consequence:** 4× more samples per prompt than the paper. May accelerate learning or may waste compute if all 16 produce the same probability (zero variance). Monitored via `frac_reward_zero_std`.
|
| 111 |
+
|
| 112 |
+
### Decision 6: Add format bonus (+0.1) for parseable output
|
| 113 |
+
|
| 114 |
+
**Rationale:**
|
| 115 |
+
- The #1 risk with a 0.5B model is it never produces a parseable probability
|
| 116 |
+
- If parse_rate = 0%, reward is always 0.0, GRPO has no signal
|
| 117 |
+
- Adding `+0.1` bonus when ANY parseable probability is found gives a baseline gradient toward format compliance
|
| 118 |
+
- This is the same "staged reward" approach from V4.2 (format reward converges first, then task reward improves)
|
| 119 |
+
- V4.2's extraction task showed this works: model learned JSON format first, then field values
|
| 120 |
+
|
| 121 |
+
**Consequence:** The reward is no longer a pure proper scoring rule. It becomes `0.1*(parseable) + 0.9*(1 - (p-y)²)`. The format bonus dominates early in training (when most outputs are unparseable), then Brier dominates once format is learned. The calibration incentive is preserved because the Brier component still constitutes 90% of the achievable reward.
|
| 122 |
+
|
| 123 |
+
### Decision 7: Use the HF pre-processed Olist dataset
|
| 124 |
+
|
| 125 |
+
**Source:** `miminmoons/olist-ecommerce-for-delivery-and-review-prediction` (~100K rows)
|
| 126 |
+
|
| 127 |
+
**Rationale:**
|
| 128 |
+
- Pre-joined columns: `delivery_delay_hours`, `time_to_ship_hours`, `purchase_count`, `avg_review_score` — saves data engineering
|
| 129 |
+
- Has `customer_unique_id` for repurchase computation
|
| 130 |
+
- Has `order_purchase_timestamp` for temporal split
|
| 131 |
+
- Has `review_score` for binary label
|
| 132 |
+
|
| 133 |
+
**Limitation:** No raw `order_estimated_delivery_date` column. `delivery_delay_hours` is pre-computed (negative = early). For Task C (delivery_ontime), the binary label becomes: `int(delivery_delay_hours <= 0)`.
|
| 134 |
+
|
| 135 |
+
**Consequence:** Task C context cannot include the estimated delivery date explicitly — instead we include the order metadata that predicts delivery success (weight, dimensions [not available], seller state, customer state, shipping time). This is actually a harder prediction task (no "cheat" feature), which makes it more interesting.
|
| 136 |
+
|
| 137 |
+
### Decision 8: LR=5e-6 for first run, with note to test 1e-6
|
| 138 |
+
|
| 139 |
+
**Rationale:**
|
| 140 |
+
- V4.2 validated 5e-6 on this exact model and framework
|
| 141 |
+
- Paper doesn't specify LR (uses TRL defaults, likely 1e-6)
|
| 142 |
+
- Higher LR extracts signal faster from small datasets (V4.2 lesson)
|
| 143 |
+
- 800 steps is modest — want to see signal quickly
|
| 144 |
+
|
| 145 |
+
**Risk:** V4.2 showed entropy collapse at ~1.5 epochs with 5e-6. With ~1500 training prompts and batch_size=2, one epoch = 750 steps. At 800 steps we're at ~1.07 epochs — right at the edge. May need to reduce to 600 steps or use 1e-6 with more steps.
|
| 146 |
+
|
| 147 |
+
---
|
| 148 |
+
|
| 149 |
+
## 3. Consequences: Expected Results
|
| 150 |
+
|
| 151 |
+
### Optimistic scenario (model learns calibration)
|
| 152 |
+
|
| 153 |
+
| Metric | Base | Tuned | Improvement |
|
| 154 |
+
|---|---|---|---|
|
| 155 |
+
| Brier score | ~0.20-0.25 | ~0.18-0.22 | −10-15% |
|
| 156 |
+
| ECE | ~0.15-0.25 | ~0.10-0.15 | −30-40% |
|
| 157 |
+
| Parse rate | ~30-60% | ~85-95% | Format learned |
|
| 158 |
+
| Wilcoxon p | — | <0.05 | Significant |
|
| 159 |
+
|
| 160 |
+
**Why optimistic is plausible:**
|
| 161 |
+
- The paper shows even 40 GRPO steps improve calibration on 32B
|
| 162 |
+
- Our V4.2 showed GRPO improves format-following on 0.5B (extraction +29.5%)
|
| 163 |
+
- Probability estimation is simpler than JSON schema compliance
|
| 164 |
+
- Dense reward signal (every sample gets meaningful reward) vs. sparse in V4.2
|
| 165 |
+
|
| 166 |
+
### Realistic scenario (partial learning)
|
| 167 |
+
|
| 168 |
+
| Metric | Base | Tuned | Notes |
|
| 169 |
+
|---|---|---|---|
|
| 170 |
+
| Brier score | ~0.22 | ~0.20 | Modest improvement |
|
| 171 |
+
| ECE | ~0.20 | ~0.15 | Some calibration gain |
|
| 172 |
+
| Parse rate | ~40% | ~80% | Format learned, calibration partial |
|
| 173 |
+
| Wilcoxon p | — | ~0.05-0.10 | Borderline significance |
|
| 174 |
+
|
| 175 |
+
**Most likely outcome:** Model first learns to output parseable probabilities (format), then slowly moves toward base rates. May not achieve fine-grained calibration (distinguishing 0.7 from 0.8) but should distinguish "likely" from "unlikely" events.
|
| 176 |
+
|
| 177 |
+
### Pessimistic scenario (model fails)
|
| 178 |
+
|
| 179 |
+
| Metric | Base | Tuned | Notes |
|
| 180 |
+
|---|---|---|---|
|
| 181 |
+
| Brier score | ~0.22 | ~0.24 | Worse (overconfident) |
|
| 182 |
+
| ECE | ~0.20 | ~0.25 | Degraded |
|
| 183 |
+
| Parse rate | <30% | <50% | Never learns format |
|
| 184 |
+
|
| 185 |
+
**When this happens:**
|
| 186 |
+
- Parse rate stays <50% despite format bonus → increase MAX_COMPLETION_LENGTH to 128 or add few-shot examples
|
| 187 |
+
- Model outputs 0.50 for everything → degenerate solution (safe under Brier), no calibration
|
| 188 |
+
- All rollouts produce same probability → `frac_reward_zero_std=1.0`, zero GRPO signal
|
| 189 |
+
|
| 190 |
+
### What we learn regardless of outcome
|
| 191 |
+
|
| 192 |
+
| Outcome | Scientific conclusion |
|
| 193 |
+
|---|---|
|
| 194 |
+
| Calibration improves | GRPO + proper scoring rule works at 0.5B scale |
|
| 195 |
+
| Format learned but no calibration | 0.5B can't reason about probabilities, but can format-follow |
|
| 196 |
+
| Nothing works | 0.5B is below minimum viable scale for probabilistic reasoning with GRPO |
|
| 197 |
+
| Task A improves but B/C don't | Calibration learning depends on base rate / task structure |
|
| 198 |
+
|
| 199 |
+
---
|
| 200 |
+
|
| 201 |
+
## 4. Implementation Plan
|
| 202 |
+
|
| 203 |
+
### Phase 1: Data Pipeline (`olist_fal_dataset.py`)
|
| 204 |
+
|
| 205 |
+
```python
|
| 206 |
+
# Pseudocode — full implementation needed
|
| 207 |
+
from datasets import load_dataset
|
| 208 |
+
import pandas as pd
|
| 209 |
+
from datetime import timedelta
|
| 210 |
+
|
| 211 |
+
TRAIN_CUTOFF = "2018-06-01"
|
| 212 |
+
EVAL_END = "2018-08-31"
|
| 213 |
+
|
| 214 |
+
ds = load_dataset("miminmoons/olist-ecommerce-for-delivery-and-review-prediction", split="train")
|
| 215 |
+
df = ds.to_pandas()
|
| 216 |
+
df["order_purchase_timestamp"] = pd.to_datetime(df["order_purchase_timestamp"])
|
| 217 |
+
|
| 218 |
+
# ── Task A: Repurchase within 90 days ────────────────────────────────────
|
| 219 |
+
# For each order, check if same customer_unique_id has another order within 90 days
|
| 220 |
+
# Context: customer history up to this order (categories, values, scores)
|
| 221 |
+
# Label: 1 if repurchase within 90 days, 0 otherwise
|
| 222 |
+
|
| 223 |
+
# ── Task B: Positive review (score ≥ 4) ──────────────────────────────────
|
| 224 |
+
# Context: order metadata at fulfillment (category, freight, delivery delta, seller)
|
| 225 |
+
# Label: int(review_score >= 4)
|
| 226 |
+
# NOTE: Do NOT include review text or score in context
|
| 227 |
+
|
| 228 |
+
# ── Task C: On-time delivery ─────────────────────────────────────────────
|
| 229 |
+
# Context: order-placed metadata (seller/customer state, product info, shipping time)
|
| 230 |
+
# Label: int(delivery_delay_hours <= 0) # negative = early
|
| 231 |
+
# NOTE: Do NOT include delivery_delay_hours in context (that's the answer)
|
| 232 |
+
|
| 233 |
+
# ── Temporal split ────────────────────────────────────────────────────────
|
| 234 |
+
train_df = df[df["order_purchase_timestamp"] < TRAIN_CUTOFF]
|
| 235 |
+
eval_df = df[(df["order_purchase_timestamp"] >= TRAIN_CUTOFF) &
|
| 236 |
+
(df["order_purchase_timestamp"] <= EVAL_END)]
|
| 237 |
+
|
| 238 |
+
# ── Output: ~500 per task in train, ~67 per task in eval ─────────────────
|
| 239 |
+
# Save as JSONL with schema: {"task", "prompt", "outcome", "split"}
|
| 240 |
+
```
|
| 241 |
+
|
| 242 |
+
**Key implementation details:**
|
| 243 |
+
|
| 244 |
+
1. **Task A repurchase computation:** Group by `customer_unique_id`, sort by timestamp, for each order check if next order from same customer is within 90 days. Orders with no subsequent order in the dataset get label 0 (conservative — some may be censored).
|
| 245 |
+
|
| 246 |
+
2. **Context construction:** Each task gets a natural language paragraph describing the relevant features. NO inclusion of the target variable or any post-outcome information.
|
| 247 |
+
|
| 248 |
+
3. **Temporal leakage prevention:**
|
| 249 |
+
- Task A: Only include customer history BEFORE the current order
|
| 250 |
+
- Task B: Include delivery delta (occurs before review) but NOT the review score
|
| 251 |
+
- Task C: Include order-placed info only (NOT delivery outcome or time_to_ship)
|
| 252 |
+
|
| 253 |
+
4. **Sampling strategy:** Stratify by task AND outcome to ensure balanced base rates in eval. Training can use natural distribution.
|
| 254 |
+
|
| 255 |
+
### Phase 2: Training Notebook (`fal_grpo_training.ipynb`)
|
| 256 |
+
|
| 257 |
+
Fork from V4.2 with these minimal changes:
|
| 258 |
+
|
| 259 |
+
```python
|
| 260 |
+
# ── Reward function ──────────────────────────────────────────────────────
|
| 261 |
+
def reward_fal(completions, outcome, **kwargs):
|
| 262 |
+
"""
|
| 263 |
+
Brier score reward with format bonus.
|
| 264 |
+
Range: [0.0, 1.0]. Higher = better calibration.
|
| 265 |
+
"""
|
| 266 |
+
rewards = []
|
| 267 |
+
for completion, y in zip(completions, outcome):
|
| 268 |
+
if isinstance(completion, list):
|
| 269 |
+
text = completion[-1]["content"] if completion else ""
|
| 270 |
+
else:
|
| 271 |
+
text = str(completion)
|
| 272 |
+
|
| 273 |
+
# Parse probability
|
| 274 |
+
match = re.search(r'PROB:\s*([01]?\.\d+|[01])', text)
|
| 275 |
+
if match is None:
|
| 276 |
+
# Fallback: last decimal in [0,1] range
|
| 277 |
+
matches = re.findall(r'\b(0\.\d{1,3}|1\.00?|0)\b', text)
|
| 278 |
+
p_raw = float(matches[-1]) if matches else None
|
| 279 |
+
else:
|
| 280 |
+
p_raw = float(match.group(1))
|
| 281 |
+
|
| 282 |
+
if p_raw is None:
|
| 283 |
+
rewards.append(0.0) # No parse → no reward
|
| 284 |
+
else:
|
| 285 |
+
p = max(0.001, min(0.999, p_raw))
|
| 286 |
+
y_val = float(y)
|
| 287 |
+
brier = 1.0 - (p - y_val) ** 2 # Range [0, 1]
|
| 288 |
+
format_bonus = 0.1 # Parseable output bonus
|
| 289 |
+
rewards.append(min(1.0, format_bonus + 0.9 * brier))
|
| 290 |
+
|
| 291 |
+
return rewards
|
| 292 |
+
|
| 293 |
+
# ── Config changes from V4.2 ─────────────────────────────────────────────
|
| 294 |
+
MAX_COMPLETION_LENGTH = 32 # "PROB: 0.73" = ~10 tokens
|
| 295 |
+
MAX_STEPS = 800
|
| 296 |
+
EVAL_STEPS = 50
|
| 297 |
+
EARLY_STOPPING_PATIENCE = 10
|
| 298 |
+
NUM_GENERATIONS = 16
|
| 299 |
+
LEARNING_RATE = 5e-6
|
| 300 |
+
TEMPERATURE = 1.0
|
| 301 |
+
BETA = 0.0
|
| 302 |
+
SCALE_REWARDS = False
|
| 303 |
+
|
| 304 |
+
# ── System prompt ────────────────────────────────────────────────────────
|
| 305 |
+
SYSTEM_FAL = (
|
| 306 |
+
"You are a Brazilian e-commerce analyst. "
|
| 307 |
+
"Given order information, estimate the probability of the stated outcome. "
|
| 308 |
+
"Answer with ONLY: PROB: followed by a number between 0.00 and 1.00. "
|
| 309 |
+
"Example: PROB: 0.73"
|
| 310 |
+
)
|
| 311 |
+
|
| 312 |
+
# ── Dataset loading ──────────────────────────────────────────────────────
|
| 313 |
+
# Load fal_train.jsonl, format as {"prompt": [...messages...], "outcome": 0/1}
|
| 314 |
+
```
|
| 315 |
+
|
| 316 |
+
### Phase 3: Evaluation (`evaluate_fal.py`)
|
| 317 |
+
|
| 318 |
+
```python
|
| 319 |
+
# 1. Generate predictions from both base and tuned models
|
| 320 |
+
# 2. Parse PROB values (fallback to 0.5 if unparseable)
|
| 321 |
+
# 3. Compute per-task and overall metrics:
|
| 322 |
+
|
| 323 |
+
def compute_metrics(predictions, outcomes):
|
| 324 |
+
"""
|
| 325 |
+
predictions: list of floats [0, 1]
|
| 326 |
+
outcomes: list of ints {0, 1}
|
| 327 |
+
"""
|
| 328 |
+
import numpy as np
|
| 329 |
+
|
| 330 |
+
preds = np.array(predictions)
|
| 331 |
+
outs = np.array(outcomes, dtype=float)
|
| 332 |
+
|
| 333 |
+
# Brier score (lower = better)
|
| 334 |
+
brier = np.mean((preds - outs) ** 2)
|
| 335 |
+
|
| 336 |
+
# ECE with 10 bins
|
| 337 |
+
n_bins = 10
|
| 338 |
+
bin_boundaries = np.linspace(0, 1, n_bins + 1)
|
| 339 |
+
ece = 0.0
|
| 340 |
+
for i in range(n_bins):
|
| 341 |
+
mask = (preds >= bin_boundaries[i]) & (preds < bin_boundaries[i+1])
|
| 342 |
+
if mask.sum() > 0:
|
| 343 |
+
bin_acc = outs[mask].mean()
|
| 344 |
+
bin_conf = preds[mask].mean()
|
| 345 |
+
ece += mask.sum() * abs(bin_acc - bin_conf)
|
| 346 |
+
ece /= len(preds)
|
| 347 |
+
|
| 348 |
+
# Base rate Brier (always predict marginal probability)
|
| 349 |
+
base_rate = outs.mean()
|
| 350 |
+
brier_baseline = np.mean((base_rate - outs) ** 2)
|
| 351 |
+
|
| 352 |
+
return {
|
| 353 |
+
"brier": float(brier),
|
| 354 |
+
"ece": float(ece),
|
| 355 |
+
"brier_baseline": float(brier_baseline),
|
| 356 |
+
"base_rate": float(base_rate),
|
| 357 |
+
"brier_skill": float(1 - brier / brier_baseline), # >0 means better than baseline
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
# 4. Plot calibration curve
|
| 361 |
+
# 5. Wilcoxon signed-rank test on per-sample Brier scores
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
### Phase 4: Deliverables
|
| 365 |
+
|
| 366 |
+
```
|
| 367 |
+
fal_demo/
|
| 368 |
+
├── olist_fal_dataset.py # Data pipeline script
|
| 369 |
+
├── fal_train.jsonl # ~1500 training samples (500/task)
|
| 370 |
+
├── fal_eval.jsonl # ~200 eval samples (67/task, stratified)
|
| 371 |
+
├── fal_grpo_training.ipynb # Training notebook
|
| 372 |
+
├── evaluate_fal.py # Evaluation + plotting
|
| 373 |
+
├── calibration_curve.png # Visual result
|
| 374 |
+
└── fal_results.json # All metrics + Wilcoxon p-value
|
| 375 |
+
```
|
| 376 |
+
|
| 377 |
+
---
|
| 378 |
+
|
| 379 |
+
## 5. Risk Mitigation
|
| 380 |
+
|
| 381 |
+
| Risk | Probability | Impact | Mitigation |
|
| 382 |
+
|---|---|---|---|
|
| 383 |
+
| Parse rate < 50% | Medium | Training fails (no signal) | Add few-shot examples to system prompt; increase MAX_COMPLETION_LENGTH to 64-128 |
|
| 384 |
+
| All rollouts same probability | Medium | GRPO zero-signal | Add noise to prompt (randomize feature order); check `frac_reward_zero_std` |
|
| 385 |
+
| Model always outputs 0.5 | Low-Medium | Degenerate equilibrium | Log score would break this (infinite penalty for 0.5 when y=0/1), but Brier doesn't. Accept partial result. |
|
| 386 |
+
| Entropy collapse at step 600+ | Medium | Same as V4.2 | Early stopping patience=10 (500 steps). V4.2 peaked at 1.5 epochs = ~750 steps on similar-sized data. |
|
| 387 |
+
| Data leakage in Task A | Low | Inflated results | Rigorous temporal check: only count repurchases AFTER the order date, never include future context |
|
| 388 |
+
| Censoring in Task A (no repurchase observed but data ends) | Medium | Noisy labels | Exclude orders within 90 days of dataset end (2018-08-31 - 90d = 2018-06-02). Only label orders where full 90-day window is observable. |
|
| 389 |
+
|
| 390 |
+
---
|
| 391 |
+
|
| 392 |
+
## 6. Timeline Estimate
|
| 393 |
+
|
| 394 |
+
| Phase | Effort | Duration |
|
| 395 |
+
|---|---|---|
|
| 396 |
+
| Data pipeline (Task 1) | 2-3 hours | Half day |
|
| 397 |
+
| Training notebook setup (Task 3) | 1-2 hours | Half day |
|
| 398 |
+
| Training run (800 steps) | ~2 hours | Automatic |
|
| 399 |
+
| Evaluation + plotting (Task 4) | 1-2 hours | Half day |
|
| 400 |
+
| **Total** | **~6-8 hours active work** | **1-2 days** |
|
| 401 |
+
|
| 402 |
+
---
|
| 403 |
+
|
| 404 |
+
## 7. Success Criteria
|
| 405 |
+
|
| 406 |
+
**Minimum viable result (ship as demo):**
|
| 407 |
+
- Parse rate ≥ 70% on both base and tuned models
|
| 408 |
+
- Tuned Brier score < Base Brier score (any improvement)
|
| 409 |
+
- Calibration curve shows tuned model closer to diagonal than base
|
| 410 |
+
|
| 411 |
+
**Strong result (publishable finding):**
|
| 412 |
+
- Parse rate ≥ 90% on tuned model
|
| 413 |
+
- Brier improvement ≥ 10% (e.g., 0.22 → 0.20)
|
| 414 |
+
- ECE improvement ≥ 20%
|
| 415 |
+
- Wilcoxon p < 0.05
|
| 416 |
+
- Calibration curve visually demonstrates improved calibration across all bins
|
| 417 |
+
|
| 418 |
+
**Negative result (still informative):**
|
| 419 |
+
- If model never learns format: "0.5B is below minimum viable scale for probability estimation"
|
| 420 |
+
- If format learned but no calibration: "Format-following ≠ reasoning at this scale"
|
| 421 |
+
- If only one task improves: "Calibration learning is task-structure dependent"
|
| 422 |
+
|
| 423 |
+
---
|
| 424 |
+
|
| 425 |
+
## 8. References
|
| 426 |
+
|
| 427 |
+
| Paper | Key finding | Relevance |
|
| 428 |
+
|---|---|---|
|
| 429 |
+
| Future-as-Label (2601.06336) | GRPO + log score → 27% Brier improvement on 32B | The method we're implementing |
|
| 430 |
+
| Dr. GRPO (2503.20783) | β=0, scale_rewards=False for rule-based rewards | Config choices validated |
|
| 431 |
+
| V4.2 Final Report | GRPO works for format-learning at 0.5B, peaks at ~1.5 epochs | Expectations and step budget |
|
| 432 |
+
| Brier (1950) | Proper scoring rule: uniquely minimized by true probability | Theoretical guarantee |
|
| 433 |
+
| Gneiting & Raftery (2007) | Proper scoring rules and calibration | Why this reward works |
|
| 434 |
+
|
| 435 |
+
---
|
| 436 |
+
|
| 437 |
+
## 9. Relationship to V4.2
|
| 438 |
+
|
| 439 |
+
This is **not V4.3** — it's a separate experiment testing a different capability (calibration vs. format-following). It shares infrastructure but has a completely different:
|
| 440 |
+
- Dataset (Olist prediction tasks vs. commerce 4-task)
|
| 441 |
+
- Reward function (single Brier score vs. 4 task-specific rewards + GDPO + IWU)
|
| 442 |
+
- Output format (decimal probability vs. JSON/text/SQL/notification)
|
| 443 |
+
- Research question ("can GRPO teach calibration at 0.5B?" vs. "how much does GRPO improve format-following?")
|
| 444 |
+
|
| 445 |
+
If successful, this demonstrates that the Tucano2-Commerce GRPO methodology generalizes beyond format-following to probabilistic reasoning — a significantly stronger claim for the project's overall contribution.
|