XPhoneBERTを使って文章から音素列に変換する

初めに

TTSを行う際にg2pを使うことがよくあります。今回はbertを使った音素変換モデルを試します

以下にて試したリポジトリは公開しています。

github.com

開発環境

環境構築

pythonの環境を作り、以下のライブラリをインストールします

pip install torch --index-url https://download.pytorch.org/whl/cu126 transformers>=4.52.4 text2phonemesequence>=0.1.4 tokenizers>=0.21.1

実行

以下のスクリプトを実行します

from transformers import AutoModel, AutoTokenizer
from text2phonemesequence import Text2PhonemeSequence
import torch
import argparse
import sys

def parse_args():
    parser = argparse.ArgumentParser(description='XPhoneBERTを使用してテキストから音素表現を抽出します')
    parser.add_argument('--text', type=str, default="これ は 、 テスト テキスト です .",
                      help='処理するテキスト(単語分割済み)')
    parser.add_argument('--language', type=str, default='jpn',
                      help='言語コード(ISO 639-3)。デフォルトは日本語(jpn)')
    parser.add_argument('--force-cpu', action='store_true',
                      help='GPUが利用可能でも強制的にCPUを使用')
    parser.add_argument('--output-file', type=str,
                      help='特徴量を保存するファイルパス(指定しない場合は保存しない)')
    return parser.parse_args()

def main():
    args = parse_args()
    
    # デバイスの設定
    device = torch.device('cpu' if args.force_cpu or not torch.cuda.is_available() else 'cuda')
    print(f"Using device: {device}")

    try:
        # 1. XPhoneBERTモデルとそのトークナイザをロード
        print("Loading XPhoneBERT model and tokenizer...")
        xphonebert = AutoModel.from_pretrained("vinai/xphonebert-base").to(device)
        tokenizer = AutoTokenizer.from_pretrained("vinai/xphonebert-base")
        print("Model and tokenizer loaded.")

        # 2. Text2PhonemeSequenceをロード
        print(f"Loading Text2PhonemeSequence for {args.language}...")
        text2phone_model = Text2PhonemeSequence(language=args.language, is_cuda=device.type == 'cuda')
        print("Text2PhonemeSequence loaded.")

        # 3. 入力テキストの処理
        print(f"Input text: {args.text}")

        # 4. テキストを音素シーケンスに変換
        print("Converting text to phoneme sequence...")
        input_phonemes = text2phone_model.infer_sentence(args.text)
        print(f"Phoneme sequence: {input_phonemes}")

        # 5. 音素シーケンスをトークナイズ
        print("Tokenizing phoneme sequence...")
        input_ids = tokenizer(input_phonemes, return_tensors="pt").to(device)
        print(f"Input IDs shape: {input_ids['input_ids'].shape}")

        # 6. XPhoneBERTで特徴量抽出
        print("Extracting features with XPhoneBERT...")
        with torch.no_grad():
            features = xphonebert(**input_ids)
        print("Features extracted.")
        print(f"Output features (last hidden state shape): {features.last_hidden_state.shape}")

        # 特徴量の保存(オプション)
        if args.output_file:
            print(f"Saving features to {args.output_file}...")
            torch.save(features.last_hidden_state.cpu(), args.output_file)
            print("Features saved.")

    except Exception as e:
        print(f"Error: {str(e)}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()

# features.last_hidden_state に音素ごとの表現が含まれます。
# features.pooler_output にはシーケンス全体の集約表現が含まれます(BERTの場合)。

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

Using device: cuda
Loading XPhoneBERT model and tokenizer...
Some weights of RobertaModel were not initialized from the model checkpoint at vinai/xphonebert-base and are newly initialized: ['pooler.dense.bias', 'pooler.dense.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Model and tokenizer loaded.
Loading Text2PhonemeSequence for jpn...
'wget' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
Text2PhonemeSequence loaded.
Input text: これ は 、 テスト テキスト です .
Converting text to phoneme sequence...
Phoneme sequence: k o ɾ e ▁ h a ▁ ɕ i ▁ t e s ɯ t o ▁ t e k i s ɯ t o ▁ d e s ɯ ▁ .
Tokenizing phoneme sequence...
Input IDs shape: torch.Size([1, 35])
Extracting features with XPhoneBERT...
Features extracted.
Output features (last hidden state shape): torch.Size([1, 35, 768])