トレーニング・ファインチューニング

FunASR のトレーニングフレームワークを使用して、自分のデータで事前学習モデルをファインチューニングできます。

概要

FunASR のトレーニングフレームワークは以下をサポートしています:

トレーニングのエントリポイントは funasr-train-dsfunasr/bin/train_ds.py)で、分散トレーニングのために torchrun を通じて起動します。

データ準備

標準フォーマット(Paraformer、SenseVoice)

トレーニングデータは JSONL フォーマットを使用します——1行に1つの JSON オブジェクト:

{"key": "utt001", "source": "/path/to/audio.wav", "source_len": 90, "target": "これは書き起こしテキストです", "target_len": 6}
{"key": "utt002", "source": "/path/to/audio2.wav", "source_len": 150, "target": "hello world", "target_len": 2}
フィールド説明
keystr一意の発話ID
sourcestr音声ファイルパス(ローカルパスまたはURL)
source_lenint音声の長さ(fbankフレーム数、1フレーム = 10ms)
targetstr書き起こしテキスト
target_lenintテキストのトークン数

wav.scp + text.txt から生成

Kaldi 形式のデータファイルがある場合、以下のように変換できます:

# train_wav.scp(タブ区切り:id \t パス)
utt001  /data/audio/001.wav
utt002  /data/audio/002.wav

# train_text.txt(タブ区切り:id \t テキスト)
utt001  これは書き起こしテキストです
utt002  hello world
# jsonl に変換
scp2jsonl \
  ++scp_file_list='["/data/list/train_wav.scp", "/data/list/train_text.txt"]' \
  ++data_type_list='["source", "target"]' \
  ++jsonl_file_out="/data/list/train.jsonl"

ChatML フォーマット(Fun-ASR-Nano)

Fun-ASR-Nano は ChatML 会話フォーマットを使用します:

{"messages": [
  {"role": "system", "content": "You are a helpful assistant."},
  {"role": "user", "content": "语音转写:<|startofspeech|>!/path/to/audio.wav<|endofspeech|>"},
  {"role": "assistant", "content": "几点了?"}
], "speech_length": 145, "text_length": 3}
フィールド説明
messages[0]システムプロンプト(固定:"You are a helpful assistant.")
messages[1]ユーザー入力:プロンプト + <|startofspeech|>!...<|endofspeech|> で囲んだ音声パス
messages[2]アシスタント応答:書き起こしテキスト
speech_lengthfbank フレーム数(各 10ms)
text_lengthトークン数(Qwen3-0.6B トークナイザーで計算)
プロンプトのバリエーション:
• 中国語:语音转写:
• 英語:Speech transcription:
• 言語をまたぐ変換:语音转写成英文:
• ITN なし:语音转写,不进行文本规整:

wav.scp + text.txt から変換:

python tools/scp2jsonl.py \
  ++scp_file=data/train_wav.scp \
  ++transcript_file=data/train_text.txt \
  ++jsonl_file=data/train_example.jsonl

Paraformer のファインチューニング

cd examples/industrial_data_pretraining/paraformer
bash finetune.sh

主要パラメータをカスタマイズする場合:

export CUDA_VISIBLE_DEVICES="0,1"
gpu_num=2

torchrun --nproc_per_node $gpu_num \
  funasr/bin/train_ds.py \
  ++model="iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch" \
  ++train_data_set_list="data/train.jsonl" \
  ++valid_data_set_list="data/val.jsonl" \
  ++dataset_conf.batch_size=6000 \
  ++dataset_conf.batch_type="token" \
  ++dataset_conf.num_workers=4 \
  ++train_conf.max_epoch=50 \
  ++train_conf.validate_interval=2000 \
  ++train_conf.save_checkpoint_interval=2000 \
  ++train_conf.keep_nbest_models=20 \
  ++train_conf.avg_nbest_model=10 \
  ++optim_conf.lr=0.0002 \
  ++output_dir="./outputs"

SenseVoice のファインチューニング

cd examples/industrial_data_pretraining/sense_voice
bash finetune.sh

データフォーマットは Paraformer と同じ(source/target JSONL)です。違いは、SenseVoice が内部的に独自の dataset クラスを使用する点です。

Fun-ASR-Nano のファインチューニング

cd examples/industrial_data_pretraining/fun_asr_nano
bash finetune.sh

Paraformer との主な違い:

# encoder + adaptor をフリーズし、LLM のみトレーニング(ドメイン適応に推奨)
++audio_encoder_conf.freeze=true
++audio_adaptor_conf.freeze=true
++llm_conf.freeze=false

# 全パラメータファインチューニング
++audio_encoder_conf.freeze=false
++audio_adaptor_conf.freeze=false
++llm_conf.freeze=false
推奨戦略:まず LLM のみのファインチューニングから始めてください(高速・少量データで可能)。結果が不十分な場合は adaptor をアンフリーズ。encoder のアンフリーズは非常に大量のデータ(1000時間超)がある場合のみ。

パラメータリファレンス

