リアルタイム音声アプリケーション向けのオンデバイス音声認識(STT)ライブラリでマルチプラットフォームに対応している Moonshine VoiceをWindowsで動かす

初めに

リアルタイム音声アプリケーション向けのオンデバイス音声認識(STT)ライブラリでマルチプラットフォームに対応している Moonshine Voiceを触ってみます

github.com

開発環境

  • Windows 11
  • uv 0.9.x
  • cuda 13.0

環境構築

以下のように依存ライブラリを追加します

uv python pin 3.12
uv add moonshine-voice

project.yamlは以下のようになります

[project]
name = "moonshine"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "datasets>=4.6.1",
    "moonshine-voice>=0.0.49",
    "soundfile>=0.13.1",
]

以下で依存ライブラリを同期します

uv sync

実行

以下で任意のパスの音声の文字お越しを行います

uv run python examples/python/basic_transcription.py path/to/audio.wav

また日本語の場合は以下のように実行します

uv run python examples/python/basic_transcription.py --language ja path/to/japanese_audio.wav

動画セグメンテーションの軽量なエンコーダのみのViTモデルをWindowsで動かす

初めに

以下のような動画の軽量なセグメンテーションのモデルが出ていたので触ってみます

オリジナルのリポジトリは以下です

github.com

開発環境

  • Windows 11
  • cuda 13.0
  • uv 0.9.0

環境構築

forkしてuv + Windowsに対応したものは以下になります。この記事は以下のリポジトリで動かすことを前提にしています

github.com

WindowsではDetectron2のC++拡張がビルドできないため、以下の手順が必要です。

1. Detectron2をクローンしてC++拡張を無効化

git clone --depth 1 https://github.com/facebookresearch/detectron2.git /path/to/detectron2

クローンした setup.py の217〜218行目を以下のように変更します:

# 変更前
ext_modules=get_extensions(),
cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension},

# 変更後
ext_modules=[],
# cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension},

2. pyproject.toml の detectron2 ソースをローカルパスに変更

[tool.uv.sources]
detectron2 = { path = "/path/to/detectron2" }

3. 依存パッケージをインストール

uv sync

注意: C++拡張なしでも基本機能(config, data, engine, modeling等)は動作しますが、一部のカスタムOPs(deformable conv等)は利用できません。

CUDA バージョンについて

pyproject.toml の PyTorch インデックスURLをシステムのCUDAバージョンに合わせて変更してください:

# CUDA 12.6 の場合
[[tool.uv.index]]
name = "pytorch-cu126"
url = "https://download.pytorch.org/whl/cu126"

# CUDA 13.0 の場合
[[tool.uv.index]]
name = "pytorch-cu130"
url = "https://download.pytorch.org/whl/cu130"

[tool.uv.sources]torchtorchvision のインデックス名も合わせて更新してください。

実行

実行コマンドは以下です

uv run python video_demo.py \
  --config-file ../configs/ytvis19/videomt/vit-small/videomt_online_ViTS.yaml \
  --input /path/to/frames \
  --output /path/to/output \
  --opts MODEL.WEIGHTS /path/to/weight.pth

ACL 2024論文「EconAgent」でGPT-4.1-miniによる100人規模のマクロ経済シミュレーションを動かす

初めに

EconAgentは、LLMを経済エージェントの意思決定エンジンとして使い、マクロ経済活動をシミュレートするフレームワークです。各エージェントに名前・年齢・職業などのプロフィールが割り当てられ、毎月の「労働意欲」と「消費率」をLLMがJSON形式で出力します。エージェント全体の行動が集積することで、物価・賃金・金利・失業率などのマクロ経済指標が内生的に変動します。

今回は公式実装をGPT-4.1-mini向けに調整し、100エージェント×240ヶ月(20年間)のフルシミュレーションを実行しました。

今回の実験用のforkしたリポジトリです

github.com

開発環境

項目 バージョン
OS Windows 11
Python 3.12
uv 0.9.x
OpenAI SDK 1.0+
モデル gpt-4.1-mini

環境構築

# 依存インストール
uv sync --extra gpt

