Imported from misumisumi/AudioLLM-listen-audio-test (
AGENTS.md). Install upstream withnpx skills add misumisumi/AudioLLM-listen-audio-test. Copyright stays with the author.
AGENTS.md
Project
LISTEN ベンチマーク評価プロジェクト。
論文「Do Audio LLMs Really LISTEN, or Just Transcribe? Measuring Lexical vs.
Acoustic Emotion Cues Reliance」(Chen et al. 2025, arXiv:2510.10444) の
LISTEN ベンチマークを、vLLM で動く音声対応 LLM で再現評価する。
特定モデルに限定せず、モデルは configs/model/<name>.yaml で切り替える
(既定: Gemma-4 E2B / E4B)。
- 公式コード: https://github.com/DeliJingyiC/LISTEN
- データセット: https://huggingface.co/datasets/VibeCheck1/LISTEN_full
- プロジェクトサイト: https://delijingyic.github.io/LISTEN-website/
Goal
LISTEN の全10サブ実験を vLLM 対応の音声 LLM で実行し、論文 Table 2 と比較可能な 指標・生データを出力する。モデル非依存の構造にし、任意の音声 LLM を追加できるようにする。
Confirmed Decisions
| 項目 | 決定 |
|---|---|
| モデル | configs/model/*.yaml で指定(既定: google/gemma-4-E2B-it / -E4B-it、BF16) |
| 推論エンジン | vLLM オフライン vllm.LLM(greedy デコード) |
| GPU | RTX 3090(Ampere SM86 / 24GB)※NVFP4 は Blackwell 専用のため BF16 を使用 |
| 設定管理 | Hydra(モデル毎に設定ファイルを分離) |
| 実験範囲 | 全10サブ実験を実装、CLI/Hydra override で個別選択可 |
| 出力 | results/<model>/<date>/ に summary.csv と samples/*.csv、summary_.csv |
NVFP4 についての注意
unsloth の NVFP4 量子化モデルは Blackwell (SM100, compute capability 10.0) 専用。
RTX 3090 は Ampere (SM86, CC 8.6) のため動作しない。vLLM の圧縮方式表でも
NVFP4 → GPU: Blackwell (SM100), 最小 compute capability 10.0 と明記されている。
Ampere では W4A16 (AWQ/GPTQ) または BF16 が推奨。本プロジェクトは BF16 を採用。
Directory Structure
.
├── AGENTS.md
├── pyproject.toml # vllm[audio], datasets, scikit-learn, numpy, pandas,
│ # hydra-core, omegaconf, pyyaml
├── README.md
├── configs/
│ ├── config.yaml # defaults: model/eval/runtime + experiments, resume
│ ├── model/
│ │ ├── gemma4_e2b.yaml # name, hf_id, backend, max_model_len, gpu_mem_util,
│ │ │ # limit_mm_per_prompt, sampling, system_prompt
│ │ └── gemma4_e4b.yaml # 既定モデル。他モデルは同形式で追加
│ ├── eval/
│ │ └── listen.yaml # seed, checkpoint 間隔, 出力先
│ └── runtime/
│ └── vllm_local.yaml # vLLM/GPU 共通設定
├── src/
│ ├── bin/
│ │ └── run_eval.py # Hydra エントリポイント
│ ├── utils/
│ │ ├── data.py # LISTEN_full ロード + experiment_type/dataset_source フィルタ
│ │ ├── prompts.py # audio / text / audio_and_text プロンプト
│ │ ├── randomization.py # choice シャッフル(seed=42)・exp3 追加感情・正解 letter
│ │ ├── letter.py # letter_instruction / extract_letter(A..J)
│ │ ├── metrics.py # WA/UAR/Macro-F1/Micro-F1/chance baseline(公式移植)
│ │ ├── checkpoint.py # 10 サンプル毎 resume
│ │ └── export.py # summary.csv / summary_<dataset>.csv / samples/*.csv
│ └── models/
│ ├── base.py # BaseAudioModel ABC(infer(prompt, audio))
│ ├── registry.py # cfg.model.backend → アダプタ解決(拡張点)
│ └── vllm_audio.py # vLLM オフライン実装(モデル非依存)
└── results/<model>/<date>/
├── summary.csv
├── summary_<dataset>.csv # データセット毎サマリ(フラット配置)
├── config.yaml # Hydra 設定スナップショット
└── samples/<dataset>.csv # データセット毎のサンプル単位データ
LISTEN Evaluation Protocol (公式準拠)
実験マッピング
| 実験 | データ (experiment_type) | 入力モード |
|---|---|---|
1_text |
1 |
text |
1_audio |
1 |
audio |
1_audio_and_text |
1 |
audio_and_text |
2A |
2A |
text |
2B |
2B |
audio |
2C |
2B のデータ |
audio_and_text |
3A |
3A |
text |
3B |
3B |
audio |
3C |
3B のデータ |
audio_and_text |
4 |
4 |
audio |
正解ラベル規則
- text mode かつ exp ∈ {2A, 3A} →
explicit_emotion - それ以外 →
answer
選択肢のランダム化
- exp ∈ {3A, 3B, 3C} は感情 8 個
(
neutral, sadness, surprise, happiness, fear, anger, excitement, frustration) を追加し重複除去(最大 10 択) random.seed(42)でシャッフル- 正解の新しい位置から期待 letter を算出
プロンプト(公式 3 テンプレート)
- audio:
Listen to the audio and classify the emotion. - text:
Read the transcription and classify the emotion.(本文にTranscription: ...) - both:
Listen to the audio and read the transcription, then classify the emotion.
各プロンプトは {ヘッダ}\n\n{question}\n\n{choices}\n\n{letter_instruction} の形。
letter_instruction(n) = Respond with only the letter (A, B, ...):
指標
- Weighted Accuracy, UAR (unweighted average recall / balanced accuracy), Macro-F1, Micro-F1, per-class accuracy
- Prediction-marginal distribution chance baseline (expected accuracy / macro-F1 / micro-F1)
公式実装の既知バグ回避
公式テンプレート test_your_model.py は randomized_choices を結果に保存しないため
calculate_comprehensive_metrics が空になり指標が 0 になる。ベースライン実装
(test_qwen2_5_omni_all.py) に合わせて randomized_choices を保持し、指標を正しく計算する。
vLLM 実装要点
llm = LLM(
model=cfg.model.hf_id,
max_model_len=cfg.model.max_model_len,
gpu_memory_utilization=cfg.model.gpu_memory_utilization,
trust_remote_code=True,
limit_mm_per_prompt=cfg.model.limit_mm_per_prompt, # {image: 0, audio: 1}
)
# 音声: processor.apply_chat_template(messages) + multi_modal_data={"audio": (array, 16000)}
# audio を text より前に配置する
# thinking は無効(system prompt に <|think|> を入れない)
- E2B/E4B はネイティブ音声入力対応(conformer audio encoder ~300M、音声最大 30 秒)
- LISTEN の音声は平均 3.5 秒なので問題なし
limit_mm_per_prompt={"image": 0, "audio": 1}で画像トークンを無効化しメモリ節約max_model_lenを小さくして 24GB に収めるvllm[audio]extras が必要
Configs (Hydra)
configs/config.yaml が defaults で model / eval / runtime を合成。
# configs/config.yaml
defaults:
- model: gemma4_e2b # CLI で model=gemma4_e4b に上書き可能
- eval: listen
- runtime: vllm_local
- _self_
experiments: [1_text, 1_audio, 1_audio_and_text, 2A, 2B, 2C, 3A, 3B, 3C, 4]
resume: true
# configs/model/gemma4_e2b.yaml
name: gemma4-e2b
hf_id: google/gemma-4-E2B-it
backend: vllm
max_model_len: 4096
gpu_memory_utilization: 0.90
limit_mm_per_prompt: {image: 0, audio: 1}
sampling: {max_tokens: 8, temperature: 0.0}
system_prompt: null
モデル追加は configs/model/<new>.yaml を追加するだけ
(models/registry.py が cfg.model.backend から vLLM アダプタを解決)。
Results Output
hydra.run.dir を results/${model.name}/${now:...} に向け、実行ごとに日時ディレクトリを生成。
results/<model_name>/<date>/
├── summary.csv # 全体(実験×モダリティ、dataset_source 列なし)
├── summary_<dataset>.csv # データセット毎サマリ(フラット配置)
├── config.yaml # 実行時の Hydra 設定スナップショット
└── samples/<dataset>.csv # データセット毎のサンプル単位データ
summary.csv(実験×モダリティごとに 1 行)
model, experiment, modality, n_samples, weighted_accuracy, uar, macro_f1, micro_f1,
chance_accuracy, chance_macro_f1, chance_micro_f1
summary_.csv(そのデータセット内で 実験×モダリティごとに 1 行)
model, dataset_source, experiment, modality, n_samples, weighted_accuracy, uar,
macro_f1, micro_f1, chance_accuracy, chance_macro_f1, chance_micro_f1
samples/.csv(1 サンプル 1 行)
sample_id, experiment, modality, dataset_source, ground_truth, prediction,
expected_letter, predicted_letter, correct, raw_response, error, randomized_choices(JSON)
randomized_choices は JSON 文字列カラムとして保持(metrics 再計算用)。
集計ロジックは utils/export.py で「全体」と「dataset_source ごと」を同じ関数から生成。
対象 dataset_source
CREMA-D, Emotion-Speech, IEMOCAP, MELD, MOSEI, MUStARD, OMG, PODCAST, RAVDESS, TESS
(実行時に dataset_source で動的にグループ化するため、上記以外も自動対応)
Implementation Order
pyproject.toml/configs// ディレクトリ骨組みutils/(data, letter, prompts, randomization, metrics, checkpoint, export)models/(base, registry, vllm_audio)bin/run_eval.py(Hydra)- スモークテスト(各実験の先頭 20 件で letter 抽出・指標・CSV 出力を検証)
- E2B → E4B の順に全 10 実験を実行、
results/出力、論文 Table 2 と比較
Commands
# 依存インストール(uv 推奨: 依存 + editable インストール + dev グループ)
uv sync
# 全実験(デフォルト = gemma4_e2b)
uv run listen-eval
# モデル・実験を選択
uv run listen-eval model=gemma4_e4b 'experiments=[1_audio,2B,3B,4]'
# 実験一覧
uv run listen-eval --list
# checkpoint を使わず最初から
uv run listen-eval --no-resume
# uv を使わない場合はスクリプト直接実行(pip install -e ".[dev]" 後)
python src/bin/run_eval.py
listen-eval は pyproject.toml の [project.scripts] で定義
(bin.run_eval:main)。
Environment Notes
- 開発環境: Python 3.14(GPU/ドライバ無し、vllm/torch 未インストール) → 実評価は GPU 搭載マシン(RTX 3090)で実行する想定
- データセットは public・gated 無し(train 11,507 / test 2,635、audio 16kHz)