データセットパラメータ

パラメータデフォルト説明
dataset_conf.batch_type"token""token":総トークン数で動的バッチング。"example":固定サンプル数。
dataset_conf.batch_size6000token モード:バッチあたりの総フレーム数。example モード:サンプル数。
dataset_conf.sort_size1024長さベースソートのバッファサイズ(パディング効率を向上)。
dataset_conf.num_workers4データ読み込みスレッド数。
dataset_conf.data_split_num1大規模トレーニング用にデータを N 分割(メモリ削減)。
dataset_conf.max_token_lengthフィルタ:この値を超えるサンプルをスキップ(フレーム/トークン単位)。
dataset_conf.min_token_lengthフィルタ:この値に満たないサンプルをスキップ。

トレーニングパラメータ

パラメータデフォルト説明
train_conf.max_epoch50総トレーニングエポック数。
train_conf.log_interval1N ステップごとに loss を表示。
train_conf.validate_interval2000N ステップごとにバリデーション実行。
train_conf.save_checkpoint_interval2000N ステップごとにモデルを保存。
train_conf.keep_nbest_models20バリデーション精度が高い上位 N 個のモデルを保持。
train_conf.avg_nbest_model10最良の N 個のモデルを平均化して最終チェックポイントを生成。
train_conf.resumetrue前回のチェックポイントが存在する場合、自動的に再開。
train_conf.use_deepspeedfalseDeepSpeed ZeRO 最適化を有効化。
optim_conf.lr0.0002学習率。

マルチ GPU トレーニング

シングルマシン・マルチ GPU

export CUDA_VISIBLE_DEVICES="0,1,2,3"
gpu_num=4

torchrun --nnodes 1 --nproc_per_node $gpu_num \
  funasr/bin/train_ds.py ${train_args}

マルチマシン

# マシン 1(マスター、IP=192.168.1.1)
torchrun --nnodes 2 --node_rank 0 --nproc_per_node 4 \
  --master_addr=192.168.1.1 --master_port=12345 \
  funasr/bin/train_ds.py ${train_args}

# マシン 2
torchrun --nnodes 2 --node_rank 1 --nproc_per_node 4 \
  --master_addr=192.168.1.1 --master_port=12345 \
  funasr/bin/train_ds.py ${train_args}

DeepSpeed

大規模モデル(Fun-ASR-Nano 8億パラメータなど)の場合、DeepSpeed ZeRO を有効にします:

++train_conf.use_deepspeed=true
++train_conf.deepspeed_config=./deepspeed_conf/ds_stage1.json

Stage 1 設定(推奨の出発点):

{
  "train_micro_batch_size_per_gpu": 1,
  "gradient_accumulation_steps": 1,
  "bf16": {"enabled": true},
  "zero_optimization": {
    "stage": 1,
    "reduce_bucket_size": 5e8,
    "allgather_bucket_size": 5e8
  }
}
各 Stage の使い分け:
• Stage 1:オプティマイザ状態を分割。ほとんどのケースに最適。
• Stage 2:さらに勾配を分割。より大きなモデル向け。
• Stage 3:さらにパラメータを分割。最大のメモリ節約だが速度は低下。

モニタリング

ログファイル

tail -f outputs/log.txt

# 出力例:
# train, rank: 0, epoch: 0/50, step: 6990, (loss_avg_rank: 0.327),
# (acc_avg_epoch: 0.795), (lr: 1.165e-04),
# GPU memory: usage: 3.8GB, peak: 18.3GB

注目すべき主要指標:

TensorBoard

tensorboard --logdir outputs/log/tensorboard
# http://localhost:6006 を開く

ファインチューニング済みモデルの使用

outputs/ に configuration.json がある場合

from funasr import AutoModel
model = AutoModel(model="./outputs")
res = model.generate(input="test.wav")
print(res[0]["text"])

configuration.json がない場合

funasr ++model="./outputs" \
  ++config-path="./outputs" \
  ++config-name="config.yaml" \
  ++init_param="./outputs/model.pt" \
  ++input="test.wav"

ヒント・トラブルシューティング

トレーニング中の OOM(メモリ不足)

  1. dataset_conf.batch_size を減らす
  2. dataset_conf.max_token_length=2000 を追加して長い発話をフィルタ
  3. DeepSpeed を有効にする(オプティマイザ状態を分割)
  4. dataset_conf.num_workers を減らす

loss が下がらない / 勾配が NaN

バリデーション精度が向上しない

大規模データ(10,000 時間超)

メモリ問題を回避するためにデータ分割を使用:

# データをチャンクに分割し、一度に 2 つずつ読み込む
++dataset_conf.data_split_num=256
# data.list には分割された jsonl ファイルへのパスを記載:
# data/train.0.jsonl
# data/train.1.jsonl
# ...
++train_data_set_list="data/data.list"

クラッシュ後のトレーニング再開

++train_conf.resume=true を設定(デフォルトで有効)。トレーニングは output_dir 内の最新チェックポイントから自動的に再開されます。