# APIキー設定(.envファイルに記載、python-dotenvで自動読み込み)
echo 'OPENAI_API_KEY=your-key' > .env

シミュレーション実行

uv run python simulate.py --num_agents 100 --episode_length 240

仕組み

毎月、各エージェントに対して経済状況(物価・賃金・貯蓄・金利・税金等)を自然言語で伝え、{'work': 0-1, 'consumption': 0-1} のJSON形式で意思決定を返させます。work の値は確率的に0/1に変換され、consumption は資産のうち消費に回す割合です。

さらに3ヶ月毎にReflection(振り返り)プロンプトを実行し、四半期の経済環境を回顧させることで、より長期的な判断を可能にしています。

シミュレーション結果

マクロ経済指標

指標 初期値 最終値 変動率
物価 $135.29 $45.08 -66.7%
賃金 $132.07 $76.53 -42.1%
金利 3% 0% -

20年間で物価が約1/3に下落する大幅なデフレが発生しました。年次のインフレ率を見ると、最初の3年間で-30〜-40%の急激なデフレが起き、その後Year 4-9でインフレに反転(+8〜+14%)、Year 10-15で安定(+2〜+5%)、Year 16-19で再デフレ(-5〜-20%)と、景気循環のような波が観察されました。

金利はテイラールールに基づいて初年度の3%からすぐに0%に低下し、以降ゼロ金利が続きました。

失業率

失業率は13%〜40%の範囲で推移しました。インフレ期(Year 7)に13%まで低下し、再デフレ期(Year 17)に40%まで上昇しており、フィリップス曲線的な逆相関パターンが確認できます。

資産分布

指標
平均資産 $984,540
中央値 $330,619
最大 $7,787,128
最小 $6,574
ジニ係数 0.6669

Top 20%が全資産の72.7%を保有するという、大きな格差が生まれました。累進課税と再分配がある環境でもこの結果です。

スキルレベルと最終資産のピアソン相関は0.78でした。富裕層と貧困層で労働月数に大きな差はなく(平均176/240ヶ月)、格差の主因は時給に相当するスキルレベルの差でした。

結果からわかること

  • 景気循環の創発: 明示的にプログラムしていないのに、デフレ→回復→安定→再デフレという景気サイクルがLLMの意思決定から自然に生まれた
  • LLMのデフレバイアス: エージェント全体が消費に慎重で、デフレが進行しやすい傾向があった
  • スキル格差の増幅: 累進課税と再分配があっても、初期スキルの差が20年で大きな資産格差に拡大した(ジニ係数0.67)

大規模社会シミュレーションを行うマルチエージェントシステム「Agent-Kernel」を用いて災害対応シミュレーションで多様性がもたらす推定を行う

初めに

大規模社会シミュレーションを行うフレームワーク Agent-Kernelを見つけたので、これをもとに具体的な社会シミレーションを行ってみます

以下が今回実験を行ったfork リポジトリです

github.com

オリジナルのリポジトリは具体的な設定はなかったため、以下の設定を行う実験を行いました。 シナリオは「地震後の避難候補地評価」です。5つの候補地を5種類の専門家チームで評価しますが、各専門家は5属性のうち2つしか観測できない設計になっています。個体では原理的に正確な評価ができないため、集団で情報を統合する必要があります。

結論から言うと、多様な専門家10人のチーム(RMSE 3.44)は、同じ専門家120人のチーム(RMSE 18.68)を圧倒する結果になりました。

開発環境

  • Windows 11
  • Python 3.11
  • uv
  • LLM: gpt-4o-mini(OpenAI API)

実験設計

シナリオ

300×300マップ上に5つの避難候補地を配置し、各候補地は5属性(構造安全性・水/衛生・医療アクセス・物資経路・収容力)を持ちます。

専門家のリング構造

5種類の専門家がそれぞれ2属性のみ観測可能で、リング状に属性を共有します。

structural ←→ structural_engineer ←→ water
water      ←→ medical_officer     ←→ medical
medical    ←→ logistics_coordinator ←→ supply
supply     ←→ safety_inspector    ←→ shelter
shelter    ←→ community_liaison   ←→ structural

