yousanのメモ (original) (raw)

初めに

Flow MatchingとDiffusion Transformer(DiT)を基盤にした完全な非自己回帰型テキスト音声合成(TTS)である F5-TTSが公開されています

以下 ライブラリの詳細です( ReadMeより ChatGPTから生成したものです)

構成要素の概要

エンコーダー(Encoder)

エンコーダーは、テキスト入力を音声に変換するための特徴表現を生成する役割を果たします。このシステムでは、ConvNeXTブロックを使用して、テキスト入力を時間的に調整された音声特徴と一致させることができます。この部分は従来のTTSで使われていた複雑な設計(例: Duration Model, Text Encoder, Phoneme Alignment)を排除し、単にテキストに「Filler Tokens」をパディングして音声の長さに合わせるという簡単な手法を取っています。これにより、モデルはテキストと音声のアライメントを簡単に行えるようにしています。

デコーダー(Decoder)

デコーダーは、エンコーダーからの特徴表現を用いて最終的な音声を生成します。このシステムでは、DiTブロック(Diffusion Transformer)がその役割を担います。DiTブロックは、予測されたフローを基に音声を生成するための時間ステップごとのスケーリングや変換を行います。このプロセスでは、時間ステップ ( t \sim U[0,1] ) から始まり、入力データを次々と処理していく「拡散プロセス」が適用されます。

ボコーダーVocoder

ボコーダーは、最終的に生成された音声スペクトログラムを実際の波形データに変換する部分です。このシステムでは、「ODE Solver」というプロセスを通じて、生成された音声メルスペクトログラムを波形データに変換します。これにより、生成されたメルスペクトログラムを直接人間の耳に聞こえる音声に変換する役割を果たします。

特徴的な部分

Sway Sampling

推論時に適用されるサンプリング戦略で、既存のFlow Matchingベースのモデルにも再トレーニングなしで適用可能です。これにより、効率と性能が大幅に向上します。推論リアルタイムファクター(RTF)は0.15と、従来の拡散ベースのTTSモデルと比較して大幅に改善されています。

ConvNeXT

入力テキストの特徴抽出にはConvNeXTブロックを使用し、これは近年の画像処理モデルで高い性能を示しているアーキテクチャです。このモデルは、テキストと音声の特徴表現を精緻に整列させる役割を果たし、従来のテキストエンコーダーよりも効率的です。

レーニングと推論

レーニングデータ

システムは100K時間の多言語データセットでトレーニングされており、その結果、高い自然性や表現力を持つ音声を生成できます。また、コードスイッチング(異なる言語間での切り替え)や速度制御も可能で、幅広い用途に対応できます。

非自己回帰型TTS

非自己回帰型TTSは、自己回帰型のモデルと異なり、推論時に音声フレームを一度に並列生成するため、速度が速くなるという利点があります。このF5-TTSシステムは、従来の自己回帰型モデルと比べて大幅な速度向上を実現しています。

環境

準備

ライブラリのインストール

公式のリポジトリだと動かなかったので、以下で修正したものがあります

github.com

uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 uv pip install -r requirements.txt

事前学習モデルの配置

以下から各モデルをダウンロードして、画像のように配置します

huggingface.co

実行

以下でttsの推論をすることができます

uv run .\test_infer_single.py

使用する事前学習モデルを変更する場合は、以下の箇所を変更します

exp_name = "E2TTS_Base"

推論時の参照音声および生成するテキストを変更するには、以下の変更します

ref_audio = "tests/ref_audio/test_en_1_ref_short.wav" ref_text = "Some call me nature, others call me mother nature." gen_text = "I don't really care what you call me. I've been a silent spectator, watching species evolve, empires rise and fall. But always remember, I am mighty and enduring. Respect me and I'll nurture you; ignore me and you shall face the consequences."

開発環境

詳細

crcmod(==1.7)をインストールする際に以下のエラーが出ました。

error: Failed to prepare distributions Caused by: Failed to fetch wheel: crcmod==1.7 Caused by: Failed to run C:\Users\name\AppData\Local\uv\cache\builds-v0\.tmpv87Zlw\Scripts\python.exe Caused by: stream did not contain valid UTF-8

以下で解決できます

$Env:PYTHONUTF8 = "1"

