File size: 3,504 Bytes
3e07043
187a7a3
 
 
 
3e07043
187a7a3
3e07043
 
 
187a7a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
title: DECLARE User Study
emoji: πŸŽ™οΈ
colorFrom: blue
colorTo: green
sdk: docker
app_port: 7860
pinned: false
---

# DECLARE User Study

Blind comparison of three ASR conditions (BASELINE / VANILLA-FT / DECLARE) on
real spoken FairSpeech-style commands. Single-file Flask app (`app.py`),
matching the flat repo layout used elsewhere (RailVaani), with the frontend in
`templates/` + `static/`.

Participants record themselves speaking a prompted command via the browser's
native microphone API; audio is routed blindly to one of three ASR conditions
(Latin-square rotation per session); a rule-based intent/entity extractor
summarises what the system understood; the participant judges
intent-correctness and (if applicable) entity-correctness separately. After
all trials, a validated 4-item UMUX usability questionnaire is shown. Model
identity is never revealed during the session.

## Folder structure

```
app.py                 -- everything: Flask routes, NLU, routing, logging, ASR
templates/index.html   -- page shell
static/style.css        -- styling
static/app.js            -- consent form, MediaRecorder, all fetch() calls
data/sample_commands.csv -- 38 stratified FairSpeech commands
checkpoints/            -- put your .nemo files here
requirements.txt
Dockerfile
README.md
```

**Important constraint:** session state lives in an in-memory Python dict in
`app.py` (`SESSIONS`). This assumes a **single worker process**. Do not run
behind multiple gunicorn/Flask workers without first moving session state to
a shared store (Redis, a database) β€” otherwise a participant's session can
"disappear" if a later request lands on a different worker.

## Before deploying with real models

The app ships in **MOCK mode** by default so the full flow β€” consent,
recording, routing, judgement, UMUX β€” can be tested without any model weights.
To run real inference:

1. Uncomment the `torch` / `nemo_toolkit[asr]` lines in `requirements.txt`.
2. Add your `VANILLA_FT` and `DECLARE` `.nemo` checkpoints under `checkpoints/`.
3. Set Space secrets/variables if you don't want to hardcode paths:
   - `BASELINE_MODEL_ID` (default: `stt_en_conformer_ctc_small_ls`)
   - `VANILLA_FT_CKPT`
   - `DECLARE_CKPT`

## Audio pipeline

Browser records via `MediaRecorder` (typically webm/opus) β†’ `app.py` saves the
raw upload β†’ converts to 16kHz mono WAV via `ffmpeg` β†’ runs ASR β†’ **deletes
both the raw upload and the WAV file**. The WAV deletion happens inside
`transcribe()`'s own `finally` block (so it happens even if inference fails);
the raw upload is deleted separately right after, in the route handler.

## Results

- `results/trial_log.csv` β€” per-trial ASR output + intent/entity correctness
- `results/participants.csv` β€” demographics + consent (keyed by session ID)
- `results/umux_responses.csv` β€” usability questionnaire responses + score

**HF Spaces' local disk is ephemeral** β€” sync these to a private HF Dataset
repo regularly if running more than a quick pilot.

## Analysis

Join `trial_log.csv` back to its (unblinded) `condition` column and run:

- Friedman test across the three conditions (per-participant correctness rate,
  computed separately for `intent_correct` and `entity_correct` β€” filter or
  stratify `entity_correct == "not_applicable"` rows out of that specific test)
- Wilcoxon signed-rank, pairwise (DECLARE vs. VANILLA-FT is the key comparison)
- Disaggregate both by the demographic columns in `participants.csv` for the
  fairness question