見出し画像

NVIDIA PersonaPlex を Google Colab で試す!リアルタイム全二重音声AI入門( 📒Google Colab ノートブック付)

📒 本記事のコードはすべてGoogle Colabで実行できます。記事末尾のリンクからお試しください!

⚠️ この記事を読む前に この記事は2026年1月時点の情報をもとにしています。PersonaPlexは最近公開されたばかりのモデルのため、今後APIや使用方法が変更される可能性があります。また、 A100 GPU(有料) が必要です。



ざっくり言うと

  • PersonaPlex は、NVIDIAが開発した「聞きながら話す」ができるリアルタイム音声AIモデル

  • テキストで「役割」を、音声で「声質」を指定できるので、カスタマーサポートAIからカジュアルな会話相手まで自在に変身

  • Google Colab の オフラインモード を使えば、音声ファイルを入力して AI の応答音声を生成できる

そもそも、PersonaPlex ってなに?

従来の音声AIは「聞く」「考える」「話す」を 順番に 処理していました。人間が話し終わるのを待ってから返答を考え始めるので、どうしても会話が不自然になります。「あー」「うん」といった相槌もできませんし、話の途中で割り込むこともできません。

PersonaPlex は、 聞きながら同時に話す ことができる「全二重(Full Duplex)」モデルです。人間同士の会話と同じように、相手の話を聞きながら「うんうん」と相槌を打ったり、適切なタイミングで話し始めたりできます。

たとえ話で説明すると...

従来のAIは トランシーバー のような会話でした。一方が話し終わってボタンを離すまで、もう一方は何も言えません。

PersonaPlex は 電話 のような会話です。お互いが同時に話すことも、相槌を打つことも、話を遮ることもできます。

PersonaPlex のすごいところ

画像

🛠️ セットアップ

Step 1: Google Drive のマウント

from google.colab import drive
drive.mount('/content/drive')

print("✅ Google Drive マウント完了")

Step 2: GPU の確認

⚠️ 重要: PersonaPlex は約30GB以上のVRAMが必要です。Colab の A100 GPU(有料プラン)を使用してください。

# GPU情報の確認
!nvidia-smi

import torch
if torch.cuda.is_available():
    gpu_name = torch.cuda.get_device_name(0)
    gpu_memory = torch.cuda.get_device_properties(0).total_memory / 1024**3
    print(f"✅ GPU: {gpu_name}")
    print(f"✅ VRAM: {gpu_memory:.1f} GB")

    if gpu_memory < 30:
        print("⚠️ 警告: VRAMが不足している可能性があります。A100 GPUを推奨します。")
else:
    print("❌ GPUが利用できません。ランタイム → ランタイムのタイプを変更 → GPU を選択してください。")

Step 3: uv のセットアップ

# uvのインストール(高速パッケージマネージャー)
print("⚡ uvをインストール中...")
!pip install uv -q

# Colab環境の制約ファイル問題を回避
import os
os.makedirs("/backend-container/containers", exist_ok=True)
!touch /backend-container/containers/requirements.constraints
!touch /backend-container/containers/build.constraints

!uv --version
print("✅ uvセットアップ完了")

Step 4: 環境変数とユーティリティの設定

import os
import time
from datetime import datetime
from contextlib import contextmanager
import json

# 保存ディレクトリの設定
SAVE_DIR = '/content/drive/MyDrive/Prj/PersonaPlex/experiments'
os.makedirs(SAVE_DIR, exist_ok=True)

# 実験ログ
experiment_log = []

@contextmanager
def timer(task_name: str, log_to_file: bool = True):
    """処理時間を計測"""
    start = time.time()
    print(f"⏱️ {task_name} 開始...")
    yield
    elapsed = time.time() - start
    print(f"✅ {task_name} 完了: {elapsed:.2f}秒")
    if log_to_file:
        experiment_log.append({
            "timestamp": datetime.now().isoformat(),
            "task": task_name,
            "elapsed_seconds": round(elapsed, 2)
        })