各属性はちょうど2職業がカバーする対称的な設計です。個体は2属性の値から5属性の合計を外挿(known_total × 5/n_known)するため、必然的に誤差が生まれます。集団の推定は全エージェントの推定値の平均を取ります。

エージェントの行動

各エージェントは LLM(gpt-4o-mini)で意思決定を行い、以下の4つのアクションから選択します。

  • move: 未訪問の候補地に移動して観測
  • share_observation: 近くの異なる専門家に観測データを共有
  • chat: 他エージェントと会話
  • rest: 休憩

実験条件

N シード 合計
実験群(5職業の混合チーム) 10, 20, 40, 60, 80, 120 42, 123, 456 18回
対照群(全員 structural_engineer) 10, 40, 120 42, 123, 456 9回

合計27回のシミュレーションを実行します。

環境構築

git clone https://github.com/yourname/Agent-Kernel.git
cd Agent-Kernel
uv sync --all-extras

.env にAPIキーを設定します。

OPENAI_API_KEY=sk-...

実行

シミュレーション実行

uv run python -m examples.standalone_test.run_wisdom_experiment survey

27条件を順番に実行します。所要時間は約60分です。

実験結果

実験群(混合専門家チーム):

N Collective RMSE Avg Individual RMSE Diversity Bonus Coverage
10 3.44 8.74 5.30 100%
20 1.86 11.83 9.96 100%
40 2.37 13.34 10.97 100%
60 2.77 14.09 11.32 100%
80 3.11 14.61 11.50 100%
120 2.59 15.63 13.05 100%

対照群(全員 structural_engineer):

N Collective RMSE Avg Individual RMSE Diversity Bonus Coverage
10 18.27 19.59 1.33 40%
40 19.19 20.66 1.47 40%
120 18.68 20.22 1.54 40%

結果のポイント

  • 多様性の効果: 混合チーム RMSE ≈ 2〜3 vs 同質チーム RMSE ≈ 18〜19 で約6倍の精度差
  • カバレッジの壁: 混合チームは全条件で Coverage 100% に対し、同質チームは 40% 止まり。同じ専門家を何人増やしても見えない情報は見えないまま
  • Diversity Bonus の拡大: N=10 で 5.30 → N=120 で 13.05。人数が増えるほど集団の利得が拡大
  • 収束パターン: Tick 0〜3 で候補地を巡回し Coverage が急上昇、Tick 3〜10 で情報共有により RMSE が急降下、Tick 10 以降で安定

可視化ダッシュボード

Society Panel の分析ダッシュボードで実験結果を可視化できます。

# Windows
scripts\start_society_panel.bat

# Linux/macOS
./scripts/start_society_panel.sh

http://localhost:5174 にアクセスし、サイドバーから「分析」を選択、録画ドロップダウンから survey_mixed_N* を選択すると、以下の6チャートが表示されます。

チャート 内容
KPIカード Collective RMSE / Diversity Bonus / Coverage / Agent数
RMSE収束曲線 集団 vs 個人平均 vs 最良個体の3本折れ線
多様性ボーナス diversity_bonus のtick推移
属性カバレッジ 0% → 100% のステップ曲線
専門家構成 5専門家のドーナツ円グラフ
候補地マップ 300×300マップ上の候補地 + エージェント(職業別色分け)

まとめ

  • 多様な専門家10人(RMSE 3.44)が、同質の専門家120人(RMSE 18.68)を圧倒する
  • 集合知の鍵は「人数」ではなく「視点の多様性」
  • Agent-Kernel のプラグインシステムを使えば、設定ファイルとプラグインの変更だけでこのような実験を構築できる

参考

  • Agent-Kernel - MicroKernel Multi-Agent System Framework
  • Agent-Kernel 論文 - arXiv:2512.01610
  • Surowiecki, J. (2004). The Wisdom of Crowds. Doubleday.
  • Hong, L., & Page, S. E. (2004). Groups of diverse problem solvers can outperform groups of high-ability problem solvers. PNAS, 101(46), 16385–16389.

音声から3Dフェイシャルアニメーションを生成する「UniTalker」を実行する

初めに

音声から3Dフェイシャルアニメーションを生成する統合ニューラルネットワークモデル「UniTalker」を実行してみます

