🙀

【感情分析】日本語音声基盤モデル「くしなだ」を使ってみた!

に公開

日本語音声基盤モデル「くしなだ」って何。

「日本語音声基盤モデル「いざなみ」「くしなだ」を公開」

https://www.aist.go.jp/aist_j/press_release/pr2025/pr20250310/pr20250310.html

ということで、「国立研究開発法人産業技術総合研究所」(略して産総研)が、作った音声言語基盤モデルです。

早速動かしてみよう!

悲しいかな、私はAIに全く詳しくない上に、大昔のシングルモーダルが主流なAI時代の人間であるため、今回の「基盤モデルって何・・・・・・・ぜんぜんわかりませえねねぇぇぇぇえん!」となっていました。ということで、基盤モデルって何ぞや?というお話は、専門家の方が丁寧にご説明してくださっていると思うので、そちらをご覧ください。
あと、音声の専門家でもないです。

ということで、サッサと動かしましょう。

import torch
import yaml
import os
from transformers import HubertModel
import torchaudio
import argparse
torch.serialization.add_safe_globals([argparse.Namespace])

# --- 設定 ---
downstream_ckpt_path = "./kushinada-hubert-large-jtes-er/s3prl/result/downstream/kushinada-hubert-large-jtes-er_fold1/dev-best.ckpt"
wav_path = "./test.wav"  # 推論したいWAVファイルのパス

# --- 1. 上流モデルをロード ---
print("Loading upstream model...")
upstream = HubertModel.from_pretrained("imprt/kushinada-hubert-large")
upstream.eval()

# --- 2. 音声ファイルをロード ---
print("Loading audio...")
waveform, sample_rate = torchaudio.load(wav_path)
if sample_rate != 16000:
    transform = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=16000)
    waveform = transform(waveform)
waveform = waveform.mean(dim=0).unsqueeze(0)

# --- 3. 特徴抽出 ---
print("Extracting features...")
with torch.no_grad():
    features = upstream(waveform).last_hidden_state  # [batch, time, feature_dim]
    # 時間方向に平均を取る(簡易集約)
    features = features.mean(dim=1)

# --- 4. 下流モデル(感情分類モデル)をロード ---
print("Loading downstream model...")
downstream_ckpt = torch.load(downstream_ckpt_path)

downstream_dict = downstream_ckpt["Downstream"]

projectorWeight = downstream_dict["projector.weight"]
projector = torch.nn.Linear(projectorWeight.size(1), projectorWeight.size(0))
projector.load_state_dict({
    "weight": projectorWeight,
    "bias": downstream_dict["projector.bias"]
})
projector.eval()

downstoreamWeight = downstream_dict["model.post_net.linear.weight"]
post_net = torch.nn.Linear(downstoreamWeight.size(1), downstoreamWeight.size(0))
post_net.load_state_dict({
    "weight": downstoreamWeight,
    "bias": downstream_dict["model.post_net.linear.bias"]
})
post_net.eval()

# JTESコーパスなので...
label_map = {
    0: "中立(neutral)",
    1: "喜び(happy)",
    2: "怒り(angry)",
    3: "悲しみ(sad)"
}

print(f"✅ 感情分類結果: ラベル番号 = {predicted_class} -> {label_map.get(predicted_class)}")

はい。書きました。
これで、pythonで実行すれば良しなに結果が出ます。

(venv) link@link:~/audio_classification$ python3 test3.py
Loading upstream model...
Loading audio...
Extracting features...
Loading downstream model...
Predicting...
tensor([[ 0.0739, -0.0405,  0.0188, -0.1719]])
✅ 感情分類結果: ラベル番号 = 0 -> 中立(neutral)

精度に関する感想

1つの録音ファイルしか試してないので、精度がどうのこうの言えないのですが、ハッキリ言って、実際のソフトウェアなどに内蔵するようなレベルにないのかな?というのは感じました。
今回、実は開発中のアプリに技術導入検討をしてみようかと思ってたのですが、その考えは白紙になりました。

現時点で日本語においては、産総研より良いモデルが無さそうなので、うーん。という感じです。
あと、初めてJTESコーパスなるものを知ったのですが、これ…ラベル少なすぎない??とは少し思いました。世の中のベーシックなのかもしれませんが、こう…もっと…感情って…

\(^o^)/

Discussion