def log_experiment(experiment_name: str, result_info: dict = None):
    """実験結果をログに記録"""
    entry = {"timestamp": datetime.now().isoformat(), "experiment": experiment_name}
    if result_info:
        entry.update(result_info)
    experiment_log.append(entry)
    print(f"📝 実験ログ記録: {experiment_name}")

def generate_timestamp():
    return datetime.now().strftime("%Y%m%d_%H%M%S")

def save_experiment_log(project_name: str):
    """実験ログをJSONファイルとして保存"""
    log_path = os.path.join(SAVE_DIR, f"{project_name}_log_{generate_timestamp()}.json")
    with open(log_path, 'w', encoding='utf-8') as f:
        json.dump(experiment_log, f, ensure_ascii=False, indent=2)
    print(f"📊 実験ログ保存: {log_path}")

print("✅ 環境設定完了")
print(f"📁 保存先: {SAVE_DIR}")

Step 5: HuggingFace 認証の設定

⚠️ 重要: PersonaPlex のモデルを使用するには、HuggingFace でライセンスに同意する必要があります。

  1. HuggingFace の PersonaPlex ページ にアクセス

  2. ライセンスに同意(NVIDIA Open Model License)

  3. HuggingFace のアクセストークンを取得

from google.colab import userdata

# Colab のシークレットから HF_TOKEN を取得
# 事前に「シークレット」タブで HF_TOKEN を設定してください
try:
    HF_TOKEN = userdata.get('HF_TOKEN')
    os.environ['HF_TOKEN'] = HF_TOKEN
    print("✅ HuggingFace トークン設定完了")
except Exception as e:
    print("⚠️ HF_TOKEN が設定されていません。")
    print("手順: サイドバー → 🔑 シークレット → HF_TOKEN を追加")
    HF_TOKEN = input("または、ここにトークンを入力: ")
    os.environ['HF_TOKEN'] = HF_TOKEN

Step 6: リポジトリのクローン

with timer("リポジトリのクローン"):
    !git clone https://github.com/NVIDIA/personaplex.git
    %cd personaplex

print("\n📁 ファイル構成:")
!ls -la

Step 7: 依存関係のインストール

with timer("依存関係のインストール"):
    # moshi パッケージのインストール
    !uv pip install ./moshi/. --system -q

    # 追加の依存関係(必要に応じて)
    !uv pip install soundfile librosa --system -q

print("✅ パッケージインストール完了")

📖 PersonaPlex の仕組みを覗いてみよう

アーキテクチャ概要

PersonaPlex は以下のコンポーネントで構成されています。

┌─────────────────────────────────────────────────────────┐
│                    PersonaPlex                          │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐ │
│  │ Mimi 音声   │    │ Transformer │    │ Mimi 音声   │ │
│  │ エンコーダ  │ →  │ (Temporal + │ →  │ デコーダ    │ │
│  │             │    │   Depth)    │    │             │ │
│  └─────────────┘    └─────────────┘    └─────────────┘ │
├─────────────────────────────────────────────────────────┤
│  入力: ユーザー音声 (24kHz)                             │
│  出力: AI応答音声 (24kHz) + テキスト                    │
└─────────────────────────────────────────────────────────┘

つまり、音声を一度「トークン」という単位に変換し、言語モデルで処理してから、また音声に戻すという流れです。

ペルソナ制御の仕組み

PersonaPlex では 2種類のプロンプト でAIの振る舞いを制御します。

画像

利用可能なボイス

画像

🧪 実験1:アシスタントモードで音声応答を生成

実行内容 テスト用の音声ファイルを入力し、PersonaPlex に応答音声を生成させます。

テストファイルの確認

# assets/test ディレクトリの内容を確認
print("📁 テストファイル一覧:")
!ls -la assets/test/