初めに

査読前の論文がアップロードされている arXivを気になったものを見ているものの気になるものを全て探すのは大変なので API経由で検索をしていきます

記事の内容のリポジトリは以下で公開しています

github.com

開発環境

詳細

やりたいこと* 気になる特定のトピックの中から最新のものを探す * タイトルおよびSummaryを見る

以下で最新の内容からトピックを探してきます

import urllib.parse import feedparser

query = 'all:"LLM" OR all:"Text to Speech" OR all:"Speech to Text" OR all:"AI Character"'

encoded_query = urllib.parse.quote(query)

base_url = 'http://export.arxiv.org/api/query?'

params = { 'search_query': encoded_query, 'start': 0,
'max_results': 10,
'sortBy': 'submittedDate',
'sortOrder': 'descending', }

query_string = '&'.join(f'{key}={value}' for key, value in params.items())

url = base_url + query_string

feed = feedparser.parse(url)

for entry in feed.entries: title = entry.title summary = entry.summary.replace('\n', ' ')
print(f'タイトル: {title}') print(f'要約: {summary}') print('-' * 80)

結果は以下のようになります

タイトル: Adaptive Inference-Time Compute: LLMs Can Predict if They Can Do Better, Even Mid-Generation 要約: Inference-time computation is a powerful paradigm to enhance the performance of large language models (LLMs), with Best-of-N sampling being a widely used technique. However, this method is computationally expensive, requiring both (1) an external reward model and (2) the generation of multiple samples. In this work, we introduce a new generative self-evaluation scheme designed to adaptively reduce the number of generated samples while maintaining or even improving performance. We use a generative reward model formulation, allowing the LLM to predict mid-generation the probability that restarting the generation will yield a better response. These predictions are obtained without an external reward model and can be used to decide whether or not to generate more samples, prune unpromising samples early on, or to pick the best sample. This capability is very inexpensive as it involves generating a single predefined token. Trained using a dataset constructed with real unfiltered LMSYS user prompts, Llama 3.1 8B's win rate against GPT-4 on AlpacaEval increases from 21% to 34% with 16 samples and math performance on GSM8K improves from 84% to 91%. By sampling only when the LLM determines that it is beneficial to do so and adaptively adjusting temperature annealing, we demonstrate that 74% of the improvement from using 16 samples can be achieved with only 1.2 samples on average. We further demonstrate that 50-75% of samples can be pruned early in generation with minimal degradation in performance. Overall, our methods enable more efficient and scalable compute utilization during inference for LLMs.

はじめに

Singularity は、高パフォーマンスコンピューティング(HPC)環境でのコンテナ実行を可能にするコンテナプラットフォームです。WSL 上で Singularity を利用することで、Linux コンテナを Windows 環境で動作させることができます。

本記事では、WSL(Ubuntu 20.04)上で SingularityCE 4.2.1 をソースからビルドしてインストールする手順になります。

開発環境

インストール方法

必要なパッケージのインストール

まず、Singularity のビルドに必要なパッケージをインストールします。

sudo apt-get update sudo apt-get install -y
build-essential
libssl-dev
uuid-dev
libgpgme-dev
squashfs-tools
libseccomp-dev
wget
pkg-config
git
cryptsetup
libglib2.0-dev
libfuse-dev
libfuse3-dev
libjson-c-dev

Go 言語のインストール

既存の Go の削除(必要な場合)

sudo rm -rf /usr/local/go

最新の Go のダウンロードとインストール

wget https://go.dev/dl/go1.21.1.linux-amd64.tar.gz

sudo tar -C /usr/local -xzf go1.21.1.linux-amd64.tar.gz

rm go1.21.1.linux-amd64.tar.gz

環境変数を設定します

echo 'export GOPATH=${HOME}/go' >> ~/.bashrc echo 'export PATH=/usr/local/go/bin:${PATH}:${GOPATH}/bin' >> ~/.bashrc source ~/.bashrc

ここでインストールしたgoのversionを確認します

go version

以下の出力ができてきます

go version go1.21.1 linux/amd64

Singularity のソースコードのダウンロード

リポジトリからクローンします

git clone https://github.com/sylabs/singularity.git cd singularity

configの設定を使って処理を実行します

./mconfig

