Compare commits

..

1 Commits

Author SHA1 Message Date
Siming Dai
2288eb0c21 Update aistudio-sdk requirement 2025-06-04 13:20:52 +08:00
277 changed files with 4487 additions and 32556 deletions

View File

@@ -1,41 +0,0 @@
name: "Check bypass"
description: "A custom action to encapsulate PFCCLab/ci-bypass"
inputs:
github-token:
description: "GitHub token"
required: true
workflow-name:
description: "Workflow name"
required: true
outputs:
can-skip:
description: "Whether the workflow can be skipped."
value: ${{ steps.check-bypass.outputs.can-skip }}
runs:
using: "composite"
steps:
- id: check-bypass
name: Check Bypass
env:
CI_TEAM_MEMBERS: '["tianshuo78520a", "swgu98", "risemeup1", "XieYunshen","luotao1","From00"]'
uses: PFCCLab/ci-bypass@v1
with:
github-token: ${{ inputs.github-token }}
non-pull-request-event-strategy: 'never-skipped'
type: 'composite'
composite-rule: |
{
"any": [
{
"type": "labeled",
"label": ["skip-ci: ${{ inputs.workflow-name }}", "skip-ci: all"],
"username": ${{ env.CI_TEAM_MEMBERS }}
},
{
"type": "commented",
"comment-pattern": [".*/skip-ci ${{ inputs.workflow-name }}.*", ".*/skip-ci all.*"],
"username": ${{ env.CI_TEAM_MEMBERS }}
}
]
}

View File

@@ -1,30 +0,0 @@
name: 'Rerun Workflow'
description: 'Re-run GitHub Actions workflow for a given Pull Request'
inputs:
GITHUB_TOKEN:
description: 'GitHub token with repo scope'
required: true
OWNER:
description: 'Repository owner'
required: true
REPO:
description: 'Repository name'
required: true
PR_ID:
description: 'Pull Request ID'
required: true
JOB_NAME:
description: 'Job name to rerun'
required: true
runs:
using: 'composite'
steps:
- run: bash ./.github/actions/rerun-workflow/rerun.sh
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.GITHUB_TOKEN }}
OWNER: ${{ inputs.OWNER }}
REPO: ${{ inputs.REPO }}
PR_ID: ${{ inputs.PR_ID }}
JOB_NAME: ${{ inputs.JOB_NAME }}

View File

@@ -1,77 +0,0 @@
# Copyright (c) 2025 PaddleNLP Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
COMMIT_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_ID" | jq -r '.head.sha')
echo "Commit SHA: $COMMIT_SHA"
response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$OWNER/$REPO/actions/runs?head_sha=$COMMIT_SHA&per_page=100")
echo "Response: $response"
run_ids=$(echo "$response" | jq -r '.workflow_runs[].id')
if [ -n "$run_ids" ]; then
echo "Found run_ids for commit $COMMIT_SHA: $run_ids"
for run_id in $run_ids; do
if [ "$JOB_NAME" = "all-failed" ]; then
echo "Rerunning all failed jobs for run_id: $run_id"
rerun_response=$(curl -X POST -s -w "%{http_code}" -o /dev/null \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/repos/$OWNER/$REPO/actions/runs/$run_id/rerun-failed-jobs")
if [ "$rerun_response" -eq 201 ]; then
echo "Successfully requested rerun for all blocked jobs in run_id: $run_id"
else
echo "Failed to request rerun for run_id: $run_id with status code $rerun_response"
fi
else
jobs_response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$OWNER/$REPO/actions/runs/$run_id/jobs")
echo "Jobs Response for run_id $run_id: $jobs_response"
# if [[ "$JOB_NAME" == *"bypass"* ]]; then
block_jobs=$(echo "$jobs_response" | jq -r --arg job_name "$JOB_NAME" \
'.jobs[] | select(.name == $job_name) | .id')
# else
# block_jobs=$(echo "$jobs_response" | jq -r --arg job_name "$JOB_NAME" \
# '.jobs[] | select(.name == $job_name and .conclusion != "success") | .id')
# fi
if [ -n "$block_jobs" ]; then
echo "Found block jobs for run_id $run_id: $block_jobs"
for job_id in $block_jobs; do
echo "Rerunning job_id: $job_id"
curl -X POST -H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$OWNER/$REPO/actions/jobs/$job_id/rerun"
done
else
echo "No block jobs found for run_id $run_id with name $JOB_NAME."
fi
fi
done
else
echo "No matching workflow runs found for commit $COMMIT_SHA."
exit 1
fi

View File

@@ -1,47 +0,0 @@
name: PaddleFormers PR Check
on:
pull_request:
types: [opened, synchronize]
env:
BRANCH: ${{ github.base_ref }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_ID: ${{ github.event.pull_request.number }}
COMMIT_ID: ${{ github.event.pull_request.head.sha }}
token: ${{ vars.ACTION_GITHUB_TOKEN }}
jobs:
check-approvers:
name: Check approval
runs-on:
group: APPROVAL
steps:
- name: Cleanup
run: |
rm -rf * .[^.]*
- name: Update paddle
run: |
wget -q --no-proxy https://xly-devops.bj.bcebos.com/PaddleTest/PaddleNLP/PaddleNLP-develop.tar.gz --no-check-certificate
tar zxf PaddleNLP-develop.tar.gz --strip-components=1 >/dev/null
rm -rf PaddleNLP-develop.tar.gz >/dev/null
git fetch origin pull/${PR_ID}/head
git checkout -b origin_pr FETCH_HEAD
git remote add upstream https://github.com/PaddlePaddle/PaddleNLP.git
git fetch upstream
git checkout -b test_pr upstream/${BRANCH}
git merge --no-edit origin_pr
git log --pretty=oneline -10
- name: Check bypass
id: check-bypass
uses: ./.github/actions/check-bypass
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
workflow-name: approval
- name: Display Required Approvers
if: steps.check-bypass.outputs.can-skip != 'true'
run: |
cd scripts/ci_approval
bash run_ci_approval_cherry-pick.sh

View File

@@ -1,47 +0,0 @@
name: Approval
on:
pull_request:
types: [opened, synchronize]
env:
BRANCH: ${{ github.base_ref }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_ID: ${{ github.event.pull_request.number }}
COMMIT_ID: ${{ github.event.pull_request.head.sha }}
token: ${{ vars.ACTION_GITHUB_TOKEN }}
jobs:
check-approvers:
name: Check approval
runs-on:
group: APPROVAL
steps:
- name: Cleanup
run: |
rm -rf * .[^.]*
- name: Update paddle
run: |
wget -q --no-proxy https://xly-devops.bj.bcebos.com/PaddleTest/PaddleNLP/PaddleNLP-develop.tar.gz --no-check-certificate
tar zxf PaddleNLP-develop.tar.gz --strip-components=1 >/dev/null
rm -rf PaddleNLP-develop.tar.gz >/dev/null
git fetch origin pull/${PR_ID}/head
git checkout -b origin_pr FETCH_HEAD
git remote add upstream https://github.com/PaddlePaddle/PaddleNLP.git
git fetch upstream
git checkout -b test_pr upstream/${BRANCH}
git merge --no-edit origin_pr
git log --pretty=oneline -10
- name: Check bypass
id: check-bypass
uses: ./.github/actions/check-bypass
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
workflow-name: approval
- name: Display Required Approvers
if: steps.check-bypass.outputs.can-skip != 'true'
run: |
cd scripts/ci_approval
bash -x run_ci_approval.sh

View File

@@ -1,52 +0,0 @@
on:
workflow_call:
inputs:
workflow-name:
required: true
type: string
secrets:
github-token:
required: true
outputs:
can-skip:
description: "Whether the workflow can be skipped."
value: ${{ jobs.check-bypass.outputs.can-skip }}
jobs:
check-bypass:
name: Check bypass
runs-on:
group: APPROVAL
permissions:
contents: read
env:
CI_TEAM_MEMBERS: '["tianshuo78520a", "swgu98", "risemeup1" , "XieYunshen","luotao1","From00"]'
outputs:
can-skip: ${{ steps.check-bypass.outputs.can-skip }}
steps:
- name: Cleanup
run: |
rm -rf * .[^.]*
- id: check-bypass
name: Check Bypass
uses: PFCCLab/ci-bypass@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
non-pull-request-event-strategy: 'never-skipped'
type: 'composite'
composite-rule: |
{
"any": [
{
"type": "labeled",
"label": ["skip-ci: ${{ inputs.workflow-name }}", "skip-ci: all"],
"username": ${{ env.CI_TEAM_MEMBERS }}
},
{
"type": "commented",
"comment-pattern": [".*/skip-ci ${{ inputs.workflow-name }}.*", ".*/skip-ci all.*"],
"username": ${{ env.CI_TEAM_MEMBERS }}
}
]
}

View File

@@ -1,80 +1,17 @@
name: Codestyle Check
name: Lint
on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
PR_ID: ${{ github.event.pull_request.number }}
COMMIT_ID: ${{ github.event.pull_request.head.sha }}
BRANCH: ${{ github.event.pull_request.base.ref }}
TASK: PaddleNLP-CI-Lint-${{ github.event.pull_request.number }}
jobs:
check-bypass:
name: Check bypass
uses: ./.github/workflows/check-bypass.yml
with:
workflow-name: 'lint'
secrets:
github-token: ${{ secrets.GITHUB_TOKEN }}
Lint:
name: Lint
needs: check-bypass
if: ${{ needs.check-bypass.outputs.can-skip != 'true' }}
runs-on: [self-hosted, ernie-cpu]
runs-on: ubuntu-24.04
steps:
- name: Run Container
env:
work_dir: ${{ github.workspace }}
python_version: "3.10"
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: checkout develop
run: |
container_name=${TASK}-$(date +%Y%m%d-%H%M%S)
echo "container_name=${container_name}" >> ${{ github.env }}
docker_image="iregistry.baidu-int.com/paddlecloud/base-images:paddlecloud-ubuntu20.04-gcc12.2-cuda12.3-cudnn9.0-nccl2.20.3.1-openmpi4.1.5-latest"
docker run -d -t --name ${container_name} --net=host -v /dev/shm:/dev/shm --shm-size=32G \
-v $work_dir/../../..:$work_dir/../../.. \
-v $work_dir:/workspace \
-v /home/.cache/pip:/home/.cache/pip \
-e BRANCH \
-e PR_ID \
-e COMMIT_ID \
-e work_dir \
-e no_proxy \
-e python_version \
-w /workspace ${docker_image}
- name: Download Code
env:
work_dir: ${{ github.workspace }}
run: |
docker exec -t ${container_name} /bin/bash -c '
rm -rf * .[^.]*
echo "Downloading PaddleNLP.tar"
wget -q --no-proxy https://paddle-qa.bj.bcebos.com/CodeSync/develop/PaddleNLP.tar --no-check-certificate
echo "Extracting PaddleNLP.tar"
tar xf PaddleNLP.tar && rm -rf PaddleNLP.tar
source $work_dir/../../../proxy
cd PaddleNLP
git config --global user.name "PaddleCI"
git config --global user.email "paddle_ci@example.com"
git pull
git submodule update --init --recursive --force
if [ -n "${PR_ID}" ]; then
git fetch origin pull/${PR_ID}/head
git checkout -b PR_${PR_ID} FETCH_HEAD
git remote add upstream https://github.com/PaddlePaddle/PaddleNLP.git
git fetch upstream ${BRANCH}
git merge ${BRANCH} --no-edit
git diff --numstat ${BRANCH} -- | awk "{print \$NF}"
else
echo "Not in a pull_request event. Skipping PR-specific operations."
fi
git log --pretty=oneline -10
if ! git show-ref --quiet refs/heads/develop; then \
echo "local develop branch is missing, creating local develop branch that tracks remote develop branch"
git fetch origin develop
@@ -82,32 +19,14 @@ jobs:
else
echo "local develop branch exist, skipping"
fi
'
- name: Setup Environment
- uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip' # caching pip dependencies
- name: Install dependencies
run: |
docker exec -t $container_name /bin/bash -c '
unlink /usr/local/bin/python
ln -sf $(which python${python_version}) /usr/local/bin/python
set -e
python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
python -m pip config set global.cache-dir "/home/.cache/pip"
source $work_dir/../../../proxy
python -m pip install --upgrade pip
cd /workspace/PaddleNLP && git config --global --add safe.directory $PWD
make install
'
- name: Test
run: |
docker exec -t $container_name /bin/bash -c '
set -e
source $work_dir/../../../proxy
cd /workspace/PaddleNLP
make lint
'
- name: Terminate And Delete the Container
if: always()
run: |
docker rm -f $container_name 2>/dev/null || true
- name: run the command
run: make lint

View File

@@ -1,207 +0,0 @@
# Disabled LLM CI workflow - manually enabled when needed
name: LLM CI (DISABLED)
#on:
# pull_request:
# types: [opened, synchronize, reopened]
# branches: [develop]
# schedule:
# - cron: "2 0 * * *"
# workflow_call:
# inputs:
# run_downstream:
# required: true
# type: string
# image_name:
# required: true
# type: string
#concurrency:
# group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
# cancel-in-progress: true
#env:
# PR_ID: ${{ github.event.pull_request.number }}
COMMIT_ID: ${{ github.event.pull_request.head.sha }}
TASK: paddlenlp-CI-${{ github.event.pull_request.number }}-llm
ci_scripts: /workspace/PaddleNLP/scripts/regression
BRANCH: ${{ github.event.pull_request.base.ref }}
AGILE_COMPILE_BRANCH: ${{ github.event.pull_request.base.ref }}
CI_name: llm-ci
no_proxy: "localhost,bj.bcebos.com,su.bcebos.com,bcebos.com,apiin.im.baidu.com,gitee.com,aliyun.com,.baidu.com,.tuna.tsinghua.edu.cn"
HF_ENDPOINT: https://hf-mirror.com
STUDIO_GIT_HOST: http://git.prod.idc-to-cloud.aistudio.baidu-int.com
PPNLP_HOME: /ssd1/paddlenlp
HF_DATASETS_CACHE: /ssd1/paddlenlp/huggingface/datasets
TRANSFORMERS_CACHE: /ssd1/paddlenlp/huggingface
CCACHE_DIR: /home/data/gzcfs/.ccache/gpubox
RUN_DOWNSTREAM: ${{ inputs.run_downstream }}
#defaults:
# run:
# shell: bash
#jobs:
# llm-ci:
# name: llm-ci (DISABLED)
runs-on: [self-hosted, ernie-8gpu]
steps:
- name: Determine Image Name
env:
IMAGE_NAME: ${{ inputs.image_name }}
run: |
if [[ -n "${IMAGE_NAME}" ]]; then
echo "IMAGE_NAME=${IMAGE_NAME}" >> "$GITHUB_ENV"
else
echo "IMAGE_NAME=iregistry.baidu-int.com/paddlecloud/base-images:paddlecloud-ubuntu18.04-gcc8.2-cuda11.8-cudnn8.6-nccl2.15.5-paddlenlp-latest" >> "$GITHUB_ENV"
fi
- name: Run Container
env:
work_dir: ${{ github.workspace }}
CACHE_DIR: /home/data/cfs/.cache
FLAGS_dynamic_static_unified_comm: "True"
python_version: "3.10"
paddle_whl: https://paddle-qa.bj.bcebos.com/paddle-pipeline/Develop-GpuSome-LinuxCentos-Gcc82-Cuda118-Cudnn86-Trt85-Py310-CINN-Compile/latest/paddlepaddle_gpu-0.0.0-cp310-cp310-linux_x86_64.whl
run: |
container_name=${TASK}-$(date +%Y%m%d-%H%M%S)
echo "container_name=${container_name}" >> "$GITHUB_ENV"
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping..."
else
docker run -d -t --name ${container_name} --net=host -v /dev/shm:/dev/shm --shm-size=32G \
-v $work_dir/../../..:$work_dir/../../.. \
-v $work_dir:/workspace \
-v /home/.cache/pip:/home/.cache/pip \
-v /ssd1/paddlenlp:/ssd1/paddlenlp \
-v /home/data/gzcfs/.ccache/gpubox:/home/data/gzcfs/.ccache/gpubox \
-e BRANCH \
-e AGILE_COMPILE_BRANCH \
-e PR_ID \
-e COMMIT_ID \
-e work_dir \
-e ci_scripts \
-e no_proxy \
-e CI_name \
-e paddle_whl \
-e HF_ENDPOINT \
-e STUDIO_GIT_HOST \
-e PPNLP_HOME \
-e HF_DATASETS_CACHE \
-e TRANSFORMERS_CACHE \
-e CACHE_DIR \
-e FLAGS_dynamic_static_unified_comm \
-e python_version \
-w /workspace --runtime=nvidia $IMAGE_NAME
fi
- name: Download Code
env:
work_dir: ${{ github.workspace }}
run: |
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping.."
else
docker exec -t $container_name /bin/bash -c '
rm -rf * .[^.]*
echo "Downloading PaddleNLP.tar.gz"
wget -q --no-proxy https://paddle-qa.bj.bcebos.com/CodeSync/develop/PaddleNLP.tar --no-check-certificate
echo "Extracting PaddleNLP.tar.gz"
tar xf PaddleNLP.tar && rm -rf PaddleNLP.tar
source $work_dir/../../../proxy
cd PaddleNLP
git config --global user.name "PaddleCI"
git config --global user.email "paddle_ci@example.com"
git pull
git submodule update --init --recursive --force
if [ -n "${PR_ID}" ]; then
git fetch origin pull/${PR_ID}/head
git checkout -b PR_${PR_ID} FETCH_HEAD
git remote add upstream https://github.com/PaddlePaddle/PaddleNLP.git
git fetch upstream ${BRANCH}
git merge ${BRANCH} --no-edit
git diff --numstat ${BRANCH} -- | awk "{print \$NF}"
else
echo "Not in a pull_request event. Skipping PR-specific operations."
fi
git log --pretty=oneline -10
'
fi
- name: Skip For Bug
run: |
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping..."
else
docker exec -t $container_name /bin/bash -c '
cd /workspace/PaddleNLP
git revert f2477c07272d04244cd3287d1f21c70482a4a85f --no-edit # 套件PR#10413引入bug-待修复
git revert d74c950e15a35b7a100d1688c89318195cc83bca --no-edit # 框架升级后配合修改,当前因框架bug固定commit适配
'
fi
- name: Test
run: |
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping..."
else
docker exec -t $container_name /bin/bash -c '
ldconfig
unlink /usr/bin/python3
ln -sf $(which python${python_version}) /usr/bin/python3
pip config set global.cache-dir "/home/.cache/pip"
set -e
source $work_dir/../../../proxy
cd /workspace/PaddleNLP && git config --global --add safe.directory $PWD
echo "Paddle PR#73283 PR#74484 import bug, set paddle_commit=8ae742 to skip "
export paddle_whl=https://paddle-qa.bj.bcebos.com/paddle-pipeline/Develop-GpuSome-LinuxCentos-Gcc82-Cuda118-Cudnn86-Trt85-Py310-CINN-Compile/8ae7423e99b2ea96e410968a0ebb3f1795e37205/paddlepaddle_gpu-0.0.0-cp310-cp310-linux_x86_64.whl
timeout 2h bash scripts/regression/run_ci.sh python${python_version} ${paddle_whl}
'
fi
- name: Upload Allure-reports & Logs
if: always()
env:
home_path: ${{ github.workspace }}/../../..
bos_file: ${{ github.workspace }}/../../../bos/BosClient.py
allure_file: ${{ github.workspace }}/../../../allure-2.19.0/bin/allure
run: |
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping..."
else
docker exec -t $container_name /bin/bash -c '
unset http_proxy && unset https_proxy
if [ ! -f "${{ env.bos_file }}" ]; then
wget -q --no-proxy -O ${{ env.home_path }}/bos_new.tar.gz https://xly-devops.bj.bcebos.com/home/bos_new.tar.gz --no-check-certificate
mkdir ${{ env.home_path }}/bos
tar xf ${{ env.home_path }}/bos_new.tar.gz -C ${{ env.home_path }}/bos
fi
if [ ! -f "${{ env.allure_file }}" ]; then
wget -q --no-proxy -O ${{ env.home_path }}/allure-2.19.0.zip https://xly-devops.bj.bcebos.com/tools/allure-2.19.0.zip --no-check-certificate
unzip -q ${{ env.home_path }}/allure-2.19.0.zip -d ${{ env.home_path }}/
fi
if [[ "${{ env.RUN_DOWNSTREAM }}" == "" && -n "${PR_ID}" ]]; then
bos_prefix="${PR_ID}/${COMMIT_ID}"
elif [[ "${{ env.RUN_DOWNSTREAM }}" == "true" && -n "${PR_ID}" ]]; then
bos_prefix="${PR_ID}/${COMMIT_ID}/test_build"
else
bos_prefix="schedule/$(date +%Y%m%d)"
fi
cd /workspace/PaddleNLP/model_logs
for FILE in /workspace/PaddleNLP/model_logs/*; do
file=$(basename "$FILE")
python ${{ env.bos_file }} $file paddle-github-action/PR/PaddleNLP/llm/${bos_prefix}/logs
echo "$file: https://paddle-github-action.bj.bcebos.com/PR/PaddleNLP/llm/${bos_prefix}/logs/$file"
done
cd /workspace/PaddleNLP/
${{ env.allure_file }} generate result -o report
tar -czf products.tar.gz report model_logs
python ${{ env.bos_file }} products.tar.gz paddle-github-action/PR/PaddleNLP/llm/${bos_prefix}/logs
echo "products: https://paddle-github-action.bj.bcebos.com/PR/PaddleNLP/llm/${bos_prefix}/logs/products.tar.gz"
'
fi
- name: Terminate And Delete the Container
if: always()
run: |
docker rm -f ${{ env.container_name }} 2>/dev/null || true

View File

@@ -1,73 +0,0 @@
name: Re-run
on:
issue_comment:
types: [created]
jobs:
re-run:
if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/re-run') && github.event.comment.user.login == github.event.issue.user.login }}
runs-on: [self-hosted, ernie-cpu]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Rerun all failed jobs
if: ${{ contains(github.event.comment.body, 'all-failed') }}
uses: ./.github/actions/rerun-workflow
with:
PR_ID: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
JOB_NAME: 'all-failed'
- name: Rerun LLM
if: ${{ contains(github.event.comment.body, 'LLM') }}
uses: ./.github/actions/rerun-workflow
with:
PR_ID: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
JOB_NAME: 'LLM CI / llm-ci'
- name: Unittest GPU
if: ${{ contains(github.event.comment.body, 'Unittest GPU') }}
uses: ./.github/actions/rerun-workflow
with:
PR_ID: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
JOB_NAME: 'Unittest GPU CI / unittest-gpu-ci'
- name: Rerun Unittest CPU
if: ${{ contains(github.event.comment.body, 'Unittest CPU') }}
uses: ./.github/actions/rerun-workflow
with:
PR_ID: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
JOB_NAME: 'Unittest CPU CI / unit-cpu-ci'
- name: Rerun Distribute (V100)
if: ${{ contains(github.event.comment.body, 'Distribute (V100)') }}
uses: ./.github/actions/rerun-workflow
with:
PR_ID: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
JOB_NAME: 'Distribute CI (V100)/ distribute-v100-ci'
- name: Rerun Codestyle Check
if: ${{ contains(github.event.comment.body, 'Codestyle Check') }}
uses: ./.github/actions/rerun-workflow
with:
PR_ID: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
JOB_NAME: 'Codestyle Check / Lint'

29
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Test
on: [push, pull_request]
jobs:
Test:
name: Test
runs-on: ubuntu-24.04
permissions:
pull-requests: write
contents: read
id-token: write
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip' # caching pip dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r tests/requirements.txt
make install
- name: run the command
run: make test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -1,120 +0,0 @@
# Disabled unittest CPU workflow - manually enabled when needed
name: Unittest CPU CI (DISABLED)
#on: [pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
PR_ID: ${{ github.event.pull_request.number }}
COMMIT_ID: ${{ github.event.pull_request.head.sha }}
BRANCH: ${{ github.event.pull_request.base.ref }}
TASK: PaddleNLP-CI-${{ github.event.pull_request.number }}-unittest-cpu
HF_ENDPOINT: https://hf-mirror.com
STUDIO_GIT_HOST: http://git.prod.idc-to-cloud.aistudio.baidu-int.com
PPNLP_HOME: /home/disk1/cache
HF_DATASETS_CACHE: /home/disk1/cache/huggingface/datasets
TRANSFORMERS_CACHE: /home/disk1/cache/huggingface
#jobs:
# unittest-cpu-ci:
# name: unittest-cpu-ci (DISABLED)
runs-on: [self-hosted, ernie-cpu]
permissions:
pull-requests: write
contents: read
id-token: write
steps:
- name: Run Container
env:
work_dir: ${{ github.workspace }}
python_version: "3.10"
run: |
container_name=${TASK}-$(date +%Y%m%d-%H%M%S)
echo "container_name=${container_name}" >> ${{ github.env }}
docker_image="iregistry.baidu-int.com/paddlecloud/base-images:paddlecloud-ubuntu20.04-gcc12.2-cuda12.3-cudnn9.0-nccl2.20.3.1-openmpi4.1.5-latest"
docker run -d -t --name ${container_name} --net=host -v /dev/shm:/dev/shm --shm-size=32G \
-v $work_dir/../../..:$work_dir/../../.. \
-v $work_dir:/workspace \
-v /home/.cache/pip:/home/.cache/pip \
-v /home/disk1/cache:/home/disk1/cache \
-e BRANCH \
-e PR_ID \
-e COMMIT_ID \
-e work_dir \
-e no_proxy \
-e python_version \
-e HF_ENDPOINT \
-e STUDIO_GIT_HOST \
-e PPNLP_HOME \
-e HF_DATASETS_CACHE \
-e TRANSFORMERS_CACHE \
-w /workspace ${docker_image}
- name: Download Code
env:
work_dir: ${{ github.workspace }}
run: |
docker exec -t ${container_name} /bin/bash -c '
rm -rf * .[^.]*
echo "Downloading PaddleNLP.tar"
wget -q --no-proxy https://paddle-qa.bj.bcebos.com/CodeSync/develop/PaddleNLP.tar --no-check-certificate
echo "Extracting PaddleNLP.tar"
tar xf PaddleNLP.tar && rm -rf PaddleNLP.tar
source $work_dir/../../../proxy
cd PaddleNLP
git config --global user.name "PaddleCI"
git config --global user.email "paddle_ci@example.com"
git pull
git submodule update --init --recursive --force
if [ -n "${PR_ID}" ]; then
git fetch origin pull/${PR_ID}/head
git checkout -b PR_${PR_ID} FETCH_HEAD
git remote add upstream https://github.com/PaddlePaddle/PaddleNLP.git
git fetch upstream ${BRANCH}
git merge ${BRANCH} --no-edit
git diff --numstat ${BRANCH} -- | awk "{print \$NF}"
else
echo "Not in a pull_request event. Skipping PR-specific operations."
fi
git log --pretty=oneline -10
'
- name: Setup Environment
run: |
docker exec -t $container_name /bin/bash -c '
unlink /usr/bin/python3
ln -sf $(which python${python_version}) /usr/bin/python3
set -e
python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
python -m pip config set global.cache-dir "/home/.cache/pip"
source $work_dir/../../../proxy
python -m pip install --upgrade pip
cd /workspace/PaddleNLP && git config --global --add safe.directory $PWD
pip install -r tests/requirements.txt
make install
'
- name: Test
run: |
docker exec -t $container_name /bin/bash -c '
source $work_dir/../../../proxy
cd /workspace/PaddleNLP
set -e
make test
'
# - name: Upload Coverage To Codecov
# uses: codecov/codecov-action@v3
# env:
# CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# files: ./PaddleNLP/coverage.xml
- name: Terminate And Delete the Container
if: always()
run: |
docker rm -f $container_name 2>/dev/null || true

View File

@@ -1,204 +0,0 @@
# Disabled unittest GPU workflow - manually enabled when needed
name: Unittest GPU CI (DISABLED)
#on:
# pull_request:
# types: [opened, synchronize, reopened]
# branches: [develop]
# schedule:
# - cron: "3 0 * * *"
# workflow_call:
# inputs:
# run_downstream:
# required: true
# type: string
# image_name:
# required: true
# type: string
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
PR_ID: ${{ github.event.pull_request.number }}
COMMIT_ID: ${{ github.event.pull_request.head.sha }}
TASK: paddlenlp-CI-${{ github.event.pull_request.number }}-unit-gpu
ci_scripts: /workspace/PaddleNLP/scripts/unit_test
BRANCH: ${{ github.event.pull_request.base.ref }}
AGILE_COMPILE_BRANCH: ${{ github.event.pull_request.base.ref }}
CI_name: unittest-gpu-ci
no_proxy: "localhost,bj.bcebos.com,su.bcebos.com,bcebos.com,apiin.im.baidu.com,gitee.com,aliyun.com,.baidu.com,.tuna.tsinghua.edu.cn"
HF_ENDPOINT: https://hf-mirror.com
STUDIO_GIT_HOST: http://git.prod.idc-to-cloud.aistudio.baidu-int.com
PPNLP_HOME: /ssd1/paddlenlp
HF_DATASETS_CACHE: /ssd1/paddlenlp/huggingface/datasets
TRANSFORMERS_CACHE: /ssd1/paddlenlp/huggingface
CCACHE_DIR: /home/data/gzcfs/.ccache/gpubox
RUN_DOWNSTREAM: ${{ inputs.run_downstream }}
defaults:
run:
shell: bash
#jobs:
# unittest-gpu-ci:
# name: unittest-gpu-ci (DISABLED)
runs-on: [self-hosted, ernie-8gpu]
steps:
- name: Determine Image Name
env:
IMAGE_NAME: ${{ inputs.image_name }}
run: |
if [[ -n "${IMAGE_NAME}" ]]; then
echo "IMAGE_NAME=${IMAGE_NAME}" >> "$GITHUB_ENV"
else
echo "IMAGE_NAME=iregistry.baidu-int.com/paddlecloud/base-images:paddlecloud-ubuntu18.04-gcc8.2-cuda11.8-cudnn8.6-nccl2.15.5-paddlenlp-latest" >> "$GITHUB_ENV"
fi
- name: Run Container
env:
work_dir: ${{ github.workspace }}
CACHE_DIR: /home/data/cfs/.cache
FLAGS_dynamic_static_unified_comm: "True"
python_version: "3.10"
paddle_whl: https://paddle-qa.bj.bcebos.com/paddle-pipeline/Develop-GpuSome-LinuxCentos-Gcc82-Cuda118-Cudnn86-Trt85-Py310-CINN-Compile/latest/paddlepaddle_gpu-0.0.0-cp310-cp310-linux_x86_64.whl
run: |
container_name=${TASK}-$(date +%Y%m%d-%H%M%S)
echo "container_name=${container_name}" >> "$GITHUB_ENV"
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping..."
else
docker run -d -t --name ${container_name} --net=host -v /dev/shm:/dev/shm --shm-size=32G \
-v $work_dir/../../..:$work_dir/../../.. \
-v $work_dir:/workspace \
-v /home/.cache/pip:/home/.cache/pip \
-v /ssd1/paddlenlp:/ssd1/paddlenlp \
-v /home/data/gzcfs/.ccache/gpubox:/home/data/gzcfs/.ccache/gpubox \
-e BRANCH \
-e AGILE_COMPILE_BRANCH \
-e PR_ID \
-e COMMIT_ID \
-e work_dir \
-e ci_scripts \
-e no_proxy \
-e CI_name \
-e paddle_whl \
-e HF_ENDPOINT \
-e STUDIO_GIT_HOST \
-e PPNLP_HOME \
-e HF_DATASETS_CACHE \
-e TRANSFORMERS_CACHE \
-e CACHE_DIR \
-e FLAGS_dynamic_static_unified_comm \
-e python_version \
-w /workspace --runtime=nvidia $IMAGE_NAME
fi
- name: Download Code
env:
work_dir: ${{ github.workspace }}
run: |
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping.."
else
docker exec -t $container_name /bin/bash -c '
rm -rf * .[^.]*
echo "Downloading PaddleNLP.tar.gz"
wget -q --no-proxy https://paddle-qa.bj.bcebos.com/CodeSync/develop/PaddleNLP.tar --no-check-certificate
echo "Extracting PaddleNLP.tar.gz"
tar xf PaddleNLP.tar && rm -rf PaddleNLP.tar
source $work_dir/../../../proxy
cd PaddleNLP
git config --global user.name "PaddleCI"
git config --global user.email "paddle_ci@example.com"
git pull
git submodule update --init --recursive --force
if [ -n "${PR_ID}" ]; then
git fetch origin pull/${PR_ID}/head
git checkout -b PR_${PR_ID} FETCH_HEAD
git remote add upstream https://github.com/PaddlePaddle/PaddleNLP.git
git fetch upstream ${BRANCH}
git merge ${BRANCH} --no-edit
git diff --numstat ${BRANCH} -- | awk "{print \$NF}"
else
echo "Not in a pull_request event. Skipping PR-specific operations."
fi
git log --pretty=oneline -10
'
fi
- name: Skip For Bug
run: |
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping..."
else
docker exec -t $container_name /bin/bash -c '
cd /workspace/PaddleNLP
echo "no skip for bug"
'
fi
- name: Test
run: |
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping..."
else
docker exec -t $container_name /bin/bash -c '
ldconfig
unlink /usr/bin/python3
ln -sf $(which python${python_version}) /usr/bin/python3
pip config set global.cache-dir "/home/.cache/pip"
source $work_dir/../../../proxy
set -e
cd /workspace/PaddleNLP && git config --global --add safe.directory $PWD
timeout 50m bash scripts/unit_test/ci_unit.sh ${paddle_whl}
'
fi
- name: Upload Allure-reports & Logs
if: always()
env:
home_path: ${{ github.workspace }}/../../..
bos_file: ${{ github.workspace }}/../../../bos/BosClient.py
allure_file: ${{ github.workspace }}/../../../allure-2.19.0/bin/allure
run: |
if [[ "$RUN_DOWNSTREAM" == "false" ]]; then
echo "Not in a pull_request or test_build event. Skipping..."
else
docker exec -t $container_name /bin/bash -c '
unset http_proxy && unset https_proxy
if [ ! -f "${{ env.bos_file }}" ]; then
wget -q --no-proxy -O ${{ env.home_path }}/bos_new.tar.gz https://xly-devops.bj.bcebos.com/home/bos_new.tar.gz --no-check-certificate
mkdir ${{ env.home_path }}/bos
tar xf ${{ env.home_path }}/bos_new.tar.gz -C ${{ env.home_path }}/bos
fi
if [ ! -f "${{ env.allure_file }}" ]; then
wget -q --no-proxy -O ${{ env.home_path }}/allure-2.19.0.zip https://xly-devops.bj.bcebos.com/tools/allure-2.19.0.zip --no-check-certificate
unzip -q ${{ env.home_path }}/allure-2.19.0.zip -d ${{ env.home_path }}/
fi
if [[ "${{ env.RUN_DOWNSTREAM }}" == "" && -n "${PR_ID}" ]]; then
bos_prefix="${PR_ID}/${COMMIT_ID}"
elif [[ "${{ env.RUN_DOWNSTREAM }}" == "true" && -n "${PR_ID}" ]]; then
bos_prefix="${PR_ID}/${COMMIT_ID}/test_build"
else
bos_prefix="schedule/$(date +%Y%m%d)"
fi
cd /workspace/PaddleNLP/unittest_logs
for FILE in /workspace/PaddleNLP/unittest_logs/*; do
file=$(basename "$FILE")
python ${{ env.bos_file }} $file paddle-github-action/PR/PaddleNLP/unittest-gpu/${bos_prefix}/logs
echo "$file: https://paddle-github-action.bj.bcebos.com/PR/PaddleNLP/unittest-gpu/${bos_prefix}/logs/$file"
done
cd /workspace/PaddleNLP/
${{ env.allure_file }} generate result -o report
tar -czf products.tar.gz report unittest_logs
python ${{ env.bos_file }} products.tar.gz paddle-github-action/PR/PaddleNLP/unittest-gpu/${bos_prefix}/logs
echo "report: https://paddle-github-action.bj.bcebos.com/PR/PaddleNLP/unittest-gpu/${bos_prefix}/logs/products.tar.gz"
'
fi
- name: Terminate And Delete the Container
if: always()
run: |
docker rm -f $container_name 2>/dev/null || true

2
.gitignore vendored
View File

@@ -140,4 +140,4 @@ autogen/
#fp8
ops/csrc/fp8/deep_gemm/include/cutlass
ops/csrc/fp8/deep_gemm/include/cute
.ccls-cache
.ccls-cache

View File

@@ -62,12 +62,3 @@ repos:
language: python
files: \.(md|markdown|rst)$
pass_filenames: true
- repo: local
hooks:
- id: clang-format
name: clang-format
description: Format files with ClangFormat.
entry: bash ./tools/codestyle/clang_format.sh -i
language: system
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|xpu|kps)$

View File

@@ -140,14 +140,12 @@ std::vector<paddle::Tensor> preprocess_for_moe_kernel(const paddle::Tensor& topk
if (num_experts == 8) {
run_align_kernel(8);
} else if (num_experts == 64) {
run_align_kernel(64);
} else if (num_experts == 256) {
run_align_kernel(256);
} else if (num_experts == 2) {
run_align_kernel(2);
} else {
printf("【ERROR】, please add the current number of experts in the case");
printf("errors");
}
const int block_threads = 256;

View File

@@ -64,5 +64,6 @@ class GetPaddingOffsetV2Test(unittest.TestCase):
assert sum(ref_cu_seqlens_q - cu_seqlens_q) == 0, "Check cu_seqlens_q failed."
assert sum(ref_cu_seqlens_k - cu_seqlens_k) == 0, "Check cu_seqlens_k failed."
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1 @@
../../../../../llm/alignment/ppo/README.md

View File

@@ -1,74 +0,0 @@
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Preprocess the GSM8k dataset to parquet format
"""
import argparse
import os
import re
import datasets
def extract_solution(solution_str):
solution = re.search("#### (\\-?[0-9\\.\\,]+)", solution_str)
assert solution is not None
final_solution = solution.group(0)
final_solution = final_solution.split("#### ")[1].replace(",", "")
return final_solution
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--local_dir", default="./gsm8k")
args = parser.parse_args()
data_source = "openai/gsm8k"
dataset = datasets.load_dataset(data_source, "main")
train_dataset = dataset["train"]
test_dataset = dataset["test"]
instruction_following = 'Let\'s think step by step and output the final answer after "####".'
# add a row to each data item that represents a unique id
def make_map_fn(split):
def process_fn(example, idx):
question_raw = "<|im_start|>user\n" + example.pop("question")
system_raw = (
"<|im_start|>system\nYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>\n"
)
question = system_raw + question_raw + " " + instruction_following + "<|im_end|>\n<|im_start|>assistant\n"
answer_raw = example.pop("answer")
solution = extract_solution(answer_raw)
data = {
"src": question,
"tgt": solution,
}
return data
return process_fn
train_dataset = train_dataset.map(function=make_map_fn("train"), with_indices=True)
test_dataset = test_dataset.map(function=make_map_fn("test"), with_indices=True)
local_dir = args.local_dir
train_dataset.to_json(os.path.join(local_dir, "train.jsonl"), orient="records", lines=True)
test_dataset.to_json(os.path.join(local_dir, "test.jsonl"), orient="records", lines=True)

View File

@@ -42,7 +42,6 @@ from paddlenlp.trainer import (
from paddlenlp.transformers import (
AutoConfig,
AutoModelForCausalLM,
AutoModelForTokenClassification,
AutoTokenizer,
PretrainedConfig,
)
@@ -135,6 +134,7 @@ def create_actor_models(
)
if not training_args.autotuner_benchmark:
reference_model.set_state_dict(actor_model.state_dict())
actor_tokenizer = AutoTokenizer.from_pretrained(
model_args.actor_model_name_or_path,
model_max_length=data_args.max_length,
@@ -210,43 +210,46 @@ def create_critic_models(
data_args: DataArgument,
training_args: TrainingArguments,
common_config: Dict,
reward_model,
):
with timers_scope_runtimer("Critic model loading time"):
critic_model_config = AutoConfig.from_pretrained(
model_args.critic_model_name_or_path,
tensor_parallel_output=training_args.tensor_parallel_output,
tensor_parallel_degree=training_args.tensor_parallel_degree,
tensor_parallel_rank=training_args.tensor_parallel_rank,
dtype=training_args.model_dtype,
recompute=training_args.critic_recompute,
recompute_granularity=model_args.critic_recompute_granularity,
recompute_use_reentrant=training_args.recompute_use_reentrant,
**common_config,
)
LlmMetaConfig.set_llm_config(critic_model_config, training_args)
critic_model_config.max_position_embeddings = data_args.max_length
critic_model_config.use_sparse_head_and_loss_fn = False
critic_model_config.num_labels = 1
critic_model_config.classifier_dropout = 0.0
critic_model_config.hidden_dropout = 0.0
logger.info(f"Loading Critic model with config:\n\t{critic_model_config}\n")
if not training_args.autotuner_benchmark:
critic_model = AutoModelForTokenClassification.from_pretrained(
model_args.critic_model_name_or_path,
config=critic_model_config,
reward_model_config = reward_model.config
if model_args.critic_model_name_or_path is None:
model_args.critic_model_name_or_path = model_args.reward_model_name_or_path
critic_model = AutoModelForScore.from_config(
reward_model_config,
dtype=training_args.model_dtype,
score_type="critic",
do_normalize=False,
clip_range_value=training_args.clip_range_value,
**common_config,
)
if not training_args.autotuner_benchmark:
critic_model.set_state_dict(reward_model.state_dict())
else:
critic_model = AutoModelForTokenClassification.from_config(
critic_model_config,
)
if not training_args.autotuner_benchmark:
critic_model = AutoModelForScore.from_pretrained(
model_args.critic_model_name_or_path,
config=reward_model_config,
score_type="critic",
do_normalize=False,
clip_range_value=training_args.clip_range_value,
**common_config,
)
else:
critic_model = AutoModelForScore.from_config(
reward_model_config,
score_type="critic",
do_normalize=False,
clip_range_value=training_args.clip_range_value,
**common_config,
)
critic_tokenizer = AutoTokenizer.from_pretrained(
model_args.critic_model_name_or_path,
model_max_length=data_args.max_length,
padding_side="left",
tokenizer_alpha=model_args.critic_tokenizer_alpha,
tokenizer_alpha=model_args.reward_critic_tokenizer_alpha,
use_fast=True,
)
if critic_tokenizer.pad_token_id is None:
@@ -258,8 +261,8 @@ def create_critic_models(
if training_args.eval_mode == "single":
config.tensor_parallel_degree = -1
config.tensor_parallel_rank = 0
with timers_scope_runtimer("Critic eval model loading time"):
critic_eval_model = AutoModelForTokenClassification.from_config(config)
with timers_scope_runtimer("Reward critic eval model loading time"):
critic_eval_model = AutoModelForScore.from_config(config)
else:
critic_eval_model = None
@@ -267,7 +270,7 @@ def create_critic_models(
def create_rl_dataset(data_args, training_args, tokenizer):
requires_label = True if training_args.use_rm_server or training_args.use_rule_reward else False
requires_label = True if training_args.use_rm_server else False
train_ds = RLHFDataset(
dataset_name_or_path=data_args.train_datasets,
tokenizer=tokenizer,
@@ -330,16 +333,15 @@ def main():
actor_model, actor_eval_model, reference_model, actor_tokenizer = create_actor_models(
model_args, data_args, training_args, common_config, reshard_controller
)
if training_args.use_rule_reward:
reward_model, reward_tokenizer = None, actor_tokenizer
elif not training_args.use_rm_server and model_args.reward_model_name_or_path is not None:
if not training_args.use_rm_server and model_args.reward_model_name_or_path is not None:
reward_model, reward_tokenizer = create_reward_models(model_args, data_args, training_args, common_config)
else:
reward_model, reward_tokenizer = model_args.reward_server, actor_tokenizer
if training_args.rl_algorithm == "ppo":
critic_model, critic_eval_model, critic_tokenizer = create_critic_models(
model_args, data_args, training_args, common_config
model_args, data_args, training_args, common_config, reward_model
)
else:
critic_model, critic_eval_model, critic_tokenizer = None, None, None
@@ -353,8 +355,7 @@ def main():
offload_tensor_to_cpu((reference_model, "freeze_model"))
if training_args.rl_algorithm == "ppo":
if not training_args.use_rm_server and not training_args.use_rule_reward:
offload_tensor_to_cpu((reward_model, "freeze_model"))
offload_tensor_to_cpu((reward_model, "freeze_model"))
if critic_eval_model is not None:
offload_tensor_to_cpu((critic_eval_model, "freeze_model"))
@@ -362,14 +363,7 @@ def main():
paddle.device.cuda.empty_cache()
def compute_metrics(eval_preds):
'''
If "use_rm_server" is TRUE, the score ranges from -3 to 3, with 3 being the only correct score (format + result).
If using the "Regularized Matching Function (use_rule_reward=True)" (currently only implemented for the gsm8k dataset), the score ranges from 0 to 1.
'''
if training_args.use_rule_reward:
accuracy = (eval_preds.predictions == 1).astype("float32").mean().item()
else:
accuracy = (eval_preds.predictions == 3).astype("float32").mean().item()
accuracy = (eval_preds.predictions == 3).astype("float32").mean().item()
return {"accuracy": accuracy}
try:
@@ -395,7 +389,7 @@ def main():
data_collator=partial(
collate_fn,
pad_token_id=actor_tokenizer.pad_token_id,
requires_label=True if training_args.use_rm_server or training_args.use_rule_reward else False,
requires_label=True if training_args.use_rm_server else False,
max_prompt_len=data_args.max_prompt_len if training_args.balance_batch else None,
), # NOTE: enforce prompt padding to max_prompt_len when using balance_batch
compute_metrics=compute_metrics, # TODO: only used for grpo (kk datasets)

View File

@@ -460,25 +460,10 @@ def main():
else:
model_args, data_args, training_args = parser.parse_args_into_dataclasses()
do_enable_linear_fused_grad_add = training_args.enable_linear_fused_grad_add
do_enable_mp_async_allreduce = (
training_args.enable_auto_parallel
and training_args.tensor_parallel_degree > 1
and "enable_mp_async_allreduce" in training_args.tensor_parallel_config
and not training_args.sequence_parallel
)
do_enable_sp_async_reduce_scatter = (
training_args.enable_auto_parallel
and training_args.tensor_parallel_degree > 1
and training_args.sequence_parallel
and "enable_sp_async_reduce_scatter" in training_args.tensor_parallel_config
)
if (
do_enable_linear_fused_grad_add or do_enable_mp_async_allreduce or do_enable_sp_async_reduce_scatter
) and not training_args.to_static:
from llm.utils.fused_layers import mock_layers
if training_args.enable_linear_fused_grad_add:
from fused_layers import mock_layers
mock_layers(do_enable_linear_fused_grad_add, do_enable_mp_async_allreduce, do_enable_sp_async_reduce_scatter)
mock_layers()
if model_args.tokenizer_name_or_path is None:
model_args.tokenizer_name_or_path = model_args.model_name_or_path

View File

@@ -38,7 +38,6 @@ from paddlenlp.transformers import (
CosineAnnealingWithWarmupDecay,
GPTConfig,
GPTForCausalLMAuto,
GPTForCausalLMAutoPP,
GPTForCausalLMNet,
GPTPretrainingCriterionAuto,
GPTPretrainingCriterionNet,
@@ -49,7 +48,6 @@ from paddlenlp.utils.tools import get_env_device
MODEL_CLASSES = {
"gpt": (GPTConfig, GPTForCausalLMAuto, GPTPretrainingCriterionAuto),
"gpt_pp": (GPTConfig, GPTForCausalLMAutoPP, GPTPretrainingCriterionAuto),
"gpt_network": (GPTConfig, GPTForCausalLMNet, GPTPretrainingCriterionNet),
}
@@ -74,12 +72,6 @@ class PreTrainingArguments(AutoTrainingArguments):
"help": "The steps use to control the learing rate. If the step > decay_steps, will use the min_learning_rate."
},
)
enable_linear_fused_grad_add: bool = field(
default=False,
metadata={
"help": "Enable fused linear grad add strategy, which will reduce elementwise add for grad accumulation in the backward of nn.Linear ."
},
)
job_schedule_profiler_start: int = field(
default=-1,
metadata={"help": "The step to start job_schedule_profiler."},
@@ -93,18 +85,13 @@ class PreTrainingArguments(AutoTrainingArguments):
)
sr: Optional[int] = field(default=0, metadata={"help": "The count of chunks without recompute."})
virtual_pipeline_seg_method: str = field(
default="LlamaDecoderLayerAuto",
metadata={"help": "The seg method of splitting pp layer for virtual pipeline."},
default="LlamaDecoderLayerAuto", metadata={"help": "The seg method of splitting pp layer for virtual pipeline."}
)
# NOTE(gongenlei): new add autotuner_benchmark
autotuner_benchmark: bool = field(
default=False,
metadata={"help": "Weather to run benchmark by autotuner. True for from_scratch and pad_max_length."},
)
n_microbatches: int = field(
default=1,
metadata={"help": "Control the num of microbatches in one pp step."},
)
pre_alloc_memory: float = field(
default=0.0,
metadata={
@@ -444,26 +431,6 @@ def main():
else:
model_args, data_args, training_args = parser.parse_args_into_dataclasses()
do_enable_linear_fused_grad_add = training_args.enable_linear_fused_grad_add
do_enable_mp_async_allreduce = (
training_args.enable_auto_parallel
and training_args.tensor_parallel_degree > 1
and "enable_mp_async_allreduce" in training_args.tensor_parallel_config
and not training_args.sequence_parallel
)
do_enable_sp_async_reduce_scatter = (
training_args.enable_auto_parallel
and training_args.tensor_parallel_degree > 1
and training_args.sequence_parallel
and "enable_sp_async_reduce_scatter" in training_args.tensor_parallel_config
)
if (
do_enable_linear_fused_grad_add or do_enable_mp_async_allreduce or do_enable_sp_async_reduce_scatter
) and not training_args.to_static:
from llm.utils.fused_layers import mock_layers
mock_layers(do_enable_linear_fused_grad_add, do_enable_mp_async_allreduce, do_enable_sp_async_reduce_scatter)
if model_args.tokenizer_name_or_path is None:
model_args.tokenizer_name_or_path = model_args.model_name_or_path
@@ -558,7 +525,6 @@ def main():
config.use_recompute = training_args.recompute
config.tensor_parallel_degree = training_args.tensor_parallel_degree
config.tensor_parallel_rank = training_args.tensor_parallel_rank
config.to_static = training_args.to_static
if training_args.strategy.pipeline.enable and config.virtual_pp_degree > 1:
pipeline = training_args.strategy.pipeline
@@ -568,14 +534,6 @@ def main():
config.hidden_dropout_prob = model_args.hidden_dropout_prob
config.attention_probs_dropout_prob = model_args.attention_probs_dropout_prob
print("Final pre-training config:", config)
if (
"replace_with_parallel_cross_entropy" in training_args.tensor_parallel_config
and config.tensor_parallel_degree > 1
and config.to_static is False
):
from llm.utils.replace_ops import replace_cross_entropy
replace_cross_entropy()
# Set the dtype for loading model
dtype = "float32"
@@ -631,7 +589,6 @@ def main():
trainer = PretrainingTrainer(
model=model,
model_type=model_args.model_type,
criterion=criterion,
args=training_args,
data_collator=data_collator,

View File

@@ -41,7 +41,6 @@ from paddlenlp.transformers import (
LinearAnnealingWithWarmupDecay,
LlamaConfig,
LlamaForCausalLM3DAuto,
LlamaForCausalLM3DAutoPP,
LlamaForCausalLMNet,
LlamaPretrainingCriterion3DAuto,
LlamaPretrainingCriterionNet,
@@ -50,7 +49,6 @@ from paddlenlp.utils.log import logger
MODEL_CLASSES = {
"llama": (LlamaConfig, LlamaForCausalLM3DAuto, LlamaPretrainingCriterion3DAuto),
"llama_pp": (LlamaConfig, LlamaForCausalLM3DAutoPP, LlamaPretrainingCriterion3DAuto),
"llama_network": (LlamaConfig, LlamaForCausalLMNet, LlamaPretrainingCriterionNet),
}
@@ -96,10 +94,6 @@ class PreTrainingArguments(AutoTrainingArguments):
default=False,
metadata={"help": "Weather to run benchmark by autotuner. True for from_scratch and pad_max_length."},
)
n_microbatches: int = field(
default=1,
metadata={"help": "Control the num of microbatches in one pp step."},
)
def __post_init__(self):
super().__post_init__()
@@ -410,20 +404,12 @@ def init_seed(seed: int = 1234, args=None):
order = ["pp", "dp", "sharding", "mp", "sep"]
elif args.hybrid_parallel_topo_order == "sharding_first":
order = ["dp", "sharding", "pp", "mp", "sep"]
if args.context_parallel_degree is not None and args.context_parallel_degree > 1:
sep_degree = args.context_parallel_degree
elif args.sep_parallel_degree is not None and args.sep_parallel_degree > 1:
sep_degree = args.sep_parallel_degree
else:
sep_degree = 1
sep_degree=args.sep_parallel_degree if args.sep_parallel_degree > 1 else args.context_parallel_degree
topo = Topology(
dist.get_rank(),
dist.get_world_size(),
dp_degree=args.dataset_world_size,
pp_degree=args.pipeline_parallel_degree,
mp_degree=args.tensor_parallel_degree,
sep_degree=sep_degree,
sharding_degree=1, # auto_parallel's sharding is not orthogonal with dp, mp and pp
order=order,
)
@@ -464,25 +450,16 @@ def main():
else:
model_args, data_args, training_args = parser.parse_args_into_dataclasses()
do_enable_linear_fused_grad_add = training_args.enable_linear_fused_grad_add
do_enable_mp_async_allreduce = (
training_args.enable_auto_parallel
and training_args.tensor_parallel_degree > 1
and "enable_mp_async_allreduce" in training_args.tensor_parallel_config
and not training_args.sequence_parallel
)
do_enable_sp_async_reduce_scatter = (
training_args.enable_auto_parallel
and training_args.tensor_parallel_degree > 1
and training_args.sequence_parallel
and "enable_sp_async_reduce_scatter" in training_args.tensor_parallel_config
)
if (
do_enable_linear_fused_grad_add or do_enable_mp_async_allreduce or do_enable_sp_async_reduce_scatter
) and not training_args.to_static:
if training_args.enable_linear_fused_grad_add and not do_enable_sp_async_reduce_scatter:
from llm.utils.fused_layers import mock_layers
mock_layers(do_enable_linear_fused_grad_add, do_enable_mp_async_allreduce, do_enable_sp_async_reduce_scatter)
mock_layers()
if model_args.tokenizer_name_or_path is None:
model_args.tokenizer_name_or_path = model_args.model_name_or_path
@@ -570,8 +547,6 @@ def main():
config.tensor_parallel_rank = training_args.tensor_parallel_rank
config.sharding_parallel_degree = training_args.sharding_parallel_degree
config.to_static = training_args.to_static
config.sep_parallel_degree = training_args.sep_parallel_degree
config.context_parallel_degree = training_args.context_parallel_degree
if training_args.strategy.pipeline.enable and config.virtual_pp_degree > 1:
pipeline = training_args.strategy.pipeline
@@ -618,6 +593,13 @@ def main():
model.apply(fn)
if do_enable_sp_async_reduce_scatter:
from llm.utils.sp_async_reduce_scatter import (
mock_layers_sp_async_reduce_scatter,
)
mock_layers_sp_async_reduce_scatter(model)
# Create the learning_rate scheduler and optimizer
if training_args.decay_steps is None:
training_args.decay_steps = training_args.max_steps
@@ -655,7 +637,6 @@ def main():
)
trainer = PretrainingTrainer(
model=model,
model_type=model_args.model_type,
criterion=criterion,
args=training_args,
data_collator=data_collator,

View File

@@ -433,25 +433,10 @@ def main():
else:
model_args, data_args, training_args = parser.parse_args_into_dataclasses()
do_enable_linear_fused_grad_add = training_args.enable_linear_fused_grad_add
do_enable_mp_async_allreduce = (
training_args.enable_auto_parallel
and training_args.tensor_parallel_degree > 1
and "enable_mp_async_allreduce" in training_args.tensor_parallel_config
and not training_args.sequence_parallel
)
do_enable_sp_async_reduce_scatter = (
training_args.enable_auto_parallel
and training_args.tensor_parallel_degree > 1
and training_args.sequence_parallel
and "enable_sp_async_reduce_scatter" in training_args.tensor_parallel_config
)
if (
do_enable_linear_fused_grad_add or do_enable_mp_async_allreduce or do_enable_sp_async_reduce_scatter
) and not training_args.to_static:
from llm.utils.fused_layers import mock_layers
if training_args.enable_linear_fused_grad_add:
from fused_layers import mock_layers
mock_layers(do_enable_linear_fused_grad_add, do_enable_mp_async_allreduce, do_enable_sp_async_reduce_scatter)
mock_layers()
if model_args.tokenizer_name_or_path is None:
model_args.tokenizer_name_or_path = model_args.model_name_or_path

View File

@@ -1,37 +0,0 @@
{
"model_name_or_path": "Qwen/Qwen2.5-0.5B-Instruct",
"dataset_name_or_path": "/home/bjh/bjh/Dislora/cs_5_lite",
"output_dir": "./checkpoints/dislora_ckpts_3",
"dislora": true,
"per_device_train_batch_size": 1,
"gradient_accumulation_steps": 5,
"num_train_epochs": 1,
"learning_rate": 2e-05,
"lr_scheduler_type": "linear",
"warmup_steps": 30,
"logging_steps": 1,
"evaluation_strategy": "no",
"save_strategy": "steps",
"save_steps": 500,
"src_length": 256,
"max_length": 512,
"bf16": true,
"do_train": true,
"do_eval": false,
"disable_tqdm": false,
"load_best_model_at_end": false,
"eval_with_do_generation": false,
"recompute": false,
"save_total_limit": 5,
"fp16_opt_level": "O2",
"sharding": "stage3",
"zero_padding": false,
"use_flash_attention": false,
"unified_checkpoint": false,
"dislora_rank": 8,
"dislora_dropout": 0.05,
"target_modules": [".*q_proj.*", ".*v_proj.*", ".*k_proj.*", ".*o_proj.*"],
"s_tsd": 8,
"ortho_lambda": 1.0,
"prefer_small_sigma": true
}

View File

@@ -1,36 +0,0 @@
{
"model_name_or_path": "Qwen/Qwen2.5-7B-Instruct",
"dataset_name_or_path": "/home/bjh/bjh/Dislora/cs_5_lite",
"output_dir": "./checkpoints/dislora_ckpts",
"dislora": true,
"per_device_train_batch_size": 1,
"gradient_accumulation_steps": 1,
"num_train_epochs": 1,
"learning_rate": 2e-05,
"lr_scheduler_type": "linear",
"warmup_steps": 30,
"logging_steps": 1,
"evaluation_strategy": "no",
"save_strategy": "steps",
"save_steps": 500,
"src_length": 256,
"max_length": 512,
"bf16": true,
"do_train": true,
"do_eval": false,
"disable_tqdm": false,
"load_best_model_at_end": false,
"eval_with_do_generation": false,
"recompute": false,
"save_total_limit": 5,
"fp16_opt_level": "O2",
"sharding": "stage3",
"zero_padding": false,
"use_flash_attention": false,
"unified_checkpoint": false,
"dislora_rank": 8,
"dislora_dropout": 0.05,
"s_tsd": 8,
"ortho_lambda": 1.0,
"prefer_small_sigma": true
}

View File

@@ -1,131 +0,0 @@
# RL algorithms
rl_algorithm: "ppo" # The reinforcement learning algorithm used, supported: "ppo", "grpo", "reinforce_plus_plus"
# models
actor_model_name_or_path: "Qwen/Qwen2.5-1.5B-Instruct" # The name or path of the actor model
reward_model_name_or_path: "" # The name or path of the reward model
critic_model_name_or_path: "Qwen/Qwen2.5-1.5B-Instruct" # The name or path of the critic model
use_rm_server: false # Whether to use the reward model server
reward_server: "http://127.0.0.1:8731" # The address of the reward model server
use_rule_reward: True # The reward for gsm8k dataset. If use_rule_reward: use_rm_server = false
# logging
logging_dir: ppo-logs # Directory for logging
logging_steps: 1 # Number of steps between logging
output_dir: "qwen2.5-1.5b-gsm8k-ppo/checkpoints" # Directory for output ckpts
report_to: "wandb" # Supported reporting options: "all", "wandb", "tensorboard", "visualdl"(default), "none"
wandb_http_proxy: "http://agent.baidu.com:8188" # HTTP proxy for wandb
run_name: "qwen2.5-1.5b-gsm8k-ppo" # Name of the run
# data
train_datasets: "gsm8k/train.jsonl" # Path to the training dataset
eval_datasets: "gsm8k/test.jsonl" # Path to the evaluation dataset
prompt_key: "src" # Key for the prompt in the dataset
response_key: "tgt" # Key for the response in the dataset
dataloader_drop_last: true # Whether to drop the last incomplete batch in the DataLoader
balance_batch: true # Whether to balance batch size across dataset_world_size
use_remove_padding: true # Whether to remove padding tokens in the input
# distributed training args
tensor_parallel_degree: 2 # Degree of tensor parallelism
sequence_parallel: true # Whether to enable sequence parallelism
sharding_parallel_degree: -1 # Degree of sharding parallelism
sharding: "stage1" # Sharding strategy, e.g., "stage1" or "stage2"
sharding_parallel_config: "enable_release_grads" # Configuration for sharding parallelism
pipeline_parallel_degree: 1 # Degree of pipeline parallelism
virtual_pp_degree: 1 # Degree of virtual pipeline parallelism
# rollout args
max_prompt_len: 1024 # Maximum length of the prompt, exceeding which will be automatically truncated
max_dec_len: 512 # Maximum length of the response
min_dec_len: 32 # Minimum length of the response
top_p: 1.0 # Top-p sampling parameter
temperature: 1.0 # Temperature parameter for sampling
repetition_penalty: 1.0 # Repetition penalty parameter
rollout_max_num_seqs: 1024 # The maximum number of sequences that can be processed in a single inference
rollout_quant_type: "" # Quantization type, e.g., "weight_only_int8"
# training args
do_train: true # Whether to perform training
seed: 42 # Random seed for reproducibility
global_batch_size: 256 # Global batch size for training (rollouts = rollout_n * global_batch_size)
global_gen_batch_size: -1 # Global generation batch size for dynamic sampling
global_mini_batch_size: 64 # Mini-batch size for training, default = global_batch_size
rollout_n: 1 # Number of rollouts
update_iters: 1 # Number of training iterations for rollout samples
per_device_logprob_batch_size: 4 # Log probability batch size per device
per_device_reward_batch_size: 2 # Reward batch size per device
per_device_value_batch_size: 2 # Value batch size per device
per_device_train_batch_size: 2 # Training micro batch size per device
# gradient_accumulation_steps: 4 # Gradient accumulation steps (auto-calculated): global_bz * rollout_n *
num_train_epochs: 5 # Number of training epochs
max_length: 2048 # Maximum length for training, should be larger than max_prompt_len + max_dec_len
adam_beta1: 0.9 # AdamW optimizer beta1
adam_beta2: 0.999 # AdamW optimizer beta2
adam_epsilon: 1e-8 # AdamW optimizer epsilon
max_grad_norm: 1.0 # Maximum gradient norm for clipping
max_steps: -1 # Maximum number of training steps
save_steps: 300 # Number of steps between model saves
save_strategy: "steps" # Strategy for saving models
ignore_save_lr_and_optim: true # Whether to ignore saving learning rate and optimizer state (leave empty if not specified)
disable_tqdm: true # Whether to disable tqdm progress bar
# actor training args
learning_rate: 1e-6 # Learning rate for training
min_learning_rate: 1e-6 # Minimum learning rate
lr_scheduler_type: "constant" # Learning rate scheduler type
weight_decay: 1e-2 # Weight decay for the AdamW optimizer
warmup_ratio: 0.0 # Number of warmup steps
# critic training args
critic_learning_rate: 1e-5 # Learning rate for critic model
critic_min_learning_rate: 1e-5 # Minimum learning rate for critic model
critic_lr_scheduler_type: "constant" # Learning rate scheduler type for critic model
critic_weight_decay: 1e-2 # Weight decay for the AdamW optimizer of critic model
critic_warmup_ratio: 0.0 # Number of warmup steps for critic model
# RL args
kl_coeff: 0.0 # KL coefficient
kl_loss_coeff: 0.001 # KL loss coefficient
pg_loss_coeff: 1.0 # Policy gradient loss coefficient
entropy_coeff: 0.001 # Entropy coefficient
clip_range_ratio: 0.2 # The clipping range for ratio between the old and new policy. (PPO algorithm)
clip_range_ratio_low: 0.2 # The clipping range for ratio between the old and new policy. (PPO algorithm)
clip_range_ratio_high: 0.2 # The clipping range for ratio between the old and new policy. (PPO algorithm)
clip_range_score: 10.0 # The clipping range for the output of the score model. The reward is clipped into [-clip_range_score, clip_range_score].
enable_overlong_reward_buffer: false # Whether to enable overlong reward buffer
overlong_reward_buffer: 256 # The length of the overlong reward buffer
overlong_penalty_factor: 1.0 # The penalty factor for overlong reward buffer
clip_range_value: 0.5 # The clipping range for the output of the value model. The value is clipped into [-clip_range_value, clip_range_value].
normalize_reward: false # Whether to normalize reward
normalize_advantage: false # Whether to normalize advantage
dynamic_sampling: false # Whether to use dynamic sampling, which is introcuded in DAPO algorithm https://arxiv.org/abs/2503.14476
max_gen_batches: 2 # Maximum number of generation batches for dynamic sampling
use_fp32_compute: true # Whether to use fp32 to compute xx_log_prob,rewards, advantages and loss
# eval args
do_eval: true # Whether to perform evaluation
per_device_eval_batch_size: 1319 # Evaluation batch size per device
evaluation_strategy: "steps" # Evaluation strategy, e.g., "steps"
eval_steps: 10 # Number of steps between evaluations
# device memory optimization args
use_flash_attention: true # Whether to use fused attention operations
use_fused_rms_norm: true # Whether to use fused RMS norm operations, which needs to install fused_ln in slm/model_zoo/gpt-3/external_ops
use_fused_rope: false # Whether to use fused rope operations
use_fused_head_and_loss_fn: true # Whether to use fused head and loss function
use_fused_linear: true # Whether to use fused linear operations. 像是一个没有用的参数
recompute: false # Whether to enable gradient checkpointing for memory optimization
recompute_use_reentrant: false # Whether to use reentrant recompute
recompute_granularity: "full" # Granularity of recompute
bf16: true # Whether to use mixed precision with bfloat16
fp16_opt_level: "O2" # Optimization level for fp16 and bf16 training
amp_master_grad: false # Whether to use float32 weight gradients for master weights in amp opt level=’O2’
amp_custom_black_list: ["reduce_sum", "softmax_with_cross_entropy", "c_softmax_with_cross_entropy", "elementwise_div", "sin", "cos"] # Custom black list for amp
amp_custom_white_list: ["lookup_table", "lookup_table_v2", "flash_attn", "matmul", "matmul_v2", "fused_gemm_epilogue"] # Custom white list for amp
offload_level: "freeze_model" # Level of model offloading to pinned memory, supported values: freeze_model, train_model, optimizer
release_grads: true # Whether to release gradients
offload_optim: false # Whether to offload optimizer to pinned memory
# benchmark args
skip_profile_timer: false # Whether to skip profiling time

View File

@@ -94,7 +94,7 @@ python -u -m paddle.distributed.launch --gpus "0,1,2,3,4,5,6,7" run_finetune.
3. 可以通过设置`weight_quantize_algo`将主干模型量化低比特,例如'weight_only_int4','weight_only_int8','nf4'或'fp4'。具体参考精调参数介绍
4. 设置`use_flash_attention`为 True 使用 FlashAttention。在 FlashAttention 打开的基础上设置`flash_mask`为 True 使用 FlashMask。
5. LoRA API 支持4D 并行策略,可以通过控制`tensor_parallel_degree`、`pipeline_parallel_degree`、 `sharding`、`sharding_parallel_degree`调整并行训练策略,可拓展至**单机 LoRA 微调千亿模型**。
6. 可配置`rslora`、`lora_plus_scale`、`pissa`、`lora_use_mixer`、`mixer_num`、`use_mora`等参数,使用 rsLoRA、LoRa+、PiSSA、MosLoRA(暂不支持张量模型并行)、LinChain(暂不支持张量模型并行)、MoRA(暂不支持张量模型并行) 等算法。
6. 可配置`rslora`、`lora_plus_scale`、`pissa`、`lora_use_mixer`、`use_mora`等参数,使用 rsLoRA、LoRa+、PiSSA、MosLoRA(暂不支持张量模型并行)、MoRA(暂不支持张量模型并行) 等算法。
为了后续的**压缩**和**静态图推理**方便,我们提供 LoRA 参数合并脚本,可以将 LoRA 参数合并到主干模型并保存相应的权重。
```
@@ -177,33 +177,7 @@ python merge_lokr_params.py \
- `device`: 运行环境,默认为 gpu。
</div>
#### 3.4.5 DisLoRA
```
# 单卡DisLoRA
python run_finetune.py ./config/llama/dislora_argument.json
# 多卡DisLoRA(暂不支持张量模型并行)
python -u -m paddle.distributed.launch --gpus "0,1,2,3,4,5,6,7" run_finetune.py ./config/llama/dislora_argument.json
```
为了后续的**压缩**和**静态图推理**方便,我们提供 DisLoRA 参数合并脚本,可以将 DisLoRA 参数合并到主干模型并保存相应的权重。
```
python merge_dislora_params.py \
--model_name_or_path ./base_model \
--dislora_path ./checkpoints/dislora_ckpts \
--merge_dislora_model_path ./checkpoints/dislora_merge \
--device "gpu" \
--low_gpu_mem True
```
<summary>&emsp; 脚本参数介绍</summary><div>
- `dislora_path`: DisLoRA 参数和配置路径,对 DisLoRA 参数进行初始化,默认为 None。
- `model_name_or_path`: 必须,主干模型参数路径,默认为 None。
- `merge_dislora_model_path`: 必须,合并参数后保存路径,默认为 None。
- `device`: 运行环境,默认为 gpu。
</div>
#### 3.4.6 ReFT
#### 3.4.4 ReFT
```
# 单卡ReFT
python run_finetune.py ./config/llama/reft_argument.json
@@ -254,8 +228,6 @@ python ./predict/reft_predictor.py \
- `vera_rank`: VeRA 算法中 rank(秩)的值,默认为8。
- `lokr`: 是否开启 [LoKr](https://arxiv.org/abs/2309.14859) 微调策略,默认为 False。
- `lokr_rank`: LoKr 算法中 rank(秩)的值,默认为8。
- `dislora`: 是否开启 [DisLoRA] 微调策略,默认为 False。
- `dislora_rank`: DisLoRA 算法中 rank(秩)的值,默认为8。
- `use_long_sequence_strategies`: 是否使用长序列扩展策略,默认为 False。
- `reft`: 是否开启 [ReFT](https://arxiv.org/abs/2404.03592) 微调策略,默认为 False。
- `use_mora`: 是否开启 [MoRA](https://arxiv.org/abs/2405.12130) 微调策略,默认为 False。

View File

@@ -40,13 +40,7 @@ except:
pass
from paddlenlp.generation import GenerationConfig, TextIteratorStreamer
from paddlenlp.peft import (
LoRAConfig,
LoRAModel,
PrefixConfig,
PrefixModelForCausalLM,
TAREModel,
)
from paddlenlp.peft import LoRAConfig, LoRAModel, PrefixConfig, PrefixModelForCausalLM
from paddlenlp.taskflow.utils import static_mode_guard
from paddlenlp.trainer import PdArgumentParser
from paddlenlp.transformers import (
@@ -91,9 +85,6 @@ class PredictorArgument:
device: str = field(default="gpu", metadata={"help": "Device"})
dtype: str = field(default=None, metadata={"help": "Model dtype"})
lora_path: str = field(default=None, metadata={"help": "The directory of LoRA parameters. Default to None"})
tare_path: str = field(default=None, metadata={"help": "The directory of TARE parameters. Default to None"})
tare_n: int = field(default=8, metadata={"help": "The num of TARE editors. Default to 8."})
tare_k: int = field(default=7, metadata={"help": "The num of TARE selected editors. Default to 7."})
export_precache: bool = field(default=False, metadata={"help": "whether use prefix weight to do infer"})
prefix_path: str = field(
default=None, metadata={"help": "The directory of Prefix Tuning parameters. Default to None"}
@@ -205,7 +196,6 @@ class PredictorArgument:
)
dynamic_insert: bool = field(default=False, metadata={"help": "whether use dynamic insert"})
total_request_num: int = field(default=None, metadata={"help": "The total number of request data"})
kv_cache_reuse: int = field(default=0)
def __post_init__(self):
if self.speculate_method is not None:
@@ -364,11 +354,6 @@ class DygraphPredictor(BasePredictor):
prefix_path=config.prefix_path,
postprocess_past_key_value=prefix_tuning_params["postprocess_past_key_value"],
)
if config.tare_path is not None:
self.model = TAREModel(base_model=self.model, n=config.tare_n, k=config.tare_k)
self.model.load_model(os.path.join(config.tare_path, "delta_vector.pth"))
self.model.eval()
@paddle.no_grad()
@@ -1170,11 +1155,6 @@ class DygraphBlockInferencePredictor(BlockInferencePredictorMixin):
for cache_k_shape, cache_v_shape in zip(self.cache_k_shapes, self.cache_v_shapes):
self.cache_kvs.append(paddle.zeros(cache_k_shape, dtype=cachekv_dtype))
self.cache_kvs.append(paddle.zeros(cache_v_shape, dtype=cachekv_dtype))
if self.config.kv_cache_reuse:
logger.warning(
f"self.config.kv_cache_reuse = {self.config.kv_cache_reuse}, break, len(self.cache_kvs) = {len(self.cache_kvs)}"
)
break
else:
# for mla's absorption
assert self.cache_v_shapes is None

View File

@@ -31,15 +31,12 @@ from paddlenlp.datasets import (
)
from paddlenlp.metrics import BLEU, Rouge1, Rouge2, RougeL
from paddlenlp.peft import (
DisLoRAConfig,
DisLoRAModel,
LoKrConfig,
LoKrModel,
LoRAConfig,
LoRAModel,
PrefixConfig,
PrefixModelForCausalLM,
TAREModel,
VeRAConfig,
VeRAModel,
)
@@ -71,7 +68,7 @@ from paddlenlp.transformers import (
)
from paddlenlp.transformers.configuration_utils import LlmMetaConfig
from paddlenlp.transformers.longlora import replace_llama_attn, set_group_size
from paddlenlp.trl import DataConfig, DisLoRATrainer, ModelConfig, SFTConfig, SFTTrainer
from paddlenlp.trl import DataConfig, ModelConfig, SFTConfig, SFTTrainer
from paddlenlp.trl.llm_utils import (
ZeroPaddingIterDatasetCallback,
compute_metrics,
@@ -169,13 +166,6 @@ def main():
qlora_weight_blocksize=model_args.qlora_weight_blocksize,
qlora_weight_double_quant=model_args.qlora_weight_double_quant,
qlora_weight_double_quant_block_size=model_args.qlora_weight_double_quant_block_size,
apply_hadamard=model_args.apply_hadamard,
hadamard_block_size=model_args.hadamard_block_size,
quant_input_grad=model_args.quant_input_grad,
quant_weight_grad=model_args.quant_weight_grad,
apply_online_actscale_step=model_args.apply_online_actscale_step,
actscale_moving_rate=model_args.actscale_moving_rate,
fp8_format_type=model_args.fp8_format_type,
)
model_config = AutoConfig.from_pretrained(
@@ -314,15 +304,6 @@ def main():
tokenizer.pad_token_id = tokenizer.eos_token_id
train_ds, dev_ds, test_ds = create_dataset(data_args, training_args)
train_dataset_size = None
if train_ds is not None and model_args.dislora:
train_dataset_size = get_dataset_size(train_ds)
if train_dataset_size is not None:
logger.info(f"Original training dataset size: {train_dataset_size}")
else:
logger.warning("Unable to determine training dataset size for dynamic dash_flag calculation")
# TODO(ZHUI & sijunhe): Temporary implementation. Generalize this logic and move to Trainer later.
if training_args.resume_from_checkpoint is not None and data_args.lazy:
logger.info(
@@ -389,9 +370,7 @@ def main():
if eval_zero_padding and test_ds is not None:
test_ds = intoken_dataset(test_ds, tokenizer=tokenizer, max_length=data_args.max_length)
model = create_peft_model(
model_args, reft_args, training_args, dtype, model_config, model, reft_layers, train_dataset_size
)
model = create_peft_model(model_args, reft_args, training_args, dtype, model_config, model, reft_layers)
def compute_metrics_do_generation(eval_preds):
rouge1 = Rouge1()
@@ -455,33 +434,20 @@ def main():
return_attention_mask=not model_args.flash_mask,
pad_to_multiple_of=data_args.pad_to_multiple_of,
)
if model_args.dislora and hasattr(model_args, "ortho_lambda"):
training_args.dislora_ortho_lambda = model_args.ortho_lambda
trainer_kwargs = {
"model": model,
"args": training_args,
"train_dataset": train_ds,
"eval_dataset": dev_ds,
"tokenizer": tokenizer,
"compute_metrics": metrics,
"data_collator": data_collator_fn if not model_args.reft else ReftDataCollator(data_collator=data_collator_fn),
"do_generation": data_args.eval_with_do_generation,
"callbacks": [ZeroPaddingIterDatasetCallback()] if isinstance(train_ds, ZeroPaddingIterableDataset) else None,
"gen_args": gen_args,
"data_args": data_args,
}
if model_args.dislora:
logger.info("Using DisLoRATrainer for training.")
trainer = DisLoRATrainer(**trainer_kwargs)
else:
trainer = SFTTrainer(**trainer_kwargs)
trainable_parameters = [
p for p in model.parameters() if not p.stop_gradient or ("quantization_linear" in p.name and "w_1" in p.name)
]
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=train_ds,
eval_dataset=dev_ds,
tokenizer=tokenizer,
compute_metrics=metrics,
data_collator=data_collator_fn if not model_args.reft else ReftDataCollator(data_collator=data_collator_fn),
do_generation=data_args.eval_with_do_generation,
callbacks=[ZeroPaddingIterDatasetCallback()] if isinstance(train_ds, ZeroPaddingIterableDataset) else None,
gen_args=gen_args,
data_args=data_args,
)
trainable_parameters = [p for p in model.parameters() if not p.stop_gradient]
trainer.set_optimizer_grouped_parameters(trainable_parameters)
if model_args.lorapro:
optimizer = AdamWLoRAPro(
@@ -501,10 +467,6 @@ def main():
elif last_checkpoint is not None:
checkpoint = last_checkpoint
train_result = trainer.train(resume_from_checkpoint=checkpoint)
if model_args.tare:
model.save_model(os.path.join(training_args.output_dir, "delta_vector.pth"))
if model_args.neftune:
neft_post_hook_handle.remove()
if training_args.benchmark:
@@ -560,9 +522,7 @@ def save_to_aistudio(model_args, training_args, trainer):
)
def create_peft_model(
model_args, reft_args, training_args, dtype, model_config, model, reft_layers, train_dataset_size
):
def create_peft_model(model_args, reft_args, training_args, dtype, model_config, model, reft_layers):
if model_args.prefix_tuning:
if training_args.pipeline_parallel_degree > 1:
raise NotImplementedError("Prefix tuning is not implemented for pipeline parallelism.")
@@ -611,9 +571,6 @@ def create_peft_model(
use_quick_lora=model_args.use_quick_lora,
lora_use_mixer=model_args.lora_use_mixer,
use_mora=model_args.use_mora,
nola=model_args.nola,
nola_basis_num=model_args.nola_basis_num,
mixer_num=model_args.mixer_num,
lorapro=model_args.lorapro,
)
if model_args.lorapro:
@@ -640,53 +597,6 @@ def create_peft_model(
else:
model = LoKrModel.from_pretrained(model=model, lokr_path=model_args.lokr_path)
if model_args.dislora:
# Calculate dynamic dash_flag based on training configuration
if train_dataset_size is not None and training_args.do_train:
# Calculate warmup steps: len(train_data) * num_epochs // (batch_size * gradient_accumulation_steps * 3)
effective_batch_size = (
training_args.per_device_train_batch_size
* training_args.gradient_accumulation_steps
* training_args.dataset_world_size # Consider data parallel
)
calculated_dash_flag = (train_dataset_size * training_args.num_train_epochs) // (effective_batch_size * 3)
# Use calculated value if it's reasonable, otherwise fall back to model_args
if calculated_dash_flag > 0:
dash_flag = calculated_dash_flag
logger.info(
f"Calculated dynamic dash_flag: {dash_flag} based on dataset size: {train_dataset_size}, "
f"epochs: {training_args.num_train_epochs}, effective batch size: {effective_batch_size}"
)
else:
dash_flag = model_args.dash_flag
logger.warning(
f"Calculated dash_flag was {calculated_dash_flag}, using model_args.dash_flag: {dash_flag}"
)
else:
dash_flag = getattr(model_args, "dash_flag", 50)
if train_dataset_size is None:
logger.info(
f"Unable to calculate dynamic dash_flag (dataset size unknown), using configured dash_flag: {dash_flag}"
)
else:
logger.info(f"Not in training mode, using configured dash_flag: {dash_flag}")
if model_args.dislora_path is None:
dislora_config = DisLoRAConfig(
target_modules=model_args.target_modules
if model_args.target_modules
else get_lora_target_modules(model),
r=model_args.dislora_rank,
dislora_alpha=1.5 * model_args.dislora_rank,
dislora_dropout=model_args.dislora_dropout,
dtype=dtype,
base_model_name_or_path=model_args.model_name_or_path,
s_tsd=model_args.s_tsd,
dash_flag=dash_flag, # Use calculated dash_flag
ortho_lambda=model_args.ortho_lambda,
)
model = DisLoRAModel(model, dislora_config)
if model_args.reft:
intervention_dtype = dtype
intervention_params = {
@@ -730,10 +640,6 @@ def create_peft_model(
model.mark_only_vera_as_trainable(notfreezeB=True)
model.print_trainable_parameters()
if model_args.tare:
model = TAREModel(base_model=model, n=model_args.tare_n, k=model_args.tare_k)
model.print_trainable_parameters()
return model
@@ -830,24 +736,5 @@ def create_dataset(data_args, training_args):
return train_ds, dev_ds, test_ds
def get_dataset_size(dataset):
"""Get the size of a dataset, handling both lazy and regular datasets"""
if dataset is None:
return None
try:
if hasattr(dataset, "__len__"):
return len(dataset)
elif hasattr(dataset, "_length"):
return dataset._length
else:
# For lazy datasets, we might need to iterate once to count
logger.warning("Unable to determine dataset size directly for lazy loading dataset")
return None
except Exception as e:
logger.warning(f"Error getting dataset size: {e}")
return None
if __name__ == "__main__":
main()

View File

@@ -1,290 +0,0 @@
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import os
import paddle
from paddlenlp.peft import DisLoRAConfig, DisLoRAModel
from paddlenlp.transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
from paddlenlp.utils.env import CONFIG_NAME
def parse_arguments():
"""解析命令行参数"""
parser = argparse.ArgumentParser()
parser.add_argument("--model_name_or_path", default=None, help="The directory of pretrained model.")
parser.add_argument("--dislora_path", default="", help="The directory of dislora parameters. Default to None")
parser.add_argument(
"--merge_dislora_model_path",
default="",
help="The directory of merged parameters. Default to None",
)
parser.add_argument("--device", type=str, default="gpu", help="Device")
parser.add_argument(
"--low_gpu_mem", type=bool, default=True, help="Whether to use low gpu memory. Default to False"
)
return parser.parse_args()
def weight_process(name, dislora_config, state_dict):
"""
Based on the DisLoRA algorithm for processing weight merging:
The final weight = W_prin + W_res + W_TSD
However, here we do not directly add the adapter to the base model; instead, we reconstruct the entire weight matrix.
Args:
name: Layer name (e.g. "model.layers.0.self_attn.q_proj")
dislora_config: DisLoRA configuration
state_dict: Model state dictionary
# Define the weight_process function to handle the DisLoRA weight merging. The parameters include the layer name, DisLoRA configuration, and the model state dictionary.
"""
weight_key = name + ".weight"
if weight_key not in state_dict:
print(f"Warning: {weight_key} not found in state_dict")
return
w_prin = state_dict[weight_key]
print(f"Processing layer: {name}")
print(f" W_prin shape: {w_prin.shape}")
scaling = dislora_config.dislora_alpha / dislora_config.r
final_weight = w_prin.clone()
ur_key = name + ".Direc_Ur.weight"
sr_key = name + ".Direc_Sr"
vhr_key = name + ".Direc_Vhr.weight"
w_res_added = False
if all(key in state_dict for key in [ur_key, sr_key, vhr_key]):
direc_ur = state_dict[ur_key] # [r, out_features]
direc_sr = state_dict[sr_key] # [r]
direc_vhr = state_dict[vhr_key] # [in_features, r]
s_diag = paddle.diag(direc_sr) # [r, r]
w_res = direc_vhr @ s_diag @ direc_ur * scaling # [in_features, out_features]
if w_res.shape != w_prin.shape:
print(f" Error: W_res shape {w_res.shape} doesn't match W_prin shape {w_prin.shape}")
return
final_weight += w_res
w_res_added = True
print(f" ✓ Added W_res with scaling factor: {scaling}")
else:
print(f" ⚠ W_res components not found for {name}")
utsd_key = name + ".Direc_Utsd.weight"
stsd_key = name + ".Direc_Stsd"
vhtsd_key = name + ".Direc_Vhtsd.weight"
w_tsd_added = False
if all(key in state_dict for key in [utsd_key, stsd_key, vhtsd_key]):
direc_utsd = state_dict[utsd_key] # [s_tsd, out_features]
direc_stsd = state_dict[stsd_key] # [s_tsd]
direc_vhtsd = state_dict[vhtsd_key] # [in_features, s_tsd]
if not paddle.all(direc_stsd == 0.0):
s_diag_tsd = paddle.diag(direc_stsd) # [s_tsd, s_tsd]
w_tsd = direc_vhtsd @ s_diag_tsd @ direc_utsd * scaling # [in_features, out_features]
if w_tsd.shape != w_prin.shape:
print(f" Error: W_TSD shape {w_tsd.shape} doesn't match W_prin shape {w_prin.shape}")
return
final_weight += w_tsd
w_tsd_added = True
print(f" ✓ Added W_TSD with scaling factor: {scaling}")
else:
print(f" ⚠ W_TSD parameters are uninitialized (all zeros) for {name}")
else:
print(f" ⚠ W_TSD components not found for {name}")
state_dict[weight_key] = final_weight
keys_to_remove = []
for key in state_dict.keys():
if key.startswith(name + ".Direc_") or key == name + ".step":
keys_to_remove.append(key)
for key in keys_to_remove:
removed_param = state_dict.pop(key)
print(f" ✓ Removed DisLoRA parameter: {key} (shape: {removed_param.shape})")
components = []
if w_res_added:
components.append("W_res")
if w_tsd_added:
components.append("W_TSD")
if components:
print(f" ✓ Successfully merged: W_prin + {' + '.join(components)}")
else:
print(" ✓ Kept original W_prin (no adaptations found)")
print()
def merge():
args = parse_arguments()
paddle.set_device(args.device)
print("Loading DisLoRA configuration...")
dislora_config = DisLoRAConfig.from_pretrained(args.dislora_path)
if dislora_config.base_model_name_or_path is None:
if args.model_name_or_path is None:
raise ValueError("We can not find a valid model_name_or_path.")
else:
dislora_config.base_model_name_or_path = args.model_name_or_path
print("Loading model configuration...")
if os.path.isfile(os.path.join(args.dislora_path, CONFIG_NAME)):
config = AutoConfig.from_pretrained(args.dislora_path)
elif args.model_name_or_path is not None:
config = AutoConfig.from_pretrained(args.model_name_or_path)
else:
raise ValueError(
f"We can not find config.json in dislora_path: {args.dislora_path} or find a valid model_name_or_path."
)
config.dtype = dislora_config.dtype
if (
dislora_config.dtype == "bfloat16"
or (
hasattr(config, "quantization_config")
and hasattr(config.quantization_config, "weight_quantize_algo")
and config.quantization_config.weight_quantize_algo in ["nf4", "fp4"]
)
) and args.device == "cpu":
raise ValueError("We can not apply bfloat16 or nf4/fp4 dislora merge on cpu.")
print("Loading base model...")
model = AutoModelForCausalLM.from_pretrained(
dislora_config.base_model_name_or_path,
config=config,
low_cpu_mem_usage=args.low_gpu_mem,
)
print("Loading DisLoRA model...")
model = DisLoRAModel.from_pretrained(model=model, dislora_path=args.dislora_path, dislora_config=dislora_config)
model.eval()
model_state_dict = model.model.state_dict()
print(f"Total parameters in state_dict: {len(model_state_dict)}")
step_keys = [key for key in model_state_dict.keys() if key.endswith(".step")]
if step_keys:
print(f"Found {len(step_keys)} step parameters in loaded model:")
for key in step_keys[:5]:
print(f" {key}")
if len(step_keys) > 5:
print(f" ... and {len(step_keys) - 5} more")
else:
print("No step parameters found in loaded model")
print()
print("Identifying DisLoRA layers...")
dislora_name_set = set()
for key in model_state_dict.keys():
if any(
dislora_param in key
for dislora_param in ["Direc_Ur", "Direc_Sr", "Direc_Vhr", "Direc_Utsd", "Direc_Stsd", "Direc_Vhtsd"]
):
for param_type in ["Direc_Ur", "Direc_Sr", "Direc_Vhr", "Direc_Utsd", "Direc_Stsd", "Direc_Vhtsd"]:
if f".{param_type}" in key:
layer_name = key.split(f".{param_type}")[0]
dislora_name_set.add(layer_name)
break
dislora_name_list = sorted(list(dislora_name_set))
print(f"Found {len(dislora_name_list)} DisLoRA layers:")
for i, name in enumerate(dislora_name_list, 1):
print(f" {i:2d}. {name}")
print()
print("Merging DisLoRA parameters...")
for i, name in enumerate(dislora_name_list, 1):
print(f"[{i}/{len(dislora_name_list)}] Processing: {name}")
weight_process(name, dislora_config, model_state_dict)
print("Cleaning up remaining step parameters...")
step_keys_to_remove = [key for key in model_state_dict.keys() if key.endswith(".step")]
for key in step_keys_to_remove:
removed_param = model_state_dict.pop(key)
print(f" ✓ Removed step parameter: {key} (shape: {removed_param.shape})")
if step_keys_to_remove:
print(f"✓ Removed {len(step_keys_to_remove)} step parameters")
else:
print("✓ No step parameters found")
print()
print("Verifying parameter cleanup...")
remaining_dislora_params = []
remaining_step_params = []
for key in model_state_dict.keys():
if any(
dislora_param in key
for dislora_param in ["Direc_Ur", "Direc_Sr", "Direc_Vhr", "Direc_Utsd", "Direc_Stsd", "Direc_Vhtsd"]
):
remaining_dislora_params.append(key)
if key.endswith(".step"):
remaining_step_params.append(key)
if remaining_dislora_params:
print(f"Warning: {len(remaining_dislora_params)} DisLoRA parameters still remain:")
for param in remaining_dislora_params:
print(f" - {param}")
else:
print("✓ All DisLoRA parameters successfully removed")
if remaining_step_params:
print(f"Warning: {len(remaining_step_params)} step parameters still remain:")
for param in remaining_step_params:
print(f" - {param}")
else:
print("✓ All step parameters successfully removed")
print()
print("Saving merged model...")
os.makedirs(args.merge_dislora_model_path, exist_ok=True)
model.model.save_pretrained(args.merge_dislora_model_path, state_dict=model_state_dict)
print("Saving tokenizer...")
tokenizer = AutoTokenizer.from_pretrained(dislora_config.base_model_name_or_path)
tokenizer.save_pretrained(args.merge_dislora_model_path)
print("=" * 80)
print("✓ DisLoRA merge completed successfully!")
print(f"✓ Merged model saved to: {args.merge_dislora_model_path}")
print(f"✓ Processed {len(dislora_name_list)} DisLoRA layers")
print("=" * 80)
if __name__ == "__main__":
merge()

View File

@@ -78,15 +78,7 @@ def weight_process(name, quant_config, lora_config, state_dict, device):
raise ValueError(f"quant_config.weight_quantize_algo {quant_config.weight_quantize_algo} is not supported.")
def get_mixer(mixer, mixer_num, index=0):
if index == mixer_num - 1:
return mixer[index]
else:
return mixer[index] @ get_mixer(mixer, mixer_num, index + 1)
def lora_process(name, layer, lora_config, state_dict, device, lora_state_dict=None):
target_device = device if device == "cpu" else device + ":0"
if (name + ".weight") not in state_dict.keys():
@@ -94,44 +86,35 @@ def lora_process(name, layer, lora_config, state_dict, device, lora_state_dict=N
weight = state_dict.pop(name + ".weight")
lora_use_mixer = lora_config.lora_use_mixer
mixer_num = lora_config.mixer_num
mixer = {}
use_mora = lora_config.use_mora
if lora_state_dict is None:
lora_A = state_dict.pop(name + ".lora_A")
if not use_mora:
lora_B = state_dict.pop(name + ".lora_B")
if lora_use_mixer:
for i in range(mixer_num):
mixer[i] = state_dict.pop(name + ".lora_mixer_" + str(i))
lora_AB = state_dict.pop(name + ".lora_AB")
else:
lora_A = lora_state_dict.pop(name + ".lora_A")
if not use_mora:
lora_B = lora_state_dict.pop(name + ".lora_B")
if lora_use_mixer:
for i in range(mixer_num):
mixer[i] = state_dict.pop(name + ".lora_mixer_" + str(i))
lora_AB = lora_state_dict.pop(name + ".lora_AB")
if device != "cpu":
weight = weight.to(target_device)
lora_A = lora_A.to(target_device)
if not use_mora:
lora_B = lora_B.to(target_device)
if lora_use_mixer:
for key in mixer.keys():
mixer[key] = mixer[key].to(target_device)
lora_AB = lora_AB.to(target_device)
if device == "cpu" and weight.dtype.name == "BF16":
weight = weight.astype("float32")
lora_A = lora_A.astype("float32")
if not use_mora:
lora_B = lora_B.astype("float32")
if lora_use_mixer:
for key in mixer.keys():
mixer[key] = mixer[key].astype(lora_config.dtype)
delta_weight = layer.get_delta_weight(lora_A, lora_B, get_mixer(mixer, mixer_num))
lora_AB = lora_AB.astype(lora_config.dtype)
delta_weight = layer.get_delta_weight(lora_A, lora_B, lora_AB)
elif use_mora:
delta_weight = layer.get_delta_weight(lora_A)
else:
@@ -139,7 +122,7 @@ def lora_process(name, layer, lora_config, state_dict, device, lora_state_dict=N
out = (weight + delta_weight).astype(lora_config.dtype)
else:
if lora_use_mixer:
delta_weight = layer.get_delta_weight(lora_A, lora_B, get_mixer(mixer, mixer_num))
delta_weight = layer.get_delta_weight(lora_A, lora_B, lora_AB)
elif use_mora:
delta_weight = layer.get_delta_weight(lora_A)
else:

View File

@@ -176,7 +176,7 @@ def get_whole_word_mask_tokens(tokens, words, max_word_length=6):
i += 1
continue
# add "##" mark on the middle tokens of Chinese words
# add "##" mark on the middel tokens of Chinese words
# such as ["通过", "利用"] -> ["通", "##过", "利", "##用"]
has_add = False
for length in range(max_word_length, 0, -1):

View File

@@ -11,31 +11,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import paddle
import paddle.distributed as dist
from paddle import _C_ops
from paddle.distributed import fleet
from paddle.distributed.fleet.utils.log_util import logger
from paddle.framework import core
_raise_cuda_env_unset_warning = True
_mp_async_allreduce = False
_sp_async_reduce_scatter = False
def _check_environment_for_overlap():
if int(os.getenv("CUDA_DEVICE_MAX_CONNECTIONS", "0")) != 1:
global _raise_cuda_env_unset_warning
if _raise_cuda_env_unset_warning:
logger.warning(
"You set enable_mp_async_allreduce or enable_sp_async_reduce_scatter, but you forget to set environment "
"variable CUDA_DEVICE_MAX_CONNECTIONS=1, which may leads to performance "
"loss. Try to export CUDA_DEVICE_MAX_CONNECTIONS=1 for better performance."
)
_raise_cuda_env_unset_warning = False
def is_fused_matmul_bias_supported():
if paddle.is_compiled_with_cuda() and not paddle.is_compiled_with_rocm() or paddle.is_compiled_with_xpu():
@@ -50,125 +29,6 @@ else:
origin_linear = paddle.nn.functional.linear
def mp_async_allreduce(x_grad):
if _mp_async_allreduce and x_grad.process_mesh is not None:
_check_environment_for_overlap()
mp_placement_index = x_grad.process_mesh.dim_names.index("mp")
if mp_placement_index != -1 and x_grad.placements[mp_placement_index].is_partial():
hcg = fleet.get_hybrid_communicate_group()
model_parallel_group = hcg.get_model_parallel_group()
task = dist.stream.all_reduce(
x_grad._local_value(),
group=model_parallel_group,
sync_op=False,
)
return task
else:
return None
else:
return None
def sp_async_reducesctter(x_grad):
if _sp_async_reduce_scatter and x_grad.process_mesh is not None:
_check_environment_for_overlap()
mp_placement_index = x_grad.process_mesh.dim_names.index("mp")
if mp_placement_index != -1 and x_grad.placements[mp_placement_index].is_partial():
hcg = fleet.get_hybrid_communicate_group()
model_parallel_group = hcg.get_model_parallel_group()
parallelism = model_parallel_group.nranks
assert (
x_grad.shape[0] % parallelism == 0
), f"Input sequence length {x_grad.shape[0]} can't be divided exactly by sequence parallelism {parallelism}"
# reduce-scatter dx
x_grad_global_shape = x_grad.shape
x_grad_global_shape[0] = x_grad_global_shape[0] // parallelism
x_grad_local = x_grad._local_value()
x_grad_local_shape = x_grad_local.shape
x_grad_local_shape[0] = x_grad_local_shape[0] // parallelism
dx_local = paddle.empty(shape=x_grad_local_shape, dtype=x_grad.dtype)
task = dist.stream.reduce_scatter(
dx_local,
x_grad_local,
op=dist.ReduceOp.SUM,
group=model_parallel_group,
sync_op=False,
)
return task, dx_local, x_grad_global_shape
else:
return None
else:
return None
def sync_mp_allreduce(task, dist_tensor):
mp_placement_index = dist_tensor.process_mesh.dim_names.index("mp")
new_placments = list()
for idx, placement in enumerate(dist_tensor.placements):
if idx == mp_placement_index:
new_placments.append(dist.Replicate())
else:
new_placments.append(placement)
place = paddle.framework._current_expected_place()
place = paddle.framework._get_paddle_place(place)
task.wait()
return paddle.Tensor(
dist_tensor._local_value(),
dims=dist_tensor.shape,
process_mesh=dist_tensor.process_mesh,
placements=new_placments,
place=place,
)
def sync_sp_reducescatter(task, dist_tensor):
task, dx_local, x_grad_global_shape = task
placements = [dist.Shard(1), dist.Shard(0)]
place = paddle.framework._current_expected_place()
place = paddle.framework._get_paddle_place(place)
task.wait()
return paddle.Tensor(
dx_local,
dims=x_grad_global_shape,
process_mesh=dist_tensor.process_mesh,
placements=placements,
place=place,
)
# modify from Paddle/python/paddle/distributed/auto_parallel/moe_utils.py
def _dist_reshape(dist_tensor):
local_tensor = dist_tensor._local_value()
tgt_global_shape = [dist_tensor.shape[0] * dist_tensor.shape[1], dist_tensor.shape[2]]
tgt_local_shape = [local_tensor.shape[0] * local_tensor.shape[1], local_tensor.shape[2]]
place = paddle.framework._current_expected_place()
place = paddle.framework._get_paddle_place(place)
local_tensor = local_tensor.reshape(tgt_local_shape)
if dist_tensor.placements[1].is_shard():
new_placements = [dist.Shard(0), dist.Shard(1)]
else:
new_placements = [dist.Shard(0), dist.Replicate()]
out = paddle.Tensor(
local_tensor,
dims=tgt_global_shape,
process_mesh=dist_tensor.process_mesh,
placements=new_placements,
place=place,
)
out.stop_gradient = dist_tensor.stop_gradient
return out
class FusedLinearWithGradAdd(paddle.autograd.PyLayer):
@staticmethod
def forward(ctx, x, weight, bias=None, name=None):
@@ -181,131 +41,39 @@ class FusedLinearWithGradAdd(paddle.autograd.PyLayer):
x, weight, bias = ctx.saved_tensor()
x_grad = paddle.matmul(y_grad, weight, transpose_y=True)
if _sp_async_reduce_scatter:
task = sp_async_reducesctter(x_grad)
else:
task = mp_async_allreduce(x_grad)
# _C_ops.fused_linear_param_grad_add(x, y_grad, weight_grad, bias_grad, multi precision, has bias)
# _C_ops.fused_linear_param_grad_add(x, y_grad, dw, db, multi precision, has bias)
if bias is None:
if hasattr(weight, "main_grad"):
weight.main_grad, _ = _C_ops.fused_linear_param_grad_add(
x, y_grad, weight.main_grad, None, True, False
)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, None
else:
if weight.grad is not None:
weight.grad, _ = _C_ops.fused_linear_param_grad_add(
x, y_grad, weight.grad, None, False if weight.grad.dtype != paddle.float32 else True, False
)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
weight.grad, _ = _C_ops.fused_linear_param_grad_add(x, y_grad, weight.grad, None, False, False)
return x_grad, None
else:
weight_grad, _ = _C_ops.fused_linear_param_grad_add(x, y_grad, None, None, False, False)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, weight_grad
if hasattr(weight, "main_grad") and hasattr(bias, "main_grad"):
weight.main_grad, bias.main_grad = _C_ops.fused_linear_param_grad_add(
x, y_grad, weight.main_grad, bias.main_grad, True, True
)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, None, None
else:
if weight.grad is not None:
assert bias.grad is not None
weight.grad, bias.grad = _C_ops.fused_linear_param_grad_add(
x, y_grad, weight.grad, bias.grad, False if weight.grad.dtype != paddle.float32 else True, True
x, y_grad, weight.grad, bias.grad, False, True
)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, None, None
else:
weight_grad, bias_grad = _C_ops.fused_linear_param_grad_add(x, y_grad, None, None, False, True)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, weight_grad, bias_grad
class OverlapLinear(paddle.autograd.PyLayer):
@staticmethod
def forward(ctx, x, weight, bias=None, name=None):
y = origin_linear(x, weight, bias)
ctx.save_for_backward(x, weight, bias)
return y
@staticmethod
def backward(ctx, y_grad):
x, weight, bias = ctx.saved_tensor()
x_grad = paddle.matmul(y_grad, weight, transpose_y=True)
if _sp_async_reduce_scatter:
task = sp_async_reducesctter(x_grad)
else:
task = mp_async_allreduce(x_grad)
if _sp_async_reduce_scatter:
y_grad = _dist_reshape(y_grad)
else:
y_grad = y_grad.reshape([-1, y_grad.shape[-1]])
weight_grad = paddle.matmul(
_dist_reshape(x) if _sp_async_reduce_scatter else x.reshape([-1, x.shape[-1]]),
y_grad,
transpose_x=True,
)
if bias is None:
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, weight_grad
else:
bias_grad = paddle.sum(y_grad, axis=0)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, weight_grad, bias_grad
def mock_layers(
enable_fused_linear_grad_add=True, enable_mp_async_allreduce=False, enable_sp_async_reduce_scatter=False
):
global _mp_async_allreduce
global _sp_async_reduce_scatter
_mp_async_allreduce = enable_mp_async_allreduce
_sp_async_reduce_scatter = enable_sp_async_reduce_scatter
if enable_fused_linear_grad_add:
paddle.nn.functional.linear = FusedLinearWithGradAdd.apply
if is_fused_matmul_bias_supported():
paddle.incubate.nn.functional.fused_linear = FusedLinearWithGradAdd.apply
else:
paddle.nn.functional.linear = OverlapLinear.apply
if is_fused_matmul_bias_supported():
paddle.incubate.nn.functional.fused_linear = OverlapLinear.apply
def mock_layers():
paddle.nn.functional.linear = FusedLinearWithGradAdd.apply
if is_fused_matmul_bias_supported():
paddle.incubate.nn.functional.fused_linear = FusedLinearWithGradAdd.apply

View File

@@ -106,7 +106,7 @@ def parallel_cross_entropy(
"1. soft_label=False is set for parallel computation (current value: {}) \n"
"2. Input tensor is properly sharded (current sharding status: {}) \n".format(
soft_label,
input.placements,
input_placement,
)
)

View File

@@ -172,7 +172,7 @@ def forward_pre_hook(layer, input):
ipp = id2ipp[id(layer)]
def forward_post_hook(layer, input, output):
def forward_post_hook(layer, input, ouput):
paddle.nn.functional.linear = paddle_nn_functional_linear
if is_fused_matmul_bias_supported():
paddle.incubate.nn.functional.fused_linear = paddle_incubate_nn_functional_fused_linear

View File

@@ -39,34 +39,17 @@ enum class Layout {
ColMajor
};
__device__ __host__ constexpr int get_num_math_warpgroups(int block_m) {
return block_m == 64 ? 1 : 2;
}
template <uint32_t kNumTMAThreads, uint32_t kNumMathThreadsPerGroup>
__device__ __host__ constexpr int get_num_threads_per_sm(int block_m) {
DG_STATIC_ASSERT(kNumMathThreadsPerGroup == 128, "Only support 128 threads per math group");
return get_num_math_warpgroups(block_m) * kNumMathThreadsPerGroup + kNumTMAThreads;
}
template <int kNumFormerIters, int kGap, int kEnd>
__device__ __host__ void outer_launch_k_iterations(const auto& inner_launch_k_iterations, const auto& func, int num_former_iters) {
if (num_former_iters == kNumFormerIters) {
inner_launch_k_iterations(func, cute::Int<kNumFormerIters>{});
return;
}
if constexpr (kNumFormerIters + kGap <= kEnd)
outer_launch_k_iterations<kNumFormerIters + kGap, kGap, kEnd>(inner_launch_k_iterations, func, num_former_iters);
return (block_m == 64 ? 1 : 2) * kNumMathThreadsPerGroup + kNumTMAThreads;
}
template <uint32_t SHAPE_N, uint32_t SHAPE_K,
uint32_t BLOCK_M, uint32_t BLOCK_N, uint32_t BLOCK_K,
uint32_t BLOCK_N_PADDING,
uint32_t kSwizzleDMode,
uint32_t kNumGroups, uint32_t kNumStages,
uint32_t kNumTMAThreads, uint32_t kNumMathThreadsPerGroup,
uint32_t kNumTMAMulticast, bool kIsTMAMulticastOnA,
uint32_t kNumTMAMulticast,
GemmType kGemmType>
__global__ void __launch_bounds__(get_num_threads_per_sm<kNumTMAThreads, kNumMathThreadsPerGroup>(BLOCK_M), 1)
fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
@@ -78,16 +61,15 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
#if (defined(__CUDA_ARCH__) and (__CUDA_ARCH__ >= 900)) or defined(__CLION_IDE__)
// Scaling checks
DG_STATIC_ASSERT(BLOCK_K == 128, "Only support per-128-channel FP8 scaling");
DG_STATIC_ASSERT(ceil_div(BLOCK_N, BLOCK_K) == 1 or (constexpr_gcd(BLOCK_N, BLOCK_K) == BLOCK_N - BLOCK_K), "Too much B scales in a single block");
DG_STATIC_ASSERT(ceil_div(BLOCK_N, BLOCK_K) == 1, "Too much B scales in a single block");
// Types
using WGMMA = typename FP8MMASelector<BLOCK_N>::type;
using Barrier = cutlass::arch::ClusterTransactionBarrier;
DG_STATIC_ASSERT(BLOCK_M % WGMMA::M == 0, "Invalid block size");
// Shared memory
static constexpr int kMustUseUniformedScaleB = (BLOCK_K % BLOCK_N == 0);
static constexpr uint32_t SMEM_D_SIZE = BLOCK_M * (BLOCK_N + BLOCK_N_PADDING) * sizeof(__nv_bfloat16);
static constexpr uint32_t SMEM_D_SIZE = BLOCK_M * BLOCK_N * sizeof(__nv_bfloat16);
static constexpr uint32_t SMEM_A_SIZE_PER_STAGE = BLOCK_M * BLOCK_K * sizeof(__nv_fp8_e4m3);
static constexpr uint32_t SMEM_B_SIZE_PER_STAGE = BLOCK_N * BLOCK_K * sizeof(__nv_fp8_e4m3);
static constexpr uint32_t SMEM_SCALES_A_SIZE_PER_STAGE = BLOCK_M * sizeof(float);
@@ -107,11 +89,7 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
cute::prefetch_tma_descriptor(reinterpret_cast<cute::TmaDescriptor const*>(&tensor_map_a));
cute::prefetch_tma_descriptor(reinterpret_cast<cute::TmaDescriptor const*>(&tensor_map_b));
cute::prefetch_tma_descriptor(reinterpret_cast<cute::TmaDescriptor const*>(&tensor_map_scales_a));
// `tensor_map_d` is only used in swizzling mode
// For the `kSwizzleDMode == 0 and BLOCK_N_PADDING == 0` case, it will be treated as padding mode
if constexpr (kSwizzleDMode > 0)
cute::prefetch_tma_descriptor(reinterpret_cast<cute::TmaDescriptor const*>(&tensor_map_d));
cute::prefetch_tma_descriptor(reinterpret_cast<cute::TmaDescriptor const*>(&tensor_map_d));
}
__syncwarp();
@@ -150,8 +128,6 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
// Initialize barriers
DG_STATIC_ASSERT(kNumTMAMulticast <= 32, "Too many TMA multicast");
if (threadIdx.x == kNumMathThreads) {
// NOTES: we always use `lane_idx` to arrive for the `lane_idx`-th CTA in the cluster,
// even with TMA multicast disabled, we want to make the behavior aligned
#pragma unroll
for (int i = 0; i < kNumStages; ++ i) {
full_barriers[i]->init(1);
@@ -169,23 +145,15 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
// For pipeline unrolling
struct DivisibleK {};
struct NotDivisibleK {};
auto launch_k_iterations = [](const auto& func, int num_former_iters) {
constexpr bool kShouldOptimize = BLOCK_K / constexpr_gcd(BLOCK_K, BLOCK_N) <= 4 and not kMustUseUniformedScaleB;
constexpr int kGap = constexpr_gcd(BLOCK_K, BLOCK_N) / 8;
constexpr int kEnd = kShouldOptimize ? BLOCK_K / 8 : 0;
// NOTES: for too-many branches (> 5), we disable this optimization
// Otherwise, the compiler must know the dynamic variable `num_former_iters`'s real value
outer_launch_k_iterations<0, kGap, kEnd>([](const auto& func, auto num_former_iters_type) {
if constexpr (SHAPE_K % kFullKOfAllStages == 0) {
for (int k_iter = 0; k_iter < kNumIterations; ++ k_iter)
func(k_iter, DivisibleK{}, num_former_iters_type);
} else {
for (int k_iter = 0; k_iter < kNumIterations - 1; ++ k_iter)
func(k_iter, DivisibleK{}, num_former_iters_type);
func(kNumIterations - 1, NotDivisibleK{}, num_former_iters_type);
}
}, func, kShouldOptimize ? num_former_iters : 0);
auto launch_k_iterations = [](const auto& func) {
if constexpr (SHAPE_K % kFullKOfAllStages == 0) {
for (int k_iter = 0; k_iter < kNumIterations; ++ k_iter)
func(k_iter, DivisibleK{});
} else {
for (int k_iter = 0; k_iter < kNumIterations - 1; ++ k_iter)
func(k_iter, DivisibleK{});
func(kNumIterations - 1, NotDivisibleK{});
}
};
// Register reconfigurations
@@ -194,7 +162,7 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
// Block scheduler
uint32_t m_block_idx, n_block_idx;
auto scheduler = Scheduler<kGemmType, SHAPE_N, BLOCK_M, BLOCK_N, kNumGroups, kNumTMAMulticast, kIsTMAMulticastOnA>(shape_m, grouped_layout);
auto scheduler = Scheduler<kGemmType, SHAPE_N, BLOCK_M, BLOCK_N, kNumGroups, kNumTMAMulticast>(shape_m, grouped_layout);
if (threadIdx.x >= kNumMathThreads) {
// TMA warp-group for loading data
@@ -204,34 +172,28 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
if (threadIdx.x == kNumMathThreads) {
// Persistently schedule over blocks
while (scheduler.get_next_block(m_block_idx, n_block_idx)) {
launch_k_iterations([&](int k_iter, auto type, auto _) {
launch_k_iterations([&](int k_iter, auto type) {
constexpr bool kHasDivisibleStages = std::is_same_v<decltype(type), DivisibleK>;
constexpr int kNumInnerStages = kHasDivisibleStages ? kNumStages : (SHAPE_K % kFullKOfAllStages) / BLOCK_K;
DG_STATIC_ASSERT(kNumInnerStages != 0, "Invalid number of inner stages");
// Assign TMA multicast number into A and B
constexpr int kNumTMAMulticastOnA = kIsTMAMulticastOnA ? kNumTMAMulticast : 1;
constexpr int kNumTMAMulticastOnB = kIsTMAMulticastOnA ? 1 : kNumTMAMulticast;
// NOTES: unrolling and `kNumInnerStages` are vital for performance, NVCC will try to eliminate all
// shared memory pointers, e.g. `full_barriers` registers, if all the access indices are constant
#pragma unroll
for (uint32_t s = 0; s < kNumInnerStages; ++ s) {
// Wait consumer release
empty_barriers[s]->wait((scheduler.current_iter * kNumIterations + k_iter + 1) & 1);
// Issue TMA A
// Issue TMA A with broadcasting
auto& full_barrier = *full_barriers[s];
int k_idx = k_iter * kFullKOfAllStages + s * BLOCK_K;
tma_copy<kNumTMAMulticastOnA>(&tensor_map_a, reinterpret_cast<uint64_t*>(&full_barrier),
smem_a[s], k_idx, scheduler.get_global_idx(shape_m, BLOCK_M, m_block_idx));
tma_copy<kNumTMAMulticastOnA>(&tensor_map_scales_a, reinterpret_cast<uint64_t*>(&full_barrier),
smem_scales_a[s], m_block_idx * BLOCK_M,
scheduler.get_global_idx(SHAPE_K_SCALES, 1, k_idx / BLOCK_K));
tma_copy<kNumTMAMulticast>(&tensor_map_a, reinterpret_cast<uint64_t*>(&full_barrier),
smem_a[s], k_idx, scheduler.get_global_idx(shape_m, BLOCK_M, m_block_idx));
tma_copy<kNumTMAMulticast>(&tensor_map_scales_a, reinterpret_cast<uint64_t*>(&full_barrier),
smem_scales_a[s], m_block_idx * BLOCK_M,
scheduler.get_global_idx(SHAPE_K_SCALES, 1, k_idx / BLOCK_K));
// Issue TMA B
tma_copy<kNumTMAMulticastOnB>(&tensor_map_b, reinterpret_cast<uint64_t*>(&full_barrier),
smem_b[s], k_idx, scheduler.get_global_idx<false>(SHAPE_N, BLOCK_N, n_block_idx, m_block_idx));
// Issue TMA B without broadcasting
tma_copy(&tensor_map_b, reinterpret_cast<uint64_t*>(&full_barrier),
smem_b[s], k_idx, scheduler.get_global_idx<false>(SHAPE_N, BLOCK_N, n_block_idx, m_block_idx));
full_barrier.arrive_and_expect_tx(SMEM_A_SIZE_PER_STAGE + SMEM_B_SIZE_PER_STAGE + SMEM_SCALES_A_SIZE_PER_STAGE);
}
@@ -241,7 +203,7 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
empty_barriers[s]->wait((scheduler.current_iter * kNumIterations + k_iter + 1) & 1);
full_barriers[s]->arrive();
}
}, 0);
});
}
// To safely deconstruct distributed shared barriers, we need another round of empty waits
@@ -282,9 +244,7 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
cutlass::arch::NamedBarrier(kNumMathThreads).sync();
// Accumulation for WGMMA or CUDA promotion
constexpr int WAVE_BLOCK_M = WGMMA::M * get_num_math_warpgroups(BLOCK_M);
DG_STATIC_ASSERT(BLOCK_M % WAVE_BLOCK_M == 0, "Invalid block sizes");
float accum[WGMMA::kNumAccum], final_accum[WGMMA::kNumAccum * (BLOCK_M / WAVE_BLOCK_M)] = {0};
float accum[WGMMA::kNumAccum], final_accum[WGMMA::kNumAccum] = {0};
// Empty barrier arrival
auto empty_barrier_arrive = [&](int s) {
@@ -296,7 +256,7 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
};
// Launch MMAs
launch_k_iterations([&](int k_iter, auto type, auto num_former_iters_type) {
launch_k_iterations([&](int k_iter, auto type) {
constexpr bool kHasDivisibleStages = std::is_same_v<decltype(type), DivisibleK>;
constexpr int kNumInnerStages = kHasDivisibleStages ? kNumStages : (SHAPE_K % kFullKOfAllStages) / BLOCK_K;
DG_STATIC_ASSERT(kNumInnerStages != 0, "Invalid number of inner stages");
@@ -312,54 +272,42 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
// Wait TMA arrivals
full_barriers[s]->wait((scheduler.current_iter * kNumIterations + k_iter) & 1);
// TODO: remove some useless computation for unaligned Ms
// Read A scales
// NOTES: all shared memory read must be prior to `warpgroup_arrive` to avoid next scheduled block polluting the results
auto scale_a_0 = ld_shared(smem_scales_a[s] + r_0), scale_a_1 = ld_shared(smem_scales_a[s] + r_1);
// Commit WGMMA instructions
#pragma unroll
for (uint32_t local_idx = 0; local_idx < BLOCK_M / WAVE_BLOCK_M; ++ local_idx) {
auto m_offset = local_idx * WAVE_BLOCK_M;
for (int i = 0; i < WGMMA::kNumAccum; ++ i)
warpgroup_fence_operand(accum[i]);
warpgroup_arrive();
#pragma unroll
for (int k = 0; k < BLOCK_K / WGMMA::K; ++ k) {
auto desc_a = make_smem_desc(smem_a[s] + math_wg_idx * WGMMA::M * BLOCK_K + k * WGMMA::K, 1);
auto desc_b = make_smem_desc(smem_b[s] + k * WGMMA::K, 1);
WGMMA::wgmma(desc_a, desc_b, accum, k);
}
warpgroup_commit_batch();
#pragma unroll
for (int i = 0; i < WGMMA::kNumAccum; ++ i)
warpgroup_fence_operand(accum[i]);
warpgroup_wait<0>();
// Read A scales
// NOTES: all shared memory read must be prior to `warpgroup_arrive` to avoid next scheduled block polluting the results
auto scale_a_0 = ld_shared(smem_scales_a[s] + r_0 + m_offset);
auto scale_a_1 = ld_shared(smem_scales_a[s] + r_1 + m_offset);
// Notify barrier arrival
empty_barrier_arrive(s);
// Commit WGMMA instructions
#pragma unroll
for (int i = 0; i < WGMMA::kNumAccum; ++ i)
warpgroup_fence_operand(accum[i]);
warpgroup_arrive();
#pragma unroll
for (int k = 0; k < BLOCK_K / WGMMA::K; ++ k) {
auto desc_a = make_smem_desc(smem_a[s] + (math_wg_idx * WGMMA::M + m_offset) * BLOCK_K + k * WGMMA::K, 1);
auto desc_b = make_smem_desc(smem_b[s] + k * WGMMA::K, 1);
WGMMA::wgmma(desc_a, desc_b, accum, k);
}
warpgroup_commit_batch();
#pragma unroll
for (int i = 0; i < WGMMA::kNumAccum; ++ i)
warpgroup_fence_operand(accum[i]);
warpgroup_wait<0>();
// Notify barrier arrival at the last warpgroup wave
if (local_idx == BLOCK_M / WAVE_BLOCK_M - 1)
empty_barrier_arrive(s);
// Promote with scales
// NOTES: making it as predicates is very important for performance, comparing to two loops
float scale_0_0 = scale_a_0 * scale_b_0, scale_1_0 = scale_a_1 * scale_b_0;
float scale_0_1, scale_1_1;
if constexpr (not kMustUseUniformedScaleB)
scale_0_1 = scale_a_0 * scale_b_1, scale_1_1 = scale_a_1 * scale_b_1;
auto shifted_accum = final_accum + WGMMA::kNumAccum * local_idx;
#pragma unroll
for (int i = 0; i < WGMMA::kNumAccum / 4; ++ i) {
// NOTES: for unrolled `num_former_iters` cases, we expect the compiler to automatically make it a constant
bool predicate = kMustUseUniformedScaleB or i < num_former_iters;
shifted_accum[i * 4 + 0] += (predicate ? scale_0_0 : scale_0_1) * accum[i * 4 + 0];
shifted_accum[i * 4 + 1] += (predicate ? scale_0_0 : scale_0_1) * accum[i * 4 + 1];
shifted_accum[i * 4 + 2] += (predicate ? scale_1_0 : scale_1_1) * accum[i * 4 + 2];
shifted_accum[i * 4 + 3] += (predicate ? scale_1_0 : scale_1_1) * accum[i * 4 + 3];
}
// Promote with scales
float scale_0_0 = scale_a_0 * scale_b_0, scale_1_0 = scale_a_1 * scale_b_0;
float scale_0_1, scale_1_1;
if constexpr (not kMustUseUniformedScaleB)
scale_0_1 = scale_a_0 * scale_b_1, scale_1_1 = scale_a_1 * scale_b_1;
#pragma unroll
for (int i = 0; i < WGMMA::kNumAccum / 4; ++ i) {
bool predicate = kMustUseUniformedScaleB or i < num_former_iters;
final_accum[i * 4 + 0] += (predicate ? scale_0_0 : scale_0_1) * accum[i * 4 + 0];
final_accum[i * 4 + 1] += (predicate ? scale_0_0 : scale_0_1) * accum[i * 4 + 1];
final_accum[i * 4 + 2] += (predicate ? scale_1_0 : scale_1_1) * accum[i * 4 + 2];
final_accum[i * 4 + 3] += (predicate ? scale_1_0 : scale_1_1) * accum[i * 4 + 3];
}
}
@@ -369,81 +317,34 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
full_barriers[s]->wait((scheduler.current_iter * kNumIterations + k_iter) & 1);
empty_barrier_arrive(s);
}
}, num_former_iters);
});
// TMA checks
constexpr uint32_t kNumElemBytes = sizeof(nv_bfloat16);
constexpr uint32_t TMA_D_BLOCK_N = kSwizzleDMode == 0 ? BLOCK_N : (kSwizzleDMode / kNumElemBytes);
constexpr uint32_t WGMMA_M_PER_WARP = WGMMA::M / 4;
DG_STATIC_ASSERT(BLOCK_M % 8 == 0, "Invalid swizzling atom");
DG_STATIC_ASSERT(BLOCK_N % TMA_D_BLOCK_N == 0 and BLOCK_N / TMA_D_BLOCK_N <= 32,
"Unaligned TMA store or too many TMA store instructions");
DG_STATIC_ASSERT(TMA_D_BLOCK_N % 8 == 0, "Invalid TMA block N");
DG_STATIC_ASSERT(static_cast<int>(kSwizzleDMode > 0) + static_cast<int>(BLOCK_N_PADDING > 0) <= 1,
"Swizzling and padding are not compatible");
// Write back to shared memory using STSM and issue TMA stores
// Write back to shared memory using STSM
DG_STATIC_ASSERT(WGMMA::kNumAccum % 4 == 0, "Invalid STSM x2 vectorization");
#pragma unroll
for (uint32_t local_idx = 0; local_idx < BLOCK_M / WAVE_BLOCK_M; ++ local_idx) {
auto m_offset = local_idx * WAVE_BLOCK_M;
auto shifted_accum = final_accum + WGMMA::kNumAccum * local_idx;
#pragma unroll
for (auto i = 0; i < WGMMA::kNumAccum / 4; ++ i) {
// Swizzle or padding into the correct address
uint8_t* smem_ptr = nullptr;
if constexpr (kSwizzleDMode > 0) {
// Calculate the swizzling atom offset and in-atom offset
constexpr int kNumBankGroupBytes = 16;
auto atom_offset = i / (TMA_D_BLOCK_N / 8), in_atom_offset = i % (TMA_D_BLOCK_N / 8);
// Calculate the index of the bank group to be written in the atom
auto bank_group_index = in_atom_offset + lane_idx * (kSwizzleDMode / kNumBankGroupBytes);
// Reshape the atom in another view and swizzle
// - original: `(BLOCK_M, kSwizzleDMode / kNumBankGroupBytes)`
// - new: `(BLOCK_M * kSwizzleDMode / kNumBankGroupBytes / 8, 8)`
constexpr bool kHasShortcut = (kSwizzleDMode / kNumBankGroupBytes) == 8;
auto row = kHasShortcut ? (in_atom_offset / 8 + lane_idx) : (bank_group_index / 8);
auto col = kHasShortcut ? (in_atom_offset) : (bank_group_index % 8);
col ^= row % (kSwizzleDMode / 16);
// Add back into the base pointer
// NOTES: think twice before modifying this, as changes may affect the number of instructions
smem_ptr = reinterpret_cast<uint8_t*>(smem_d) + // Base pointer
warp_idx * (WGMMA_M_PER_WARP * kSwizzleDMode) + // Warp offset
m_offset * kSwizzleDMode + // Wave offset
atom_offset * BLOCK_M * kSwizzleDMode + // Swizzle atom offset (constants)
row * (kNumBankGroupBytes * 8) + col * kNumBankGroupBytes; // In-atom offset
} else {
// No swizzling, just padding
// NOTES: padding must be zero for BF16 output
DG_STATIC_ASSERT(BLOCK_N_PADDING == 0, "Padding must be zero for BF16 output");
smem_ptr = reinterpret_cast<uint8_t*>(smem_d + (m_offset + warp_idx * WGMMA_M_PER_WARP + lane_idx) * (BLOCK_N + BLOCK_N_PADDING) + i * 8);
}
// NOTES: only 16 lanes' addresses are used
SM90_U32x2_STSM_N<nv_bfloat162>::copy(
__float22bfloat162_rn({shifted_accum[i * 4 + 0], shifted_accum[i * 4 + 1]}),
__float22bfloat162_rn({shifted_accum[i * 4 + 2], shifted_accum[i * 4 + 3]}),
smem_ptr
);
}
for (auto i = 0; i < WGMMA::kNumAccum / 8; ++ i) {
SM90_U32x4_STSM_N<nv_bfloat162>::copy(
__float22bfloat162_rn({final_accum[i * 8 + 0], final_accum[i * 8 + 1]}),
__float22bfloat162_rn({final_accum[i * 8 + 2], final_accum[i * 8 + 3]}),
__float22bfloat162_rn({final_accum[i * 8 + 4], final_accum[i * 8 + 5]}),
__float22bfloat162_rn({final_accum[i * 8 + 6], final_accum[i * 8 + 7]}),
smem_d + (warp_idx * 16 + lane_idx % 16) * BLOCK_N + i * 16 + 8 * (lane_idx / 16)
);
}
if constexpr (WGMMA::kNumAccum % 8 != 0) {
SM90_U32x2_STSM_N<nv_bfloat162>::copy(
__float22bfloat162_rn({final_accum[WGMMA::kNumAccum / 8 * 8 + 0], final_accum[WGMMA::kNumAccum / 8 * 8 + 1]}),
__float22bfloat162_rn({final_accum[WGMMA::kNumAccum / 8 * 8 + 2], final_accum[WGMMA::kNumAccum / 8 * 8 + 3]}),
smem_d + (warp_idx * 16 + lane_idx % 16) * BLOCK_N + WGMMA::kNumAccum / 8 * 16
);
}
cute::tma_store_fence();
cutlass::arch::NamedBarrier(kNumMathThreads).sync();
// Use TMA store to write back to global memory
// TODO: compatible with FP32 output
DG_STATIC_ASSERT(kNumMathThreads >= BLOCK_N / TMA_D_BLOCK_N, "Too many TMA blocks");
if (threadIdx.x < BLOCK_N / TMA_D_BLOCK_N) {
auto in_block_n_offset = threadIdx.x * TMA_D_BLOCK_N;
auto smem_ptr = smem_d + in_block_n_offset * BLOCK_M;
cute::SM90_TMA_STORE_2D::copy(&tensor_map_d, smem_ptr,
n_block_idx * BLOCK_N + in_block_n_offset,
if (threadIdx.x == 0) {
cute::SM90_TMA_STORE_2D::copy(&tensor_map_d, smem_d, n_block_idx * BLOCK_N,
scheduler.get_global_idx(shape_m, BLOCK_M, m_block_idx));
// Wait TMA to be finished
cute::tma_store_arrive();
cute::tma_store_wait<0>();
}
@@ -458,10 +359,8 @@ fp8_gemm_kernel(__nv_bfloat16* gmem_d, float* scales_b, int* grouped_layout,
template <uint32_t SHAPE_N, uint32_t SHAPE_K,
uint32_t BLOCK_M, uint32_t BLOCK_N, uint32_t BLOCK_K,
uint32_t BLOCK_N_PADDING,
uint32_t kSwizzleDMode,
uint32_t kNumGroups, uint32_t kNumStages,
uint32_t kNumTMAMulticast, bool kIsTMAMulticastOnA,
uint32_t kNumTMAMulticast,
GemmType kGemmType>
class Gemm {
private:
@@ -481,13 +380,9 @@ public:
// NOTES: we must use 4 warps to do TMA, because `setmaxnreg.aligned` requires 4 warps
constexpr uint32_t kNumTMAThreads = 128;
constexpr uint32_t kNumMathThreadsPerGroup = 128;
auto kernel = fp8_gemm_kernel<SHAPE_N, SHAPE_K,
BLOCK_M, BLOCK_N, BLOCK_K,
BLOCK_N_PADDING,
kSwizzleDMode,
kNumGroups, kNumStages,
kNumTMAThreads, kNumMathThreadsPerGroup,
kNumTMAMulticast, kIsTMAMulticastOnA, kGemmType>;
auto kernel = fp8_gemm_kernel<SHAPE_N, SHAPE_K, BLOCK_M, BLOCK_N, BLOCK_K,
kNumGroups, kNumStages, kNumTMAThreads, kNumMathThreadsPerGroup,
kNumTMAMulticast, kGemmType>;
DG_HOST_ASSERT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size) == cudaSuccess);
// Cluster launch
@@ -527,17 +422,10 @@ public:
template <typename T>
static CUtensorMap make_2d_tma_d_desc(T* global_address, uint32_t shape_m) {
auto swizzle_mode = CUtensorMapSwizzle::CU_TENSOR_MAP_SWIZZLE_NONE;
if constexpr (kSwizzleDMode == 32) swizzle_mode = CU_TENSOR_MAP_SWIZZLE_32B;
if constexpr (kSwizzleDMode == 64) swizzle_mode = CU_TENSOR_MAP_SWIZZLE_64B;
if constexpr (kSwizzleDMode == 128) swizzle_mode = CU_TENSOR_MAP_SWIZZLE_128B;
// Swizzling requires the inner box dim less or equal than `kSwizzleDMode` bytes
// So `BLOCK_N * sizeof(T) / kSwizzleDMode` TMA stores are required
return make_2d_tma_desc(global_address, Layout::RowMajor,
shape_m * (kGemmType == GemmType::GroupedMasked ? kNumGroups : 1), SHAPE_N,
BLOCK_M, kSwizzleDMode == 0 ? BLOCK_N : kSwizzleDMode / sizeof(T),
swizzle_mode);
min(BLOCK_M, shape_m), BLOCK_N,
CUtensorMapSwizzle::CU_TENSOR_MAP_SWIZZLE_NONE);
}
template <typename T>

View File

@@ -20,13 +20,742 @@
#include <cuda.h>
#include <cute/arch/mma_sm90_gmma.hpp>
#include <cute/arch/mma_sm90_gmma_ext.hpp>
#include "utils.cuh"
namespace deep_gemm {
struct SM90_64x16x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %10, 0;\n"
"wgmma.mma_async.sync.aligned.m64n16k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7},"
" %8,"
" %9,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 16;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x24x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %14, 0;\n"
"wgmma.mma_async.sync.aligned.m64n24k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11},"
" %12,"
" %13,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 24;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x32x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %18, 0;\n"
"wgmma.mma_async.sync.aligned.m64n32k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15},"
" %16,"
" %17,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 32;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x40x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %22, 0;\n"
"wgmma.mma_async.sync.aligned.m64n40k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19},"
" %20,"
" %21,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 40;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x48x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %26, 0;\n"
"wgmma.mma_async.sync.aligned.m64n48k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23},"
" %24,"
" %25,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 48;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x56x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %30, 0;\n"
"wgmma.mma_async.sync.aligned.m64n56k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27}, "
" %28,"
" %29,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 56;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x64x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %34, 0;\n"
"wgmma.mma_async.sync.aligned.m64n64k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31}, "
" %32,"
" %33,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 64;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x72x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %38, 0;\n"
"wgmma.mma_async.sync.aligned.m64n72k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35}, "
" %36,"
" %37,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 72;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x80x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35, float& d36, float& d37, float& d38, float& d39,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %42, 0;\n"
"wgmma.mma_async.sync.aligned.m64n80k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35, %36, %37, %38, %39}, "
" %40,"
" %41,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35), "+f"(d36), "+f"(d37), "+f"(d38), "+f"(d39)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 80;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x88x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35, float& d36, float& d37, float& d38, float& d39,
float& d40, float& d41, float& d42, float& d43,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %46, 0;\n"
"wgmma.mma_async.sync.aligned.m64n88k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35, %36, %37, %38, %39, "
" %40, %41, %42, %43}, "
" %44,"
" %45,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35), "+f"(d36), "+f"(d37), "+f"(d38), "+f"(d39),
"+f"(d40), "+f"(d41), "+f"(d42), "+f"(d43)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39],
d[40], d[41], d[42], d[43],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 88;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x96x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35, float& d36, float& d37, float& d38, float& d39,
float& d40, float& d41, float& d42, float& d43, float& d44, float& d45, float& d46, float& d47,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %50, 0;\n"
"wgmma.mma_async.sync.aligned.m64n96k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35, %36, %37, %38, %39, "
" %40, %41, %42, %43, %44, %45, %46, %47}, "
" %48,"
" %49,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35), "+f"(d36), "+f"(d37), "+f"(d38), "+f"(d39),
"+f"(d40), "+f"(d41), "+f"(d42), "+f"(d43), "+f"(d44), "+f"(d45), "+f"(d46), "+f"(d47)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39],
d[40], d[41], d[42], d[43], d[44], d[45], d[46], d[47],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 96;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x104x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35, float& d36, float& d37, float& d38, float& d39,
float& d40, float& d41, float& d42, float& d43, float& d44, float& d45, float& d46, float& d47,
float& d48, float& d49, float& d50, float& d51,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %54, 0;\n"
"wgmma.mma_async.sync.aligned.m64n104k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35, %36, %37, %38, %39, "
" %40, %41, %42, %43, %44, %45, %46, %47, "
" %48, %49, %50, %51}, "
" %52,"
" %53,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35), "+f"(d36), "+f"(d37), "+f"(d38), "+f"(d39),
"+f"(d40), "+f"(d41), "+f"(d42), "+f"(d43), "+f"(d44), "+f"(d45), "+f"(d46), "+f"(d47),
"+f"(d48), "+f"(d49), "+f"(d50), "+f"(d51)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39],
d[40], d[41], d[42], d[43], d[44], d[45], d[46], d[47],
d[48], d[49], d[50], d[51],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 104;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x112x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35, float& d36, float& d37, float& d38, float& d39,
float& d40, float& d41, float& d42, float& d43, float& d44, float& d45, float& d46, float& d47,
float& d48, float& d49, float& d50, float& d51, float& d52, float& d53, float& d54, float& d55,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %58, 0;\n"
"wgmma.mma_async.sync.aligned.m64n112k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35, %36, %37, %38, %39, "
" %40, %41, %42, %43, %44, %45, %46, %47, "
" %48, %49, %50, %51, %52, %53, %54, %55}, "
" %56,"
" %57,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35), "+f"(d36), "+f"(d37), "+f"(d38), "+f"(d39),
"+f"(d40), "+f"(d41), "+f"(d42), "+f"(d43), "+f"(d44), "+f"(d45), "+f"(d46), "+f"(d47),
"+f"(d48), "+f"(d49), "+f"(d50), "+f"(d51), "+f"(d52), "+f"(d53), "+f"(d54), "+f"(d55)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39],
d[40], d[41], d[42], d[43], d[44], d[45], d[46], d[47],
d[48], d[49], d[50], d[51], d[52], d[53], d[54], d[55],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 112;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x120x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35, float& d36, float& d37, float& d38, float& d39,
float& d40, float& d41, float& d42, float& d43, float& d44, float& d45, float& d46, float& d47,
float& d48, float& d49, float& d50, float& d51, float& d52, float& d53, float& d54, float& d55,
float& d56, float& d57, float& d58, float& d59,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %62, 0;\n"
"wgmma.mma_async.sync.aligned.m64n120k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35, %36, %37, %38, %39, "
" %40, %41, %42, %43, %44, %45, %46, %47, "
" %48, %49, %50, %51, %52, %53, %54, %55, "
" %56, %57, %58, %59}, "
" %60,"
" %61,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35), "+f"(d36), "+f"(d37), "+f"(d38), "+f"(d39),
"+f"(d40), "+f"(d41), "+f"(d42), "+f"(d43), "+f"(d44), "+f"(d45), "+f"(d46), "+f"(d47),
"+f"(d48), "+f"(d49), "+f"(d50), "+f"(d51), "+f"(d52), "+f"(d53), "+f"(d54), "+f"(d55),
"+f"(d56), "+f"(d57), "+f"(d58), "+f"(d59)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39],
d[40], d[41], d[42], d[43], d[44], d[45], d[46], d[47],
d[48], d[49], d[50], d[51], d[52], d[53], d[54], d[55],
d[56], d[57], d[58], d[59],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 120;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x128x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35, float& d36, float& d37, float& d38, float& d39,
float& d40, float& d41, float& d42, float& d43, float& d44, float& d45, float& d46, float& d47,
float& d48, float& d49, float& d50, float& d51, float& d52, float& d53, float& d54, float& d55,
float& d56, float& d57, float& d58, float& d59, float& d60, float& d61, float& d62, float& d63,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %66, 0;\n"
"wgmma.mma_async.sync.aligned.m64n128k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35, %36, %37, %38, %39, "
" %40, %41, %42, %43, %44, %45, %46, %47, "
" %48, %49, %50, %51, %52, %53, %54, %55, "
" %56, %57, %58, %59, %60, %61, %62, %63}, "
" %64,"
" %65,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35), "+f"(d36), "+f"(d37), "+f"(d38), "+f"(d39),
"+f"(d40), "+f"(d41), "+f"(d42), "+f"(d43), "+f"(d44), "+f"(d45), "+f"(d46), "+f"(d47),
"+f"(d48), "+f"(d49), "+f"(d50), "+f"(d51), "+f"(d52), "+f"(d53), "+f"(d54), "+f"(d55),
"+f"(d56), "+f"(d57), "+f"(d58), "+f"(d59), "+f"(d60), "+f"(d61), "+f"(d62), "+f"(d63)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39],
d[40], d[41], d[42], d[43], d[44], d[45], d[46], d[47],
d[48], d[49], d[50], d[51], d[52], d[53], d[54], d[55],
d[56], d[57], d[58], d[59], d[60], d[61], d[62], d[63],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 128;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
struct SM90_64x192x32_F32E4M3E4M3_SS {
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b,
float& d00, float& d01, float& d02, float& d03, float& d04, float& d05, float& d06, float& d07,
float& d08, float& d09, float& d10, float& d11, float& d12, float& d13, float& d14, float& d15,
float& d16, float& d17, float& d18, float& d19, float& d20, float& d21, float& d22, float& d23,
float& d24, float& d25, float& d26, float& d27, float& d28, float& d29, float& d30, float& d31,
float& d32, float& d33, float& d34, float& d35, float& d36, float& d37, float& d38, float& d39,
float& d40, float& d41, float& d42, float& d43, float& d44, float& d45, float& d46, float& d47,
float& d48, float& d49, float& d50, float& d51, float& d52, float& d53, float& d54, float& d55,
float& d56, float& d57, float& d58, float& d59, float& d60, float& d61, float& d62, float& d63,
float& d64, float& d65, float& d66, float& d67, float& d68, float& d69, float& d70, float& d71,
float& d72, float& d73, float& d74, float& d75, float& d76, float& d77, float& d78, float& d79,
float& d80, float& d81, float& d82, float& d83, float& d84, float& d85, float& d86, float& d87,
float& d88, float& d89, float& d90, float& d91, float& d92, float& d93, float& d94, float& d95,
bool scale_d) {
asm volatile("{\n"
".reg .pred p;\n"
"setp.ne.b32 p, %98, 0;\n"
"wgmma.mma_async.sync.aligned.m64n192k32.f32.e4m3.e4m3"
"{%0, %1, %2, %3, %4, %5, %6, %7, "
" %8, %9, %10, %11, %12, %13, %14, %15, "
" %16, %17, %18, %19, %20, %21, %22, %23, "
" %24, %25, %26, %27, %28, %29, %30, %31, "
" %32, %33, %34, %35, %36, %37, %38, %39, "
" %40, %41, %42, %43, %44, %45, %46, %47, "
" %48, %49, %50, %51, %52, %53, %54, %55, "
" %56, %57, %58, %59, %60, %61, %62, %63, "
" %64, %65, %66, %67, %68, %69, %70, %71, "
" %72, %73, %74, %75, %76, %77, %78, %79, "
" %80, %81, %82, %83, %84, %85, %86, %87, "
" %88, %89, %90, %91, %92, %93, %94, %95}, "
" %96,"
" %97,"
" p , 1, 1;\n"
"}\n"
: "+f"(d00), "+f"(d01), "+f"(d02), "+f"(d03), "+f"(d04), "+f"(d05), "+f"(d06), "+f"(d07),
"+f"(d08), "+f"(d09), "+f"(d10), "+f"(d11), "+f"(d12), "+f"(d13), "+f"(d14), "+f"(d15),
"+f"(d16), "+f"(d17), "+f"(d18), "+f"(d19), "+f"(d20), "+f"(d21), "+f"(d22), "+f"(d23),
"+f"(d24), "+f"(d25), "+f"(d26), "+f"(d27), "+f"(d28), "+f"(d29), "+f"(d30), "+f"(d31),
"+f"(d32), "+f"(d33), "+f"(d34), "+f"(d35), "+f"(d36), "+f"(d37), "+f"(d38), "+f"(d39),
"+f"(d40), "+f"(d41), "+f"(d42), "+f"(d43), "+f"(d44), "+f"(d45), "+f"(d46), "+f"(d47),
"+f"(d48), "+f"(d49), "+f"(d50), "+f"(d51), "+f"(d52), "+f"(d53), "+f"(d54), "+f"(d55),
"+f"(d56), "+f"(d57), "+f"(d58), "+f"(d59), "+f"(d60), "+f"(d61), "+f"(d62), "+f"(d63),
"+f"(d64), "+f"(d65), "+f"(d66), "+f"(d67), "+f"(d68), "+f"(d69), "+f"(d70), "+f"(d71),
"+f"(d72), "+f"(d73), "+f"(d74), "+f"(d75), "+f"(d76), "+f"(d77), "+f"(d78), "+f"(d79),
"+f"(d80), "+f"(d81), "+f"(d82), "+f"(d83), "+f"(d84), "+f"(d85), "+f"(d86), "+f"(d87),
"+f"(d88), "+f"(d89), "+f"(d90), "+f"(d91), "+f"(d92), "+f"(d93), "+f"(d94), "+f"(d95)
: "l"(desc_a), "l"(desc_b), "r"(int32_t(scale_d)));
}
__device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
wgmma(desc_a, desc_b,
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23],
d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31],
d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39],
d[40], d[41], d[42], d[43], d[44], d[45], d[46], d[47],
d[48], d[49], d[50], d[51], d[52], d[53], d[54], d[55],
d[56], d[57], d[58], d[59], d[60], d[61], d[62], d[63],
d[64], d[65], d[66], d[67], d[68], d[69], d[70], d[71],
d[72], d[73], d[74], d[75], d[76], d[77], d[78], d[79],
d[80], d[81], d[82], d[83], d[84], d[85], d[86], d[87],
d[88], d[89], d[90], d[91], d[92], d[93], d[94], d[95],
scale_d);
}
static constexpr int M = 64;
static constexpr int N = 192;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
template <typename dtype_t>
struct SM90_U32x2_STSM_N {
__device__ __forceinline__ static void
@@ -48,15 +777,15 @@ struct SM90_U32x4_STSM_N {
}
};
__forceinline__ __device__ void warpgroup_arrive() {
__device__ void warpgroup_arrive() {
asm volatile("wgmma.fence.sync.aligned;\n" ::: "memory");
}
__forceinline__ __device__ void warpgroup_commit_batch() {
__device__ void warpgroup_commit_batch() {
asm volatile("wgmma.commit_group.sync.aligned;\n" ::: "memory");
}
__forceinline__ __device__ void warpgroup_fence_operand(float& reg) {
__device__ void warpgroup_fence_operand(float& reg) {
asm volatile("" : "+f"(reg) :: "memory");
}
@@ -147,53 +876,25 @@ __device__ GmmaDescriptor make_smem_desc(PointerType smem_ptr, int layout_type,
return desc;
}
template <int N_, typename MMA>
struct FP8MMA {
template <size_t ...Idx>
__forceinline__ __device__ static void call_fma_impl(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d, std::index_sequence<Idx...>) {
using namespace cute::SM90::GMMA;
MMA::fma(desc_a, desc_b, d[Idx]..., (scale_d ? ScaleOut::One : ScaleOut::Zero));
}
__forceinline__ __device__ static void wgmma(uint64_t const& desc_a, uint64_t const& desc_b, float* d, bool scale_d) {
call_fma_impl(desc_a, desc_b, d, scale_d, std::make_index_sequence<N_/2>{});
}
static constexpr int M = 64;
static constexpr int N = N_;
static constexpr int K = 32;
static constexpr int kNumAccum = M * N / 128;
};
template <int N>
struct FP8MMASelector {
static constexpr auto select_mma() {
using namespace cute::SM90::GMMA;
if constexpr (N == 16) return MMA_64x16x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 24) return MMA_64x24x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 32) return MMA_64x32x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 40) return MMA_64x40x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 48) return MMA_64x48x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 56) return MMA_64x56x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 64) return MMA_64x64x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 72) return MMA_64x72x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 80) return MMA_64x80x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 88) return MMA_64x88x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 96) return MMA_64x96x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 104) return MMA_64x104x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 112) return MMA_64x112x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 120) return MMA_64x120x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 128) return MMA_64x128x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 144) return MMA_64x144x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 152) return MMA_64x152x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 160) return MMA_64x160x32_F32E4M3E4M3_SS_TN();
if constexpr (N == 192) return MMA_64x192x32_F32E4M3E4M3_SS_TN();
}
static constexpr auto select_type() {
return FP8MMA<N, decltype(select_mma())>();
if constexpr (N == 16) return SM90_64x16x32_F32E4M3E4M3_SS();
if constexpr (N == 24) return SM90_64x24x32_F32E4M3E4M3_SS();
if constexpr (N == 32) return SM90_64x32x32_F32E4M3E4M3_SS();
if constexpr (N == 40) return SM90_64x40x32_F32E4M3E4M3_SS();
if constexpr (N == 48) return SM90_64x48x32_F32E4M3E4M3_SS();
if constexpr (N == 56) return SM90_64x56x32_F32E4M3E4M3_SS();
if constexpr (N == 64) return SM90_64x64x32_F32E4M3E4M3_SS();
if constexpr (N == 72) return SM90_64x72x32_F32E4M3E4M3_SS();
if constexpr (N == 80) return SM90_64x80x32_F32E4M3E4M3_SS();
if constexpr (N == 88) return SM90_64x88x32_F32E4M3E4M3_SS();
if constexpr (N == 96) return SM90_64x96x32_F32E4M3E4M3_SS();
if constexpr (N == 104) return SM90_64x104x32_F32E4M3E4M3_SS();
if constexpr (N == 112) return SM90_64x112x32_F32E4M3E4M3_SS();
if constexpr (N == 120) return SM90_64x120x32_F32E4M3E4M3_SS();
if constexpr (N == 128) return SM90_64x128x32_F32E4M3E4M3_SS();
if constexpr (N == 192) return SM90_64x192x32_F32E4M3E4M3_SS();
}
using type = decltype(select_type());

View File

@@ -30,10 +30,9 @@ enum class GemmType {
#pragma ide diagnostic ignored "cppcoreguidelines-pro-type-member-init"
template <GemmType kGemmType,
uint32_t SHAPE_N, uint32_t BLOCK_M, uint32_t BLOCK_N,
uint32_t kNumGroups,
uint32_t kNumTMAMulticast, bool kIsTMAMulticastOnA,
uint32_t kNumGroups, uint32_t kNumTMAMulticast,
uint32_t kNumNBlocks = ceil_div(SHAPE_N, BLOCK_N),
uint32_t kNum1DBlocksPerGroup = 16>
uint32_t kNumNBlocksPerGroup = 16>
struct Scheduler {
int current_iter = -1;
uint32_t num_aligned_m_blocks;
@@ -62,27 +61,16 @@ struct Scheduler {
}
__device__ __forceinline__ void get_swizzled_block_idx(const uint32_t num_m_blocks, int block_idx, uint32_t& m_block_idx, uint32_t& n_block_idx) {
DG_STATIC_ASSERT(kNum1DBlocksPerGroup % kNumTMAMulticast == 0, "Invalid group size");
DG_STATIC_ASSERT(kNumNBlocksPerGroup % kNumTMAMulticast == 0, "Invalid group size");
// Swizzle for better L2 usages
// TODO: unify these 2 branches
if constexpr (kIsTMAMulticastOnA) {
auto num_blocks_per_group = num_m_blocks * kNum1DBlocksPerGroup;
auto group_idx = block_idx / num_blocks_per_group;
auto first_n_block_idx = group_idx * kNum1DBlocksPerGroup;
auto num_n_blocks_in_group = min(kNum1DBlocksPerGroup, kNumNBlocks - first_n_block_idx);
auto in_group_idx = block_idx % num_blocks_per_group;
m_block_idx = in_group_idx / num_n_blocks_in_group;
n_block_idx = first_n_block_idx + in_group_idx % num_n_blocks_in_group;
} else {
auto num_blocks_per_group = kNumNBlocks * kNum1DBlocksPerGroup;
auto group_idx = block_idx / num_blocks_per_group;
auto first_m_block_idx = group_idx * kNum1DBlocksPerGroup;
auto num_m_blocks_in_group = min(kNum1DBlocksPerGroup, num_m_blocks - first_m_block_idx);
auto in_group_idx = block_idx % num_blocks_per_group;
m_block_idx = first_m_block_idx + in_group_idx % num_m_blocks_in_group;
n_block_idx = in_group_idx / num_m_blocks_in_group;
}
auto num_blocks_per_group = num_m_blocks * kNumNBlocksPerGroup;
auto group_idx = block_idx / num_blocks_per_group;
auto first_n_block_idx = group_idx * kNumNBlocksPerGroup;
auto num_n_blocks_in_group = min(kNumNBlocksPerGroup, kNumNBlocks - first_n_block_idx);
auto in_group_idx = block_idx % num_blocks_per_group;
m_block_idx = in_group_idx / num_n_blocks_in_group;
n_block_idx = first_n_block_idx + in_group_idx % num_n_blocks_in_group;
}
template <bool kIgnoreGroupedForGroupedContiguous=true>
@@ -128,7 +116,6 @@ struct Scheduler {
return true;
}
};
#pragma clang diagnostic pop
} // namespace deep_gemm

View File

@@ -58,18 +58,20 @@ constexpr CUtensorMapDataType get_CUtensorMapDataType() {
}
}
inline PFN_cuTensorMapEncodeTiled get_cuTensorMapEncodeTiled() {
PFN_cuTensorMapEncodeTiled get_cuTensorMapEncodeTiled() {
// Get pointer to `cuTensorMapEncodeTiled`
cudaDriverEntryPointQueryResult driver_status;
void* cuTensorMapEncodeTiled_ptr = nullptr;
/*
#if CUDA_VERSION >= 12050
cudaGetDriverEntryPointByVersion("cuTensorMapEncodeTiled", &cuTensorMapEncodeTiled_ptr, 12000,
cudaEnableDefault, &driver_status);
#else
*/
cudaGetDriverEntryPoint("cuTensorMapEncodeTiled", &cuTensorMapEncodeTiled_ptr,
cudaEnableDefault, &driver_status);
#endif
//#endif
if (driver_status != cudaDriverEntryPointSuccess)
throw std::runtime_error("driver_status != cudaDriverEntryPointSuccess");
@@ -81,15 +83,16 @@ CUtensorMap make_2d_tma_copy_desc(T* global_address, uint64_t gmem_dim[2],
uint64_t stride_in_bytes, uint32_t smem_dim[2],
CUtensorMapSwizzle swizzle_type,
PFN_cuTensorMapEncodeTiled encode_func = nullptr) {
CUtensorMap tensor_map = {};
uint64_t global_stride[1] = {stride_in_bytes};
uint32_t elem_strides[2] = {1, 1};
CUtensorMap tensor_map{};
constexpr uint32_t rank = 2;
uint64_t global_stride[rank - 1] = {stride_in_bytes};
uint32_t elem_strides[rank] = {1, 1};
if (encode_func == nullptr)
encode_func = get_cuTensorMapEncodeTiled();
auto result = encode_func(
&tensor_map, get_CUtensorMapDataType<std::remove_cv_t<T>>(), 2,
&tensor_map, get_CUtensorMapDataType<typename std::remove_cv<T>::type>(), rank,
global_address, gmem_dim, global_stride, smem_dim, elem_strides,
CUtensorMapInterleave::CU_TENSOR_MAP_INTERLEAVE_NONE, swizzle_type,
CUtensorMapL2promotion::CU_TENSOR_MAP_L2_PROMOTION_L2_256B,

View File

@@ -63,9 +63,4 @@ do {
template <typename T>
__device__ __host__ constexpr T ceil_div(T a, T b) {
return (a + b - 1) / b;
}
template <typename T>
__device__ __host__ constexpr T constexpr_gcd(T a, T b) {
return b == 0 ? a : constexpr_gcd(b, a % b);
}

View File

@@ -16,6 +16,6 @@
# Copyright (c) 2025 DeepSeek
# Licensed under the MIT License - https://github.com/deepseek-ai/DeepEP/blob/main/LICENSE
from .compiler import get_nvcc_compiler, build
from .template import cpp_format, generate
from .compiler import build, get_nvcc_compiler
from .runtime import Runtime
from .template import cpp_format, generate

View File

@@ -16,8 +16,8 @@
# Copyright (c) 2025 DeepSeek
# Licensed under the MIT License - https://github.com/deepseek-ai/DeepEP/blob/main/LICENSE
import hashlib
import functools
import hashlib
import os
import re
import subprocess
@@ -75,7 +75,9 @@ def get_nvcc_compiler() -> Tuple[str, str]:
match = version_pattern.search(os.popen(f"{path} --version").read())
version = match.group(1)
assert match, f"Cannot get the version of NVCC compiler {path}"
assert version >= least_version_required, f"NVCC {path} version {version} is lower than {least_version_required}"
assert (
version >= least_version_required
), f"NVCC {path} version {version} is lower than {least_version_required}"
return path, version
raise RuntimeError("Cannot find any available NVCC compiler")
@@ -115,13 +117,18 @@ def put(path, data, is_binary=False):
def build(name: str, arg_defs: tuple, code: str) -> Runtime:
# Compiler flags
cpp_standard = int(os.getenv("DG_NVCC_OVERRIDE_CPP_STANDARD", 20))
nvcc_flags = [f"-std=c++{cpp_standard}", "-shared", "-O3", "--expt-relaxed-constexpr", "--expt-extended-lambda",
"-gencode=arch=compute_90a,code=sm_90a",
"--ptxas-options=--register-usage-level=10" + (",--verbose" if "DG_PTXAS_VERBOSE" in os.environ else ""),
# Suppress some unnecessary warnings, such as unused variables for certain `constexpr` branch cases
"--diag-suppress=39,174,177,940"]
cxx_flags = ["-fPIC", "-O3", "-Wno-deprecated-declarations", "-Wno-abi", "-fconcepts"]
nvcc_flags = [
"-std=c++17",
"-shared",
"-O3",
"--expt-relaxed-constexpr",
"--expt-extended-lambda",
"-gencode=arch=compute_90a,code=sm_90a",
"--ptxas-options=--register-usage-level=10" + (",--verbose" if "DG_PTXAS_VERBOSE" in os.environ else ""),
# Suppress some unnecessary warnings, such as unused variables for certain `constexpr` branch cases
"--diag-suppress=177,174,940",
]
cxx_flags = ["-fPIC", "-O3", "-Wno-deprecated-declarations", "-Wno-abi"]
flags = [*nvcc_flags, f'--compiler-options={",".join(cxx_flags)}']
include_dirs = [get_jit_include_dir()]
@@ -148,12 +155,8 @@ def build(name: str, arg_defs: tuple, code: str) -> Runtime:
# Compile into a temporary SO file
so_path = f"{path}/kernel.so"
tmp_so_path = f"{make_tmp_dir()}/nvcc.tmp.{str(uuid.uuid4())}.{hash_to_hex(so_path)}.so"
# Compile
command = [get_nvcc_compiler()[0],
src_path, "-o", tmp_so_path,
*flags,
*[f"-I{d}" for d in include_dirs]]
command = [get_nvcc_compiler()[0], src_path, "-o", tmp_so_path, *flags, *[f"-I{d}" for d in include_dirs]]
if os.getenv("DG_JIT_DEBUG", None) or os.getenv("DG_JIT_PRINT_NVCC_COMMAND", False):
print(f"Compiling JIT runtime {name} with command {command}")
return_code = subprocess.check_call(command)

View File

@@ -93,7 +93,7 @@ def parse_registers(line):
def modify_segment(m, name, ffma_lines):
num_lines = (len(ffma_lines) * 9 // 16) // 2 * 2
num_lines = len(ffma_lines)
assert num_lines % 2 == 0
le_bytes, new_le_bytes = [], []

View File

@@ -23,6 +23,8 @@ from typing import Optional
import paddle
from paddle import Tensor
from .template import map_ctype
class Runtime:
def __init__(self, path: str) -> None:
@@ -44,52 +46,24 @@ class Runtime:
def __call__(self, *args) -> int:
# Load SO file
if self.lib is None:
if self.lib is None or self.args is None:
self.lib = ctypes.CDLL(os.path.join(self.path, "kernel.so"))
with open(os.path.join(self.path, "kernel.args"), "r") as f:
self.args = eval(f.read())
# Check args and launch
assert len(args) == len(self.args), f"Expected {len(self.args)} arguments, got {len(args)}"
cargs = []
for arg, (name, dtype) in zip(args, self.args):
if isinstance(arg, Tensor):
assert arg.dtype == dtype, f"Expected tensor dtype `{dtype}` for `{name}`, got `{arg.dtype}`"
else:
assert isinstance(arg, dtype), f"Expected built-in type `{dtype}` for `{name}`, got `{type(arg)}`"
cargs.append(map_ctype(arg))
if len(args) == 9:
cargs = [
ctypes.c_void_p(args[0].data_ptr()),
ctypes.c_void_p(args[1].data_ptr()),
ctypes.c_void_p(args[2].data_ptr()),
ctypes.c_void_p(args[3].data_ptr()),
ctypes.c_void_p(args[4].data_ptr()),
ctypes.c_int(args[5]),
ctypes.c_void_p(args[6].cuda_stream),
ctypes.c_int(args[7]),
ctypes.c_int(args[8]),
]
elif len(args) == 10:
cargs = [
ctypes.c_void_p(args[0].data_ptr()),
ctypes.c_void_p(args[1].data_ptr()),
ctypes.c_void_p(args[2].data_ptr()),
ctypes.c_void_p(args[3].data_ptr()),
ctypes.c_void_p(args[4].data_ptr()),
ctypes.c_void_p(args[5].data_ptr()),
ctypes.c_int(args[6]),
ctypes.c_void_p(args[7].cuda_stream),
ctypes.c_int(args[8]),
ctypes.c_int(args[9]),
]
elif len(args) == 11:
cargs = [
ctypes.c_void_p(args[0].data_ptr()),
ctypes.c_void_p(args[1].data_ptr()),
ctypes.c_void_p(args[2].data_ptr()),
ctypes.c_void_p(args[3].data_ptr()),
ctypes.c_void_p(args[4].data_ptr()),
ctypes.c_void_p(args[5].data_ptr()),
ctypes.c_int(args[6]),
ctypes.c_int(args[7]),
ctypes.c_void_p(args[8].cuda_stream),
ctypes.c_int(args[9]),
ctypes.c_int(args[10]),
]
else:
raise ValueError("Invalid number of arguments")
return_code = ctypes.c_int(0)
self.lib.launch(*cargs, ctypes.byref(return_code))
return return_code.value
class RuntimeCache:

View File

@@ -16,16 +16,22 @@
# Copyright (c) 2025 DeepSeek
# Licensed under the MIT License - https://github.com/deepseek-ai/DeepEP/blob/main/LICENSE
import math
from functools import lru_cache
import functools
from typing import Tuple
import paddle
from paddle import Tensor
from .tuner import jit_tuner
from .utils import get_num_sms, ceil_div, get_col_major_tma_aligned_tensor, get_m_alignment_for_contiguous_layout
from .utils import (
ceil_div,
get_col_major_tma_aligned_tensor,
get_m_alignment_for_contiguous_layout,
get_num_sms,
)
# C++ code templates
includes = ('"deep_gemm/fp8_gemm.cuh"', )
includes = ('"deep_gemm/fp8_gemm.cuh"',)
template = """
using namespace deep_gemm;
@@ -33,58 +39,32 @@ using namespace deep_gemm;
constexpr auto N = {N}, K = {K};
constexpr auto BLOCK_M = {BLOCK_M};
constexpr auto BLOCK_N = {BLOCK_N};
constexpr auto BLOCK_K = 128;
constexpr auto BLOCK_N_PADDING = {BLOCK_N_PADDING};
constexpr auto kSwizzleDMode = {SWIZZLE_D_MODE};
constexpr auto kNumGroups = 1;
constexpr auto kNumStages = {NUM_STAGES};
constexpr auto kNumTMAMulticast = {NUM_TMA_MULTICAST};
constexpr auto kIsTMAMulticastOnA = {IS_TMA_MULTICAST_ON_A};
// Make a templated GEMM
using gemm_t = Gemm<N, K, BLOCK_M, BLOCK_N, BLOCK_K, BLOCK_N_PADDING, kSwizzleDMode, kNumGroups, kNumStages, kNumTMAMulticast, kIsTMAMulticastOnA, GemmType::Normal>;
using GemmType = Gemm<N, K, BLOCK_M, BLOCK_N, 128, 1, kNumStages, kNumTMAMulticast, GemmType::Normal>;
// Launch kernel
auto tma_a_desc = gemm_t::make_2d_tma_a_desc(lhs, m);
auto tma_b_desc = gemm_t::make_2d_tma_b_desc(rhs);
auto tma_scales_a_desc = gemm_t::make_2d_tma_scales_a_desc(lhs_scales, m);
auto tma_d_desc = gemm_t::make_2d_tma_d_desc(out, m);
gemm_t::run(out, rhs_scales, nullptr,
m,
tma_a_desc, tma_b_desc, tma_scales_a_desc, tma_d_desc,
stream, num_sms, smem_size);
auto tma_a_desc = GemmType::make_2d_tma_a_desc(lhs, m);
auto tma_b_desc = GemmType::make_2d_tma_b_desc(rhs);
auto tma_scales_a_desc = GemmType::make_2d_tma_scales_a_desc(lhs_scales, m);
auto tma_d_desc = GemmType::make_2d_tma_d_desc(out, m);
GemmType::run(out, rhs_scales, nullptr,
m,
tma_a_desc, tma_b_desc, tma_scales_a_desc, tma_d_desc,
stream, num_sms, smem_size);
"""
def is_tma_multicast_legal(shape_dim: int, block_dim: int, num_tma_multicast: int, num_sms: int) -> bool:
def is_tma_multicast_legal(n: int, block_n: int, num_tma_multicast: int, num_sms: int) -> bool:
if num_tma_multicast == 1:
return True
return (shape_dim % (block_dim * num_tma_multicast) == 0) and num_sms % num_tma_multicast == 0
return (n % (block_n * num_tma_multicast) == 0) and num_sms % num_tma_multicast == 0
def get_swizzle_mode(block_n: int) -> int:
# TODO: remove some candidates if slow
elem_size = 2
for mode_bytes in (128, 64, 32):
if (block_n * elem_size) % mode_bytes == 0:
return mode_bytes
return 0
def get_block_n_padding_for_smem_d(block_n: int) -> int:
# NOTES: padding is for solving bank conflicts, but wastes shared memory space
elem_size, requirement = 2, (4, 8)
bank_stride = (block_n * elem_size) // 4
padding = (requirement[0] - bank_stride) % requirement[1]
return (((padding + requirement[1]) if padding < 0 else padding) * 4) // elem_size
def get_smem_config(num_stages: int, k: int, block_m: int, block_n: int, block_k: int = 128) -> Tuple[int, int, int]:
# Try swizzle first, as it does not waste shared memory
swizzle_mode = get_swizzle_mode(block_n)
block_n_padding = get_block_n_padding_for_smem_d(block_n) if swizzle_mode == 0 else 0
smem_d = block_m * (block_n + block_n_padding) * 2
def get_smem_size(num_stages: int, k: int, block_m: int, block_n: int, block_k: int = 128) -> int:
smem_d = block_m * block_n * 2
smem_a_per_stage = block_m * block_k
smem_scales_a_per_stage = block_m * 4
smem_b_per_stage = block_n * block_k
@@ -98,22 +78,18 @@ def get_smem_config(num_stages: int, k: int, block_m: int, block_n: int, block_k
smem_size += num_stages * smem_b_per_stage
smem_size += ceil_div(smem_scales_b * (1 if block_k % block_n == 0 else 2), 8) * 8
smem_size += smem_barrier
# Swizzle and padding are not compatible
assert int(swizzle_mode > 0) + int(block_n_padding > 0) <= 1
return smem_size, swizzle_mode, block_n_padding
return smem_size
@lru_cache(maxsize=None)
def get_best_configs(m: int, n: int, k: int, num_groups: int, num_sms: int,
is_grouped_contiguous: bool = False, is_grouped_masked: bool = False) -> \
Tuple[int, int, int, int, Tuple[int, bool], Tuple[int, int, int]]:
def get_best_configs(
m: int, n: int, k: int, num_groups: int, num_sms: int, is_grouped_contiguous: bool = False
) -> Tuple[int, int, int, int, int]:
if not is_grouped_contiguous:
block_ms = (64, 128, 256)
# TODO: for some cases, smaller M block is better, add them into tuning space
block_ms = (64 if m <= 64 else 128,)
else:
block_ms = (get_m_alignment_for_contiguous_layout(), )
block_ns = tuple(range(16, 129, 8)) + (144, 160, )
block_ms = (get_m_alignment_for_contiguous_layout(),)
block_ns = tuple(range(16, 129, 8))
fix_wave_saturate = lambda x: num_sms if x == 0 else x
get_num_waves = lambda bm, bn: (ceil_div(ceil_div(m, bm) * ceil_div(n, bn) * num_groups, num_sms) if bm else None)
@@ -122,9 +98,7 @@ def get_best_configs(m: int, n: int, k: int, num_groups: int, num_sms: int,
# Decide block sizes by waves
best_block_m, best_block_n = None, None
for block_m in block_ms:
# NOTES: the block sizes can not be too large, so at least one dim less than 128
for block_n in filter(lambda bn: block_m <= 128 or bn <= 128, block_ns):
for block_n in block_ns:
success = False
num_waves, best_num_waves = get_num_waves(block_m, block_n), get_num_waves(best_block_m, best_block_n)
if best_block_m is None or best_block_n is None:
@@ -135,76 +109,48 @@ def get_best_configs(m: int, n: int, k: int, num_groups: int, num_sms: int,
# Check last wave utilization
util = get_last_wave_util(block_m, block_n)
best_util = get_last_wave_util(best_block_m, best_block_n)
success = util > best_util
if util == best_util:
# Case 1: same `block_m`, smaller `block_n` (wasted)
success |= block_m == best_block_m and block_n < best_block_n
# Case 2: same `block_n`, smaller `block_m` (wasted)
success |= block_n == best_block_n and block_m < best_block_m
# Case 3: different for both `block_m` and `block_n`, `block_n` larger is better
success |= block_m != best_block_m and block_n > best_block_n
success = util > best_util or (
util == best_util
and (block_m > best_block_m or (block_m == best_block_m and block_n < best_block_n))
)
best_block_m, best_block_n = (block_m, block_n) if success else (best_block_m, best_block_n)
assert best_block_m is not None and best_block_n is not None
# Always pick the longest one
# NOTES: for double B scales, the best number of stages may be reduced
best_num_stages, best_smem_config, sm90_capacity = None, None, 232448
stage_candidates = (8, 7, 6, 5, 4, 3)
if 128 % best_block_n != 0 and 128 // math.gcd(128, best_block_n) <= 4:
# Unrolling both stages and `num_former_iters` will cause large code size
stage_candidates = (4, 3)
for num_stages in stage_candidates:
best_smem_config = get_smem_config(num_stages, k, best_block_m, best_block_n)
if best_smem_config[0] <= sm90_capacity:
best_num_stages, best_smem_size, sm90_capacity = None, None, 232448
for num_stages in (6, 5, 4) if 128 % best_block_n != 0 else (8, 7, 6, 5, 4):
best_smem_size = get_smem_size(num_stages, k, best_block_m, best_block_n)
if best_smem_size <= sm90_capacity:
best_num_stages = num_stages
break
assert best_smem_config is not None
assert best_num_stages is not None
# Decide the number of TMA multicast and whether broadcast on A
best_tma_multicast_config = (1, 1)
# Decide the number of TMA multicast
best_num_tma_multicast = 1
if m >= 1024 and is_tma_multicast_legal(n, best_block_n, 2, num_sms) and num_groups == 1:
best_num_tma_multicast = 2
# Try to multicast on the larger block side first
is_dense_gemm = (not is_grouped_contiguous) and (not is_grouped_masked)
is_multicast_legal = {
'A': is_tma_multicast_legal(n, best_block_n, 2, num_sms),
'B': is_tma_multicast_legal(m, best_block_m, 2, num_sms) and is_dense_gemm,
}
for i in ('A', 'B') if best_block_m > best_block_n else ('B', 'A'):
if m >= 512 and is_multicast_legal[i]:
best_tma_multicast_config = (2, int(i == 'A'))
break
return best_block_m, best_block_n, best_num_stages, best_num_tma_multicast, best_smem_size
# Recompute the minimal number of SMs required
# NOTES: less L2 cache usage and less GPU frequency drop
num_waves = get_num_waves(best_block_m, best_block_n)
num_min_sms = ceil_div(ceil_div(m, best_block_m) * ceil_div(n, best_block_n) * num_groups, num_waves)
num_min_sms = ceil_div(num_min_sms, best_tma_multicast_config[0]) * best_tma_multicast_config[0]
assert num_min_sms <= num_sms
return num_min_sms, best_block_m, best_block_n, best_num_stages, best_tma_multicast_config, best_smem_config
@lru_cache()
def auto_tuning_with_compilation(m, n, k, num_sms):
@functools.lru_cache()
def auto_tuning_with_compilation(m, n, k):
global includes, template
if num_sms is None:
num_sms = get_num_sms()
num_sms, block_m, block_n, num_stages, tma_multicast_config, smem_config = get_best_configs(m, n, k, 1, num_sms)
num_sms = get_num_sms()
block_m, block_n, num_stages, num_tma_multicast, smem_size = get_best_configs(m, n, k, 1, num_sms)
runtime = jit_tuner.compile_and_tune(
m,
n,
k,
name="gemm_fp8_fp8_bf16_nt",
keys={
'SWIZZLE_D_MODE': smem_config[1],
'BLOCK_N_PADDING': smem_config[2],
"BLOCK_M": block_m,
"BLOCK_N": block_n,
"K": k,
"N": n,
"NUM_STAGES": num_stages,
"NUM_TMA_MULTICAST": tma_multicast_config[0],
'IS_TMA_MULTICAST_ON_A': tma_multicast_config[1],
"NUM_TMA_MULTICAST": num_tma_multicast,
},
space=(),
includes=includes,
@@ -221,10 +167,10 @@ def auto_tuning_with_compilation(m, n, k, num_sms):
),
template=template,
)
return runtime, num_sms, smem_config
return runtime, num_sms, smem_size
def gemm_fp8_fp8_bf16_nt(lhs: Tuple[Tensor, Tensor], rhs: Tuple[Tensor, Tensor], out: Tensor, num_sms=132) -> None:
def gemm_fp8_fp8_bf16_nt(lhs: Tuple[Tensor, Tensor], rhs: Tuple[Tensor, Tensor], out: Tensor) -> None:
"""
Do a normal GEMM with FP8 inputs and BF16 output, with 1x128 LHS scaling and 128x128 RHS scaling.
LHS, RHS, RHS scaling factors, and output tensors must be in contiguous format.
@@ -264,7 +210,7 @@ def gemm_fp8_fp8_bf16_nt(lhs: Tuple[Tensor, Tensor], rhs: Tuple[Tensor, Tensor],
# Do nothing if `m` is zero
if m == 0:
return
runtime, num_sms, smem_config = auto_tuning_with_compilation(m, n, k, num_sms)
args = (lhs, lhs_scales, rhs, rhs_scales, out, m, paddle.device.current_stream().stream_base, num_sms, smem_config[0])
runtime, num_sms, smem_size = auto_tuning_with_compilation(m, n, k)
args = (lhs, lhs_scales, rhs, rhs_scales, out, m, paddle.device.cuda.current_stream(), num_sms, smem_size)
# Run the kernel.
runtime(*args)

View File

@@ -17,17 +17,17 @@
# Licensed under the MIT License - https://github.com/deepseek-ai/DeepEP/blob/main/LICENSE
import functools
from functools import reduce
from typing import Tuple
import paddle
from paddle import Tensor
from .gemm import get_best_configs, get_block_n_padding_for_smem_d
from .gemm import get_best_configs
from .tuner import jit_tuner
from .utils import get_col_major_tma_aligned_tensor, get_num_sms
# C++ code templates
includes = ('"deep_gemm/fp8_gemm.cuh"', )
includes = ('"deep_gemm/fp8_gemm.cuh"',)
template = """
using namespace deep_gemm;
@@ -35,35 +35,30 @@ using namespace deep_gemm;
constexpr auto N = {N}, K = {K};
constexpr auto BLOCK_M = {BLOCK_M};
constexpr auto BLOCK_N = {BLOCK_N};
constexpr auto BLOCK_K = 128;
constexpr auto BLOCK_N_PADDING = {BLOCK_N_PADDING};
constexpr auto kSwizzleDMode = {SWIZZLE_D_MODE};
constexpr auto kNumGroups = {NUM_GROUPS};
constexpr auto kNumStages = {NUM_STAGES};
constexpr auto kNumTMAMulticast = {NUM_TMA_MULTICAST};
constexpr auto kIsTMAMulticastOnA = {IS_TMA_MULTICAST_ON_A};
// Make a templated grouped GEMM
using gemm_t = Gemm<N, K, BLOCK_M, BLOCK_N, BLOCK_K, BLOCK_N_PADDING, kSwizzleDMode, kNumGroups, kNumStages, kNumTMAMulticast, kIsTMAMulticastOnA, GemmType::{GEMM_TYPE}>;
using GemmType = Gemm<N, K, BLOCK_M, BLOCK_N, 128, {NUM_GROUPS}, kNumStages, kNumTMAMulticast, GemmType::{GEMM_TYPE}>;
// Launch kernel
auto tma_a_desc = gemm_t::make_2d_tma_a_desc(lhs, m);
auto tma_b_desc = gemm_t::make_2d_tma_b_desc(rhs);
auto tma_scales_a_desc = gemm_t::make_2d_tma_scales_a_desc(lhs_scales, m);
auto tma_d_desc = gemm_t::make_2d_tma_d_desc(out, m);
gemm_t::run(out, rhs_scales, grouped_layout,
m,
tma_a_desc, tma_b_desc, tma_scales_a_desc, tma_d_desc,
stream, num_sms, smem_size);
auto tma_a_desc = GemmType::make_2d_tma_a_desc(lhs, m);
auto tma_b_desc = GemmType::make_2d_tma_b_desc(rhs);
auto tma_scales_a_desc = GemmType::make_2d_tma_scales_a_desc(lhs_scales, m);
auto tma_d_desc = GemmType::make_2d_tma_d_desc(out, m);
GemmType::run(out, rhs_scales, grouped_layout,
m,
tma_a_desc, tma_b_desc, tma_scales_a_desc, tma_d_desc,
stream, num_sms, smem_size);
"""
@functools.lru_cache()
def auto_tuning_with_compilation_grouped_gemm_contiguous(m, n, k, num_groups, num_sms):
global includes, template
if num_sms is None:
num_sms = get_num_sms()
num_sms, block_m, block_n, num_stages, tma_multicast_config, smem_config = get_best_configs(m, n, k, 1, num_sms, is_grouped_contiguous=True)
block_m, block_n, num_stages, num_tma_multicast, smem_size = get_best_configs(
m, n, k, 1, num_sms, is_grouped_contiguous=True
)
runtime = jit_tuner.compile_and_tune(
m,
n,
@@ -72,15 +67,12 @@ def auto_tuning_with_compilation_grouped_gemm_contiguous(m, n, k, num_groups, nu
keys={
"BLOCK_M": block_m,
"BLOCK_N": block_n,
"SWIZZLE_D_MODE": smem_config[1],
"BLOCK_N_PADDING": smem_config[2],
"GEMM_TYPE": "GroupedContiguous",
"K": k,
"N": n,
"NUM_GROUPS": num_groups,
"NUM_STAGES": num_stages,
"NUM_TMA_MULTICAST": tma_multicast_config[0],
"IS_TMA_MULTICAST_ON_A": tma_multicast_config[1],
"NUM_TMA_MULTICAST": num_tma_multicast,
},
space=(),
includes=includes,
@@ -99,11 +91,11 @@ def auto_tuning_with_compilation_grouped_gemm_contiguous(m, n, k, num_groups, nu
),
template=template,
)
return runtime, num_sms, smem_config
return runtime, num_sms, smem_size
def m_grouped_gemm_fp8_fp8_bf16_nt_contiguous(
lhs: Tuple[Tensor, Tensor], rhs: Tuple[Tensor, Tensor], out: Tensor, m_indices: Tensor, num_sms=132
lhs: Tuple[Tensor, Tensor], rhs: Tuple[Tensor, Tensor], out: Tensor, m_indices: Tensor
) -> None:
"""
Do a grouped GEMM (contiguous format) with FP8 inputs and BF16 output, with 1x128 LHS scaling and 128x128 RHS scaling.
@@ -131,8 +123,7 @@ def m_grouped_gemm_fp8_fp8_bf16_nt_contiguous(
m, k = lhs.shape
num_groups, n, k_ = rhs.shape
m_, n_ = out.shape
m_shape = m_indices.shape
m__ = reduce(lambda x, y: x * y, m_shape)
m__ = m_indices.numel()
# Type and shape checks
assert m == m_ == m__ and k == k_ and n == n_
assert lhs_scales.shape == [m, (k + 127) // 128]
@@ -153,7 +144,8 @@ def m_grouped_gemm_fp8_fp8_bf16_nt_contiguous(
return
# Auto-tuning with compilation
global includes, template
runtime, num_sms, smem_config = auto_tuning_with_compilation_grouped_gemm_contiguous(m, n, k, num_groups, num_sms)
num_sms = get_num_sms()
runtime, num_sms, smem_size = auto_tuning_with_compilation_grouped_gemm_contiguous(m, n, k, num_groups, num_sms)
args = (
lhs,
@@ -164,68 +156,15 @@ def m_grouped_gemm_fp8_fp8_bf16_nt_contiguous(
m_indices,
m,
num_groups,
paddle.device.current_stream().stream_base,
paddle.device.cuda.current_stream(),
num_sms,
smem_config[0],
smem_size,
)
runtime(*args)
@functools.lru_cache()
def auto_tuning_with_compilation_grouped_gemm_masked(m, expected_m, n, k, num_groups, num_sms):
# Auto-tuning with compilation
global includes, template
if num_sms is None:
num_sms = get_num_sms()
num_sms, block_m, block_n, num_stages, tma_multicast_config, smem_config = get_best_configs(
expected_m, n, k, num_groups, num_sms, is_grouped_masked=True
)
# Extra checks for TMA store
if num_groups > 1 and m > block_m:
while m % block_m != 0 and block_m > 128:
block_m = block_m // 2
assert (
m % block_m == 0
), f"For masked grouped GEMM, shape M should be multiple of the block M (current block M: {block_m})"
runtime = jit_tuner.compile_and_tune_group_gemm_masked(
name="m_grouped_gemm_fp8_fp8_bf16_nt",
keys={
"N": n,
"K": k,
"BLOCK_M": block_m,
"BLOCK_N": block_n,
'SWIZZLE_D_MODE': smem_config[1],
'BLOCK_N_PADDING': smem_config[2],
"NUM_GROUPS": num_groups,
"NUM_STAGES": num_stages,
"NUM_TMA_MULTICAST": tma_multicast_config[0],
'IS_TMA_MULTICAST_ON_A': tma_multicast_config[1],
"GEMM_TYPE": "GroupedMasked",
},
space=(),
includes=includes,
arg_defs=(
("lhs", paddle.float8_e4m3fn),
("lhs_scales", paddle.float32),
("rhs", paddle.float8_e4m3fn),
("rhs_scales", paddle.float32),
("out", paddle.bfloat16),
("grouped_layout", paddle.int32),
("m", int),
("stream", paddle.device.cuda.Stream),
("num_sms", int),
("smem_size", int),
),
template=template,
)
return runtime, num_sms, smem_config
def m_grouped_gemm_fp8_fp8_bf16_nt_masked(
lhs: Tuple[Tensor, Tensor], rhs: Tuple[Tensor, Tensor], out: Tensor, masked_m: Tensor, expected_m: int, num_sms=132
lhs: Tuple[Tensor, Tensor], rhs: Tuple[Tensor, Tensor], out: Tensor, masked_m: Tensor, expected_m: int
) -> None:
"""
Do a grouped GEMM (masked format) with FP8 inputs and BF16 output, with 1x128 LHS scaling and 128x128 RHS scaling.
@@ -252,8 +191,7 @@ def m_grouped_gemm_fp8_fp8_bf16_nt_masked(
num_groups, m, k = lhs.shape
num_groups_, n, k_ = rhs.shape
num_groups__, m_, n_ = out.shape
masked_m_shape = masked_m.shape
num_groups___ = reduce(lambda x, y: x * y, masked_m_shape)
num_groups___ = masked_m.numel()
# Type and shape checks
assert num_groups == num_groups_ == num_groups__ == num_groups___
@@ -271,10 +209,20 @@ def m_grouped_gemm_fp8_fp8_bf16_nt_masked(
# LHS scales must be transposed for TMA load, but not for RHS scales
lhs_scales = get_col_major_tma_aligned_tensor(lhs_scales)
assert rhs_scales.is_contiguous()
runtime, num_sms, smem_config = auto_tuning_with_compilation_grouped_gemm_masked(
m, expected_m, n, k, num_groups, num_sms
# Auto-tuning with compilation
global includes, template
num_sms = get_num_sms()
block_m, block_n, num_stages, num_tma_multicast, smem_size = get_best_configs(
expected_m, n, k, num_groups, num_sms
)
# Extra checks for TMA store
if num_groups > 1 and m > block_m:
assert (
m % block_m == 0
), f"For masked grouped GEMM, shape M should be multiple of the block M (current block M: {block_m})"
args = (
lhs,
lhs_scales,
@@ -283,9 +231,38 @@ def m_grouped_gemm_fp8_fp8_bf16_nt_masked(
out,
masked_m,
m,
paddle.device.current_stream().stream_base,
paddle.device.cuda.current_stream(),
num_sms,
smem_config[0],
smem_size,
)
runtime = jit_tuner.compile_and_tune_group_gemm_masked(
name="m_grouped_gemm_fp8_fp8_bf16_nt",
keys={
"N": n,
"K": k,
"BLOCK_M": block_m,
"BLOCK_N": block_n,
"NUM_GROUPS": num_groups,
"NUM_STAGES": num_stages,
"NUM_TMA_MULTICAST": num_tma_multicast,
"GEMM_TYPE": "GroupedMasked",
},
space=(),
includes=includes,
arg_defs=(
("lhs", paddle.float8_e4m3fn),
("lhs_scales", paddle.float32),
("rhs", paddle.float8_e4m3fn),
("rhs_scales", paddle.float32),
("out", paddle.bfloat16),
("grouped_layout", paddle.int32),
("m", int),
("stream", paddle.device.cuda.Stream),
("num_sms", int),
("smem_size", int),
),
template=template,
args=args,
)
# Run the kernel

View File

@@ -20,6 +20,8 @@ import copy
import os
from typing import Any, Dict
import paddle
from ..jit import Runtime, build, cpp_format, generate
@@ -35,6 +37,7 @@ class JITTuner:
includes: tuple,
arg_defs: tuple,
template: str,
args: tuple,
) -> Runtime:
# NOTES: we always assume the space and template will not change
# We also assume the GPU device will not be changed
@@ -50,6 +53,7 @@ class JITTuner:
print(f"Auto-tuning JIT kernel {name} with keys {keys}")
assert signature not in self.tuned
assert args is not None
space = (dict(),) if len(space) == 0 else space
kernels = []
@@ -64,7 +68,32 @@ class JITTuner:
best_runtime, best_time, best_keys = None, None, None
for runtime, tuned_keys in kernels:
elapsed_time = 0
if len(space) > 1:
# Check kernel validity
return_code = runtime(*args)
if return_code != 0:
# Pass illegal kernels, e.g. insufficient shared memory capacity
if os.getenv("DG_JIT_DEBUG", None):
print(
f"Illegal JIT kernel {name} with keys {keys} and tuned keys {tuned_keys}: error code {return_code}"
)
continue
# Measure performance with L2 flush and a large GEMM kernel before to reduce overhead between kernels
start_event = paddle.device.cuda.Event(enable_timing=True)
end_event = paddle.device.cuda.Event(enable_timing=True)
paddle.empty(int(256e6 // 4), dtype=paddle.int32).zero_()
paddle.randn((8192, 8192), dtype=paddle.float32, device="cuda") @ paddle.randn(
(8192, 8192), dtype=paddle.float32
)
start_event.record()
for i in range(20):
assert runtime(*args) == 0
end_event.record()
end_event.synchronize()
elapsed_time = start_event.elapsed_time(end_event)
else:
elapsed_time = 0
# Compare if better
if best_time is None or elapsed_time < best_time:
@@ -90,6 +119,7 @@ class JITTuner:
includes: tuple,
arg_defs: tuple,
template: str,
# args: tuple,
) -> Runtime:
# NOTES: we always assume the space and template will not change
# We also assume the GPU device will not be changed

View File

@@ -109,13 +109,6 @@ def get_col_major_tma_aligned_tensor(x: Tensor) -> Tensor:
assert x.dim() in (2, 3)
remove_dim = False
if x.dim() == 2:
m, n = x.shape
aligned_m = get_tma_aligned_size(m, x.element_size())
if aligned_m == m and x.strides[0] == 1 and x.strides[1] == aligned_m:
return x
x, remove_dim = x.unsqueeze(0), True
b, m, n = x.shape

View File

@@ -92,7 +92,7 @@ if __name__ == "__main__":
packages=["deep_gemm", "deep_gemm/jit", "deep_gemm/jit_kernels"],
package_data={
"deep_gemm": [
"include/deep_gemm/*",
"include/deep_gemm/**/*",
"include/cute/**/*",
"include/cutlass/**/*",
]

View File

@@ -114,8 +114,10 @@ def construct_grouped(
def test_gemm() -> None:
print("Testing GEMM:")
for m in (64, 128, 4096):
for k, n in [(7168, 2112), (1536, 24576), (512, 32768), (16384, 7168), (7168, 4096), (2048, 7168)]:
for m in (64,):
for k, n in [
(7168, 2112),
]:
x_fp8, y_fp8, out, ref_out = construct(m, k, n)
deep_gemm.gemm_fp8_fp8_bf16_nt(x_fp8, y_fp8, out)
diff = calc_diff(out, ref_out)
@@ -127,7 +129,7 @@ def test_gemm() -> None:
def test_m_grouped_gemm_contiguous() -> None:
print("Testing grouped contiguous GEMM:")
for num_groups, m, k, n in ((8, 4096, 7168, 4096), (8, 4096, 2048, 7168), (4, 8192, 2048, 7168), (4, 8192, 7168, 4096), ):
for num_groups, m, k, n in ((4, 8192, 7168, 4096),):
# TODO: make a stronger test
x_fp8, y_fp8, out, ref_out = construct_grouped(num_groups, m, k, n, is_masked=False)
m_indices = paddle.arange(0, num_groups, dtype=paddle.int32)
@@ -142,9 +144,9 @@ def test_m_grouped_gemm_contiguous() -> None:
def test_m_grouped_gemm_masked() -> None:
print("Testing grouped masked GEMM:")
for num_groups, m in ((1, 1024), (2, 512), (4, 256)):
for k, n in ((7168, 4096), (2048, 7168), ):
for num_groups, m in ((1, 1024),):
for k, n in ((7168, 4096),):
# Test correctness
masked_m_candidates = list(filter(lambda candidate: candidate <= m, (64, 128, 192, 256, 320, 384)))
for i in range(10):
@@ -156,7 +158,7 @@ def test_m_grouped_gemm_masked() -> None:
masked_m_float = paddle.cast(masked_m, "float32")
masked_m_mean = paddle.mean(masked_m_float)
masked_m_mean_int = paddle.cast(masked_m_mean, "int32")
expected_m = min(int(masked_m_mean_int + 1), m)
expected_m = min(masked_m_mean_int + 1, m)
deep_gemm.m_grouped_gemm_fp8_fp8_bf16_nt_masked(x_fp8, y_fp8, out, masked_m, expected_m)
for j in range(num_groups):
diff = calc_diff(out[j, : masked_m[j].item()], ref_out[j, : masked_m[j].item()])

View File

@@ -53,6 +53,20 @@ def run_single(func):
p.join()
def run_multi(func_list):
processes = []
for func in func_list:
processes.append(multiprocessing.Process(target=func))
processes.append(multiprocessing.Process(target=func))
processes.append(multiprocessing.Process(target=func))
for p in processes:
p.start()
for p in processes:
p.join()
cc_flag = get_gencode_flags(compiled_all=False)
cc = get_sm_version()
@@ -237,14 +251,17 @@ def setup_paddle_bwd_ops():
ext_modules=CUDAExtension(
include_dirs=paddle_includes,
sources=sources,
extra_compile_args={}
),
)
if __name__ == "__main__":
setup_fast_ln()
setup_fused_ln()
setup_causal_conv1d()
setup_selective_scan()
setup_paddle_bwd_ops()
run_multi(
[
setup_fast_ln,
setup_fused_ln,
setup_causal_conv1d,
setup_selective_scan,
setup_paddle_bwd_ops,
],
)

View File

@@ -94,22 +94,22 @@ class DistDataLoader(paddle.io.DataLoader):
if self._need_data:
self._dataloader = paddle.io.DataLoader(
dataset=dataset,
feed_list=feed_list,
places=places,
return_list=return_list,
batch_sampler=batch_sampler,
batch_size=batch_size,
shuffle=shuffle,
drop_last=drop_last,
collate_fn=collate_fn,
num_workers=num_workers,
use_buffer_reader=use_buffer_reader,
prefetch_factor=prefetch_factor,
use_shared_memory=use_shared_memory,
timeout=timeout,
worker_init_fn=worker_init_fn,
persistent_workers=persistent_workers,
dataset,
feed_list,
places,
return_list,
batch_sampler,
batch_size,
shuffle,
drop_last,
collate_fn,
num_workers,
use_buffer_reader,
prefetch_factor,
use_shared_memory,
timeout,
worker_init_fn,
persistent_workers,
)
self._lazy_dataloader_iter = None

View File

@@ -1,5 +1,4 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
# Copyright 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,19 +16,15 @@
Implement base data transfer protocol between any two functions, modules.
We can subclass Protocol to define more detailed batch info with specific keys
"""
import copy
from dataclasses import dataclass, field
from typing import Dict, List, Sequence, Union
from typing import Dict, List, Union
import numpy as np
import paddle
import pandas as pd
from paddle.io import DataLoader
from ...utils import logger
from ...utils.nested import flatten_list
__all__ = [
"DataProto",
"union_tensor_dict",
@@ -54,36 +49,7 @@ class TensorDict:
self._tensors[key] = tensor
def __getitem__(self, key):
if isinstance(key, str):
return self._tensors[key]
elif isinstance(key, slice):
strides = [1] if key.step is None else [key.step]
tensor_dict_slice = {
k: paddle.strided_slice(v, axes=[0], starts=[key.start], ends=[key.stop], strides=strides)
for k, v in self._tensors.items()
}
batch_size = tensor_dict_slice[list(tensor_dict_slice.keys())[0]].shape[: self.num_batch_dims]
return TensorDict(tensor_dict_slice, batch_size=batch_size, num_batch_dims=self.num_batch_dims)
else:
raise KeyError(f"Unsupported key type: {type(key)}")
def __contains__(self, key):
return key in self._tensors
def update(self, other):
"""Updates the tensor dictionary of the current object.
Args:
other (TensorDict or dict): A TensorDict or a dictionary containing tensors to update with.
Returns:
None
"""
if isinstance(other, TensorDict):
other = other._tensors
for key, value in other.items():
# consistency was not checked
self._tensors[key] = value
return self._tensors[key]
def keys(self):
return self._tensors.keys()
@@ -96,218 +62,16 @@ class TensorDict:
self._tensors[key] = self._tensors[key].to(device)
return self
def pop(self, key: str, default=None):
"""Removes the value associated with the specified key from the TensorDict and returns it.
Args:
key (str): The key to remove from the TensorDict.
default (Optional[Any]): The default value to return if the key is not found. Defaults to None.
Returns:
Any: The value corresponding to the key if it exists; otherwise, returns the default value if provided.
Raises:
KeyError: If the key does not exist and no default value is provided.
"""
if key in self._tensors:
value = self._tensors.pop(key)
return value
elif default is not None:
return default
else:
raise KeyError(f"Key '{key}' not found in TensorDict and no default value provided.")
def select(self, *keys, default=None):
"""Selects tensors by the given keys and returns a new TensorDict object.
Args:
*keys (tuple): One or more keys to select.
default (Any, optional): The default value to return if a key does not exist. Defaults to None.
Returns:
TensorDict: A new TensorDict containing the selected tensors.
Raises:
KeyError: If a key does not exist and no default value is provided.
"""
selected_tensors = {}
for key in keys:
if key in self._tensors:
selected_tensors[key] = self._tensors[key]
elif default is not None:
selected_tensors[key] = default
else:
raise KeyError(f"Key '{key}' not found in TensorDict and no default value provided.")
batch_size = (
list(selected_tensors[list(selected_tensors.keys())[0]].shape[: self.num_batch_dims])
if selected_tensors
else None
)
return TensorDict(selected_tensors, batch_size=batch_size, num_batch_dims=self.num_batch_dims)
def rename_key_(self, old_keys, new_keys):
"""Renames keys in the TensorDict for use with the `rename` method of DataProto.
The naming convention is directly adopted from the official PyTorch implementation.
Args:
old_keys (str or List[str]): The key or list of keys to be renamed.
new_keys (str or List[str]): The new key or list of new keys.
Returns:
self: The updated TensorDict instance with renamed keys.
Raises:
ValueError: If the lengths of `old_keys` and `new_keys` do not match.
KeyError: If any key in `old_keys` does not exist in the TensorDict,
or if any key in `new_keys` already exists in the TensorDict.
"""
if isinstance(old_keys, (str,)):
old_keys = [old_keys]
if isinstance(new_keys, (str,)):
new_keys = [new_keys]
if len(old_keys) != len(new_keys):
raise ValueError("old_keys and new_keys must have the same length.")
for old_key, new_key in zip(old_keys, new_keys):
if old_key not in self._tensors:
raise KeyError(f"Key '{old_key}' not found in TensorDict.")
if new_key in self._tensors:
raise KeyError(f"Key '{new_key}' already exists in TensorDict.")
self._tensors[new_key] = self._tensors.pop(old_key)
return self
@classmethod
def concat(cls, tensordict_list, axis=0):
"""Concatenate multiple TensorDict objects along a specified axis.
Args:
cls (class): The TensorDict class.
tensordict_list (list of TensorDict): The list of TensorDict objects to concatenate.
axis (int, optional): The axis to concatenate along, default is 0.
Returns:
TensorDict: The concatenated TensorDict object.
Raises:
ValueError: If tensordict_list is empty, or the keys of TensorDict objects are different,
or the dimensions other than the concatenation axis do not match, a ValueError will be raised.
"""
if not tensordict_list:
raise ValueError("tensordict_list must not be empty")
first_tensordict = tensordict_list[0]
first_keys = first_tensordict.keys()
first_shapes = {key: tensor.shape for key, tensor in first_tensordict.items()}
for tensordict in tensordict_list:
if tensordict.keys() != first_keys:
raise ValueError("All TensorDict objects must have the same keys")
for key in first_keys:
if (
tensordict[key].shape[:axis] + tensordict[key].shape[axis + 1 :]
!= first_shapes[key][:axis] + first_shapes[key][axis + 1 :]
):
raise ValueError(f"Shapes of tensor '{key}' do not match except on concatenation axis {axis}")
concatenated_tensors = {
key: paddle.concat([tensordict[key] for tensordict in tensordict_list], axis=axis) for key in first_keys
}
batch_size = concatenated_tensors[list(concatenated_tensors.keys())[0]].shape[
: tensordict_list[0].num_batch_dims
]
return cls(concatenated_tensors, batch_size=batch_size, num_batch_dims=tensordict_list[0].num_batch_dims)
def get(self, key, default=None):
return self._tensors.get(key, default)
def tensor_or_tensordict_concat(
x: Union[Sequence[paddle.Tensor], Sequence[TensorDict]],
axis: int | paddle.Tensor = 0,
name: str | None = None,
):
"""Concatenates multiple tensors or TensorDicts along the specified axis.
Args:
x (Union[Sequence[paddle.Tensor], Sequence[TensorDict]]): A sequence of tensors or TensorDicts to concatenate.
axis (int or paddle.Tensor, optional): The axis along which to concatenate. Defaults to 0.
If `x` is a sequence of tensors, `axis` should be an integer;
if `x` is a sequence of TensorDicts, `axis` should be a paddle.Tensor. Defaults to 0.
name (str, optional): The name of the operation (optional). Defaults to None.
Returns:
Union[paddle.Tensor, TensorDict]: The concatenated tensor or TensorDict.
"""
def is_tensor_sequence():
if isinstance(x[0], paddle.Tensor):
return True
else:
return False
if not is_tensor_sequence() and paddle.in_dynamic_mode():
return TensorDict.concat(x)
else:
return paddle.concat(x, axis, name)
def union_tensor_dict(tensor_dict1: TensorDict, tensor_dict2: TensorDict) -> TensorDict:
"""Union two tensordicts."""
assert (
tensor_dict1.batch_size == tensor_dict2.batch_size
), f"Two tensor dict must have identical batch size. Got {tensor_dict1.batch_size} and {tensor_dict2.batch_size}"
for key in tensor_dict2.keys():
if key not in tensor_dict1.keys():
tensor_dict1[key] = tensor_dict2[key]
else:
assert tensor_dict1[key].equal(
tensor_dict2[key]
), f"{key} in tensor_dict1 and tensor_dict2 are not the same object"
return tensor_dict1
def union_numpy_dict(tensor_dict1: dict[np.ndarray], tensor_dict2: dict[np.ndarray]) -> dict[np.ndarray]:
"""Merges two dictionaries containing NumPy arrays.
Args:
tensor_dict1 (dict[np.ndarray]): The first dictionary containing NumPy arrays.
tensor_dict2 (dict[np.ndarray]): The second dictionary containing NumPy arrays.
Returns:
dict[np.ndarray]: The merged dictionary.
Raises:
AssertionError: If the values corresponding to the same key in both dictionaries are not NumPy arrays,
or if the arrays are not equal (considering NaNs and object types).
"""
for key, val in tensor_dict2.items():
if key in tensor_dict1:
assert isinstance(tensor_dict2[key], np.ndarray)
assert isinstance(tensor_dict1[key], np.ndarray)
# to properly deal with nan and object type
assert pd.DataFrame(tensor_dict2[key]).equals(
pd.DataFrame(tensor_dict1[key])
), f"{key} in tensor_dict1 and tensor_dict2 are not the same object"
tensor_dict1[key] = val
return tensor_dict1
def union_two_dict(dict1: Dict, dict2: Dict):
"""Merges two dictionaries. Raises an error if the same key exists in both dictionaries but maps to different objects.
"""Union two dict. Will throw an error if there is an item not the same object with the same key.
Args:
dict1 (Dict): The first dictionary.
dict2 (Dict): The second dictionary.
dict1:
dict2:
Returns:
Dict: The merged dictionary.
Raises:
AssertionError: If the same key exists in both dictionaries with different values.
"""
for key, val in dict2.items():
if key in dict1:
@@ -344,39 +108,42 @@ def pad_dataproto_to_divisor(data: "DataProto", size_divisor: int):
def unpad_dataproto(data: "DataProto", pad_size):
"""Removes the padded parts from the given DataProto object.
Args:
data (DataProto): The DataProto object containing the data.
pad_size (int): The size of the padding to remove.
Returns:
DataProto: The DataProto object with the padding removed.
"""
if pad_size != 0:
data = data[:-pad_size]
return data
def union_tensor_dict(tensor_dict1: TensorDict, tensor_dict2: TensorDict) -> TensorDict:
"""Union two tensordicts."""
assert (
tensor_dict1.batch_size == tensor_dict2.batch_size
), f"Two tensor dict must have identical batch size. Got {tensor_dict1.batch_size} and {tensor_dict2.batch_size}"
for key in tensor_dict2.keys():
if key not in tensor_dict1.keys():
tensor_dict1[key] = tensor_dict2[key]
else:
assert tensor_dict1[key].equal(
tensor_dict2[key]
), f"{key} in tensor_dict1 and tensor_dict2 are not the same object"
return tensor_dict1
def union_numpy_dict(tensor_dict1: dict[np.ndarray], tensor_dict2: dict[np.ndarray]) -> dict[np.ndarray]:
for key, val in tensor_dict2.items():
if key in tensor_dict1:
assert isinstance(tensor_dict2[key], np.ndarray)
assert isinstance(tensor_dict1[key], np.ndarray)
# to properly deal with nan and object type
assert pd.DataFrame(tensor_dict2[key]).equals(
pd.DataFrame(tensor_dict1[key])
), f"{key} in tensor_dict1 and tensor_dict2 are not the same object"
tensor_dict1[key] = val
return tensor_dict1
def list_of_dict_to_dict_of_list(list_of_dict: list[dict]):
"""Converts a list of dictionaries into a dictionary of lists.
Args:
list_of_dict (list[dict]): A list containing dictionaries.
Returns:
dict: A dictionary where each key maps to a list of values collected from the input dictionaries.
Raises:
AssertionError: Raised if any key in the dictionaries is not present in the final output dictionary during iteration.
Examples:
>>> list_of_dict = [{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}]
>>> result = list_of_dict_to_dict_of_list(list_of_dict)
>>> print(result)
{'name': ['Alice', 'Bob'], 'age': [25, 30]}
"""
if len(list_of_dict) == 0:
return {}
keys = list_of_dict[0].keys()
@@ -428,15 +195,6 @@ def unfold_batch_dim(data: "DataProto", batch_dims=2):
def collate_fn(x: list["DataProtoItem"]):
"""Combines a list of DataProtoItem objects into a single batch.
Args:
x (list["DataProtoItem"]): A list of DataProtoItem objects.
Returns:
DataProto: A DataProto object containing the combined batch data and non-tensor batch data.
"""
batch = []
non_tensor_batch = []
for data in x:
@@ -449,26 +207,6 @@ def collate_fn(x: list["DataProtoItem"]):
return DataProto(batch=batch, non_tensor_batch=non_tensor_batch)
def make_eos_mask(response_id, eos_token_ids=0, dtype=paddle.int64):
"""
end of sentence token can be int or list: 1 or [1, 2]
e.g. eos_token=1
response_id: [0, 0, 2, 42, 3, 5, 1, 0, 0]
eos_mask: [1, 1, 1, 1, 1, 1, 1, 0, 0]
"""
if isinstance(eos_token_ids, int):
eos_token_ids = [eos_token_ids]
eos_mask = paddle.zeros_like(response_id, dtype=paddle.bool)
for token_id in eos_token_ids:
eos_mask |= response_id == token_id
eos_mask = eos_mask.to("int64")
eos_mask = (paddle.cumsum(eos_mask, axis=1) - eos_mask).to("bool")
eos_mask = paddle.logical_not(eos_mask).to(dtype)
return eos_mask
@dataclass
class DataProtoItem:
# TODO(zhangchi.usc1992) add consistency check
@@ -501,49 +239,11 @@ class DataProto:
return 0
def __getitem__(self, item):
"""
Enhanced indexing for DataProto objects.
Args:
item: Can be one of:
- int: A single index
- slice: A slice object (start:stop:step)
- list: A list of indices
- numpy.ndarray: An array of indices
- torch.Tensor: A tensor of indices
Returns:
DataProto: For all indexing types except single integers
DataProtoItem: Only for single integer indices
"""
# Case 1: Slice object - use the slice method
if isinstance(item, slice):
return self.slice(item.start, item.stop, item.step)
# Case 2: List, numpy array, or torch tensor - use sel_idxs
elif isinstance(item, (list, np.ndarray, paddle.Tensor)):
return self.select_idxs(item)
# Case 3: Single integer - return DataProtoItem for backward compatibility
elif isinstance(item, (int, np.integer)):
tensor_data = self.batch[item]
non_tensor_data = {key: val[item] for key, val in self.non_tensor_batch.items()}
return_type = DataProto if isinstance(item, slice) else DataProtoItem
return return_type(batch=tensor_data, non_tensor_batch=non_tensor_data, meta_info=self.meta_info)
# Case 4: Unsupported type
else:
raise TypeError(f"Indexing with {type(item)} is not supported")
tensor_data = self.batch[item]
non_tensor_data = {key: val[item] for key, val in self.non_tensor_batch.items()}
return DataProtoItem(batch=tensor_data, non_tensor_batch=non_tensor_data, meta_info=self.meta_info)
def print_size(self, prefix=""):
"""Prints the sizes of `tensordict` and `non_tensor_batch`.
Args:
prefix (str, optional): A prefix string to identify the log message. Defaults to an empty string.
Returns:
None
"""
size_of_tensordict = 0
for key, tensor in self.batch.items():
size_of_tensordict += tensor.element_size() * tensor.numel()
@@ -586,19 +286,6 @@ class DataProto:
@classmethod
def from_single_dict(cls, data: Dict[str, Union[paddle.Tensor, np.ndarray]], meta_info=None):
"""Converts a dictionary containing Paddle tensors and NumPy arrays into a DataProto object.
Args:
cls (class): The class invoking this method.
data (Dict[str, Union[paddle.Tensor, np.ndarray]]): A dictionary containing Paddle tensors and NumPy arrays.
meta_info (Any, optional): Optional metadata. Defaults to None.
Returns:
DataProto: A DataProto object containing `tensors` and `non_tensors`.
Raises:
ValueError: If the input `data` contains unsupported data types.
"""
tensors = {}
non_tensors = {}
@@ -649,117 +336,6 @@ class DataProto:
tensor_dict = TensorDict(source=tensors, batch_size=batch_size, num_batch_dims=num_batch_dims)
return cls(batch=tensor_dict, non_tensor_batch=non_tensors, meta_info=meta_info)
@staticmethod
def concatenate_tensors(
gathered_list: List[Union[paddle.Tensor, np.ndarray, List]],
data_parallel_group=None,
sharding_parallel_group=None,
) -> Union[paddle.Tensor, np.ndarray]:
"""Concatenates a list of tensors/arrays that have been gathered from
different distributed ranks. Handles both Paddle Tensors and NumPy arrays,
and accounts for nested lists from `all_gather_object` with multiple ranks.
Args:
gathered_list (List[Union[paddle.Tensor, np.ndarray, List]]):
A list containing tensors or arrays gathered from all ranks.
This list might be nested if `all_gather_object` was used with
multiple groups (e.g., sharding then data parallel).
data_parallel_group (Optional[paddle.distributed.collective.Group]):
The data parallel communication group.
sharding_parallel_group (Optional[paddle.distributed.collective.Group]):
The sharding parallel communication group.
Returns:
Union[paddle.Tensor, np.ndarray]: The concatenated tensor/array.
If the input was Paddle Tensor, returns Paddle Tensor.
If the input was NumPy array, returns NumPy array.
"""
if not gathered_list:
raise ValueError("Input gathered_list cannot be empty.")
is_paddle_tensor = isinstance(gathered_list[0], paddle.Tensor)
if is_paddle_tensor:
return paddle.concat(gathered_list, axis=0)
else:
return np.concatenate(flatten_list(gathered_list), axis=0)
@staticmethod
def pad_tensor(tensor_list, pad_index, dtype, padding_side):
"""Pads a list of tensors or numpy arrays to the same size.
Args:
tensor_list (list): A list of tensors or numpy arrays. All elements must have the same first dimension (shape[0]).
pad_index (int): The value used for padding.
dtype (paddle.DataType): The data type of the resulting padded tensor.
padding_side (str): The padding direction. Use 'right' to pad on the right, or 'left' to pad on the left.
Returns:
Tensor or ndarray: The padded tensor or numpy array with shape (data_num, max_size).
Raises:
ValueError: If `padding_side` is not 'right' or 'left'.
"""
max_size = max([i.shape[-1] for i in tensor_list])
data_num = sum([i.shape[0] for i in tensor_list])
if isinstance(tensor_list[0], paddle.Tensor):
new_tensor = paddle.full((data_num, max_size), pad_index, dtype=dtype)
elif isinstance(tensor_list[0], np.ndarray):
new_tensor = np.full((data_num, max_size), pad_index, dtype=dtype)
offset = 0
for idx, i in enumerate(tensor_list):
# new_tensor[offset : offset + i.shape[0], : i.shape[-1]] = i
data_length = i.shape[-1]
if padding_side == "right":
new_tensor[offset : offset + i.shape[0], :data_length] = i
elif padding_side == "left":
new_tensor[offset : offset + i.shape[0], -data_length:] = i
else:
raise ValueError("padding_side must be 'right' or 'left'")
offset += i.shape[0]
return new_tensor
@staticmethod
def pad_or_concat_tensor_list(tensor_list, pad_index, key):
"""Pads or concatenates a given list of tensors.
Args:
tensor_list (list): The list of tensors to be processed.
pad_index (int): The value used for padding.
key (str): The key identifying the tensor list.
Returns:
Union[np.ndarray, torch.Tensor]: The processed tensor or list of tensors.
"""
left_padding_key = ("prompt", "label_ids")
pad = False if len(flatten_list(tensor_list)[0].shape) == 1 else True
padding_side = "left" if (key in left_padding_key) else "right"
dtype = flatten_list(tensor_list)[0].dtype
if not pad:
return DataProto.concatenate_tensors(tensor_list)
else:
return DataProto.pad_tensor(tensor_list, pad_index, dtype, padding_side)
@staticmethod
def pad_batch_data(tensor_list: List[paddle.Tensor] = None, pad_token_id=None) -> List[paddle.Tensor]:
"""Pads a batch of tensors.
Args:
tensor_list (List[paddle.Tensor], optional): A list of tensors to be padded. Defaults to None.
pad_token_id (int, optional): The token ID used for padding. Defaults to None.
Returns:
List[paddle.Tensor]: The list of padded tensors.
"""
tensor_list = [paddle.unsqueeze(v, axis=0) if v.ndim == 1 else v for v in tensor_list]
padded_tensors = DataProto.pad_tensor(
tensor_list,
pad_index=pad_token_id,
dtype=tensor_list[0].dtype,
padding_side="right",
)
return padded_tensors
def to(self, device) -> "DataProto":
"""move the batch to device
@@ -809,87 +385,6 @@ class DataProto:
return DataProto(batch=sub_batch, non_tensor_batch=non_tensor_batch, meta_info=sub_meta_info)
def select_idxs(self, idxs):
"""
Select specific indices from the DataProto.
Args:
idxs (torch.Tensor or numpy.ndarray or list): Indices to select
Returns:
DataProto: A new DataProto containing only the selected indices
"""
if isinstance(idxs, list):
idxs = paddle.tensor(idxs, dtype=paddle.int32)
if isinstance(idxs, np.ndarray):
idxs_np = idxs
idxs_paddle = paddle.from_numpy(idxs)
else: # torch.Tensor
idxs_paddle = idxs
idxs_np = idxs.numpy()
if self.batch is not None:
# Use TensorDict's built-in indexing capabilities
selected_batch = TensorDict(
source={key: tensor[idxs_paddle] for key, tensor in self.batch.items()},
batch_size=(idxs_paddle.shape[0],),
)
else:
selected_batch = None
selected_non_tensor = {}
for key, val in self.non_tensor_batch.items():
selected_non_tensor[key] = val[idxs_np]
return DataProto(batch=selected_batch, non_tensor_batch=selected_non_tensor, meta_info=self.meta_info)
def slice(self, start=None, end=None, step=None):
"""
Slice the DataProto and return a new DataProto object.
This is an improved version of direct slicing which returns a DataProtoItem.
Args:
start (int, optional): Start index. Defaults to None (start from beginning).
end (int, optional): End index (exclusive). Defaults to None (go to end).
step (int, optional): Step size. Defaults to None (step=1).
Returns:
DataProto: A new DataProto containing the sliced data
Examples:
# Using the slice method directly
sliced_data = data_proto.slice(10, 20)
# Using enhanced indexing (returns DataProto)
sliced_data = data_proto[10:20]
sliced_data = data_proto[::2] # Every other element
# Using list indexing (returns DataProto)
indices = [1, 5, 10]
selected_data = data_proto[indices]
# Single index still returns DataProtoItem
single_item = data_proto[5]
"""
# Create a slice object
slice_obj = slice(start, end, step)
# Handle the batch data
if self.batch is not None:
# Use TensorDict's built-in slicing capabilities
sliced_batch = self.batch[slice_obj]
else:
sliced_batch = None
# Handle the non-tensor batch data
sliced_non_tensor = {}
for key, val in self.non_tensor_batch.items():
sliced_non_tensor[key] = val[slice_obj]
# Return a new DataProto object
return DataProto(batch=sliced_batch, non_tensor_batch=sliced_non_tensor, meta_info=self.meta_info)
def pop(self, batch_keys=None, non_tensor_batch_keys=None, meta_info_keys=None) -> "DataProto":
"""Pop a subset of the DataProto via `batch_keys` and `meta_info_keys`
@@ -923,21 +418,8 @@ class DataProto:
return DataProto.from_dict(tensors=tensors, non_tensors=non_tensors, meta_info=meta_info)
def rename(self, old_keys=None, new_keys=None) -> "DataProto":
"""Rename the keys in the batch.
Args:
old_keys (Optional[Union[str, List[str]]]): The old keys to be renamed.
new_keys (Optional[Union[str, List[str]]]): The new keys after renaming.
Returns:
DataProto: Returns the instance itself.
Raises:
TypeError: If old_keys or new_keys is not a string or a list.
ValueError: If the lengths of old_keys and new_keys are not the same.
Note:
This function only renames the keys in the batch.
"""
Note that this function only rename the key in the batch
"""
def validate_input(keys):
@@ -965,7 +447,6 @@ class DataProto:
def union(self, other: "DataProto") -> "DataProto":
"""Union with another DataProto. Union batch and meta_info separately.
Throw an error if
- there are conflict keys in batch and they are not equal
- the batch size of two data batch is not the same
- there are conflict keys in meta_info and they are not the same.
@@ -1063,7 +544,7 @@ class DataProto:
for batch in data:
batch_lst.append(batch.batch)
if batch_lst[0] is not None:
new_batch = tensor_or_tensordict_concat(batch_lst, axis=0)
new_batch = paddle.concat(batch_lst, axis=0)
else:
new_batch = None
@@ -1096,8 +577,7 @@ class DataProto:
if interleave:
# Interleave the data
repeated_tensors = {
key: paddle.repeat_interleave(tensor, repeats=repeat_times, axis=0)
for key, tensor in self.batch.items()
key: tensor.repeat_interleave(repeat_times, dim=0) for key, tensor in self.batch.items()
}
else:
# Stack the data
@@ -1108,7 +588,7 @@ class DataProto:
repeated_batch = TensorDict(
source=repeated_tensors,
batch_size=[self.batch.batch_size[0] * repeat_times],
batch_size=(self.batch.batch_size[0] * repeat_times,),
)
else:
repeated_batch = None
@@ -1125,36 +605,3 @@ class DataProto:
non_tensor_batch=repeated_non_tensor_batch,
meta_info=self.meta_info,
)
def split_batch_into_micro_batches(self, batch_size, pad_token_id=0) -> List["DataProto"]:
"""
Splits total_batch into micro-batches of size `batch_size`.
Args:
total_batch (dict): Dictionary containing full batched tensors.
batch_size (int): Micro batch size per device.
Returns:
list of dict: A list of micro-batches.
"""
micro_batches = []
num_micro_batches = self.batch["input_ids"].shape[0] // batch_size
if self.batch["input_ids"].shape[0] % batch_size != 0:
num_micro_batches += 1
if num_micro_batches <= 0:
logger.warning(
"The total batch size is smaller than the batch size, please consider using a smaller batch size or a larger global_batch_size."
)
num_micro_batches = 1
for i in range(num_micro_batches):
micro_batch = {}
for key, data in self.batch.items():
micro_batch[key] = data[i * batch_size : (i + 1) * batch_size]
# if os.getenv("PROCESS_PROMPT_AND_RESPONSE", "1").lower() in ["1", "t", "true", "yes", "y"]:
# micro_batch = process_prompt_and_response(micro_batch=micro_batch, pad_token_id=pad_token_id)
micro_batches.append(DataProto.from_single_dict(micro_batch))
return micro_batches

View File

@@ -47,6 +47,9 @@ from paddlenlp.transformers.deepseek_v2.modeling import (
yarn_get_mscale,
yarn_linear_ramp_mask,
)
from paddlenlp.transformers.model_outputs import (
BaseModelOutputWithPastAndCrossAttentions,
)
from paddlenlp.transformers.model_utils import (
dy2st_nocheck_guard_context,
register_base_model,
@@ -263,6 +266,7 @@ class DeepseekV2BlockInferenceModel(DeepseekV2PretrainedModel):
self.weight_block_size = config.weight_block_size
self.moe_quant_type = config.moe_quant_type
self.rope_theta = config.rope_theta
self.return_full_hidden_states = config.get("return_full_hidden_states", False)
self.use_weight_only = False
self.weightonly_group_size = -1
@@ -587,6 +591,7 @@ class DeepseekV2BlockInferenceModel(DeepseekV2PretrainedModel):
speculate_config = SpeculateConfig(
speculate_method=config.get("speculate_method", None),
speculate_max_draft_token_num=config.get("speculate_max_draft_token_num", 5),
return_full_hidden_states=config.get("return_full_hidden_states", False),
)
transformer_config = FusedMultiTransformerConfig(
@@ -617,9 +622,9 @@ class DeepseekV2BlockInferenceModel(DeepseekV2PretrainedModel):
rotary_emb=self.rotary_emb,
norm_type="rmsnorm",
rank_id=config.tensor_parallel_rank,
append_attn=config.append_attn,
moe_config=moe_config,
mla_config=mla_config,
append_attn=config.append_attn,
speculate_config=speculate_config,
)
@@ -1284,7 +1289,7 @@ class DeepseekV2BlockInferenceModel(DeepseekV2PretrainedModel):
inputs_embeds = inputs_embeds.reshape([-1, inputs_embeds.shape[2]])
with dy2st_nocheck_guard_context():
hidden_states, full_hidden_states = self.transformer_block(
hidden_states, _ = self.transformer_block(
input_ids=input_ids,
src=inputs_embeds,
cum_offsets=cum_offsets,
@@ -1296,7 +1301,13 @@ class DeepseekV2BlockInferenceModel(DeepseekV2PretrainedModel):
)
hidden_states = self.norm(hidden_states)
return hidden_states, full_hidden_states
return BaseModelOutputWithPastAndCrossAttentions(
last_hidden_state=hidden_states,
past_key_values=None,
hidden_states=None,
attentions=None,
cum_offsets=cum_offsets,
)
@register_base_model
@@ -1956,7 +1967,7 @@ class MTPDeepseekV2BlockInferenceModel(DeepseekV2BlockInferenceModel):
inputs_embeds = self.eh_proj(inputs_embeds)
with dy2st_nocheck_guard_context():
hidden_states, full_hidden_states = self.transformer_block(
hidden_states, _ = self.transformer_block(
input_ids=input_ids,
src=inputs_embeds,
cum_offsets=cum_offsets,
@@ -1969,7 +1980,12 @@ class MTPDeepseekV2BlockInferenceModel(DeepseekV2BlockInferenceModel):
)
hidden_states = self.norm(hidden_states)
return hidden_states, full_hidden_states
return BaseModelOutputWithPastAndCrossAttentions(
last_hidden_state=hidden_states,
past_key_values=None,
hidden_states=None,
attentions=None,
)
class DeepseekV2ForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, DeepseekV2PretrainedModel):
@@ -2196,7 +2212,7 @@ class DeepseekV2ForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, De
draft_tokens=None,
output_padding_offset=None,
):
hidden_states, full_hidden_states = self.deepseek_v2(
outputs = self.deepseek_v2(
input_ids,
inputs_embeds=inputs_embeds,
src_mask=src_mask,
@@ -2214,7 +2230,21 @@ class DeepseekV2ForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, De
draft_tokens=draft_tokens,
output_padding_offset=output_padding_offset,
)
if self.return_full_hidden_states:
from paddlenlp_ops import rebuild_padding_v2
full_hidden_states = outputs[0]
cum_offsets = outputs[1]
hidden_states = rebuild_padding_v2(
full_hidden_states,
cum_offsets,
seq_lens_decoder,
seq_lens_encoder,
output_padding_offset,
self.max_seq_len,
)
else:
hidden_states = outputs[0]
logits = self.lm_head(
hidden_states,
tensor_parallel_output=False,
@@ -2224,6 +2254,8 @@ class DeepseekV2ForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, De
else:
return logits
return logits
@paddle.no_grad()
def set_state_dict(self, state_dict):
if "lm_head.weight" in state_dict:
@@ -2331,7 +2363,7 @@ class MTPDeepseekV2ForCausalLMBlockInferenceModel(DeepseekV2ForCausalLMBlockInfe
output_padding_offset=None,
pre_hidden_states=None,
):
hidden_states, _ = self.mtp(
outputs = self.mtp(
input_ids,
src_mask=src_mask,
caches=caches,
@@ -2350,6 +2382,8 @@ class MTPDeepseekV2ForCausalLMBlockInferenceModel(DeepseekV2ForCausalLMBlockInfe
pre_hidden_states=pre_hidden_states,
)
hidden_states = outputs[0]
logits = self.lm_head(
hidden_states,
tensor_parallel_output=False,

View File

@@ -1481,8 +1481,7 @@ class FusedMultiTransformerBase(Layer):
self.pre_process(**kwargs)
kwargs["cum_offsets"] = cum_offsets
kv_cache_reuse = kwargs.get("kv_cache_reuse", None)
if caches is not None and kv_cache_reuse is not True:
if caches is not None:
assert len(caches) == len(self.linear_weights) or len(caches) == 2 * len(self.linear_weights)
assert self.num_layers == len(self.linear_weights)
@@ -1590,7 +1589,7 @@ class FusedMultiTransformerBase(Layer):
kwargs["input_ids"] = input_ids
out = self.post_process(**kwargs)
return out, kwargs["multi_block_output"]
return out, caches
class FusedMultiTransformerPostLayernorm(FusedMultiTransformerBase):
@@ -3173,17 +3172,10 @@ class FusedBlockMultiTransformer(FusedMultiTransformerBase):
k_dequant_scales = kwargs.get("k_dequant_scales", None)
v_dequant_scales = kwargs.get("v_dequant_scales", None)
kv_cache_reuse = kwargs.get("kv_cache_reuse", None)
if kv_cache_reuse:
k_cache_index = 0
v_cache_index = 1
else:
k_cache_index = 2 * i
v_cache_index = 2 * i + 1
fmha_out = paddle.incubate.nn.functional.block_multihead_attention(
qkv_out,
caches[k_cache_index],
caches[v_cache_index],
caches[2 * i],
caches[2 * i + 1],
kwargs.get("seq_lens_encoder", None),
kwargs.get("seq_lens_decoder", None),
kwargs.get("seq_lens_this_time", None),

View File

@@ -295,7 +295,7 @@ class GenerationInferenceModel(GenerationMixin):
model_inputs = self.prepare_inputs_for_generation(input_ids, cache_kvs, **args)
return self(**model_inputs)
def _post_process_(logits, top_p, temperature, step_idx_ori, model_kwargs):
def _post_process_(outputs, top_p, temperature, step_idx_ori, model_kwargs):
cache = model_kwargs.get("cache", None)
just_decoder = model_kwargs["seq_len_encoder"] == 0
if cache is None: # first decoder
@@ -314,6 +314,7 @@ class GenerationInferenceModel(GenerationMixin):
step_idx,
model_kwargs["stop_flags"],
)
logits = outputs[0] if isinstance(outputs, tuple) else outputs
logits = paddle.cast(logits, paddle.float32)
logits = logits_processors(model_kwargs["all_input_ids"], logits, decoding_step=step_idx_ori)
@@ -372,7 +373,7 @@ class GenerationInferenceModel(GenerationMixin):
outputs = _forward_(**model_kwargs)
# first decoder
next_tokens, model_kwargs = _post_process_(
outputs[0] if isinstance(outputs, tuple) else outputs,
outputs,
top_p,
temperature,
step_idx_ori,
@@ -388,9 +389,8 @@ class GenerationInferenceModel(GenerationMixin):
paddle.sum(paddle.cast(model_kwargs["stop_flags"], "int64")),
model_kwargs["stop_nums"],
):
outputs = _forward_(**model_kwargs)
next_tokens, model_kwargs = _post_process_(
outputs[0] if isinstance(outputs, tuple) else outputs,
_forward_(**model_kwargs),
top_p,
temperature,
step_idx_ori,
@@ -692,7 +692,7 @@ class GenerationBlockInferenceModel(GenerationMixin):
return self(**model_inputs)
def _post_process_(
logits,
outputs,
top_k,
top_p,
penalty_score,
@@ -702,7 +702,7 @@ class GenerationBlockInferenceModel(GenerationMixin):
model_kwargs,
):
step_idx = model_kwargs["step_idx"]
logits = paddle.cast(logits, paddle.float32)
logits = paddle.cast(outputs, paddle.float32)
from paddlenlp_ops import set_preids_token_penalty_multi_scores
@@ -777,7 +777,7 @@ class GenerationBlockInferenceModel(GenerationMixin):
outputs = _forward_(**model_kwargs) # [bs, 1, dim_embed]
# first decoder
next_tokens = _post_process_(
outputs[0] if isinstance(outputs, tuple) else outputs,
outputs,
top_k,
top_p,
penalty_score,
@@ -806,7 +806,7 @@ class GenerationBlockInferenceModel(GenerationMixin):
return self(**model_inputs)
def _post_process_(
logits,
outputs,
top_k,
top_p,
penalty_score,
@@ -816,7 +816,7 @@ class GenerationBlockInferenceModel(GenerationMixin):
model_kwargs,
):
step_idx = model_kwargs["step_idx"]
logits = paddle.cast(logits, paddle.float32)
logits = paddle.cast(outputs, paddle.float32)
from paddlenlp_ops import speculate_get_token_penalty_multi_scores
@@ -959,7 +959,7 @@ class GenerationBlockInferenceModel(GenerationMixin):
return self(**model_inputs)
def _post_process_(
logits,
outputs,
top_k,
top_p,
penalty_score,
@@ -968,7 +968,7 @@ class GenerationBlockInferenceModel(GenerationMixin):
temperature,
model_kwargs,
):
logits = paddle.cast(logits, paddle.float32)
logits = paddle.cast(outputs, paddle.float32)
probs = F.softmax(logits)
@@ -1191,7 +1191,7 @@ class GenerationAvxInferenceModel(GenerationMixin):
model_inputs = self.prepare_inputs_for_generation(input_ids, **args)
return self(**model_inputs)
def _post_process_(logits, top_p, temperature, step_idx_ori, model_kwargs):
def _post_process_(outputs, top_p, temperature, step_idx_ori, model_kwargs):
cache = model_kwargs.get("cache", None)
just_decoder = model_kwargs["seq_len_encoder"] == 0
if cache is None: # first decoder
@@ -1211,6 +1211,7 @@ class GenerationAvxInferenceModel(GenerationMixin):
step_idx,
model_kwargs["stop_flags"],
)
logits = outputs[0] if isinstance(outputs, tuple) else outputs
logits = paddle.cast(logits, paddle.float32)
logits = logits_processors(model_kwargs["all_input_ids"], logits, decoding_step=step_idx_ori)

View File

@@ -150,15 +150,13 @@ class LlamaAvxInferenceModel(LlamaPretrainedModel):
ln_scale_attrs = [paddle.ParamAttr(name="fusellama.{}.ln_scale".format(i)) for i in range(self.num_layers)]
qkv_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.qkv_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.qkv_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
out_proj_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.out_proj_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.out_proj_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
@@ -167,22 +165,19 @@ class LlamaAvxInferenceModel(LlamaPretrainedModel):
]
gate_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.gate_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.gate_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
up_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.up_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.up_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
ffn2_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.ffn2_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.ffn2_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
@@ -262,12 +257,19 @@ class LlamaAvxInferenceModel(LlamaPretrainedModel):
# merge batch and seq_len dimension.
inputs_embeds = inputs_embeds.reshape([batch * seq_len, hidden_dim])
output_hidden_states = (
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
)
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
if inputs_embeds is None:
inputs_embeds = self.embed_tokens(input_ids)
hidden_states = inputs_embeds
# decoder layers
all_hidden_states = () if output_hidden_states else None
with dy2st_nocheck_guard_context():
hidden_states = self.transformer_block(
input_ids=input_ids,
@@ -278,7 +280,11 @@ class LlamaAvxInferenceModel(LlamaPretrainedModel):
)
hidden_states = self.norm(hidden_states)
return hidden_states
if output_hidden_states:
all_hidden_states = all_hidden_states + (hidden_states,)
if not return_dict:
return tuple(v for v in [hidden_states, None, all_hidden_states, None] if v is not None)
@paddle.no_grad()
# avx
@@ -333,8 +339,7 @@ class LlamaAvxInferenceModel(LlamaPretrainedModel):
) # reshape(3, self.num_attention_heself.hidden_sizeads // self.config.tensor_parallel_degree, head_size, )
if "llama.layers.{}.mlp.gate_up_fused_proj.weight".format(idx) in state_dict.keys():
concated_ffn1_weight = np.concatenate(
split_fn(state_dict["llama.layers.{}.mlp.gate_up_fused_proj.weight".format(idx)]),
axis=-1,
split_fn(state_dict["llama.layers.{}.mlp.gate_up_fused_proj.weight".format(idx)]), axis=-1
)
else:
unfused_state_dict["mlp.gate_proj.weight"] = state_dict[
@@ -342,11 +347,7 @@ class LlamaAvxInferenceModel(LlamaPretrainedModel):
]
unfused_state_dict["mlp.up_proj.weight"] = state_dict["llama.layers.{}.mlp.up_proj.weight".format(idx)]
concated_ffn1_weight = np.concatenate(
[
unfused_state_dict["mlp.gate_proj.weight"],
unfused_state_dict["mlp.up_proj.weight"],
],
axis=-1,
[unfused_state_dict["mlp.gate_proj.weight"], unfused_state_dict["mlp.up_proj.weight"]], axis=-1
)
gate_up_list = split_fn(concated_ffn1_weight)
gate_weight_tensor = paddle.to_tensor(gate_up_list[0])
@@ -400,6 +401,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
self.epsilon = config.rms_norm_eps
self.max_position_embeddings = config.max_position_embeddings
self.quant_type = config.get("quant_type", "")
self.return_full_hidden_states = config.get("return_full_hidden_states", False)
self.rope_theta = config.rope_theta
self.use_neox = True
@@ -485,15 +487,13 @@ class LlamaInferenceModel(LlamaPretrainedModel):
ln_scale_attrs = [paddle.ParamAttr(name="fusellama.{}.ln_scale".format(i)) for i in range(self.num_layers)]
qkv_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.qkv_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.qkv_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
out_proj_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.out_proj_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.out_proj_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
@@ -503,30 +503,26 @@ class LlamaInferenceModel(LlamaPretrainedModel):
if "fp8" in self.quant_type:
ffn1_0_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.ffn1_0_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.ffn1_0_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
ffn1_1_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.ffn1_1_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.ffn1_1_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
else:
ffn1_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.ffn1_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.ffn1_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
ffn2_weight_attrs = [
paddle.ParamAttr(
name="fusellama.{}.ffn2_weight".format(i),
initializer=paddle.nn.initializer.Constant(value=0),
name="fusellama.{}.ffn2_weight".format(i), initializer=paddle.nn.initializer.Constant(value=0)
)
for i in range(self.num_layers)
]
@@ -621,6 +617,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
speculate_config = SpeculateConfig(
speculate_method=config.get("speculate_method", None),
speculate_max_draft_token_num=config.get("speculate_max_draft_token_num", 5),
return_full_hidden_states=config.get("return_full_hidden_states", False),
)
hpu_config = HpuConfig(
@@ -800,12 +797,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
from paddlenlp_ops import fused_get_rotary_embedding
new_rope = fused_get_rotary_embedding(
input_ids,
position_ids,
self.head_dim_shape_tensor,
position_offset,
self.rope_theta,
self.use_neox,
input_ids, position_ids, self.head_dim_shape_tensor, position_offset, self.rope_theta, self.use_neox
)
with dy2st_nocheck_guard_context():
@@ -821,7 +813,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
seq_lens=seq_lens,
rotary_embs=new_rope,
rotary_emb_dims=1,
time_step=(paddle.increment(paddle.shape(attention_mask)[-1], -1) if is_decoder else None),
time_step=paddle.increment(paddle.shape(attention_mask)[-1], -1) if is_decoder else None,
)
hidden_states = self.norm(hidden_states)
@@ -852,18 +844,14 @@ class LlamaInferenceModel(LlamaPretrainedModel):
weight_scale_json_path = resolve_file_path(self.quant_model_path, "weight_scales.json")
if self.config.tensor_parallel_degree > 1 and not self.config.single_card_ptq:
act_scale_json_path = resolve_file_path(
self.quant_model_path,
f"act_scales_{self.config.tensor_parallel_rank}.json",
self.quant_model_path, f"act_scales_{self.config.tensor_parallel_rank}.json"
)
weight_scale_json_path = resolve_file_path(
self.quant_model_path,
f"weight_scales_{self.config.tensor_parallel_rank}.json",
self.quant_model_path, f"weight_scales_{self.config.tensor_parallel_rank}.json"
)
act_scales = ActScalesLoader(
act_scale_json_path,
act_scale_map_dict,
num_of_layers=self.config.num_hidden_layers,
act_scale_json_path, act_scale_map_dict, num_of_layers=self.config.num_hidden_layers
)
weight_scales = PerTensorWeightScalesLoader(
@@ -894,17 +882,13 @@ class LlamaInferenceModel(LlamaPretrainedModel):
weight_scale_json_path = resolve_file_path(self.quant_model_path, "weight_scales.json")
if self.config.tensor_parallel_degree > 1 and not self.config.single_card_ptq:
act_scale_json_path = resolve_file_path(
self.quant_model_path,
f"act_scales_{self.config.tensor_parallel_rank}.json",
self.quant_model_path, f"act_scales_{self.config.tensor_parallel_rank}.json"
)
weight_scale_json_path = resolve_file_path(
self.quant_model_path,
f"weight_scales_{self.config.tensor_parallel_rank}.json",
self.quant_model_path, f"weight_scales_{self.config.tensor_parallel_rank}.json"
)
act_scale_loader = ActScalesLoader(
act_scale_json_path,
act_scale_map_dict,
num_of_layers=self.config.num_hidden_layers,
act_scale_json_path, act_scale_map_dict, num_of_layers=self.config.num_hidden_layers
)
weight_scales_loader = WeightScalesLoader(
weight_scale_json_path,
@@ -985,8 +969,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
cache_scale_json_path = resolve_file_path(self.quant_model_path, "cachekv_scales.json")
if self.config.tensor_parallel_degree > 1 and not self.config.single_card_ptq:
cache_scale_json_path = resolve_file_path(
self.quant_model_path,
f"cachekv_scales_{self.config.tensor_parallel_rank}.json",
self.quant_model_path, f"cachekv_scales_{self.config.tensor_parallel_rank}.json"
)
cache_scales_loader = CacheScaleLoader(
cache_scale_json_path,
@@ -1125,9 +1108,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
if self.use_weight_only:
qkv_weight_tensor = paddle.transpose(qkv_weight_tensor, perm=[1, 0])
qkv_quanted_weight_tensor, qkv_weight_scale_tensor = weight_quantize(
qkv_weight_tensor.cpu(),
algo=self.quant_algo,
group_size=self.weightonly_group_size,
qkv_weight_tensor.cpu(), algo=self.quant_algo, group_size=self.weightonly_group_size
)
self.transformer_block.qkv_weights[idx].set_value(qkv_quanted_weight_tensor.cuda())
self.transformer_block.qkv_weights_scale[idx].set_value(qkv_weight_scale_tensor.cuda())
@@ -1145,9 +1126,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
).cast(paddle.get_default_dtype())
if self.use_weight_only:
linear_quanted_weight_tensor, linear_weight_scale_tensor = weight_quantize(
linear_weight_tensor.cpu(),
algo=self.quant_algo,
group_size=self.weightonly_group_size,
linear_weight_tensor.cpu(), algo=self.quant_algo, group_size=self.weightonly_group_size
)
self.transformer_block.linear_weights[idx].set_value(linear_quanted_weight_tensor.cuda())
self.transformer_block.linear_weights_scale[idx].set_value(linear_weight_scale_tensor.cuda())
@@ -1194,8 +1173,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
if "llama.layers.{}.mlp.gate_up_fused_proj.weight".format(idx) in state_dict.keys():
concated_ffn1_weight = np.concatenate(
split_fn(state_dict["llama.layers.{}.mlp.gate_up_fused_proj.weight".format(idx)]),
axis=-1,
split_fn(state_dict["llama.layers.{}.mlp.gate_up_fused_proj.weight".format(idx)]), axis=-1
)
else:
unfused_state_dict["mlp.gate_proj.weight"] = state_dict[
@@ -1203,19 +1181,13 @@ class LlamaInferenceModel(LlamaPretrainedModel):
]
unfused_state_dict["mlp.up_proj.weight"] = state_dict["llama.layers.{}.mlp.up_proj.weight".format(idx)]
concated_ffn1_weight = np.concatenate(
[
unfused_state_dict["mlp.gate_proj.weight"],
unfused_state_dict["mlp.up_proj.weight"],
],
axis=-1,
[unfused_state_dict["mlp.gate_proj.weight"], unfused_state_dict["mlp.up_proj.weight"]], axis=-1
)
ffn1_weight_tensor = paddle.to_tensor(concated_ffn1_weight).cast(paddle.get_default_dtype())
if self.use_weight_only:
ffn1_quanted_weight_tensor, ffn1_weight_scale_tensor = weight_quantize(
ffn1_weight_tensor.cpu(),
algo=self.quant_algo,
group_size=self.weightonly_group_size,
ffn1_weight_tensor.cpu(), algo=self.quant_algo, group_size=self.weightonly_group_size
)
self.transformer_block.ffn1_weights[idx].set_value(ffn1_quanted_weight_tensor.cuda())
self.transformer_block.ffn1_weights_scale[idx].set_value(ffn1_weight_scale_tensor.cuda())
@@ -1242,10 +1214,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
)
else:
self.transformer_block.ffn1_weights[idx].set_value(
paddle.cast(
paddle.to_tensor(concated_ffn1_weight).transpose((1, 0)),
w_dtype,
)
paddle.cast(paddle.to_tensor(concated_ffn1_weight).transpose((1, 0)), w_dtype)
)
else:
self.transformer_block.ffn1_weights[idx].set_value(ffn1_weight_tensor)
@@ -1255,9 +1224,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
)
if self.use_weight_only:
ffn2_quanted_weight_tensor, ffn2_weight_scale_tensor = weight_quantize(
ffn2_weight_tensor.cpu(),
algo=self.quant_algo,
group_size=self.weightonly_group_size,
ffn2_weight_tensor.cpu(), algo=self.quant_algo, group_size=self.weightonly_group_size
)
self.transformer_block.ffn2_weights[idx].set_value(ffn2_quanted_weight_tensor.cuda())
self.transformer_block.ffn2_weights_scale[idx].set_value(ffn2_weight_scale_tensor.cuda())
@@ -1277,8 +1244,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
if paddle.is_compiled_with_rocm():
self.transformer_block.ffn2_weights[idx].set_value(
paddle.cast(
paddle.to_tensor(state_dict["llama.layers.{}.mlp.down_proj.weight".format(idx)]),
w_dtype,
paddle.to_tensor(state_dict["llama.layers.{}.mlp.down_proj.weight".format(idx)]), w_dtype
)
)
else:
@@ -1344,8 +1310,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
if self.use_fake_parameter:
if "llama.layers.{}.input_layernorm.bias".format(idx) not in state_dict:
state_dict["llama.layers.{}.input_layernorm.bias".format(idx)] = paddle.zeros(
shape=[self.hidden_size],
dtype=paddle.get_default_dtype(),
shape=[self.hidden_size], dtype=paddle.get_default_dtype()
)
state_dict["llama.layers.{}.post_attention_layernorm.bias".format(idx)] = paddle.zeros(
[self.hidden_size], dtype=paddle.get_default_dtype()
@@ -1363,12 +1328,10 @@ class LlamaInferenceModel(LlamaPretrainedModel):
dtype=paddle.get_default_dtype(),
)
unfused_state_dict["mlp.gate_proj.bias"] = paddle.zeros(
shape=[self.intermediate_size],
dtype=paddle.get_default_dtype(),
shape=[self.intermediate_size], dtype=paddle.get_default_dtype()
)
unfused_state_dict["mlp.up_proj.bias"] = paddle.zeros(
shape=[self.intermediate_size],
dtype=paddle.get_default_dtype(),
shape=[self.intermediate_size], dtype=paddle.get_default_dtype()
)
else:
unfused_state_dict["self_attn.q_proj.bias"] = state_dict[
@@ -1406,11 +1369,7 @@ class LlamaInferenceModel(LlamaPretrainedModel):
self.transformer_block.qkv_biases[idx].set_value(paddle.to_tensor(concated_qkv_biases))
concated_ffn1_bias = np.concatenate(
[
unfused_state_dict["mlp.gate_proj.bias"],
unfused_state_dict["mlp.up_proj.bias"],
],
axis=-1,
[unfused_state_dict["mlp.gate_proj.bias"], unfused_state_dict["mlp.up_proj.bias"]], axis=-1
)
self.transformer_block.ffn1_biases[idx].set_value(paddle.to_tensor(concated_ffn1_bias))
@@ -1458,23 +1417,10 @@ class LlamaBlockInferenceModel(LlamaInferenceModel):
token_num = paddle.sum(seq_lens_this_time)
from paddlenlp_ops import get_padding_offset_v2
cum_offsets_now = paddle.cast(cum_offsets_now, dtype="int32")
ids_remove_padding, cum_offsets, padding_offset, cu_seqlens_q, cu_seqlens_k = get_padding_offset_v2(
input_ids,
cum_offsets_now,
token_num,
seq_lens_this_time,
draft_tokens,
seq_lens_encoder,
)
return (
ids_remove_padding,
padding_offset,
cum_offsets,
cu_seqlens_q,
cu_seqlens_k,
input_ids, cum_offsets_now, token_num, seq_lens_this_time, draft_tokens, seq_lens_encoder
)
return ids_remove_padding, padding_offset, cum_offsets, cu_seqlens_q, cu_seqlens_k
def forward(
self,
@@ -1533,21 +1479,13 @@ class LlamaBlockInferenceModel(LlamaInferenceModel):
# whether speculative decoding or not
if draft_tokens is None:
(
ids_remove_padding,
padding_offset,
cum_offsets,
cu_seqlens_q,
cu_seqlens_k,
) = self.remove_padding(input_ids, seq_lens_this_time)
ids_remove_padding, padding_offset, cum_offsets, cu_seqlens_q, cu_seqlens_k = self.remove_padding(
input_ids, seq_lens_this_time
)
else:
(
ids_remove_padding,
padding_offset,
cum_offsets,
cu_seqlens_q,
cu_seqlens_k,
) = self.remove_padding(input_ids, seq_lens_this_time, draft_tokens, seq_lens_encoder)
ids_remove_padding, padding_offset, cum_offsets, cu_seqlens_q, cu_seqlens_k = self.remove_padding(
input_ids, seq_lens_this_time, draft_tokens, seq_lens_encoder
)
kwargs["cu_seqlens_q"] = cu_seqlens_q
kwargs["cu_seqlens_k"] = cu_seqlens_k
@@ -1558,7 +1496,7 @@ class LlamaBlockInferenceModel(LlamaInferenceModel):
inputs_embeds = self.embed_tokens(ids_remove_padding)
with dy2st_nocheck_guard_context():
hidden_states, full_hidden_states = self.transformer_block(
hidden_states, _ = self.transformer_block(
input_ids=input_ids,
src=inputs_embeds,
cum_offsets=cum_offsets,
@@ -1570,11 +1508,17 @@ class LlamaBlockInferenceModel(LlamaInferenceModel):
)
hidden_states = self.norm(hidden_states)
return hidden_states, full_hidden_states
return BaseModelOutputWithPastAndCrossAttentions(
last_hidden_state=hidden_states,
past_key_values=None,
hidden_states=None,
attentions=None,
cum_offsets=cum_offsets,
)
@register_base_model
class EagleForLlamaBlockInferenceModel(LlamaBlockInferenceModel):
class EagleForLlamaInferenceModel(LlamaBlockInferenceModel):
def __init__(self, config: LlamaConfig):
self.append_attn = config.append_attn
super().__init__(config)
@@ -1584,11 +1528,7 @@ class EagleForLlamaBlockInferenceModel(LlamaBlockInferenceModel):
if config.tensor_parallel_degree > 1:
self.fc = ColumnParallelLinear(
self.hidden_size * 2,
self.hidden_size,
has_bias=True,
gather_output=True,
fuse_matmul_bias=True,
self.hidden_size * 2, self.hidden_size, has_bias=True, gather_output=True, fuse_matmul_bias=True
)
else:
self.fc = nn.Linear(self.hidden_size * 2, self.hidden_size, bias_attr=True)
@@ -1748,11 +1688,7 @@ class LlamaForCausalLMInferenceModel(GenerationInferenceModel, LlamaPretrainedMo
super().__init__(config)
self.llama = LlamaInferenceModel(config)
if config.tie_word_embeddings:
self.lm_head = LlamaLMHead(
config,
embedding_weights=self.llama.embed_tokens.weight,
transpose_y=True,
)
self.lm_head = LlamaLMHead(config, embedding_weights=self.llama.embed_tokens.weight, transpose_y=True)
self.tie_weights()
else:
self.lm_head = LlamaLMHead(config)
@@ -1950,11 +1886,7 @@ class LlamaForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, LlamaPr
self.llama = LlamaBlockInferenceModel(config)
if config.tie_word_embeddings:
self.lm_head = LlamaLMHead(
config,
embedding_weights=self.llama.embed_tokens.weight,
transpose_y=True,
)
self.lm_head = LlamaLMHead(config, embedding_weights=self.llama.embed_tokens.weight, transpose_y=True)
self.tie_weights()
else:
self.lm_head = LlamaLMHead(config)
@@ -2146,9 +2078,8 @@ class LlamaForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, LlamaPr
excess_blocks=None,
draft_tokens=None,
output_padding_offset=None,
**kwargs,
):
hidden_states, full_hidden_states = self.llama(
outputs = self.llama(
input_ids,
src_mask=src_mask,
caches=caches,
@@ -2165,9 +2096,24 @@ class LlamaForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, LlamaPr
excess_blocks=excess_blocks,
draft_tokens=draft_tokens,
output_padding_offset=output_padding_offset,
**kwargs,
)
# hidden_states = outputs[0]
if self.return_full_hidden_states:
from paddlenlp_ops import rebuild_padding_v2
# full_hidden_states = outputs[1]
full_hidden_states = outputs[0]
cum_offsets = outputs[1]
hidden_states = rebuild_padding_v2(
full_hidden_states,
cum_offsets,
seq_lens_decoder,
seq_lens_encoder,
output_padding_offset,
self.max_seq_len,
)
else:
hidden_states = outputs[0]
logits = self.lm_head(
hidden_states,
tensor_parallel_output=False,
@@ -2193,13 +2139,9 @@ class EagleLlamaForCausalLMBlockInferenceModel(LlamaForCausalLMBlockInferenceMod
self.verify_window = config.get("speculate_verify_window", 2)
self.max_seq_len = config.max_seq_len
self.eagle = EagleForLlamaBlockInferenceModel(config)
self.eagle = EagleForLlamaInferenceModel(config)
if config.tie_word_embeddings:
self.lm_head = LlamaLMHead(
config,
embedding_weights=self.llama.embed_tokens.weight,
transpose_y=True,
)
self.lm_head = LlamaLMHead(config, embedding_weights=self.llama.embed_tokens.weight, transpose_y=True)
self.tie_weights()
else:
self.lm_head = LlamaLMHead(config)

View File

@@ -1,15 +0,0 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .modeling import *

File diff suppressed because it is too large Load Diff

View File

@@ -54,6 +54,7 @@ from paddlenlp.transformers import Qwen2Config, Qwen2PretrainedModel
from paddlenlp.transformers.conversion_utils import split_param_func
from paddlenlp.transformers.model_outputs import ( # CausalLMOutputWithCrossAttentions,
BaseModelOutputWithPast,
BaseModelOutputWithPastAndCrossAttentions,
CausalLMOutputWithPast,
)
from paddlenlp.transformers.model_utils import (
@@ -1324,7 +1325,7 @@ class Qwen2BlockInferenceModel(Qwen2InferenceModel):
inputs_embeds = inputs_embeds.reshape([-1, inputs_embeds.shape[2]])
with dy2st_nocheck_guard_context():
hidden_states, full_hidden_states = self.transformer_block(
hidden_states, _ = self.transformer_block(
input_ids=input_ids,
src=inputs_embeds,
cum_offsets=cum_offsets,
@@ -1336,7 +1337,12 @@ class Qwen2BlockInferenceModel(Qwen2InferenceModel):
)
hidden_states = self.norm(hidden_states)
return hidden_states, full_hidden_states
return BaseModelOutputWithPastAndCrossAttentions(
last_hidden_state=hidden_states,
past_key_values=None,
hidden_states=None,
attentions=None,
)
class Qwen2ForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, Qwen2PretrainedModel):
@@ -1353,7 +1359,6 @@ class Qwen2ForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, Qwen2Pr
self.max_candidate_len = config.get("speculate_max_candidate_len", 5)
self.verify_window = config.get("speculate_verify_window", 2)
self.max_seq_len = config.max_seq_len
self.return_full_hidden_states = config.get("return_full_hidden_states", False)
self.qwen2 = Qwen2BlockInferenceModel(config, base_model_prefix)
if config.tie_word_embeddings:
@@ -1550,7 +1555,7 @@ class Qwen2ForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, Qwen2Pr
draft_tokens=None,
output_padding_offset=None,
):
hidden_states, full_hidden_states = self.qwen2(
outputs = self.qwen2(
input_ids,
inputs_embeds=inputs_embeds,
src_mask=src_mask,
@@ -1570,15 +1575,13 @@ class Qwen2ForCausalLMBlockInferenceModel(GenerationBlockInferenceModel, Qwen2Pr
output_padding_offset=output_padding_offset,
)
hidden_states = outputs[0]
logits = self.lm_head(
hidden_states,
tensor_parallel_output=False,
)
if self.return_full_hidden_states:
return logits, full_hidden_states
else:
return logits
return logits
@paddle.no_grad()
def set_state_dict(self, state_dict):

View File

@@ -69,7 +69,6 @@ class MergeConfig:
rescale: bool = field(default=True, metadata={"help": "Rescale the weights after sparsifying."})
reserve_p: float = field(default=0.7, metadata={"help": "Random reserve probability for the sparsify model."})
epsilon: float = field(default=0.14, metadata={"help": "Epsilon value for magprune."})
remove_keys: List[str] = field(default=None, metadata={"help": "Keys to be removed from the model."})
def __post_init__(self):
self.config_check()
@@ -117,9 +116,6 @@ class MergeConfig:
f"Error: reserve_p +- epsilon/2 must be in the range (0, 1). reserve_p + epsilon/2 = {self.reserve_p + self.epsilon / 2 }, reserve_p - epsilon/2 = {self.reserve_p - self.epsilon / 2 }"
)
if self.remove_keys is None:
self.remove_keys = []
@property
def __dict__(self):
return asdict(self)

View File

@@ -15,7 +15,7 @@ import json
import math
import os
import shutil
from multiprocessing import Process, Queue
from multiprocessing import Process
import numpy as np
import paddle
@@ -54,11 +54,6 @@ SPARSIFY_MERGE_MAPPING = {
}
def run_merge_in_process(merger_obj, base_index, shard_file, lora_config, file_type_list, key_list, q):
remove_nbytes = merger_obj.shard_lora_merge(base_index, shard_file, lora_config, file_type_list, key_list)
q.put(remove_nbytes)
class MergeModel:
def __init__(self, merge_config):
self.reset_merge_model(merge_config=merge_config)
@@ -87,7 +82,6 @@ class MergeModel:
# init merge method
sparsify_method = SparsifyMethod(self.merge_config)
self.merge_method = MergeMethod(merge_config, sparsify_method)
self.remove_keys = getattr(merge_config, "remove_keys", [])
def merge_model(self):
if self.merge_config.lora_model_path is not None:
@@ -109,10 +103,7 @@ class MergeModel:
else:
src_path = self.merge_config.model_path_list[0]
for file in self.merge_config.copy_file_list:
if file == "config.json":
src_file = os.path.join(self.merge_config.lora_model_path, file)
else:
src_file = os.path.join(src_path, file)
src_file = os.path.join(src_path, file)
dst_file = os.path.join(self.merge_config.output_path, file)
if os.path.isfile(src_file):
shutil.copy2(src_file, dst_file)
@@ -244,7 +235,7 @@ class MergeModel:
logger.info(f"Model index file saved in {save_index_file}.")
self.merge_config.save_pretrained(self.merge_config.output_path)
def get_model_state_dict(self, model_path, file_type, key_list=None, file=None, need_remove=False):
def get_model_state_dict(self, model_path, file_type, key_list=None, file=None):
if file_type == "safetensors":
state_dict = {}
with open(os.path.join(model_path, self.safe_index_name()), "r", encoding="utf-8") as f:
@@ -293,14 +284,6 @@ class MergeModel:
state_dict[k] = f.get_tensor(k)
else:
raise ValueError(f"Unsupported file_type: {file_type}")
if need_remove:
remove_nbytes = 0
for k in list(state_dict.keys()):
if k in self.remove_keys:
tensor = state_dict.pop(k)
remove_nbytes += int(np.prod(tensor.shape) * self.numpy_dtype_map[str(tensor.dtype)])
return state_dict, remove_nbytes
return state_dict
def get_safetensor_index(self, model_path, file_type):
@@ -574,12 +557,8 @@ class MergeModel:
merge_state_dict = {}
lora_state_dict = self.get_model_state_dict(self.merge_config.lora_model_path, file_type_list[0])
logger.info("Load LoRA weight successfully.")
base_state_dict, remove_nbytes = self.get_model_state_dict(
self.merge_config.base_model_path,
file_type_list[1],
key_list=key_list,
file=file,
need_remove=True,
base_state_dict = self.get_model_state_dict(
self.merge_config.base_model_path, file_type_list[1], key_list=key_list, file=file
)
logger.info("Load model weight successfully.")
if not lora_config.rslora:
@@ -621,7 +600,6 @@ class MergeModel:
metadata={"format": "np"},
)
logger.info(f"Model weights saved in {save_file_name}.")
return remove_nbytes
def merge_safetensor_lora_model(self, file_type_list):
# Load index
@@ -635,66 +613,45 @@ class MergeModel:
index["weight_map"] = {}
# LoRA Merge
old_key_list = list(base_index["weight_map"].keys())
key_list = [k for k in old_key_list if k not in self.remove_keys]
total_remove_nbytes = 0
key_list = list(base_index["weight_map"].keys())
if not self.is_cpu:
rank = dist.get_rank()
file_list = sorted(list(set(base_index["weight_map"].values())))
if file_type_list[-1] == "safetensors" and len(file_list) >= dist.get_world_size():
positions = divide_positions(len(file_list), dist.get_world_size())
logger.info(f"Merging file list: {file_list[positions[rank] : positions[rank + 1]]}")
for shard_file in file_list[positions[rank] : positions[rank + 1]]:
logger.info(f"Start merging tensor in {shard_file}")
remove_nbytes = self.shard_lora_merge(
base_index, shard_file, lora_config, file_type_list, file=shard_file
)
total_remove_nbytes += remove_nbytes
self.shard_lora_merge(base_index, shard_file, lora_config, file_type_list, file=shard_file)
index["weight_map"] = base_index["weight_map"]
for k in list(index["weight_map"].keys()):
if k in self.remove_keys:
del index["weight_map"][k]
else:
divided_key_list = divide_lora_key_list(key_list, dist.get_world_size(), lora_config)
local_keys = divided_key_list[rank]
shard_file = (
f"{self.merge_config.merge_prefix}-{rank+1:05d}-of-{dist.get_world_size():05d}.safetensors"
)
remove_nbytes = self.shard_lora_merge(
base_index, shard_file, lora_config, file_type_list, key_list=local_keys
)
total_remove_nbytes += remove_nbytes
self.shard_lora_merge(base_index, shard_file, lora_config, file_type_list, key_list=local_keys)
for i in range(len(divided_key_list)):
shard_file = (
f"{self.merge_config.merge_prefix}-{i+1:05d}-of-{dist.get_world_size():05d}.safetensors"
)
for k in divided_key_list[i]:
index["weight_map"][k] = shard_file
# update total_remove_nbytes in main process
total_remove_nbytes_tensor = paddle.to_tensor(total_remove_nbytes)
if dist.get_world_size() > 1:
dist.reduce(total_remove_nbytes_tensor, dst=0) # op=ReduceOp.SUM
total_remove_nbytes = total_remove_nbytes_tensor.item()
else:
divided_key_list = divide_lora_key_list(key_list, self.merge_config.n_process, lora_config)
threads = []
queue = Queue()
for i in range(len(divided_key_list)):
shard_file = (
f"{self.merge_config.merge_prefix}-{i+1:05d}-of-{self.merge_config.n_process:05d}.safetensors"
)
t = Process(
target=run_merge_in_process,
target=self.shard_lora_merge,
args=(
self,
base_index, # base index
shard_file, # shard_file name
lora_config,
file_type_list,
divided_key_list[i], # key_list
queue,
),
)
threads.append(t)
@@ -706,12 +663,8 @@ class MergeModel:
for t in threads:
t.join()
while not queue.empty():
total_remove_nbytes += queue.get()
# Save safe index file
if paddle.distributed.get_rank() == 0:
index["metadata"]["total_size"] -= total_remove_nbytes
save_index_file = os.path.join(self.merge_config.output_path, self.safe_index_name())
with open(save_index_file, "w", encoding="utf-8") as f:
f.write(json.dumps(index, indent=2) + "\n")
@@ -722,11 +675,7 @@ class MergeModel:
# Load & check state dict
lora_state_dict = self.get_model_state_dict(self.merge_config.lora_model_path, file_type_list[0])
logger.info("Load LoRA weight successfully.")
base_state_dict, remove_nbytes = self.get_model_state_dict(
self.merge_config.base_model_path,
file_type_list[1],
need_remove=True,
)
base_state_dict = self.get_model_state_dict(self.merge_config.base_model_path, file_type_list[1])
logger.info("Load model weight successfully.")
for key in lora_state_dict.keys():
if "lora_A" in key:

View File

@@ -12,10 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from .dislora import DisLoRAConfig, DisLoRALinear, DisLoRAModel
from .lokr import LoKrConfig, LoKrModel
from .lora import LoRAAutoConfig, LoRAAutoModel, LoRAConfig, LoRAModel
from .prefix import PrefixConfig, PrefixModelForCausalLM
from .reft import ReFTModel
from .tare import TAREModel
from .vera import VeRAConfig, VeRAModel

View File

@@ -1,19 +0,0 @@
# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .dislora_config import DisLoRAConfig
from .dislora_layer import DisLoRALinear
from .dislora_model import DisLoRAModel
__all__ = ["DisLoRAConfig", "DisLoRAModel", "DisLoRALinear"]

View File

@@ -1,160 +0,0 @@
# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import os
from dataclasses import asdict, dataclass, field
from typing import List, Optional, Union
from ...utils.env import DISLORA_CONFIG_NAME
@dataclass
class DisLoRAConfig:
"""
This is the configuration class to store the configuration of a [`DisLoRAModel`].
Args:
target_modules (`Union[List[str],str]`): The names of the modules to apply DisLoRA to.
trainable_modules (`List[str]`): The names of the modules to train when applying DisLoRA.
dislora_alpha (`float`): The alpha parameter for DisLoRA scaling.
merge_weights (`bool`):
Whether to merge the weights of the DisLoRA layers with the base transfoisrmer model in `eval` mode.
"""
base_model_name_or_path: Optional[str] = field(
default=None, metadata={"help": "The name of the base model to use."}
)
r: int = field(default=8, metadata={"help": "DisLoRA attention dimension"})
target_modules: Optional[Union[List[str], str]] = field(
default=None,
metadata={
"help": "List of module names or regex expression of the module names to replace with DisLoRA."
"For example, ['q', 'v'] or '.*decoder.*(SelfAttention|EncDecAttention).*(q|v)$' "
},
)
trainable_modules: Optional[List[str]] = field(
default=None,
metadata={
"help": "List of module names or regex expression of the module names to train when applying with DisLoRA."
"For example, ['q', 'v'] or '.*decoder.*(SelfAttention|EncDecAttention).*(q|v)$' "
},
)
dislora_alpha: int = field(default=12, metadata={"help": "DisLoRA alpha"})
dislora_dropout: float = field(default=0.0, metadata={"help": "DisLoRA dropout"})
merge_weights: bool = field(
default=False, metadata={"help": "Merge weights of the original model and the DisLoRA model"}
)
trainable_bias: Optional[str] = field(
default=None, metadata={"help": "Define trainable bias parameters for the DisLoRA model."}
)
tensor_parallel_degree: int = field(default=-1, metadata={"help": "1 for not use tensor parallel"})
dtype: Optional[str] = field(default=None, metadata={"help": "The data type of tensor"})
dash_flag: int = field( # characteristic
default=50,
metadata={"help": "The number of preheating steps before introducing additional low-rank updates"},
)
s_tsd: int = field( # characteristic
default=8,
metadata={"help": "The number of top-k singular vectors dynamically selected after preheating"},
)
ortho_lambda: float = field( # characteristic
default=1,
metadata={"help": "The weight of orthogonal regularization loss"},
)
prefer_small_sigma: bool = field(
default=True,
metadata={"help": "Whether to prioritize the smallest singular value in the top-k selection process"},
)
def __post_init__(self):
if self.target_modules is None:
raise ValueError("The target_modules must be specified as a string or a list of strings.")
if self.r <= 0:
raise ValueError("The rank r of LoRA must be greater than 0.")
if self.dislora_alpha <= 0:
raise ValueError("dislora_alpha must be greater than 0")
if self.r < self.s_tsd:
raise ValueError("The rank r of LoRA must be larger than the number of top-k singular values.")
@property
def scaling(self):
return self.dislora_alpha / self.r
@property
def __dict__(self):
return asdict(self)
def to_dict(self):
return self.__dict__
def save_pretrained(self, save_directory):
r"""
This method saves the configuration of your adapter model in a directory.
Args:
save_directory (`str`):
The directory where the configuration will be saved.
"""
if os.path.isfile(save_directory):
raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file")
os.makedirs(save_directory, exist_ok=True)
output_dict = self.__dict__
output_dict["scaling"] = self.scaling
output_path = os.path.join(save_directory, DISLORA_CONFIG_NAME)
# save it
with open(output_path, "w") as writer:
writer.write(json.dumps(output_dict, indent=2, sort_keys=True))
@classmethod
def from_pretrained(cls, pretrained_model_name_or_path, **kwargs):
r"""
This method loads the configuration of your adapter model from a directory.
Args:
pretrained_model_name_or_path (`str`):
The directory or the hub-id where the configuration is saved.
**kwargs:
Additional keyword arguments passed along to the child class initialization.
"""
if os.path.isfile(os.path.join(pretrained_model_name_or_path, DISLORA_CONFIG_NAME)):
config_file = os.path.join(pretrained_model_name_or_path, DISLORA_CONFIG_NAME)
else:
raise ValueError(f"Can't find dislora_config.json at '{pretrained_model_name_or_path}'")
loaded_attributes = cls.from_json_file(config_file)
loaded_attributes.pop("scaling", None)
merged_kwargs = {**loaded_attributes, **kwargs}
config = cls(**merged_kwargs)
return config
@classmethod
def from_json_file(cls, path_json_file):
r"""
Loads a configuration file from a json file.
Args:
path_json_file (`str`):
The path to the json file.
"""
with open(path_json_file, "r") as file:
json_object = json.load(file)
return json_object

View File

@@ -1,312 +0,0 @@
# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
import warnings
from typing import Union
import paddle
import paddle.nn as nn
class DisLoRALinear(nn.Linear):
"""
Paddle implementation of Direct Low-Rank Adaptation (DisLoRA) layer.
DisLoRA decomposes W into backbone (W_prin) and task-specific (W_res) subspaces via SVD,
further identifying task-specific directions (W_TSD) for fine tuning.
"""
def __init__(
self,
in_features: int,
out_features: int,
r: int = 8,
dislora_alpha: int = 8,
dislora_dropout: float = 0.0,
dash_flag: int = 50,
s_tsd: int = 8,
prefer_small_sigma: bool = True,
merge_weights: bool = False,
init_lora_weights: Union[bool, str] = True,
**kwargs
):
if r <= 0:
raise ValueError(f"`r` must be a positive integer, got {r}")
if s_tsd <= 0:
raise ValueError(f"`s_tsd` must be a positive integer, got {s_tsd}")
nn.Linear.__init__(self, in_features, out_features, **kwargs)
original_weight = self.weight.clone()
original_bias = self.bias.clone() if self.bias is not None else None
self.base_dtype = original_weight.dtype
delattr(self, "weight")
if hasattr(self, "bias") and self.bias is not None:
delattr(self, "bias")
self.weight = self.create_parameter(
shape=[in_features, out_features],
default_initializer=nn.initializer.Assign(original_weight),
dtype=self.base_dtype,
attr=paddle.ParamAttr(trainable=False),
)
if original_bias is not None:
self.bias = self.create_parameter(
shape=[out_features],
default_initializer=nn.initializer.Assign(original_bias),
dtype=self.base_dtype,
attr=paddle.ParamAttr(trainable=True),
)
else:
self.bias = None
self.r = r
self.dislora_alpha = dislora_alpha
self.scaling = dislora_alpha / r
self.dislora_dropout = nn.Dropout(p=dislora_dropout) if dislora_dropout > 0.0 else nn.Identity()
self.dash_flag = dash_flag
self.s_tsd = s_tsd
self.prefer_small_sigma = prefer_small_sigma
self.merge_weights = merge_weights
self.init_lora_weights = init_lora_weights
self._disable_adapters = False
self.merged = False
self.register_buffer("step", paddle.to_tensor(0, dtype="int64"))
self.U = None
self.S = None
self.Vh = None
self.Direc_Ur = nn.Linear(r, out_features, bias_attr=False)
self.Direc_Sr = self.create_parameter(
shape=[r], default_initializer=nn.initializer.Constant(0.0), dtype=self.base_dtype
)
self.Direc_Vhr = nn.Linear(in_features, r, bias_attr=False)
self.Direc_Ur.weight.stop_gradient = False
self.Direc_Sr.stop_gradient = False
self.Direc_Vhr.weight.stop_gradient = False
self.Direc_Utsd = nn.Linear(s_tsd, out_features, bias_attr=False)
self.Direc_Stsd = self.create_parameter(
shape=[s_tsd], default_initializer=nn.initializer.Constant(0.0), dtype=self.base_dtype
)
self.Direc_Vhtsd = nn.Linear(in_features, s_tsd, bias_attr=False)
self.Direc_Utsd.weight.stop_gradient = True
self.Direc_Vhtsd.weight.stop_gradient = True
self._align_dtypes()
if init_lora_weights:
self._init_lora_weights()
def _align_dtypes(self):
"""Ensure that the data types of all parameters are consistent with those of the base layer."""
target_dtype = self.base_dtype
if self.Direc_Ur.weight.dtype != target_dtype:
self.Direc_Ur.weight.set_value(self.Direc_Ur.weight.astype(target_dtype))
if self.Direc_Vhr.weight.dtype != target_dtype:
self.Direc_Vhr.weight.set_value(self.Direc_Vhr.weight.astype(target_dtype))
if self.Direc_Utsd.weight.dtype != target_dtype:
self.Direc_Utsd.weight.set_value(self.Direc_Utsd.weight.astype(target_dtype))
if self.Direc_Vhtsd.weight.dtype != target_dtype:
self.Direc_Vhtsd.weight.set_value(self.Direc_Vhtsd.weight.astype(target_dtype))
if self.Direc_Sr.dtype != target_dtype:
self.Direc_Sr.set_value(self.Direc_Sr.astype(target_dtype))
if self.Direc_Stsd.dtype != target_dtype:
self.Direc_Stsd.set_value(self.Direc_Stsd.astype(target_dtype))
def _init_lora_weights(self):
"""
Initialize LoRA weights using SVD
Decompose the original weight W into W_prin (frozen backbone) + W_res (trainable residual)
Note: The shape of the Linear weight in PaddlePaddle is [in_features, out_features]
"""
weight_float32 = self.weight.astype("float32")
weight_transposed = weight_float32.T
U, S, Vh = paddle.linalg.svd(weight_transposed, full_matrices=False)
self.U = U.astype(self.base_dtype)
self.S = S.astype(self.base_dtype)
self.Vh = Vh.astype(self.base_dtype)
if self.prefer_small_sigma:
_, indices = paddle.topk(S, self.r, largest=False)
else:
_, indices = paddle.topk(S, self.r, largest=True)
self.Direc_Ur.weight.set_value(U[:, indices].T.astype(self.base_dtype))
self.Direc_Sr.set_value(S[indices].astype(self.base_dtype))
self.Direc_Vhr.weight.set_value(Vh[indices, :].T.astype(self.base_dtype))
self.Direc_Ur.weight.stop_gradient = False
self.Direc_Sr.stop_gradient = False
self.Direc_Vhr.weight.stop_gradient = False
self.Direc_Stsd.stop_gradient = False
S_diag = paddle.diag(self.Direc_Sr) # [r, r]
W_res_T = self.Direc_Ur.weight.T @ S_diag @ self.Direc_Vhr.weight.T # [out_features, in_features]
W_res = W_res_T.T * self.scaling # [in_features, out_features]
if W_res.shape != self.weight.shape:
raise ValueError(f"Expected W_res shape {self.weight.shape}, but got {W_res.shape}.")
self.weight.set_value(self.weight - W_res.astype(self.base_dtype))
self.weight.stop_gradient = True
def forward(self, x: paddle.Tensor) -> paddle.Tensor:
"""
Forward propagation: W_prin @ x + W_res @ x + W_TSD @ x
- W_prin is calculated through the base_layer
- W_res is calculated through the trainable LoRA structure
- W_TSD is calculated through the frozen dynamic vector (after warmup)
"""
if self._disable_adapters:
if self.merged:
self.unmerge()
return super().forward(x)
if self.merged:
return super().forward(x)
result = super().forward(x)
temp = self.dislora_dropout(x)
temp = self.Direc_Vhr(temp)
temp = temp * self.Direc_Sr
temp = self.Direc_Ur(temp)
result += temp * self.scaling
if self.step < self.dash_flag:
pass
elif self.step == self.dash_flag:
self._initialize_dynamic_vectors()
else:
temp = self.dislora_dropout(x)
temp = self.Direc_Vhtsd(temp)
temp = temp * self.Direc_Stsd
temp = self.Direc_Utsd(temp)
result += temp * self.scaling
if self.training:
with paddle.no_grad():
self.step += 1
return result
def _initialize_dynamic_vectors(self):
"""
After the warm-up steps, initialize the dynamic singular vector W_TSD.
Based on the current change of W_res, select the most important s_tsd directions.
"""
with paddle.no_grad():
S_diag = paddle.diag(self.Direc_Sr) # [r, r]
deltaW_T = self.Direc_Ur.weight.T @ S_diag @ self.Direc_Vhr.weight.T # [out_features, in_features]
delta_sigma = paddle.diag(self.U.T @ deltaW_T @ self.Vh.T)
top_indices = self.calculate_change_rate(
self.S, delta_sigma, self.s_tsd, largest=not self.prefer_small_sigma
)
self.Direc_Utsd.weight.set_value(self.U[:, top_indices].T.astype(self.base_dtype))
self.Direc_Stsd.set_value(self.S[top_indices].astype(self.base_dtype))
self.Direc_Vhtsd.weight.set_value(self.Vh[top_indices, :].T.astype(self.base_dtype))
self.Direc_Utsd.weight.stop_gradient = True
self.Direc_Vhtsd.weight.stop_gradient = True
def calculate_change_rate(self, a: paddle.Tensor, b: paddle.Tensor, s: int, largest: bool = True) -> paddle.Tensor:
"""
Calculate the rate of change of singular values and
select the top-s index change_rate = |b| / (|a| + eps)
"""
with paddle.no_grad():
change_rate = paddle.abs(b) / (paddle.abs(a) + 1e-8)
_, top_s_indices = paddle.topk(change_rate, s, largest=largest)
return top_s_indices
def merge(self):
"""
Merge the trainable W_res into the base weights.
After merging: base_layer.weight = W_prin + W_res
Note: W_TSD remains frozen and does not participate in the merge.
"""
if self.merged:
warnings.warn("Already merged. Nothing to do.")
return
if self.r > 0:
delta_weight = self.get_delta_weight()
orig_weights = self.weight.clone()
orig_weights += delta_weight
self.weight.set_value(orig_weights)
self.merged = True
def unmerge(self):
"""
Remove the merging of W_res from the base weights.
After the merging is removed: base_layer.weight = W_prin
"""
if not self.merged:
warnings.warn("Already unmerged. Nothing to do.")
return
if self.r > 0:
delta_weight = self.get_delta_weight()
self.weight.set_value(self.weight - delta_weight)
self.merged = False
def get_delta_weight(self) -> paddle.Tensor:
"""
Calculate the trainable LoRA incremental weights
It consists of two parts:
1. W_res = Ur @ diag(Sr) @ Vhr * scaling (transposed)
2. W_tsd = Utsd @ diag(Stsd) @ Vhtsd * scaling (transposed)
Return the incremental weights with the shape of [in_features, out_features]
"""
S_diag_r = paddle.diag(self.Direc_Sr) # [r, r]
delta_weight_T = self.Direc_Ur.weight.T @ S_diag_r @ self.Direc_Vhr.weight.T # [out_features, in_features]
delta_weight = delta_weight_T.T * self.scaling # [in_features, out_features]
if not paddle.all(self.Direc_Stsd == 0.0):
S_diag_tsd = paddle.diag(self.Direc_Stsd) # [s_tsd, s_tsd]
delta_weight_tsd_T = (
self.Direc_Utsd.weight.T @ S_diag_tsd @ self.Direc_Vhtsd.weight.T
) # [out_features, in_features]
delta_weight += delta_weight_tsd_T.T * self.scaling # [in_features, out_features]
return delta_weight.astype(self.base_dtype)
def enable_adapters(self):
"""Enable the adapter"""
self._disable_adapters = False
def disable_adapters(self):
"""Disable adapter"""
self._disable_adapters = True
def __repr__(self) -> str:
rep = super().__repr__()
return rep

View File

@@ -1,446 +0,0 @@
# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import os
import re
from collections import OrderedDict
from typing import Dict, Union
import numpy as np
import paddle
import paddle.nn as nn
from paddle.distributed.fleet.meta_parallel import PipelineLayer
from paddlenlp.transformers import AutoConfig, PretrainedModel
from paddlenlp.transformers.model_utils import _add_variant, dtype_guard
from paddlenlp.utils.log import logger
from ...utils.env import DISLORA_WEIGHTS_NAME
from .dislora_config import DisLoRAConfig
def get_dislora_layers():
from .dislora_layer import DisLoRALinear
return {
"DisLoRALinear": DisLoRALinear,
}
dislora_layers = get_dislora_layers()
DisLoRALinear = dislora_layers["DisLoRALinear"]
AVAILABLE_LAYERS = [
DisLoRALinear,
]
class DisLoRAModel(nn.Layer):
restore_layer_map: Dict[nn.Layer, nn.Layer] = {
DisLoRALinear: nn.Linear,
}
def __init__(self, model, dislora_config: DisLoRAConfig) -> None:
super().__init__()
self.model_config = AutoConfig.from_pretrained(dislora_config.base_model_name_or_path)
self.quantized = False
self.dislora_config = dislora_config
self.dislora_split_mapping = {}
if self.dislora_config.dtype is None:
self.dislora_config.dtype = paddle.get_default_dtype()
with dtype_guard(self.dislora_config.dtype):
self.model = self.get_dislora_model(model, dislora_config)
self.is_pipelinemodel = False
if issubclass(type(self.model), PipelineLayer):
raise NotImplementedError("dislora don't support pipeline parallel now")
if dislora_config.tensor_parallel_degree > 1:
self.dislora_config.tensor_parallel_degree = -1
self.model.config.tensor_parallel_degree = -1
raise NotImplementedError("dislora don't support tensor parallel now")
# currently tensor_parallel_degree should all be set to -1.
self.forward = self.model.forward
logger.info("Mark only dislora and trainable_module as trainable.")
self.mark_only_dislora_as_trainable()
@classmethod
def from_pretrained(cls, model, dislora_path, **kwargs):
dislora_config = kwargs.pop("dislora_config", None)
# init dislora config & dislora model
if not isinstance(dislora_config, DisLoRAConfig):
dislora_config = DisLoRAConfig.from_pretrained(dislora_path)
# define a new variable to conserve original lora_config.tensor_parallel_degree value which will update while initializing lora model
dislora_config_tensor_parallel_degree = dislora_config.tensor_parallel_degree
dislora_model = cls(model, dislora_config)
# define dislora weight name
dislora_weight_name = DISLORA_WEIGHTS_NAME
# load and set dislora weight parameter
dislora_weight_path = os.path.join(dislora_path, dislora_weight_name)
if os.path.exists(dislora_weight_path):
# load dislora weight parameter
dislora_state_dict = paddle.load(dislora_weight_path, return_numpy=True)
logger.info(f"Loading the DisLoRA weights from {dislora_weight_path}")
if (
dislora_config_tensor_parallel_degree > 1
and dislora_config_tensor_parallel_degree != model.config.tensor_parallel_degree
):
raise NotImplementedError(
f"{dislora_config_tensor_parallel_degree} is not equal to {model.config.tensor_parallel_degree}. Please merge DisLoRA weights first."
)
# set dislora state dict
dislora_model.set_state_dict(dislora_state_dict)
else:
logger.error(f"DisLoRA weights not found under {dislora_path}, creating DisLoRA weights from scratch")
return dislora_model
def set_state_dict(self, state_dict):
import warnings
warnings.filterwarnings(
action="ignore", message=".*Skip loading for.*", category=Warning, lineno=0, append=False
)
self.model.set_state_dict(state_dict)
logger.info("Load dislora weight successfully")
def save_pretrained(self, save_directory: str, merge_tensor_parallel: bool = False, **kwargs):
logger.info("save dislora pretrained")
save_model_config = kwargs.get("save_model_config", True)
variant = kwargs.get("variant", None)
is_main_process = kwargs.get("is_main_process", paddle.distributed.get_rank() == 0)
assert not os.path.isfile(
save_directory
), f"Saving directory ({save_directory}) should be a directory, not a file"
os.makedirs(save_directory, exist_ok=True)
dislora_config_to_save = DisLoRAConfig(**self.dislora_config.to_dict())
trainable_state_dict = self.get_trainable_state_dict()
# save dislora weight
dislora_weight_name = _add_variant(DISLORA_WEIGHTS_NAME, variant)
weight_filename = os.path.join(save_directory, dislora_weight_name)
paddle.save(trainable_state_dict, weight_filename)
# save dislora config
if is_main_process:
dislora_config_to_save.save_pretrained(save_directory)
if save_model_config:
model_config_to_save = copy.deepcopy(self.model.config)
if merge_tensor_parallel:
model_config_to_save.tensor_parallel_degree = -1
model_config_to_save.save_pretrained(save_directory)
def _find_and_replace_module(self, model, module_name, dislora_config):
if any(dislora_keyword in module_name.lower() for dislora_keyword in ["dislora", "direc_"]):
logger.debug(f"Skipping {module_name} - appears to be a DisLoRA submodule")
return
try:
parent_module = model
attribute_chain = module_name.split(".")
for name in attribute_chain[:-1]:
parent_module = getattr(parent_module, name)
module = getattr(parent_module, attribute_chain[-1])
except AttributeError as e:
logger.error(f"Cannot access module {module_name}: {e}")
raise ValueError(f"Cannot access target module {module_name}: {e}")
if isinstance(module, nn.Linear):
logger.debug(f"Converting {module_name} from nn.Linear to DisLoRALinear")
try:
dislora_module = DisLoRALinear(
in_features=module.weight.shape[0],
out_features=module.weight.shape[1],
r=dislora_config.r,
dislora_alpha=dislora_config.dislora_alpha,
dislora_dropout=dislora_config.dislora_dropout,
dash_flag=dislora_config.dash_flag,
s_tsd=dislora_config.s_tsd,
prefer_small_sigma=dislora_config.prefer_small_sigma,
merge_weights=dislora_config.merge_weights,
bias_attr=False if module.bias is None else None,
init_lora_weights=False,
)
dislora_module.weight.set_value(module.weight)
if module.bias is not None:
dislora_module.bias.set_value(module.bias)
dislora_module._init_lora_weights()
setattr(parent_module, attribute_chain[-1], dislora_module)
logger.debug(f"Successfully replaced {module_name}")
except Exception as e:
logger.error(f"Failed to create DisLoRALinear for {module_name}: {e}")
raise ValueError(f"Failed to create DisLoRALinear for {module_name}: {e}")
elif isinstance(module, DisLoRALinear):
logger.debug(f"Module {module_name} is already a DisLoRALinear, skipping")
else:
module_type = type(module).__name__
if any(keyword in module_name.lower() for keyword in ["dislora_dropout", "direc_"]):
logger.debug(f"Skipping DisLoRA submodule {module_name} ({module_type})")
return
else:
error_msg = f"Target module {module_name} is {module_type}, not nn.Linear. DisLoRA can only replace nn.Linear modules."
logger.error(f"Cannot replace {module_name}: expected nn.Linear, got {module_type}")
raise ValueError(error_msg)
def _find_and_restore_module(self, module_name):
parent_module = self.model
attribute_chain = module_name.split(".")
for name in attribute_chain[:-1]:
parent_module = getattr(parent_module, name)
module = getattr(parent_module, attribute_chain[-1])
original_model_class = self.restore_layer_map[module.__class__]
original_module = original_model_class(in_features=module.weight.shape[0], out_features=module.weight.shape[1])
original_module.weight = module.weight
if isinstance(module, DisLoRALinear):
if not module.merged:
complete_weight = module.weight + module.get_delta_weight()
original_module.weight.set_value(complete_weight)
else:
original_module.weight.set_value(module.weight)
else:
original_module.weight.set_value(module.weight)
if module.bias is not None:
original_module.bias.set_value(module.bias)
setattr(parent_module, attribute_chain[-1], original_module)
def get_trainable_state_dict(self):
"""
Obtain the required state dictionary to be saved, including:
1. Trainable parameters (stop_gradient = False)
2. Main weight W_prin (although frozen, must be saved)
3. TSD direction parameters (although frozen, must be saved)
4. QAT-related parameters
"""
trainable_state_dict = OrderedDict()
for name, weight in self.model.state_dict().items():
# Save trainable parameters and QAT parameters
if not weight.stop_gradient or "activation_quanter" in name or "weight_quanter" in name:
trainable_state_dict[name] = weight
# Save the main branch weight W_prin (for critical fixes)
elif "weight" in name and any(layer_name in name for layer_name in [".weight"]) and "Direc_" not in name:
trainable_state_dict[name] = weight
logger.debug(f"Saving backbone weight: {name}")
# Save all TSD parameters (excluding Direc_Stsd)
elif any(tsd_param in name for tsd_param in ["Direc_Utsd", "Direc_Vhtsd"]):
trainable_state_dict[name] = weight
logger.debug(f"Saving TSD parameter: {name}")
# Save the bias parameters (if any)
elif "bias" in name and "Direc_" not in name:
trainable_state_dict[name] = weight
logger.debug(f"Saving bias parameter: {name}")
return trainable_state_dict
def print_trainable_parameters(self) -> None:
freeze_numel = 0
trainable_numel = 0
for _, weight in self.model.state_dict().items():
if weight.stop_gradient:
freeze_numel += np.prod(weight.shape)
else:
trainable_numel += np.prod(weight.shape)
logger.debug(
f"Frozen parameters: {freeze_numel:.2e} || Trainable parameters:{trainable_numel:.2e} || Total parameters:{freeze_numel+trainable_numel:.2e}|| Trainable:{trainable_numel / (freeze_numel+trainable_numel):.2%}"
)
def mark_only_dislora_as_trainable(self) -> None:
"""
Mark only the parameters related to DisLoRA as trainable, while ensuring that the TSD parameters remain in a frozen state.
"""
for full_param_name, weight in self.model.state_dict().items():
is_dislora_layer = any(
re.fullmatch(target_module, full_param_name.rsplit(".", 1)[0])
for target_module in self.dislora_config.target_modules
)
if is_dislora_layer:
param_name = full_param_name.split(".")[-1]
if param_name == "weight" and "Direc_" not in full_param_name:
weight.stop_gradient = True
logger.debug(f"Freezing backbone weight: {full_param_name}")
elif param_name == "bias" and "Direc_" not in full_param_name:
if self.dislora_config.trainable_bias in ["dislora", "all"]:
weight.stop_gradient = False
logger.debug(f"Setting bias as trainable: {full_param_name}")
else:
weight.stop_gradient = True
logger.debug(f"Freezing bias: {full_param_name}")
elif any(tsd_param in full_param_name for tsd_param in ["Direc_Utsd", "Direc_Vhtsd"]):
weight.stop_gradient = True
logger.debug(f"Keeping TSD parameter frozen: {full_param_name}")
elif any(
trainable_param in full_param_name
for trainable_param in ["Direc_Ur", "Direc_Sr", "Direc_Vhr", "Direc_Stsd"]
):
weight.stop_gradient = False
logger.debug(f"Setting DisLoRA parameter as trainable: {full_param_name}")
else:
weight.stop_gradient = True
logger.debug(f"Freezing other parameter: {full_param_name}")
else:
param_name = full_param_name.split(".")[-1]
if self.dislora_config.trainable_bias == "all" and param_name == "bias":
weight.stop_gradient = False
logger.debug(f"Setting bias as trainable in non-DisLoRA layer: {full_param_name}")
else:
weight.stop_gradient = True
logger.debug(f"Freezing parameter in non-DisLoRA layer: {full_param_name}")
if self.dislora_config.trainable_modules is not None:
for full_param_name, weight in self.model.state_dict().items():
if any(
re.fullmatch(trainable_module, full_param_name)
for trainable_module in self.dislora_config.trainable_modules
):
if not any(tsd_param in full_param_name for tsd_param in ["Direc_Utsd", "Direc_Vhtsd"]):
weight.stop_gradient = False
logger.debug(f"Setting additional trainable module parameter: {full_param_name}")
else:
logger.warning(
f"TSD parameter {full_param_name} matched trainable_modules pattern but kept frozen"
)
def get_dislora_model(self, model: Union[PretrainedModel, nn.Layer], dislora_config: DisLoRAConfig):
"""
Iterate all base model layers, change target modules to DisLoRALayer.
"""
if dislora_config.target_modules is None:
return model
else:
target_modules = dislora_config.target_modules
target_module_names = []
existing_dislora_paths = set()
for module_name, module in model.named_sublayers():
if isinstance(module, DisLoRALinear):
existing_dislora_paths.add(module_name)
for target_module in target_modules:
for module_name, module in model.named_sublayers():
if re.fullmatch(target_module, module_name):
if not isinstance(module, DisLoRALinear):
is_submodule = any(
module_name.startswith(dislora_path + ".") for dislora_path in existing_dislora_paths
)
if not is_submodule:
target_module_names.append(module_name)
else:
logger.debug(f"Skipping {module_name} - it's a submodule of existing DisLoRA module")
else:
logger.debug(f"Skipping {module_name} - already a DisLoRA module")
for module_name in target_module_names:
try:
self._find_and_replace_module(model, module_name, dislora_config)
logger.debug(f"Replaced {module_name} with DisLoRALinear")
except ValueError as e:
raise e
except Exception as e:
logger.warning(f"Failed to replace {module_name}: {e}")
return model
def restore_original_model(self):
# make sure W and dislora weights are not merged before we restore the original model
for layer_name, layer in self.model.named_sublayers():
if isinstance(layer, DisLoRALinear):
self._find_and_restore_module(layer_name)
return self.model
def __getattr__(self, name: str):
"""
Forward missing attributes to the wrapped module.
"""
try:
return super().__getattr__(name) # defer to nn.Layer's logic
except AttributeError:
return getattr(self.model, name)
def train(self):
self.training = True
self.model.training = True
for layer in self.model.sublayers():
layer.training = True
layer.train()
def eval(self):
self.training = False
self.model.training = False
for layer in self.model.sublayers():
layer.training = False
layer.eval()
def disable_dislora(self):
"""
Disable the DisLoRA adapter
"""
for _, layer in self.model.named_sublayers():
if isinstance(layer, DisLoRALinear):
layer.disable_adapters()
def enable_dislora(self):
"""
Enable the DisLoRA adapter
"""
for _, layer in self.model.named_sublayers():
if isinstance(layer, DisLoRALinear):
layer.enable_adapters()
def merge(self):
for _, layer in self.model.named_sublayers():
if any(isinstance(layer, dislora_layer) for dislora_layer in AVAILABLE_LAYERS):
layer.merge()
def unmerge(self):
for _, layer in self.model.named_sublayers():
if any(isinstance(layer, dislora_layer) for dislora_layer in AVAILABLE_LAYERS):
layer.unmerge()
def get_model_config(
self,
):
return self.model_config.to_dict()

View File

@@ -77,8 +77,6 @@ class LoRAConfig:
rslora: bool = field(default=False, metadata={"help": "Whether to use RsLoRA"})
pissa: bool = field(default=False, metadata={"help": "Whether to use Pissa: https://arxiv.org/pdf/2404.02948.pdf"})
loraga: bool = field(default=False, metadata={"help": "Whether to LoRA-GA"})
nola: bool = field(default=False, metadata={"help": "Whether to use Nola: https://arxiv.org/pdf/2310.02556"})
nola_basis_num: int = field(default=1, metadata={"help": "When use nola, the number of basis"})
use_mora: bool = field(
default=False, metadata={"help": "Whether to use MoRA: https://arxiv.org/pdf/2405.12130.pdf"}
)
@@ -96,12 +94,6 @@ class LoRAConfig:
default=False,
metadata={"help": "Whether to use mos lora."},
)
mixer_num: int = field(
default=1,
metadata={
"help": "Num of mixer matrices. Mixer matrices will be added between the LoRA_A and LoRA_B matrices, as referenced in the paper https://arxiv.org/abs/2411.00039."
},
)
lorapro: bool = field(default=False, metadata={"help": "Whether to use LoRA-PRO"})
def __post_init__(self):

View File

@@ -63,10 +63,7 @@ class LoRALinear(nn.Linear):
rslora: bool = False,
lora_plus_scale: float = 1.0,
pissa: bool = False,
nola: bool = False,
nola_basis_num: int = 1,
lora_use_mixer: bool = False,
mixer_num: int = 1,
use_mora: bool = False,
lorapro: bool = False,
mp_moe: bool = False,
@@ -87,10 +84,7 @@ class LoRALinear(nn.Linear):
# Mark the weight as unmerged
self.merged = False
self.pissa = pissa
self.nola = nola
self.nola_basis_num = nola_basis_num
self.lora_use_mixer = lora_use_mixer
self.mixer_num = mixer_num
self.lorapro = lorapro
# Actual trainable parameters
@@ -124,20 +118,14 @@ class LoRALinear(nn.Linear):
),
)
if self.lora_use_mixer:
for i in range(self.mixer_num):
key = "lora_mixer_" + str(i)
setattr(
self,
key,
self.create_parameter(
shape=[r, r],
dtype=self._dtype,
is_bias=False,
default_initializer=nn.initializer.KaimingUniform(
negative_slope=math.sqrt(5), nonlinearity="leaky_relu"
),
),
)
self.lora_AB = self.create_parameter(
shape=[r, r],
dtype=self._dtype,
is_bias=False,
default_initializer=nn.initializer.KaimingUniform(
negative_slope=math.sqrt(5), nonlinearity="leaky_relu"
),
)
self.lora_B = self.create_parameter(
shape=[r, out_features],
dtype=self._dtype,
@@ -148,32 +136,6 @@ class LoRALinear(nn.Linear):
),
)
self.apply_pissa = False
if nola:
# Initialize placeholders for NOLA parameters
self.nola_basis_A = self.create_parameter(
shape=[nola_basis_num, in_features, r],
dtype=self._dtype,
is_bias=False,
)
self.nola_basis_A.stop_gradient = True
self.nola_basis_B = self.create_parameter(
shape=[nola_basis_num, r, out_features],
dtype=self._dtype,
is_bias=False,
)
self.nola_basis_B.stop_gradient = True
self.nola_alpha = self.create_parameter(
shape=[nola_basis_num],
dtype=self._dtype,
is_bias=False,
default_initializer=nn.initializer.Constant(value=0.0),
)
self.nola_beta = self.create_parameter(
shape=[nola_basis_num],
dtype=self._dtype,
is_bias=False,
default_initializer=nn.initializer.Constant(value=0.0),
)
if use_mora or pissa:
self.scaling = 1.0
elif not rslora:
@@ -209,16 +171,6 @@ class LoRALinear(nn.Linear):
weight = res.astype(dtype)
self.weight.set_value(weight)
def get_nola_lora_matrices(self):
"""Compute LoRA matrices A and B from NOLA basis and coefficients."""
if not self.nola:
return self.lora_A, self.lora_B
# Compute A = sum(alpha_i * A_i)
lora_A = paddle.einsum("k,kir->ir", self.nola_alpha, self.nola_basis_A) # [in_features, r]
# Compute B = sum(beta_j * B_j)
lora_B = paddle.einsum("k,kro->ro", self.nola_beta, self.nola_basis_B) # [r, out_features]
return lora_A, lora_B
def rope_init(self):
if self.cos is None or self.sin is None:
inv_freq = 1.0 / (10000 ** (paddle.arange(0, self.r, 2, dtype=paddle.float32) / self.r))
@@ -269,7 +221,7 @@ class LoRALinear(nn.Linear):
if self.lora_use_mixer:
lora_A = lora_A if lora_A is not None else self.lora_A
lora_B = lora_B if lora_B is not None else self.lora_B
lora_AB = lora_AB if lora_AB is not None else self.get_mixer_params(0)
lora_AB = lora_AB if lora_AB is not None else self.lora_AB
delta_weight = lora_A @ lora_AB @ lora_B * self.scaling
elif self.use_mora:
lora_A = lora_A if lora_A is not None else self.lora_A
@@ -297,9 +249,6 @@ class LoRALinear(nn.Linear):
w = w[: self.out_features]
final_weight = w
delta_weight = final_weight.T
elif self.nola:
lora_A, lora_B = self.get_nola_lora_matrices()
delta_weight = lora_A @ lora_B * self.scaling
else:
lora_A = lora_A if lora_A is not None else self.lora_A
lora_B = lora_B if lora_B is not None else self.lora_B
@@ -307,25 +256,18 @@ class LoRALinear(nn.Linear):
return delta_weight
def get_mixer_params(self, index):
key = "lora_mixer_" + str(index)
if index == self.mixer_num - 1:
return getattr(self, key)
else:
return getattr(self, key) @ self.get_mixer_params(index + 1)
def merge(self):
if not self.merged:
delta_weight = self.get_delta_weight()
new_weight = self.weight + delta_weight
self.weight.set_value(new_weight.astype(self.weight.dtype))
self.weight.set_value(new_weight)
self.merged = True
def unmerge(self):
if self.merged:
delta_weight = self.get_delta_weight()
new_weight = self.weight - delta_weight
self.weight.set_value(new_weight.astype(self.weight.dtype))
self.weight.set_value(new_weight)
self.merged = False
def forward(self, input: paddle.Tensor, *args, **kwargs):
@@ -342,17 +284,10 @@ class LoRALinear(nn.Linear):
input = self.lora_dropout(input)
mora_out = self._apply_mora(input)
result += mora_out
elif self.nola:
result = F.linear(x=input, weight=self.weight, bias=self.bias, name=self.name)
input = self.lora_dropout(input)
lora_A, lora_B = self.get_nola_lora_matrices()
result += (self.lora_dropout(input) @ lora_A @ lora_B) * self.scaling
else:
result = F.linear(x=input, weight=self.weight, bias=self.bias, name=self.name)
if self.lora_use_mixer:
result += (
self.lora_dropout(input) @ self.lora_A @ self.get_mixer_params(0) @ self.lora_B
) * self.scaling
result += (self.lora_dropout(input) @ self.lora_A @ self.lora_AB @ self.lora_B) * self.scaling
else:
result += (self.lora_dropout(input) @ self.lora_A @ self.lora_B) * self.scaling
return result
@@ -375,16 +310,14 @@ class RowParallelLoRALinear(RowParallelLinear):
use_quick_lora: bool = False,
pissa: bool = False,
use_mora: bool = False,
nola: bool = False,
nola_basis_num: int = 1,
**kwargs
):
RowParallelLinear.__init__(self, in_features, out_features, **kwargs)
if not isinstance(r, int) or r <= 0:
raise ValueError("Lora rank r should be a positive integer")
if pissa or use_mora or nola:
raise ValueError("Pissa, Mora or NoLA is not supported in model parallel by now")
if pissa or use_mora:
raise ValueError("Pissa or Mora is not supported in model parallel by now")
self.r = r
self.lora_alpha = lora_alpha
@@ -643,16 +576,14 @@ class ColumnParallelLoRALinear(ColumnParallelLinear):
use_quick_lora: bool = False,
pissa: bool = False,
use_mora: bool = False,
nola: bool = False,
nola_basis_num: int = 1,
**kwargs
):
ColumnParallelLinear.__init__(self, in_features, out_features, **kwargs)
if not isinstance(r, int) or r <= 0:
raise ValueError("Lora rank r should be a positive integer")
if pissa or use_mora or nola:
raise ValueError("Pissa, Mora or NoLA is not supported in model parallel by now")
if pissa or use_mora:
raise ValueError("Pissa or Mora is not supported in model parallel by now")
self.r = r
self.lora_alpha = lora_alpha

View File

@@ -482,12 +482,9 @@ class LoRAModel(nn.Layer):
rslora=lora_config.rslora,
lora_plus_scale=lora_config.lora_plus_scale,
pissa=lora_config.pissa,
nola=lora_config.nola,
nola_basis_num=lora_config.nola_basis_num,
bias_attr=False if module.bias is None else None,
use_quick_lora=lora_config.use_quick_lora,
lora_use_mixer=lora_config.lora_use_mixer,
mixer_num=lora_config.mixer_num,
use_mora=lora_config.use_mora,
mp_moe=getattr(module.weight, "mp_moe", False),
is_distributed=getattr(module.weight, "is_distributed", False),
@@ -523,8 +520,6 @@ class LoRAModel(nn.Layer):
rslora=lora_config.rslora,
lora_plus_scale=lora_config.lora_plus_scale,
pissa=lora_config.pissa,
nola=lora_config.nola,
nola_basis_num=lora_config.nola_basis_num,
lora_A_weight_attr=paddle.ParamAttr(
initializer=nn.initializer.KaimingUniform(negative_slope=math.sqrt(5), nonlinearity="leaky_relu")
),
@@ -551,8 +546,6 @@ class LoRAModel(nn.Layer):
rslora=lora_config.rslora,
lora_plus_scale=lora_config.lora_plus_scale,
pissa=lora_config.pissa,
nola=lora_config.nola,
nola_basis_num=lora_config.nola_basis_num,
use_quick_lora=lora_config.use_quick_lora,
)
# Lora column parallel will spilt lora A matrix

View File

@@ -1,17 +0,0 @@
# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .tare_model import TAREModel
__all__ = ["TAREModel"]

View File

@@ -1,336 +0,0 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
import time
import paddle
import paddle.nn.functional as F
def cast_all_params_(layer: paddle.nn.Layer, dtype):
"""把整模里所有 Parameter 的 dtype 原地转成 dtype。"""
for _, p in layer.named_parameters():
if p.dtype != dtype:
p.set_value(p.astype(dtype))
def device2str(type=None, index=None, *, device=None):
type = device if device else type
if isinstance(type, int):
type = f"gpu:{type}"
elif isinstance(type, str):
if "cuda" in type:
type = type.replace("cuda", "gpu")
if "cpu" in type:
type = "cpu"
elif index is not None:
type = f"{type}:{index}"
elif isinstance(type, paddle.CPUPlace) or (type is None):
type = "cpu"
elif isinstance(type, paddle.CUDAPlace):
type = f"gpu:{type.get_device_id()}"
return type
def _get_token(obj, tok: str):
"""
根据 tok 从 obj 里取下一层:
- name[idx] 形式:先 getattr(obj, name) 再 [idx]
- 纯数字:认为 obj 是 list-like,直接 [int(tok)]
- 普通标识符:getattr(obj, tok)
"""
m = re.fullmatch(r"([A-Za-z_]\w*)\[(\d+)\]", tok)
if m:
name, idx = m.group(1), int(m.group(2))
container = getattr(obj, name)
return container[idx]
if tok.isdigit():
return obj[int(tok)]
return getattr(obj, tok)
def _resolve_parent_and_name(root, path: str):
"""
沿着点路径解析,返回 (parent, last_name_or_index_str, obj)
例如 "llama.layers.0.mlp.down_proj" ->
parent=<mlp Layer>, last="down_proj", obj=<Linear>
"""
parts = path.split(".") if path else []
if not parts:
raise AttributeError(f"Empty path for resolve: {path}")
cur = root
parent = None
last = None
for tok in parts:
parent = cur
last = tok
cur = _get_token(cur, tok)
return parent, last, cur
def _set_child(parent, name_or_idx: str, new_obj):
"""
把 parent 的子模块替换为 new_obj。
- 如果 name_or_idx 是数字,认为 parent 是 list-like,做 parent[int] = new_obj
- 否则 setattr(parent, name_or_idx, new_obj)
"""
print("parent:", parent)
print("name_or_idx:", name_or_idx)
print("new_obj", new_obj)
if name_or_idx.isdigit():
print("Yes!!!!!!")
parent[int(name_or_idx)] = new_obj
else:
setattr(parent, name_or_idx, new_obj)
device = device2str("cuda" if paddle.device.cuda.device_count() >= 1 else "cpu")
target_dict = {}
total_parameter = 0
paddle.set_device(device)
class _DeltaUnit(paddle.nn.Layer):
def __init__(self, hidden_size, dtype):
super().__init__()
self.activation_scaling = self.create_parameter(
shape=[1, hidden_size],
dtype=dtype,
default_initializer=paddle.nn.initializer.Constant(1.0),
)
self.activation_bias = self.create_parameter(
shape=[1, hidden_size],
dtype=dtype,
default_initializer=paddle.nn.initializer.Constant(0.0),
)
class ActivationLayer(paddle.nn.Layer):
def __init__(self, hidden_size, update_layer, layer_type="all", op_position="ffn", is_llama=False, n=8, k=7):
super().__init__()
self.update_layer = update_layer
self.layer_type = layer_type
self.op_position = op_position
self.n = n
self.k = k
self.fc = paddle.nn.Linear(in_features=5120, out_features=self.n)
if is_llama:
self.weight_type = paddle.bfloat16
else:
self.weight_type = paddle.float16
if self.layer_type == "all":
self.delta_vector = paddle.nn.LayerList(
_DeltaUnit(hidden_size, dtype=self.weight_type) for _ in range(self.n)
)
elif self.layer_type == "scaling":
self.delta_vector = paddle.nn.ParameterDict(
parameters={"activation_scaling": paddle.nn.parameter.Parameter(paddle.ones(1, hidden_size))}
)
elif self.layer_type == "bias":
self.delta_vector = paddle.nn.ParameterDict(
parameters={"activation_bias": paddle.nn.parameter.Parameter(paddle.zeros(1, hidden_size))}
)
elif self.layer_type == "ln":
self.delta_vector = paddle.nn.ParameterDict(
parameters={
"activation_ln": paddle.nn.LayerNorm(normalized_shape=hidden_size),
"activation_scaling": paddle.nn.parameter.Parameter(paddle.ones(1, hidden_size)),
"activation_bias": paddle.nn.parameter.Parameter(paddle.zeros(1, hidden_size)),
}
)
self.weight = paddle.rand(shape=[1], dtype=self.weight_type)
def forward(self, x, input_tensor=None):
# self.delta_vector.to(self.weight_type).to(device)
if self.op_position == "res" or self.op_position == "res_with_attn" or self.op_position == "res_with_res":
hidden_states = self.update_layer(x, input_tensor)
else:
hidden_states = self.update_layer(x)
if self.layer_type == "all":
hidden_states_new = self.fc(hidden_states)
# hidden_states_updated = hidden_states.clone()
# print('hidden_states_new:',hidden_states_new.shape)
topk_values, topk_indices = paddle.topk(hidden_states_new, self.k, axis=-1)
# topk_weights = paddle.softmax(topk_values, axis=-1)
topk_weights = F.softmax(topk_values, axis=-1)
scaling_tensor = paddle.stack([v.activation_scaling for v in self.delta_vector], axis=0).squeeze(
1
) # [8, hidden]
bias_tensor = paddle.stack([v.activation_bias for v in self.delta_vector], axis=0).squeeze(
1
) # [8, hidden]
hidden_states_updated = paddle.zeros_like(hidden_states) # [..., hidden]
for i in range(self.k):
idx = topk_indices[..., i] # [...,]
s_i = scaling_tensor[idx] # [..., hidden]
b_i = bias_tensor[idx] # [..., hidden]
hd_i = hidden_states * s_i + b_i # [..., hidden]
w_i = topk_weights[..., i : i + 1] # [..., 1]
hidden_states_updated = hidden_states_updated + hd_i * w_i # 广播到 [..., hidden]
hidden_states = hidden_states_updated.squeeze(-1)
elif self.layer_type == "scaling":
hidden_states = hidden_states * self.delta_vector["activation_scaling"]
elif self.layer_type == "bias":
hidden_states = hidden_states + self.delta_vector["activation_bias"]
elif self.layer_type == "ln":
hidden_states = hidden_states * self.delta_vector["activation_scaling"]
hidden_states = hidden_states + self.delta_vector["activation_bias"]
hidden_states = self.delta_vector["activation_ln"](hidden_states)
if self.op_position == "res_with_res":
hidden_states = hidden_states + x
return hidden_states
class TAREModel(paddle.nn.Layer):
_no_split_modules = ["LlamaDecoderLayer"]
def __init__(self, base_model, op_position="ffn", layer_type="all", exclude_layers=[], only_lora=False, n=8, k=7):
print("初始化模型!!!!!!")
time.sleep(5)
super().__init__()
self.base_model = base_model
self.model_type = "llama-7b"
self.layer_type = layer_type
self.op_position = op_position
self.exclude_layers = exclude_layers
self.n = n
self.k = k
if exclude_layers:
pattern_str = "|".join(map(str, exclude_layers))
pattern = re.compile("\\b(?:" + pattern_str + ")\\b")
self.frozen_model()
if only_lora is False:
key_list = [key for key, _ in base_model.named_sublayers(include_self=True)]
for key in key_list:
if exclude_layers:
match = pattern.search(key)
if match:
continue
if self.check_update(key):
self.replace_layer(key)
print(self.print_trainable_parameters())
def check_update(self, key):
if self.op_position == "ffn":
return self.match_substring(key)
def generate(self, **args):
return self.base_model.generate(**args)
def replace_layer(self, key):
# 打印一下,便于定位真实前缀(有的模型是 self.llama,有的是 self.model)
print("base_model:", self.base_model)
print("key:", key)
# 逐段解析,拿到 旧模块 与 其父模块
parent, last_name, old_mod = _resolve_parent_and_name(self.base_model, key)
print("replaced_module(old):", old_mod)
# 构造你的替换模块;确保 forward 签名与 old_mod 兼容
new_module = ActivationLayer(
hidden_size=self.base_model.config.hidden_size,
update_layer=old_mod,
layer_type=self.layer_type,
op_position=self.op_position,
is_llama=True,
n=self.n,
k=self.k,
)
# 覆盖到父模块上
_set_child(parent, last_name, new_module)
def print_trainable_parameters(self):
total_parameters = 0
trainable_parameters = 0
for name, param in self.base_model.named_parameters():
# 统计参数量
try:
n = int(paddle.numel(param))
except Exception:
# 兜底
n = 1
for d in param.shape:
n *= int(d)
total_parameters += n
# 在 Paddle 中,用 stop_gradient 判断是否可训练
is_trainable = not getattr(param, "stop_gradient", True)
if is_trainable:
trainable_parameters += n
pct = (100.0 * trainable_parameters / total_parameters) if total_parameters else 0.0
return {
"total_para:": total_parameters,
"trainable_para: ": trainable_parameters,
"trainable%:": f"{pct:.4f}",
}
def frozen_model(self):
for name, param in self.base_model.named_parameters():
if "lora" in name:
param.stop_gradient = not True
else:
param.stop_gradient = not False
def match_substring(self, input_string):
pattern = "down_proj"
pattern2 = "c_proj"
match = re.search(pattern, input_string) or re.search(pattern2, input_string)
if match:
return True
else:
return False
def forward(self, input_ids, attention_mask=None, labels=None, position_ids=None):
output = self.base_model(
input_ids=input_ids, attention_mask=attention_mask, labels=labels, position_ids=position_ids
)
return output
def load_model(self, save_path):
save_sd = paddle.load(str(save_path))
model_sd = self.base_model.state_dict()
loaded = 0
for k, v in save_sd.items():
# print(k)
if k in model_sd:
tgt = model_sd[k]
if v.dtype != tgt.dtype:
v = v.astype(tgt.dtype)
assert list(v.shape) == list(tgt.shape), f"{k} shape {v.shape} vs {tgt.shape}"
tgt.set_value(v)
tgt.stop_gradient = False # 若这些参数要训练
loaded += 1
print(f"loaded {loaded} tensors")
time.sleep(5)
def get_save_dict(self):
state_dict = self.base_model.state_dict()
for k in state_dict:
if "activation_" in k or "lora" in k or "fc" in k:
print(k)
save_dict = {k: state_dict[k] for k in state_dict if "activation_" in k or "lora" in k or "fc" in k}
return save_dict
def save_model(self, save_path):
save_dict = self.get_save_dict()
paddle.save(obj=save_dict, path=save_path)

View File

@@ -14,8 +14,6 @@
import paddle
from paddlenlp.utils import infohub
def matmul_hadU(X):
@@ -33,37 +31,22 @@ def matmul_hadU(X):
return input.reshape(X.shape)
def create_hadamard_matrix(block_size, dtype):
Q = paddle.diag(paddle.ones((block_size), dtype=dtype))
block = matmul_hadU(Q)
return block
def hadamard_matmul(input, side, hadamard_matrix, block_size):
# left -> H.T@input right -> input@H
origin_shape = input.shape
input = input.reshape([-1, origin_shape[-1]])
if side == "left":
# H.T@input -> (input.T@H).T
input = input.transpose([1, 0])
block_num = input.shape[-1] // block_size
output = input.reshape([-1, block_num, block_size]) @ hadamard_matrix
output = output.reshape([-1, block_num * block_size])
if side == "left":
output = output.transpose([1, 0])
output = output.reshape(origin_shape)
return output
def apply_hadamard_matmul(x, side, block_size):
if getattr(infohub, "hadamard") is None:
setattr(infohub, "hadamard", {})
if block_size in infohub.hadamard:
hadamard_matrix = infohub.hadamard[block_size]
def random_hadamard_matrix(size, dtype, is_block=False):
if not is_block:
A = paddle.randint(low=0, high=2, shape=[size, size]).astype("float32") * 2 - 1
Q, _ = paddle.linalg.qr(A)
return Q.astype(dtype), 1
else:
hadamard_matrix = create_hadamard_matrix(block_size, x.dtype)
infohub.hadamard[block_size] = hadamard_matrix
target_x = hadamard_matmul(x, side, hadamard_matrix, block_size)
return target_x
num_blocks = size
while not (num_blocks % 2):
num_blocks = num_blocks // 2
block_size = size // num_blocks
Q = paddle.diag(paddle.ones((block_size,), dtype="float32"))
block = matmul_hadU(Q)
large_matrix = paddle.zeros([size, size])
for i in range(num_blocks):
start_row = i * block_size
start_col = i * block_size
large_matrix[start_row : start_row + block_size, start_col : start_col + block_size] = block
return large_matrix.cast(dtype), block_size

View File

@@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from copy import deepcopy
import paddle
from paddle.autograd import PyLayer
from .hadamard_utils import apply_hadamard_matmul
from paddlenlp.utils import infohub
from .hadamard_utils import random_hadamard_matrix
try:
from transformer_engine import transformer_engine_paddle as tex
@@ -28,156 +31,151 @@ try:
paddle.float8_e4m3fn: tex.DType.kFloat8E4M3,
paddle.float8_e5m2: tex.DType.kFloat8E5M2,
}
SUPPORT_TE = True
USE_FP8_GEMM = True
except ImportError:
SUPPORT_TE = False
from paddle.linalg import fp8_fp8_half_gemm_fused
QMIN_QMAX_MAPPING = {
"a8w8linear_activation": (-128, 127),
"a8w4linear_activation": (-128, 127),
"a8w8linear_weight": (-128, 127),
"a8w4linear_weight": (-8, 7),
"float8_e4m3fn": (-488, 488),
"float8_e5m2": (-57344, 57344),
}
USE_FP8_GEMM = False
def quantize(
x,
weight_quantize_algo,
tensor_type,
quantization_config,
side="right",
apply_hadamard=False,
act_scale=None,
state=0,
training=False,
group=None,
):
if apply_hadamard:
target_x = apply_hadamard_matmul(x, side, quantization_config.hadamard_block_size)
hadamard_scale = quantization_config.hadamard_block_size
def quantize_tensorwise(x, quantization_config=None, bit_length=8, state=0, training=False, act_scale=None):
qmax = (1 << (bit_length - 1)) - 1
qmin = -1 * qmax - 1
if quantization_config.apply_hadamard:
target_x = x @ infohub.hadamard[x.shape[-1]][0]
else:
target_x, hadamard_scale = x, 1.0
if weight_quantize_algo in ["fp8linear"]:
qmin, qmax = QMIN_QMAX_MAPPING[quantization_config.fp8_format[tensor_type]]
else:
qmin, qmax = QMIN_QMAX_MAPPING[weight_quantize_algo + "_" + tensor_type]
if tensor_type == "activation":
if act_scale is not None:
if training:
scale = (paddle.max(paddle.abs(target_x)) / qmax + quantization_config.scale_epsilon).reshape([1])
if group is not None:
paddle.distributed.all_reduce(scale, op=paddle.distributed.ReduceOp.MAX, group=group, sync_op=True)
if state < quantization_config.apply_online_actscale_step:
act_scale[:] = (state * act_scale + scale) / (state + 1)
else:
scale = (
1 - quantization_config.actscale_moving_rate
) * act_scale + quantization_config.actscale_moving_rate * scale
act_scale[:] = scale
target_x = x
if act_scale is not None:
if training:
scale = paddle.max(paddle.abs(target_x)) / qmax + quantization_config.epsilon
if state < quantization_config.skip_first_act_scale_step:
act_scale.set_value((state * act_scale + scale) / (state + 1))
else:
act_scale.set_value(
(1 - quantization_config.moving_rate) * act_scale + quantization_config.moving_rate * scale
)
scale = act_scale
else:
scale = (paddle.max(paddle.abs(target_x)) / qmax + quantization_config.scale_epsilon).reshape([1])
if weight_quantize_algo in ["a8w8linear", "a8w4linear"]:
quant_x = paddle.clip((target_x / scale).round(), qmin, qmax).astype("int8")
elif weight_quantize_algo in ["fp8linear"]:
quant_x = (target_x / scale).astype(quantization_config.fp8_format[tensor_type])
else:
raise NotImplementedError(f"Unknown {weight_quantize_algo}.")
elif tensor_type == "weight":
if weight_quantize_algo in ["a8w8linear", "a8w4linear"]:
# channelwise
scale = paddle.max(paddle.abs(target_x), axis=0, keepdim=True) / qmax + quantization_config.scale_epsilon
if group is not None:
paddle.distributed.all_reduce(scale, op=paddle.distributed.ReduceOp.MAX, group=group, sync_op=True)
quant_x = paddle.clip((target_x / scale).round(), qmin, qmax).astype("int8").T
scale = scale.squeeze(0) / hadamard_scale
elif weight_quantize_algo in ["fp8linear"]:
scale = paddle.max(paddle.abs(target_x)) / qmax + quantization_config.scale_epsilon
if group is not None:
paddle.distributed.all_reduce(scale, op=paddle.distributed.ReduceOp.MAX, group=group, sync_op=True)
quant_x = (target_x / scale).astype(quantization_config.fp8_format[tensor_type]).view("int8").T
scale = (scale / hadamard_scale).reshape([1])
else:
raise NotImplementedError(f"Unknown {weight_quantize_algo}.")
elif tensor_type == "grad_output":
if weight_quantize_algo in ["fp8linear"]:
scale = (paddle.max(paddle.abs(target_x)) / qmax + quantization_config.scale_epsilon).reshape([1])
quant_x = (target_x / scale).astype(quantization_config.fp8_format[tensor_type])
scale = scale / hadamard_scale
else:
raise NotImplementedError(f"Unknown {weight_quantize_algo}.")
scale = act_scale
else:
raise NotImplementedError(f"Unknown {tensor_type}.")
scale.stop_gradient = True
return quant_x, scale
scale = paddle.max(paddle.abs(target_x)) / qmax + quantization_config.epsilon
x_int8 = paddle.clip((target_x / scale).round(), qmin, qmax).astype("int8")
return x_int8, scale
def dequantize(
quant_x, scale, tensor_type, weight_quantize_algo, quantization_config, apply_hadamard=False, side="left"
):
if tensor_type == "weight":
if weight_quantize_algo in ["a8w8linear", "a8w4linear"]:
x = quant_x.T.astype(scale.dtype)
elif weight_quantize_algo in ["fp8linear"]:
x = quant_x.view(quantization_config.fp8_format[tensor_type]).T.astype(scale.dtype)
else:
raise NotImplementedError(f"Unknown weight_quantize_algo: {weight_quantize_algo}")
if apply_hadamard:
x = apply_hadamard_matmul(x, side, quantization_config.hadamard_block_size)
x *= scale
else:
raise NotImplementedError(f"Unknown {tensor_type}.")
def dequantize_tensorwise(x_int8, scale, apply_hadamard=False):
x = x_int8.astype(scale.dtype) * scale
if apply_hadamard:
x = x @ infohub.hadamard[x.shape[-1]][0].T
return x
def int8_forward(
x,
quant_w,
scale_w,
weight_quantize_algo,
bias=None,
quantization_config=None,
state=0,
training=False,
act_scale=None,
group=None,
):
quant_x, scale_x = quantize(
x=x,
weight_quantize_algo=weight_quantize_algo,
tensor_type="activation",
quantization_config=quantization_config,
side="right",
apply_hadamard=quantization_config.apply_hadamard,
act_scale=act_scale,
state=state,
training=training,
group=group,
)
def fp8_quantize_tensorwise(x, tensor_type, quantization_config=None, state=0, training=False, act_scale=None):
assert tensor_type in ["weight", "activation", "grad_output"], "Only support weight, activation and grad_output"
fp8_format = quantization_config.fp8_format[tensor_type]
qmin, qmax = (-448, 448) if fp8_format == "float8_e4m3fn" else (-57344, 57344)
tensor_type_to_shape_index = {"weight": 0, "activation": -1, "grad_output": -2}
out = paddle.matmul(quant_x, quant_w.T).astype(scale_w.dtype) * (scale_x * scale_w)
if quantization_config is not None and quantization_config.apply_hadamard:
if getattr(infohub, "hadamard") is None:
setattr(infohub, "hadamard", {})
hadamard_matrix_shape = x.shape[tensor_type_to_shape_index[tensor_type]]
if hadamard_matrix_shape in infohub.hadamard:
hadamard_matrix, block_size = infohub.hadamard[hadamard_matrix_shape]
else:
hadamard_matrix, block_size = random_hadamard_matrix(hadamard_matrix_shape, x.dtype, is_block=True)
infohub.hadamard[hadamard_matrix_shape] = (hadamard_matrix, block_size)
target_x = hadamard_matrix.T @ x if tensor_type in ["weight", "grad_output"] else x @ hadamard_matrix
else:
target_x = x
block_size = 1
if act_scale is not None:
if training:
scale = paddle.max(paddle.abs(target_x)) / qmax + quantization_config.epsilon
if state < quantization_config.skip_first_act_scale_step:
act_scale.set_value((state * act_scale + scale) / (state + 1))
else:
act_scale.set_value(
(1 - quantization_config.moving_rate) * act_scale + quantization_config.moving_rate * scale
)
# scale = act_scale
else:
scale = act_scale
else:
scale = paddle.max(paddle.abs(target_x)) / qmax + quantization_config.epsilon
x_fp8 = target_x / scale
x_fp8 = x_fp8.astype(fp8_format).view("int8")
x_fp8 = x_fp8.T if tensor_type == "weight" else x_fp8
scale.stop_gradient = True
scale = scale / block_size if tensor_type in ["weight", "grad_output"] else scale
return x_fp8, scale
def fp8_dequantize_tensorwise(x_fp8, scale, tensor_type, quantization_config=None):
x_fp8 = x_fp8.view(quantization_config.fp8_format[tensor_type])
x_fp8 = x_fp8.T if tensor_type == "weight" else x_fp8
x = x_fp8.astype(scale.dtype) * scale
if quantization_config.apply_hadamard:
hadamard_matrix_shape = x.shape[0] if tensor_type == "weight" else x.shape[-1]
hadamard_matrix, _ = infohub.hadamard[hadamard_matrix_shape]
x = hadamard_matrix @ x if tensor_type == "weight" else x @ hadamard_matrix.T
return x
def quantize_channelwise(w, apply_hadamard=False, bit_length=8):
qmax = (1 << (bit_length - 1)) - 1
qmin = -1 * qmax - 1
if apply_hadamard:
if getattr(infohub, "hadamard") is None:
setattr(infohub, "hadamard", {})
if w.shape[0] in infohub.hadamard:
hadamard_matrix, block_size = infohub.hadamard[w.shape[0]]
else:
hadamard_matrix, block_size = random_hadamard_matrix(w.shape[0], w.dtype, is_block=True)
infohub.hadamard[w.shape[0]] = (hadamard_matrix, block_size)
w = hadamard_matrix.T @ w
else:
block_size = 1
scale = paddle.max(paddle.abs(w), axis=0, keepdim=True) / qmax
w_int8 = paddle.clip((w / scale).round(), qmin, qmax).astype("int8")
scale.stop_gradient = True
return w_int8.T, scale.squeeze(0) / block_size
def dequantize_channelwise(w_int8, scale, apply_hadamard=False):
w = w_int8.T.astype(scale.dtype) * scale
if apply_hadamard:
w = infohub.hadamard[w_int8.shape[1]][0] @ w
return w
def a8w8_forward(
x, w_int8, w_scale=None, bias=None, dtype=None, quantization_config=None, state=0, training=False, act_scale=None
):
x_int8, x_scale = quantize_tensorwise(
x, quantization_config, bit_length=8, state=state, training=training, act_scale=act_scale
)
out = paddle.matmul(x_int8, w_int8.T).astype(dtype) * (x_scale * w_scale.unsqueeze(0))
if bias is not None:
out += bias
return out, quant_x, scale_x
return out, x_int8, x_scale
def int8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale):
def a8w8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale):
if not ctx.x_stop_gradient:
qdq_weight = dequantize(
quant_weight,
quant_scale,
"weight",
ctx.weight_quantize_algo,
ctx.quantization_config,
ctx.quantization_config.apply_hadamard,
"left",
)
input_grad = paddle.matmul(grad_output, qdq_weight.T)
if ctx.quantization_config.quant_input_grad:
grad_output_int8, grad_output_scale = quantize_tensorwise(grad_output * quant_scale)
input_grad = paddle.matmul(grad_output_int8, quant_weight).astype(ctx.dtype) * grad_output_scale
if ctx.quantization_config.apply_hadamard:
input_grad = input_grad @ infohub.hadamard[quant_weight.shape[-1]][0].T
else:
qdq_weight = dequantize_channelwise(
quant_weight, quant_scale, apply_hadamard=ctx.quantization_config.apply_hadamard
)
input_grad = paddle.matmul(grad_output, qdq_weight.T)
else:
input_grad = None
@@ -195,59 +193,64 @@ def int8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_sca
def fp8_forward(
x,
w_fp8,
w_scale,
weight_quantize_algo,
bias=None,
dtype=None,
quantization_config=None,
state=0,
training=False,
act_scale=None,
group=None,
x, w_fp8, w_scale=None, bias=None, dtype=None, quantization_config=None, state=0, training=False, act_scale=None
):
x_fp8, x_scale = quantize(
x_fp8, x_scale = fp8_quantize_tensorwise(
x,
weight_quantize_algo,
"activation",
quantization_config,
side="right",
apply_hadamard=quantization_config.apply_hadamard,
act_scale=act_scale,
tensor_type="activation",
quantization_config=quantization_config,
state=state,
training=training,
group=group,
act_scale=act_scale,
)
x_fp8 = x_fp8.view(quantization_config.fp8_format["activation"])
w_fp8 = w_fp8.view(quantization_config.fp8_format["weight"])
out = fp8_fp8_half_gemm_fused(
x_fp8,
w_fp8,
transpose_x=False,
transpose_y=True,
bias=bias,
scale=x_scale * w_scale,
output_dtype=dtype,
)
if USE_FP8_GEMM:
x_shape = x_fp8.shape
x_fp8 = x_fp8.view((-1, x_fp8.shape[-1]))
fwd_scales = paddle.stack([x_scale.astype("float32"), w_scale.astype("float32")])
out, _ = fp8_gemm(
A=w_fp8,
A_scale_inv=fwd_scales,
A_fp8_tensor=FP8FwdTensors.GEMM1_WEIGHT,
A_dtype=TE_DType[w_fp8.dtype],
B=x_fp8,
B_scale_inv=fwd_scales,
B_fp8_tensor=FP8FwdTensors.GEMM1_INPUT,
B_dtype=TE_DType[x_fp8.dtype],
out_dtype=dtype,
workspace=get_workspace(),
bias=bias,
use_bias=True if bias is not None else False,
use_split_accumulator=True,
)
x_fp8 = x_fp8.view(x_shape)
out = out.view((*x_shape[:-1], -1))
else:
x = x_fp8.astype(dtype) * x_scale
w = w_fp8.astype(dtype) * w_scale
out = paddle.matmul(x, w.T).astype(dtype)
if bias is not None:
out += bias
return out, x_fp8, x_scale
def fp8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale):
if not ctx.x_stop_gradient:
if ctx.quantization_config.quant_input_grad:
grad_output_fp8, grad_output_scale = quantize(
grad_output_fp8, grad_output_scale = fp8_quantize_tensorwise(
grad_output,
ctx.weight_quantize_algo,
"grad_output",
ctx.quantization_config,
side="left",
apply_hadamard=False,
tensor_type="grad_output",
quantization_config=ctx.quantization_config,
)
grad_output_fp8 = grad_output_fp8.view(ctx.quantization_config.fp8_format["grad_output"])
quant_weight = quant_weight.view(ctx.quantization_config.fp8_format["weight"])
if SUPPORT_TE:
if USE_FP8_GEMM:
grad_output_shape = grad_output_fp8.shape
grad_output_fp8 = grad_output_fp8.view((-1, grad_output_fp8.shape[-1]))
fwd_scales = paddle.concat([x_scale.astype("float32"), quant_scale.astype("float32")])
fwd_scales = paddle.stack([x_scale.astype("float32"), quant_scale.astype("float32")])
bwd_scales = grad_output_scale[None].astype("float32")
input_grad, _ = fp8_gemm(
A=quant_weight.T,
@@ -268,16 +271,11 @@ def fp8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scal
weight_ = quant_weight.astype(ctx.dtype) * quant_scale
input_grad = paddle.matmul(grad_output_, weight_).astype(ctx.dtype)
if ctx.quantization_config.apply_hadamard:
input_grad = apply_hadamard_matmul(input_grad, "right", ctx.quantization_config.hadamard_block_size)
input_grad = infohub.hadamard[grad_output.shape[-2]][0] @ input_grad
input_grad = input_grad @ infohub.hadamard[quant_weight.shape[-1]][0].T
else:
qdq_weight = dequantize(
quant_weight,
quant_scale,
"weight",
ctx.weight_quantize_algo,
ctx.quantization_config,
apply_hadamard=ctx.quantization_config.apply_hadamard,
side="left",
qdq_weight = fp8_dequantize_tensorwise(
quant_weight, quant_scale, tensor_type="weight", quantization_config=ctx.quantization_config
)
input_grad = paddle.matmul(grad_output, qdq_weight.T)
else:
@@ -285,36 +283,20 @@ def fp8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scal
if not ctx.w_stop_gradient:
if ctx.quantization_config.quant_weight_grad:
grad_output_fp8, grad_output_scale = quantize(
x=grad_output,
weight_quantize_algo=ctx.weight_quantize_algo,
quantization_config_ = deepcopy(ctx.quantization_config)
quantization_config_.apply_hadamard = False
grad_output_fp8, grad_output_scale = fp8_quantize_tensorwise(
grad_output,
tensor_type="grad_output",
quantization_config=ctx.quantization_config,
apply_hadamard=False,
quantization_config=quantization_config_,
)
if SUPPORT_TE:
grad_output_fp8 = grad_output_fp8.view(ctx.quantization_config.fp8_format["grad_output"])
quant_x = quant_x.view(ctx.quantization_config.fp8_format["activation"])
if USE_FP8_GEMM:
quant_x = quant_x.view((-1, quant_x.shape[-1]))
grad_output_fp8 = grad_output_fp8.view((-1, grad_output_fp8.shape[-1]))
fwd_scales = paddle.concat([x_scale.astype("float32"), quant_scale.astype("float32")])
fwd_scales = paddle.stack([x_scale.astype("float32"), quant_scale.astype("float32")])
bwd_scales = grad_output_scale[None].astype("float32")
# FP8 gemm need k % 16 = 0
ALIGNMENT_SIZE = 16
def pad_tensor_to_multiple(tensor, dtype):
current_size = tensor.shape[0]
padding_size = ALIGNMENT_SIZE - current_size % ALIGNMENT_SIZE
# Create padding zeros with matching shape and dtype
padding_shape = [padding_size, tensor.shape[1]]
padding = paddle.zeros(padding_shape, dtype=dtype)
padded_tensor = paddle.concat([tensor, padding], axis=0)
return padded_tensor
if quant_x.shape[0] % ALIGNMENT_SIZE != 0:
quant_x = pad_tensor_to_multiple(quant_x, ctx.quantization_config.fp8_format["activation"])
grad_output_fp8 = pad_tensor_to_multiple(
grad_output_fp8, ctx.quantization_config.fp8_format["grad_output"]
)
weight_grad, _ = fp8_gemm(
A=grad_output_fp8.T,
A_scale_inv=bwd_scales,
@@ -339,8 +321,9 @@ def fp8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scal
grad_output_.reshape([-1, grad_output_.shape[-1]]),
).astype(ctx.dtype)
if ctx.quantization_config.apply_hadamard:
weight_grad = weight_grad / ctx.quantization_config.hadamard_block_size
weight_grad = apply_hadamard_matmul(weight_grad, "left", ctx.quantization_config.hadamard_block_size)
hadamard_matrix, block_size = infohub.hadamard[quant_x.shape[-1]]
weight_grad = weight_grad / block_size
weight_grad = hadamard_matrix @ weight_grad
else:
if len(x.shape) == 2:
weight_grad = paddle.matmul(x.transpose([1, 0]), grad_output)
@@ -367,39 +350,33 @@ class QATFunc(PyLayer):
state,
training,
act_scale,
weight_quantize_algo,
group,
):
quant_x, x_scale = None, None
if weight_quantize_algo in ["fp8linear"]:
if quantization_config.weight_quantize_algo in ["fp8linear"]:
output, quant_x, x_scale = fp8_forward(
x,
quant_weight,
w_scale=quant_scale,
weight_quantize_algo=weight_quantize_algo,
bias=bias,
dtype=dtype,
quantization_config=quantization_config,
state=state,
training=training,
act_scale=act_scale,
group=group,
)
else:
output, quant_x, x_scale = int8_forward(
output, quant_x, x_scale = a8w8_forward(
x,
quant_w=quant_weight,
scale_w=quant_scale,
weight_quantize_algo=weight_quantize_algo,
quant_weight,
w_scale=quant_scale,
bias=bias,
dtype=dtype,
quantization_config=quantization_config,
state=state,
training=training,
act_scale=act_scale,
group=group,
)
ctx.quantization_config = quantization_config
ctx.weight_quantize_algo = weight_quantize_algo
ctx.dtype = dtype
ctx.x_stop_gradient = x.stop_gradient
ctx.w_stop_gradient = quant_weight.stop_gradient
@@ -422,7 +399,7 @@ class QATFunc(PyLayer):
if ctx.quantization_config.weight_quantize_algo in ["fp8linear"]:
input_grad, weight_grad = fp8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale)
else:
input_grad, weight_grad = int8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale)
input_grad, weight_grad = a8w8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale)
if not ctx.b_stop_gradient:
bias_grad = grad_output.sum(axis=[0, 1])

View File

@@ -20,6 +20,7 @@ quant_inference_mapping = {"avg": "abs_max", "abs_max_channel_wise": "abs_max_ch
fp8_format_mapping = {
"hybrid": {"weight": "float8_e4m3fn", "activation": "float8_e4m3fn", "grad_output": "float8_e5m2"},
"e4m3": {"weight": "float8_e4m3fn", "activation": "float8_e4m3fn", "grad_output": "float8_e4m3fn"},
"e5m2": {"weight": "float8_e5m2", "activation": "float8_e5m2", "grad_output": "float8_e5m2"},
}
@@ -40,7 +41,6 @@ class QuantizationConfig:
qlora_weight_double_quant_block_size: Block size for quant_scale of weight quant_scale.
weight_quant_method: The method for weight quantization.
act_quant_method: The method for activation quantization.
apply_online_actscale_step: Use online (per-step) activation scales for the first N steps. During these steps, activation scales are also collected to compute their mean for later use.
"""
def __init__(
@@ -64,14 +64,13 @@ class QuantizationConfig:
dtype=None,
ignore_modules=None,
group_size=-1,
apply_hadamard=False,
hadamard_block_size=32,
apply_hadamard=True,
quant_input_grad=False,
quant_weight_grad=False,
apply_online_actscale_step=200,
actscale_moving_rate=0.01,
skip_first_act_scale_step=20,
moving_rate=0.01,
epsilon=1e-8,
fp8_format_type="hybrid",
scale_epsilon=1e-8,
**kwargs,
):
if weight_quantize_algo is not None:
@@ -140,17 +139,12 @@ class QuantizationConfig:
self.ignore_modules = ignore_modules
self.group_size = group_size
self.apply_hadamard = apply_hadamard
self.hadamard_block_size = hadamard_block_size
self.quant_input_grad = quant_input_grad
self.quant_weight_grad = quant_weight_grad
self.apply_online_actscale_step = apply_online_actscale_step
self.actscale_moving_rate = actscale_moving_rate
self.fp8_format_type = fp8_format_type
self.scale_epsilon = scale_epsilon
@property
def fp8_format(self):
return fp8_format_mapping[self.fp8_format_type]
self.skip_first_act_scale_step = skip_first_act_scale_step
self.moving_rate = moving_rate
self.epsilon = epsilon
self.fp8_format = fp8_format_mapping[fp8_format_type]
def is_weight_quantize(self):
if isinstance(self.weight_quantize_algo, dict):
@@ -220,7 +214,6 @@ class QuantizationConfig:
config_dict = self.to_dict()
# get the default config dict
default_config_dict = QuantizationConfig().to_dict()
serializable_config_dict = {}

View File

@@ -15,7 +15,6 @@
import paddle
import paddle.nn as nn
from paddle.autograd import PyLayer
from paddle.distributed import fleet
from paddle.distributed.fleet.base import topology as tp
from paddle.distributed.fleet.layers.mpu import mp_ops
from paddle.distributed.fleet.utils.sequence_parallel_utils import (
@@ -24,8 +23,6 @@ from paddle.distributed.fleet.utils.sequence_parallel_utils import (
)
from paddle.nn.quant import llm_int8_linear, weight_dequantize, weight_only_linear
from paddlenlp.utils import infohub
from .qat_utils import QATFunc
try:
@@ -212,20 +209,10 @@ def quant_weight_linear(
):
if weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
state, training, act_scale, group = act_state
state, training, act_scale = act_state
return QATFunc.apply(
x,
quant_weight,
bias,
quant_scale,
quantization_config,
dtype,
state,
training,
act_scale,
weight_quantize_algo,
group,
x, quant_weight, bias, quant_scale, quantization_config, dtype, state, training, act_scale
)
else:
return QuantizationLinearFunc.apply(
@@ -241,22 +228,6 @@ def quant_weight_linear(
)
def get_act_scale_group(is_row=False):
if paddle.distributed.is_initialized():
if getattr(infohub, "scale_group") is None:
hcg = fleet.get_hybrid_communicate_group()
rank = hcg._dp_degree * hcg._sharding_degree
group_no_row = hcg.create_fuse_group(["data", "sharding"])[1] if rank > 1 else None
rank *= hcg._mp_degree
group_row = hcg.create_fuse_group(["data", "sharding", "model"])[1] if rank > 1 else None
setattr(infohub, "scale_group", [group_no_row, group_row])
group = infohub.scale_group[1] if is_row else infohub.scale_group[0]
else:
group = None
return group
class QuantizationLinear(nn.Layer):
"""Quantization Linear layer."""
@@ -307,10 +278,9 @@ class QuantizationLinear(nn.Layer):
raise NotImplementedError("Not yet support grouwise weightonly quantization.")
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
self.act_scale = self.create_parameter(
shape=[1], dtype=self._dtype, is_bias=False, default_initializer=nn.initializer.Constant(value=0.0)
shape=[], dtype=self._dtype, is_bias=False, default_initializer=nn.initializer.Constant(value=0.0)
)
self.act_scale.stop_gradient = True
self.group = get_act_scale_group()
elif self.weight_quantize_algo in ["fp4", "nf4"]:
if qlora_weight_linear is None:
@@ -370,7 +340,6 @@ class QuantizationLinear(nn.Layer):
for p in self.parameters():
p.is_distributed = is_distributed
p.mp_moe = mp_moe
self.quant_weight.weight_quantize_algo = self.weight_quantize_algo
def forward(self, x):
output = quant_weight_linear(
@@ -385,7 +354,7 @@ class QuantizationLinear(nn.Layer):
if (self.weight_quantize_algo in ["fp4", "nf4"] and self.quantization_config.qlora_weight_double_quant)
else None,
bias=self.bias,
act_state=(self.state, self.training, self.act_scale, self.group)
act_state=(self.state, self.training, self.act_scale)
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]
else None,
)
@@ -465,10 +434,7 @@ class ColumnParallelQuantizationLinear(nn.Layer):
is_bias=False,
)
self.quant_scale.stop_gradient = True
if self.weight_quantize_algo in ["fp8linear", "a8w4linear", "a8w8linear"]:
self.quant_scale.is_distributed = False
else:
self.quant_scale.is_distributed = True if self.is_mp else False
self.quant_scale.is_distributed = True if self.is_mp else False
if self.quant_scale.is_distributed:
self.quant_scale.split_axis = 0
else:
@@ -476,11 +442,10 @@ class ColumnParallelQuantizationLinear(nn.Layer):
raise NotImplementedError("Not yet support grouwise weightonly quantization.")
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
self.act_scale = self.create_parameter(
shape=[1], dtype=self._dtype, is_bias=False, default_initializer=nn.initializer.Constant(value=0.0)
shape=[], dtype=self._dtype, is_bias=False, default_initializer=nn.initializer.Constant(value=0.0)
)
self.act_scale.is_distributed = False
self.act_scale.is_distributed = True if self.is_mp else False
self.act_scale.stop_gradient = True
self.group = get_act_scale_group()
else:
raise NotImplementedError(f"Not yet support weight_quantize_algo: {self.weight_quantize_algo}")
if bias_attr is False:
@@ -495,7 +460,6 @@ class ColumnParallelQuantizationLinear(nn.Layer):
self.bias.is_distributed = True if self.is_mp else False
if self.bias.is_distributed:
self.bias.split_axis = 0
self.quant_weight.weight_quantize_algo = self.weight_quantize_algo
def forward(self, x):
if self.is_mp:
@@ -522,7 +486,7 @@ class ColumnParallelQuantizationLinear(nn.Layer):
if (self.weight_quantize_algo in ["fp4", "nf4"] and self.quantization_config.qlora_weight_double_quant)
else None,
bias=self.bias,
act_state=(self.state, self.training, self.act_scale, self.group)
act_state=(self.state, self.training, self.act_scale)
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]
else None,
)
@@ -609,10 +573,7 @@ class RowParallelQuantizationLinear(nn.Layer):
is_bias=False,
)
self.quant_scale.stop_gradient = True
if self.weight_quantize_algo in ["fp8linear", "a8w4linear", "a8w8linear"]:
self.quant_scale.is_distributed = False
else:
self.quant_scale.is_distributed = True if self.is_mp else False
self.quant_scale.is_distributed = True if self.is_mp else False
if self.quant_scale.is_distributed:
self.quant_scale.split_axis = 0
else:
@@ -622,9 +583,8 @@ class RowParallelQuantizationLinear(nn.Layer):
self.act_scale = self.create_parameter(
shape=[1], dtype=self._dtype, is_bias=False, default_initializer=nn.initializer.Constant(value=0.0)
)
self.act_scale.is_distributed = False
self.act_scale.is_distributed = True if self.is_mp else False
self.act_scale.stop_gradient = True
self.group = get_act_scale_group(is_row=True)
else:
raise NotImplementedError(f"Not yet support weight_quantize_algo: {self.weight_quantize_algo}")
@@ -638,8 +598,6 @@ class RowParallelQuantizationLinear(nn.Layer):
is_bias=True,
)
self.quant_weight.weight_quantize_algo = self.weight_quantize_algo
def forward(self, x):
if self.input_is_parallel or (not self.is_mp):
input_parallel = x
@@ -661,7 +619,7 @@ class RowParallelQuantizationLinear(nn.Layer):
if (self.weight_quantize_algo in ["fp4", "nf4"] and self.quantization_config.qlora_weight_double_quant)
else None,
bias=None,
act_state=(self.state, self.training, self.act_scale, self.group)
act_state=(self.state, self.training, self.act_scale)
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]
else None,
)
@@ -689,7 +647,7 @@ class RowParallelQuantizationLinear(nn.Layer):
if (self.weight_quantize_algo in ["fp4", "nf4"] and self.quantization_config.qlora_weight_double_quant)
else None,
bias=self.bias,
act_state=(self.state, self.training, self.act_scale, self.group)
act_state=(self.state, self.training, self.act_scale)
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]
else None,
)

View File

@@ -34,7 +34,7 @@ except:
qlora_weight_quantize = None
from ..utils.log import logger
from .qat_utils import quantize
from .qat_utils import fp8_quantize_tensorwise, quantize_channelwise
from .quantization_linear import (
ColumnParallelQuantizationLinear,
QuantizationLinear,
@@ -155,17 +155,25 @@ def convert_to_weight_quantize_state_dict(state_dict, name, quantization_config,
if weight_name in state_dict:
# gpu weight_quantize will fix in future
target_weight = state_dict.pop(weight_name).cast(dtype).cuda()
if weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
quant_weight, quant_scale = quantize(
target_weight,
weight_quantize_algo,
"weight",
quantization_config,
side="left",
apply_hadamard=quantization_config.apply_hadamard,
if weight_quantize_algo in ["a8w8linear"]:
quant_weight, quant_scale = quantize_channelwise(
target_weight, quantization_config.apply_hadamard, bit_length=8
)
act_scale = paddle.ones([1], dtype=dtype).cuda()
act_scale = paddle.zeros([], dtype="bfloat16").cuda()
act_scale.stop_gradient = True
state_dict[act_scale_name] = act_scale
elif weight_quantize_algo in ["a8w4linear"]:
quant_weight, quant_scale = quantize_channelwise(
target_weight, quantization_config.apply_hadamard, bit_length=4
)
act_scale = paddle.zeros([], dtype="bfloat16").cuda()
act_scale.stop_gradient = True
state_dict[act_scale_name] = act_scale
elif weight_quantize_algo in ["fp8linear"]:
quant_weight, quant_scale = fp8_quantize_tensorwise(
target_weight, tensor_type="weight", quantization_config=quantization_config
)
act_scale = paddle.zeros([], dtype="bfloat16").cuda()
act_scale.stop_gradient = True
state_dict[act_scale_name] = act_scale
else:

View File

@@ -20,32 +20,57 @@ import paddle
from ..utils.comm_utils import masked_whiten
@paddle.no_grad()
def compute_gae_advantage_return(
token_level_rewards: paddle.Tensor,
values: paddle.Tensor,
sequence_mask: paddle.Tensor,
start: int,
gamma: paddle.Tensor,
lam: paddle.Tensor,
use_tgt_len_return: bool = True,
) -> Tuple[paddle.Tensor, paddle.Tensor]:
"""Compute advantages and returns using Generalized Advantage Estimation (GAE)."""
# Modified from https://github.com/CarperAI/trlx/blob/main/trlx/models/modeling_ppo.py
lastgaelam = 0.0
advantages_reversed = []
gen_len = token_level_rewards.shape[-1]
values = values * sequence_mask
token_level_rewards = token_level_rewards * sequence_mask
for t in reversed(range(0, gen_len)):
if use_tgt_len_return and start > 0:
# consistent with Beaver
# values length is src+tgt-1, start is src-1, return length is tgt
pass
elif use_tgt_len_return:
# values length is tgt, start is 0, return length is tgt
assert start == 0
else:
# values length is src+tgt-1, start is src-1, return length is src+tgt-1
pass
for t in reversed(range(start, gen_len)): # pylint: disable=invalid-name
next_values = values[:, t + 1] if t < gen_len - 1 else 0.0
delta = token_level_rewards[:, t] + gamma * next_values - values[:, t]
lastgaelam = delta + gamma * lam * lastgaelam
advantages_reversed.append(lastgaelam)
advantages = paddle.stack(advantages_reversed[::-1], axis=1)
returns = advantages + values.contiguous()
returns = advantages + values[:, start:].contiguous()
if not use_tgt_len_return:
advantages = paddle.concat(
[
paddle.zeros([advantages.shape[0], start], dtype=advantages.dtype),
advantages,
],
axis=-1,
)
returns = paddle.concat(
[
paddle.zeros([returns.shape[0], start], dtype=returns.dtype),
returns,
],
axis=-1,
)
return advantages.detach(), returns
@@ -133,7 +158,6 @@ def compute_reinforce_plus_plus_advantages_and_returns(
return advantages, returns
@paddle.no_grad()
def add_kl_divergence_regularization(
prompt: paddle.Tensor, # size = (B, S) # pylint: disable=unused-argument
log_probs: paddle.Tensor, # size = (B, L)

View File

@@ -30,12 +30,6 @@ from paddle.distributed import fleet
from paddle.distributed.fleet.layers.mpu import mp_ops
from paddle.distributed.fleet.meta_parallel import ParallelCrossEntropy
try:
from paddle.distributed.fleet.utils.sequence_parallel_utils import GatherOp
except:
pass
from ...transformers.llama.modeling import (
LlamaPretrainingCriterion as PretrainingCriterion,
)
@@ -452,16 +446,6 @@ class RLHFPPOMixedLoss(nn.Layer):
else:
hidden_states, weight, bias, transpose_y = logits
if self.config.tensor_parallel_degree > 1 and self.config.sequence_parallel:
hidden_states = GatherOp.apply(hidden_states)
hidden_states = hidden_states.reshape(
[
input_ids.shape[0],
-1,
hidden_states.shape[-1],
]
)
if use_remove_padding:
input_ids = raw_input_ids
if pad_size > 0:
@@ -615,7 +599,7 @@ class RLHFPPOMixedLoss(nn.Layer):
@merge_fwd_labels
class RLHFValueLoss(nn.Layer):
def __init__(self, config, clip_range_value=5.0, use_fp32_compute=False):
def __init__(self, config, clip_range_value=5.0):
"""
Initializes the `ClipRewardRange` object.
@@ -633,7 +617,6 @@ class RLHFValueLoss(nn.Layer):
super().__init__()
self.clip_range_value = clip_range_value
self.config = config
self.use_fp32_compute = use_fp32_compute
def critic_loss_fn(
self,
@@ -653,48 +636,54 @@ class RLHFValueLoss(nn.Layer):
vf_loss2 = paddle.square(values_clipped - returns)
return 0.5 * paddle.sum(paddle.maximum(vf_loss1, vf_loss2) * mask) / mask.sum()
def forward(
self,
reward_values,
old_reward_values,
reward_returns,
sequence_mask,
response_start=0,
# for varlen flaskmask
pad_size=0,
raw_input_ids=None,
indices=None,
raw_input_shape=None,
input_ids_rmpad_rolled=None,
):
""" """
reward_values = reward_values[0].squeeze(0)
if self.config.sequence_parallel:
from paddle.distributed.fleet.utils.sequence_parallel_utils import GatherOp
def forward(self, reward_values, old_reward_values, reward_returns, sequence_mask):
"""
计算奖励值的损失函数。
如果输入的奖励值和旧奖励值的长度相同,则使用给定的序列掩码来确定有效长度。
如果输入的奖励值的长度比旧奖励值少一个,则将最后一个元素视为与输入IDs一致的填充,并删除它。
否则,奖励值只有tgt长度。
reward_values = GatherOp.apply(reward_values)
Args:
reward_values (paddle.Tensor, list of paddle.Tensor or None, optional): 奖励值,可以是单个张量或列表中的多个张量。默认为None。
old_reward_values (paddle.Tensor, optional): 旧奖励值。
reward_returns (paddle.Tensor, optional): 奖励返回值。
sequence_mask (paddle.Tensor, optional): 序列掩码。
use_remove_padding = indices is not None
if use_remove_padding:
if pad_size > 0:
reward_values = reward_values[:-pad_size, :]
Returns:
paddle.Tensor, float32: 奖励值的损失函数。
from ..utils.bert_padding import pad_input
reward_values = pad_input(
reward_values.squeeze(0), indices, batch=raw_input_shape[0], seqlen=raw_input_shape[1]
).contiguous()
if self.use_fp32_compute and reward_values.dtype != paddle.float32:
reward_values = reward_values.cast(paddle.float32)
reward_values = reward_values.squeeze(axis=-1)[:, response_start:-1]
Raises:
ValueError: 当奖励值和旧奖励值的长度不匹配时引发。
"""
reward_values = reward_values if isinstance(reward_values, paddle.Tensor) else reward_values[0]
reward_values = reward_values.squeeze(axis=-1)[:, :-1]
if reward_values.shape[1] == old_reward_values.shape[1]:
# labels (old_reward_values, reward_returns, sequence_mask) has
# src+tgt-1 length, valid length is determined by sequence_mask
pass
elif reward_values.shape[1] < old_reward_values.shape[1]:
# labels (old_reward_values, reward_returns, sequence_mask) has
# src+tgt length and the last one is a padding to be consistent
# with input_ids
assert reward_values.shape[1] == old_reward_values.shape[1] - 1
reward_values = paddle.concat(
[
reward_values,
paddle.zeros([reward_values.shape[0], 1], dtype=reward_values.dtype),
],
-1,
)
else:
# labels (old_reward_values, reward_returns, sequence_mask) has
# tgt length
reward_values = reward_values[:, -old_reward_values.shape[1] :]
reward_critic_loss = self.critic_loss_fn(
reward_values,
old_reward_values,
reward_returns,
sequence_mask,
)
return reward_critic_loss

View File

@@ -1,41 +0,0 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
def extract_solution(solution_str, method="strict"):
assert method in ["strict", "flexible"]
if method == "strict":
# this also tests the formatting of the model
solutions = re.findall("#### (\\-?[0-9\\.\\,]+)", solution_str)
if len(solutions) == 0:
final_answer = None
else:
# take the last solution
final_answer = solutions[-1].replace(",", "").replace("$", "")
elif method == "flexible":
answer = re.findall("(\\-?[0-9\\.\\,]+)", solution_str)
final_answer = None
if len(answer) == 0:
# no reward is there is no answer
pass
else:
invalid_str = ["", "."]
# find the last number that is not '.'
for final_answer in reversed(answer):
if final_answer not in invalid_str:
break
return final_answer

View File

@@ -18,7 +18,6 @@ from typing import Any, Optional
import paddle
from paddle import nn
from paddle.distributed.fleet.utils.sequence_parallel_utils import GatherOp
from ...transformers import PretrainedConfig, PretrainedModel
from ...transformers.auto import AutoModel
@@ -41,35 +40,10 @@ class AutoModelForScore(ScoreModelMixin, PretrainedModel):
TypeError: If the config is not an instance of `PretrainedConfig`.
"""
super().__init__(config)
self.model = AutoModel.from_config(config)
@classmethod
def from_pretrained(cls, pretrained_model_name_or_path: str, *model_args, **kwargs):
config = kwargs.pop("config", None)
model = cls(config, **kwargs)
model.config = config
model.model = AutoModel.from_pretrained(pretrained_model_name_or_path, config=config)
model.init_score_head(config, hidden_size=config.hidden_size, **kwargs)
model.head_init_weights()
return model
def head_init_weights(self):
self.score_head.weight.set_value(
paddle.tensor.normal(
mean=0.0,
std=self.config.initializer_range,
shape=self.score_head.weight.shape,
)
)
if hasattr(self.score_head, "bias") and isinstance(self.score_head.bias, paddle.Tensor):
self.score_head.bias.set_value(paddle.zeros_like(self.score_head.bias))
@classmethod
def from_config(cls, config, **kwargs):
model = cls(config, **kwargs)
model.model = AutoModel.from_config(config)
model.config = config
model.init_score_head(config, hidden_size=config.hidden_size, **kwargs)
return model
# config.architectures = [self.__class__.__name__]
self.init_score_head(config, hidden_size=config.hidden_size, **kwargs)
def get_input_embeddings(self) -> Optional[nn.Embedding]:
"""
@@ -117,6 +91,7 @@ class AutoModelForScore(ScoreModelMixin, PretrainedModel):
def forward(
self,
input_ids: paddle.Tensor,
attention_mask: paddle.Tensor,
position_ids: paddle.Tensor | None = None,
past_key_values: list[paddle.Tensor] | None = None,
inputs_embeds: paddle.Tensor | None = None,
@@ -124,8 +99,6 @@ class AutoModelForScore(ScoreModelMixin, PretrainedModel):
output_attentions: bool | None = None,
output_hidden_states: bool | None = None,
return_dict: bool | None = None,
attn_mask_startend_row_indices: paddle.Tensor | None = None,
**kwargs
) -> tuple[paddle.Tensor, paddle.Tensor] | ScoreModelOutput:
"""
Forward pass of the sentence.
@@ -157,7 +130,7 @@ class AutoModelForScore(ScoreModelMixin, PretrainedModel):
AssertionError:
Raised when `attention_mask` is not None.
"""
assert attention_mask is not None
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
output_hidden_states = (
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
@@ -166,26 +139,19 @@ class AutoModelForScore(ScoreModelMixin, PretrainedModel):
outputs = self.model(
input_ids=input_ids,
attention_mask=attention_mask,
position_ids=position_ids,
past_key_values=past_key_values,
inputs_embeds=inputs_embeds,
use_cache=use_cache,
output_attentions=output_attentions,
output_hidden_states=output_hidden_states,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
return_dict=return_dict,
)
critic_hidden_states = outputs[0] # size = (B, L, E)
if self.config.sequence_parallel:
gather_hidden_states = GatherOp.apply(critic_hidden_states)
hidden_states = paddle.reshape_(
gather_hidden_states, [-1, position_ids.shape[1], gather_hidden_states.shape[-1]]
)
hidden_states = outputs[0] # size = (B, L, E)
return self.get_score(
hidden_states,
attention_mask=attention_mask,
position_ids=position_ids,
return_dict=return_dict,
)

View File

@@ -53,7 +53,6 @@ class ScoreModelMixin:
normalize_function: NormalizeFunction = "affine"
_initialized: bool = False
@classmethod
def init_score_head(self, config: PretrainedConfig, hidden_size: int, **kwargs: Any) -> None:
"""Initialize the score head."""
if self._initialized:
@@ -114,13 +113,11 @@ class ScoreModelMixin:
def get_score(
self,
hidden_state: paddle.Tensor, # size = (B, L, E)
position_ids: paddle.Tensor | None = None, # size = (B, L)
attn_mask_startend_row_indices: paddle.Tensor | None = None, # size = (B, 1), (B, 2), (B, 3) or (B, 4)
return_dict: bool | None = None,
attention_mask: paddle.Tensor | None = None, # size = (B, L)
position_ids: paddle.Tensor | None = None, # size = (B, L)
return_dict: bool | None = None,
) -> ScoreModelOutput:
"""Forward pass of the score model."""
hidden_state = hidden_state.cast(paddle.float32)
scores = self.score_head(hidden_state) # size = (B, L, D)
if scores.dtype != hidden_state.dtype: # EB rm cast to float32
scores = scores.cast(hidden_state.dtype)
@@ -173,6 +170,7 @@ class ScoreModelMixin:
if self.do_normalize:
scores = self.normalizer.normalize(scores)
end_score = self.normalizer.normalize(end_score)
if not return_dict:
return scores, end_score
@@ -182,8 +180,6 @@ class ScoreModelMixin:
end_scores=end_score, # size = (B, D)
)
return scores
def set_normalize(self, mode: bool = True) -> None:
"""
Set whether to normalize the input data, default is True.

View File

@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid
from typing import List
from typing import Any, Dict, List
import numpy as np
import paddle
@@ -21,7 +21,6 @@ from paddle.distributed import fleet
from paddle.distributed.fleet.layers.mpu import mp_ops
from paddle.distributed.fleet.meta_parallel import ParallelCrossEntropy
from ...datasets.rlhf_datasets.protocol import DataProto
from ..models.ppo_model_utils import (
RLHFPPOMixedLoss,
create_startend_row_indices,
@@ -37,7 +36,7 @@ class ActorReferenceTrainerBase(RLTrainer):
loss_identifier = lambda self, inputs: "actor_loss"
@paddle.no_grad()
def compute_logprob(self, batch: DataProto, key) -> DataProto:
def compute_logprob(self, input_ids: paddle.Tensor, position_ids: paddle.Tensor = None, **kwargs) -> paddle.Tensor:
"""
Computes the log probability of each token during the rollout process.
@@ -61,15 +60,11 @@ class ActorReferenceTrainerBase(RLTrainer):
Raises:
None.
"""
input_ids = batch.batch["input_ids"]
position_ids = batch.batch["position_ids"]
prompt = batch.batch.get("prompt", None)
if self.args.use_fused_head_and_loss_fn:
return self.compute_fused_logprob(
input_ids=input_ids,
key=key,
position_ids=position_ids,
prompt=prompt,
**kwargs,
)
log_probs_list = []
@@ -80,7 +75,7 @@ class ActorReferenceTrainerBase(RLTrainer):
# Pipe model outputs a logits tensor with LMHead, while non-pipe model
# outputs a tuple with logits tensor as the only one element.
startend_row_indices = create_startend_row_indices(input_ids, self.tokenizer.pad_token_id)
response_start = prompt.shape[-1] - 1 if prompt is not None else 0
response_start = kwargs["prompt"].shape[-1] - 1 if "prompt" in kwargs else 0
for i in range(num_batches):
# Calculate the start and end indices for the current batch
@@ -160,14 +155,11 @@ class ActorReferenceTrainerBase(RLTrainer):
logits = None
paddle.device.cuda.empty_cache()
return DataProto.from_single_dict(
{key: paddle.concat(log_probs_list, axis=0)}, meta_info={"temperature": self.args.temperature}
)
return paddle.concat(log_probs_list, axis=0)
@paddle.no_grad()
def compute_fused_logprob(
self, input_ids: paddle.Tensor, key, position_ids: paddle.Tensor = None, prompt=None, loop_chunk_size=1024
) -> DataProto:
self, input_ids: paddle.Tensor, position_ids: paddle.Tensor = None, loop_chunk_size=1024, **kwargs
):
log_probs_list = []
batch_size, sequence_length = input_ids.shape
per_device_logprob_batch_size = self.args.per_device_logprob_batch_size
@@ -176,7 +168,7 @@ class ActorReferenceTrainerBase(RLTrainer):
# Pipe model outputs a logits tensor with LMHead, while non-pipe model
# outputs a tuple with logits tensor as the only one element.
startend_row_indices = create_startend_row_indices(input_ids, self.tokenizer.pad_token_id)
response_start = (prompt.shape[-1] - 1) if prompt is not None else 0
response_start = kwargs["prompt"].shape[-1] - 1 if "prompt" in kwargs else 0
num_embeddings = self.model.config.vocab_size
tensor_parallel_degree = self.model.config.tensor_parallel_degree
@@ -219,7 +211,6 @@ class ActorReferenceTrainerBase(RLTrainer):
position_ids=current_position_ids,
attn_mask_startend_row_indices=current_startend_row_indices,
)
self.model.training = False
if self.args.use_remove_padding:
@@ -311,22 +302,20 @@ class ActorReferenceTrainerBase(RLTrainer):
log_prob_chunks = None
paddle.device.cuda.empty_cache()
return DataProto.from_single_dict(
{key: paddle.concat(log_probs_list, axis=0)}, meta_info={"temperature": self.args.temperature}
)
return paddle.concat(log_probs_list, axis=0)
def update_actor(self, rl_batch: DataProto) -> DataProto:
def update_actor(self, rl_batch: Dict[str, paddle.Tensor]) -> Dict[str, Any]:
# inputs shared by policy and value trainer
input_ids = rl_batch.batch["input_ids"].contiguous() # length: src+tgt
position_ids = rl_batch.batch["position_ids"] # length: src+tgt
sequence_mask = rl_batch.batch["eos_mask"] # length: tgt(-1)
input_ids = rl_batch["input_ids"].contiguous() # length: src+tgt
position_ids = rl_batch["position_ids"] # length: src+tgt
sequence_mask = rl_batch["eos_mask"] # length: tgt(-1)
if self.args.use_fp32_compute and sequence_mask.dtype != paddle.float32:
sequence_mask = sequence_mask.cast(paddle.float32)
# inputs used by policy trainer
old_log_probs = rl_batch.batch["log_probs"] # length: tgt(-1)
reward_advantages = rl_batch.batch["reward_advantages"] # length: tgt(-1)
old_log_probs = rl_batch["log_probs"] # length: tgt(-1)
reward_advantages = rl_batch["reward_advantages"] # length: tgt(-1)
response_start = rl_batch.batch["prompt"].shape[-1] - 1
response_start = rl_batch["prompt"].shape[-1] - 1
attn_mask_startend_row_indices = create_startend_row_indices(input_ids, self.tokenizer.pad_token_id)
policy_trainer_inputs = {
@@ -340,7 +329,7 @@ class ActorReferenceTrainerBase(RLTrainer):
}
if self.args.rl_algorithm == "grpo":
policy_trainer_inputs.update({"ref_log_probs": rl_batch.batch["ref_log_probs"]})
policy_trainer_inputs.update({"ref_log_probs": rl_batch["ref_log_probs"]})
else:
policy_trainer_inputs.update({"ref_log_probs": None})
@@ -348,65 +337,61 @@ class ActorReferenceTrainerBase(RLTrainer):
# metric
with paddle.no_grad():
rewards = rl_batch.batch["rewards"].mean()
ori_rewards = rl_batch.batch["ori_rewards"].mean()
rewards = rl_batch["rewards"].mean()
ori_rewards = rl_batch["ori_rewards"].mean()
mask_cast = sequence_mask.cast(paddle.float32)
if self.args.rl_algorithm in ["ppo", "reinforce_plus_plus"]:
kl_rewards = (rl_batch.batch["kl_rewards"] * mask_cast).sum() / mask_cast.sum()
rewards_with_kl = (rl_batch.batch["rewards_with_kl"] * mask_cast).sum() / mask_cast.sum()
kl_rewards = (rl_batch["kl_rewards"] * mask_cast).sum() / mask_cast.sum()
rewards_with_kl = (rl_batch["rewards_with_kl"] * mask_cast).sum() / mask_cast.sum()
if self.args.rl_algorithm == "ppo":
values = (rl_batch.batch["reward_values"] * mask_cast).sum() / mask_cast.sum()
returns = (rl_batch.batch["reward_returns"] * mask_cast).sum() / mask_cast.sum()
ref_log_probs = rl_batch.batch["ref_log_probs"]
values = (rl_batch["reward_values"] * mask_cast).sum() / mask_cast.sum()
returns = (rl_batch["reward_returns"] * mask_cast).sum() / mask_cast.sum()
ref_log_probs = rl_batch["ref_log_probs"]
kl_divergence = ((old_log_probs - ref_log_probs) * mask_cast).sum() / mask_cast.sum()
mean_generated_length = mask_cast.sum(axis=-1).mean()
max_generated_length = mask_cast.sum(axis=-1).max()
min_generated_length = mask_cast.sum(axis=-1).min()
return DataProto(
meta_info={
"metrics": {
# when using PipelienParallel, the loss returned is 0 when not reach
# accumulated step and the loss returned at accumulated step is a
# mixed loss.
"train_policy_loss": actor_loss,
**(
{
"train_pure_policy_loss": self.info_buffer.get("pure_policy_loss"),
"train_kl_loss": self.info_buffer.get("kl_loss"),
"train_entropy_loss": self.info_buffer.get("entropy_loss"),
}
if self.args.rl_algorithm == "grpo"
else {}
),
"train_reward": ori_rewards, # use original reward to log
**(
{
"train_norm_reward": rewards,
"train_kl_reward": kl_rewards,
"train_norm_reward_with_kl": rewards_with_kl,
"train_pure_policy_loss": self.info_buffer.get("pure_policy_loss"),
"train_entropy_loss": self.info_buffer.get("entropy_loss"),
**({"train_values": values} if self.args.rl_algorithm == "ppo" else {}),
"train_returns": returns,
}
if self.args.rl_algorithm in ["ppo", "reinforce_plus_plus"]
else {}
),
"train_kl_divergence": kl_divergence,
"train_mean_generated_length": mean_generated_length,
"train_max_generated_length": max_generated_length,
"train_min_generated_length": min_generated_length,
return {
# when using PipelienParallel, the loss returned is 0 when not reach
# accumulated step and the loss returned at accumulated step is a
# mixed loss.
"train_policy_loss": actor_loss,
**(
{
"train_pure_policy_loss": self.info_buffer.get("pure_policy_loss"),
"train_kl_loss": self.info_buffer.get("kl_loss"),
"train_entropy_loss": self.info_buffer.get("entropy_loss"),
}
}
)
if self.args.rl_algorithm == "grpo"
else {}
),
"train_reward": ori_rewards, # use original reward to log
**(
{
"train_norm_reward": rewards,
"train_kl_reward": kl_rewards,
"train_norm_reward_with_kl": rewards_with_kl,
"train_pure_policy_loss": self.info_buffer.get("pure_policy_loss"),
"train_entropy_loss": self.info_buffer.get("entropy_loss"),
**({"train_values": values} if self.args.rl_algorithm == "ppo" else {}),
"train_returns": returns,
}
if self.args.rl_algorithm in ["ppo", "reinforce_plus_plus"]
else {}
),
"train_kl_divergence": kl_divergence,
"train_mean_generated_length": mean_generated_length,
"train_max_generated_length": max_generated_length,
"train_min_generated_length": min_generated_length,
}
class ActorReferenceTrainer(ActorReferenceTrainerBase):
@paddle.no_grad()
def generate_sequences(self, prompt_only_batch: DataProto, do_eval=False) -> List[DataProto]:
def generate_sequences(self, prompt_only_batch: Dict, do_eval=False) -> List[Dict[str, Any]]:
"""Rollout a batch of experiences."""
input_ids = prompt_only_batch.batch["input_ids"]
input_ids = prompt_only_batch["input_ids"]
repeat_num = 1 if do_eval else self.args.rollout_n
@@ -422,8 +407,8 @@ class ActorReferenceTrainer(ActorReferenceTrainerBase):
if repeat_num > 1:
input_ids = input_ids.repeat_interleave(repeat_num, axis=0)
if self.args.use_rm_server or self.args.use_rule_reward:
label_ids = prompt_only_batch.batch["label_ids"]
if self.args.use_rm_server:
label_ids = prompt_only_batch["label_ids"]
if repeat_num > 1:
label_ids = label_ids.repeat_interleave(repeat_num, axis=0)
@@ -432,17 +417,11 @@ class ActorReferenceTrainer(ActorReferenceTrainerBase):
sequences = sequences.transpose([1, 0, 2])
# prompt, sequence, attention_mask
return [
DataProto.from_single_dict(
{
"prompt": input_ids[idx * len(seq) : (idx + 1) * len(seq)], # src prompt
"input_ids": seq,
**(
{"label_ids": label_ids[idx * len(seq) : (idx + 1) * len(seq)]}
if self.args.use_rm_server or self.args.use_rule_reward
else {}
), # tgt response
"index": np.array([str(uuid.uuid4())] * len(seq), dtype=object),
}
)
{
"prompt": input_ids,
"input_ids": seq,
**({"label_ids": label_ids[idx * len(seq) : (idx + 1) * len(seq)]} if self.args.use_rm_server else {}),
"index": np.array([str(uuid.uuid4())] * len(seq), dtype=object),
}
for idx, seq in enumerate(sequences)
]

View File

@@ -15,15 +15,15 @@
from __future__ import annotations
from typing import Any, Dict
import paddle
from ...datasets.rlhf_datasets.protocol import DataProto
from ...transformers import PretrainedTokenizer
from ..models.ppo_model_utils import RLHFValueLoss, create_startend_row_indices
# from ..utils.comm_utils import CriticStages
# from ..utils.offload_utils import reload_and_offload_scope
# from ..utils.timer_utils import TimerScope
from ..utils.comm_utils import CriticStages
from ..utils.offload_utils import reload_and_offload_scope
from ..utils.timer_utils import TimerScope
from .rl_trainer import RLTrainer
@@ -33,79 +33,28 @@ class CriticTrainer(RLTrainer):
# define loss name for logging
loss_identifier = lambda self, inputs: "reward_critic_loss"
@paddle.no_grad()
def compute_value(
self,
batch: DataProto,
input_ids: paddle.Tensor,
position_ids: paddle.Tensor = None,
input_ids_tokenizer: PretrainedTokenizer = None,
) -> DataProto:
self.model.eval()
**kwargs,
) -> Dict[str, paddle.Tensor]:
# TODO: confirm actor_tokenizer or reward_tokenizer or critic_tokenizer
# need retokenize?
attn_mask_startend_row_indices = create_startend_row_indices(input_ids, self.tokenizer.pad_token_id)
reward_value = self.model(
input_ids,
attention_mask=None,
position_ids=position_ids,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
)[0]
reward_value = reward_value.squeeze(axis=-1)
reward_value = reward_value[:, :-1]
input_ids = batch.batch["input_ids"]
position_ids = batch.batch["position_ids"]
return reward_value
values_list = []
batch_size, sequence_length = input_ids.shape
per_device_value_batch_size = self.args.per_device_value_batch_size
num_batches = (batch_size + per_device_value_batch_size - 1) // per_device_value_batch_size
startend_row_indices = create_startend_row_indices(input_ids, self.tokenizer.pad_token_id)
response_start = batch.batch["prompt"].shape[-1] - 1 if "prompt" in batch.batch else 0
for i in range(num_batches):
start_index = i * per_device_value_batch_size
end_index = min(start_index + per_device_value_batch_size, batch_size)
# Extract the current batch
current_input_ids = input_ids[start_index:end_index]
current_startend_row_indices = (
startend_row_indices[start_index:end_index] if startend_row_indices is not None else None
)
current_position_ids = position_ids[start_index:end_index] if position_ids is not None else None
if self.args.use_remove_padding:
from ..utils.bert_padding import prepare_flashmask_inputs
update_inputs = prepare_flashmask_inputs(
current_input_ids,
current_position_ids,
self.tokenizer.pad_token_id,
self.model.config.sequence_parallel,
self.model.config.tensor_parallel_degree,
)
current_input_ids = update_inputs["input_ids"]
current_position_ids = update_inputs["position_ids"]
current_startend_row_indices = update_inputs["attn_mask_startend_row_indices"]
indices = update_inputs["indices"]
raw_input_shape = update_inputs["raw_input_shape"]
pad_size = update_inputs["pad_size"]
reward_value = self.model(
current_input_ids,
position_ids=current_position_ids,
attn_mask_startend_row_indices=current_startend_row_indices,
use_cache=False,
)[0]
reward_value = reward_value.squeeze(0)
if self.model.config.sequence_parallel:
from paddle.distributed.fleet.utils.sequence_parallel_utils import (
GatherOp,
)
reward_value = GatherOp.apply(reward_value)
if self.args.use_remove_padding:
from ..utils.bert_padding import pad_input
if pad_size > 0:
reward_value = reward_value[:-pad_size, :]
reward_value = pad_input(
reward_value.squeeze(0).unsqueeze(-1), indices, batch=raw_input_shape[0], seqlen=raw_input_shape[1]
).squeeze(-1)
reward_value = reward_value[:, response_start:-1].contiguous()
values_list.append(reward_value.squeeze(-1))
reward_value = None
paddle.device.cuda.empty_cache()
return DataProto.from_single_dict({"reward_values": paddle.concat(values_list, axis=0)})
def update_critic(self, rl_batch: DataProto) -> DataProto:
def update_critic(self, rl_batch: Dict[str, paddle.Tensor]) -> Dict[str, Any]:
"""
Update the parameters of the critic (reward function).
@@ -120,30 +69,30 @@ class CriticTrainer(RLTrainer):
Returns (Dict[str, Any]):
- train_value_loss (float): Training loss of the critic (reward function).
"""
self.model.train()
# Inputs shared by policy and value trainer
input_ids = rl_batch.batch["input_ids"].contiguous() # length: src+tgt
position_ids = rl_batch.batch["position_ids"] # length: src+tgt
sequence_mask = rl_batch.batch["eos_mask"] # length: src+tgt(-1)
if self.args.use_fp32_compute and sequence_mask.dtype != paddle.float32:
sequence_mask = sequence_mask.cast(paddle.float32)
input_ids = rl_batch["input_ids"].contiguous() # length: src+tgt
attention_mask = rl_batch["attention_mask"] # length: src+tgt
position_ids = rl_batch["position_ids"] # length: src+tgt
sequence_mask = rl_batch["sequence_mask"] # length: src+tgt(-1)
# Inputs used by value trainer
old_reward_values = rl_batch.batch["reward_values"] # length: src+tgt(-1)
reward_returns = rl_batch.batch["reward_returns"] # length: src+tgt(-1)
old_reward_values = rl_batch["reward_values"] # length: src+tgt(-1)
reward_returns = rl_batch["reward_returns"] # length: src+tgt(-1)
attn_mask_startend_row_indices = create_startend_row_indices(input_ids, self.tokenizer.pad_token_id)
value_trainer_inputs = {
"input_ids": input_ids,
"attention_mask": attention_mask,
"position_ids": position_ids,
"old_reward_values": old_reward_values,
"reward_returns": reward_returns,
"sequence_mask": sequence_mask,
"response_start": rl_batch.batch["prompt"].shape[-1] - 1,
"attn_mask_startend_row_indices": attn_mask_startend_row_indices,
}
reward_critic_loss = self.full_training_step(**value_trainer_inputs)
with TimerScope(
self.timers, CriticStages.MODEL_ENABLE_DISABLE, minus_names=[CriticStages.CRITIC_TRAINING_STEP]
):
with reload_and_offload_scope(self, self.model, self.optimizer):
with TimerScope(self.timers, CriticStages.CRITIC_TRAINING_STEP):
reward_critic_loss = self.full_training_step(**value_trainer_inputs)
# return DataProto(meta_info={"metrics": {"train_value_loss": reward_critic_loss}})
return {"train_value_loss": reward_critic_loss}
return reward_critic_loss

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,6 @@ from paddle.distributed import fleet
from paddle.io import Dataset
from ...data import DataCollator
from ...datasets.rlhf_datasets.protocol import DataProto
from ...trainer.trainer import (
EvalPrediction,
TrainerCallback,
@@ -34,7 +33,6 @@ from ...trainer.trainer import (
)
from ...transformers import PretrainedModel, PretrainedTokenizer
from ..models.ppo_model_utils import create_startend_row_indices
from ..models.reward_utils import extract_solution
from .rl_trainer import RLTrainer
from .trainer_utils import batch_retokenize
@@ -57,10 +55,7 @@ class RewardTrainer(RLTrainer):
preprocess_logits_for_metrics: Optional[Callable[[paddle.Tensor, paddle.Tensor], paddle.Tensor]] = None,
reward_server: str = None,
):
if args.use_rule_reward:
self.args = args
self.tokenizer = tokenizer
elif args.use_rm_server:
if args.use_rm_server:
assert isinstance(model, str), "reward trainer need a str (http://xxx:port) for request"
self.args = args
self.tokenizer = tokenizer
@@ -84,27 +79,13 @@ class RewardTrainer(RLTrainer):
@paddle.no_grad()
def compute_reward(
self,
batch: DataProto,
input_ids: paddle.Tensor,
position_ids: paddle.Tensor = None,
input_ids_tokenizer: PretrainedTokenizer = None,
label_ids: paddle.Tensor = None,
**kwargs,
) -> Dict[str, paddle.Tensor]:
input_ids = batch.batch["input_ids"]
position_ids = batch.batch["position_ids"]
label_ids = batch.batch["label_ids"]
prompt = batch.batch["prompt"]
if self.args.use_rule_reward:
prompt_len = prompt.shape[-1]
if label_ids is None:
raise ValueError("Rule-based reward needs labels.")
tgt = input_ids_tokenizer.batch_decode(label_ids, skip_special_tokens=False)
response = input_ids_tokenizer.batch_decode(input_ids[:, prompt_len:], skip_special_tokens=False)
ground_truth = [i.replace(self.tokenizer.pad_token, "") for i in tgt]
response = [i.replace(self.tokenizer.pad_token, "") for i in response]
reward_score = self.compute_score(
solution=response,
ground_truth=ground_truth,
)
elif not self.args.use_rm_server:
if not self.args.use_rm_server:
if self.tokenizer is not input_ids_tokenizer:
# right padding
reward_tokenize_output = batch_retokenize(
@@ -126,7 +107,7 @@ class RewardTrainer(RLTrainer):
position_ids=reward_position_ids,
)[1]
else:
prompt_len = prompt.shape[-1]
prompt_len = kwargs["prompt"].shape[-1]
if label_ids is None:
raise ValueError("Rule-based reward needs labels.")
src = input_ids_tokenizer.batch_decode(input_ids[:, :prompt_len], skip_special_tokens=False)
@@ -141,6 +122,8 @@ class RewardTrainer(RLTrainer):
reward_score = reward_score.squeeze(axis=-1)
return reward_score
# if self.args.rl_algorithm in ["grpo", "reinforce_plus_plus"]:
# return {"rewards": reward_score}
def request_reward_server(self, src, tgt, response):
data = {"src": src, "tgt": tgt, "response": response}
@@ -182,30 +165,4 @@ class RewardTrainer(RLTrainer):
paddle.distributed.barrier(tp_group)
paddle.distributed.broadcast(reward_score, src=tp_group.ranks[0], group=tp_group)
reward_score = reward_score.unsqueeze(-1)
return reward_score
def compute_score(self, solution, ground_truth, method="strict", format_score=0.0, score=1.0):
"""The scoring function for GSM8k.
Reference: Trung, Luong, et al. "Reft: Reasoning with reinforced fine-tuning." Proceedings of the 62nd Annual
Meeting of the Association for Computational Linguistics (Volume 1: Long Papers). 2024.
Args:
solution: the solution text
ground_truth: the ground truth
method: the method to extract the solution, choices are 'strict' and 'flexible'
format_score: the score for the format
score: the score for the correct answer
"""
reward_tensor = paddle.zeros((len(solution), 1), dtype=paddle.float32)
for i in range(len(solution)):
answer = extract_solution(solution_str=solution[i], method=method)
if answer is None:
reward_tensor[i] = 0
else:
if answer == ground_truth[i]:
reward_tensor[i] = score
else:
reward_tensor[i] = format_score
return reward_tensor
return reward_score.unsqueeze(-1)

View File

@@ -15,7 +15,6 @@
import sys
from collections import defaultdict
from enum import Enum, auto
from functools import wraps
import numpy as np
import paddle
@@ -23,7 +22,6 @@ import paddle.distributed as dist
from paddle import nn
from paddle.distributed import fleet
from ...datasets.rlhf_datasets.protocol import DataProto, make_eos_mask
from ...trainer.trainer import Trainer, logger
from ...utils.nested import flatten_list, nested_broadcast_tensor_with_empty
from ..models.ppo_model_utils import make_position_ids_from_input_ids
@@ -751,6 +749,29 @@ def masked_whiten(values, mask, shift_mean=True):
return whitened
def pad_tensor(tensor_list, pad_index=0.0, dtype="bfloat16", padding_side="right"):
max_size = max([i.shape[-1] for i in tensor_list])
data_num = sum([i.shape[0] for i in tensor_list])
if isinstance(tensor_list[0], paddle.Tensor):
new_tensor = paddle.full((data_num, max_size), pad_index, dtype=dtype)
elif isinstance(tensor_list[0], np.ndarray):
new_tensor = np.full((data_num, max_size), pad_index, dtype=dtype)
offset = 0
for idx, i in enumerate(tensor_list):
# new_tensor[offset : offset + i.shape[0], : i.shape[-1]] = i
data_length = i.shape[-1]
if padding_side == "right":
new_tensor[offset : offset + i.shape[0], :data_length] = i
elif padding_side == "left":
new_tensor[offset : offset + i.shape[0], -data_length:] = i
else:
raise ValueError("padding_side must be 'right' or 'left'")
offset += i.shape[0]
return new_tensor
def gather_and_pad(tensor, dp_group=None, sd_group=None, pad_index=0.0, pad=True, padding_side="right"):
"""Gather tensor from all devices."""
@@ -773,7 +794,7 @@ def gather_and_pad(tensor, dp_group=None, sd_group=None, pad_index=0.0, pad=True
else:
return np.concatenate(tensor, axis=0)
else:
return DataProto.pad_tensor(tensor, pad_index=pad_index, dtype=dtype, padding_side=padding_side)
return pad_tensor(tensor, pad_index=pad_index, dtype=dtype, padding_side=padding_side)
def map_func(weight):
if isinstance(weight, paddle.Tensor):
@@ -806,10 +827,49 @@ def gather_and_pad(tensor, dp_group=None, sd_group=None, pad_index=0.0, pad=True
else:
return np.concatenate(flatten_list(gathered_tensor), axis=0)
else:
return DataProto.pad_tensor(gathered_tensor, pad_index=pad_index, dtype=dtype, padding_side=padding_side)
return pad_tensor(gathered_tensor, pad_index=pad_index, dtype=dtype, padding_side=padding_side)
def filter_valid_reward_groups(combined_batch: DataProto, total_batch, rollout_n, variance_threshold=1e-6):
def combine_micro_batches_into_batch(micro_batches, pad_token_id=0):
"""combine micro batches to get a complete batch"""
if not isinstance(micro_batches, list):
return micro_batches
combined_batch = {}
for micro_batch in micro_batches:
for key, value in micro_batch.items():
if isinstance(value, list):
if isinstance(value[0], paddle.Tensor):
if key == "label_ids":
value = [paddle.unsqueeze(v, axis=0) if v.ndim == 1 else v for v in value]
concat_value = pad_tensor(
value,
pad_index=pad_token_id,
dtype=value[0].dtype,
padding_side="left",
)
else:
concat_value = paddle.concat(value, axis=0)
elif isinstance(value[0], np.ndarray):
concat_value = np.concatenate(value, axis=0)
combined_batch.setdefault(key, []).append(concat_value)
else:
combined_batch.setdefault(key, []).append(value)
for key, values in combined_batch.items():
if len(combined_batch[key][0].shape) > 1:
pad_index = pad_token_id
padding_side = "left" if (key == "prompt" or key == "label_ids") else "right"
combined_batch[key] = gather_and_pad(values, pad_index=pad_index, padding_side=padding_side)
elif isinstance(values[0], paddle.Tensor):
combined_batch[key] = paddle.concat(values, axis=0)
elif isinstance(values[0], np.ndarray):
combined_batch[key] = np.concatenate(values, axis=0)
return combined_batch
def filter_valid_reward_groups(combined_batch, total_batch, rollout_n, variance_threshold=1e-6):
"""
Filters out invalid prompt groups based on reward variance, and appends the valid samples to total_batch.
@@ -827,14 +887,10 @@ def filter_valid_reward_groups(combined_batch: DataProto, total_batch, rollout_n
"""
# Choose the reward key to filter by
select_key = (
"rewards_before_length_penalty"
if "rewards_before_length_penalty" in combined_batch.batch.keys()
else "rewards"
)
select_key = "rewards_before_length_penalty" if "rewards_before_length_penalty" in combined_batch else "rewards"
rewards = combined_batch.batch[select_key].flatten() # paddle.Tensor
indices = combined_batch.non_tensor_batch["index"].flatten() # numpy.ndarray
rewards = combined_batch[select_key].flatten() # paddle.Tensor
indices = combined_batch["index"].flatten() # numpy.ndarray
# Group by prompt index
group_map = defaultdict(list)
@@ -856,11 +912,8 @@ def filter_valid_reward_groups(combined_batch: DataProto, total_batch, rollout_n
# Select only valid samples for each key and append to total_batch
valid_indices = np.array(valid_indices, dtype=int)
for key in combined_batch.batch.keys():
filtered = combined_batch.batch[key][valid_indices]
total_batch[key].append(filtered)
for key in combined_batch.non_tensor_batch.keys():
filtered = combined_batch.non_tensor_batch[key][valid_indices]
for key in combined_batch:
filtered = combined_batch[key][valid_indices]
total_batch[key].append(filtered)
return total_batch, num_valid_prompts
@@ -983,93 +1036,62 @@ def process_prompt_and_response(micro_batch, pad_token_id=0):
return micro_batch
def gather_tensor(tensor, dp_group=None, sd_group=None):
"""Gather tensor from all devices."""
def split_batch_into_micro_batches(total_batch, batch_size, pad_token_id=0):
"""
Splits total_batch into micro-batches of size `batch_size`.
if not isinstance(tensor, list):
tensor = [tensor]
Args:
total_batch (dict): Dictionary containing full batched tensors.
batch_size (int): Micro batch size per device.
if isinstance(tensor[0], paddle.Tensor):
type = "tensor"
elif isinstance(tensor[0], np.ndarray):
type = "numpy"
else:
raise TypeError(f"{type(tensor[0])} is not supported for gather and pad")
Returns:
list of dict: A list of micro-batches.
"""
micro_batches = []
num_micro_batches = total_batch["input_ids"].shape[0] // batch_size
if total_batch["input_ids"].shape[0] % batch_size != 0:
num_micro_batches += 1
if num_micro_batches <= 0:
logger.warning(
"The total batch size is smaller than the batch size, please consider using a smaller batch size or a larger global_batch_size."
)
num_micro_batches = 1
dtype = tensor[0].dtype
for i in range(num_micro_batches):
micro_batch = {}
for key, data in total_batch.items():
if isinstance(data, paddle.Tensor):
micro_batch[key] = data[i * batch_size : (i + 1) * batch_size]
elif isinstance(data, np.ndarray):
micro_batch[key] = data[i * batch_size : (i + 1) * batch_size]
elif isinstance(data, list):
micro_batch[key] = data[i * batch_size : (i + 1) * batch_size]
else:
raise TypeError(f"Unsupported data type for key {key}: {type(data)}")
if (dp_group is None and sd_group is None) or (dp_group.nranks == 1 and sd_group.nranks == 1):
return tensor
# if os.getenv("PROCESS_PROMPT_AND_RESPONSE", "1").lower() in ["1", "t", "true", "yes", "y"]:
# micro_batch = process_prompt_and_response(micro_batch=micro_batch, pad_token_id=pad_token_id)
def map_func(weight):
if isinstance(weight, paddle.Tensor):
weight = weight.numpy()
return weight
micro_batches.append(micro_batch)
tensor = [map_func(i) for i in tensor]
sd_gathered_tensor = []
if sd_group.nranks > 1:
dist.all_gather_object(sd_gathered_tensor, tensor, group=sd_group)
dp_gathered_tensor = []
if dp_group.nranks > 1:
if len(sd_gathered_tensor) > 0:
tensor = sd_gathered_tensor
dist.all_gather_object(dp_gathered_tensor, tensor, group=dp_group)
if len(dp_gathered_tensor) > 0:
gathered_tensor = dp_gathered_tensor
else:
gathered_tensor = sd_gathered_tensor
if type == "tensor":
gathered_tensor = [paddle.to_tensor(i, dtype=dtype) for i in flatten_list(gathered_tensor)]
return gathered_tensor
return micro_batches
def gather_tensor_list(dp_group=None, sd_group=None):
def decorator(func):
@wraps(func)
def wrapper(tensors_list, *args, **kwargs):
gathered = gather_tensor(tensors_list, dp_group, sd_group)
return func(gathered, *args, **kwargs)
def make_eos_mask(response_id, eos_token_ids=0, dtype=paddle.int64):
"""
end of sentence token can be int or list: 1 or [1, 2]
e.g. eos_token=1
response_id: [0, 0, 2, 42, 3, 5, 1, 0, 0]
eos_mask: [1, 1, 1, 1, 1, 1, 1, 0, 0]
"""
if isinstance(eos_token_ids, int):
eos_token_ids = [eos_token_ids]
return wrapper
eos_mask = paddle.zeros_like(response_id, dtype=paddle.bool)
for token_id in eos_token_ids:
eos_mask |= response_id == token_id
return decorator
def gather_and_pad_dataproto(batch, dp_group, sd_group, eos_token_ids, pad_token_id, select_keys) -> "DataProto":
new_batch = {}
gather_then_pad_or_concat = gather_tensor_list(dp_group, sd_group)(DataProto.pad_or_concat_tensor_list)
if "eos_mask" in select_keys:
eos_mask = make_eos_mask(
batch.batch["input_ids"][:, batch.batch["prompt"].shape[-1] :],
eos_token_ids=eos_token_ids,
).to(batch.batch["log_probs"].dtype)
new_batch["eos_mask"] = gather_then_pad_or_concat(eos_mask, pad_token_id, key="eos_mask")
for key in select_keys:
if key == "eos_mask":
continue
if key in batch.batch:
value = batch.batch[key]
elif key in batch.non_tensor_batch:
value = batch.non_tensor_batch[key]
else:
raise KeyError(f"{key} not found in batch or non_tensor_batch")
if not isinstance(value, list):
tensor_list = [value]
else:
tensor_list = value
gathered = gather_then_pad_or_concat(tensor_list, pad_token_id, key=key)
new_batch[key] = gathered
return DataProto.from_single_dict(new_batch)
eos_mask = eos_mask.to("int64")
eos_mask = (paddle.cumsum(eos_mask, axis=1) - eos_mask).to("bool")
eos_mask = paddle.logical_not(eos_mask).to(dtype)
return eos_mask

View File

@@ -300,7 +300,6 @@ class TrainingArguments(TrainingArguments):
metadata={"help": "Whether to use tgt for KL."},
)
use_rm_server: bool = field(default=False, metadata={"help": "Use reward server instead of reward model."})
use_rule_reward: bool = field(default=False, metadata={"help": "Use rule-based reward only for gsm8k, to date."})
use_fp32_compute: bool = field(
default=False, metadata={"help": "Use fp32 to compute xx_log_prob,rewards, advantages and loss."}
)
@@ -383,7 +382,6 @@ class TrainingArguments(TrainingArguments):
// self.per_device_train_batch_size
// self.dataset_world_size
)
if self.gradient_accumulation_steps <= 0:
logger.warning(
f"gradient_accumulation_steps: {self.gradient_accumulation_steps} must be greater than zero!"
@@ -445,14 +443,14 @@ class TrainingArguments(TrainingArguments):
self.normalize_advantage = False
max_per_device_eval_batch_size = (
self.global_batch_size * self.rollout_n * self.update_iters // self.dataset_world_size
self.global_mini_batch_size * self.rollout_n * self.update_iters // self.dataset_world_size
)
if self.per_device_eval_batch_size > max_per_device_eval_batch_size:
logger.warning(
f"per_device_eval_batch_size: {self.per_device_eval_batch_size} is larger than "
f"global_batch_size: {self.global_batch_size} * rollout_n: "
f"global_mini_batch_size: {self.global_mini_batch_size} * rollout_n: "
f"{self.rollout_n} * update_iters: {self.update_iters}, which may cause infer error. "
f"We will set it to global_batch_size * rollout_n * update_iters // dataset_world_size!"
f"We will set it to global_mini_batch_size * rollout_n * update_iters // dataset_world_size!"
)
self.per_device_eval_batch_size = max_per_device_eval_batch_size
@@ -532,7 +530,7 @@ class ModelArgument:
)
actor_tokenizer_alpha: float = field(default=None, metadata={"help": "Tokenizer will tokenize randomly"})
reward_tokenizer_alpha: float = field(default=None, metadata={"help": "Tokenizer will tokenize randomly"})
critic_tokenizer_alpha: float = field(default=None, metadata={"help": "Tokenizer will tokenize randomly"})
reward_critic_tokenizer_alpha: float = field(default=None, metadata={"help": "Tokenizer will tokenize randomly"})
stage: str = field(default="PPO", metadata={"help": "The type of training."})
critic_recompute_granularity: str = field(
default="full",

View File

@@ -183,18 +183,14 @@ def reload_and_offload_scope(trainer, *args):
offload_map = {
trainer.actor_model: "train_model",
trainer.reference_model: "freeze_model",
**(
{trainer.reward_model: "freeze_model"}
if not trainer.args.use_rm_server and not trainer.args.use_rule_reward
else {}
),
**({trainer.reward_model: "freeze_model"} if not trainer.args.use_rm_server else {}),
trainer.actor_trainer.optimizer: "optimizer",
}
if trainer.args.rl_algorithm == "ppo":
offload_map.update(
{
trainer.critic_model: "train_model",
trainer.reward_critic_model: "train_model",
trainer.critic_trainer.optimizer: "optimizer",
}
)
@@ -205,8 +201,8 @@ def reload_and_offload_scope(trainer, *args):
# NOTE(gongenlei): for export_evaluate_model
objs.append((trainer.actor_model, offload_map.get(trainer.actor_model, "")))
if trainer.args.rl_algorithm == "ppo":
if trainer.critic_model not in [i for i, _ in objs]:
if trainer.reward_critic_model not in [i for i, _ in objs]:
if getattr(trainer.critic_trainer, "_inner_eval_model", None) is not None:
# NOTE(gongenlei): for export_evaluate_model
objs.append((trainer.critic_model, offload_map.get(trainer.critic_model, "")))
objs.append((trainer.reward_critic_model, offload_map.get(trainer.reward_critic_model, "")))
return OffloadController(objs)

View File

@@ -157,8 +157,7 @@ class PdArgumentParser(ArgumentParser):
elif field.default is dataclasses.MISSING:
kwargs["required"] = True
else:
kwargs["type"] = json.loads if origin_type is dict else field.type
kwargs["type"] = json.loads if field.type is dict else field.type
if field.default is not dataclasses.MISSING:
kwargs["default"] = field.default
elif field.default_factory is not dataclasses.MISSING:

View File

@@ -16,7 +16,6 @@ import copy
import os
import random
import time
import types
from typing import Any, Dict, Optional, Union
import numpy as np
@@ -25,15 +24,12 @@ import paddle.distributed as dist
import paddle.distributed.auto_parallel.intermediate.parallelize as parallelize
import paddle.nn as nn
from paddle.distributed import fleet
from paddle.distributed.auto_parallel._utils import _patch_grads_for_step
from paddle.profiler.utils import switch_job_schedule_profiler
from tqdm.auto import tqdm
from paddlenlp.trainer import Trainer
from ..transformers.context_parallel_utils import auto_split_sequence_dim_load_balance
from ..transformers.model_utils import clean_model_class_name, unwrap_model
from ..transformers.segment_parallel_utils import auto_split_inputs_sequence_dim
from ..transformers.model_utils import unwrap_model
from ..utils.batch_sampler import DistributedBatchSampler as NlpDistributedBatchSampler
from ..utils.env import (
PREFIX_CHECKPOINT_DIR,
@@ -50,9 +46,7 @@ from .trainer_utils import ( # set_hyrbid_parallel_seed,
ShardingOption,
TrainOutput,
_exec_mode_guard,
check_auto_parallel_pipeline_support,
get_last_checkpoint,
get_pp_schedule,
has_length,
speed_metrics,
)
@@ -83,7 +77,6 @@ class AutoTrainer(Trainer):
kwargs.update({"criterion": loss_func})
self.auto_dist_config = kwargs.pop("auto_dist_config", None)
model = kwargs.get("model", None)
self.model_type = kwargs.pop("model_type", None)
assert model is not None
if kwargs.get("args", None) is not None and kwargs["args"].use_intermediate_api:
if not parallelize.has_parallelized_model:
@@ -100,21 +93,12 @@ class AutoTrainer(Trainer):
if not param._is_initialized() and param._init_func is not None:
param.initialize()
kwargs["model"] = model
super().__init__(*args, **kwargs)
assert self.args.enable_auto_parallel
self.global_mesh = fleet.auto.get_mesh()
self.comm_group_in_pp = fleet.get_hybrid_communicate_group().get_pipe_parallel_group()
if self.args.pipeline_parallel_degree > 1 and check_auto_parallel_pipeline_support(self.model_type):
self.pp_schedule = get_pp_schedule(
model,
self.model_type,
self.args.n_microbatches,
self.criterion,
self.args.pipeline_schedule_mode,
self.args.pipeline_parallel_degree,
self.comm_group_in_pp,
)
self._in_pir_mode = paddle.base.framework.get_flags("FLAGS_enable_pir_api")["FLAGS_enable_pir_api"]
@classmethod
@@ -145,7 +129,6 @@ class AutoTrainer(Trainer):
"data_sharding_parallel": training_args.dataset_world_size > 1,
"sharding": training_args.sharding,
"sharding_mesh_dim": training_args.sharding_parallel_mesh_dimension,
"context_parallel": training_args.context_parallel_degree > 1 or training_args.sep_parallel_degree > 1,
}
auto_dist_config = model._generate_auto_dist_config(auto_dist_degree)
model = parallelize.parallelize_model(
@@ -229,16 +212,6 @@ class AutoTrainer(Trainer):
)
else:
self.optimizer = dist.shard_optimizer(self.optimizer, None, self.args.gradient_accumulation_steps)
if (
hasattr(self.optimizer, "_enable_tensor_fusion")
and "enable_tensor_fusion" in self.args.sharding_parallel_config
):
self.optimizer._enable_tensor_fusion()
if (
hasattr(self.optimizer, "_enable_sharding_overlap")
and "enable_overlap" in self.args.sharding_parallel_config
):
self.optimizer._enable_sharding_overlap(model)
if self.args.to_static:
unified_strategy = dist.Strategy()
@@ -520,18 +493,6 @@ class AutoTrainer(Trainer):
npu_accelerate_plugin(self.optimizer)
model, dist_loader = self._wrap_for_auto(model, train_dataloader)
if (
dist.in_auto_parallel_align_mode()
): # When in auto parallel align mode, patching the optimizer step function
orig_step = (
self.optimizer.step.__func__ if hasattr(self.optimizer.step, "__func__") else self.optimizer.step
)
decorator = _patch_grads_for_step(amp_master_grad=self.args.amp_master_grad)
new_step = decorator(orig_step)
self.optimizer.__dict__["step"] = types.MethodType(new_step, self.optimizer)
train_dataloader = dist_loader()
if resume_from_checkpoint is not None:
self._load_from_checkpoint(resume_from_checkpoint)
@@ -584,10 +545,7 @@ class AutoTrainer(Trainer):
if step_control % args.gradient_accumulation_steps == 0:
self.control = self.callback_handler.on_step_begin(args, self.state, self.control)
self.timers and self.timers("forward-backward").start()
if self.args.sep_parallel_degree > 1 and self.args.split_inputs_sequence_dim:
inputs = auto_split_inputs_sequence_dim(inputs)
if self.args.context_parallel_degree > 1 and self.args.split_inputs_sequence_dim:
inputs = auto_split_sequence_dim_load_balance(inputs)
tr_loss_step = self.training_step(model, inputs)
with _exec_mode_guard("dynamic"):
@@ -745,56 +703,7 @@ class AutoTrainer(Trainer):
return (loss, outputs) if return_outputs else loss
def compute_pipeline_loss(self, model, inputs, return_outputs=False):
"""
How the loss is computed by Trainer. By default, all models return the loss in the first element.
Subclass and override for custom behavior.
"""
if self.criterion is not None:
if "labels" in inputs:
labels = inputs.pop("labels")
elif "start_positions" in inputs and "end_positions" in inputs:
labels = (inputs.pop("start_positions"), inputs.pop("end_positions"))
elif self.args.label_names is not None:
labels = []
for label in self.label_names:
labels.append(inputs.pop(label))
labels = tuple(labels)
elif "generator_labels" in inputs:
labels = inputs["generator_labels"]
else:
labels = None
pp_rank = self.comm_group_in_pp.rank
losses = []
if pp_rank == 0: # 第一个pp_stage,参数传入数据流
self.pp_schedule.step(**inputs) # 最后的pp_stage,参数传入label, 并输出loss
elif pp_rank == self.args.pipeline_parallel_degree - 1:
self.pp_schedule.step(target=labels, losses=losses)
else:
self.pp_schedule.step()
final_loss = None
if len(losses) != 0:
final_loss = paddle.stack(losses).mean()
return final_loss
def dynamic_auto_parallel_pipeline_training(
self, model: nn.Layer, inputs: Dict[str, Union[paddle.Tensor, Any]]
) -> paddle.Tensor:
assert self.args.pipeline_parallel_degree > 1, "pipeline_parallel_degree must be greater than 1."
assert check_auto_parallel_pipeline_support(
self.model_type
), "dynamic auto_parallel pipeline only supports special models"
with self.autocast_smart_context_manager():
loss = self.compute_pipeline_loss(model, inputs)
return loss
def dynamic_training(self, model: nn.Layer, inputs: Dict[str, Union[paddle.Tensor, Any]]) -> paddle.Tensor:
if self.args.pipeline_parallel_degree > 1 and check_auto_parallel_pipeline_support(self.model_type):
return self.dynamic_auto_parallel_pipeline_training(model, inputs)
with self.autocast_smart_context_manager():
loss = self.compute_loss(model, inputs)
@@ -1001,19 +910,15 @@ class AutoTrainer(Trainer):
config_to_save = copy.deepcopy(model_to_save.config)
config_to_save.mp_degree = getattr(config_to_save, "config_to_save", 1)
# Attach architecture to the config
config_to_save.architectures = [clean_model_class_name(model_to_save.__class__.__name__)]
config_to_save.architectures = [model_to_save.__class__.__name__]
config_to_save.save_pretrained(output_dir)
if self.model.can_generate():
model_to_save.generation_config.save_pretrained(output_dir)
if self.args.should_save_model_state:
if state_dict is None:
self._save_ckpt_func(self.model.state_dict(), output_dir)
logger.info(f"Model weights saved in {output_dir}")
else:
self._save_ckpt_func(state_dict, output_dir)
logger.info(f"Model weights and optimizer states saved in {output_dir}")
self._save_ckpt_func(self.model.state_dict(), output_dir)
logger.info(f"Model weights and optimizer states saved in {output_dir}")
def _load_from_checkpoint(self, resume_from_checkpoint=None):

View File

@@ -403,132 +403,81 @@ class WandbCallback(TrainerCallback):
class SwanLabCallback(TrainerCallback):
"""
A [`TrainerCallback`] that logs metrics, media, model checkpoints to [SwanLab](https://swanlab.cn/).
A [`TrainerCallback`] that logs metrics, media to [Swanlab](https://swanlab.cn/).
"""
def __init__(self):
if not is_swanlab_available():
raise RuntimeError("SwanLabCallback requires swanlab to be installed. Run `pip install swanlab`.")
import swanlab
has_swanlab = is_swanlab_available()
if not has_swanlab:
raise RuntimeError("SwanlabCallback requires swanlab to be installed. Run `pip install swanlab`.")
if has_swanlab:
import swanlab
self._swanlab = swanlab
self._swanlab = swanlab
self._initialized = False
self._log_model = os.getenv("SWANLAB_LOG_MODEL", None)
def setup(self, args, state, model, **kwargs):
"""
Setup the optional SwanLab (*swanlab*) integration.
One can subclass and override this method to customize the setup if needed. Find more information
[here](https://docs.swanlab.cn/guide_cloud/integration/integration-huggingface-transformers.html).
You can also override the following environment variables. Find more information about environment
variables [here](https://docs.swanlab.cn/en/api/environment-variable.html#environment-variables)
Setup the optional Swanlab integration.
One can subclass and override this method to customize the setup if needed.
variables:
Environment:
- **SWANLAB_API_KEY** (`str`, *optional*, defaults to `None`):
Cloud API Key. During login, this environment variable is checked first. If it doesn't exist, the system
checks if the user is already logged in. If not, the login process is initiated.
- If a string is passed to the login interface, this environment variable is ignored.
- If the user is already logged in, this environment variable takes precedence over locally stored
login information.
- **SWANLAB_PROJECT** (`str`, *optional*, defaults to `None`):
Set this to a custom string to store results in a different project. If not specified, the name of the current
running directory is used.
- **SWANLAB_LOG_DIR** (`str`, *optional*, defaults to `swanlog`):
This environment variable specifies the storage path for log files when running in local mode.
By default, logs are saved in a folder named swanlog under the working directory.
- **SWANLAB_MODE** (`Literal["local", "cloud", "disabled"]`, *optional*, defaults to `cloud`):
SwanLab's parsing mode, which involves callbacks registered by the operator. Currently, there are three modes:
local, cloud, and disabled. Note: Case-sensitive. Find more information
[here](https://docs.swanlab.cn/en/api/py-init.html#swanlab-init)
- **SWANLAB_LOG_MODEL** (`str`, *optional*, defaults to `None`):
SwanLab does not currently support the save mode functionality.This feature will be available in a future
release
- **SWANLAB_WEB_HOST** (`str`, *optional*, defaults to `None`):
Web address for the SwanLab cloud environment for private version (its free)
- **SWANLAB_API_HOST** (`str`, *optional*, defaults to `None`):
API address for the SwanLab cloud environment for private version (its free)
- **SWANLAB_MODE** (`str`, *optional*, defaults to `"cloud"`):
Whether to use swanlab cloud, local or disabled. Set `SWANLAB_MODE="local"` to use local. Set `SWANLAB_MODE="disabled"` to disable.
- **SWANLAB_PROJECT** (`str`, *optional*, defaults to `"PaddleNLP"`):
Set this to a custom string to store results in a different project.
"""
if self._swanlab is None:
return
self._initialized = True
if state.is_world_process_zero:
logger.info('Automatic SwanLab logging enabled, to disable set os.environ["SWANLAB_MODE"] = "disabled"')
logger.info('Automatic Swanlab logging enabled, to disable set os.environ["SWANLAB_MODE"] = "disabled"')
combined_dict = {**args.to_dict()}
if hasattr(model, "config") and model.config is not None:
model_config = model.config if isinstance(model.config, dict) else model.config.to_dict()
model_config = model.config.to_dict()
combined_dict = {**model_config, **combined_dict}
if hasattr(model, "lora_config") and model.lora_config is not None:
lora_config = model.lora_config if isinstance(model.lora_config, dict) else model.lora_config.to_dict()
combined_dict = {**{"lora_config": lora_config}, **combined_dict}
trial_name = state.trial_name
init_args = {}
if trial_name is not None and args.run_name is not None:
init_args["experiment_name"] = f"{args.run_name}-{trial_name}"
elif args.run_name is not None:
init_args["experiment_name"] = args.run_name
elif trial_name is not None:
init_args["experiment_name"] = trial_name
# new add this for experiment_name
experiment_name = os.getenv("SWANLAB_EXP_NAME", None)
if experiment_name is not None:
init_args["experiment_name"] = experiment_name
init_args["project"] = os.getenv("SWANLAB_PROJECT", None)
if args.logging_dir is not None:
init_args["logdir"] = os.getenv("SWANLAB_LOG_DIR", args.logging_dir)
if trial_name is not None:
init_args["name"] = trial_name
init_args["group"] = args.run_name
else:
if not (args.run_name is None or args.run_name == args.output_dir):
init_args["name"] = args.run_name
init_args["dir"] = args.logging_dir
if self._swanlab.get_run() is None:
self._swanlab.init(
project=os.getenv("SWANLAB_PROJECT", "PaddleNLP"),
**init_args,
)
# show paddlenlp logo!
self._swanlab.config["FRAMEWORK"] = "paddlenlp"
# add config parameters (run may have been created manually)
self._swanlab.config.update(combined_dict)
self._swanlab.config.update(combined_dict, allow_val_change=True)
def on_train_begin(self, args, state, control, model=None, **kwargs):
if self._swanlab is None:
return
if not self._initialized:
self.setup(args, state, model, **kwargs)
def on_train_end(self, args, state, control, model=None, processing_class=None, **kwargs):
if self._log_model is not None and self._initialized and state.is_world_process_zero:
logger.warning(
"SwanLab does not currently support the save mode functionality. "
"This feature will be available in a future release."
)
def on_train_end(self, args, state, control, model=None, tokenizer=None, **kwargs):
if self._swanlab is None:
return
def on_log(self, args, state, control, model=None, logs=None, **kwargs):
single_value_scalars = [
"train_runtime",
"train_samples_per_second",
"train_steps_per_second",
"train_loss",
"total_flos",
]
if self._swanlab is None:
return
if not self._initialized:
self.setup(args, state, model)
if state.is_world_process_zero:
for k, v in logs.items():
if k in single_value_scalars:
self._swanlab.log({f"single_value/{k}": v}, step=state.global_step)
non_scalar_logs = {k: v for k, v in logs.items() if k not in single_value_scalars}
non_scalar_logs = rewrite_logs(non_scalar_logs)
self._swanlab.log({**non_scalar_logs, "train/global_step": state.global_step}, step=state.global_step)
def on_save(self, args, state, control, **kwargs):
if self._log_model is not None and self._initialized and state.is_world_process_zero:
logger.warning(
"SwanLab does not currently support the save mode functionality. "
"This feature will be available in a future release."
)
def on_predict(self, args, state, control, metrics, **kwargs):
if not self._initialized:
self.setup(args, state, **kwargs)
if state.is_world_process_zero:
metrics = rewrite_logs(metrics)
self._swanlab.log(metrics)
logs = rewrite_logs(logs)
self._swanlab.log({**logs, "train/global_step": state.global_step}, step=state.global_step)
class AutoNLPCallback(TrainerCallback):

File diff suppressed because it is too large Load Diff

View File

@@ -20,18 +20,10 @@ Callbacks to use with the Trainer class and customize the training loop.
"""
import dataclasses
import json
import time
from dataclasses import dataclass
from typing import Dict, List, Optional, Union
import numpy as np
from paddle.distributed.fleet import fleet
from paddle.distributed.fleet.utils.hybrid_parallel_util import (
fused_allreduce_gradients_with_group,
)
from paddle.distributed.fleet.utils.sequence_parallel_utils import (
is_sequence_parallel_parameter,
)
from tqdm.auto import tqdm
from paddlenlp.utils.log import logger
@@ -157,7 +149,6 @@ class TrainerControl:
should_save: bool = False
should_evaluate: bool = False
should_log: bool = False
should_save_hf: bool = False
def _new_training(self):
"""Internal method that resets the variable for a new training."""
@@ -172,7 +163,6 @@ class TrainerControl:
self.should_save = False
self.should_evaluate = False
self.should_log = False
self.should_save_hf = False
class TrainerCallback:
@@ -308,12 +298,6 @@ class TrainerCallback:
"""
pass
def on_save_hf(self, args: TrainingArguments, state: TrainerState, control: TrainerControl, **kwargs):
"""
Event called after a huggingface checkpoint save.
"""
pass
class CallbackHandler(TrainerCallback):
"""Internal class that just calls the list of callbacks in order."""
@@ -394,7 +378,6 @@ class CallbackHandler(TrainerCallback):
control.should_log = False
control.should_evaluate = False
control.should_save = False
control.should_save_hf = False
return self.call_event("on_step_begin", args, state, control)
def on_load_data_end(self, args: TrainingArguments, state: TrainerState, control: TrainerControl, inputs: Dict):
@@ -427,10 +410,6 @@ class CallbackHandler(TrainerCallback):
def on_prediction_step(self, args: TrainingArguments, state: TrainerState, control: TrainerControl):
return self.call_event("on_prediction_step", args, state, control)
def on_save_hf(self, args: TrainingArguments, state: TrainerState, control: TrainerControl):
control.should_save_hf = False
return self.call_event("on_save_hf", args, state, control)
def call_event(self, event, args, state, control, **kwargs):
for callback in self.callbacks:
result = getattr(callback, event)(
@@ -487,14 +466,6 @@ class DefaultFlowCallback(TrainerCallback):
if state.global_step >= state.max_steps:
control.should_training_stop = True
# Save hf
if (
args.save_strategy == IntervalStrategy.STEPS
and args.save_hf_steps > 0
and state.global_step % args.save_hf_steps == 0
):
control.should_save_hf = True
return control
def on_epoch_end(self, args: TrainingArguments, state: TrainerState, control: TrainerControl, **kwargs):
@@ -638,33 +609,3 @@ class EarlyStoppingCallback(TrainerCallback):
self.check_metric_value(args, state, control, metric_value)
if self.early_stopping_patience_counter >= self.early_stopping_patience:
control.should_training_stop = True
class SPGradSyncCallback(TrainerCallback):
"""
SPGradSyncCallback
只能在非 sharding stage2 的情况下使用。
开启sharding stage2 时,在 `on_optimizer_begin` 的时候 grad 已经被清空了
"""
def __init__(self, model):
assert hasattr(fleet, "_hcg"), "must use MP when calling this Callback"
logger.info("using sp callback")
params = []
self.model = model
for n, p in model.named_parameters():
if is_sequence_parallel_parameter(p):
logger.info(f"register bw hook for:{n}")
params.append(p)
logger.info(f"#-sp-sync param:{len(params)}")
self._sp_params = params
def on_optimizer_begin(self, args, state, control, **kwargs):
"""on_optimizer_begin"""
if self._sp_params:
now = time.time()
mp_group = fleet.get_hybrid_communicate_group().get_model_parallel_group()
fused_allreduce_gradients_with_group(self._sp_params, group=mp_group, scale=1.0) # sum not mean
another_time = time.time()
logger.info(f"sync gradients takes {another_time - now} time")

View File

@@ -30,17 +30,12 @@ import threading
import time
from contextlib import contextmanager
from enum import Enum
from pathlib import Path
from typing import Dict, List, NamedTuple, Optional, Tuple, Union
import numpy as np
import paddle
import paddle.distributed as dist
from paddle.distributed import fleet
from paddle.distributed.fleet.meta_optimizers.dygraph_optimizer.dygraph_sharding_optimizer import (
DygraphShardingOptimizer,
DygraphShardingOptimizerV2,
)
from paddle.distributed.fleet.meta_parallel import get_rng_state_tracker
from paddle.io import IterableDataset
from paddle.optimizer.lr import LambdaDecay
@@ -48,7 +43,6 @@ from paddle.optimizer.lr import LambdaDecay
from paddlenlp.ops import Topology
from ..trainer.argparser import strtobool
from ..transformers import get_gpt_pp_schedule, get_llama_pp_schedule
from ..transformers.tokenizer_utils_base import BatchEncoding
from ..utils.env import PREFIX_CHECKPOINT_DIR, _re_checkpoint # noqa for compatibility
from ..utils.fault_tolerance import PDC_DOWNLOAD_ERROR
@@ -324,6 +318,8 @@ class OptimizerNames(ExplicitEnum):
ADAFACTOR = "adafactor"
ADAMW_MINI = "adamw_mini"
ADAMW_CUSTOM = "adamw_custom"
ADAMW_16BIT_MOMENT = "adamw_16bit_moment"
AdamW_Qweight = "adamw_qweight"
class ShardingOption(ExplicitEnum):
@@ -1259,189 +1255,3 @@ def download_recovery_ckpt_from_pdc(recovery_checkpoint_path, timeout):
raise RuntimeError(
f"{PDC_DOWNLOAD_ERROR}; Error occurred when trying to download checkpoint from PDC, recovery_checkpoint_path: {recovery_checkpoint_path}, timeout: {timeout}; error details: {PDCErrorMessageMap[result]}"
)
def check_auto_parallel_pipeline_support(model_type=None):
support_types = ["llama_pp", "gpt_pp"]
return model_type in support_types
def get_pp_schedule(model, model_type, n_microbatches, loss_fn, mode, pp_degree, group):
assert check_auto_parallel_pipeline_support(model_type)
if model_type == "llama_pp":
return get_llama_pp_schedule(model, n_microbatches, loss_fn, mode, pp_degree, group)
elif model_type == "gpt_pp":
return get_gpt_pp_schedule(model, n_microbatches, loss_fn, mode, pp_degree, group)
def parse_nccl_config_file(config_dir):
json_file = Path(config_dir)
if json_file.exists():
with open(json_file, "r") as file:
data = json.load(file)
def get_full_config_from_dict(comm_config):
assert type(comm_config) is dict
min_val = {
"ll_buffsize": 2**15, # 32KB
"ll128_buffsize": 2**17, # 128KB
"simple_buffsize": 2**17, # 128KB
}
final_config = {}
# if user does not set group name, use the default name set by Paddle
if comm_config.get("name", None) is not None:
final_config["commName"] = comm_config["name"]
final_config["buffsize_align"] = comm_config.get("buffsize_align", 1024)
final_config["algoStr"] = comm_config.get("algo", "")
final_config["protoStr"] = comm_config.get("proto", "")
final_config["nchannels"] = comm_config.get("n_channels", -1)
# ll part
# -1 means using the default value
final_config["ll_buffsize"] = comm_config.get("ll_buffsize", -1)
# keep the buffsize > the min value
if final_config["ll_buffsize"] != -1:
final_config["ll_buffsize"] = max(final_config["ll_buffsize"], min_val["ll_buffsize"])
# ll128 part
final_config["ll128_buffsize"] = comm_config.get("ll128_buffsize", -1)
if final_config["ll128_buffsize"] != -1:
final_config["ll128_buffsize"] = max(final_config["ll128_buffsize"], min_val["ll128_buffsize"])
# simple part
final_config["simple_buffsize"] = comm_config.get("simple_buffsize", -1)
if final_config["simple_buffsize"] != -1:
final_config["simple_buffsize"] = max(final_config["simple_buffsize"], min_val["simple_buffsize"])
# set the buffer size of unused protocols to the minimum value
if final_config["protoStr"] != "":
protos = split_parallel_config(final_config["protoStr"].lower())
for proto in ["ll", "ll128", "simple"]:
if proto not in protos:
final_config[(proto + "_buffsize")] = min_val[(proto + "_buffsize")]
return final_config
for key in data.keys():
data[key] = get_full_config_from_dict(data[key])
return data
else:
raise FileNotFoundError(f"The argument file {json_file} does not exist.")
def init_nccl_config(nccl_comm_group_config, strategy):
nccl_config = parse_nccl_config_file(nccl_comm_group_config)
def set_comm_config(configs, attr, dict_obj):
if strategy.hybrid_configs.get(configs, None) is None or dict_obj is None:
return
if not hasattr(strategy.hybrid_configs[configs], attr):
return
attr_obj = getattr(strategy.hybrid_configs[configs], attr)
for key, value in dict_obj.items():
if hasattr(attr_obj, key):
setattr(attr_obj, key, value)
set_comm_config("pp_configs", "coll_nccl_config", nccl_config.get("pp", None))
set_comm_config("pp_configs", "p2p_nccl_config", nccl_config.get("pp_p2p", None))
set_comm_config("pp_configs", "shared_nccl_config", nccl_config.get("pp_shared", None))
set_comm_config("mp_configs", "nccl_config", nccl_config.get("tp", None))
set_comm_config("sharding_configs", "nccl_config", nccl_config.get("sharding", None))
set_comm_config("sharding_configs", "check_nccl_config", nccl_config.get("sharding_check", None))
set_comm_config("dp_configs", "nccl_config", nccl_config.get("dp", None))
set_comm_config("dp_configs", "check_nccl_config", nccl_config.get("dp_check", None))
set_comm_config("sep_configs", "nccl_config", nccl_config.get("sep", None))
set_comm_config("dp_sep_configs", "nccl_config", nccl_config.get("dp_sep", None))
set_comm_config("pp_tp_configs", "nccl_config", nccl_config.get("pp_tp", None))
set_comm_config("ep_configs", "nccl_config", nccl_config.get("ep", None))
set_comm_config("ep_configs", "grad_nccl_config", nccl_config.get("ep_grad", None))
set_comm_config("moe_sharding_configs", "nccl_config", nccl_config.get("moe_sharding", None))
set_comm_config("moe_sharding_configs", "check_nccl_config", nccl_config.get("moe_sharding_check", None))
set_comm_config("default_comm_group_configs", "nccl_config", nccl_config.get("default", None))
return strategy
def init_optimizer(optimizer, model_sharded_state_dict, state_dict_metadata):
"""
Initialize the optimizer's states according to its type.
For DygraphShardingOptimizer (V1), initializes accumulators for local parameters.
For DygraphShardingOptimizerV2, manually initializes master weights and state dict for sharded parameters.
For other cases, initializes accumulators for all parameters.
Args:
optimizer: The optimizer instance to be initialized.
"""
optimizer_state_names = [".moment1_0", ".moment2_0", ".beta1_pow_acc_0", ".beta2_pow_acc_0", ".w_0"]
inner_opt = getattr(optimizer, "_inner_opt", None)
static_to_struct_mapping = {}
model_sharded_state_dict = dict(sorted(model_sharded_state_dict.items()))
for k, v in model_sharded_state_dict.items():
if v.local_tensor.name not in static_to_struct_mapping:
static_to_struct_mapping[v.local_tensor.name] = k
if isinstance(inner_opt, DygraphShardingOptimizer):
local_params = optimizer._rank2params[optimizer._sharding_rank]
param_list = []
for param in local_params:
param_name = param.name
struct_name = static_to_struct_mapping[param_name]
if not any(struct_name + state_name in state_dict_metadata for state_name in optimizer_state_names):
continue
param_list.append(param)
optimizer._create_accumulators(paddle.base.framework.default_main_program().global_block(), param_list)
return
elif isinstance(inner_opt, DygraphShardingOptimizerV2):
def init_param_optimizer_states(param_iter):
master_weights = {}
state_dict = {}
moments = ("moment1_0", "moment2_0")
betas = ("beta1_pow_acc_0", "beta2_pow_acc_0")
for static_name, shape, no_need_master_weights in param_iter:
if not no_need_master_weights:
master_weights[static_name] = paddle.zeros(shape, dtype="float32")
prefix = f"{static_name}_fp32_master_0_"
else:
prefix = f"{static_name}_"
for moment in moments:
key = f"{prefix}{moment}"
state_dict[key] = paddle.zeros(shape, dtype="float32")
for beta in betas:
key = f"{prefix}{beta}"
state_dict[key] = paddle.zeros((1,), dtype="float32")
return master_weights, state_dict
def buffer_params():
for buffer in optimizer._comm_buffer_list:
for param_name, grad_view in buffer._sharding_param_grad_view.items():
struct_name = static_to_struct_mapping[param_name]
if not any(
struct_name + state_name in state_dict_metadata for state_name in optimizer_state_names
):
continue
param_begin = grad_view._param_begin
param_end = grad_view._param_end
shape = (param_end - param_begin,)
no_need_master_weights = grad_view._param.dtype == paddle.float32
if shape[0] > 0:
yield param_name, shape, no_need_master_weights
master_weights, state_dict = init_param_optimizer_states(buffer_params())
state_dict["master_weights"] = master_weights
state_dict["LR_Scheduler"] = {"last_epoch": 1, "last_lr": 5e-06}
optimizer.set_state_dict(state_dict)
return
param_list = []
for param in optimizer._parameter_list:
param_name = param.name
struct_name = static_to_struct_mapping[param_name]
if not any(struct_name + state_name in state_dict_metadata for state_name in optimizer_state_names):
continue
param_list.append(param)
optimizer._create_accumulators(paddle.base.framework.default_main_program().global_block(), param_list)

View File

@@ -31,11 +31,6 @@ import paddle
import paddle.distributed as dist
from paddle.distributed import fleet
try:
from paddle.distributed.fleet.base.topology import message2nccl_config
except ImportError:
message2nccl_config = None
from ..utils.env import PREFIX_CHECKPOINT_DIR
from ..utils.log import logger
from ..utils.pdc_sdk import FLASH_DEVICE
@@ -44,7 +39,6 @@ from .trainer_utils import (
OptimizerNames,
SchedulerType,
ShardingOption,
init_nccl_config,
split_parallel_config,
)
@@ -407,29 +401,6 @@ class TrainingArguments:
Whether to release gradients during training. Default is `False`.
ckpt_quant_stage (`str`, *optional*):
Whether activate checkpoint quantization. O0: deactivate, O1: Int8 compression, O2: Int4 compression. (default: O0).
save_checkpoint_format (`str`, *optional*):
Specifies the format for saving checkpoints. Options are: None, 'sharding_io', 'unified_checkpoint', 'flex_checkpoint'. (default: None). This setting is ignored if the corresponding switch is configured.
load_checkpoint_format (`str`, *optional*):
Specifies the format for loading checkpoints. Options are: None, 'sharding_io', 'unified_checkpoint', 'flex_checkpoint'. (default: None). This setting is ignored if the corresponding switch is configured.
aoa_config (`Optional[dict[str, list[str]]]`, *optional*):
The AoA configuration of FlexCheckpoint, used to describe the mapping between model weights and the checkpoint content. Default is None.
save_hf_steps (`int`, *optional*, defaults to 500):
Number of updates steps before two huggingface checkpoint saves if `save_strategy="steps"`.
load_via_cpu (bool, optional):
Whether to load checkpoint data into CPU memory first before transferring to GPU.
This helps mitigate GPU memory shortage by staging data on the CPU and only moving required parts to the GPU on demand during communication.
Defaults to False.
load_from_hf (bool, optional):
Whether to load a checkpoint in the HuggingFace format.
Defaults to False.
flex_ckpt_comm_method (str, optional):
Communication method used for checkpoint resharding.
Choices are "send_recv", "broadcast", "multi_group_broadcast", and "grouped_send_recv".
Defaults to "broadcast".
replicate_saved_into_local (bool, optional):
Whether to save checkpoint replicas into local files in a distributed save/load system.
If set to True, replicas will be stored locally on each node/machine.
Defaults to False.
"""
output_dir: str = field(
@@ -637,16 +608,6 @@ class TrainingArguments:
)
},
)
sharding_offload_opt_buffersize_GB: int = field(
default=-1,
metadata={
"help": (
"Set the size of the optimizer offload buffer when need_hack_offload_optimizer() is True. This option only takes effect when "
"use DygraphShardingOptimizerV2. The default value is -1, which means that all of the optimizer states will be offloaded. Only "
"works when export HACK_OFFLOAD_OPTIMIZER=1. "
)
},
)
save_sharded_model: bool = field(
default=False,
@@ -666,16 +627,6 @@ class TrainingArguments:
},
)
load_sharded_model_remap_parameter_name: bool = field(
default=False,
metadata={"help": "Whether to remap parameter name when load_sharded_model = true."},
)
sharded_model_from_ema: bool = field(
default=False,
metadata={"help": "Whether to load sharded model from EMA."},
)
tensor_parallel_degree: int = field(
default=-1,
metadata={
@@ -825,7 +776,7 @@ class TrainingArguments:
"Following options are supported:\n"
"- pp_first. the topo order is dp, pp, sharding, mp \n"
"- sharding_first. the topo order is dp, sharding, pp, mp \n"
"Default is None, for sharding_first"
"Default is None, for pp_first"
)
},
)
@@ -917,10 +868,6 @@ class TrainingArguments:
default="adamw",
metadata={"help": "The optimizer to use."},
)
use_lowprecision_moment: bool = field(
default=False,
metadata={"help": "AdamW use 16bit moment as model parameter."},
)
report_to: Optional[List[str]] = field(
default=None, metadata={"help": "The list of integrations to report the results and logs to."}
)
@@ -964,29 +911,6 @@ class TrainingArguments:
default=False,
metadata={"help": "Whether to use async_save instead of paddle.save."},
)
save_checkpoint_format: Optional[str] = field(
default=None,
metadata={
"help": (
"Specifies the format used to save checkpoints. "
"Available options: 'sharding_io', 'unified_checkpoint', "
"'flex_checkpoint'."
"This setting is ignored if the corresponding switch is configured."
)
},
)
load_checkpoint_format: Optional[str] = field(
default=None,
metadata={
"help": (
"Specifies the format used to load checkpoints. "
"Available options: 'sharding_io', 'unified_checkpoint', "
"'flex_checkpoint'."
"This setting is ignored if the corresponding switch is configured."
)
},
)
ordered_save_group_size: int = field(
default=0,
metadata={
@@ -1072,16 +996,6 @@ class TrainingArguments:
default=False,
metadata={"help": "Offload optimizer after optimizer.step()"},
)
tensorwise_offload_optimizer: Optional[bool] = field(
default=False,
metadata={
"help": (
"Offload all optimizer states to CPU memory. "
"The corresponding parameters will only be loaded to GPU during optimizer step, "
"which reduces GPU memory usage but may increase step time."
)
},
)
save_sharding_stage1_model_include_freeze_params: Optional[bool] = field(
default=False, metadata={"help": "Save Sharding Stage1 Model Exclude Freeze Params"}
)
@@ -1111,10 +1025,6 @@ class TrainingArguments:
default=1,
metadata={"help": "Interval between updating EMA parameters."},
)
zcc_ema_loss_threshold: Optional[float] = field(
default=None,
metadata={"help": "If set not None, only do EMA when the training loss is smaller than the threshold value"},
)
save_tokenizer: Optional[bool] = field(
default=True,
metadata={"help": "Save tokenizer to output_dir."},
@@ -1148,56 +1058,10 @@ class TrainingArguments:
metadata={"help": "是否开启单路sharding时global norm通信拆分全局通信组为pp通信和mp通信分别做"},
)
nccl_comm_group_config: Optional[str] = field(
default=None, metadata={"help": "NCCL中通信组的细粒度控制的配置文件路径, 默认值为None, 代表不启用此项配置"}
)
aoa_config: Optional[dict[str, list[str]]] = field(
default=None,
metadata={
"help": "The AoA configuration of FlexCheckpoint, used to describe the mapping between model weights and the checkpoint content. Default is None."
},
)
save_hf_steps: int = field(default=-1, metadata={"help": "Save huggingface checkpoint every X updates steps."})
load_via_cpu: Optional[bool] = field(
default=False,
metadata={
"help": "If True, loads checkpoint data to CPU first, then transfers required parts to GPU on demand to reduce GPU memory usage. Defaults to False."
},
)
load_from_hf: Optional[bool] = field(
default=False,
metadata={"help": "Whether to load a checkpoint in the HuggingFace format."},
)
flex_ckpt_comm_method: Optional[str] = field(
default="broadcast",
metadata={
"help": (
"Communication method used by FlexCheckpoint for checkpoint resharding. "
'Choices are "send_recv", "broadcast", "multi_group_broadcast", and "grouped_send_recv". '
'Default is "broadcast".'
)
},
)
replicate_saved_into_local: Optional[bool] = field(
default=False,
metadata={"help": "Whether to save replicas cross files in distributed save load system."},
)
def __post_init__(self):
world_size = paddle.distributed.get_world_size()
if in_auto_parallel_align_mode():
# self.max_grad_norm = 0.0
# The current auto_hybrid_pp has aligned the handling of ClipGradByGlobalNorm with the original dygraph semi-auto parallel and dynamic manual-parallel modes and can correctly handle grad_clip, so it is no longer necessary to set max_grad_norm=0.0.
if self.max_grad_norm != 0.0:
warnings.warn(
"max_grad_norm is not 0.0,We will execute ClipGradByGlobalNorm,if you want to disable it,please set max_grad_norm=0.0"
)
self.max_grad_norm = 0.0
os.environ["FLAGS_max_inplace_grad_add"] = "65536"
os.environ["FLAGS_embedding_deterministic"] = "1"
os.environ["FLAGS_cudnn_deterministic"] = "1"
@@ -1293,8 +1157,7 @@ class TrainingArguments:
raise ValueError("AdamW Mini currently doesn't support tensor parallelism.")
self._post_init_parallel_degree()
self._post_init_save_checkpoint_format()
self._post_init_load_checkpoint_format()
if self.to_static:
assert world_size == 1 or self.enable_auto_parallel, (
"It's not supported for training in static mode except the following cases : "
@@ -1313,6 +1176,7 @@ class TrainingArguments:
# use_hybrid_parallel
if self.use_hybrid_parallel:
if ShardingOption.OFFLOAD in self.sharding:
warnings.warn("`offload` is not supported NOW!")
@@ -1347,8 +1211,6 @@ class TrainingArguments:
"use_dualpipev",
"forward_backward_overlap_scheduler",
"enable_dynamic_shape",
"sync_moment",
"sync_param",
]:
raise ValueError(
f"Found unknown pipeline mode config {x}, accept config is disable_p2p_cache_shape, disable_partial_send_recv."
@@ -1401,18 +1263,6 @@ class TrainingArguments:
in pipeline_parallel_config,
"enable_dynamic_shape": "enable_dynamic_shape" in pipeline_parallel_config,
}
pp_sync_param = "sync_param" in pipeline_parallel_config
pp_sync_moment = "sync_moment" in pipeline_parallel_config
if pp_sync_param:
logger.info("setting pp sync_param")
strategy.hybrid_configs["pp_configs"].sync_param = True
if pp_sync_moment:
logger.info("setting pp sync_moment")
strategy.hybrid_configs["pp_configs"].sync_moment = True
if dygraph_pp_configs["dp_comm_overlap"]:
raise ValueError("overlap has accuracy issue") # TODO: fix `overalap` + `delay_scale` issue
@@ -1509,17 +1359,6 @@ class TrainingArguments:
logger.warning("segment parallel is not supported!!!, Ignore it.")
return support_sep
def is_context_parallel_supported():
import inspect
members = [
name for (name, date) in inspect.getmembers(fleet.base.topology.EPHybridCommunicateGroup)
]
support_cp = "get_context_parallel_world_size" in members
if not support_cp:
logger.warning("context parallel is not supported!!! Ignore it.")
return support_cp
if self.hybrid_parallel_topo_order == "pp_first":
if is_segment_parallel_supported():
order = ["dp", "pp", "sharding", "sep", "mp"]
@@ -1531,25 +1370,9 @@ class TrainingArguments:
else:
order = ["dp", "sharding", "pp", "mp"]
if self.use_expert_parallel:
if self.moe_sharding_parallel_degree >= 1 and self.expert_parallel_degree > 1:
if is_context_parallel_supported():
order = ["sharding", "moe_sharding", "pp", "sep", "cp", "dp", "ep", "mp"]
else:
order = ["sharding", "moe_sharding", "pp", "sep", "dp", "ep", "mp"]
else:
order = ["sharding", "pp", "sep", "dp", "mp"]
order = order[1:-1] + ["dp", "mp"]
if is_context_parallel_supported():
hybrid_configs = {
"dp_degree": self.data_parallel_degree,
"mp_degree": self.tensor_parallel_degree,
"pp_degree": self.pipeline_parallel_degree,
"sharding_degree": self.sharding_parallel_degree,
"sep_degree": self.sep_parallel_degree,
"cp_degree": self.context_parallel_degree,
"order": order,
}
elif is_segment_parallel_supported():
if is_segment_parallel_supported():
hybrid_configs = {
"dp_degree": self.data_parallel_degree,
"mp_degree": self.tensor_parallel_degree,
@@ -1569,13 +1392,6 @@ class TrainingArguments:
"order": order,
}
if self.expert_parallel_degree > 1:
assert (
self.use_expert_parallel is True and self.moe_sharding_parallel_degree >= 0
), f"invalid expert_parallel_degree {self.expert_parallel_degree} and use_expert_paralle:{self.use_expert_parallel}."
hybrid_configs["ep_degree"] = self.expert_parallel_degree
hybrid_configs["moe_sharding_degree"] = self.moe_sharding_parallel_degree
try:
if self.split_norm_comm:
hybrid_configs["split_norm_comm"] = True
@@ -1629,11 +1445,6 @@ class TrainingArguments:
self.sharding_comm_buffer_size_MB
)
if hasattr(strategy.hybrid_configs["sharding_configs"], "offload_opt_buffer_size"):
strategy.hybrid_configs["sharding_configs"].offload_opt_buffer_size = int(
self.sharding_offload_opt_buffersize_GB
)
if "split_param" in sharding_parallel_config:
strategy.hybrid_configs["sharding_configs"].split_param = True
assert self.amp_master_grad, "Currently sharding stage1 v2 only support amp_master_grad"
@@ -1695,6 +1506,12 @@ class TrainingArguments:
assert (
"split_param" in sharding_parallel_config
), "split_param should be set when enable_stage1_allgather_overlap."
use_casual_mask = os.getenv("USE_CASUAL_MASK", "False")
assert use_casual_mask, "enable_stage1_allgather_overlap requires USE_CASUAL_MASK=True."
assert self.logging_steps > 1, (
"The logging_steps should be greater than 1 for enable_stage1_allgather_overlap, "
f"but got logging_steps={self.logging_steps}."
)
if "split_param" in sharding_parallel_config:
if ShardingOption.SHARD_OP not in self.sharding:
@@ -1703,12 +1520,12 @@ class TrainingArguments:
self.amp_master_grad
), "If `split_param` in sharding_parallel_config, `amp_master_grad` must be True."
if self.nccl_comm_group_config is not None:
strategy = init_nccl_config(self.nccl_comm_group_config, strategy)
fleet.init(is_collective=True, strategy=strategy)
logger.info(strategy)
if self.expert_parallel_degree > 1:
self.add_moe_comm_group()
elif self.enable_auto_parallel:
self.tensor_parallel_degree = max(self.tensor_parallel_degree, 1)
self.sep_parallel_degree = max(self.sep_parallel_degree, 1)
@@ -1722,7 +1539,10 @@ class TrainingArguments:
if self.sharding_parallel_degree == -1:
if len(self.sharding) > 0:
self.sharding_parallel_degree = world_size // (
self.tensor_parallel_degree * self.sep_parallel_degree * self.pipeline_parallel_degree
self.tensor_parallel_degree
* self.sep_parallel_degree
* self.context_parallel_degree
* self.pipeline_parallel_degree
)
self.sharding_parallel_degree = max(self.sharding_parallel_degree, 1)
@@ -1902,25 +1722,12 @@ class TrainingArguments:
amp.custom_white_list = self.amp_custom_white_list if self.amp_custom_white_list is not None else []
self.strategy = strategy
sep_degree = self.sep_parallel_degree if self.sep_parallel_degree > 1 else self.context_parallel_degree
if self.hybrid_parallel_topo_order == "pp_first":
order = ["pp", "dp", "mp"]
degree = [
self.pipeline_parallel_degree,
self.dataset_world_size,
self.tensor_parallel_degree,
]
degree = [self.pipeline_parallel_degree, self.dataset_world_size, self.tensor_parallel_degree]
elif self.hybrid_parallel_topo_order == "sharding_first":
order = ["dp", "pp", "mp"]
degree = [
self.dataset_world_size,
self.pipeline_parallel_degree,
self.tensor_parallel_degree,
]
if sep_degree > 1:
order.insert(-1, "sep")
degree.insert(-1, sep_degree)
degree = [self.dataset_world_size, self.pipeline_parallel_degree, self.tensor_parallel_degree]
mesh_dims = list(zip(order, degree))
fleet.auto.create_mesh(mesh_dims)
@@ -1939,7 +1746,6 @@ class TrainingArguments:
"dp_degree": self.dataset_world_size,
"mp_degree": self.tensor_parallel_degree,
"pp_degree": self.pipeline_parallel_degree,
"sep_degree": sep_degree,
"order": order,
}
fleet.init(is_collective=True, strategy=strategy)
@@ -1947,10 +1753,7 @@ class TrainingArguments:
else:
if world_size > 1:
if not paddle.distributed.parallel.parallel_helper._is_parallel_ctx_initialized():
if self.save_checkpoint_format in [
"unified_checkpoint",
"flex_checkpoint",
] or self.load_checkpoint_format in ["unified_checkpoint", "flex_checkpoint"]:
if self.unified_checkpoint:
# DP use hybrid group
strategy = fleet.DistributedStrategy()
fleet.init(is_collective=True, strategy=strategy)
@@ -1958,20 +1761,16 @@ class TrainingArguments:
paddle.distributed.init_parallel_env()
if (
(
self.save_checkpoint_format == "unified_checkpoint"
or self.load_checkpoint_format == "unified_checkpoint"
)
self.unified_checkpoint
and self.sharding_parallel_degree > 0
and ShardingOption.FULL_SHARD in self.sharding
):
logger.warning(
"Unified checkpoint currently do not support sharding stage3, disabling unified_checkpoint format."
"Unified checkpoint currently do not support sharding stage3, set `unified_checkpoint` to False."
)
self.save_checkpoint_format = None
self.load_checkpoint_format = None
self.unified_checkpoint = False
if self.save_checkpoint_format == "unified_checkpoint" or self.load_checkpoint_format == "unified_checkpoint":
if self.unified_checkpoint:
unified_checkpoint_config = set(self.unified_checkpoint_config.split(" "))
if sys.platform.startswith("win") and "async_save" in self.unified_checkpoint_config:
raise ValueError("Currently do not support asynchronous saving for Windows system!")
@@ -2095,7 +1894,7 @@ class TrainingArguments:
assert (
self.save_steps % self.zcc_ema_interval == 0
), f"save_steps[{self.save_steps}] must be divisible by zcc_ema_interval[{self.zcc_ema_interval}]"
if self.enable_zero_cost_checkpoint and self.zcc_save_ema_coef is not None:
if self.zcc_save_ema_coef is not None:
assert (
self.zcc_workers_num == 1
), "EMA function in zero cost checkpoint mode does not support zcc_workers_num > 1 for now."
@@ -2154,7 +1953,10 @@ class TrainingArguments:
if self.sharding_parallel_degree == -1:
if len(self.sharding) > 0:
self.sharding_parallel_degree = world_size // (
tensor_parallel_degree * sep_parallel_degree * pipeline_parallel_degree
tensor_parallel_degree
* sep_parallel_degree
* context_parallel_degree
* pipeline_parallel_degree
)
sharding_parallel_degree = max(self.sharding_parallel_degree, 1)
@@ -2162,28 +1964,19 @@ class TrainingArguments:
logger.warning("sharding_parallel_degree=1 means no sharding, please set sharding to empty!")
self.sharding = []
if sharding_parallel_degree > 1:
assert (
sharding_parallel_degree % expert_parallel_degree == 0
), f"sharding_parallel_degree should be divided by expert_parallel_degree, current sharding_parallel_degree: {sharding_parallel_degree}, expert_parallel_degree: {expert_parallel_degree}."
self.data_parallel_degree = world_size // (
sharding_parallel_degree * tensor_parallel_degree * sep_parallel_degree * pipeline_parallel_degree
sharding_parallel_degree
* tensor_parallel_degree
* sep_parallel_degree
* context_parallel_degree
* pipeline_parallel_degree
)
if expert_parallel_degree > 1:
assert (
self.expert_tensor_parallel_degree <= 1
), "expert_tensor_parallel_degree > 1 is not supported when expert_parallel_degree > 1"
moe_sharding_parallel_degree = world_size // (pipeline_parallel_degree * expert_parallel_degree)
else:
moe_sharding_parallel_degree = 1
moe_sharding_parallel_degree = max(moe_sharding_parallel_degree, 1)
if moe_sharding_parallel_degree > 1 and self.data_parallel_degree > 1:
raise NotImplementedError(
f"Currently only support use expert_data_parallel strategy together with sharding_parallel strategy, but not with data_parallel strategy. But got data_parallel_degree: {self.data_parallel_degree}, expert_parallel_degree: {expert_parallel_degree}, moe_sharding_parallel_degree: {moe_sharding_parallel_degree}."
)
if sharding_parallel_degree > 1 and moe_sharding_parallel_degree > 1:
assert (
sharding_parallel_degree % moe_sharding_parallel_degree == 0
), f"sharding_parallel_degree should be divided by moe_sharding_parallel_degree, current sharding_parallel_degree: {sharding_parallel_degree}, moe_sharding_parallel_degree: {moe_sharding_parallel_degree}."
assert not (
self.data_parallel_degree > 1 and expert_parallel_degree > 1
), f"Currently only support use expert_data_parallel strategy together with sharding_parallel strategy, but not with data_parallel strategy. Currently data_parallel_degree is {self.data_parallel_degree}."
@@ -2205,7 +1998,6 @@ class TrainingArguments:
self.context_parallel_degree = context_parallel_degree
self.expert_parallel_degree = expert_parallel_degree
self.expert_tensor_parallel_degree = expert_tensor_parallel_degree
self.moe_sharding_parallel_degree = moe_sharding_parallel_degree
if not self.use_hybrid_parallel:
self.sharding = []
@@ -2218,38 +2010,13 @@ class TrainingArguments:
self.expert_tensor_parallel_degree = -1
if self.hybrid_parallel_topo_order is None:
self.hybrid_parallel_topo_order = "sharding_first"
self.hybrid_parallel_topo_order = "pp_first"
assert self.hybrid_parallel_topo_order in ["pp_first", "sharding_first"]
if self.use_hybrid_parallel and self.enable_auto_parallel:
self.use_hybrid_parallel = False
def _post_init_save_checkpoint_format(self):
if self.save_checkpoint_format:
valid_modes = ["unified_checkpoint", "sharding_io", "flex_checkpoint"]
assert (
self.save_checkpoint_format in valid_modes
), f"Invalid save_checkpoint_format: {self.save_checkpoint_format}, Only these formats are allowed: {valid_modes}."
else:
if self.unified_checkpoint:
self.save_checkpoint_format = "unified_checkpoint"
elif self.save_sharded_model:
self.save_checkpoint_format = "sharding_io"
def _post_init_load_checkpoint_format(self):
if self.load_checkpoint_format:
valid_modes = ["unified_checkpoint", "sharding_io", "flex_checkpoint"]
assert (
self.load_checkpoint_format in valid_modes
), f"Invalid load_checkpoint_format: {self.load_checkpoint_format}, Only these formats are allowed: {valid_modes}."
else:
if self.unified_checkpoint:
self.load_checkpoint_format = "unified_checkpoint"
elif self.load_sharded_model:
self.load_checkpoint_format = "sharding_io"
def add_moe_comm_group(self):
hybrid_configs = fleet.fleet._user_defined_strategy.hybrid_configs
hcg = fleet.get_hybrid_communicate_group()
topo = hcg._topo
sharding_parallel_groups = topo.get_comm_list("sharding")
@@ -2261,12 +2028,7 @@ class TrainingArguments:
for i in range(experts_replicas):
rank_indices = list(range(i * self.expert_parallel_degree, (i + 1) * self.expert_parallel_degree))
ranks = [ranks_in_current_sharding_group[i] for i in rank_indices]
if message2nccl_config is not None and hybrid_configs.get("ep_configs", None) is not None:
group = dist.new_group(
ranks=ranks, nccl_config=message2nccl_config(hybrid_configs["ep_configs"].nccl_config, "ep")
)
else:
group = dist.new_group(ranks=ranks)
group = dist.new_group(ranks=ranks)
if dist.get_rank() in ranks:
assert not hasattr(hcg, "expert_parallel_group"), "expert_parallel_group can not be set repeate"
setattr(hcg, "expert_parallel_group", group)
@@ -2275,13 +2037,7 @@ class TrainingArguments:
for i in range(self.expert_parallel_degree):
rank_indices = list(range(i, self.sharding_parallel_degree, self.expert_parallel_degree))
ranks = [ranks_in_current_sharding_group[i] for i in rank_indices]
if message2nccl_config is not None and hybrid_configs.get("ep_configs", None) is not None:
group = dist.new_group(
ranks=ranks,
nccl_config=message2nccl_config(hybrid_configs["ep_configs"].grad_nccl_config, "ep_grad"),
)
else:
group = dist.new_group(ranks=ranks)
group = dist.new_group(ranks=ranks)
if dist.get_rank() in ranks:
assert not hasattr(hcg, "expert_grad_comm_group"), "expert_grad_comm_group can not be set repeate"
setattr(hcg, "expert_grad_comm_group", group)
@@ -2373,17 +2129,6 @@ class TrainingArguments:
else:
return 0
@property
def moe_sharding_parallel_rank(self):
if self.use_hybrid_parallel:
hcg = fleet.get_hybrid_communicate_group()
if hasattr(hcg, "get_moe_sharding_parallel_group"):
return max(hcg.get_moe_sharding_parallel_group().rank, 0)
else:
return 0
else:
return 0
@property
def tensor_parallel_rank(self):
if self.use_hybrid_parallel:
@@ -2408,28 +2153,6 @@ class TrainingArguments:
else:
return 0
@property
def expert_parallel_rank(self):
if self.use_hybrid_parallel:
hcg = fleet.get_hybrid_communicate_group()
if hasattr(hcg, "get_expert_parallel_rank"):
return max(hcg.get_expert_parallel_rank(), 0)
else:
return 0
else:
return 0
@property
def context_parallel_rank(self):
if self.use_hybrid_parallel:
hcg = fleet.get_hybrid_communicate_group()
if hasattr(hcg, "get_context_parallel_rank"):
return max(hcg.get_context_parallel_rank(), 0)
else:
return 0
else:
return 0
def _format_name(self, prefix, rank, degree):
size = 2
return f"{prefix}{rank:0>{size}d}"
@@ -2444,7 +2167,7 @@ class TrainingArguments:
name.append(self._format_name("pp", self.pipeline_parallel_rank, self.pipeline_parallel_degree))
if self.sharding_parallel_degree > 1:
name.append(self._format_name("shard", self.sharding_parallel_rank, self.sharding_parallel_degree))
if self.use_expert_parallel and self.expert_parallel_degree <= 1:
if self.use_expert_parallel:
name.append(self._format_name("moe", self.data_parallel_rank, self.data_parallel_degree))
return "_".join(name)
else:
@@ -2460,10 +2183,8 @@ class TrainingArguments:
name.append(self._format_name("tp", self.tensor_parallel_rank, self.tensor_parallel_degree))
if self.pipeline_parallel_degree > 1:
name.append(self._format_name("pp", self.pipeline_parallel_rank, self.pipeline_parallel_degree))
if self.use_expert_parallel and self.expert_parallel_degree <= 1:
if self.use_expert_parallel:
name.append(self._format_name("moe", self.data_parallel_rank, self.data_parallel_degree))
if self.use_expert_parallel and self.expert_parallel_degree > 1:
name.append(self._format_name("moe_sharding", self.expert_parallel_rank, self.expert_parallel_degree))
return "_".join(name)
else:
@@ -2471,9 +2192,7 @@ class TrainingArguments:
return self._format_name("moe", self.data_parallel_rank, self.data_parallel_degree)
return None
def sharded_name_suffix(self, shard_id=None, pp_id=None, moe_id=None, sharding_parallel_degree=None):
if sharding_parallel_degree is None:
sharding_parallel_degree = self.sharding_parallel_degree
def sharded_name_suffix(self, shard_id=None, pp_id=None, moe_id=None):
if self.use_hybrid_parallel:
name = []
if self.tensor_parallel_degree > 1:
@@ -2483,12 +2202,12 @@ class TrainingArguments:
pp_id = self.pipeline_parallel_rank
assert isinstance(pp_id, int)
name.append(self._format_name("pp", pp_id, self.pipeline_parallel_degree))
if sharding_parallel_degree > 1:
if self.sharding_parallel_degree > 1:
if shard_id is None:
shard_id = self.sharding_parallel_rank
assert isinstance(shard_id, int)
name.append(self._format_name("shard", shard_id, sharding_parallel_degree))
if self.use_expert_parallel and self.expert_parallel_degree <= 1:
name.append(self._format_name("shard", shard_id, self.sharding_parallel_degree))
if self.use_expert_parallel:
if moe_id is None:
moe_id = self.data_parallel_rank
assert isinstance(moe_id, int)
@@ -2589,13 +2308,9 @@ class TrainingArguments:
return True
elif self.enable_auto_parallel:
return True
elif self.save_checkpoint_format == "flex_checkpoint":
return False
elif self.use_hybrid_parallel:
# save on dataset rank 0
return (
self.sharding_parallel_rank == 0 and (self.data_parallel_rank == 0 or self.use_expert_parallel)
) or (self.expert_parallel_degree > 1 and self.moe_sharding_parallel_rank == 0)
return self.sharding_parallel_rank == 0 and (self.data_parallel_rank == 0 or self.use_expert_parallel)
else:
return self.process_index == 0 or self.use_expert_parallel
@@ -2611,16 +2326,16 @@ class TrainingArguments:
if self.enable_auto_parallel:
return False
return (
ShardingOption.SHARD_OP in self.sharding
and self.sharding_parallel_degree > 1
and self.save_checkpoint_format == "sharding_io"
ShardingOption.SHARD_OP in self.sharding and self.sharding_parallel_degree > 1 and self.save_sharded_model
)
@property
def should_load_sharding_stage1_model(self):
if self.enable_auto_parallel:
return False
return self.load_checkpoint_format == "sharding_io"
return (
ShardingOption.SHARD_OP in self.sharding and self.sharding_parallel_degree > 1 and self.load_sharded_model
)
@property
def should_load_dataset(self):

View File

@@ -233,7 +233,7 @@ def load_unified_optimizer_locally(args, model, optimizer, resume_from_checkpoin
)
else:
tp_actions = model.get_tensor_parallel_convert_actions(
model.config, model_keys, ignore_error=True, is_optim=True
model.config, model_keys, ignore_error=True
)
if not is_master_weights:
tp_actions = mapping_optimizer_tp_actions(tp_actions, expected_keys)

View File

@@ -37,7 +37,6 @@ dtype_mapping = {
paddle.int32: np.int32,
paddle.int64: np.int64,
paddle.uint8: np.uint8,
paddle.int8: np.int8,
paddle.bool: np.bool_,
paddle.float16: np.float16,
paddle.bfloat16: np.uint16,

View File

@@ -648,7 +648,7 @@ def unified_optimizer_into_shards(
tp_actions = model._get_tensor_parallel_convert_actions(model_keys, is_split=False, ignore_error=True)
else:
tp_actions = model.get_tensor_parallel_convert_actions(
model.config, model_keys, is_split=False, ignore_error=True, is_optim=True
model.config, model_keys, is_split=False, ignore_error=True
)
logger.info("Unified optimizer tensor parallel in shards")
optim_state_dict = merge_tensor_parallel_for_optimizer(

View File

@@ -26,7 +26,6 @@ from paddlenlp.trainer.trainer_utils import ExplicitEnum, ShardingOption
from paddlenlp.trainer.utils.helper import distributed_isfile
from paddlenlp.transformers.model_utils import (
PretrainedModel,
clean_model_class_name,
get_parameter_dtype,
unwrap_model,
)
@@ -766,9 +765,9 @@ def save_model_config(model_to_save, save_directory):
config_to_save = save_config(model_to_save)
# Attach architecture to the config
if isinstance(model_to_save, LoRAModel) or isinstance(model_to_save, PrefixModelForCausalLM):
config_to_save.architectures = [clean_model_class_name(model_to_save.model.__class__.__name__)]
config_to_save.architectures = [model_to_save.model.__class__.__name__]
else:
config_to_save.architectures = [clean_model_class_name(model_to_save.__class__.__name__)]
config_to_save.architectures = [model_to_save.__class__.__name__]
config_to_save.save_pretrained(save_directory)
# save generation config

Some files were not shown because too many files have changed in this diff Show More