# 入力音声ファイルの情報
print("\n🎤 入力音声ファイル情報:")
!file assets/test/input_assistant.wav

オフラインモードで実行

import subprocess

with timer("PersonaPlex オフライン推論(アシスタントモード)"):
    # 出力ファイルパス
    output_wav = os.path.join(SAVE_DIR, f"output_assistant_{generate_timestamp()}.wav")
    output_json = os.path.join(SAVE_DIR, f"output_assistant_{generate_timestamp()}.json")

    # コマンド実行
    cmd = [
        "python", "-m", "moshi.offline",
        "--voice-prompt", "NATF2.pt",
        "--input-wav", "assets/test/input_assistant.wav",
        "--seed", "42424242",
        "--output-wav", output_wav,
        "--output-text", output_json
    ]

    result = subprocess.run(cmd, capture_output=True, text=True, env=os.environ)

    if result.returncode == 0:
        print(f"✅ 出力音声: {output_wav}")
        print(f"✅ 出力テキスト: {output_json}")
    else:
        print(f"❌ エラー発生:")
        print(result.stderr)

log_experiment(
    experiment_name="assistant_mode_inference",
    result_info={
        "voice_prompt": "NATF2.pt",
        "input_file": "input_assistant.wav",
        "output_wav": output_wav
    }
)

結果の確認と再生

from IPython.display import Audio, display
import json

# 出力音声を再生
if os.path.exists(output_wav):
    print("🔊 生成された応答音声:")
    display(Audio(output_wav))

# 出力テキストを確認
if os.path.exists(output_json):
    print("\n📝 生成されたテキスト:")
    with open(output_json, 'r') as f:
        text_output = json.load(f)
    print(json.dumps(text_output, indent=2, ensure_ascii=False))

🧪 実験2:カスタマーサービスモードで試す

実行内容 テキストプロンプトでカスタマーサービス担当者の役割を指定し、より専門的な応答を生成します。

プロンプトの作成

# カスタマーサービス用のプロンプトを作成
service_prompt = """You work for TechSupport Pro which is a software support company and your name is Alex Chen.
Information: Verify customer name and product version.
Available support tiers: Basic (free), Premium ($29/month), Enterprise (custom pricing).
Common issues: password reset, license activation, software updates.
Support hours: 24/7 for Premium and Enterprise, 9 AM - 6 PM for Basic."""

# プロンプトを一時ファイルに保存
prompt_file = "/content/service_prompt.txt"
with open(prompt_file, 'w') as f:
    f.write(service_prompt)

print("📝 カスタマーサービスプロンプト:")
print(service_prompt)

カスタマーサービスモードで実行

with timer("PersonaPlex オフライン推論(カスタマーサービスモード)"):
    output_wav_service = os.path.join(SAVE_DIR, f"output_service_{generate_timestamp()}.wav")
    output_json_service = os.path.join(SAVE_DIR, f"output_service_{generate_timestamp()}.json")

    # プロンプトファイルを読み込み
    with open(prompt_file, 'r') as f:
        text_prompt = f.read()

    cmd = [
        "python", "-m", "moshi.offline",
        "--voice-prompt", "NATM1.pt",  # 男性の声を使用
        "--text-prompt", text_prompt,
        "--input-wav", "assets/test/input_service.wav",
        "--seed", "42424242",
        "--output-wav", output_wav_service,
        "--output-text", output_json_service
    ]

    result = subprocess.run(cmd, capture_output=True, text=True, env=os.environ)

    if result.returncode == 0:
        print(f"✅ 出力音声: {output_wav_service}")
    else:
        print(f"❌ エラー発生:")
        print(result.stderr)

log_experiment(
    experiment_name="service_mode_inference",
    result_info={
        "voice_prompt": "NATM1.pt",
        "text_prompt": "customer_service",
        "output_wav": output_wav_service
    }
)

結果の確認