uvに統合 + ドキュメントの日本語対応したforkリポジトリは以下です

github.com

サンプル音声を実行した結果は以下のようになりました

youtube.com

また日本語の音声の場合は以下のようになりました( 音声はjvsを使用)

youtube.com

開発環境

  • Windows 11
  • uv

環境構築

以下のforkしたリポジトリをcloneします

github.com

環境を構築します

uv sync

実行

以下で推論をしてデータから3Dのフェイシャルアニメーションにレンダリングをします

# 推論
uv run python -m main.demo --config config/unitalker.yaml test_out_path ./test_results/demo.npz

# レンダリング
uv run python -m main.render ./test_results/demo.npz ./test_audios ./test_results/

自己教師あり音声特徴量に対する線形回帰のみで音声変換を行うLinearVCを動かす

初めに

LinearVC は、線形回帰のみで音声変換(Voice Conversion)を行う手法です。 Interspeech 2025 に採択された論文の実装になります。

github.com

arxiv.org

処理パイプラインは以下の3ステップで構成されています。

  1. WavLM Large(レイヤー6)で自己教師あり特徴量を抽出
  2. ソース話者とターゲット話者の特徴量から線形回帰で射影行列を学習
  3. 特徴量を変換し HiFiGAN で波形を生成

開発環境

項目 詳細
OS Windows 11
CPU AMD Ryzen 9 7950X
GPU NVIDIA GeForce RTX 4090
メモリ 128GB
Python 3.13.8
CUDA 12.6
uv 0.6.x

環境構築

fork したリポジトリを clone します。

github.com

git clone https://github.com/ayutaz/linearvc.git
cd linearvc

pyproject.toml は以下の内容です。 PyTorch を CUDA 12.6 対応版でインストールするため、explicit = true で専用のインデックスを指定しています。

[project]
name = "linearvc"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.13"
dependencies = [
    "celer>=0.7.4",
    "ipython>=9.10.0",
    "jupyter>=1.1.1",
    "numpy>=2.4.2",
    "torch>=2.10.0",
    "torchaudio>=2.10.0",
    "tqdm>=4.67.3",
]

[[tool.uv.index]]
name = "pytorch-cu126"
url = "https://download.pytorch.org/whl/cu126"
explicit = true

[tool.uv.sources]
torch = [{ index = "pytorch-cu126" }]
torchaudio = [{ index = "pytorch-cu126" }]

依存パッケージをインストールします。

uv sync

データの準備

音声変換には、ソース話者(変換元)とターゲット話者(変換先)の音声データが必要です。 今回はソース話者に LibriSpeech dev-clean の話者 1272(英語男性)、ターゲット話者につくよみちゃんコーパス Vol.1(日本語女性)を使用します。

LibriSpeech dev-clean のダウンロード

mkdir -p data
cd data
wget https://www.openslr.org/resources/12/dev-clean.tar.gz
tar xzf dev-clean.tar.gz
cd ..

展開すると以下のようなディレクトリ構成になります。

data/LibriSpeech/dev-clean/
├── 1272/          # 話者ID
│   ├── 128104/    # チャプターID
│   │   ├── 1272-128104-0000.flac
│   │   ├── 1272-128104-0001.flac
│   │   └── ...
│   └── 135031/
│       └── ...
└── ...

サブセットの作成

linearvc.py--extension オプションはソースとターゲット両方に適用されるため、拡張子を .wav に統一します。 LibriSpeech の .flac.wav に変換し、それぞれ数ファイルずつのサブセットを作成します。

import torchaudio
from pathlib import Path

src_dir = Path("data/LibriSpeech/dev-clean/1272")
wav_dir = Path("data/subset_libri1272")
wav_dir.mkdir(exist_ok=True)

for flac_file in sorted(src_dir.rglob("*.flac"))[:3]:
    wav, sr = torchaudio.load(flac_file)
    out_path = wav_dir / flac_file.name.replace(".flac", ".wav")
    torchaudio.save(str(out_path), wav, sr)

つくよみちゃんコーパスも同様に数ファイルコピーします。