ビルドをしてインストールをします

make -C builddir sudo make -C builddir install

以下でインストール後の確認ができます

singularity --version

以下のような出力がされてたら成功です

singularity-ce version 4.2.1

エラー対応

  1. glib-2.0 headers are required to build conmon.

以下で追加のライブラリを入れます

sudo apt-get install libglib2.0-dev

参考サイト

zenn.dev

参考

(この記事はとても参考になっています)

zenn.dev

開発環境

内容

requirements.txt を使って ライブラリをインストールするときのメモ

pythonのversionの固定

以下で 特定のversionをインストールします

uv python install 3.9.20

以下で使用するversionを固定します

uv venv -p 3.9.20

ライブラリをインストール

以下で インストールをすることができます

uv pip sync requirements.txt

また以下をすることによってライブラリの依存を pyproject.toml に記載してくれます

uv add -r requirements.txt

初めに

fish-speech v1.4がリリースされたので、改めて触ってみます。

Excited to introduce Fish Speech 1.4 - now open-source and more powerful than ever! 🎉 Our mission is to make cutting-edge voice tech accessible to everyone.

What's new:
- Trained on 700k hours of multilingual data (up from 200k)
- Now supports 8 languages: English, Chinese,… pic.twitter.com/A9vS1yGuRR

— Fish Audio (@FishAudio) 2024年9月11日

前回 CLIで推論を行う記事を書いたので、今回は つくよみちゃん会話AI育成計画(会話テキストデータセット配布) を使って学習を進めていきます

ayousanz.hatenadiary.jp

環境

docker環境の作成

まずは fish-speechをcloneします。

https://github.com/fishaudio/fish-speech.git

docker環境が準備されているので、docker環境をビルドして立ち上げます。

docker-compose -f docker-compose.dev.yml up -d

ビルドしたdocker環境には以下のコマンドで入ることができます

docker exec -it fish-speech /bin/bash

音声データセットの準備

今回は つくよみちゃんデータセットを使うので、データセットをダウンロードしてきます。

次に 以下のようなフォルダ構成を作成します

/exp/ ├── data-raw/ │ ├── tsukuyomi-chan/

この中に音声ファイルを入れます。

ノーマライゼーション処理

はじめに 音声のノーマライゼーションを行います

fap loudness-norm data-raw data --clean

fapコマンドがない場合は、以下でインストールをします

pip install fish-audio-preprocess

(docker環境内でほかのライブラリと依存関係が一致しない場合は、docker外で別環境を作成してそちらでインストールを行うとうまく動きました。)

この処理が終わると /data に処理後の音声が保存されます。

ファイルによってはうまく処理がされない場合があるので、以下の修正を行ったverを作成しました。

github.com

必要に応じてこちらをcloseして以下でインストールを行い処理をします。

pip install -e .

文字お越しファイルの作成

/data の音声から それぞれの音声ファイルに対する .lab ファイルを作成します。

以下のコードで つくよみちゃんの文字起こしのテキストファイルと音声ファイルを使って labファイルを作成します。

import re import os import argparse

def process_text_file(input_file, output_dir):

if not os.path.exists(output_dir):
    os.makedirs(output_dir)

with open(input_file, 'r', encoding='utf-8') as f:
    lines = f.readlines()

for line in lines:
    
    if line.strip() == "":
        continue

    match = re.match(r'(VOICEACTRESS100_\d+):(.+)', line.strip())
    if match:
        file_name = match.group(1) + '.lab'
        content = match.group(2).strip()
    else:
        
        file_name = f'line_{lines.index(line) + 1}.lab'
        content = line.strip()
    
    
    with open(os.path.join(output_dir, file_name), 'w', encoding='utf-8') as out_file:
        out_file.write(content)

def main(): parser = argparse.ArgumentParser(description='Generate .lab files from input text file.') parser.add_argument('input_file', help='Path to the input text file') parser.add_argument('--output_dir', default='output', help='Path to the output directory (default: output)')

args = parser.parse_args()

process_text_file(args.input_file, args.output_dir)

if name == "main": main()

実行は以下のコマンドで実行することができます。

python generate_lab_files.py input_text.txt --output_dir custom_output

セマンティックトークンのバッチ抽出

VQGANの重みをダウンロードをダウンロードします。