if os.path.exists(output_wav_service):
    print("🔊 カスタマーサービスモードの応答音声:")
    display(Audio(output_wav_service))

🧪 実験3:カジュアル会話モードを試す

実行内容 自由な会話を楽しむためのプロンプトを使用します。

# カジュアル会話用のプロンプト
casual_prompt = "You enjoy having a good conversation. Have a casual discussion about favorite movies and TV shows."

with timer("PersonaPlex オフライン推論(カジュアル会話モード)"):
    output_wav_casual = os.path.join(SAVE_DIR, f"output_casual_{generate_timestamp()}.wav")
    output_json_casual = os.path.join(SAVE_DIR, f"output_casual_{generate_timestamp()}.json")

    cmd = [
        "python", "-m", "moshi.offline",
        "--voice-prompt", "NATF3.pt",  # 別の女性の声
        "--text-prompt", casual_prompt,
        "--input-wav", "assets/test/input_assistant.wav",  # テスト用入力を流用
        "--seed", "12345678",
        "--output-wav", output_wav_casual,
        "--output-text", output_json_casual
    ]

    result = subprocess.run(cmd, capture_output=True, text=True, env=os.environ)

    if result.returncode == 0:
        print(f"✅ 出力音声: {output_wav_casual}")
    else:
        print(f"❌ エラー発生:")
        print(result.stderr)

log_experiment(
    experiment_name="casual_mode_inference",
    result_info={
        "voice_prompt": "NATF3.pt",
        "text_prompt": casual_prompt
    }
)

if os.path.exists(output_wav_casual):
    print("\n🔊 カジュアル会話モードの応答音声:")
    display(Audio(output_wav_casual))

🎤 実験4:自分の音声で試す(オプション)

実行内容 自分で録音した音声ファイルをアップロードして、PersonaPlex と会話してみましょう。

音声ファイルのアップロード

from google.colab import files

print("📤 音声ファイルをアップロードしてください(.wav形式、24kHz推奨)")
uploaded = files.upload()

if uploaded:
    uploaded_filename = list(uploaded.keys())[0]
    print(f"✅ アップロード完了: {uploaded_filename}")
else:
    uploaded_filename = None
    print("⚠️ ファイルがアップロードされませんでした")

アップロードした音声で推論

if uploaded_filename:
    with timer("PersonaPlex オフライン推論(カスタム入力)"):
        output_wav_custom = os.path.join(SAVE_DIR, f"output_custom_{generate_timestamp()}.wav")

        cmd = [
            "python", "-m", "moshi.offline",
            "--voice-prompt", "NATF2.pt",
            "--input-wav", uploaded_filename,
            "--seed", "99999999",
            "--output-wav", output_wav_custom,
            "--output-text", output_wav_custom.replace('.wav', '.json')
        ]

        result = subprocess.run(cmd, capture_output=True, text=True, env=os.environ)

        if result.returncode == 0:
            print(f"✅ 出力音声: {output_wav_custom}")
            print("\n🔊 生成された応答音声:")
            display(Audio(output_wav_custom))
        else:
            print(f"❌ エラー発生:")
            print(result.stderr)

    log_experiment(
        experiment_name="custom_input_inference",
        result_info={"input_file": uploaded_filename}
    )

何がわかったの?

PersonaPlex を使った実験から、以下のことがわかりました。

  1. テキストプロンプト で役割や知識を指定すると、AIの応答内容が変化する

  2. ボイスプロンプト で声質を選択できるので、用途に合わせた音声を生成できる

  3. オフラインモード を使えば、サーバーを立てなくても音声応答を生成できる

💡 ポイント PersonaPlex は「全二重」が売りですが、オフラインモードでは入力音声と同じ長さの出力が生成されます。リアルタイムの対話体験を得るには、サーバーモードとWebUIを使う必要があります。

じゃあ、どうすればいいの?〜次のステップ〜

ローカル環境でサーバーモードを試す

