0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GSPO (Group Sequence Policy Optimization) をVERLを使って実装する

0
Last updated at Posted at 2025-10-31

※下記のコードはチームメンバーのある方(後日公開予定)と協力し作成したものになります。

概要

DeepSeek-R1推論用の環境をコピーしてGSPO用環境を構築し、ByteDanceのverlを用いてGSPOを実行する手順です。本コードではフルパラメータチューニングで行っています。
※LoRAを用いる場合はこちら「GSPO (Group Sequence Policy Optimization) をVERLを使って実装する~LoRA編~
※既存環境をコピーしているのは、DeepSeek-R1でのGSPO実行を想定していたためです。

GSPOはMixture-of-Experts (MoE) モデルでGRPOよりも安定した学習ができると主張されている強化学習手法です。実際に、提案プレプリントで紹介されていたように、GRPOをよりも素早くLossが落ちることを確認済みです。

提案プレプリントからの抜粋した図
↑ 提案プレプリントからの抜粋した図

参考文献)

前提条件

Part 1: 環境構築

Step 1: 8GPUノードを取得

srun --partition=××× \\
     --nodes=1 --gpus-per-node=8 \\
     --cpus-per-task=240 --time=04:00:00 \\
     --job-name="gspo_setup" --pty bash -i


Step 2: DeepSeek環境をコピー

# モジュールのロード
module reset
module load nccl/2.22.3
module load hpcx/2.18.1-gcc-cuda12/hpcx-mt
module load miniconda/24.7.1-py311
source /home/appli/miniconda3/24.7.1-py311/etc/profile.d/conda.sh

# 環境のコピー
export DEEPSEEK_ENV="$HOME/conda_env_deepseek"
export GSPO_ENV="$HOME/conda_env_gspo"

# 既存のGSPO環境があれば削除
[ -d "$GSPO_ENV" ] && conda env remove --prefix "$GSPO_ENV" -y && rm -rf "$GSPO_ENV"

# DeepSeek環境をクローン
conda create --prefix "$GSPO_ENV" --clone "$DEEPSEEK_ENV" -y
conda activate "$GSPO_ENV"


Step 3: VERLインストールと依存関係の導入

cd ~/
mkdir -p deps
cd ~/deps

# 既存のVERLがあれば削除
[ -d "verl" ] && rm -rf verl

# VERLをクローン(GSPOサポート済み)
git clone <https://github.com/volcengine/verl.git>
cd verl

# 依存関係のインストール(cffiを追加)
pip install --no-cache-dir six regex deepspeed wandb \\
    huggingface_hub tensorboard mpi4py sentencepiece nltk ninja \\
    packaging wheel transformers accelerate safetensors einops peft \\
    datasets trl matplotlib sortedcontainers cffi

# VERLをインストール
pip install --no-deps -e .

# hydra-coreの追加(必須)
pip install hydra-core omegaconf

# Protobufバージョン固定
pip install --force-reinstall "protobuf==4.25.5"

cd ~/


Step 4: 【重要】依存関係のバージョンを固定

vllm, transformers, trlなどのライブラリ間の複雑な互換性問題を回避するため、以下のコマンドで**動作確認済みのバージョンの組み合わせ(ゴールデンセット)**を強制的にインストールします。←バージョン固定しないとエラーが延々と起きる

pip install --force-reinstall \\
    numpy==1.26.4 \\
    vllm==0.9.2 \\
    transformers==4.53.2 \\
    tokenizers==0.21.1 \\
    fsspec==2025.3.0 \\
    trl==0.20.0


Step 5: 環境変数の設定

# .bashrcに追加
cat >> ~/.bashrc << 'EOF'

# GSPO環境用
alias gspo-env='conda activate $HOME/conda_env_gspo'
export PYTHONPATH="$HOME/deps/verl:$PYTHONPATH"
EOF

source ~/.bashrc


Step 6: 環境の確認

以下のスクリプトを実行し、バージョンが一致することを確認します(一部警告が出ることがありますが、主要ライブラリのバージョンが合っていれば問題ありません)。

python - <<'PY'
import sys, torch, vllm, transformers, flash_attn
try:
    import verl
    verl_version = verl.__version__
except:
    verl_version = "Not installed"

print("=== GSPO環境検証 ===")
print("Python       :", sys.version.split()[0])
print("Torch        :", torch.__version__)
print("CUDA         :", torch.version.cuda)
print("FlashAttn    :", flash_attn.__version__)
print("vLLM         :", vllm.__version__)
print("VERL         :", verl_version)
print("Transformers :", transformers.__version__)
print("GPU count    :", torch.cuda.device_count())
print("✅ 環境構築完了!")
PY

【期待される出力バージョン】

  • vLLM: 0.9.2
  • Transformers: 4.53.2

Part 2: GSPO実行

Step 1: ログイン設定

huggingface-cliがユーザーのローカル領域を参照してエラーになる場合があるため、python -mを付けてConda環境内のコマンドを明示的に実行します。

# HuggingFaceログイン(トークンが必要)
python -m huggingface_hub.commands.huggingface_cli login

# WandBログイン(APIキーが必要)
python -m wandb login


Step 2: 実験ディレクトリ作成

mkdir -p ~/experiments/gspo/{checkpoints,logs}
cd ~/experiments/gspo


Step 3: GSPOトレーニングスクリプト作成

実行前にモデルをダウンロードしてください。また、下記のモデルは検証用にLlama-32.B-1B-Instructを用いているものの、Qwen-3-32Bでも動きました(記憶が正しければ...)。ただし、1ノードではOut-of-Memoryになるため、マルチノードで実行する必要があります。一方で、フルパラメータチューニングではなく、LoRAにすれば1ノードでGSPOを実行できました。
※LoRAを用いたコードはこちら「GSPO (Group Sequence Policy Optimization) をVERLを使って実装する~LoRA編~