huggingface-cli download fishaudio/fish-speech-1.4 --local-dir checkpoints/fish-speech-1.4

セマンティックトークンを抽出します。

python tools/vqgan/extract_vq.py data
--num-workers 1 --batch-size 16
--config-name "firefly_gan_vq"
--checkpoint-path "checkpoints/fish-speech-1.4/firefly-gan-vq-fsq-8x1024-21hz-generator.pth"

データセットをprotobufにパック

以下でデータセットからprotobufを生成します。

python tools/llama/build_dataset.py
--input "data"
--output "data/protos"
--text-extension .lab
--num-workers 16

LoRAの学習

LLAMAの重みをダウンロードします。

huggingface-cli download fishaudio/fish-speech-1.4 --local-dir checkpoints/fish-speech-1.4

docker環境で学習をする場合、shm_sizeが小さいとエラーになるため docker_compose.dev.yml に以下を追加します。

shm_size: 30gb

追加後は以下にようになります

services: fish-speech: build: . container_name: fish-speech shm_size: 30gb

batchサイズやデータの並列処理部分のパラメータは text2semantic_finetune.yaml の 以下で変更することができます。

num_workers: 2 batch_size: 4

学習時のステップ数や保存するタイミングは以下のパラメータになります。

max_steps: 5000 limit_val_batches: 1000 val_check_interval: 1000

以下でLoRAの学習を開始することができます。 my_voice_project は任意のプロジェクト名を指定してください。

python fish_speech/train.py --config-name text2semantic_finetune
project=my_voice_project
+lora@model.model.lora_config=r_8_alpha_16

学習後のLoRAの重みを変換

学習が終わった後に生成されるファイルは .ckpt というファイルになっているため、こちらを .pth に変換する必要があります。

以下のコマンドで重みの変換を行います。

python tools/llama/merge_lora.py
--lora-config r_8_alpha_16
--base-weight checkpoints/fish-speech-1.4
--lora-weight results/$project/checkpoints/step_000000010.ckpt
--output checkpoints/fish-speech-1.4-yth-lora/

学習後のモデルを使って推論

音声推論をする場合は、CLIとWeb版などいくつか選ぶことができます。

CLIは以下を参考にしてください。

ayousanz.hatenadiary.jp

今回は視覚的にわかりやすいWeb版を立ち上げていきます。

まずは必要なモデルをダウンロードします。

huggingface-cli download fishaudio/fish-speech-1.4 --local-dir checkpoints/fish-speech-1.4

以下でWebUIを立ち上げることができます

python -m tools.webui
--llama-checkpoint-path "変換したLoRAの重みを保存したパス"
--decoder-checkpoint-path "checkpoints/fish-speech-1.4/firefly-gan-vq-fsq-8x1024-21hz-generator.pth"
--decoder-config-name firefly_gan_vq

備考

変更差分は以下にpushしています。

github.com

開発環境

モデルの構造確認

以下のコードでモデルの構造を確認できます

import torch from pathlib import Path import json from safetensors import safe_open import argparse

def load_model(model_path): if model_path.suffix == '.safetensors': with safe_open(model_path, framework="pt", device="cpu") as f: return {key: f.get_tensor(key) for key in f.keys()} elif model_path.suffix == '.pth': return torch.load(model_path, map_location='cpu') else: raise ValueError(f"Unsupported file format: {model_path.suffix}")

def analyze_model_structure(model_dict): sizes = set() important_shapes = {} for name, param in model_dict.items(): if isinstance(param, torch.Tensor): if len(param.shape) > 0: sizes.add(param.shape[-1]) if param.shape[-1] in [256, 512]: important_shapes[name] = param.shape return sizes, important_shapes

def analyze_models(model_paths, config_path): config_path = Path(config_path)

with open(config_path, 'r') as f:
    config = json.load(f)

all_sizes = set()
all_important_shapes = {}

for model_path in model_paths:
    model_path = Path(model_path)
    print(f"\nAnalyzing {model_path.name}:")

    model_dict = load_model(model_path)
    if 'model' in model_dict:
        model_dict = model_dict['model']

    sizes, important_shapes = analyze_model_structure(model_dict)
    all_sizes.update(sizes)
    all_important_shapes.update(important_shapes)

    print(f"Unique sizes found: {sorted(sizes)}")
    print("Important shapes (256 or 512):")
    for name, shape in important_shapes.items():
        print(f"  {name}: shape = {shape}")