Google Colab のオフラインモードでは、PersonaPlex の「リアルタイム対話」機能は体験できません。本格的に試すには、以下の環境が必要です。

  • GPU: NVIDIA GPU(VRAM 16GB以上推奨)

  • 環境: ローカルPCまたはクラウドGPUインスタンス

  • ブラウザ: WebUIにアクセスするためのモダンブラウザ

# ローカル環境での実行例
SSL_DIR=$(mktemp -d)
python -m moshi.server --ssl "$SSL_DIR"
# ブラウザで https://localhost:8998 にアクセス

Docker を使う

より簡単に環境を構築したい場合は、Docker を使用できます。

# .env ファイルに HF_TOKEN を設定
echo "HF_TOKEN=your_token_here" > .env

# Docker Compose で起動
docker-compose up

まだわかっていないこと

  • 日本語対応: 現時点では英語のみ対応。日本語音声の入出力は未確認

  • ファインチューニング: 独自の声やペルソナを追加する方法は公開されていない

  • 量子化: メモリ削減のための量子化バージョンは未提供

まとめ

NVIDIA PersonaPlex は、「聞きながら話す」ができる次世代の音声AIモデルです。テキストプロンプトで役割を、ボイスプロンプトで声質を指定できるので、カスタマーサポートから雑談相手まで、さまざまな用途に活用できます。

Google Colab のオフラインモードでは、リアルタイム対話こそ体験できませんが、音声入力から応答音声を生成する基本的なワークフローを試すことができました。

📊 実験サマリーの表示と保存

def print_experiment_summary():
    print("\n" + "=" * 60)
    print("📊 実験サマリー")
    print("=" * 60)

    total_time = sum(e.get('elapsed_seconds', 0) for e in experiment_log)
    print(f"\n📈 総実験数: {len(experiment_log)}")
    print(f"⏱️ 総実行時間: {total_time:.2f}秒")

    print("\n📝 実験一覧:")
    for i, entry in enumerate(experiment_log, 1):
        exp_name = entry.get('experiment', entry.get('task', 'N/A'))
        elapsed = entry.get('elapsed_seconds', '-')
        print(f"  {i}. {exp_name}: {elapsed}秒")

    print("=" * 60)

print_experiment_summary()
save_experiment_log("personaplex")
print("\n🎉 すべての実験が完了しました!")

もっと詳しく知りたい人へ

参考にした資料

  1. PersonaPlex GitHub リポジトリ - NVIDIA, 2026

  2. PersonaPlex HuggingFace モデルカード - NVIDIA, 2026

  3. PersonaPlex プロジェクトページ - NVIDIA Research, 2026

  4. PersonaPlex 論文プレプリント - ICASSP 2026 採択

関連技術

  • Moshi (Kyutai) - PersonaPlex のベースとなった全二重音声モデル

  • Helium LLM - PersonaPlex の言語モデルバックボーン

📒 Google Colab ノートブック

本記事のコードはすべてGoogle Colabで実行できます。

https://colab.research.google.com/drive/1oGtinc0RVFU7nbYUKQLWpWyKxo5JkSdP?usp=sharing


使い方


  1. 上のボタンをクリックしてノートブックを開く

  2. ランタイム → ランタイムのタイプを変更 → A100 GPU を選択

  3. HuggingFace でライセンスに同意し、トークンをシークレットに設定

  4. 記事の各コードブロックを順番に実行するだけ!

💡 ヒント A100 GPU を使用するには Colab Pro+ が必要です。T4 GPU では VRAM 不足でエラーになる可能性があります。

参考リンク

この記事は2026年1月23日時点の情報です。

いいなと思ったら応援しよう!

コメント

コメントするには、 ログイン または 会員登録 をお願いします。
NVIDIA PersonaPlex を Google Colab で試す!リアルタイム全二重音声AI入門( 📒Google Colab ノートブック付)|Maki@Sunwood.ai.labs
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1