話者ダイアライゼーション
複数話者録音で誰がいつ話したかを識別します。2つのダイアライゼーションエンジンが利用可能です:2段階のPyannoteパイプライン(セグメンテーション + アクティビティベースの話者チェーン、その後のポストホック埋め込み)とエンドツーエンドのSortformerモデル(CoreML、Neural Engine)。
エンジン
--engine pyannote(デフォルト)または--engine sortformerでエンジンを選択します。
Pyannote(デフォルト)
2段階パイプライン:Pyannoteセグメンテーションはオーバーラップする窓をアクティビティベースの話者チェーン(オーバーラップゾーンでのピアソン相関)で処理し、グローバル話者ラベルを割り当てます。ポストホックWeSpeaker埋め込み抽出により、登録音声経由のターゲット話者識別が可能になります。
Sortformer(CoreML)
NVIDIAのエンドツーエンドニューラルダイアライゼーションモデル。最大4話者まで、別々の埋め込みやクラスタリング段階なしで、フレームごとの話者アクティビティを直接予測します。ストリーミング状態バッファ(FIFO + 話者キャッシュ)を備えたCoreML経由でNeural Engine上で動作します。
Sortformerは話者embeddingを生成しません。--target-speakerと--embedding-engineフラグはPyannoteエンジンでのみ使用できます。
Pyannoteパイプライン
デフォルトパイプラインは2段階で実行されます:
ステージ1: セグメンテーション + 話者チェーン
Pyannote segmentation-3.0は、50%オーバーラップの10秒スライディング窓を処理します。powersetデコーダーは7クラス出力を話者ごとの確率(窓あたり最大3ローカル話者)に変換します。隣接する窓は5秒のオーバーラップを共有します — 話者のアイデンティティは、オーバーラップゾーンの確率トラック間でピアソン相関を計算し、一貫したグローバル話者IDのためのグリーディ排他マッチングにより、窓を横断して伝播されます。
ステージ2: ポストホック埋め込み
ダイアライゼーション後、WeSpeaker ResNet34-LMは話者ごとに256次元のセントロイド埋め込みを抽出します。これらの埋め込みはターゲット話者抽出(--target-speaker)を可能にしますが、話者割り当て自体を駆動するものではありません。
CLIの使用法
# 基本的なダイアライゼーション(pyannote、デフォルト)
.build/release/audio diarize meeting.wav
# エンドツーエンドSortformer(CoreML)
.build/release/audio diarize meeting.wav --engine sortformer
# RTTM出力形式(評価用)
.build/release/audio diarize meeting.wav --rttm
# JSON出力
.build/release/audio diarize meeting.wav --json
ターゲット話者抽出
既知の話者の登録音声を提供して、録音からそのセグメントのみを抽出します。パイプラインは登録音声の話者埋め込みを計算し、最大のコサイン類似度を持つクラスターを見つけます。
# 特定の話者のセグメントを抽出
.build/release/audio diarize meeting.wav --target-speaker enrollment.wav
DERスコアリング
リファレンスRTTMファイルに対してスコアリングして、ダイアライゼーション品質を評価します。パイプラインは、誤って帰属された時間の割合を測定するDiarization Error Rate (DER)を計算します。
# リファレンスRTTMに対してスコアリング
.build/release/audio diarize meeting.wav --score-against reference.rttm
RTTM出力
--rttmフラグは、ダイアライゼーション評価で使用される標準形式であるRich Transcription Time Marked出力を生成します。各行は以下の形式に従います:
SPEAKER filename 1 start_time duration <NA> <NA> speaker_id <NA> <NA>
オプション
| オプション | 説明 |
|---|---|
--target-speaker | ターゲット話者抽出用の登録音声(pyannoteのみ) |
--embedding-engine | 話者埋め込みエンジン:mlxまたはcoreml(pyannoteのみ) |
--vad-filter | Silero VADで事前フィルタリング(pyannoteのみ) |
--rttm | RTTM形式で出力 |
--json | JSON出力形式 |
--score-against | DER評価用のリファレンスRTTMファイル |
ダイアライゼーションは、明確な話者ターンのある録音で最高に機能します。高度にオーバーラップする音声は精度を低下させる可能性があります。話者数は自動的に決定されます。
モデルダウンロード
モデルは初回使用時に自動的にダウンロードされます:
| コンポーネント | モデル | サイズ | HuggingFace |
|---|---|---|---|
| セグメンテーション | Pyannote-Segmentation-3.0 | 約5.7 MB | aufklarer/Pyannote-Segmentation-MLX |
| 話者埋め込み | WeSpeaker-ResNet34-LM (MLX) | 約25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| 話者埋め込み | WeSpeaker-ResNet34-LM (CoreML) | 約25 MB | aufklarer/WeSpeaker-ResNet34-LM-CoreML |
| Sortformer | Sortformer Diarization (CoreML) | 約240 MB | aufklarer/Sortformer-Diarization-CoreML |
Swift API
import SpeechVAD
let pipeline = try await DiarizationPipeline.fromPretrained()
let result = pipeline.diarize(audio: samples, sampleRate: 16000)
for seg in result.segments {
print("Speaker \(seg.speakerId): [\(seg.startTime)s - \(seg.endTime)s]")
}
// ターゲット話者抽出
let targetEmb = pipeline.embeddingModel.embed(audio: enrollmentAudio, sampleRate: 16000)
let segments = pipeline.extractSpeaker(
audio: meetingAudio, sampleRate: 16000,
targetEmbedding: targetEmb
)