wanderlust-chatbot / docs /evaluation_report.md
Kiriten892's picture
docs(chatbot): sync evaluation report to Wave 5, add 4 new intents to docs
2493935
|
Raw
History Blame Contribute Delete
14.4 kB
# Wanderlust Chatbot ML Evaluation Report
Generated: 2026-04-18 | **Last run: 2026-05-20 (Wave 5 — all 17 intents have test coverage)**
Evaluator: `app/training/evaluate.py` (read-only, no retraining)
> **Wave 5 update (2026-05-20):** Added test coverage for all 4 previously
> uncovered intents. Strategy: stratified 15% split from train (seed=42) for
> `show_map` (68 test), `destination_info` (63 test), `nearby_attractions` (26→15
> after data quality fix), `travel_tips` (25 test). Replaced all 176 `nearby_attractions`
> samples (were Reddit travel posts, not user queries) with 99 genuine nearby-POI
> queries (VI+EN). Removed ~120 noise samples from `destination_info` (Reddit posts,
> travel alerts, airport queries). Added 90 distinctive VI+EN `destination_info`
> samples. Results: Accuracy 90.97%, Macro F1 **89.02%** on all 17 intents. No
> intent below F1 0.77.
>
> **Wave 4 update (2026-05-19):** Added `destination_info`, `nearby_attractions`,
> `show_map`, `travel_tips` to classifier (4 new intents). Held-out test set had 0
> samples for these → Macro F1 dropped 0.94 → 0.76 due to F1=0.00 on 4 intents.
> Fixed in Wave 5.
>
> **Wave 2 update (2026-04-18):** Added 120 `compare_destinations` training
> samples (80 VI + 40 EN), expanded NER fixture 30 to 85 samples, fixed `origin`
> end-of-sentence pattern, fixed `hotel_type` accommodation FP in budget context,
> fixed `cuisine` vs `dietary` overlap, fixed `budget` "ruoi" pattern, fixed
> `budget_priority` broader patterns, fixed `transport` MRT/VI, fixed
> `time_preference` duration-context FP, fixed `_match_keyword_dict` consumed-text
> double-match.
>
> **Wave 3 update (2026-04-18):** Expanded NER fixture 85 → 102 samples (12
> `time_preference` + 5 `people` edge cases). Fixed `time_preference` F1 0.40 →
> 0.98 via: `buổi sáng sớm` early_morning keyword, VI compound scrubbing (`ăn sáng`,
> `chợ đêm`), EN compound scrubbing (`night market/tour/life`), 4 gold annotation
> gaps fixed. Added `a week / one/two/three weeks` EN duration patterns. Added VI
> people patterns (`vợ chồng`, `có N trẻ em`), EN patterns (`group of N`, `N of us`,
> `\bsolo\b`). Extended location FP blocklist (`temple, shrine, sunrise, batu, anh,
> market, beach`). NER micro F1: 0.912 (85 samples) → **0.920** (102 samples).
>
> **Wave 1 update (2026-04-18):** Intent classifier retrained with
> `FeatureUnion([char_ngram, word_tfidf])` + OOD rejector. NER regex
> fixes applied (hotel_type, dietary, travel_style, origin). Commit `ff6ef214`.
## Scope
Two models with objective metrics: **intent_classifier** and **NER**.
Eight other models (trip_optimizer, budget_optimizer, cuisine_recommender,
user_profiler, route_optimizer, response_generator, destination
recommender, LLM) are rule-based / template-based / untrained; their
sections document what *cannot* currently be measured and what inputs
the team must collect first.
---
## 1. Intent Classifier
### Status
**Held-out test set** -- `intent_test.json` produced by `split_dataset.py`
(stratified 15% split, seed=42). Model retrained with `FeatureUnion`
pipeline (word + char TF-IDF) + OOD rejector.
### Overall Metrics — Wave 5 run (2026-05-20, held-out n=1251, all 17 intents covered)
| Metric | Wave 2 | Wave 4 (13 intents) | Wave 5 (17 intents) | Note |
|---|---|---|---|---|
| Samples | 1,094 | 1,094 | **1,251** | +157 samples for 4 new intents |
| Accuracy | 0.9388 | 0.9305 | **0.9097** | slight drop expected (harder intents added) |
| Macro F1 | 0.9412 | 0.7635 (all) / 0.9400 (13) | **0.8902** | all 17 intents, no zero-support intents |
| Weighted F1 | — | — | **0.9117** | |
> All 17 intents now have test samples. No intent below F1 0.77.
### Per-Intent F1 — Wave 5 (sorted ascending)
| Intent | P | R | F1 | Support | Status |
|---|---:|---:|---:|---:|---|
| nearby_attractions | 0.75 | 0.80 | **0.77** | 15 | |
| destination_info | 0.79 | 0.78 | **0.78** | 49 | |
| greeting | 0.73 | 0.90 | **0.81** | 30 | |
| compare_destinations | 0.74 | 0.94 | **0.83** | 18 | |
| travel_tips | 0.95 | 0.76 | **0.84** | 25 | |
| show_map | 0.95 | 0.78 | **0.85** | 68 | |
| activity_suggest | 0.83 | 0.89 | **0.86** | 116 | |
| fallback | 0.91 | 0.87 | **0.89** | 120 | |
| find_hotel | 0.83 | 0.97 | **0.89** | 30 | |
| food_recommend | 0.93 | 0.87 | **0.90** | 120 | |
| weather_info | 0.94 | 0.94 | **0.94** | 120 | |
| plan_trip | 0.95 | 0.94 | **0.95** | 120 | |
| budget_advice | 0.94 | 0.96 | **0.95** | 120 | |
| transport_info | 0.97 | 0.93 | **0.95** | 120 | |
| find_flight | 0.94 | 0.99 | **0.96** | 120 | |
| visa_info | 0.94 | 1.00 | **0.97** | 30 | |
| thank | 0.97 | 1.00 | **0.98** | 30 | |
### Top Confusion Pairs (Wave 5)
| True intent | Predicted intent | Count |
|---|---|---:|
| food_recommend | activity_suggest | 8 |
| activity_suggest | destination_info | 4 |
| budget_advice | find_flight | 4 |
| destination_info | plan_trip | 3 |
| fallback | activity_suggest | 3 |
### Confidence Distribution (Wave 5)
- Predictions via `ml_model`: 1195 / 1251 = 95.5%
- Predictions via `keyword_fallback`: 50 / 1251 = 4.0%
- Predictions via `ood_rejector`: 6 / 1251 = 0.5%
### Issues Flagged for Follow-up
1. **`nearby_attractions` (n=15)** — small test set, F1 estimate unstable. Add more test samples for reliable measurement.
2. **`food_recommend` + `activity_suggest` confusion** — 8+4 bidirectional errors; semantically close intents; acceptable overlap.
3. **`destination_info` ↔ `plan_trip`** — inherent semantic overlap ("Tell me about X" vs "Plan a trip to X"); partially resolved by adding distinctive training samples.
---
## 2. Named Entity Recognition (NER)
### Status
**Real evaluation** against expanded hand-curated fixture
`app/data/datasets/ner_eval.json` (n=**102**: original 30 + 55 Wave 2 + 17 Wave 3
samples covering `time_preference` and `people` edge cases).
### Overall Metrics -- Wave 3 (102 samples)
| Metric | Wave 1 (30 samples) | Wave 2 (85 samples) | Wave 3 (102 samples) |
|---|---|---|---|
| Micro Precision | 0.894 | 0.899 | **0.914** |
| Micro Recall | 0.953 | 0.926 | **0.926** |
| Micro F1 | 0.923 | 0.912 | **0.920** |
| Samples with >=1 error | 14/30 (47%) | 38/85 (45%) | **48/102 (47%)** |
> **Note:** Wave 3 adds 17 samples specifically designed to expose `time_preference`
> and `people` edge cases. Overall micro F1 rose from 0.912 to 0.920 despite harder
> samples.
### Per-Entity-Type Metrics -- Wave 3
| Entity type | P | R | F1 | TP | FP | FN | vs Wave 2 F1 |
|---|---:|---:|---:|---:|---:|---:|---:|
| destination | 0.93 | 0.96 | **0.95** | 104 | 8 | 4 | same |
| duration | 0.96 | 0.93 | **0.95** | 69 | 3 | 5 | +0.01 (week patterns) |
| location | 0.95 | 0.97 | **0.96** | 121 | 7 | 4 | **+0.02** (blocklist expansion) |
| people | 0.81 | 0.85 | **0.83** | 22 | 5 | 4 | same |
| time_preference | 1.00 | 0.96 | **0.98** | 25 | 0 | 1 | **+0.58** (major fix) |
| transport | 0.94 | 0.94 | **0.94** | 15 | 1 | 1 | same |
| **Micro** | **0.914** | **0.926** | **0.920** | | | | **+0.008** |
### Wave 3 Fix Summary
1. **`time_preference` -- F1 0.40 → 0.98 (+0.58)**
- Added `buổi sáng sớm` → `early_morning` keyword (was only `sáng sớm`; `buổi
sáng` matched first as `morning` due to length-priority)
- VI compound scrubbing: `ăn sáng` (breakfast) removed before morning check;
`chợ đêm` (night market VI) removed before night check
- EN compound scrubbing: `night market/tour/life/show/club/bus` removed before
night check
- Fixed 4 gold annotation gaps in original samples (`buổi tối`, `hoàng hôn`,
`buổi chiều` cases where model was correct but gold was incomplete)
2. **`duration` -- added EN natural-language week patterns**
- `a week`, `one week`, `two weeks`, `three weeks``{days:7/14/21, nights:6/13/20}`
3. **`people` -- improved edge-case coverage**
- VI: `(\d+) vợ chồng``{adults:2}`, `(\d+) người.*có (\d+) trẻ em` → split
- EN: `group of N``{adults:N}`, `N of us``{adults:N}`, `\bsolo\b` (word
boundary fix to avoid matching inside words)
4. **`location` -- FP blocklist extended**
- Added: `temple`, `shrine`, `sunrise`, `batu`, `anh`, `market`, `beach`
- Location FP count: 24 (before fix) → 7 (after blocklist + dual annotation fix)
| Entity type | P | R | F1 | TP | FP | FN | vs Wave 1 F1 |
|---|---:|---:|---:|---:|---:|---:|---:|
| budget | 1.00 | 0.82 | **0.90** | 9 | 0 | 2 | up from 0.80 (ruoi fix) |
| budget_priority | 0.89 | 0.73 | **0.80** | 8 | 1 | 3 | up from 0.74 (broader patterns) |
| cuisine | 0.91 | 0.91 | **0.91** | 10 | 1 | 1 | up from 0.80 (dietary overlap fix) |
| dietary | 0.94 | 0.88 | **0.91** | 15 | 1 | 2 | up from 0.88 (consumed-text fix) |
| duration | 0.97 | 0.92 | **0.94** | 60 | 2 | 5 | same |
| location | 0.91 | 0.96 | **0.94** | 104 | 10 | 4 | same |
| activity_preference | 0.85 | 0.96 | **0.90** | 22 | 4 | 1 | slight drop (harder samples) |
| travel_style | 0.94 | 0.88 | **0.91** | 29 | 2 | 4 | up from 0.86 |
| destination | 0.89 | 0.96 | **0.92** | 87 | 11 | 4 | same |
| transport | 0.94 | 0.94 | **0.94** | 15 | 1 | 1 | up from 0.90 (MRT VI fix) |
| origin | 0.94 | 0.83 | **0.88** | 15 | 1 | 3 | **up from 0.73** |
| people | 0.82 | 0.86 | **0.84** | 18 | 4 | 3 | down (harder samples) |
| hotel_type | 0.78 | 1.00 | **0.88** | 18 | 5 | 0 | **up from 0.80** (accommodation budget ctx) |
| time_preference | 1.00 | 0.96 | **0.98** | 25 | 0 | 1 | **+0.58** (Wave 3 fix) |
### Error Categories (Wave 2)
1. **`origin` -- F1 0.88 (up from 0.73)**
End-of-sentence `tu X` pattern added; LOW_CONF gate blocks when
`tu` is present. 3 FNs remain from ambiguous `"tu X di Y"` where
both X and Y are plausible destinations.
2. **`hotel_type` -- F1 0.88 (up from 0.80)**
Accommodation FP suppressed when budget-allocation words (`tiet kiem`,
`chi nhieu`, etc.) present. EN `budget` FP suppressed when monetary
amount is present. 5 FPs remain from cases like `"cho o dep"` (nice
accommodation) where budget context is absent.
3. **`cuisine` vs `dietary` -- F1 0.91 (up from 0.80)**
`"di ung hai san"` no longer triggers `cuisine=seafood`; negation
phrase detection skips cuisine keywords already covered by dietary.
4. **`budget` -- F1 0.90 (up from 0.80)**
`"3 trieu ruoi"` (3.5 million VND) pattern added. 2 FNs from
compound expressions not yet covered.
5. **`budget_priority` -- F1 0.80 (up from 0.74)**
Patterns broadened: `tiet kiem tien an`, `chi nhieu hon cho`,
`uu tien an/accommodation` added. 3 FNs remain from unusual phrasing.
6. **`time_preference` -- F1 0.40 → 0.98 (Wave 3 fix)**
Wave 2: evaluator bug — `time_preference` was missing from `_extract_predicted()`
so TP=0 regardless of model output. Wave 3: fixed evaluator + compound scrubbing
+ gold annotation gaps corrected → F1 0.98 on 25 gold annotations.
7. **`dietary` -- F1 0.91 (up from 0.88)**
`_match_keyword_dict` now uses consumed-text approach: after matching
`thuan chay` to `vegan`, the substring `chay` is consumed and no
longer fires a spurious `vegetarian` FP.
8. **`transport` -- F1 0.94 (up from 0.90)**
`'mrt'` / `'gan mrt'` added to Vietnamese transport keywords.
### Coverage Summary
All 13 entity types exercised on 102 samples (Wave 3). Remaining weak points:
- `budget_priority` (F1=0.80): 3 FNs -- uncommon phrasing not covered by
regex patterns.
- `people` (F1=0.83): complex family + group-size texts expose edge cases.
---
## 3. Destination Recommender
**Not evaluable with current data.** No ground-truth user preference labels.
Needs synthetic preference queries (n=50) or production interaction logs.
---
## 4. LLM (SeaLLMs-v3-7B-Chat)
**LoRA adapters directory exists but is empty / untrained** (Qwen-trained adapters
incompatible with SeaLLMs — skipped). Production relies on SeaLLMs-v3-7B self-hosted
on HF Space GPU (T4) + RAG grounding for quality.
Gate for LoRA training: collect 400+ human-written travel QA pairs before starting.
---
## 5. Rule-Based Components
| Model | Current algorithm | Why not scored |
|---|---|---|
| trip_optimizer | Haversine + clustering | Deterministic given inputs |
| budget_optimizer | Static tier allocation | No ground truth |
| cuisine_recommender | Keyword filters | No labelled dataset |
| user_profiler | In-memory keyword bucketing | No persistence across sessions |
| route_optimizer | Haversine-only path | No ground truth |
| response_generator | 95% template | Not yet wired in evaluator |
---
## 6. Recommendations Ranked by ROI
| Priority | Change | Expected gain | Status |
|---|---|---|---|
| 1 | Produce held-out `intent_test.json` | Trusted metric | Done -- 1094 samples |
| 2 | Fix `hotel_type` NER extraction bug | F1: 0.40 to 0.88 | Done |
| 3 | OOD rejector for `fallback`/`greeting` | greeting P: 0.59 to 0.84 | Done |
| 4 | FeatureUnion word+char TF-IDF | food/activity confusion -22 | Done |
| 5 | Add `compare_destinations` training data | F1: 0.00 to 0.92 | Done -- 120 samples |
| 6 | Fix `origin` disambiguation | F1: 0.73 to 0.88 | Done |
| 7 | Expand `ner_eval.json` 30 to 85 samples | Tighter CIs | Done -- 55 harder samples |
| 8 | Fix cuisine/dietary overlap | cuisine F1: 0.80 to 0.91 | Done |
| 9 | Fix budget ruoi + budget_priority patterns | budget F1: 0.80 to 0.90 | Done |
| **10** | Expand `time_preference` gold fixture (10+ samples) | F1: 0.40 to ~0.80 | Low cost |
| **11** | Fix `people` edge cases (complex family + group) | F1: 0.84 to ~0.90 | ~2 hrs |
| **12** | Fine-tune PhoBERT-base on intent dataset | +3-5 F1 on low-F1 intents | Wave 5 Macro F1 = 0.8902 < 0.93 gate — evaluate |
| **13** | Collect 400+ human-written travel QA | Enables LLM LoRA training | chatbot-data-curator |
| **14** | Frontend interaction logging + recommender CF | Enables Precision@k | Frontend instrumentation |
---
## Artefacts
- `app/data/datasets/ner_eval.json` -- 102-sample labelled NER fixture (Wave 3)
- `app/data/datasets/intent_train.json` -- ~7,014 training samples, 17 intents (Wave 5)
- `app/data/datasets/intent_test.json` -- 1,251 held-out test samples, 17 intents (Wave 5)
- `scripts/sensitivity_analysis.py` -- Monte Carlo budget weight sensitivity (500 iterations)
- `notebooks/wanderlust_ml_evaluation.ipynb` -- full ML workflow notebook with academic citations
- `app/training/evaluate.py` -- reproducible evaluator