cat > run_gspo.sh << 'EOF'
#!/bin/bash
# GSPO Training Script - Final Fix: Correct config location

# プロジェクトパスの設定
export PROJECT_BASE="/home/llm_project"
export MODEL_BASE="$PROJECT_BASE/models/base"
export DATA_BASE="$PROJECT_BASE/data/raw"
export MY_EXP_BASE="$HOME/experiments"

# ネットワーク設定
export NCCL_SOCKET_IFNAME=enp25s0np0
export NVTE_FUSED_ATTN=0
export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
export VLLM_ATTENTION_BACKEND=FLASH_ATTN
unset ROCR_VISIBLE_DEVICES
ulimit -v unlimited

# 警告を抑制
export PYTHONWARNINGS="ignore::UserWarning"

# WandB設定
export WANDB_ENTITY="YOUR_USERNAME"
export WANDB_PROJECT_NAME="llm_competition"
export WANDB_RUN_NAME="gspo_$(date +%Y%m%d_%H%M%S)"

echo "=== GSPO Training Configuration ==="
echo "Base Model: $MODEL_BASE/Llama-3.2-1B-Instruct"
echo "Data: $DATA_BASE/gsm8k"
echo "Output: $MY_EXP_BASE/gspo/checkpoints"
echo "=================================="

# データファイルの存在確認
if [ ! -f "$DATA_BASE/gsm8k/train.parquet" ]; then
    echo "Error: Training data not found at $DATA_BASE/gsm8k/train.parquet"
    exit 1
fi

PYTHONUNBUFFERED=1 python -m verl.trainer.main_ppo \
  algorithm.adv_estimator=grpo \
  actor_rollout_ref.actor.policy_loss.loss_mode=gspo \
  actor_rollout_ref.actor.loss_agg_mode=seq-mean-token-mean \
  data.train_files="$DATA_BASE/gsm8k/train.parquet" \
  data.val_files="$DATA_BASE/gsm8k/test.parquet" \
  data.train_batch_size=512 \
  data.max_prompt_length=512 \
  data.max_response_length=256 \
  data.dataloader_num_workers=0 \
  data.shuffle=true \
  actor_rollout_ref.rollout.name=vllm \
  actor_rollout_ref.rollout.mode=sync \
  actor_rollout_ref.rollout.dtype=bfloat16 \
  actor_rollout_ref.model.path="$MODEL_BASE/Llama-3.2-1B-Instruct" \
  actor_rollout_ref.model.use_remove_padding=true \
  actor_rollout_ref.actor.optim.lr=1e-6 \
  actor_rollout_ref.actor.ppo_mini_batch_size=128 \
  actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=8 \
  actor_rollout_ref.actor.use_kl_loss=false \
  actor_rollout_ref.actor.kl_loss_coef=0.0 \
  actor_rollout_ref.actor.entropy_coeff=0 \
  actor_rollout_ref.actor.clip_ratio_low=0.0003 \
  actor_rollout_ref.actor.clip_ratio_high=0.0004 \
  actor_rollout_ref.actor.use_dynamic_bsz=true \
  actor_rollout_ref.actor.entropy_checkpointing=true \
  actor_rollout_ref.rollout.n=16 \
  actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=16 \
  actor_rollout_ref.rollout.tensor_model_parallel_size=1 \
  actor_rollout_ref.rollout.gpu_memory_utilization=0.8 \
  actor_rollout_ref.rollout.temperature=1.0 \
  actor_rollout_ref.rollout.top_p=1.0 \
  actor_rollout_ref.rollout.top_k=-1 \
  actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=8 \
  algorithm.use_kl_in_reward=false \
  reward_model.reward_manager=dapo \
  +reward_model.reward_kwargs.overlong_buffer_cfg.enable=false \
  +reward_model.reward_kwargs.overlong_buffer_cfg.len=128 \
  +reward_model.reward_kwargs.overlong_buffer_cfg.penalty_factor=1.0 \
  +reward_model.reward_kwargs.overlong_buffer_cfg.log=false \
  +reward_model.reward_kwargs.max_resp_len=256 \
  trainer.critic_warmup=0 \
  trainer.logger="['console','wandb']" \
  trainer.val_before_train=false \
  trainer.n_gpus_per_node=8 \
  trainer.nnodes=1 \
  trainer.save_freq=10 \
  trainer.test_freq=10 \
  trainer.default_local_dir="$MY_EXP_BASE/gspo/checkpoints" \
  trainer.project_name="$WANDB_PROJECT_NAME" \
  trainer.experiment_name="$WANDB_RUN_NAME" \
  trainer.total_epochs=10 \
  trainer.total_training_steps=500 \
  2>&1 | tee "gspo_training_$(date +%Y%m%d_%H%M%S).log"
EOF

chmod +x run_gspo.sh


Step 4: WandBユーザー名の設定

# WandBユーザー名を実際の値に変更(重要!)
sed -i 's/YOUR_USERNAME/あなたのWandBユーザー名/' run_gspo.sh


Step 5: トレーニング実行

# Rayのクリーンアップ(念のため)
ray stop

# GSPOトレーニング開始
./run_gspo.sh


本プロジェクトは、国立研究開発法人新エネルギー・産業技術総合開発機構(「NEDO」)の「日本語版医療特化型LLMの社会実装に向けた安全性検証・実証」における基盤モデルの開発プロジェクトの一環として行われました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?