File size: 16,358 Bytes
7e6c03a | 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | # Multihop Temporal Reasoning Tasks β Detailed Walkthrough
## Architecture Overview
All 8 new task generators follow a shared architecture built on `MultihopBaseGenerator`, which provides:
- **Scene building**: Constructing multi-event audio sequences with controlled order, durations, volumes, and silences
- **Dataset generation**: Sample-loop orchestration with balanced question type distribution
- **CSV output**: Standardized MCQ, open-text, and metadata CSV writing
```mermaid
graph TD
A[MultihopBaseGenerator] --> B[ConditionalCountTaskGenerator]
A --> C[ConditionalDurationTaskGenerator]
A --> D[BetweenEventsTaskGenerator]
A --> E[EventDensityTaskGenerator]
A --> F[DurationGapTaskGenerator]
A --> G[TemporalArithmeticTaskGenerator]
A --> H[TemporalLoudnessTaskGenerator]
A --> I[MultiHopTaskGenerator]
J[ESC50Dataset] --> A
K[PreprocessedESC50Dataset] --> C
K --> F
K --> G
K --> I
L[AudioProcessor] --> A
M[QuestionGenerator] --> A
```
### Key Design Decision: Why a Shared Base?
The existing 7 tasks (count, duration, order, etc.) are standalone classes with no shared base. This worked because each task has fundamentally different audio construction logic (e.g., overlap uses `overlay()`, during_contains uses temporal containment, silence_gap uses controlled gaps).
The **multihop tasks are different** β they all build the same kind of audio scene (sequential events with gaps and volume variation) but ask different questions over that scene. The complexity is in the **question generation and answer computation**, not the audio construction. So a shared base class makes sense here.
---
## Base Class: `MultihopBaseGenerator`
**File:** [multihop_base.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/multihop_base.py)
### What it provides
| Method | Purpose |
|--------|---------|
| `__init__` | Loads config, initializes dataset/processor/question generator, creates output dirs |
| `build_sequential_scene()` | Builds an audio scene with N events, controlled gaps, optional per-event volume |
| `generate_dataset()` | Orchestrates sample generation loop with balanced question types |
| `_save_mcq_csv()` | Writes MCQ CSV in standard pipeline format |
| `_save_open_text_csv()` | Writes open-text CSV in standard pipeline format |
| `_save_metadata_csv()` | Writes metadata CSV |
### Scene Building Logic
```
βββββββββββ gap_0 βββββββββββ gap_1 βββββββββββ gap_2 βββββββββββ
β Event 0 βββββββββΊβ Event 1 βββββββββΊβ Event 2 βββββββββΊβ Event 3 β
β cat_A β β cat_B β β cat_A β β cat_C β
β vol: 0dBβ β vol:-6dB β β vol:+3dB β β vol:-2dB β
βββββββββββ βββββββββββ βββββββββββ βββββββββββ
```
1. Sample `n_events` unique categories from ESC-50
2. Load and extend each source clip to fill its time slot via `concatenate_to_target_duration()`
3. Apply per-event volume adjustments (if specified)
4. Generate controlled gap durations using `generate_controlled_gap_durations()` (ensures the longest gap is β₯ `gap_multiplier Γ shortest gap`)
5. Concatenate: `event + silence + event + silence + ...`
6. Return per-event metadata (start_ms, end_ms, duration, volume, category)
---
## Task 1: `ConditionalCountTaskGenerator`
**File:** [task_conditional_count.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_conditional_count.py)
**Family:** Multihop Singular
**Reasoning chain:** Temporal condition β Count
### What it does
Counts sound events **after applying a temporal filter**. Unlike the simple COUNT task which asks "how many unique sounds?", this task asks "how many X sounds occur **after** Y?" β requiring the model to first identify when Y occurs, then count X events only in the filtered region.
### Audio Construction
Builds a scene with **4β8 events**, intentionally allowing **repeated categories** (unlike between_events which uses all unique). This is critical because counting repeated occurrences is the core question.
```python
# Example scene: categories = [dog, cat, dog, bird, cat, dog, bird]
# count_after(target=dog, anchor=cat[0]) β 3 (indices 2, 5 after first cat)
# count_before(target=bird, anchor=dog[2]) β 1 (index 3 before third dog)
```
### Question Types & Answer Computation
| Type | Logic |
|------|-------|
| `count_after` | Pick anchor index β count `target_sound` in `categories[anchor+1:]` |
| `count_before` | Pick anchor index β count `target_sound` in `categories[:anchor]` |
| `count_between` | Pick two anchor indices β count events in `categories[idx1+1:idx2]` |
| `count_during` | Pick anchor β count all other `target_sound` occurrences |
| `count_overlap` | Pick anchor β count adjacent events (neighbors at Β±1 index) |
### MCQ Generation
Uses `QuestionGenerator.generate_count_mcq()` β generates 4 integer options including the correct count, with distractors sampled from nearby integers.
---
## Task 2: `ConditionalDurationTaskGenerator`
**File:** [task_conditional_duration.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_conditional_duration.py)
**Family:** Multihop Singular
**Reasoning chain:** Temporal condition β Duration comparison
### What it does
Compares durations **only among events in a temporal subset**. "Which sound **after** Y lasts the longest?" requires: (1) identify Y's position, (2) consider only sounds after Y, (3) compare their durations.
### Audio Construction
Uses **`PreprocessedESC50Dataset`** (not plain ESC50Dataset) to get **effective durations** β the actual content duration after silence trimming. This ensures duration comparisons are meaningful (a 5s clip with 3s of trailing silence has effective duration 2s).
```python
# Each event loaded with known effective duration:
fname, fpath, eff_dur = self.dataset.sample_file_from_category_with_duration(cat)
```
### Question Types & Answer Computation
| Type | Logic |
|------|-------|
| `longest_after` | Filter events after anchor β `max(effective_durations)` |
| `shortest_after` | Filter events after anchor β `min(effective_durations)` |
| `longest_before` | Filter events before anchor β `max(effective_durations)` |
| `shortest_before` | Filter events before anchor β `min(effective_durations)` |
| `repeated_compare` | Find category appearing both before AND after anchor β compare their durations |
### MCQ Generation
Uses `QuestionGenerator.generate_category_mcq()` β the answer is a sound category name, with distractors drawn from other categories present in the audio.
---
## Task 3: `BetweenEventsTaskGenerator`
**File:** [task_between_events.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_between_events.py)
**Family:** Multihop Singular
**Reasoning chain:** Define temporal window β Identify/Count events inside
### What it does
Identifies or counts sounds occurring **inside the temporal window between two anchor events**. "Which sound occurs between X and Y?" requires: (1) find X, (2) find Y, (3) identify events strictly between them.
### Audio Construction
Uses **all unique categories** (no repeats) so that "which sound between X and Y?" has an unambiguous answer. Scene has **5β8 events**.
### Question Types & Answer Computation
| Type | Logic |
|------|-------|
| `identify_between` | Pick two anchors `idx1 < idx2` β randomly select one event from `categories[idx1+1:idx2]` as answer |
| `count_between` | Pick two anchors β `len(categories[idx1+1:idx2])` |
| `yes_no_between` | Pick target β check if target is in the between-window (50/50 true/false balance) |
### Key Constraint
Anchors are chosen with **at least 2 positions apart** (`idx2 β₯ idx1 + 2`) to guarantee at least 1 event between them. Samples with fewer than 3 events are rejected.
---
## Task 4: `EventDensityTaskGenerator`
**File:** [task_event_density.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_event_density.py)
**Family:** Multihop Singular
**Reasoning chain:** Segment audio into regions β Compare event counts
### What it does
Compares **event frequency across temporal regions**. "Which half of the audio contains more sound events?" requires counting events in each half and comparing.
### Audio Construction
Builds scenes with **6β10 events** (larger scenes to make density differences meaningful). **Allows repeated categories** so label-specific density questions are possible. Categories are shuffled randomly β the natural random distribution creates asymmetry.
### Question Types & Answer Computation
| Type | Logic |
|------|-------|
| `half_density` | Split at midpoint β compare `len(first_half)` vs `len(second_half)` β answer is "first half" / "second half" / "equal" |
| `before_after_density` | Pick anchor β compare count of events before vs after anchor |
| `label_density` | Pick anchor + target label β compare count of `target_sound` before vs after anchor |
### MCQ Generation
Custom option construction β options are textual ("first half", "second half", "equal", "cannot determine") rather than category names.
---
## Task 5: `DurationGapTaskGenerator`
**File:** [task_duration_gap.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_duration_gap.py)
**Family:** Multihop Inter-Task
**Reasoning chain:** Duration reasoning β Silence-gap reasoning
### What it does
**Crosses two task families** β compares sound event duration against silence gap duration. "Which is longer: the dog sound or the silence after the dog sound?" requires perceiving both the event's length and the gap's length, then comparing.
### Audio Construction
Uses `PreprocessedESC50Dataset` for effective durations. **Wider gap range** (500β4000ms) compared to other tasks, ensuring gaps are sometimes longer and sometimes shorter than events β making the comparison non-trivial.
Each event's metadata tracks both `effective_duration_ms` and `gap_before_ms` / `gap_after_ms`.
### Question Types & Answer Computation
| Type | Logic |
|------|-------|
| `event_vs_after_gap` | Pick event (not last) β compare `effective_duration_ms` vs `gap_after_ms` |
| `event_vs_before_gap` | Pick event (not first) β compare `effective_duration_ms` vs `gap_before_ms` |
| `gap_vs_event` | Pick a gap between events A,B β pick third event C β compare `gap_duration` vs `C.effective_duration_ms` |
---
## Task 6: `TemporalArithmeticTaskGenerator`
**File:** [task_temporal_arithmetic.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_temporal_arithmetic.py)
**Family:** Multihop Inter-Task
**Reasoning chain:** Duration + Count/Repetition + Silence reasoning
### What it does
Compares **aggregated** durations β total active time of one label vs another, or total sound time vs total silence time. Requires mentally summing up all occurrences of a repeated sound.
### Audio Construction
Uses `PreprocessedESC50Dataset`. **Allows repeated categories** (critical for aggregation). Computes two aggregate quantities:
- **`label_totals`**: Total effective duration per category (summed across all occurrences)
- **`total_silence_ms`**: Sum of all gap durations
### Question Types & Answer Computation
| Type | Logic |
|------|-------|
| `total_label_duration` | Pick two categories β compare `label_totals[sound1]` vs `label_totals[sound2]` |
| `label_vs_silence` | Pick one category β compare `label_totals[target]` vs `total_silence_ms` |
| `combined_duration` | Pick one category β compare `label_totals[target]` vs `max(gap_durations)` (longest single silence) |
---
## Task 7: `TemporalLoudnessTaskGenerator`
**File:** [task_temporal_loudness.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_temporal_loudness.py)
**Family:** Multihop Inter-Task
**Reasoning chain:** Temporal filter β Loudness comparison
### What it does
Combines **temporal filtering** with **loudness comparison**. "Which sound after Y is the loudest?" requires: (1) identify Y's position, (2) consider only sounds after Y, (3) compare their loudness levels.
### Audio Construction
Each event gets a **random volume adjustment** from `volume_range_db` (default: [-12, +6] dB). A minimum volume difference (`min_volume_diff_db`: 3dB) is enforced to ensure the loudest/softest distinction is perceivable.
Actual loudness is measured using **LUFS** (`get_lufs_loudness()`) for perceptually accurate loudness comparison.
### Question Types & Answer Computation
| Type | Logic |
|------|-------|
| `loudest_after` | Filter events after anchor β `max(loudness_lufs)` |
| `softest_after` | Filter events after anchor β `min(loudness_lufs)` |
| `loudest_before` | Filter events before anchor β `max(loudness_lufs)` |
| `softest_before` | Filter events before anchor β `min(loudness_lufs)` |
| `repeated_loudness` | Find category with multiple occurrences β which occurrence is loudest? Also detects trend (louder/softer over time) |
---
## Task 8: `MultiHopTaskGenerator`
**File:** [task_multi_hop.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_multi_hop.py)
**Family:** Multihop Inter-Task
**Reasoning chain:** Explicit 2β3 step chains across different properties
### What it does
The **most complex task** β requires explicit multi-step reasoning across different temporal/acoustic properties. "What sound occurs after the longest sound?" requires: (1) identify the longest sound (duration property), (2) find what comes after it (order property).
### Audio Construction
The **richest scene** β combines ALL controllable dimensions:
- **Duration variation** via PreprocessedESC50Dataset effective durations
- **Volume variation** via per-event dB adjustments ([-10, +6] dB range)
- **Silence variation** via controlled gaps (500β3000ms range, 2.5Γ multiplier)
- **Category repetition** for count-based sub-questions
### Question Types & Reasoning Chains
| Type | Step 1 | Step 2 | Step 3 |
|------|--------|--------|--------|
| `after_longest` | Find longest duration event | Return next event in sequence | β |
| `before_longest` | Find longest duration event | Return previous event | β |
| `after_shortest` | Find shortest duration event | Return next event | β |
| `before_loudest` | Find loudest event (LUFS) | Return previous event | β |
| `after_longest_gap` | Find longest silence gap | Return event after that gap | (or count remaining events) |
| `loudest_after_anchor` | Find anchor position | Filter events after anchor | Find loudest among them |
| `longest_before_anchor` | Find anchor position | Filter events before anchor | Find longest among them |
| `count_before_loudest` | Find loudest event | Filter events before it | Count target_sound occurrences |
| `count_after_longest` | Find longest event | Filter events after it | Count target_sound occurrences |
### Answer Computation Example
```python
# after_longest: "What sound occurs after the longest sound?"
pivot_idx = durations_ms.index(max(durations_ms)) # Step 1: find longest
target_idx = pivot_idx + 1 # Step 2: get next event
correct = categories[target_idx] # Answer: that category
```
---
## Summary: What Makes Each Task Unique
| Task | Audio Uniqueness | Question Uniqueness |
|------|-----------------|-------------------|
| **conditional_count** | Repeated categories | Temporal filter β integer count |
| **conditional_duration** | PreprocessedESC50 effective durations | Temporal filter β duration superlative |
| **between_events** | All unique categories, β₯3 events | Temporal window identification |
| **event_density** | 6β10 events, asymmetric distribution | Region-based count comparison |
| **duration_gap** | Wide gap range (500β4000ms) | Cross-property: event-dur vs silence-dur |
| **temporal_arithmetic** | Repeated categories + tracked totals | Aggregated duration arithmetic |
| **temporal_loudness** | Per-event volume variation + LUFS measurement | Temporal filter β loudness superlative |
| **multi_hop** | All dimensions varied simultaneously | 2β3 step reasoning chains |
|