print("\nOverall summary:")
print(f"All unique sizes found across models: {sorted(all_sizes)}")

print("\nImportant config information:")
print(f"Model name: {config.get('model_name', 'Not specified')}")
print(f"Version: {config.get('version', 'Not specified')}")
print(f"Gin channels: {config['model'].get('gin_channels', 'Not specified')}")
print(f"Hidden channels: {config['model'].get('hidden_channels', 'Not specified')}")

if name == "main": parser = argparse.ArgumentParser(description="Analyze VITS2 model files") parser.add_argument("config_path", type=str, help="Path to the config.json file") parser.add_argument("model_paths", type=str, nargs='+', help="Paths to the model files (.pth or .safetensors)")

args = parser.parse_args()

analyze_models(args.model_paths, args.config_path)

以下のようにモデルの構造を確認することができます

python .\analyze_vits2_model_structure.py .\model_assets\test\config.json .\model_assets\test\test_e1000_s25000.safetensors

Analyzing test_e1000_s25000.safetensors: Unique sizes found: [1, 2, 3, 5, 7, 8, 11, 16, 29, 32, 64, 96, 128, 192, 256, 384, 512, 768] Important shapes (256 or 512): dec.cond.bias: shape = torch.Size([512]) dec.conv_pre.bias: shape = torch.Size([512]) dec.resblocks.0.convs1.0.bias: shape = torch.Size([256]) dec.resblocks.0.convs1.1.bias: shape = torch.Size([256]) dec.resblocks.0.convs1.2.bias: shape = torch.Size([256]) dec.resblocks.0.convs2.0.bias: shape = torch.Size([256]) dec.resblocks.0.convs2.1.bias: shape = torch.Size([256]) dec.resblocks.0.convs2.2.bias: shape = torch.Size([256]) dec.resblocks.1.convs1.0.bias: shape = torch.Size([256]) dec.resblocks.1.convs1.1.bias: shape = torch.Size([256]) dec.resblocks.1.convs1.2.bias: shape = torch.Size([256]) dec.resblocks.1.convs2.0.bias: shape = torch.Size([256]) dec.resblocks.1.convs2.1.bias: shape = torch.Size([256]) dec.resblocks.1.convs2.2.bias: shape = torch.Size([256]) dec.resblocks.2.convs1.0.bias: shape = torch.Size([256]) dec.resblocks.2.convs1.1.bias: shape = torch.Size([256]) dec.resblocks.2.convs1.2.bias: shape = torch.Size([256]) dec.resblocks.2.convs2.0.bias: shape = torch.Size([256]) dec.resblocks.2.convs2.1.bias: shape = torch.Size([256]) dec.resblocks.2.convs2.2.bias: shape = torch.Size([256]) dec.ups.0.bias: shape = torch.Size([256]) dp.conv_1.bias: shape = torch.Size([256]) dp.conv_2.bias: shape = torch.Size([256]) dp.norm_1.beta: shape = torch.Size([256]) dp.norm_1.gamma: shape = torch.Size([256]) dp.norm_2.beta: shape = torch.Size([256]) dp.norm_2.gamma: shape = torch.Size([256]) emb_g.weight: shape = torch.Size([1, 512]) enc_p.encoder.spk_emb_linear.weight: shape = torch.Size([192, 512]) enc_p.style_proj.weight: shape = torch.Size([192, 256]) flow.flows.0.enc.spk_emb_linear.weight: shape = torch.Size([192, 512]) flow.flows.2.enc.spk_emb_linear.weight: shape = torch.Size([192, 512]) flow.flows.4.enc.spk_emb_linear.weight: shape = torch.Size([192, 512]) flow.flows.6.enc.spk_emb_linear.weight: shape = torch.Size([192, 512])

Overall summary: All unique sizes found across models: [1, 2, 3, 5, 7, 8, 11, 16, 29, 32, 64, 96, 128, 192, 256, 384, 512, 768]

Important config information: Model name: nadeko Version: 2.4.1-JP-Extra Gin channels: 512 Hidden channels: 192