mkdir -p data/subset_tsukuyomi
cp path/to/つくよみちゃんコーパス/02\ WAV(+12dB増幅)/VOICEACTRESS100_00{1,2,3}.wav data/subset_tsukuyomi/

実行

linearvc.py を実行して音声変換を行います。 ソース話者のディレクトリ、ターゲット話者のディレクトリ、変換したい入力音声ファイル、出力ファイル名を指定します。

文では非並列モードで話者あたり約3分以上のデータを推奨しています。そのため3-5分程度を最低限指定するようにしてください

パターン1: LibriSpeech 1272 → つくよみちゃん(英語男性 → 日本語女性)

uv run python linearvc.py \
    data/subset_libri1272 \
    data/subset_tsukuyomi \
    data/subset_libri1272/1272-128104-0000.wav \
    output.wav

実行すると以下のようなログが出力されます。

Reading from: data\subset_libri1272
Reading from: data\subset_tsukuyomi
Reading: data\subset_libri1272\1272-128104-0000.wav
Source features:
100%|██████████| 3/3 [00:00<00:00, 14.13it/s]
Target features:
100%|██████████| 3/3 [00:00<00:00, 21.18it/s]
Writing: output.wav

パターン2: つくよみちゃん → LibriSpeech 1272(日本語女性 → 英語男性)

uv run python linearvc.py \
    data/subset_tsukuyomi \
    data/subset_libri1272 \
    data/subset_tsukuyomi/VOICEACTRESS100_001.wav \
    output_tsukuyomi2libri.wav
Reading from: data\subset_tsukuyomi
Reading from: data\subset_libri1272
Reading: data\subset_tsukuyomi\VOICEACTRESS100_001.wav
Source features:
100%|██████████| 3/3 [00:00<00:00, 20.83it/s]
Target features:
100%|██████████| 3/3 [00:00<00:00, 36.68it/s]
Writing: output_tsukuyomi2libri.wav

パターン3: LibriSpeech 1272 → LibriSpeech 1462(英語男性 → 英語女性)

uv run python linearvc.py \
    data/subset_libri1272 \
    data/subset_libri1462 \
    data/subset_libri1272/1272-128104-0000.wav \
    output_libri2libri.wav
Reading from: data\subset_libri1272
Reading from: data\subset_libri1462
Reading: data\subset_libri1272\1272-128104-0000.wav
Source features:
100%|██████████| 3/3 [00:00<00:00, 20.81it/s]
Target features:
100%|██████████| 3/3 [00:00<00:00, 20.43it/s]
Writing: output_libri2libri.wav

各パターンとも output.wav が生成されていれば成功です。

精度はそれなりのものができてそうでした

参考

Roboflow の trackers ライブラリで YOLO + ByteTrack による動画マルチオブジェクト追跡をする

初めに

trackers は、Roboflow が提供するマルチオブジェクト追跡(MOTアルゴリズムクリーンルーム実装ライブラリです。Apache 2.0 ライセンスで公開されており、任意の検出モデルと組み合わせて使えます。

今回は YOLO で物体を検出し、ByteTrack で追跡、supervision で可視化する一連のパイプラインを動かします。ByteTrack は二段階アソシエーション(高信頼度 → 低信頼度の順でマッチング)を行うアルゴリズムで、低信頼度の検出も活用することでオクルージョンに強い追跡が可能です。

結果

入力

出力

開発環境

項目 バージョン
OS Windows 11
Python 3.13
パッケージマネージャ uv
trackers 2.2.0rc0
YOLO モデル yolo11m.pt (Ultralytics)

環境構築

リポジトリをクローンして依存パッケージをインストールします。

git clone https://github.com/roboflow/trackers.git
cd trackers
uv sync

trackers は ultralyticsopencv-python を依存に含んでいるため、追加インストールは不要です。

テスト用動画のダウンロード

supervision ライブラリの download_assets を使うとサンプル動画を簡単に取得できます。

from supervision.assets import download_assets, VideoAssets

video_path = download_assets(VideoAssets.PEOPLE_WALKING)

実行すると people-walking.mp4 がカレントディレクトリにダウンロードされます。

デモスクリプトの解説

スクリプトexamples/demo_bytetrack.py にあります。

引数

引数 デフォルト 説明
--source (必須) 入力動画のパス
--output output.mp4 出力動画のパス
--model yolo11m.pt YOLO モデル名またはパス

検出

YOLO で各フレームの物体を検出し、sv.Detections.from_ultralytics() で supervision の形式に変換します。

results = model(frame, verbose=False)[0]
detections = sv.Detections.from_ultralytics(results)

追跡

ByteTrackTracker().update(detections) を呼ぶだけでトラッカーID が付与されます。tracker_id-1 の場合は、まだ連続フレーム数の閾値(デフォルト2フレーム)に達していない未成熟なトラックです。

tracker = ByteTrackTracker()
detections = tracker.update(detections)

可視化

sv.BoxAnnotatorsv.LabelAnnotatorバウンディングボックスとラベルを描画します。

box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()

annotated = box_annotator.annotate(scene=frame.copy(), detections=detections)
annotated = label_annotator.annotate(
    scene=annotated, detections=detections, labels=labels
)

ソースコード全文

import argparse
import time

import cv2
import supervision as sv
from ultralytics import YOLO

from trackers import ByteTrackTracker


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="YOLO + ByteTrack object tracking demo"
    )
    parser.add_argument("--source", type=str, required=True)
    parser.add_argument("--output", type=str, default="output.mp4")
    parser.add_argument("--model", type=str, default="yolo11m.pt")
    return parser.parse_args()


