トレーニング・ファインチューニング
FunASR のトレーニングフレームワークを使用して、自分のデータで事前学習モデルをファインチューニングできます。
概要
データ準備
Paraformer のファインチューニング
SenseVoice のファインチューニング
Fun-ASR-Nano のファインチューニング
パラメータリファレンス
マルチ GPU トレーニング
DeepSpeed
モニタリング
ファインチューニング済みモデルの使用
ヒント・トラブルシューティング
概要
FunASR のトレーニングフレームワークは以下をサポートしています:
- カスタムドメインデータで任意の事前学習モデルをファインチューニング
- PyTorch DDP によるマルチ GPU トレーニング(シングルノード/マルチノード)
- DeepSpeed ZeRO Stage 1/2/3 による大規模モデルのトレーニング
- トークン数またはサンプル数による動的バッチング
- 最高性能のためのチェックポイント平均化
- 中断後のトレーニング再開
トレーニングのエントリポイントは funasr-train-ds(funasr/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}
| フィールド | 型 | 説明 |
|---|---|---|
key | str | 一意の発話ID |
source | str | 音声ファイルパス(ローカルパスまたはURL) |
source_len | int | 音声の長さ(fbankフレーム数、1フレーム = 10ms) |
target | str | 書き起こしテキスト |
target_len | int | テキストのトークン数 |
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_length | fbank フレーム数(各 10ms) |
text_length | トークン数(Qwen3-0.6B トークナイザーで計算) |
プロンプトのバリエーション:
• 中国語:
• 英語:
• 言語をまたぐ変換:
• ITN なし:
• 中国語:
语音转写:• 英語:
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 との主な違い:
- ChatML データフォーマットを使用(上記参照)
++trust_remote_code=trueが必要- 選択的フリーズをサポート:encoder/adaptor をフリーズし、LLM decoder のみトレーニング
# 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_size | 6000 | token モード:バッチあたりの総フレーム数。example モード:サンプル数。 |
dataset_conf.sort_size | 1024 | 長さベースソートのバッファサイズ(パディング効率を向上)。 |
dataset_conf.num_workers | 4 | データ読み込みスレッド数。 |
dataset_conf.data_split_num | 1 | 大規模トレーニング用にデータを N 分割(メモリ削減)。 |
dataset_conf.max_token_length | — | フィルタ:この値を超えるサンプルをスキップ(フレーム/トークン単位)。 |
dataset_conf.min_token_length | — | フィルタ:この値に満たないサンプルをスキップ。 |
トレーニングパラメータ
| パラメータ | デフォルト | 説明 |
|---|---|---|
train_conf.max_epoch | 50 | 総トレーニングエポック数。 |
train_conf.log_interval | 1 | N ステップごとに loss を表示。 |
train_conf.validate_interval | 2000 | N ステップごとにバリデーション実行。 |
train_conf.save_checkpoint_interval | 2000 | N ステップごとにモデルを保存。 |
train_conf.keep_nbest_models | 20 | バリデーション精度が高い上位 N 個のモデルを保持。 |
train_conf.avg_nbest_model | 10 | 最良の N 個のモデルを平均化して最終チェックポイントを生成。 |
train_conf.resume | true | 前回のチェックポイントが存在する場合、自動的に再開。 |
train_conf.use_deepspeed | false | DeepSpeed ZeRO 最適化を有効化。 |
optim_conf.lr | 0.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:さらにパラメータを分割。最大のメモリ節約だが速度は低下。
• 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
注目すべき主要指標:
loss_avg_epoch:トレーニングの進行に伴い減少すべきacc_avg_epoch:継続的に上昇すべき(最重要指標)lr:現在のステップでの学習率GPU memory:ピーク値が GPU の VRAM を超えないこと
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(メモリ不足)
dataset_conf.batch_sizeを減らすdataset_conf.max_token_length=2000を追加して長い発話をフィルタ- DeepSpeed を有効にする(オプティマイザ状態を分割)
dataset_conf.num_workersを減らす
loss が下がらない / 勾配が NaN
- 学習率を下げる(0.00005 を試す)
- データ品質を確認——破損した音声ファイルは NaN の原因になる
- Fun-ASR-Nano の場合:encoder をフリーズした状態から始める
バリデーション精度が向上しない
- トレーニングデータを増やす(ファインチューニングには最低約 10 時間必要)
- ドメインの一致を確認——大きく異なるドメインでは汎化しにくい
- 段階的により多くのレイヤーをアンフリーズする
大規模データ(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 内の最新チェックポイントから自動的に再開されます。