def main() -> None:
    args = parse_args()

    model = YOLO(args.model)
    tracker = ByteTrackTracker()
    box_annotator = sv.BoxAnnotator()
    label_annotator = sv.LabelAnnotator()

    cap = cv2.VideoCapture(args.source)
    if not cap.isOpened():
        print(f"Error: cannot open video '{args.source}'")
        return

    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    fourcc = cv2.VideoWriter_fourcc(*"mp4v")
    writer = cv2.VideoWriter(args.output, fourcc, fps, (width, height))

    frame_idx = 0
    prev_time = time.time()

    print(f"Processing '{args.source}' ({total_frames} frames, {fps:.1f} FPS)")
    print(f"Output: '{args.output}'")
    print("Press 'q' to stop early.\n")

    while True:
        ret, frame = cap.read()
        if not ret:
            break

        results = model(frame, verbose=False)[0]
        detections = sv.Detections.from_ultralytics(results)
        detections = tracker.update(detections)

        tracker_ids = (
            detections.tracker_id
            if detections.tracker_id is not None
            else []
        )
        labels = [
            f"#{int(tid)}" if tid >= 0 else "?"
            for tid in tracker_ids
        ]

        annotated = box_annotator.annotate(
            scene=frame.copy(), detections=detections
        )
        annotated = label_annotator.annotate(
            scene=annotated, detections=detections, labels=labels
        )

        now = time.time()
        processing_fps = 1.0 / max(now - prev_time, 1e-9)
        prev_time = now

        frame_idx += 1
        n_tracks = (
            int((detections.tracker_id >= 0).sum())
            if detections.tracker_id is not None
            else 0
        )
        print(
            f"\rFrame {frame_idx}/{total_frames}"
            f"  FPS: {processing_fps:.1f}"
            f"  Tracks: {n_tracks}",
            end="", flush=True,
        )

        writer.write(annotated)
        cv2.imshow("ByteTrack Demo", annotated)
        if cv2.waitKey(1) & 0xFF == ord("q"):
            print("\nStopped by user.")
            break

    cap.release()
    writer.release()
    cv2.destroyAllWindows()
    print(f"\n\nDone. Output saved to '{args.output}'")


if __name__ == "__main__":
    main()

実行

以下のコマンドで実行します。

uv run python examples/demo_bytetrack.py --source people-walking.mp4

ターミナルに処理状況が表示されます。

Processing 'people-walking.mp4' (210 frames, 30.0 FPS)
Output: 'output.mp4'
Press 'q' to stop early.

Frame 210/210  FPS: 15.2  Tracks: 7

ウィンドウが開いてリアルタイムで追跡結果が表示されます。途中で止めたい場合は q キーを押してください。完了すると output.mp4 が出力されます。