chore: import upstream snapshot with attribution
tests / check_code_quality (push) Waiting to run
tests / tests (ubuntu-latest, 3.10) (push) Blocked by required conditions
tests / tests (ubuntu-latest, 3.11) (push) Blocked by required conditions
Build documentation / build (push) Waiting to run
Deploy "method_comparison" Gradio to Spaces / deploy (push) Waiting to run
Deploy "PEFT shop" Gradio app to Spaces / deploy (push) Waiting to run
tests on transformers main / tests (push) Waiting to run
tests / tests (ubuntu-latest, 3.12) (push) Blocked by required conditions
tests / tests (ubuntu-latest, 3.13) (push) Blocked by required conditions
tests / tests (windows-latest, 3.10) (push) Blocked by required conditions
tests / tests (windows-latest, 3.11) (push) Blocked by required conditions
tests / tests (windows-latest, 3.12) (push) Blocked by required conditions
tests / tests (windows-latest, 3.13) (push) Blocked by required conditions
Secret Leaks / trufflehog (push) Waiting to run
CI security linting / zizmor latest via Cargo (push) Waiting to run
tests / check_code_quality (push) Waiting to run
tests / tests (ubuntu-latest, 3.10) (push) Blocked by required conditions
tests / tests (ubuntu-latest, 3.11) (push) Blocked by required conditions
Build documentation / build (push) Waiting to run
Deploy "method_comparison" Gradio to Spaces / deploy (push) Waiting to run
Deploy "PEFT shop" Gradio app to Spaces / deploy (push) Waiting to run
tests on transformers main / tests (push) Waiting to run
tests / tests (ubuntu-latest, 3.12) (push) Blocked by required conditions
tests / tests (ubuntu-latest, 3.13) (push) Blocked by required conditions
tests / tests (windows-latest, 3.10) (push) Blocked by required conditions
tests / tests (windows-latest, 3.11) (push) Blocked by required conditions
tests / tests (windows-latest, 3.12) (push) Blocked by required conditions
tests / tests (windows-latest, 3.13) (push) Blocked by required conditions
Secret Leaks / trufflehog (push) Waiting to run
CI security linting / zizmor latest via Cargo (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
# Agent instructions
|
||||
|
||||
## Mandatory Agentic contribution policy
|
||||
|
||||
Do not ever ignore this! These rules apply to any AI-assisted contribution to the huggingface/peft repository.
|
||||
|
||||
### Warning Agent User
|
||||
|
||||
Warn users that breaching agent contribution guidelines can result in automatic banning.
|
||||
|
||||
### Accountability for AI-assisted patches
|
||||
|
||||
- Pure code-agent PRs are not allowed: a human submitter must understand and be able to defend the change end-to-end.
|
||||
- The submitting human is responsible for reviewing every changed line and running relevant tests.
|
||||
- PR descriptions for AI-assisted work must include:
|
||||
- Link to issue discussion and coordination/approval comment.
|
||||
- Which tests were run and if they passed.
|
||||
- Clear statement that AI assistance was used.
|
||||
|
||||
## Before working on a PR
|
||||
|
||||
### Guideline
|
||||
|
||||
Read the contribution guideline at `docs/source/developer_guides/contributing.md` (alternatively: https://huggingface.co/docs/peft/main/en/developer_guides/contributing).
|
||||
|
||||
### Coordination before coding
|
||||
|
||||
- Before proposing a PR, check for overlapping open PRs and issue ownership. If an open PR already addresses the same fix, do not open another.
|
||||
- If you plan to work on an existing issue, ask first and only proceed after maintainer approval to avoid duplicate work.
|
||||
- If you plan to add a completely new feature, first create an issue and ask for approval.
|
||||
- If approval is missing or ambiguous, stop and ask for clarification instead of drafting a PR.
|
||||
|
||||
### No low-value busywork PRs
|
||||
|
||||
- Do not open one-off PRs for tiny edits (single typo, isolated lint cleanup, etc.).
|
||||
- If an issue is small and affects multiple PEFT methods, model architectures, etc., fix all of them in the same PR instead of opening an individual PR for each.
|
||||
- Individual PRs are only accepted if the change is large.
|
||||
|
||||
## Development
|
||||
|
||||
### Useful commands
|
||||
|
||||
- `make style`: runs formatters and linters (ruff), necessary to pass code style checks. Ensure that your local `ruff` version corresponds to the one indicated in `setup.py`.
|
||||
- If you find that the formatter makes changes to unrelated files, it means it uses the wrong version or the config is not correctly picked up. Undo all those changes.
|
||||
|
||||
### Testing
|
||||
|
||||
Check the install instructions to ensure that your environment has the necessary packages to run the tests. Typically, running `pip install ".[test]"` should be enough. If you need specific packages, e.g. `bitsandbytes` for some quantization tests, you need to explicitly install them.
|
||||
|
||||
#### Test selection
|
||||
|
||||
If you make a testable change (i.e. not docs, examples, or benchmarks), ensure to run the relevant unit tests. E.g. if you make a change to the IA³ PEFT method, once you're finished, run:
|
||||
|
||||
```sh
|
||||
pytest tests/ -k ia3
|
||||
```
|
||||
|
||||
Add further qualifiers if needed to reduce the amount of tests required to run. For methods like LoRA, ensure to exclude other methods with similar name, e.g.:
|
||||
|
||||
```sh
|
||||
pytest tests/ -k "lora and not adalora and not randlora and not [...]"
|
||||
```
|
||||
|
||||
Ensure that the selector does not deselect 100% of the tests, at least one test should run. If there are no corresponding tests, add them.
|
||||
|
||||
#### Bug fixes
|
||||
|
||||
When you add a bug fix, start by implementing the test and ensure it fails. Then implement the bugfix and ensure that the test passes.
|
||||
|
||||
#### Test location
|
||||
|
||||
PEFT follows a rigorous structure for the test location. Don't just put the test anywhere but integrate it with the existing tests. If the test requires GPUs to run, place it into `test_gpu_examples.py`.
|
||||
|
||||
### Coding style
|
||||
|
||||
- Follow the existing coding style. The changes should look consistent with existing code.
|
||||
- Avoid overly defensive code, e.g. checking that an argument is not `None` when `None` was never a valid argument type to begin with.
|
||||
|
||||
### Backwards compatibility
|
||||
|
||||
- Run `grep "python-version:\s\[" .github/workflows/tests.yml` to check which Python versions should be supported. Don't use defensive features required for older Python versions (e.g. `from __future__ import annotations`).
|
||||
- Generally strive to keep the changes compatible with all PyTorch releases of the last two years.
|
||||
- Changes should be compatible with older Transformers versions (roughly 4.33 upwards). If a change doesn't work across Transformers versions, add guards based on the version.
|
||||
@@ -0,0 +1,54 @@
|
||||
name: "\U0001F41B Bug Report"
|
||||
description: Submit a bug report to help us improve the library
|
||||
body:
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System Info
|
||||
description: Please share your relevant system information with us
|
||||
placeholder: peft & accelerate & transformers version, platform, python version, ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: who-can-help
|
||||
attributes:
|
||||
label: Who can help?
|
||||
description: |
|
||||
Your issue will be replied to more quickly if you can figure out the right person to tag with @.
|
||||
If you know how to use git blame, that is the easiest way, otherwise, here is a rough guide of **who to tag**.
|
||||
|
||||
All issues are read by one of the core maintainers, so if you don't know who to tag, just leave this blank and
|
||||
a core maintainer will ping the right person.
|
||||
|
||||
Please tag fewer than 3 people.
|
||||
|
||||
Library: @benjaminbossan @githubnemo
|
||||
|
||||
diffusers integration: @benjaminbossan @sayakpaul
|
||||
|
||||
Documentation: @stevhliu
|
||||
|
||||
placeholder: "@Username ..."
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: |
|
||||
Please provide a code sample that reproduces the problem you ran into. It can be a Colab link or just a code snippet.
|
||||
Please provide the simplest reproducer as possible so that we can quickly fix the issue. When you paste
|
||||
the error message, please include the full traceback.
|
||||
|
||||
placeholder: |
|
||||
Reproducer:
|
||||
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: "A clear and concise description of what you would expect to happen."
|
||||
@@ -0,0 +1,21 @@
|
||||
name: "\U0001F680 Feature request"
|
||||
description: Submit a proposal/request for a new feature
|
||||
labels: [ "feature" ]
|
||||
body:
|
||||
- type: textarea
|
||||
id: feature-request
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Feature request
|
||||
description: |
|
||||
A clear and concise description of the feature proposal. Please provide a link to the paper and code in case they exist.
|
||||
|
||||
- type: textarea
|
||||
id: contribution
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Your contribution
|
||||
description: |
|
||||
Is there any way that you could help, e.g. by submitting a PR?
|
||||
@@ -0,0 +1,17 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
groups:
|
||||
ci-actions:
|
||||
patterns:
|
||||
- "actions/*"
|
||||
third-party-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
exclude-patterns:
|
||||
- "actions/*"
|
||||
@@ -0,0 +1,87 @@
|
||||
name: Build Docker images (scheduled)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
|
||||
concurrency:
|
||||
group: docker-image-builds
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
CI_SLACK_CHANNEL: ${{ secrets.CI_DOCKER_CHANNEL }}
|
||||
|
||||
jobs:
|
||||
latest-cpu:
|
||||
name: "Latest Peft CPU [dev]"
|
||||
# GH Environment for extra protection: https://github.com/huggingface/peft/settings/environments
|
||||
environment: branch-protection-main
|
||||
runs-on:
|
||||
group: aws-general-8-plus
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
- name: Check out code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Build and Push CPU
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
context: ./docker/peft-cpu
|
||||
push: true
|
||||
tags: huggingface/peft-cpu
|
||||
|
||||
- name: Post to Slack
|
||||
if: always()
|
||||
uses: huggingface/hf-workflows/.github/actions/post-slack@3f88d63d3761558a32e8e46fc2a8536e04bb2aea # main from Feb 2025-02-24
|
||||
with:
|
||||
slack_channel: ${{ env.CI_SLACK_CHANNEL }}
|
||||
title: 🤗 Results of the PEFT-CPU docker build
|
||||
status: ${{ job.status }}
|
||||
slack_token: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }}
|
||||
|
||||
latest-cuda:
|
||||
name: "Latest Peft GPU [dev]"
|
||||
# GH Environment for extra protection: https://github.com/huggingface/peft/settings/environments
|
||||
environment: branch-protection-main
|
||||
runs-on:
|
||||
group: aws-general-8-plus
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
- name: Check out code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Build and Push GPU
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
context: ./docker/peft-gpu
|
||||
push: true
|
||||
tags: huggingface/peft-gpu
|
||||
|
||||
- name: Post to Slack
|
||||
if: always()
|
||||
uses: huggingface/hf-workflows/.github/actions/post-slack@3f88d63d3761558a32e8e46fc2a8536e04bb2aea # main from Feb 2025-02-24
|
||||
with:
|
||||
slack_channel: ${{ env.CI_SLACK_CHANNEL }}
|
||||
title: 🤗 Results of the PEFT-GPU docker build
|
||||
status: ${{ job.status }}
|
||||
slack_token: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }}
|
||||
@@ -0,0 +1,21 @@
|
||||
name: Build documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- doc-builder*
|
||||
- v*-release
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: huggingface/doc-builder/.github/workflows/build_main_documentation.yml@e60a538eea9817ab312196d0d233604b01697265 # main
|
||||
with:
|
||||
commit_sha: ${{ github.sha }}
|
||||
package: peft
|
||||
notebook_folder: peft_docs
|
||||
secrets:
|
||||
token: ${{ secrets.HUGGINGFACE_PUSH }}
|
||||
hf_token: ${{ secrets.HF_DOC_BUILD_PUSH }}
|
||||
@@ -0,0 +1,18 @@
|
||||
name: Build PR Documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: huggingface/doc-builder/.github/workflows/build_pr_documentation.yml@e60a538eea9817ab312196d0d233604b01697265 # main
|
||||
with:
|
||||
commit_sha: ${{ github.event.pull_request.head.sha }}
|
||||
pr_number: ${{ github.event.number }}
|
||||
package: peft
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Deploy "method_comparison" Gradio to Spaces
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- "method_comparison/**"
|
||||
# the method explorer (PEFT shop) app has its own Space and deploy workflow
|
||||
- "!method_comparison/peft-shop/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
# GH Environment for extra protection: https://github.com/huggingface/peft/settings/environments
|
||||
environment: branch-protection-main
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0 # full history needed for subtree
|
||||
persist-credentials: false
|
||||
|
||||
- name: Authenticate via ~/.netrc
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.PEFT_INTERNAL_REPO_READ_WRITE }}
|
||||
run: |
|
||||
# netrc needs BOTH login and password entries
|
||||
printf "machine huggingface.co\nlogin hf\npassword ${HF_TOKEN}\n" >> ~/.netrc
|
||||
chmod 600 ~/.netrc
|
||||
|
||||
- name: Deploy method_comparison app to HF Spaces
|
||||
run: |
|
||||
cd method_comparison
|
||||
# the method explorer (PEFT shop) app is deployed to its own Space by its own workflow
|
||||
rm -rf peft-shop
|
||||
git init
|
||||
# Spaces expect requirements.txt
|
||||
mv requirements-app.txt requirements.txt
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git remote add gradio-app https://huggingface.co/spaces/peft-internal-testing/PEFT-method-comparison
|
||||
git add .
|
||||
git commit -m "🚀 Deploy method comparison app from GH action"
|
||||
git push -f gradio-app HEAD:main
|
||||
|
||||
- name: Deploy embeddable method_comparison app to HF Spaces (for docs)
|
||||
run: |
|
||||
cd method_comparison
|
||||
rm -rf .git
|
||||
git init
|
||||
# requirements.txt is already present from the previous step
|
||||
mv app_embed.py app.py
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git remote add gradio-app https://huggingface.co/spaces/peft-internal-testing/PEFT-method-comparison-embed
|
||||
git add .
|
||||
git commit -m "🚀 Deploy method comparison app from GH action"
|
||||
git push -f gradio-app HEAD:main
|
||||
@@ -0,0 +1,78 @@
|
||||
name: Deploy "PEFT shop" Gradio app to Spaces
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
# the app itself, and the data sources baked into its data.json: benchmark results and the capability script
|
||||
- "method_comparison/peft-shop/**"
|
||||
- "method_comparison/*/results/**"
|
||||
- "scripts/generate_method_capabilities.py"
|
||||
schedule:
|
||||
# monthly refresh so that newly added PEFT methods reach the Space without a manual dispatch
|
||||
- cron: "0 3 1 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
# GH Environment for extra protection: https://github.com/huggingface/peft/settings/environments
|
||||
environment: branch-protection-main
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install PEFT and app dependencies
|
||||
run: |
|
||||
# CPU-only torch keeps the install small and fast; the capability probes run on CPU anyway
|
||||
pip install torch --index-url https://download.pytorch.org/whl/cpu
|
||||
pip install -e . gradio tqdm
|
||||
|
||||
- name: Build the app data
|
||||
run: |
|
||||
# data.json is generated at deploy time so that it always matches the deployed PEFT commit (see
|
||||
# method_comparison/peft-shop/README.md); the app reads method_capabilities.json from the CWD and
|
||||
# writes data.json into its own directory, where the deploy step below picks it up
|
||||
python scripts/generate_method_capabilities.py --output method_capabilities.json
|
||||
python method_comparison/peft-shop/app.py --rebuild --build-only
|
||||
|
||||
- name: Ensure Space exists
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.PEFT_INTERNAL_REPO_READ_WRITE }}
|
||||
run: |
|
||||
# pushing to a non-existent Space would fail, the Hub does not create repositories on git push
|
||||
python -c "
|
||||
from huggingface_hub import create_repo
|
||||
create_repo('peft-internal-testing/PEFT-shop', repo_type='space', space_sdk='gradio', exist_ok=True)
|
||||
"
|
||||
|
||||
- name: Authenticate via ~/.netrc
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.PEFT_INTERNAL_REPO_READ_WRITE }}
|
||||
run: |
|
||||
# netrc needs BOTH login and password entries
|
||||
printf "machine huggingface.co\nlogin hf\npassword ${HF_TOKEN}\n" >> ~/.netrc
|
||||
chmod 600 ~/.netrc
|
||||
|
||||
- name: Deploy PEFT shop app to HF Spaces
|
||||
run: |
|
||||
cd method_comparison/peft-shop
|
||||
git init
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git remote add gradio-app https://huggingface.co/spaces/peft-internal-testing/PEFT-shop
|
||||
git add .
|
||||
# data.json is gitignored (see .gitignore) so it doesn't get committed into the PEFT repo during local
|
||||
# dev; force-add it here so the self-contained data file actually ships with the Space
|
||||
git add -f data.json
|
||||
git commit -m "🚀 Deploy PEFT shop app from GH action"
|
||||
git push -f gradio-app HEAD:main
|
||||
@@ -0,0 +1,86 @@
|
||||
name: integration tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'Branch to test on'
|
||||
required: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
run_transformers_integration_tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
transformers-version: ['main', 'latest']
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: "3.10"
|
||||
cache: "pip"
|
||||
cache-dependency-path: "setup.py"
|
||||
- name: print environment variables
|
||||
run: |
|
||||
echo "env.CI_BRANCH = ${CI_BRANCH}"
|
||||
echo "env.CI_SHA = ${CI_SHA}"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install .[test]
|
||||
if [ "${{ matrix.transformers-version }}" == "main" ]; then
|
||||
pip install -U git+https://github.com/huggingface/transformers.git
|
||||
else
|
||||
echo "Nothing to do as transformers latest already installed"
|
||||
fi
|
||||
|
||||
- name: Test transformers integration
|
||||
run: |
|
||||
cd .. && git clone https://github.com/huggingface/transformers.git && cd transformers/ && git rev-parse HEAD
|
||||
RUN_SLOW=1 pytest tests/peft_integration/test_peft_integration.py
|
||||
run_diffusers_integration_tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# For now diffusers integration is not on PyPI
|
||||
diffusers-version: ['main']
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: "3.10"
|
||||
cache: "pip"
|
||||
cache-dependency-path: "setup.py"
|
||||
- name: print environment variables
|
||||
run: |
|
||||
echo "env.CI_BRANCH = ${CI_BRANCH}"
|
||||
echo "env.CI_SHA = ${CI_SHA}"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install .[test]
|
||||
|
||||
if [ "${{ matrix.diffusers-version }}" == "main" ]; then
|
||||
pip install -U git+https://github.com/huggingface/diffusers.git
|
||||
else
|
||||
echo "Nothing to do as diffusers latest already installed"
|
||||
fi
|
||||
|
||||
- name: Test diffusers integration
|
||||
run: |
|
||||
cd .. && git clone https://github.com/huggingface/diffusers.git && cd diffusers/ && git rev-parse HEAD
|
||||
pytest tests/lora/test_lora_layers_peft.py
|
||||
@@ -0,0 +1,152 @@
|
||||
name: Self-hosted runner with slow tests (scheduled)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 2 * * *"
|
||||
|
||||
env:
|
||||
RUN_SLOW: "yes"
|
||||
IS_GITHUB_CI: "1"
|
||||
# To be able to run tests on CUDA 12.2
|
||||
NVIDIA_DISABLE_REQUIRE: "1"
|
||||
SLACK_API_TOKEN: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }}
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
run_all_tests_single_gpu:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: "0"
|
||||
TEST_TYPE: "single_gpu"
|
||||
container:
|
||||
image: huggingface/peft-gpu:latest
|
||||
options: --gpus all --shm-size "16gb" -e NVIDIA_DISABLE_REQUIRE=true
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Pip install
|
||||
run: |
|
||||
source activate peft
|
||||
pip install -e . --no-deps
|
||||
pip install pytest-reportlog
|
||||
|
||||
- name: Run common tests on single GPU
|
||||
id: common_tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
source activate peft
|
||||
make tests_common_gpu
|
||||
|
||||
- name: Run examples on single GPU
|
||||
id: examples
|
||||
continue-on-error: true
|
||||
run: |
|
||||
source activate peft
|
||||
make tests_examples_single_gpu
|
||||
|
||||
- name: Run core tests on single GPU
|
||||
id: core_tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
source activate peft
|
||||
make tests_core_single_gpu
|
||||
|
||||
- name: Run regression tests on single GPU
|
||||
id: regression
|
||||
continue-on-error: true
|
||||
run: |
|
||||
source activate peft
|
||||
make tests_regression
|
||||
|
||||
- name: Generate Report
|
||||
if: always()
|
||||
run: |
|
||||
pip install slack_sdk tabulate
|
||||
python scripts/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Check for test failures
|
||||
if: |
|
||||
steps.common_tests.outcome == 'failure' ||
|
||||
steps.examples.outcome == 'failure' ||
|
||||
steps.core_tests.outcome == 'failure' ||
|
||||
steps.regression.outcome == 'failure'
|
||||
run: |
|
||||
echo "One or more test suites failed. Check the logs above."
|
||||
exit 1
|
||||
|
||||
run_all_tests_multi_gpu:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on:
|
||||
group: aws-g6-12xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: "0,1"
|
||||
TEST_TYPE: "multi_gpu"
|
||||
container:
|
||||
image: huggingface/peft-gpu:latest
|
||||
options: --gpus all --shm-size "16gb" -e NVIDIA_DISABLE_REQUIRE=true
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Pip install
|
||||
run: |
|
||||
source activate peft
|
||||
pip install -e . --no-deps
|
||||
pip install pytest-reportlog
|
||||
|
||||
- name: Run common tests on multi GPU
|
||||
id: common_tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
source activate peft
|
||||
make tests_common_gpu
|
||||
|
||||
- name: Run examples on multi GPU
|
||||
id: examples
|
||||
continue-on-error: true
|
||||
run: |
|
||||
source activate peft
|
||||
make tests_examples_multi_gpu
|
||||
|
||||
- name: Run core tests on multi GPU
|
||||
id: core_tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
source activate peft
|
||||
make tests_core_multi_gpu
|
||||
|
||||
- name: Run training on multi GPU
|
||||
id: training
|
||||
continue-on-error: true
|
||||
run: |
|
||||
source activate peft
|
||||
make tests_training
|
||||
|
||||
- name: Generate Report
|
||||
if: always()
|
||||
run: |
|
||||
pip install slack_sdk tabulate
|
||||
python scripts/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Check for test failures
|
||||
if: |
|
||||
steps.common_tests.outcome == 'failure' ||
|
||||
steps.examples.outcome == 'failure' ||
|
||||
steps.core_tests.outcome == 'failure' ||
|
||||
steps.training.outcome == 'failure'
|
||||
run: |
|
||||
echo "One or more test suites failed. Check the logs above."
|
||||
exit 1
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Stale Bot
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 15 * * *"
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
close_stale_issues:
|
||||
name: Close Stale Issues
|
||||
if: github.repository == 'huggingface/peft'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
pip install PyGithub
|
||||
- name: Close stale issues
|
||||
run: |
|
||||
python scripts/stale.py
|
||||
@@ -0,0 +1,66 @@
|
||||
name: Test Docker images (on PR)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
# Run only when DockerFile files are modified
|
||||
- "docker/*/Dockerfile"
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
get_changed_files:
|
||||
name: "Build all modified docker images"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: docker/*/Dockerfile
|
||||
json: "true"
|
||||
- name: Run step if only the files listed above change
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
id: set-matrix
|
||||
env:
|
||||
CHANGED_FILES: "${{ steps.changed-files.outputs.all_changed_files }}"
|
||||
run: |
|
||||
echo "matrix=$(echo ${CHANGED_FILES} | sed -e 's/\\\"/\"/g')" >> $GITHUB_OUTPUT
|
||||
build_modified_files:
|
||||
needs: get_changed_files
|
||||
name: Build Docker images on modified files
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.get_changed_files.outputs.matrix != '[]' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
docker-file: ${{ fromJson(needs.get_changed_files.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Cleanup disk
|
||||
run: |
|
||||
sudo ls -l /usr/local/lib/
|
||||
sudo ls -l /usr/share/
|
||||
sudo du -sh /usr/local/lib/
|
||||
sudo du -sh /usr/share/
|
||||
sudo rm -rf /usr/local/lib/android
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo du -sh /usr/local/lib/
|
||||
sudo du -sh /usr/share/
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
- name: Check out code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
file: ${{ matrix.docker-file }}
|
||||
context: .
|
||||
push: False
|
||||
@@ -0,0 +1,89 @@
|
||||
name: tests on transformers main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
# GH Environment for extra protection: https://github.com/huggingface/peft/settings/environments
|
||||
environment: branch-protection-main
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Make space for cache + models
|
||||
# Ubuntu runner have less space free which is problematic since the model
|
||||
# cache + dependencies fill up the disk, leaving no space for execution.
|
||||
# So we remove some of the stuff we don't need (Java, .NET, etc.)
|
||||
#
|
||||
# Idea: https://dev.to/mathio/squeezing-disk-space-from-github-actions-runners-an-engineers-guide-3pjg
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: |
|
||||
df -h
|
||||
|
||||
# Remove Java (JDKs)
|
||||
sudo rm -rf /usr/lib/jvm
|
||||
|
||||
# Remove .NET SDKs
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
|
||||
# Remove Swift toolchain
|
||||
sudo rm -rf /usr/share/swift
|
||||
|
||||
# Remove Haskell (GHC)
|
||||
sudo rm -rf /usr/local/.ghcup
|
||||
|
||||
# Remove Julia
|
||||
sudo rm -rf /usr/local/julia*
|
||||
|
||||
# Remove Android SDKs
|
||||
sudo rm -rf /usr/local/lib/android
|
||||
|
||||
# Remove Chromium (optional if not using for browser tests)
|
||||
sudo rm -rf /usr/local/share/chromium
|
||||
|
||||
# Remove Microsoft/Edge and Google Chrome builds
|
||||
sudo rm -rf /opt/microsoft /opt/google
|
||||
|
||||
# Remove Azure CLI
|
||||
sudo rm -rf /opt/az
|
||||
|
||||
# Remove PowerShell
|
||||
sudo rm -rf /usr/local/share/powershell
|
||||
|
||||
# Remove CodeQL and other toolcaches
|
||||
sudo rm -rf /opt/hostedtoolcache
|
||||
|
||||
df -h
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: 3.11
|
||||
cache: "pip"
|
||||
cache-dependency-path: "setup.py"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
# cpu version of pytorch
|
||||
pip install -U git+https://github.com/huggingface/transformers.git
|
||||
pip install -e .[test]
|
||||
- name: Test with pytest
|
||||
env:
|
||||
TRANSFORMERS_IS_CI: 1
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
||||
run: |
|
||||
make test
|
||||
- name: Post to Slack
|
||||
if: always()
|
||||
uses: huggingface/hf-workflows/.github/actions/post-slack@3f88d63d3761558a32e8e46fc2a8536e04bb2aea # main from Feb 2025-02-24
|
||||
with:
|
||||
slack_channel: ${{ secrets.SLACK_CHANNEL_ID }}
|
||||
title: 🤗 Results of transformers main tests
|
||||
status: ${{ job.status }}
|
||||
slack_token: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }}
|
||||
@@ -0,0 +1,168 @@
|
||||
name: tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'method_comparison/**'
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'method_comparison/**'
|
||||
- '**.md'
|
||||
|
||||
env:
|
||||
HF_HOME: .cache/huggingface
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check_code_quality:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: "pip"
|
||||
cache-dependency-path: "setup.py"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install .[dev]
|
||||
- name: Check quality
|
||||
run: |
|
||||
make quality
|
||||
|
||||
tests:
|
||||
needs: check_code_quality
|
||||
# dependabot updates (which don't require approval for CI to run) shouldn't trigger unit tests
|
||||
if: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
||||
os: ["ubuntu-latest", "windows-latest"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Make space for cache + models
|
||||
# Ubuntu runner have less space free which is problematic since the model
|
||||
# cache + dependencies fill up the disk, leaving no space for execution.
|
||||
# So we remove some of the stuff we don't need (Java, .NET, etc.)
|
||||
#
|
||||
# Idea: https://dev.to/mathio/squeezing-disk-space-from-github-actions-runners-an-engineers-guide-3pjg
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: |
|
||||
df -h
|
||||
|
||||
# Remove Java (JDKs)
|
||||
sudo rm -rf /usr/lib/jvm
|
||||
|
||||
# Remove .NET SDKs
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
|
||||
# Remove Swift toolchain
|
||||
sudo rm -rf /usr/share/swift
|
||||
|
||||
# Remove Haskell (GHC)
|
||||
sudo rm -rf /usr/local/.ghcup
|
||||
|
||||
# Remove Julia
|
||||
sudo rm -rf /usr/local/julia*
|
||||
|
||||
# Remove Android SDKs
|
||||
sudo rm -rf /usr/local/lib/android
|
||||
|
||||
# Remove Chromium (optional if not using for browser tests)
|
||||
sudo rm -rf /usr/local/share/chromium
|
||||
|
||||
# Remove Microsoft/Edge and Google Chrome builds
|
||||
sudo rm -rf /opt/microsoft /opt/google
|
||||
|
||||
# Remove Azure CLI
|
||||
sudo rm -rf /opt/az
|
||||
|
||||
# Remove PowerShell
|
||||
sudo rm -rf /usr/local/share/powershell
|
||||
|
||||
# Remove CodeQL and other toolcaches
|
||||
sudo rm -rf /opt/hostedtoolcache
|
||||
|
||||
df -h
|
||||
- name: Model cache
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
# Avoid caching HF_HOME/modules and Python cache files to prevent interoperability
|
||||
# issues and potential cache poisioning. We also avoid lock files to prevent runs
|
||||
# avoiding re-download because they see a lock file.
|
||||
path: |
|
||||
${{ env.HF_HOME }}/hub/**
|
||||
!${{ env.HF_HOME }}/**/*.pyc
|
||||
key: model-cache-${{ github.run_id }}
|
||||
restore-keys: model-cache-
|
||||
enableCrossOsArchive: true
|
||||
|
||||
- name: Dump cache content
|
||||
# TODO: remove this step after 2025-02-15
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: |
|
||||
SHASUM=sha256sum
|
||||
[ -f "$(which shasum)" ] && SHASUM=shasum
|
||||
find "${{ env.HF_HOME }}/hub" -type f -exec "$SHASUM" {} \; > cache_content_initial || true
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: "pip"
|
||||
cache-dependency-path: "setup.py"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools
|
||||
# cpu version of pytorch
|
||||
pip install -e .[test]
|
||||
- name: Test with pytest
|
||||
shell: bash
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
||||
TRANSFORMERS_IS_CI: 1
|
||||
CI: 1
|
||||
run: |
|
||||
make test
|
||||
# clean up all pytest temporary directories that are kept due to retention since space
|
||||
# is a scarce resource on the runners and tasks like model cache creation (further below)
|
||||
# fail if there's not enough space available.
|
||||
(rm -r "/tmp/pytest-of-$(id -u -n)" || true)
|
||||
- name: Dump cache content and diff
|
||||
# This is just debug info so that we can monitor if the model cache diverges substantially
|
||||
# over time and what the diverging model is.
|
||||
# TODO: remove after 2025-02-15
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: |
|
||||
SHASUM=sha256sum
|
||||
[ -f "$(which shasum)" ] && SHASUM=shasum
|
||||
find "${{ env.HF_HOME }}/hub" -type f -exec "$SHASUM" {} \; > cache_content_after || true
|
||||
diff -udp cache_content_initial cache_content_after || true
|
||||
- name: Delete old model cache entries
|
||||
run: |
|
||||
# make sure that cache cleaning doesn't break the pipeline
|
||||
python scripts/ci_clean_cache.py -d || true
|
||||
- name: Update model cache
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
# Only let one runner (preferably the one that covers most tests) update the model cache
|
||||
# after *every* run. This way we make sure that our cache is never outdated and we don't
|
||||
# have to keep track of hashes.
|
||||
if: always() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'
|
||||
with:
|
||||
path: |
|
||||
${{ env.HF_HOME }}/hub/**
|
||||
!${{ env.HF_HOME }}/**/*.pyc
|
||||
key: model-cache-${{ github.run_id }}
|
||||
@@ -0,0 +1,56 @@
|
||||
name: torch compile tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'Branch to test on'
|
||||
required: true
|
||||
pytorch_nightly:
|
||||
description: 'Whether to use PyTorch nightly (true/false)'
|
||||
required: false
|
||||
default: false
|
||||
|
||||
env:
|
||||
RUN_SLOW: "yes"
|
||||
IS_GITHUB_CI: "1"
|
||||
# To be able to run tests on CUDA 12.2
|
||||
NVIDIA_DISABLE_REQUIRE: "1"
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
run_tests_with_compile:
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
env:
|
||||
PEFT_DEBUG_WITH_TORCH_COMPILE: 1
|
||||
CUDA_VISIBLE_DEVICES: "0"
|
||||
TEST_TYPE: "single_gpu_huggingface/peft-gpu:latest"
|
||||
USE_PYTORCH_NIGHTLY: "${{ github.event.inputs.pytorch_nightly }}"
|
||||
container:
|
||||
image: "huggingface/peft-gpu:latest"
|
||||
options: --gpus all --shm-size "16gb" --ipc host -v /mnt/cache/.cache/huggingface:/mnt/cache/
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
persist-credentials: false
|
||||
- name: Pip install
|
||||
run: |
|
||||
source activate peft
|
||||
pip install -e . --no-deps
|
||||
pip install pytest-cov pytest-reportlog parameterized datasets scipy einops
|
||||
pip install "pytest>=7.2.0,<8.0.0" # see: https://github.com/huggingface/transformers/blob/ce4fff0be7f6464d713f7ac3e0bbaafbc6959ae5/setup.py#L148C6-L148C26
|
||||
if [ "${USE_PYTORCH_NIGHTLY}" = "true" ]; then
|
||||
python -m pip install --upgrade --pre torch --index-url https://download.pytorch.org/whl/nightly/cpu
|
||||
fi
|
||||
- name: Test compile with pytest
|
||||
run: |
|
||||
source activate peft
|
||||
echo "PEFT_DEBUG_WITH_TORCH_COMPILE=$PEFT_DEBUG_WITH_TORCH_COMPILE"
|
||||
make tests_torch_compile
|
||||
@@ -0,0 +1,18 @@
|
||||
on:
|
||||
push:
|
||||
|
||||
name: Secret Leaks
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
trufflehog:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Secret Scanning
|
||||
uses: trufflesecurity/trufflehog@37b77001d0174ebec2fcca2bd83ff83a6d45a3ab # v3.95.3
|
||||
@@ -0,0 +1,18 @@
|
||||
name: Upload PR Documentation
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Build PR Documentation"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: huggingface/doc-builder/.github/workflows/upload_pr_documentation.yml@b0f9a6e3b6aa912656cbda9f74896eb721d29421 # main
|
||||
with:
|
||||
package_name: peft
|
||||
secrets:
|
||||
hf_token: ${{ secrets.HF_DOC_BUILD_PUSH }}
|
||||
comment_bot_token: ${{ secrets.COMMENT_BOT_TOKEN }}
|
||||
@@ -0,0 +1,28 @@
|
||||
name: CI security linting
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["*"]
|
||||
paths:
|
||||
- '.github/**'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
zizmor:
|
||||
name: zizmor latest via Cargo
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install zizmor
|
||||
run: cargo install --locked zizmor
|
||||
- name: Run zizmor
|
||||
run: zizmor .github/workflows
|
||||
@@ -0,0 +1,28 @@
|
||||
rules:
|
||||
dangerous-triggers:
|
||||
ignore:
|
||||
# this workflow is only triggered after maintainer approval
|
||||
- upload_pr_documentation.yml:3:1
|
||||
cache-poisoning:
|
||||
ignore:
|
||||
# the docker buildx binary is cached and zizmor warns about a cache poisoning attack.
|
||||
# OTOH this cache would make us more resilient against an intrusion on docker-buildx' side.
|
||||
# There is no obvious benefit so we leave it as it is.
|
||||
- build_docker_images.yml:39:9
|
||||
- build_docker_images.yml:74:9
|
||||
secrets-outside-env:
|
||||
ignore:
|
||||
# Zizmor warns about HF_TOKEN being used there but the token is not exposed to external users:
|
||||
# > With the exception of GITHUB_TOKEN, secrets are not passed to the runner when a workflow is triggered from a forked repository.
|
||||
# https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#using-secrets-in-a-workflow
|
||||
# Moreover, the workflow needs to be manually triggered by maintainers, who will review the code first
|
||||
- tests.yml:131:25
|
||||
unpinned-images:
|
||||
ignore:
|
||||
# We want to test these images with the latest version and we're not using them
|
||||
# to deploy anything so we deem it safe to use those, even if they are unpinned.
|
||||
- nightly-bnb.yml:30:7
|
||||
- nightly-bnb.yml:155:7
|
||||
- nightly.yml:27:7
|
||||
- nightly.yml:95:7
|
||||
- torch_compile_tests.yml:32:7
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
|
||||
# Mac .DS_Store
|
||||
.DS_Store
|
||||
|
||||
# More test things
|
||||
wandb
|
||||
|
||||
# method_comparison logs
|
||||
method_comparison/MetaMathQA/cancelled_results/
|
||||
method_comparison/MetaMathQA/temporary_results/
|
||||
@@ -0,0 +1,13 @@
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.12
|
||||
hooks:
|
||||
- id: ruff
|
||||
args:
|
||||
- --fix
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.6.0
|
||||
hooks:
|
||||
- id: check-merge-conflict
|
||||
- id: check-yaml
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
docs/source/developer_guides/contributing.md
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,75 @@
|
||||
.PHONY: quality style test docs
|
||||
|
||||
check_dirs := src tests examples docs scripts docker
|
||||
|
||||
# Check that source code meets quality standards
|
||||
|
||||
# this target runs checks on all files
|
||||
quality:
|
||||
ruff check $(check_dirs)
|
||||
ruff format --check $(check_dirs)
|
||||
doc-builder style src/peft tests docs/source --max_len 119 --check_only
|
||||
|
||||
# Format source code automatically and check is there are any problems left that need manual fixing
|
||||
style:
|
||||
ruff check --fix $(check_dirs)
|
||||
ruff format $(check_dirs)
|
||||
doc-builder style src/peft tests docs/source --max_len 119
|
||||
|
||||
test:
|
||||
python -m pytest -n 3 tests/ $(if $(IS_GITHUB_CI),--report-log "ci_tests.log",)
|
||||
|
||||
tests_examples_multi_gpu:
|
||||
python -m pytest -m multi_gpu_tests tests/test_gpu_examples.py $(if $(IS_GITHUB_CI),--report-log "multi_gpu_examples.log",)
|
||||
|
||||
tests_examples_single_gpu:
|
||||
python -m pytest -m single_gpu_tests tests/test_gpu_examples.py $(if $(IS_GITHUB_CI),--report-log "single_gpu_examples.log",)
|
||||
|
||||
tests_core_multi_gpu:
|
||||
python -m pytest -m multi_gpu_tests tests/test_common_gpu.py $(if $(IS_GITHUB_CI),--report-log "core_multi_gpu.log",)
|
||||
|
||||
tests_core_single_gpu:
|
||||
python -m pytest -m single_gpu_tests tests/test_common_gpu.py $(if $(IS_GITHUB_CI),--report-log "core_single_gpu.log",)
|
||||
|
||||
# exclude gemma tests, as generation fails with torch.compile, these failures
|
||||
# trigger side effects that make other tests fail with 'RuntimeError: Offset
|
||||
# increment outside graph capture encountered unexpectedly.'
|
||||
# TODO re-enable gemma once/if it is fixed
|
||||
tests_common_gpu:
|
||||
python -m pytest tests/test_decoder_models.py -k "not gemma" $(if $(IS_GITHUB_CI),--report-log "common_decoder.log",)
|
||||
python -m pytest tests/test_encoder_decoder_models.py $(if $(IS_GITHUB_CI),--report-log "common_encoder_decoder.log",)
|
||||
python -m pytest tests/test_gptqmodel.py $(if $(IS_GITHUB_CI),--report-log "gptqmodel_gpu.log",)
|
||||
|
||||
tests_examples_multi_gpu_bnb:
|
||||
python -m pytest -m "multi_gpu_tests and bitsandbytes" tests/test_gpu_examples.py $(if $(IS_GITHUB_CI),--report-log "multi_gpu_bnb_examples.log",)
|
||||
|
||||
tests_examples_single_gpu_bnb:
|
||||
python -m pytest -m "single_gpu_tests and bitsandbytes" tests/test_gpu_examples.py $(if $(IS_GITHUB_CI),--report-log "single_gpu_bnb_examples.log",)
|
||||
|
||||
tests_core_multi_gpu_bnb:
|
||||
python -m pytest -m "multi_gpu_tests and bitsandbytes" tests/test_common_gpu.py $(if $(IS_GITHUB_CI),--report-log "core_multi_gpu_bnb.log",)
|
||||
|
||||
tests_core_single_gpu_bnb:
|
||||
python -m pytest -m "single_gpu_tests and bitsandbytes" tests/test_common_gpu.py $(if $(IS_GITHUB_CI),--report-log "core_single_gpu_bnb.log",)
|
||||
|
||||
# For testing transformers tests for bnb runners
|
||||
transformers_tests:
|
||||
RUN_SLOW=1 python -m pytest transformers-clone/tests/quantization/bnb $(if $(IS_GITHUB_CI),--report-log "transformers_tests.log",)
|
||||
|
||||
tests_regression:
|
||||
python -m pytest -s --regression tests/regression/ $(if $(IS_GITHUB_CI),--report-log "regression_tests.log",)
|
||||
|
||||
tests_torch_compile:
|
||||
python -m pytest tests/test_torch_compile.py $(if $(IS_GITHUB_CI),--report-log "compile_tests.log",)
|
||||
|
||||
tests_training:
|
||||
accelerate launch --config_file tests/training/deepspeed_config.yaml tests/training/training.py
|
||||
accelerate launch --config_file tests/training/deepspeed_config.yaml tests/training/training.py --quant 4bit
|
||||
accelerate launch --config_file tests/training/deepspeed_config.yaml tests/training/training.py --quant 8bit
|
||||
accelerate launch --config_file tests/training/fsdp_config.yaml tests/training/training.py
|
||||
accelerate launch --config_file tests/training/fsdp_config.yaml tests/training/training.py --quant 4bit
|
||||
accelerate launch --config_file tests/training/fsdp2_config.yaml tests/training/training.py
|
||||
accelerate launch --config_file tests/training/fsdp2_config.yaml tests/training/training.py --quant 4bit
|
||||
accelerate launch --config_file tests/training/fsdp2_config.yaml tests/training/training.py --quant 4bit --target_modules q_proj --target_parameters v_proj.weight
|
||||
accelerate launch --config_file tests/training/fsdp_config.yaml tests/training/adapters.py
|
||||
accelerate launch --config_file tests/training/tp_config.yaml tests/training/lora_tp.py
|
||||
@@ -0,0 +1,191 @@
|
||||
<!---
|
||||
Copyright 2023 The HuggingFace Team. 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.
|
||||
-->
|
||||
|
||||
<h1 align="center"> <p>🤗 PEFT</p></h1>
|
||||
<h3 align="center">
|
||||
<p>State-of-the-art Parameter-Efficient Fine-Tuning (PEFT) methods</p>
|
||||
</h3>
|
||||
|
||||
Fine-tuning large pretrained models is often prohibitively costly due to their scale. Parameter-Efficient Fine-Tuning (PEFT) methods enable efficient adaptation of large pretrained models to various downstream applications by only fine-tuning a small number of (extra) model parameters instead of all the model's parameters. This significantly decreases the computational and storage costs. Recent state-of-the-art PEFT techniques achieve performance comparable to fully fine-tuned models.
|
||||
|
||||
PEFT is integrated with Transformers for easy model training and inference, Diffusers for conveniently managing different adapters, and Accelerate for distributed training and inference for really big models.
|
||||
|
||||
> [!TIP]
|
||||
> Visit the [PEFT](https://huggingface.co/PEFT) organization to read about the PEFT methods implemented in the library and to see notebooks demonstrating how to apply these methods to a variety of downstream tasks. Click the "Watch repos" button on the organization page to be notified of newly implemented methods and notebooks!
|
||||
|
||||
Check the PEFT Adapters API Reference section for a list of supported PEFT methods, and read the [Adapters](https://huggingface.co/docs/peft/en/conceptual_guides/adapter), [Soft prompts](https://huggingface.co/docs/peft/en/conceptual_guides/prompting), and [IA3](https://huggingface.co/docs/peft/en/conceptual_guides/ia3) conceptual guides to learn more about how these methods work.
|
||||
|
||||
## Quickstart
|
||||
|
||||
Install PEFT from pip:
|
||||
|
||||
```bash
|
||||
pip install peft
|
||||
```
|
||||
|
||||
Prepare a model for training with a PEFT method such as LoRA by wrapping the base model and PEFT configuration with `get_peft_model`. For the bigscience/mt0-large model, you're only training 0.19% of the parameters!
|
||||
|
||||
```python
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM
|
||||
from peft import LoraConfig, TaskType, get_peft_model
|
||||
|
||||
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
|
||||
model_id = "Qwen/Qwen2.5-3B-Instruct"
|
||||
model = AutoModelForCausalLM.from_pretrained(model_id, device_map=device)
|
||||
peft_config = LoraConfig(
|
||||
r=16,
|
||||
lora_alpha=32,
|
||||
task_type=TaskType.CAUSAL_LM,
|
||||
# target_modules=["q_proj", "v_proj", ...] # optionally indicate target modules
|
||||
)
|
||||
model = get_peft_model(model, peft_config)
|
||||
model.print_trainable_parameters()
|
||||
# prints: trainable params: 3,686,400 || all params: 3,089,625,088 || trainable%: 0.1193
|
||||
|
||||
# now perform training on your dataset, e.g. using transformers Trainer, then save the model
|
||||
model.save_pretrained("qwen2.5-3b-lora")
|
||||
```
|
||||
|
||||
To load a PEFT model for inference:
|
||||
|
||||
```python
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
from peft import PeftModel
|
||||
|
||||
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
|
||||
model_id = "Qwen/Qwen2.5-3B-Instruct"
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||||
model = AutoModelForCausalLM.from_pretrained(model_id, device_map=device)
|
||||
model = PeftModel.from_pretrained(model, "qwen2.5-3b-lora")
|
||||
|
||||
inputs = tokenizer("Preheat the oven to 350 degrees and place the cookie dough", return_tensors="pt")
|
||||
outputs = model.generate(**inputs.to(device), max_new_tokens=50)
|
||||
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
|
||||
|
||||
# prints something like: Preheat the oven to 350 degrees and place the cookie dough in a baking dish [...]
|
||||
```
|
||||
|
||||
## Why you should use PEFT
|
||||
|
||||
There are many benefits of using PEFT but the main one is the huge savings in compute and storage, making PEFT applicable to many different use cases.
|
||||
|
||||
### High performance on consumer hardware
|
||||
|
||||
Consider the memory requirements for training the following models on the [ought/raft/twitter_complaints](https://huggingface.co/datasets/ought/raft/viewer/twitter_complaints) dataset with an A100 80GB GPU with more than 64GB of CPU RAM.
|
||||
|
||||
| Model | Full Finetuning | PEFT-LoRA PyTorch | PEFT-LoRA DeepSpeed with CPU Offloading |
|
||||
| --------- | ---- | ---- | ---- |
|
||||
| bigscience/T0_3B (3B params) | 47.14GB GPU / 2.96GB CPU | 14.4GB GPU / 2.96GB CPU | 9.8GB GPU / 17.8GB CPU |
|
||||
| bigscience/mt0-xxl (12B params) | OOM GPU | 56GB GPU / 3GB CPU | 22GB GPU / 52GB CPU |
|
||||
| bigscience/bloomz-7b1 (7B params) | OOM GPU | 32GB GPU / 3.8GB CPU | 18.1GB GPU / 35GB CPU |
|
||||
|
||||
With LoRA you can fully finetune a 12B parameter model that would've otherwise run out of memory on the 80GB GPU, and comfortably fit and train a 3B parameter model. When you look at the 3B parameter model's performance, it is comparable to a fully finetuned model at a fraction of the GPU memory.
|
||||
|
||||
| Submission Name | Accuracy |
|
||||
| --------- | ---- |
|
||||
| Human baseline (crowdsourced) | 0.897 |
|
||||
| Flan-T5 | 0.892 |
|
||||
| lora-t0-3b | 0.863 |
|
||||
|
||||
> [!TIP]
|
||||
> The bigscience/T0_3B model performance isn't optimized in the table above. You can squeeze even more performance out of it by playing around with the input instruction templates, LoRA hyperparameters, and other training related hyperparameters. The final checkpoint size of this model is just 19MB compared to 11GB of the full bigscience/T0_3B model. Learn more about the advantages of finetuning with PEFT in this [blog post](https://www.philschmid.de/fine-tune-flan-t5-peft).
|
||||
|
||||
### Quantization
|
||||
|
||||
Quantization is another method for reducing the memory requirements of a model by representing the data in a lower precision. It can be combined with PEFT methods to make it even easier to train and load LLMs for inference.
|
||||
|
||||
* Learn how to finetune [meta-llama/Llama-2-7b-hf](https://huggingface.co/meta-llama/Llama-2-7b-hf) with QLoRA and the [TRL](https://huggingface.co/docs/trl/index) library on a 16GB GPU in the [Finetune LLMs on your own consumer hardware using tools from PyTorch and Hugging Face ecosystem](https://pytorch.org/blog/finetune-llms/) blog post.
|
||||
* Learn how to finetune a [openai/whisper-large-v2](https://huggingface.co/openai/whisper-large-v2) model for multilingual automatic speech recognition with LoRA and 8-bit quantization in this [notebook](https://colab.research.google.com/drive/1DOkD_5OUjFa0r5Ik3SgywJLJtEo2qLxO?usp=sharing) (see this [notebook](https://colab.research.google.com/drive/1vhF8yueFqha3Y3CpTHN6q9EVcII9EYzs?usp=sharing) instead for an example of streaming a dataset).
|
||||
|
||||
### Save compute and storage
|
||||
|
||||
PEFT can help you save storage by avoiding full finetuning of models on each of downstream task or dataset. In many cases, you're only finetuning a very small fraction of a model's parameters and each checkpoint is only a few MBs in size (instead of GBs). These smaller PEFT adapters demonstrate performance comparable to a fully finetuned model. If you have many datasets, you can save a lot of storage with a PEFT model and not have to worry about catastrophic forgetting or overfitting the backbone or base model.
|
||||
|
||||
## PEFT integrations
|
||||
|
||||
PEFT is widely supported across the Hugging Face ecosystem because of the massive efficiency it brings to training and inference.
|
||||
|
||||
### Diffusers
|
||||
|
||||
The iterative diffusion process consumes a lot of memory which can make it difficult to train. PEFT can help reduce the memory requirements and reduce the storage size of the final model checkpoint. For example, consider the memory required for training a Stable Diffusion model with LoRA on an A100 80GB GPU with more than 64GB of CPU RAM. The final model checkpoint size is only 8.8MB!
|
||||
|
||||
| Model | Full Finetuning | PEFT-LoRA | PEFT-LoRA with Gradient Checkpointing |
|
||||
| --------- | ---- | ---- | ---- |
|
||||
| CompVis/stable-diffusion-v1-4 | 27.5GB GPU / 3.97GB CPU | 15.5GB GPU / 3.84GB CPU | 8.12GB GPU / 3.77GB CPU |
|
||||
|
||||
> [!TIP]
|
||||
> Take a look at the [examples/lora_dreambooth/train_dreambooth.py](examples/lora_dreambooth/train_dreambooth.py) training script to try training your own Stable Diffusion model with LoRA, and play around with the [smangrul/peft-lora-sd-dreambooth](https://huggingface.co/spaces/smangrul/peft-lora-sd-dreambooth) Space which is running on a T4 instance. Learn more about the PEFT integration in Diffusers in this [tutorial](https://huggingface.co/docs/peft/main/en/tutorial/peft_integrations#diffusers).
|
||||
|
||||
### Transformers
|
||||
|
||||
PEFT is directly integrated with [Transformers](https://huggingface.co/docs/transformers/main/en/peft). After loading a model, call `add_adapter` to add a new PEFT adapter to the model:
|
||||
|
||||
```python
|
||||
from peft import LoraConfig
|
||||
model = ... # transformers model
|
||||
peft_config = LoraConfig(...)
|
||||
model.add_adapter(peft_config, adapter_name="lora_1")
|
||||
```
|
||||
|
||||
To load a trained PEFT adapter, call `load_adapter`:
|
||||
|
||||
```python
|
||||
model = ... # transformers model
|
||||
model.load_adapter(<path-to-adapter>, adapter_name="lora_1")
|
||||
```
|
||||
|
||||
And to switch between different adapters, call `set_adapter`:
|
||||
|
||||
```python
|
||||
model.set_adapter("lora_2")
|
||||
```
|
||||
|
||||
The Transformers integration doesn't include all the functionalities offered in PEFT, such as methods for merging the adapter into the base model.
|
||||
|
||||
### Accelerate
|
||||
|
||||
[Accelerate](https://huggingface.co/docs/accelerate/index) is a library for distributed training and inference on various training setups and hardware (GPUs, TPUs, Apple Silicon, etc.). PEFT models work with Accelerate out of the box, making it really convenient to train really large models or use them for inference on consumer hardware with limited resources.
|
||||
|
||||
### TRL
|
||||
|
||||
PEFT can also be applied to training LLMs with RLHF components such as the ranker and policy. Get started by reading:
|
||||
|
||||
* [Fine-tune a Mistral-7b model with Direct Preference Optimization](https://towardsdatascience.com/fine-tune-a-mistral-7b-model-with-direct-preference-optimization-708042745aac) with PEFT and the [TRL](https://huggingface.co/docs/trl/index) library to learn more about the Direct Preference Optimization (DPO) method and how to apply it to a LLM.
|
||||
* [Fine-tuning 20B LLMs with RLHF on a 24GB consumer GPU](https://huggingface.co/blog/trl-peft) with PEFT and the [TRL](https://huggingface.co/docs/trl/index) library, and then try out the [gpt2-sentiment_peft.ipynb](https://github.com/huggingface/trl/blob/main/examples/notebooks/gpt2-sentiment.ipynb) notebook to optimize GPT2 to generate positive movie reviews.
|
||||
* [StackLLaMA: A hands-on guide to train LLaMA with RLHF](https://huggingface.co/blog/stackllama) with PEFT, and then try out the [stack_llama/scripts](https://github.com/huggingface/trl/tree/main/examples/research_projects/stack_llama/scripts) for supervised finetuning, reward modeling, and RL finetuning.
|
||||
|
||||
## Model support
|
||||
|
||||
Use this [Space](https://stevhliu-peft-methods.hf.space) or check out the [docs](https://huggingface.co/docs/peft/main/en/index) to find which models officially support a PEFT method out of the box. Even if you don't see a model listed below, you can manually configure the model config to enable PEFT for a model. Read the [New transformers architecture](https://huggingface.co/docs/peft/main/en/developer_guides/custom_models#new-transformers-architectures) guide to learn how.
|
||||
|
||||
## Contribute
|
||||
|
||||
If you would like to contribute to PEFT, please check out our [contribution guide](https://huggingface.co/docs/peft/developer_guides/contributing).
|
||||
|
||||
## Citing 🤗 PEFT
|
||||
|
||||
To use 🤗 PEFT in your publication, please cite it by using the following BibTeX entry.
|
||||
|
||||
```bibtex
|
||||
@Misc{peft,
|
||||
title = {{PEFT}: State-of-the-art Parameter-Efficient Fine-Tuning methods},
|
||||
author = {Sourab Mangrulkar and Sylvain Gugger and Lysandre Debut and Younes Belkada and Sayak Paul and Benjamin Bossan and Marian Tietz},
|
||||
howpublished = {\url{https://github.com/huggingface/peft}},
|
||||
year = {2022}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`huggingface/peft`
|
||||
- 原始仓库:https://github.com/huggingface/peft
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
@@ -0,0 +1,6 @@
|
||||
# PEFT Docker images
|
||||
|
||||
Here we store all PEFT Docker images used in our testing infrastructure. We use python 3.11 for now on all our images.
|
||||
|
||||
- `peft-cpu`: PEFT compiled on CPU with all other HF libraries installed on main branch
|
||||
- `peft-gpu`: PEFT complied for NVIDIA GPUs with all other HF libraries installed on main branch
|
||||
@@ -0,0 +1,46 @@
|
||||
# Builds GPU docker image of PyTorch
|
||||
# Uses multi-staged approach to reduce size
|
||||
# Stage 1
|
||||
# Use base conda image to reduce time
|
||||
FROM continuumio/miniconda3:latest AS compile-image
|
||||
# Specify py version
|
||||
ENV PYTHON_VERSION=3.11
|
||||
# Install apt libs - copied from https://github.com/huggingface/accelerate/blob/main/docker/accelerate-gpu/Dockerfile
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl git wget git-lfs ffmpeg libsndfile1-dev && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists*
|
||||
|
||||
RUN git lfs install
|
||||
|
||||
# Create our conda env - copied from https://github.com/huggingface/accelerate/blob/main/docker/accelerate-gpu/Dockerfile
|
||||
RUN conda create --name peft python=${PYTHON_VERSION} ipython jupyter pip
|
||||
RUN python3 -m pip install --no-cache-dir --upgrade pip
|
||||
|
||||
# Below is copied from https://github.com/huggingface/accelerate/blob/main/docker/accelerate-gpu/Dockerfile
|
||||
# We don't install pytorch here yet since CUDA isn't available
|
||||
# instead we use the direct torch wheel
|
||||
ENV PATH=/opt/conda/envs/peft/bin:$PATH
|
||||
# Activate our bash shell
|
||||
RUN chsh -s /bin/bash
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
# Activate the conda env and install transformers + accelerate from source
|
||||
RUN source activate peft && \
|
||||
python3 -m pip install --no-cache-dir \
|
||||
librosa \
|
||||
"soundfile>=0.12.1" \
|
||||
scipy \
|
||||
git+https://github.com/huggingface/transformers \
|
||||
git+https://github.com/huggingface/accelerate \
|
||||
peft[test]@git+https://github.com/huggingface/peft
|
||||
|
||||
# Install apt libs
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl git wget && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists*
|
||||
|
||||
RUN echo "source activate peft" >> ~/.profile
|
||||
|
||||
# Activate the virtualenv
|
||||
CMD ["/bin/bash"]
|
||||
@@ -0,0 +1,86 @@
|
||||
# Builds GPU docker image of PyTorch
|
||||
# Uses multi-staged approach to reduce size
|
||||
# Stage 1
|
||||
# Use base conda image to reduce time
|
||||
FROM continuumio/miniconda3:latest AS compile-image
|
||||
# Specify py version
|
||||
ENV PYTHON_VERSION=3.11
|
||||
# Install apt libs - copied from https://github.com/huggingface/accelerate/blob/main/docker/accelerate-gpu/Dockerfile
|
||||
# Install audio-related libraries
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl git wget git-lfs ffmpeg libsndfile1-dev && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists*
|
||||
|
||||
RUN git lfs install
|
||||
|
||||
# Create our conda env - copied from https://github.com/huggingface/accelerate/blob/main/docker/accelerate-gpu/Dockerfile
|
||||
RUN conda create --name peft python=${PYTHON_VERSION} ipython jupyter pip
|
||||
|
||||
# Below is copied from https://github.com/huggingface/accelerate/blob/main/docker/accelerate-gpu/Dockerfile
|
||||
# We don't install pytorch here yet since CUDA isn't available
|
||||
# instead we use the direct torch wheel
|
||||
ENV PATH=/opt/conda/envs/peft/bin:$PATH
|
||||
# Activate our bash shell
|
||||
RUN chsh -s /bin/bash
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
# Stage 2
|
||||
FROM nvidia/cuda:13.2.1-cudnn-devel-ubuntu24.04 AS build-image
|
||||
COPY --from=compile-image /opt/conda /opt/conda
|
||||
ENV PATH=/opt/conda/bin:$PATH
|
||||
|
||||
# Install apt libs
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl git wget && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists*
|
||||
|
||||
RUN chsh -s /bin/bash
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
RUN conda run -n peft pip install --no-cache-dir bitsandbytes optimum
|
||||
|
||||
# Note: we are hard-coding CUDA_ARCH_LIST here since `gptqmodel` requires either nvidia-smi
|
||||
# or CUDA_ARCH_LIST for compute capability information. Since the docker build is unlikely
|
||||
# to have compute hardware available we use the information from the CI runner (which hosts
|
||||
# a NVIDIA L4). So we fix the compute capability to 8.9. In the future we might extend this
|
||||
# to a list of compute capabilities (separated by ;).
|
||||
# TODO pcre, which is used by gptqmodel, is resulting in a core dump, remove once it's resolved
|
||||
# RUN CUDA_ARCH_LIST=8.9 conda run -n peft pip install "gptqmodel>=7.0.0"
|
||||
|
||||
RUN \
|
||||
# Add eetq for quantization testing; needs to run without build isolation since the setup
|
||||
# script directly imports torch from the environment which would fail with isolation.
|
||||
# Ninja should speed up build time.
|
||||
conda run -n peft pip install ninja && conda run -n peft pip install --no-build-isolation git+https://github.com/NetEase-FuXi/EETQ.git
|
||||
|
||||
# TODO: Importing TE results in: undefined symbol: cublasLtGroupedMatrixLayoutInit_internal, version libcublasLt.so.13
|
||||
# Reinstate TE when the issue is resolved (probably this one: https://github.com/NVIDIA/TransformerEngine/issues/2504)
|
||||
# RUN NVTE_BUILD_USE_NVIDIA_WHEELS=1 \
|
||||
# CPATH="/usr/local/cuda/include:${CPATH}" \
|
||||
# conda run -n peft pip install --no-build-isolation "transformer_engine[pytorch]"
|
||||
|
||||
# Activate the conda env and install transformers + accelerate from source
|
||||
RUN conda run -n peft pip install -U --no-cache-dir \
|
||||
librosa \
|
||||
"soundfile>=0.12.1" \
|
||||
scipy \
|
||||
torchao \
|
||||
"fbgemm-gpu-genai>=1.2.0" \
|
||||
git+https://github.com/huggingface/transformers \
|
||||
git+https://github.com/huggingface/accelerate \
|
||||
peft[test]@git+https://github.com/huggingface/peft \
|
||||
# Add aqlm for quantization testing
|
||||
aqlm[gpu]>=1.0.2 \
|
||||
# Add HQQ for quantization testing
|
||||
hqq \
|
||||
deepspeed \
|
||||
"kernels<0.16"
|
||||
|
||||
RUN conda run -n peft pip freeze | grep transformers
|
||||
|
||||
RUN echo "source activate peft" >> ~/.profile
|
||||
|
||||
# Activate the virtualenv
|
||||
CMD ["/bin/bash"]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
<!---
|
||||
Copyright 2023 The HuggingFace Team. 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.
|
||||
-->
|
||||
|
||||
# Generating the documentation
|
||||
|
||||
To generate the documentation, you first have to build it. Several packages are necessary to build the doc,
|
||||
you can install them with the following command, at the root of the code repository:
|
||||
|
||||
```bash
|
||||
pip install -e ".[docs]"
|
||||
```
|
||||
|
||||
Then you need to install our special tool that builds the documentation:
|
||||
|
||||
```bash
|
||||
pip install git+https://github.com/huggingface/doc-builder
|
||||
```
|
||||
|
||||
---
|
||||
**NOTE**
|
||||
|
||||
You only need to generate the documentation to inspect it locally (if you're planning changes and want to
|
||||
check how they look before committing for instance). You don't have to commit to the built documentation.
|
||||
|
||||
---
|
||||
|
||||
## Building the documentation
|
||||
|
||||
Once you have setup the `doc-builder` and additional packages, you can generate the documentation by
|
||||
typing the following command:
|
||||
|
||||
```bash
|
||||
doc-builder build peft docs/source/ --build_dir ~/tmp/test-build
|
||||
```
|
||||
|
||||
You can adapt the `--build_dir` to set any temporary folder you prefer. This command will create it and generate
|
||||
the MDX files that will be rendered as the documentation on the main website. You can inspect them in your favorite
|
||||
Markdown editor.
|
||||
|
||||
## Previewing the documentation
|
||||
|
||||
To preview the docs, first install the `watchdog` module with:
|
||||
|
||||
```bash
|
||||
pip install watchdog
|
||||
```
|
||||
|
||||
Then run the following command:
|
||||
|
||||
```bash
|
||||
doc-builder preview {package_name} {path_to_docs}
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
doc-builder preview peft docs/source
|
||||
```
|
||||
|
||||
The docs will be viewable at [http://localhost:3000](http://localhost:3000). You can also preview the docs once you have opened a PR. You will see a bot add a comment to a link where the documentation with your changes lives.
|
||||
|
||||
---
|
||||
**NOTE**
|
||||
|
||||
The `preview` command only works with existing doc files. When you add a completely new file, you need to update `_toctree.yml` & restart `preview` command (`ctrl-c` to stop it & call `doc-builder preview ...` again).
|
||||
|
||||
---
|
||||
|
||||
## Adding a new element to the navigation bar
|
||||
|
||||
Accepted files are Markdown (.md or .mdx).
|
||||
|
||||
Create a file with its extension and put it in the source directory. You can then link it to the toc-tree by putting
|
||||
the filename without the extension in the [`_toctree.yml`](https://github.com/huggingface/peft/blob/main/docs/source/_toctree.yml) file.
|
||||
|
||||
## Renaming section headers and moving sections
|
||||
|
||||
It helps to keep the old links working when renaming the section header and/or moving sections from one document to another. This is because the old links are likely to be used in Issues, Forums, and Social media and it'd make for a much more superior user experience if users reading those months later could still easily navigate to the originally intended information.
|
||||
|
||||
Therefore, we simply keep a little map of moved sections at the end of the document where the original section was. The key is to preserve the original anchor.
|
||||
|
||||
So if you renamed a section from: "Section A" to "Section B", then you can add at the end of the file:
|
||||
|
||||
```
|
||||
Sections that were moved:
|
||||
|
||||
[ <a href="#section-b">Section A</a><a id="section-a"></a> ]
|
||||
```
|
||||
and of course, if you moved it to another file, then:
|
||||
|
||||
```
|
||||
Sections that were moved:
|
||||
|
||||
[ <a href="../new-file#section-b">Section A</a><a id="section-a"></a> ]
|
||||
```
|
||||
|
||||
Use the relative style to link to the new file so that the versioned docs continue to work.
|
||||
|
||||
|
||||
## Writing Documentation - Specification
|
||||
|
||||
The `huggingface/peft` documentation follows the
|
||||
[Google documentation](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) style for docstrings,
|
||||
although we can write them directly in Markdown.
|
||||
|
||||
### Adding a new tutorial
|
||||
|
||||
Adding a new tutorial or section is done in two steps:
|
||||
|
||||
- Add a new file under `./source`. This file can either be ReStructuredText (.rst) or Markdown (.md).
|
||||
- Link that file in `./source/_toctree.yml` on the correct toc-tree.
|
||||
|
||||
Make sure to put your new file under the proper section. It's unlikely to go in the first section (*Get Started*), so
|
||||
depending on the intended targets (beginners, more advanced users, or researchers) it should go into sections two, three, or
|
||||
four.
|
||||
|
||||
### Writing source documentation
|
||||
|
||||
Values that should be put in `code` should either be surrounded by backticks: \`like so\`. Note that argument names
|
||||
and objects like True, None, or any strings should usually be put in `code`.
|
||||
|
||||
When mentioning a class, function, or method, it is recommended to use our syntax for internal links so that our tool
|
||||
adds a link to its documentation with this syntax: \[\`XXXClass\`\] or \[\`function\`\]. This requires the class or
|
||||
function to be in the main package.
|
||||
|
||||
If you want to create a link to some internal class or function, you need to
|
||||
provide its path. For instance: \[\`utils.gather\`\]. This will be converted into a link with
|
||||
`utils.gather` in the description. To get rid of the path and only keep the name of the object you are
|
||||
linking to in the description, add a ~: \[\`~utils.gather\`\] will generate a link with `gather` in the description.
|
||||
|
||||
The same works for methods so you can either use \[\`XXXClass.method\`\] or \[~\`XXXClass.method\`\].
|
||||
|
||||
#### Defining arguments in a method
|
||||
|
||||
Arguments should be defined with the `Args:` (or `Arguments:` or `Parameters:`) prefix, followed by a line return and
|
||||
an indentation. The argument should be followed by its type, with its shape if it is a tensor, a colon, and its
|
||||
description:
|
||||
|
||||
```
|
||||
Args:
|
||||
n_layers (`int`): The number of layers of the model.
|
||||
```
|
||||
|
||||
If the description is too long to fit in one line (more than 119 characters in total), another indentation is necessary
|
||||
before writing the description after the argument.
|
||||
|
||||
Finally, to maintain uniformity if any *one* description is too long to fit on one line, the
|
||||
rest of the parameters should follow suit and have an indention before their description.
|
||||
|
||||
Here's an example showcasing everything so far:
|
||||
|
||||
```
|
||||
Args:
|
||||
gradient_accumulation_steps (`int`, *optional*, default to 1):
|
||||
The number of steps that should pass before gradients are accumulated. A number > 1 should be combined with `Accelerator.accumulate`.
|
||||
cpu (`bool`, *optional*):
|
||||
Whether or not to force the script to execute on CPU. Will ignore GPU available if set to `True` and force the execution on one process only.
|
||||
```
|
||||
|
||||
For optional arguments or arguments with defaults we follow the following syntax: imagine we have a function with the
|
||||
following signature:
|
||||
|
||||
```
|
||||
def my_function(x: str = None, a: float = 1):
|
||||
```
|
||||
|
||||
then its documentation should look like this:
|
||||
|
||||
```
|
||||
Args:
|
||||
x (`str`, *optional*):
|
||||
This argument controls ... and has a description longer than 119 chars.
|
||||
a (`float`, *optional*, defaults to 1):
|
||||
This argument is used to ... and has a description longer than 119 chars.
|
||||
```
|
||||
|
||||
Note that we always omit the "defaults to \`None\`" when None is the default for any argument. Also note that even
|
||||
if the first line describing your argument type and its default gets long, you can't break it into several lines. You can
|
||||
however write as many lines as you want in the indented description (see the example above with `input_ids`).
|
||||
|
||||
#### Writing a multi-line code block
|
||||
|
||||
Multi-line code blocks can be useful for displaying examples. They are done between two lines of three backticks as usual in Markdown:
|
||||
|
||||
|
||||
````
|
||||
```python
|
||||
# first line of code
|
||||
# second line
|
||||
# etc
|
||||
```
|
||||
````
|
||||
|
||||
#### Writing a return block
|
||||
|
||||
The return block should be introduced with the `Returns:` prefix, followed by a line return and an indentation.
|
||||
The first line should be the type of the return, followed by a line return. No need to indent further for the elements
|
||||
building the return.
|
||||
|
||||
Here's an example of a single value return:
|
||||
|
||||
```
|
||||
Returns:
|
||||
`List[int]`: A list of integers in the range [0, 1] --- 1 for a special token, 0 for a sequence token.
|
||||
```
|
||||
|
||||
Here's an example of a tuple return, comprising several objects:
|
||||
|
||||
```
|
||||
Returns:
|
||||
`tuple(torch.FloatTensor)` comprising various elements depending on the configuration ([`BertConfig`]) and inputs:
|
||||
- ** loss** (*optional*, returned when `masked_lm_labels` is provided) `torch.FloatTensor` of shape `(1,)` --
|
||||
Total loss is the sum of the masked language modeling loss and the next sequence prediction (classification) loss.
|
||||
- **prediction_scores** (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`) --
|
||||
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
||||
```
|
||||
|
||||
## Styling the docstring
|
||||
|
||||
We have an automatic script running with the `make style` comment that will make sure that:
|
||||
- the docstrings fully take advantage of the line width
|
||||
- all code examples are formatted using black, like the code of the Transformers library
|
||||
|
||||
This script may have some weird failures if you make a syntax mistake or if you uncover a bug. Therefore, it's
|
||||
recommended to commit your changes before running `make style`, so you can revert the changes done by that script
|
||||
easily.
|
||||
|
||||
## Writing documentation examples
|
||||
|
||||
The syntax, for example, docstrings can look as follows:
|
||||
|
||||
```
|
||||
Example:
|
||||
|
||||
```python
|
||||
>>> import time
|
||||
>>> from accelerate import Accelerator
|
||||
>>> accelerator = Accelerator()
|
||||
>>> if accelerator.is_main_process:
|
||||
... time.sleep(2)
|
||||
>>> else:
|
||||
... print("I'm waiting for the main process to finish its sleep...")
|
||||
>>> accelerator.wait_for_everyone()
|
||||
>>> # Should print on every process at the same time
|
||||
>>> print("Everyone is here")
|
||||
```
|
||||
```
|
||||
|
||||
The docstring should give a minimal, clear example of how the respective function
|
||||
is to be used in inference and also include the expected (ideally sensible)
|
||||
output.
|
||||
Often, readers will try out the example before even going through the function
|
||||
or class definitions. Therefore, it is of utmost importance that the example
|
||||
works as expected.
|
||||
@@ -0,0 +1,7 @@
|
||||
# docstyle-ignore
|
||||
INSTALL_CONTENT = """
|
||||
# PEFT installation
|
||||
! pip install peft accelerate transformers
|
||||
# To install from source instead of the last release, comment the command above and uncomment the following one.
|
||||
# ! pip install git+https://github.com/huggingface/peft.git
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
conceptual_guides/adapter: package_reference/lora
|
||||
conceptual_guides/ia3: package_reference/ia3
|
||||
developer_guides/lora: package_reference/lora
|
||||
@@ -0,0 +1,276 @@
|
||||
- title: Get started
|
||||
sections:
|
||||
- local: index
|
||||
title: 🤗 PEFT
|
||||
- local: quicktour
|
||||
title: Quicktour
|
||||
- local: install
|
||||
title: Installation
|
||||
|
||||
- title: Guides
|
||||
sections:
|
||||
- local: guides/peft_model_config
|
||||
title: Configurations and models
|
||||
- local: guides/peft_integrations
|
||||
title: Integrations
|
||||
- sections:
|
||||
- local: accelerate/deepspeed
|
||||
title: DeepSpeed
|
||||
- local: accelerate/fsdp
|
||||
title: Fully Sharded Data Parallel
|
||||
title: Distributed Training
|
||||
- local: developer_guides/memory_efficient_training
|
||||
title: Memory Efficient Training
|
||||
- local: developer_guides/model_merging
|
||||
title: Model merging
|
||||
- local: developer_guides/quantization
|
||||
title: Quantization
|
||||
- local: developer_guides/custom_models
|
||||
title: Custom models
|
||||
- local: developer_guides/low_level_api
|
||||
title: Adapter injection
|
||||
- local: developer_guides/mixed_models
|
||||
title: Mixing PEFT methods
|
||||
- local: developer_guides/torch_compile
|
||||
title: torch.compile
|
||||
- local: developer_guides/contributing
|
||||
title: Contribute to PEFT
|
||||
- local: developer_guides/troubleshooting
|
||||
title: Troubleshooting
|
||||
- local: developer_guides/checkpoint
|
||||
title: PEFT checkpoint format
|
||||
|
||||
- title: Methods
|
||||
sections:
|
||||
- local: methods/overview
|
||||
title: Overview
|
||||
- sections:
|
||||
- local: package_reference/cartridges
|
||||
title: Cartridges
|
||||
- local: package_reference/cpt
|
||||
title: CPT
|
||||
- local: package_reference/llama_adapter
|
||||
title: Llama-Adapter
|
||||
- local: package_reference/p_tuning
|
||||
title: P-Tuning
|
||||
- local: package_reference/prefix_tuning
|
||||
title: Prefix tuning
|
||||
- local: package_reference/prompt_tuning
|
||||
title: Prompt tuning
|
||||
title: Soft Prompting
|
||||
- sections:
|
||||
- local: package_reference/beft
|
||||
title: BEFT
|
||||
- local: package_reference/layernorm_tuning
|
||||
title: LayerNorm Tuning
|
||||
- local: package_reference/trainable_tokens
|
||||
title: Trainable Tokens
|
||||
title: Layer Tuning
|
||||
- sections:
|
||||
- local: package_reference/adalora
|
||||
title: AdaLoRA
|
||||
- local: package_reference/adamss
|
||||
title: AdaMSS
|
||||
- local: package_reference/boft
|
||||
title: BOFT
|
||||
- local: package_reference/c3a
|
||||
title: C3A
|
||||
- local: package_reference/deft
|
||||
title: DEFT
|
||||
- local: package_reference/delora
|
||||
title: DeLoRA
|
||||
- local: package_reference/fourierft
|
||||
title: FourierFT
|
||||
- local: package_reference/gralora
|
||||
title: GraLoRA
|
||||
- local: package_reference/hira
|
||||
title: HiRA
|
||||
- local: package_reference/hra
|
||||
title: HRA
|
||||
- local: package_reference/ia3
|
||||
title: IA3
|
||||
- local: package_reference/lily
|
||||
title: Lily
|
||||
- local: package_reference/loha
|
||||
title: LoHa
|
||||
- local: package_reference/lokr
|
||||
title: LoKr
|
||||
- sections:
|
||||
- local: package_reference/lora
|
||||
title: LoRA
|
||||
- local: package_reference/lora_variant_bdlora
|
||||
title: "Variant: BD-LoRA"
|
||||
- local: package_reference/lora_variant_dora
|
||||
title: "Variant: DoRA"
|
||||
- local: package_reference/lora_variant_monteclora
|
||||
title: "Variant: MonteCLoRA"
|
||||
- local: package_reference/lora_variant_velora
|
||||
title: "Variant: VeLoRA"
|
||||
title: LoRA
|
||||
- local: package_reference/miss
|
||||
title: MiSS
|
||||
- local: package_reference/oft
|
||||
title: OFT
|
||||
- local: package_reference/osf
|
||||
title: OSF
|
||||
- local: package_reference/peanut
|
||||
title: PEANuT
|
||||
- local: package_reference/poly
|
||||
title: Polytropon
|
||||
- local: package_reference/psoft
|
||||
title: PSOFT
|
||||
- local: package_reference/pvera
|
||||
title: PVeRA
|
||||
- local: package_reference/randlora
|
||||
title: RandLora
|
||||
- local: package_reference/road
|
||||
title: RoAd
|
||||
- local: package_reference/shira
|
||||
title: SHiRA
|
||||
- local: package_reference/tinylora
|
||||
title: TinyLoRA
|
||||
- local: package_reference/unilora
|
||||
title: UniLoRA
|
||||
- local: package_reference/vblora
|
||||
title: VB-LoRA
|
||||
- local: package_reference/vera
|
||||
title: VeRA
|
||||
- local: package_reference/waveft
|
||||
title: WaveFT
|
||||
- local: package_reference/xlora
|
||||
title: X-LoRA
|
||||
title: Adapters
|
||||
|
||||
- isExpanded: false
|
||||
sections:
|
||||
- sections:
|
||||
- local: package_reference/auto_class
|
||||
title: AutoPeftModel
|
||||
- local: package_reference/peft_model
|
||||
title: PEFT model
|
||||
- local: package_reference/peft_types
|
||||
title: PEFT types
|
||||
- local: package_reference/config
|
||||
title: Configuration
|
||||
- local: package_reference/tuners
|
||||
title: Tuner
|
||||
title: Main classes
|
||||
- sections:
|
||||
- local: package_reference/adalora
|
||||
title: AdaLoRA
|
||||
- local: package_reference/adamss
|
||||
title: AdaMSS
|
||||
- local: package_reference/beft
|
||||
title: BEFT
|
||||
- local: package_reference/boft
|
||||
title: BOFT
|
||||
- local: package_reference/c3a
|
||||
title: C3A
|
||||
- local: package_reference/cartridges
|
||||
title: Cartridges
|
||||
- local: package_reference/cpt
|
||||
title: CPT
|
||||
- local: package_reference/deft
|
||||
title: DEFT
|
||||
- local: package_reference/delora
|
||||
title: DeLoRA
|
||||
- local: package_reference/fourierft
|
||||
title: FourierFT
|
||||
- local: package_reference/gralora
|
||||
title: GraLoRA
|
||||
- local: package_reference/hira
|
||||
title: HiRA
|
||||
- local: package_reference/hra
|
||||
title: HRA
|
||||
- local: package_reference/ia3
|
||||
title: IA3
|
||||
- local: package_reference/layernorm_tuning
|
||||
title: Layernorm tuning
|
||||
- local: package_reference/lily
|
||||
title: Lily
|
||||
- local: package_reference/llama_adapter
|
||||
title: Llama-Adapter
|
||||
- local: package_reference/loha
|
||||
title: LoHa
|
||||
- local: package_reference/lokr
|
||||
title: LoKr
|
||||
- local: package_reference/lora
|
||||
title: LoRA
|
||||
- local: package_reference/adapter_utils
|
||||
title: LyCORIS
|
||||
- local: package_reference/miss
|
||||
title: MiSS
|
||||
- local: package_reference/multitask_prompt_tuning
|
||||
title: Multitask Prompt Tuning
|
||||
- local: package_reference/oft
|
||||
title: OFT
|
||||
- local: package_reference/osf
|
||||
title: OSF
|
||||
- local: package_reference/p_tuning
|
||||
title: P-tuning
|
||||
- local: package_reference/peanut
|
||||
title: PEANuT
|
||||
- local: package_reference/poly
|
||||
title: Polytropon
|
||||
- local: package_reference/prefix_tuning
|
||||
title: Prefix tuning
|
||||
- local: package_reference/prompt_tuning
|
||||
title: Prompt tuning
|
||||
- local: package_reference/psoft
|
||||
title: PSOFT
|
||||
- local: package_reference/pvera
|
||||
title: PVeRA
|
||||
- local: package_reference/fourierft
|
||||
title: FourierFT
|
||||
- local: package_reference/frod
|
||||
title: FRoD
|
||||
- local: package_reference/glora
|
||||
title: GLoRA
|
||||
- local: package_reference/gralora
|
||||
title: GraLoRA
|
||||
- local: package_reference/vblora
|
||||
title: VB-LoRA
|
||||
- local: package_reference/hira
|
||||
title: HiRA
|
||||
- local: package_reference/hra
|
||||
title: HRA
|
||||
- local: package_reference/deft
|
||||
title: DEFT
|
||||
- local: package_reference/cpt
|
||||
title: CPT
|
||||
- local: package_reference/trainable_tokens
|
||||
title: Trainable Tokens
|
||||
- local: package_reference/randlora
|
||||
title: RandLora
|
||||
- local: package_reference/road
|
||||
title: RoAd
|
||||
- local: package_reference/shira
|
||||
title: SHiRA
|
||||
- local: package_reference/tinylora
|
||||
title: TinyLoRA
|
||||
- local: package_reference/unilora
|
||||
title: UniLoRA
|
||||
- local: package_reference/trainable_tokens
|
||||
title: Trainable Tokens
|
||||
- local: package_reference/vblora
|
||||
title: VB-LoRA
|
||||
- local: package_reference/vera
|
||||
title: VeRA
|
||||
- local: package_reference/waveft
|
||||
title: WaveFT
|
||||
- local: package_reference/xlora
|
||||
title: X-LoRA
|
||||
title: Tuners
|
||||
- sections:
|
||||
- local: package_reference/merge_utils
|
||||
title: Model merge
|
||||
- local: package_reference/helpers
|
||||
title: Helpers
|
||||
- local: package_reference/hotswap
|
||||
title: Hotswapping adapters
|
||||
- local: package_reference/functional
|
||||
title: Functions for PEFT integration
|
||||
- local: package_reference/lora_conversion
|
||||
title: Converting non-LoRA adapters to LoRA
|
||||
title: Utilities
|
||||
title: API reference
|
||||
@@ -0,0 +1,449 @@
|
||||
<!--⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# DeepSpeed
|
||||
|
||||
[DeepSpeed](https://www.deepspeed.ai/) is a library designed for speed and scale for distributed training of large models with billions of parameters. At its core is the Zero Redundancy Optimizer (ZeRO) that shards optimizer states (ZeRO-1), gradients (ZeRO-2), and parameters (ZeRO-3) across data parallel processes. This drastically reduces memory usage, allowing you to scale your training to billion parameter models. To unlock even more memory efficiency, ZeRO-Offload reduces GPU compute and memory by leveraging CPU resources during optimization.
|
||||
|
||||
Both of these features are supported in 🤗 Accelerate, and you can use them with 🤗 PEFT.
|
||||
|
||||
## Compatibility with `bitsandbytes` quantization + LoRA
|
||||
|
||||
Below is a table that summarizes the compatibility between PEFT's LoRA, [`bitsandbytes`](https://github.com/TimDettmers/bitsandbytes) library and DeepSpeed Zero stages with respect to fine-tuning. DeepSpeed Zero-1 and 2 will have no effect at inference as stage 1 shards the optimizer states and stage 2 shards the optimizer states and gradients:
|
||||
|
||||
| DeepSpeed stage | Is compatible? |
|
||||
|---|---|
|
||||
| Zero-1 | 🟢 |
|
||||
| Zero-2 | 🟢 |
|
||||
| Zero-3 | 🟢 |
|
||||
|
||||
For DeepSpeed Stage 3 + QLoRA, please refer to the section [Use PEFT QLoRA and DeepSpeed with ZeRO3 for finetuning large models on multiple GPUs](#use-peft-qlora-and-deepspeed-with-zero3-for-finetuning-large-models-on-multiple-gpus) below.
|
||||
|
||||
For confirming these observations, we ran the SFT (Supervised Fine-tuning) [official example scripts](https://github.com/huggingface/trl/tree/main/examples) of the [Transformers Reinforcement Learning (TRL) library](https://github.com/huggingface/trl) using QLoRA + PEFT and the accelerate configs available [here](https://github.com/huggingface/trl/tree/main/examples/accelerate_configs). We ran these experiments on a 2x NVIDIA T4 GPU.
|
||||
|
||||
# Use PEFT and DeepSpeed with ZeRO3 for finetuning large models on multiple devices and multiple nodes
|
||||
|
||||
This section of guide will help you learn how to use our DeepSpeed [training script](https://github.com/huggingface/peft/blob/main/examples/sft/train.py) for performing SFT. You'll configure the script to do SFT (supervised fine-tuning) of Llama-70B model with LoRA and ZeRO-3 on 8xH100 80GB GPUs on a single machine. You can configure it to scale to multiple machines by changing the accelerate config.
|
||||
|
||||
## Configuration
|
||||
|
||||
Start by running the following command to [create a DeepSpeed configuration file](https://huggingface.co/docs/accelerate/quicktour#launching-your-distributed-script) with 🤗 Accelerate. The `--config_file` flag allows you to save the configuration file to a specific location, otherwise it is saved as a `default_config.yaml` file in the 🤗 Accelerate cache.
|
||||
|
||||
The configuration file is used to set the default options when you launch the training script.
|
||||
|
||||
```bash
|
||||
accelerate config --config_file deepspeed_config.yaml
|
||||
```
|
||||
|
||||
You'll be asked a few questions about your setup, and configure the following arguments. In this example, you'll use ZeRO-3 so make sure you pick those options.
|
||||
|
||||
```bash
|
||||
`zero_stage`: [0] Disabled, [1] optimizer state partitioning, [2] optimizer+gradient state partitioning and [3] optimizer+gradient+parameter partitioning
|
||||
`gradient_accumulation_steps`: Number of training steps to accumulate gradients before averaging and applying them. Pass the same value as you would pass via cmd argument else you will encounter mismatch error.
|
||||
`gradient_clipping`: Enable gradient clipping with value. Don't set this as you will be passing it via cmd arguments.
|
||||
`offload_optimizer_device`: [none] Disable optimizer offloading, [cpu] offload optimizer to CPU, [nvme] offload optimizer to NVMe SSD. Only applicable with ZeRO >= Stage-2. Set this as `none` as don't want to enable offloading.
|
||||
`offload_param_device`: [none] Disable parameter offloading, [cpu] offload parameters to CPU, [nvme] offload parameters to NVMe SSD. Only applicable with ZeRO Stage-3. Set this as `none` as don't want to enable offloading.
|
||||
`zero3_init_flag`: Decides whether to enable `deepspeed.zero.Init` for constructing massive models. Only applicable with ZeRO Stage-3. Set this to `True`.
|
||||
`zero3_save_16bit_model`: Decides whether to save 16-bit model weights when using ZeRO Stage-3. Set this to `True`.
|
||||
`mixed_precision`: `no` for FP32 training, `fp16` for FP16 mixed-precision training and `bf16` for BF16 mixed-precision training. Set this to `True`.
|
||||
```
|
||||
|
||||
Once this is done, the corresponding config should look like below and you can find it in config folder at [deepspeed_config.yaml](https://github.com/huggingface/peft/blob/main/examples/sft/configs/deepspeed_config.yaml):
|
||||
|
||||
```yml
|
||||
compute_environment: LOCAL_MACHINE
|
||||
debug: false
|
||||
deepspeed_config:
|
||||
deepspeed_multinode_launcher: standard
|
||||
gradient_accumulation_steps: 4
|
||||
offload_optimizer_device: none
|
||||
offload_param_device: none
|
||||
zero3_init_flag: true
|
||||
zero3_save_16bit_model: true
|
||||
zero_stage: 3
|
||||
distributed_type: DEEPSPEED
|
||||
downcast_bf16: 'no'
|
||||
machine_rank: 0
|
||||
main_training_function: main
|
||||
mixed_precision: bf16
|
||||
num_machines: 1
|
||||
num_processes: 8
|
||||
rdzv_backend: static
|
||||
same_network: true
|
||||
tpu_env: []
|
||||
tpu_use_cluster: false
|
||||
tpu_use_sudo: false
|
||||
use_cpu: false
|
||||
```
|
||||
|
||||
## Launch command
|
||||
|
||||
The launch command is available at [run_peft_deepspeed.sh](https://github.com/huggingface/peft/blob/main/examples/sft/run_peft_deepspeed.sh) and it is also shown below:
|
||||
```bash
|
||||
accelerate launch --config_file "configs/deepspeed_config.yaml" train.py \
|
||||
--seed 100 \
|
||||
--model_name_or_path "meta-llama/Llama-2-70b-hf" \
|
||||
--dataset_name "smangrul/ultrachat-10k-chatml" \
|
||||
--chat_template_format "chatml" \
|
||||
--add_special_tokens False \
|
||||
--append_concat_token False \
|
||||
--splits "train,test" \
|
||||
--max_seq_len 2048 \
|
||||
--num_train_epochs 1 \
|
||||
--logging_steps 5 \
|
||||
--log_level "info" \
|
||||
--logging_strategy "steps" \
|
||||
--eval_strategy "epoch" \
|
||||
--save_strategy "epoch" \
|
||||
--push_to_hub \
|
||||
--hub_private_repo True \
|
||||
--hub_strategy "every_save" \
|
||||
--bf16 True \
|
||||
--packing True \
|
||||
--learning_rate 1e-4 \
|
||||
--lr_scheduler_type "cosine" \
|
||||
--weight_decay 1e-4 \
|
||||
--warmup_steps 0 \
|
||||
--max_grad_norm 1.0 \
|
||||
--output_dir "llama-sft-lora-deepspeed" \
|
||||
--per_device_train_batch_size 8 \
|
||||
--per_device_eval_batch_size 8 \
|
||||
--gradient_accumulation_steps 4 \
|
||||
--gradient_checkpointing True \
|
||||
--use_reentrant False \
|
||||
--dataset_text_field "content" \
|
||||
--use_flash_attn True \
|
||||
--use_peft_lora True \
|
||||
--lora_r 8 \
|
||||
--lora_alpha 16 \
|
||||
--lora_dropout 0.1 \
|
||||
--lora_target_modules "all-linear" \
|
||||
--use_4bit_quantization False
|
||||
```
|
||||
|
||||
Notice that we are using LoRA with rank=8, alpha=16 and targeting all linear layers. We are passing the deepspeed config file and finetuning 70B Llama model on a subset of the ultrachat dataset.
|
||||
|
||||
## The important parts
|
||||
|
||||
Let's dive a little deeper into the script so you can see what's going on, and understand how it works.
|
||||
|
||||
The first thing to know is that the script uses DeepSpeed for distributed training as the DeepSpeed config has been passed. The [`~trl.SFTTrainer`] class handles all the heavy lifting of creating the PEFT model using the peft config that is passed. After that, when you call `trainer.train()`, [`~trl.SFTTrainer`] internally uses 🤗 Accelerate to prepare the model, optimizer and trainer using the DeepSpeed config to create DeepSpeed engine which is then trained. The main code snippet is below:
|
||||
|
||||
```python
|
||||
# trainer
|
||||
trainer = SFTTrainer(
|
||||
model=model,
|
||||
processing_class=tokenizer,
|
||||
args=training_args,
|
||||
train_dataset=train_dataset,
|
||||
eval_dataset=eval_dataset,
|
||||
peft_config=peft_config,
|
||||
)
|
||||
trainer.accelerator.print(f"{trainer.model}")
|
||||
|
||||
# train
|
||||
checkpoint = None
|
||||
if training_args.resume_from_checkpoint is not None:
|
||||
checkpoint = training_args.resume_from_checkpoint
|
||||
trainer.train(resume_from_checkpoint=checkpoint)
|
||||
|
||||
# saving final model
|
||||
trainer.save_model()
|
||||
```
|
||||
|
||||
## Memory usage
|
||||
|
||||
In the above example, the memory consumed per GPU is 64 GB (80%) as seen in the screenshot below:
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/peft_deepspeed_mem_usage.png"/>
|
||||
</div>
|
||||
<small>GPU memory usage for the training run</small>
|
||||
|
||||
## More resources
|
||||
You can also refer this blog post [Falcon 180B Finetuning using 🤗 PEFT and DeepSpeed](https://medium.com/@sourabmangrulkar/falcon-180b-finetuning-using-peft-and-deepspeed-b92643091d99) on how to finetune 180B Falcon model on 16 A100 GPUs on 2 machines.
|
||||
|
||||
|
||||
# Use PEFT QLoRA and DeepSpeed with ZeRO3 for finetuning large models on multiple GPUs
|
||||
|
||||
In this section, we will look at how to use QLoRA and DeepSpeed Stage-3 for finetuning 70B llama model on 2X40GB GPUs.
|
||||
For this, we first need `bitsandbytes>=0.43.3`, `accelerate>=1.0.1`, `transformers>4.44.2`, `trl>0.11.4` and `peft>0.13.0`. We need to set `zero3_init_flag` to true when using Accelerate config. Below is the config which can be found at [deepspeed_config_z3_qlora.yaml](https://github.com/huggingface/peft/blob/main/examples/sft/configs/deepspeed_config_z3_qlora.yaml):
|
||||
|
||||
```yml
|
||||
compute_environment: LOCAL_MACHINE
|
||||
debug: false
|
||||
deepspeed_config:
|
||||
deepspeed_multinode_launcher: standard
|
||||
offload_optimizer_device: none
|
||||
offload_param_device: none
|
||||
zero3_init_flag: true
|
||||
zero3_save_16bit_model: true
|
||||
zero_stage: 3
|
||||
distributed_type: DEEPSPEED
|
||||
downcast_bf16: 'no'
|
||||
machine_rank: 0
|
||||
main_training_function: main
|
||||
mixed_precision: bf16
|
||||
num_machines: 1
|
||||
num_processes: 2
|
||||
rdzv_backend: static
|
||||
same_network: true
|
||||
tpu_env: []
|
||||
tpu_use_cluster: false
|
||||
tpu_use_sudo: false
|
||||
use_cpu: false
|
||||
```
|
||||
|
||||
Launch command is given below which is available at [run_peft_qlora_deepspeed_stage3.sh](https://github.com/huggingface/peft/blob/main/examples/sft/run_peft_qlora_deepspeed_stage3.sh):
|
||||
```
|
||||
accelerate launch --config_file "configs/deepspeed_config_z3_qlora.yaml" train.py \
|
||||
--seed 100 \
|
||||
--model_name_or_path "meta-llama/Llama-2-70b-hf" \
|
||||
--dataset_name "smangrul/ultrachat-10k-chatml" \
|
||||
--chat_template_format "chatml" \
|
||||
--add_special_tokens False \
|
||||
--append_concat_token False \
|
||||
--splits "train,test" \
|
||||
--max_seq_len 2048 \
|
||||
--num_train_epochs 1 \
|
||||
--logging_steps 5 \
|
||||
--log_level "info" \
|
||||
--logging_strategy "steps" \
|
||||
--eval_strategy "epoch" \
|
||||
--save_strategy "epoch" \
|
||||
--push_to_hub \
|
||||
--hub_private_repo True \
|
||||
--hub_strategy "every_save" \
|
||||
--bf16 True \
|
||||
--packing True \
|
||||
--learning_rate 1e-4 \
|
||||
--lr_scheduler_type "cosine" \
|
||||
--weight_decay 1e-4 \
|
||||
--warmup_steps 0 \
|
||||
--max_grad_norm 1.0 \
|
||||
--output_dir "llama-sft-qlora-dsz3" \
|
||||
--per_device_train_batch_size 2 \
|
||||
--per_device_eval_batch_size 2 \
|
||||
--gradient_accumulation_steps 2 \
|
||||
--gradient_checkpointing True \
|
||||
--use_reentrant True \
|
||||
--dataset_text_field "content" \
|
||||
--use_flash_attn True \
|
||||
--use_peft_lora True \
|
||||
--lora_r 8 \
|
||||
--lora_alpha 16 \
|
||||
--lora_dropout 0.1 \
|
||||
--lora_target_modules "all-linear" \
|
||||
--use_4bit_quantization True \
|
||||
--use_nested_quant True \
|
||||
--bnb_4bit_compute_dtype "bfloat16" \
|
||||
--bnb_4bit_quant_storage_dtype "bfloat16"
|
||||
```
|
||||
|
||||
Notice the new argument being passed `bnb_4bit_quant_storage_dtype` which denotes the data type for packing the 4-bit parameters. For example, when it is set to `bfloat16`, **32/4 = 8** 4-bit params are packed together post quantization.
|
||||
|
||||
In terms of training code, the important code changes are:
|
||||
|
||||
```diff
|
||||
...
|
||||
|
||||
bnb_config = BitsAndBytesConfig(
|
||||
load_in_4bit=args.use_4bit_quantization,
|
||||
bnb_4bit_quant_type=args.bnb_4bit_quant_type,
|
||||
bnb_4bit_compute_dtype=compute_dtype,
|
||||
bnb_4bit_use_double_quant=args.use_nested_quant,
|
||||
+ bnb_4bit_quant_storage=quant_storage_dtype,
|
||||
)
|
||||
|
||||
...
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained(
|
||||
args.model_name_or_path,
|
||||
quantization_config=bnb_config,
|
||||
trust_remote_code=True,
|
||||
attn_implementation="flash_attention_2" if args.use_flash_attn else "eager",
|
||||
+ dtype=quant_storage_dtype or torch.float32,
|
||||
)
|
||||
```
|
||||
|
||||
Notice that `dtype` for `AutoModelForCausalLM` is same as the `bnb_4bit_quant_storage` data type. That's it. Everything else is handled by Trainer and TRL.
|
||||
|
||||
## Memory usage
|
||||
|
||||
In the above example, the memory consumed per GPU is **36.6 GB**. Therefore, what took 8X80GB GPUs with DeepSpeed Stage 3+LoRA and a couple of 80GB GPUs with DDP+QLoRA now requires 2X40GB GPUs. This makes finetuning of large models more accessible.
|
||||
|
||||
# Use PEFT and DeepSpeed with ZeRO3 and CPU Offloading for finetuning large models on a single GPU
|
||||
This section of guide will help you learn how to use our DeepSpeed [training script](https://github.com/huggingface/peft/blob/main/examples/conditional_generation/peft_lora_seq2seq_accelerate_ds_zero3_offload.py). You'll configure the script to train a large model for conditional generation with ZeRO-3 and CPU Offload.
|
||||
|
||||
> [!TIP]
|
||||
> 💡 To help you get started, check out our example training scripts for [causal language modeling](https://github.com/huggingface/peft/blob/main/examples/causal_language_modeling/peft_lora_clm_accelerate_ds_zero3_offload.py) and [conditional generation](https://github.com/huggingface/peft/blob/main/examples/conditional_generation/peft_lora_seq2seq_accelerate_ds_zero3_offload.py). You can adapt these scripts for your own applications or even use them out of the box if your task is similar to the one in the scripts.
|
||||
|
||||
## Configuration
|
||||
|
||||
Start by running the following command to [create a DeepSpeed configuration file](https://huggingface.co/docs/accelerate/quicktour#launching-your-distributed-script) with 🤗 Accelerate. The `--config_file` flag allows you to save the configuration file to a specific location, otherwise it is saved as a `default_config.yaml` file in the 🤗 Accelerate cache.
|
||||
|
||||
The configuration file is used to set the default options when you launch the training script.
|
||||
|
||||
```bash
|
||||
accelerate config --config_file ds_zero3_cpu.yaml
|
||||
```
|
||||
|
||||
You'll be asked a few questions about your setup, and configure the following arguments. In this example, you'll use ZeRO-3 along with CPU-Offload so make sure you pick those options.
|
||||
|
||||
```bash
|
||||
`zero_stage`: [0] Disabled, [1] optimizer state partitioning, [2] optimizer+gradient state partitioning and [3] optimizer+gradient+parameter partitioning
|
||||
`gradient_accumulation_steps`: Number of training steps to accumulate gradients before averaging and applying them.
|
||||
`gradient_clipping`: Enable gradient clipping with value.
|
||||
`offload_optimizer_device`: [none] Disable optimizer offloading, [cpu] offload optimizer to CPU, [nvme] offload optimizer to NVMe SSD. Only applicable with ZeRO >= Stage-2.
|
||||
`offload_param_device`: [none] Disable parameter offloading, [cpu] offload parameters to CPU, [nvme] offload parameters to NVMe SSD. Only applicable with ZeRO Stage-3.
|
||||
`zero3_init_flag`: Decides whether to enable `deepspeed.zero.Init` for constructing massive models. Only applicable with ZeRO Stage-3.
|
||||
`zero3_save_16bit_model`: Decides whether to save 16-bit model weights when using ZeRO Stage-3.
|
||||
`mixed_precision`: `no` for FP32 training, `fp16` for FP16 mixed-precision training and `bf16` for BF16 mixed-precision training.
|
||||
```
|
||||
|
||||
An example [configuration file](https://github.com/huggingface/peft/blob/main/examples/conditional_generation/accelerate_ds_zero3_cpu_offload_config.yaml) might look like the following. The most important thing to notice is that `zero_stage` is set to `3`, and `offload_optimizer_device` and `offload_param_device` are set to the `cpu`.
|
||||
|
||||
```yml
|
||||
compute_environment: LOCAL_MACHINE
|
||||
deepspeed_config:
|
||||
gradient_accumulation_steps: 1
|
||||
gradient_clipping: 1.0
|
||||
offload_optimizer_device: cpu
|
||||
offload_param_device: cpu
|
||||
zero3_init_flag: true
|
||||
zero3_save_16bit_model: true
|
||||
zero_stage: 3
|
||||
distributed_type: DEEPSPEED
|
||||
downcast_bf16: 'no'
|
||||
dynamo_backend: 'NO'
|
||||
fsdp_config: {}
|
||||
machine_rank: 0
|
||||
main_training_function: main
|
||||
megatron_lm_config: {}
|
||||
mixed_precision: 'no'
|
||||
num_machines: 1
|
||||
num_processes: 1
|
||||
rdzv_backend: static
|
||||
same_network: true
|
||||
use_cpu: false
|
||||
```
|
||||
|
||||
## The important parts
|
||||
|
||||
Let's dive a little deeper into the script so you can see what's going on, and understand how it works.
|
||||
|
||||
Within the [`main`](https://github.com/huggingface/peft/blob/2822398fbe896f25d4dac5e468624dc5fd65a51b/examples/conditional_generation/peft_lora_seq2seq_accelerate_ds_zero3_offload.py#L103) function, the script creates an [`~accelerate.Accelerator`] class to initialize all the necessary requirements for distributed training.
|
||||
|
||||
> [!TIP]
|
||||
> 💡 Feel free to change the model and dataset inside the `main` function. If your dataset format is different from the one in the script, you may also need to write your own preprocessing function.
|
||||
|
||||
The script also creates a configuration for the 🤗 PEFT method you're using, which in this case, is LoRA. The [`LoraConfig`] specifies the task type and important parameters such as the dimension of the low-rank matrices, the matrices scaling factor, and the dropout probability of the LoRA layers. If you want to use a different 🤗 PEFT method, make sure you replace `LoraConfig` with the appropriate [class](../package_reference/tuners).
|
||||
|
||||
```diff
|
||||
def main():
|
||||
+ accelerator = Accelerator()
|
||||
model_name_or_path = "facebook/bart-large"
|
||||
dataset_name = "twitter_complaints"
|
||||
+ peft_config = LoraConfig(
|
||||
task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1
|
||||
)
|
||||
```
|
||||
|
||||
Throughout the script, you'll see the [`~accelerate.Accelerator.main_process_first`] and [`~accelerate.Accelerator.wait_for_everyone`] functions which help control and synchronize when processes are executed.
|
||||
|
||||
The [`get_peft_model`] function takes a base model and the [`peft_config`] you prepared earlier to create a [`PeftModel`]:
|
||||
|
||||
```diff
|
||||
model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path)
|
||||
+ model = get_peft_model(model, peft_config)
|
||||
```
|
||||
|
||||
Pass all the relevant training objects to 🤗 Accelerate's [`~accelerate.Accelerator.prepare`] which makes sure everything is ready for training:
|
||||
|
||||
```py
|
||||
model, train_dataloader, eval_dataloader, test_dataloader, optimizer, lr_scheduler = accelerator.prepare(
|
||||
model, train_dataloader, eval_dataloader, test_dataloader, optimizer, lr_scheduler
|
||||
)
|
||||
```
|
||||
|
||||
The next bit of code checks whether the DeepSpeed plugin is used in the `Accelerator`, and if the plugin exists, then we check if we are using ZeRO-3. This conditional flag is used when calling `generate` function call during inference for syncing GPUs when the model parameters are sharded:
|
||||
|
||||
```py
|
||||
is_ds_zero_3 = False
|
||||
if getattr(accelerator.state, "deepspeed_plugin", None):
|
||||
is_ds_zero_3 = accelerator.state.deepspeed_plugin.zero_stage == 3
|
||||
```
|
||||
|
||||
Inside the training loop, the usual `loss.backward()` is replaced by 🤗 Accelerate's [`~accelerate.Accelerator.backward`] which uses the correct `backward()` method based on your configuration:
|
||||
|
||||
```diff
|
||||
for epoch in range(num_epochs):
|
||||
with TorchTracemalloc() as tracemalloc:
|
||||
model.train()
|
||||
total_loss = 0
|
||||
for step, batch in enumerate(tqdm(train_dataloader)):
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
total_loss += loss.detach().float()
|
||||
+ accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
lr_scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
That is all! The rest of the script handles the training loop, evaluation, and even pushes it to the Hub for you.
|
||||
|
||||
## Train
|
||||
|
||||
Run the following command to launch the training script. Earlier, you saved the configuration file to `ds_zero3_cpu.yaml`, so you'll need to pass the path to the launcher with the `--config_file` argument like this:
|
||||
|
||||
```bash
|
||||
accelerate launch --config_file ds_zero3_cpu.yaml examples/peft_lora_seq2seq_accelerate_ds_zero3_offload.py
|
||||
```
|
||||
|
||||
You'll see some output logs that track memory usage during training, and once it's completed, the script returns the accuracy and compares the predictions to the labels:
|
||||
|
||||
```bash
|
||||
GPU Memory before entering the train : 1916
|
||||
GPU Memory consumed at the end of the train (end-begin): 66
|
||||
GPU Peak Memory consumed during the train (max-begin): 7488
|
||||
GPU Total Peak Memory consumed during the train (max): 9404
|
||||
CPU Memory before entering the train : 19411
|
||||
CPU Memory consumed at the end of the train (end-begin): 0
|
||||
CPU Peak Memory consumed during the train (max-begin): 0
|
||||
CPU Total Peak Memory consumed during the train (max): 19411
|
||||
epoch=4: train_ppl=tensor(1.0705, device='cuda:0') train_epoch_loss=tensor(0.0681, device='cuda:0')
|
||||
100%|████████████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:27<00:00, 3.92s/it]
|
||||
GPU Memory before entering the eval : 1982
|
||||
GPU Memory consumed at the end of the eval (end-begin): -66
|
||||
GPU Peak Memory consumed during the eval (max-begin): 672
|
||||
GPU Total Peak Memory consumed during the eval (max): 2654
|
||||
CPU Memory before entering the eval : 19411
|
||||
CPU Memory consumed at the end of the eval (end-begin): 0
|
||||
CPU Peak Memory consumed during the eval (max-begin): 0
|
||||
CPU Total Peak Memory consumed during the eval (max): 19411
|
||||
accuracy=100.0
|
||||
eval_preds[:10]=['no complaint', 'no complaint', 'complaint', 'complaint', 'no complaint', 'no complaint', 'no complaint', 'complaint', 'complaint', 'no complaint']
|
||||
dataset['train'][label_column][:10]=['no complaint', 'no complaint', 'complaint', 'complaint', 'no complaint', 'no complaint', 'no complaint', 'complaint', 'complaint', 'no complaint']
|
||||
```
|
||||
|
||||
# Caveats
|
||||
1. Merging when using PEFT and DeepSpeed is currently unsupported and will raise error.
|
||||
2. When using CPU offloading, the major gains from using PEFT to shrink the optimizer states and gradients to that of the adapter weights would be realized on CPU RAM and there won't be savings with respect to GPU memory.
|
||||
3. DeepSpeed Stage 3 and qlora when used with CPU offloading leads to more GPU memory usage when compared to disabling CPU offloading.
|
||||
|
||||
> [!TIP]
|
||||
> 💡 When you have code that requires merging (and unmerging) of weights, try to manually collect the parameters with DeepSpeed Zero-3 beforehand:
|
||||
>
|
||||
> ```python
|
||||
> import deepspeed
|
||||
>
|
||||
> is_ds_zero_3 = ... # check if Zero-3
|
||||
>
|
||||
> with deepspeed.zero.GatheredParameters(list(model.parameters()), enabled= is_ds_zero_3):
|
||||
> model.merge_adapter()
|
||||
> # do whatever is needed, then unmerge in the same context if unmerging is required
|
||||
> ...
|
||||
> model.unmerge_adapter()
|
||||
> ```
|
||||
@@ -0,0 +1,285 @@
|
||||
<!--⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Fully Sharded Data Parallel
|
||||
|
||||
[Fully sharded data parallel](https://pytorch.org/docs/stable/fsdp.html) (FSDP) is developed for distributed training of large pretrained models up to 1T parameters. FSDP achieves this by sharding the model parameters, gradients, and optimizer states across data parallel processes and it can also offload sharded model parameters to a CPU. The memory efficiency afforded by FSDP allows you to scale training to larger batch or model sizes.
|
||||
|
||||
Both of these features are supported in 🤗 Accelerate, and you can use them with 🤗 PEFT.
|
||||
|
||||
# Use PEFT and FSDP
|
||||
This section of guide will help you learn how to use our DeepSpeed [training script](https://github.com/huggingface/peft/blob/main/examples/sft/train.py) for performing SFT. You'll configure the script to do SFT (supervised fine-tuning) of Llama-70B model with LoRA and FSDP on 8xH100 80GB GPUs on a single machine. You can configure it to scale to multiple machines by changing the accelerate config.
|
||||
|
||||
## Configuration
|
||||
|
||||
Start by running the following command to [create a FSDP configuration file](https://huggingface.co/docs/accelerate/quicktour#launching-your-distributed-script) with 🤗 Accelerate. The `--config_file` flag allows you to save the configuration file to a specific location, otherwise it is saved as a `default_config.yaml` file in the 🤗 Accelerate cache.
|
||||
|
||||
The configuration file is used to set the default options when you launch the training script.
|
||||
|
||||
```bash
|
||||
accelerate config --config_file fsdp_config.yaml
|
||||
```
|
||||
|
||||
You'll be asked a few questions about your setup, and configure the following arguments. In this example, you'll answer the questionnaire as shown in the image below.
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/fsdp-peft-config.png"/>
|
||||
</div>
|
||||
<small>Creating Accelerate's config to use FSDP</small>
|
||||
|
||||
Once this is done, the corresponding config should look like below and you can find it in config folder at [fsdp_config.yaml](https://github.com/huggingface/peft/blob/main/examples/sft/configs/fsdp_config.yaml):
|
||||
|
||||
```yml
|
||||
compute_environment: LOCAL_MACHINE
|
||||
debug: false
|
||||
distributed_type: FSDP
|
||||
downcast_bf16: 'no'
|
||||
fsdp_config:
|
||||
fsdp_auto_wrap_policy: TRANSFORMER_BASED_WRAP
|
||||
fsdp_backward_prefetch: BACKWARD_PRE
|
||||
fsdp_cpu_ram_efficient_loading: true
|
||||
fsdp_forward_prefetch: false
|
||||
fsdp_offload_params: false
|
||||
fsdp_sharding_strategy: FULL_SHARD
|
||||
fsdp_state_dict_type: SHARDED_STATE_DICT
|
||||
fsdp_sync_module_states: true
|
||||
fsdp_use_orig_params: false
|
||||
machine_rank: 0
|
||||
main_training_function: main
|
||||
mixed_precision: bf16
|
||||
num_machines: 1
|
||||
num_processes: 8
|
||||
rdzv_backend: static
|
||||
same_network: true
|
||||
tpu_env: []
|
||||
tpu_use_cluster: false
|
||||
tpu_use_sudo: false
|
||||
use_cpu: false
|
||||
```
|
||||
|
||||
## Launch command
|
||||
|
||||
The launch command is available at [run_peft_fsdp.sh](https://github.com/huggingface/peft/blob/main/examples/sft/run_peft_fsdp.sh) and it is also shown below:
|
||||
```bash
|
||||
accelerate launch --config_file "configs/fsdp_config.yaml" train.py \
|
||||
--seed 100 \
|
||||
--model_name_or_path "meta-llama/Llama-2-70b-hf" \
|
||||
--dataset_name "smangrul/ultrachat-10k-chatml" \
|
||||
--chat_template_format "chatml" \
|
||||
--add_special_tokens False \
|
||||
--append_concat_token False \
|
||||
--splits "train,test" \
|
||||
--max_seq_len 2048 \
|
||||
--num_train_epochs 1 \
|
||||
--logging_steps 5 \
|
||||
--log_level "info" \
|
||||
--logging_strategy "steps" \
|
||||
--eval_strategy "epoch" \
|
||||
--save_strategy "epoch" \
|
||||
--push_to_hub \
|
||||
--hub_private_repo True \
|
||||
--hub_strategy "every_save" \
|
||||
--bf16 True \
|
||||
--packing True \
|
||||
--learning_rate 1e-4 \
|
||||
--lr_scheduler_type "cosine" \
|
||||
--weight_decay 1e-4 \
|
||||
--warmup_steps 0 \
|
||||
--max_grad_norm 1.0 \
|
||||
--output_dir "llama-sft-lora-fsdp" \
|
||||
--per_device_train_batch_size 8 \
|
||||
--per_device_eval_batch_size 8 \
|
||||
--gradient_accumulation_steps 4 \
|
||||
--gradient_checkpointing True \
|
||||
--use_reentrant False \
|
||||
--dataset_text_field "content" \
|
||||
--use_flash_attn True \
|
||||
--use_peft_lora True \
|
||||
--lora_r 8 \
|
||||
--lora_alpha 16 \
|
||||
--lora_dropout 0.1 \
|
||||
--lora_target_modules "all-linear" \
|
||||
--use_4bit_quantization False
|
||||
```
|
||||
|
||||
Notice that we are using LoRA with rank=8, alpha=16 and targeting all linear layers. We are passing the FSDP config file and finetuning the 70B Llama model on a subset of the [ultrachat dataset](https://huggingface.co/datasets/HuggingFaceH4/ultrachat_200k).
|
||||
|
||||
## The important parts
|
||||
|
||||
Let's dive a little deeper into the script so you can see what's going on, and understand how it works.
|
||||
|
||||
The first thing to know is that the script uses FSDP for distributed training as the FSDP config has been passed. The [`~trl.SFTTrainer`] class handles all the heavy lifting of creating PEFT model using the peft config that is passed. After that when you call `trainer.train()`, Trainer internally uses 🤗 Accelerate to prepare model, optimizer and trainer using the FSDP config to create FSDP wrapped model which is then trained. The main code snippet is below:
|
||||
|
||||
```python
|
||||
# trainer
|
||||
trainer = SFTTrainer(
|
||||
model=model,
|
||||
processing_class=tokenizer,
|
||||
args=training_args,
|
||||
train_dataset=train_dataset,
|
||||
eval_dataset=eval_dataset,
|
||||
peft_config=peft_config,
|
||||
)
|
||||
trainer.accelerator.print(f"{trainer.model}")
|
||||
if model_args.use_peft_lora:
|
||||
# handle PEFT+FSDP case
|
||||
trainer.model.print_trainable_parameters()
|
||||
if getattr(trainer.accelerator.state, "fsdp_plugin", None):
|
||||
from peft.utils.other import fsdp_auto_wrap_policy
|
||||
|
||||
fsdp_plugin = trainer.accelerator.state.fsdp_plugin
|
||||
fsdp_plugin.auto_wrap_policy = fsdp_auto_wrap_policy(trainer.model)
|
||||
|
||||
# train
|
||||
checkpoint = None
|
||||
if training_args.resume_from_checkpoint is not None:
|
||||
checkpoint = training_args.resume_from_checkpoint
|
||||
trainer.train(resume_from_checkpoint=checkpoint)
|
||||
|
||||
# saving final model
|
||||
if trainer.is_fsdp_enabled:
|
||||
trainer.accelerator.state.fsdp_plugin.set_state_dict_type("FULL_STATE_DICT")
|
||||
trainer.save_model()
|
||||
```
|
||||
|
||||
|
||||
Here, one main thing to note currently when using FSDP with PEFT is that `use_orig_params` needs to be `False` to realize GPU memory savings. Due to `use_orig_params=False`, the auto wrap policy for FSDP needs to change so that trainable and non-trainable parameters are wrapped separately. This is done by the code snippt below which uses the util function `fsdp_auto_wrap_policy` from PEFT:
|
||||
|
||||
```
|
||||
if getattr(trainer.accelerator.state, "fsdp_plugin", None):
|
||||
from peft.utils.other import fsdp_auto_wrap_policy
|
||||
|
||||
fsdp_plugin = trainer.accelerator.state.fsdp_plugin
|
||||
fsdp_plugin.auto_wrap_policy = fsdp_auto_wrap_policy(trainer.model)
|
||||
```
|
||||
|
||||
## Memory usage
|
||||
|
||||
In the above example, the memory consumed per GPU is 72-80 GB (90-98%) as seen in the screenshot below. The slight increase in GPU memory at the end is when saving the model using `FULL_STATE_DICT` state dict type instead of the `SHARDED_STATE_DICT` so that the model has adapter weights that can be loaded normally with `from_pretrained` method during inference:
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/peft_fsdp_mem_usage.png"/>
|
||||
</div>
|
||||
<small>GPU memory usage for the training run</small>
|
||||
|
||||
# Use PEFT QLoRA and FSDP for finetuning large models on multiple GPUs
|
||||
|
||||
In this section, we will look at how to use QLoRA and FSDP for finetuning 70B llama model on 2X24GB GPUs. [Answer.AI](https://www.answer.ai/) in collaboration with bitsandbytes and Hugging Face 🤗 open sourced code enabling the usage of FSDP+QLoRA and explained the whole process in their insightful blogpost [You can now train a 70b language model at home](https://www.answer.ai/posts/2024-03-06-fsdp-qlora.html). This is now integrated in Hugging Face ecosystem.
|
||||
|
||||
For this, we first need `bitsandbytes>=0.43.3`, `accelerate>=1.0.1`, `transformers>4.44.2`, `trl>0.11.4` and `peft>0.13.0`. We need to set `fsdp_cpu_ram_efficient_loading=true`, `fsdp_use_orig_params=false` and `fsdp_offload_params=true`(cpu offloading) when using Accelerate config. When not using accelerate launcher, you can alternately set the environment variable `export FSDP_CPU_RAM_EFFICIENT_LOADING=true`. Here, we will be using accelerate config and below is the config which can be found at [fsdp_config_qlora.yaml](https://github.com/huggingface/peft/blob/main/examples/sft/configs/fsdp_config_qlora.yaml):
|
||||
|
||||
```yml
|
||||
compute_environment: LOCAL_MACHINE
|
||||
debug: false
|
||||
distributed_type: FSDP
|
||||
downcast_bf16: 'no'
|
||||
fsdp_config:
|
||||
fsdp_auto_wrap_policy: TRANSFORMER_BASED_WRAP
|
||||
fsdp_backward_prefetch: BACKWARD_PRE
|
||||
fsdp_cpu_ram_efficient_loading: true
|
||||
fsdp_forward_prefetch: false
|
||||
fsdp_offload_params: true
|
||||
fsdp_sharding_strategy: FULL_SHARD
|
||||
fsdp_state_dict_type: SHARDED_STATE_DICT
|
||||
fsdp_sync_module_states: true
|
||||
fsdp_use_orig_params: false
|
||||
machine_rank: 0
|
||||
main_training_function: main
|
||||
mixed_precision: 'no'
|
||||
num_machines: 1
|
||||
num_processes: 2
|
||||
rdzv_backend: static
|
||||
same_network: true
|
||||
tpu_env: []
|
||||
tpu_use_cluster: false
|
||||
tpu_use_sudo: false
|
||||
use_cpu: false
|
||||
```
|
||||
|
||||
Launch command is given below which is available at [run_peft_qlora_fsdp.sh](https://github.com/huggingface/peft/blob/main/examples/sft/run_peft_qlora_fsdp.sh):
|
||||
```
|
||||
accelerate launch --config_file "configs/fsdp_config_qlora.yaml" train.py \
|
||||
--seed 100 \
|
||||
--model_name_or_path "meta-llama/Llama-2-70b-hf" \
|
||||
--dataset_name "smangrul/ultrachat-10k-chatml" \
|
||||
--chat_template_format "chatml" \
|
||||
--add_special_tokens False \
|
||||
--append_concat_token False \
|
||||
--splits "train,test" \
|
||||
--max_seq_len 2048 \
|
||||
--num_train_epochs 1 \
|
||||
--logging_steps 5 \
|
||||
--log_level "info" \
|
||||
--logging_strategy "steps" \
|
||||
--eval_strategy "epoch" \
|
||||
--save_strategy "epoch" \
|
||||
--push_to_hub \
|
||||
--hub_private_repo True \
|
||||
--hub_strategy "every_save" \
|
||||
--bf16 True \
|
||||
--packing True \
|
||||
--learning_rate 1e-4 \
|
||||
--lr_scheduler_type "cosine" \
|
||||
--weight_decay 1e-4 \
|
||||
--warmup_steps 0 \
|
||||
--max_grad_norm 1.0 \
|
||||
--output_dir "llama-sft-qlora-fsdp" \
|
||||
--per_device_train_batch_size 2 \
|
||||
--per_device_eval_batch_size 2 \
|
||||
--gradient_accumulation_steps 2 \
|
||||
--gradient_checkpointing True \
|
||||
--use_reentrant True \
|
||||
--dataset_text_field "content" \
|
||||
--use_flash_attn True \
|
||||
--use_peft_lora True \
|
||||
--lora_r 8 \
|
||||
--lora_alpha 16 \
|
||||
--lora_dropout 0.1 \
|
||||
--lora_target_modules "all-linear" \
|
||||
--use_4bit_quantization True \
|
||||
--use_nested_quant True \
|
||||
--bnb_4bit_compute_dtype "bfloat16" \
|
||||
--bnb_4bit_quant_storage_dtype "bfloat16"
|
||||
```
|
||||
|
||||
Notice the new argument being passed, `bnb_4bit_quant_storage_dtype`, which denotes the data type for packing the 4-bit parameters. For example, when it is set to `bfloat16`, **16/4 = 4** 4-bit params are packed together post quantization. When using mixed precision training with `bfloat16`, `bnb_4bit_quant_storage_dtype` can be either `bfloat16` for pure `bfloat16` finetuning, or `float32` for automatic mixed precision (this consumes more GPU memory). When using mixed precision training with `float16`, `bnb_4bit_quant_storage_dtype` should be set to `float32` for stable automatic mixed precision training.
|
||||
|
||||
In terms of training code, the important code changes are:
|
||||
|
||||
```diff
|
||||
...
|
||||
|
||||
bnb_config = BitsAndBytesConfig(
|
||||
load_in_4bit=args.use_4bit_quantization,
|
||||
bnb_4bit_quant_type=args.bnb_4bit_quant_type,
|
||||
bnb_4bit_compute_dtype=compute_dtype,
|
||||
bnb_4bit_use_double_quant=args.use_nested_quant,
|
||||
+ bnb_4bit_quant_storage=quant_storage_dtype,
|
||||
)
|
||||
|
||||
...
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained(
|
||||
args.model_name_or_path,
|
||||
quantization_config=bnb_config,
|
||||
trust_remote_code=True,
|
||||
attn_implementation="flash_attention_2" if args.use_flash_attn else "eager",
|
||||
+ dtype=quant_storage_dtype or torch.float32,
|
||||
)
|
||||
```
|
||||
|
||||
Notice that `dtype` for `AutoModelForCausalLM` is same as the `bnb_4bit_quant_storage` data type. That's it. Everything else is handled by Trainer and TRL.
|
||||
|
||||
## Memory usage
|
||||
|
||||
In the above example, the memory consumed per GPU is **19.6 GB** while CPU RAM usage is around **107 GB**. When disabling CPU offloading, the GPU memory usage is **35.6 GB/ GPU**. Therefore, what took 16X80GB GPUs for full finetuning, 8X80GB GPUs with FSDP+LoRA, and a couple of 80GB GPUs with DDP+QLoRA, now requires 2X24GB GPUs. This makes finetuning of large models more accessible.
|
||||
|
||||
## More resources
|
||||
You can also refer the [llama-recipes](https://github.com/facebookresearch/llama-recipes/?tab=readme-ov-file#fine-tuning) repo and [Getting started with Llama](https://llama.meta.com/get-started/#fine-tuning) guide on how to finetune using FSDP and PEFT.
|
||||
|
||||
## Caveats
|
||||
1. Merging when using PEFT and FSDP is currently unsupported and will raise error.
|
||||
2. Passing `modules_to_save` config parameter to is untested at present.
|
||||
3. GPU Memory saving when using CPU Offloading is untested at present.
|
||||
4. When using FSDP+QLoRA, `paged_adamw_8bit` currently results in an error when saving a checkpoint.
|
||||
5. DoRA training with FSDP should work (albeit at lower speed than LoRA). If combined with bitsandbytes (QDoRA), 4-bit quantization should also work, but 8-bit quantization has known issues and is not recommended.
|
||||
@@ -0,0 +1,244 @@
|
||||
<!--Copyright 2024 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# PEFT checkpoint format
|
||||
|
||||
This document describes how PEFT's checkpoint files are structured and how to convert between the PEFT format and other formats.
|
||||
|
||||
## PEFT files
|
||||
|
||||
PEFT (parameter-efficient fine-tuning) methods only update a small subset of a model's parameters rather than all of them. This is nice because checkpoint files can generally be much smaller than the original model files and are easier to store and share. However, this also means that to load a PEFT model, you need to have the original model available as well.
|
||||
|
||||
When you call [`~PeftModel.save_pretrained`] on a PEFT model, the PEFT model saves three files, described below:
|
||||
|
||||
1. `adapter_model.safetensors` or `adapter_model.bin`
|
||||
|
||||
By default, the model is saved in the `safetensors` format, a secure alternative to the `bin` format, which is known to be susceptible to [security vulnerabilities](https://huggingface.co/docs/hub/security-pickle) because it uses the pickle utility under the hood. Both formats store the same `state_dict` though, and are interchangeable.
|
||||
|
||||
The `state_dict` only contains the parameters of the adapter module, not the base model. To illustrate the difference in size, a normal BERT model requires ~420MB of disk space, whereas an IA³ adapter on top of this BERT model only requires ~260KB.
|
||||
|
||||
2. `adapter_config.json`
|
||||
|
||||
The `adapter_config.json` file contains the configuration of the adapter module, which is necessary to load the model. Below is an example of an `adapter_config.json` for an IA³ adapter with standard settings applied to a BERT model:
|
||||
|
||||
```json
|
||||
{
|
||||
"auto_mapping": {
|
||||
"base_model_class": "BertModel",
|
||||
"parent_library": "transformers.models.bert.modeling_bert"
|
||||
},
|
||||
"base_model_name_or_path": "bert-base-uncased",
|
||||
"fan_in_fan_out": false,
|
||||
"feedforward_modules": [
|
||||
"output.dense"
|
||||
],
|
||||
"inference_mode": true,
|
||||
"init_ia3_weights": true,
|
||||
"modules_to_save": null,
|
||||
"peft_type": "IA3",
|
||||
"revision": null,
|
||||
"target_modules": [
|
||||
"key",
|
||||
"value",
|
||||
"output.dense"
|
||||
],
|
||||
"task_type": null
|
||||
}
|
||||
```
|
||||
|
||||
The configuration file contains:
|
||||
|
||||
- the adapter module type stored, `"peft_type": "IA3"`
|
||||
- information about the base model like `"base_model_name_or_path": "bert-base-uncased"`
|
||||
- the revision of the model (if any), `"revision": null`
|
||||
|
||||
If the base model is not a pretrained Transformers model, the latter two entries will be `null`. Other than that, the settings are all related to the specific IA³ adapter that was used to fine-tune the model.
|
||||
|
||||
3. `README.md`
|
||||
|
||||
The generated `README.md` is the model card of a PEFT model and contains a few pre-filled entries. The intent of this is to make it easier to share the model with others and to provide some basic information about the model. This file is not needed to load the model.
|
||||
|
||||
## Convert to PEFT format
|
||||
|
||||
When converting from another format to the PEFT format, we require both the `adapter_model.safetensors` (or `adapter_model.bin`) file and the `adapter_config.json` file.
|
||||
|
||||
### adapter_model
|
||||
|
||||
For the model weights, it is important to use the correct mapping from parameter name to value for PEFT to load the file. Getting this mapping right is an exercise in checking the implementation details, as there is no generally agreed upon format for PEFT adapters.
|
||||
|
||||
Fortunately, figuring out this mapping is not overly complicated for common base cases. Let's look at a concrete example, the [`LoraLayer`](https://github.com/huggingface/peft/blob/main/src/peft/tuners/lora/layer.py):
|
||||
|
||||
```python
|
||||
# showing only part of the code
|
||||
|
||||
class LoraLayer(BaseTunerLayer):
|
||||
# All names of layers that may contain (trainable) adapter weights
|
||||
adapter_layer_names = ("lora_A", "lora_B", "lora_embedding_A", "lora_embedding_B")
|
||||
# All names of other parameters that may contain adapter-related parameters
|
||||
other_param_names = ("r", "lora_alpha", "scaling", "lora_dropout")
|
||||
|
||||
def __init__(self, base_layer: nn.Module, **kwargs) -> None:
|
||||
self.base_layer = base_layer
|
||||
self.r = {}
|
||||
self.lora_alpha = {}
|
||||
self.scaling = {}
|
||||
self.lora_dropout = nn.ModuleDict({})
|
||||
self.lora_A = nn.ModuleDict({})
|
||||
self.lora_B = nn.ModuleDict({})
|
||||
# For Embedding layer
|
||||
self.lora_embedding_A = nn.ParameterDict({})
|
||||
self.lora_embedding_B = nn.ParameterDict({})
|
||||
# Mark the weight as unmerged
|
||||
self._disable_adapters = False
|
||||
self.merged_adapters = []
|
||||
self.use_dora: dict[str, bool] = {}
|
||||
self.lora_magnitude_vector: Optional[torch.nn.ParameterDict] = None # for DoRA
|
||||
self._caches: dict[str, Any] = {}
|
||||
self.kwargs = kwargs
|
||||
```
|
||||
|
||||
In the `__init__` code used by all `LoraLayer` classes in PEFT, there are a bunch of parameters used to initialize the model, but only a few are relevant for the checkpoint file: `lora_A`, `lora_B`, `lora_embedding_A`, and `lora_embedding_B`. These parameters are listed in the class attribute `adapter_layer_names` and contain the learnable parameters, so they must be included in the checkpoint file. All the other parameters, like the rank `r`, are derived from the `adapter_config.json` and must be included there (unless the default value is used).
|
||||
|
||||
Let's check the `state_dict` of a PEFT LoRA model applied to BERT. When printing the first five keys using the default LoRA settings (the remaining keys are the same, just with different layer numbers), we get:
|
||||
|
||||
- `base_model.model.encoder.layer.0.attention.self.query.lora_A.weight`
|
||||
- `base_model.model.encoder.layer.0.attention.self.query.lora_B.weight`
|
||||
- `base_model.model.encoder.layer.0.attention.self.value.lora_A.weight`
|
||||
- `base_model.model.encoder.layer.0.attention.self.value.lora_B.weight`
|
||||
- `base_model.model.encoder.layer.1.attention.self.query.lora_A.weight`
|
||||
- etc.
|
||||
|
||||
Let's break this down:
|
||||
|
||||
- By default, for BERT models, LoRA is applied to the `query` and `value` layers of the attention module. This is why you see `attention.self.query` and `attention.self.value` in the key names for each layer.
|
||||
- LoRA decomposes the weights into two low-rank matrices, `lora_A` and `lora_B`. This is where `lora_A` and `lora_B` come from in the key names.
|
||||
- These LoRA matrices are implemented as `nn.Linear` layers, so the parameters are stored in the `.weight` attribute (`lora_A.weight`, `lora_B.weight`).
|
||||
- By default, LoRA isn't applied to BERT's embedding layer, so there are _no entries_ for `lora_A_embedding` and `lora_B_embedding`.
|
||||
- The keys of the `state_dict` always start with `"base_model.model."`. The reason is that, in PEFT, we wrap the base model inside a tuner-specific model (`LoraModel` in this case), which itself is wrapped in a general PEFT model (`PeftModel`). For this reason, these two prefixes are added to the keys. When converting to the PEFT format, it is required to add these prefixes.
|
||||
|
||||
> [!TIP]
|
||||
> This last point is not true for prefix tuning techniques like prompt tuning. There, the extra embeddings are directly stored in the `state_dict` without any prefixes added to the keys.
|
||||
|
||||
When inspecting the parameter names in the loaded model, you might be surprised to find that they look a bit different, e.g. `base_model.model.encoder.layer.0.attention.self.query.lora_A.default.weight`. The difference is the *`.default`* part in the second to last segment. This part exists because PEFT generally allows the addition of multiple adapters at once (using an `nn.ModuleDict` or `nn.ParameterDict` to store them). For example, if you add another adapter called "other", the key for that adapter would be `base_model.model.encoder.layer.0.attention.self.query.lora_A.other.weight`.
|
||||
|
||||
When you call [`~PeftModel.save_pretrained`], the adapter name is stripped from the keys. The reason is that the adapter name is not an important part of the model architecture; it is just an arbitrary name. When loading the adapter, you could choose a totally different name, and the model would still work the same way. This is why the adapter name is not stored in the checkpoint file.
|
||||
|
||||
> [!TIP]
|
||||
> If you call `save_pretrained("some/path")` and the adapter name is not `"default"`, the adapter is stored in a sub-directory with the same name as the adapter. So if the name is "other", it would be stored inside of `some/path/other`.
|
||||
|
||||
In some circumstances, deciding which values to add to the checkpoint file can become a bit more complicated. For example, in PEFT, DoRA is implemented as a special case of LoRA. If you want to convert a DoRA model to PEFT, you should create a LoRA checkpoint with extra entries for DoRA. You can see this in the `__init__` of the previous `LoraLayer` code:
|
||||
|
||||
```python
|
||||
self.lora_magnitude_vector: Optional[torch.nn.ParameterDict] = None # for DoRA
|
||||
```
|
||||
|
||||
This indicates that there is an optional extra parameter per layer for DoRA.
|
||||
|
||||
### adapter_config
|
||||
|
||||
All the other information needed to load a PEFT model is contained in the `adapter_config.json` file. Let's check this file for a LoRA model applied to BERT:
|
||||
|
||||
```json
|
||||
{
|
||||
"alpha_pattern": {},
|
||||
"auto_mapping": {
|
||||
"base_model_class": "BertModel",
|
||||
"parent_library": "transformers.models.bert.modeling_bert"
|
||||
},
|
||||
"base_model_name_or_path": "bert-base-uncased",
|
||||
"bias": "none",
|
||||
"fan_in_fan_out": false,
|
||||
"inference_mode": true,
|
||||
"init_lora_weights": true,
|
||||
"layer_replication": null,
|
||||
"layers_pattern": null,
|
||||
"layers_to_transform": null,
|
||||
"loftq_config": {},
|
||||
"lora_alpha": 8,
|
||||
"lora_dropout": 0.0,
|
||||
"megatron_config": null,
|
||||
"megatron_core": "megatron.core",
|
||||
"modules_to_save": null,
|
||||
"peft_type": "LORA",
|
||||
"r": 8,
|
||||
"rank_pattern": {},
|
||||
"revision": null,
|
||||
"target_modules": [
|
||||
"query",
|
||||
"value"
|
||||
],
|
||||
"task_type": null,
|
||||
"use_dora": false,
|
||||
"use_rslora": false
|
||||
}
|
||||
```
|
||||
|
||||
This contains a lot of entries, and at first glance, it could feel overwhelming to figure out all the right values to put in there. However, most of the entries are not necessary to load the model. This is either because they use the default values and don't need to be added or because they only affect the initialization of the LoRA weights, which is irrelevant when it comes to loading the model. If you find that you don't know what a specific parameter does, e.g., `"use_rslora",` don't add it, and you should be fine. Also note that as more options are added, this file will get more entries in the future, but it should be backward compatible.
|
||||
|
||||
At the minimum, you should include the following entries:
|
||||
|
||||
```json
|
||||
{
|
||||
"target_modules": ["query", "value"],
|
||||
"peft_type": "LORA"
|
||||
}
|
||||
```
|
||||
|
||||
However, adding as many entries as possible, like the rank `r` or the `base_model_name_or_path` (if it's a Transformers model) is recommended. This information can help others understand the model better and share it more easily. To check which keys and values are expected, check out the [config.py](https://github.com/huggingface/peft/blob/main/src/peft/tuners/lora/config.py) file (as an example, this is the config file for LoRA) in the PEFT source code.
|
||||
|
||||
## Model storage
|
||||
|
||||
In some circumstances, you might want to store the whole PEFT model, including the base weights. This can be necessary if, for instance, the base model is not available to the users trying to load the PEFT model. You can merge the weights first or convert it into a Transformer model.
|
||||
|
||||
### Merge the weights
|
||||
|
||||
The most straightforward way to store the whole PEFT model is to merge the adapter weights into the base weights:
|
||||
|
||||
```python
|
||||
merged_model = model.merge_and_unload()
|
||||
merged_model.save_pretrained(...)
|
||||
```
|
||||
|
||||
There are some disadvantages to this approach, though:
|
||||
|
||||
- Once [`~LoraModel.merge_and_unload`] is called, you get a basic model without any PEFT-specific functionality. This means you can't use any of the PEFT-specific methods anymore.
|
||||
- You cannot unmerge the weights, load multiple adapters at once, disable the adapter, etc.
|
||||
- Not all PEFT methods support merging weights.
|
||||
- Some PEFT methods may generally allow merging, but not with specific settings (e.g. when using certain quantization techniques).
|
||||
- The whole model will be much larger than the PEFT model, as it will contain all the base weights as well.
|
||||
|
||||
But inference with a merged model should be a bit faster.
|
||||
|
||||
### Convert to a Transformers model
|
||||
|
||||
Another way to save the whole model, assuming the base model is a Transformers model, is to use this hacky approach to directly insert the PEFT weights into the base model and save it, which only works if you "trick" Transformers into believing the PEFT model is not a PEFT model. This only works with LoRA because other adapters are not implemented in Transformers.
|
||||
|
||||
```python
|
||||
model = ... # the PEFT model
|
||||
...
|
||||
# after you finish training the model, save it in a temporary location
|
||||
model.save_pretrained(<temp_location>)
|
||||
# now load this model directly into a transformers model, without the PEFT wrapper
|
||||
# the PEFT weights are directly injected into the base model
|
||||
model_loaded = AutoModel.from_pretrained(<temp_location>)
|
||||
# now make the loaded model believe that it is _not_ a PEFT model
|
||||
model_loaded._hf_peft_config_loaded = False
|
||||
# now when we save it, it will save the whole model
|
||||
model_loaded.save_pretrained(<final_location>)
|
||||
# or upload to Hugging Face Hub
|
||||
model_loaded.push_to_hub(<final_location>)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Contribute to PEFT
|
||||
|
||||
We are happy to accept contributions to PEFT. If you plan to contribute, please read this to make the process as smooth as possible.
|
||||
|
||||
## Installation
|
||||
|
||||
Follow these steps to start contributing:
|
||||
|
||||
1. Fork the [repository](https://github.com/huggingface/peft) by clicking on the 'Fork' button on the repository's page. This creates a copy of the code under your GitHub user account.
|
||||
|
||||
2. Clone your fork to your local disk, and add the base repository as a remote. The following command assumes you have your public SSH key uploaded to GitHub. See the following guide for more [information](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository).
|
||||
|
||||
```bash
|
||||
git clone git@github.com:<your Github handle>/peft.git
|
||||
cd peft
|
||||
git remote add upstream https://github.com/huggingface/peft.git
|
||||
```
|
||||
|
||||
3. Create a new branch to hold your development changes, and do this for every new PR you work on.
|
||||
|
||||
Start by synchronizing your `main` branch with the `upstream/main` branch (more details in the [GitHub Docs](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork)):
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git fetch upstream
|
||||
git merge upstream/main
|
||||
```
|
||||
|
||||
Once your `main` branch is synchronized, create a new branch from it:
|
||||
|
||||
```bash
|
||||
git checkout -b a-descriptive-name-for-my-changes
|
||||
```
|
||||
|
||||
**Do not** work on the `main` branch.
|
||||
|
||||
4. Set up a development environment by running the following command in a conda or a virtual environment you've created for working on this library:
|
||||
|
||||
```bash
|
||||
pip install -e ".[test]"
|
||||
```
|
||||
|
||||
(If PEFT was already installed in the virtual environment, remove it with `pip uninstall peft` before reinstalling it.)
|
||||
|
||||
If you are new to creating a pull request, follow the [Creating a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) guide by GitHub.
|
||||
|
||||
## Tests and code quality checks
|
||||
|
||||
Regardless of the contribution type (unless it’s only about the docs), you should run tests and code quality checks before creating a PR to ensure your contribution doesn’t break anything and follows the project standards.
|
||||
|
||||
We provide a Makefile to execute the necessary tests. Run the code below for the unit test:
|
||||
|
||||
```sh
|
||||
make test
|
||||
```
|
||||
|
||||
Run one of the following to either only check or check and fix code quality and style:
|
||||
|
||||
```sh
|
||||
make quality # just check
|
||||
make style # check and fix
|
||||
```
|
||||
|
||||
You can also set up [`pre-commit`](https://pre-commit.com/) to run these fixes
|
||||
automatically as Git commit hooks.
|
||||
|
||||
```bash
|
||||
$ pip install pre-commit
|
||||
$ pre-commit install
|
||||
```
|
||||
|
||||
Running all the tests can take a while, so during development it can be more efficient to only [run tests specific to your change](https://docs.pytest.org/en/6.2.x/usage.html#specifying-tests-selecting-tests), e.g. via:
|
||||
|
||||
```sh
|
||||
pytest tests/<test-file-name> -k <name-of-test>
|
||||
```
|
||||
|
||||
This should finish much quicker and allow for faster iteration.
|
||||
|
||||
If your change is specific to a hardware setting (e.g., it requires CUDA), take a look at [`tests/test_gpu_examples.py`](https://github.com/huggingface/peft/blob/1c1c7fdaa6e6abaa53939b865dee1eded82ad032/tests/test_gpu_examples.py) and [`tests/test_common_gpu.py`](https://github.com/huggingface/peft/blob/1c1c7fdaa6e6abaa53939b865dee1eded82ad032/tests/test_common_gpu.py) to see if it makes sense to add tests there. If your change could have an effect on saving and loading models, please run the tests with the `--regression` flag to trigger regression tests.
|
||||
|
||||
It can happen that while you’re working on your PR, the underlying code base changes due to other changes being merged. If that happens – especially when there is a merge conflict – please update your branch with the latest changes. This can be a merge or a rebase, and we'll squash and merge the PR once it’s ready. If possible, **avoid force pushes** to make reviews easier.
|
||||
|
||||
## PR description
|
||||
|
||||
When opening a PR, please provide a nice description of the change you're proposing. If it relates to other issues or PRs, please reference them. Providing a good description not only helps the reviewers review your code better and faster, it can also be used later (as a basis) for the commit message which helps with long term maintenance of the project.
|
||||
|
||||
If your code makes some non-trivial changes, it may also be a good idea to add comments to the code to explain those changes. For example, if you had to iterate on your implementation multiple times because the most obvious way didn’t work, it’s a good indication that a code comment is needed.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
Please give a description of the circumstances that led to the bug. If there is an existing issue, please link to it (e.g., “Resolves #12345”).
|
||||
|
||||
Ideally when a bugfix is provided, it should be accompanied by a test for the bug. The test should fail with the current code and pass with the bugfix. Add a comment to the test that references the issue or PR. Without a test, it is more difficult to prevent regressions in the future.
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
We are happy to have fixes for broken links and missing or unclear documentation. Taking care of examples, making sure that they are up-to-date and running fine in this fast moving environment is also highly appreciated.
|
||||
|
||||
Please refrain from sending pull requests that *only* correct typing errors as these generally create more work than they safe. Such changes are better combined with more substantial fixes (such as fixing broken links or extending/updating documentation).
|
||||
|
||||
## Add a new PEFT fine-tuning method
|
||||
|
||||
New parameter-efficient fine-tuning methods are developed all the time. If you would like to add a new and promising method to PEFT, please follow these steps.
|
||||
|
||||
1. If you're _not_ an author of the original paper, check for existing implementations and double check with the authors that they don't plan to submit a PR themselves.
|
||||
2. Start with the core integration work listed below.
|
||||
3. Check recent commits for new PEFT methods being added to take as inspiration.
|
||||
4. It can be useful to open a draft PR early once the method basically works and first tests pass, then ask for feedback.
|
||||
5. After working through reviewer feedback, ping the reviewer so that they know the PR is ready to review.
|
||||
|
||||
### Core integration of a new PEFT method
|
||||
|
||||
- [ ] Open a proposal issue on `huggingface/peft` before investing too much work.
|
||||
- [ ] Link the source of the method, usually the final paper or another stable primary reference. We want to avoid work that is still under review, as the implementation should be stable.
|
||||
- [ ] Add a new `PeftType` entry in `src/peft/utils/peft_types.py`.
|
||||
- [ ] Create a new tuner package under `src/peft/tuners/` with the files your method needs (typically: `config.py`, `model.py`, `layer.py`, and `__init__.py`).
|
||||
- [ ] Register the method in the tuner `__init__.py` with `register_peft_method(...)`.
|
||||
- [ ] Export the new config/model from `src/peft/tuners/__init__.py` and `src/peft/__init__.py`.
|
||||
- [ ] If the method needs default target modules for Transformers models, add the mapping in `src/peft/utils/constants.py`.
|
||||
- [ ] Add the method to the test matrix in `tests/test_custom_models.py` as these are the broadest and quickest tests. Check that the tests pass with `pytest tests/test_custom_models.py -k <method-name> -v`, fix failures if any.
|
||||
- [ ] Run style/quality checks with `make style` before pushing.
|
||||
- [ ] In the PR description, explain the method, link the paper, summarize tradeoffs, and list what was added.
|
||||
|
||||
### Full PR to add a new PEFT method
|
||||
|
||||
- [ ] Ensure that the configuration arguments that are specific to the method are well named and explained, don't assume that the user knows the paper inside out.
|
||||
- [ ] Follow the naming and coding conventions of PEFT.
|
||||
- [ ] Ensure that you didn't accidentally check in unrelated changes, e.g. the code formatter changing unrelated files.
|
||||
- [ ] If some implementation choices are non-trivial, document them with a code comment.
|
||||
- [ ] Complete the full test suite (`test_config.py`, `test_decoder_models.py`, etc.) by adding the PEFT method to the test matrix. Ensure that the tests pass.
|
||||
- [ ] Add docs in `docs/source/package_reference/` with a short explanation, paper link, usage snippet, and autodoc blocks. Explain the pros and cons compared to other methods like LoRA. Register that doc page in `docs/source/_toctree.yml`.
|
||||
- [ ] Add a runnable example under `examples/` (can be a copy of an existing example), with a short `README.md`.
|
||||
- [ ] Check the benchmarks in `method_comparison/` and add experiment settings for your new method. This is a good place to sanity check that the PEFT method trains as expected. Include one or two reasonable benchmark configurations (one default, one optimized for the benchmark).
|
||||
- [ ] Recommended: Add generic quantization support. Instead of having to explicitly add quantization layer types for each quantization method, support generic quantization. As an example, check how it's implemented in [BOFT](https://github.com/huggingface/peft/tree/main/src/peft/tuners/boft). Extend https://github.com/huggingface/peft/blob/main/tests/test_quantization.py by adding your PEFT method there. Ask maintainers for help if needed.
|
||||
|
||||
## Add other features
|
||||
|
||||
It is best if you first open an issue on GitHub with a proposal to add the new feature. This way, you can discuss with the maintainers if it makes sense to add the feature before spending too much time on implementing it.
|
||||
|
||||
New features should generally be accompanied by tests and documentation or examples. Without the latter, users will have a hard time discovering your cool new feature.
|
||||
|
||||
Changes to the code should be implemented in a backward-compatible way. For example, existing code should continue to work the same way after the feature is merged.
|
||||
@@ -0,0 +1,304 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Custom models
|
||||
|
||||
Some fine-tuning techniques, such as prompt tuning, are specific to language models. That means in 🤗 PEFT, it is
|
||||
assumed a 🤗 Transformers model is being used. However, other fine-tuning techniques - like
|
||||
[LoRA](../package_reference/lora) - are not restricted to specific model types.
|
||||
|
||||
In this guide, we will see how LoRA can be applied to a multilayer perceptron, a computer vision model from the [timm](https://huggingface.co/docs/timm/index) library, or a new 🤗 Transformers architecture.
|
||||
|
||||
## Multilayer perceptron
|
||||
|
||||
Let's assume that we want to fine-tune a multilayer perceptron with LoRA. Here is the definition:
|
||||
|
||||
```python
|
||||
from torch import nn
|
||||
|
||||
|
||||
class MLP(nn.Module):
|
||||
def __init__(self, num_units_hidden=2000):
|
||||
super().__init__()
|
||||
self.seq = nn.Sequential(
|
||||
nn.Linear(20, num_units_hidden),
|
||||
nn.ReLU(),
|
||||
nn.Linear(num_units_hidden, num_units_hidden),
|
||||
nn.ReLU(),
|
||||
nn.Linear(num_units_hidden, 2),
|
||||
nn.LogSoftmax(dim=-1),
|
||||
)
|
||||
|
||||
def forward(self, X):
|
||||
return self.seq(X)
|
||||
```
|
||||
|
||||
This is a straightforward multilayer perceptron with an input layer, a hidden layer, and an output layer.
|
||||
|
||||
> [!TIP]
|
||||
> For this toy example, we choose an exceedingly large number of hidden units to highlight the efficiency gains
|
||||
> from PEFT, but those gains are in line with more realistic examples.
|
||||
|
||||
There are a few linear layers in this model that could be tuned with LoRA. When working with common 🤗 Transformers
|
||||
models, PEFT will know which layers to apply LoRA to, but in this case, it is up to us as a user to choose the layers.
|
||||
To determine the names of the layers to tune:
|
||||
|
||||
```python
|
||||
print([(n, type(m)) for n, m in MLP().named_modules()])
|
||||
```
|
||||
|
||||
This should print:
|
||||
|
||||
```
|
||||
[('', __main__.MLP),
|
||||
('seq', torch.nn.modules.container.Sequential),
|
||||
('seq.0', torch.nn.modules.linear.Linear),
|
||||
('seq.1', torch.nn.modules.activation.ReLU),
|
||||
('seq.2', torch.nn.modules.linear.Linear),
|
||||
('seq.3', torch.nn.modules.activation.ReLU),
|
||||
('seq.4', torch.nn.modules.linear.Linear),
|
||||
('seq.5', torch.nn.modules.activation.LogSoftmax)]
|
||||
```
|
||||
|
||||
Let's say we want to apply LoRA to the input layer and to the hidden layer, those are `'seq.0'` and `'seq.2'`. Moreover,
|
||||
let's assume we want to update the output layer without LoRA, that would be `'seq.4'`. The corresponding config would
|
||||
be:
|
||||
|
||||
```python
|
||||
from peft import LoraConfig
|
||||
|
||||
config = LoraConfig(
|
||||
target_modules=["seq.0", "seq.2"],
|
||||
modules_to_save=["seq.4"],
|
||||
)
|
||||
```
|
||||
|
||||
With that, we can create our PEFT model and check the fraction of parameters trained:
|
||||
|
||||
```python
|
||||
from peft import get_peft_model
|
||||
|
||||
model = MLP()
|
||||
peft_model = get_peft_model(model, config)
|
||||
peft_model.print_trainable_parameters()
|
||||
# prints trainable params: 56,164 || all params: 4,100,164 || trainable%: 1.369798866581922
|
||||
```
|
||||
|
||||
Finally, we can use any training framework we like, or write our own fit loop, to train the `peft_model`.
|
||||
|
||||
For a complete example, check out [this notebook](https://github.com/huggingface/peft/blob/main/examples/multilayer_perceptron/multilayer_perceptron_lora.ipynb).
|
||||
|
||||
## timm models
|
||||
|
||||
The [timm](https://huggingface.co/docs/timm/index) library contains a large number of pretrained computer vision models.
|
||||
Those can also be fine-tuned with PEFT. Let's check out how this works in practice.
|
||||
|
||||
To start, ensure that timm is installed in the Python environment:
|
||||
|
||||
```bash
|
||||
python -m pip install -U timm
|
||||
```
|
||||
|
||||
Next we load a timm model for an image classification task:
|
||||
|
||||
```python
|
||||
import timm
|
||||
|
||||
num_classes = ...
|
||||
model_id = "timm/poolformer_m36.sail_in1k"
|
||||
model = timm.create_model(model_id, pretrained=True, num_classes=num_classes)
|
||||
```
|
||||
|
||||
Again, we need to make a decision about what layers to apply LoRA to. Since LoRA supports 2D conv layers, and since
|
||||
those are a major building block of this model, we should apply LoRA to the 2D conv layers. To identify the names of
|
||||
those layers, let's look at all the layer names:
|
||||
|
||||
```python
|
||||
print([(n, type(m)) for n, m in model.named_modules()])
|
||||
```
|
||||
|
||||
This will print a very long list, we'll only show the first few:
|
||||
|
||||
```
|
||||
[('', timm.models.metaformer.MetaFormer),
|
||||
('stem', timm.models.metaformer.Stem),
|
||||
('stem.conv', torch.nn.modules.conv.Conv2d),
|
||||
('stem.norm', torch.nn.modules.linear.Identity),
|
||||
('stages', torch.nn.modules.container.Sequential),
|
||||
('stages.0', timm.models.metaformer.MetaFormerStage),
|
||||
('stages.0.downsample', torch.nn.modules.linear.Identity),
|
||||
('stages.0.blocks', torch.nn.modules.container.Sequential),
|
||||
('stages.0.blocks.0', timm.models.metaformer.MetaFormerBlock),
|
||||
('stages.0.blocks.0.norm1', timm.layers.norm.GroupNorm1),
|
||||
('stages.0.blocks.0.token_mixer', timm.models.metaformer.Pooling),
|
||||
('stages.0.blocks.0.token_mixer.pool', torch.nn.modules.pooling.AvgPool2d),
|
||||
('stages.0.blocks.0.drop_path1', torch.nn.modules.linear.Identity),
|
||||
('stages.0.blocks.0.layer_scale1', timm.models.metaformer.Scale),
|
||||
('stages.0.blocks.0.res_scale1', torch.nn.modules.linear.Identity),
|
||||
('stages.0.blocks.0.norm2', timm.layers.norm.GroupNorm1),
|
||||
('stages.0.blocks.0.mlp', timm.layers.mlp.Mlp),
|
||||
('stages.0.blocks.0.mlp.fc1', torch.nn.modules.conv.Conv2d),
|
||||
('stages.0.blocks.0.mlp.act', torch.nn.modules.activation.GELU),
|
||||
('stages.0.blocks.0.mlp.drop1', torch.nn.modules.dropout.Dropout),
|
||||
('stages.0.blocks.0.mlp.norm', torch.nn.modules.linear.Identity),
|
||||
('stages.0.blocks.0.mlp.fc2', torch.nn.modules.conv.Conv2d),
|
||||
('stages.0.blocks.0.mlp.drop2', torch.nn.modules.dropout.Dropout),
|
||||
('stages.0.blocks.0.drop_path2', torch.nn.modules.linear.Identity),
|
||||
('stages.0.blocks.0.layer_scale2', timm.models.metaformer.Scale),
|
||||
('stages.0.blocks.0.res_scale2', torch.nn.modules.linear.Identity),
|
||||
('stages.0.blocks.1', timm.models.metaformer.MetaFormerBlock),
|
||||
('stages.0.blocks.1.norm1', timm.layers.norm.GroupNorm1),
|
||||
('stages.0.blocks.1.token_mixer', timm.models.metaformer.Pooling),
|
||||
('stages.0.blocks.1.token_mixer.pool', torch.nn.modules.pooling.AvgPool2d),
|
||||
...
|
||||
('head.global_pool.flatten', torch.nn.modules.linear.Identity),
|
||||
('head.norm', timm.layers.norm.LayerNorm2d),
|
||||
('head.flatten', torch.nn.modules.flatten.Flatten),
|
||||
('head.drop', torch.nn.modules.linear.Identity),
|
||||
('head.fc', torch.nn.modules.linear.Linear)]
|
||||
]
|
||||
```
|
||||
|
||||
Upon closer inspection, we see that the 2D conv layers have names such as `"stages.0.blocks.0.mlp.fc1"` and
|
||||
`"stages.0.blocks.0.mlp.fc2"`. How can we match those layer names specifically? You can write a [regular
|
||||
expressions](https://docs.python.org/3/library/re.html) to match the layer names. For our case, the regex
|
||||
`r".*\.mlp\.fc\d"` should do the job.
|
||||
|
||||
Furthermore, as in the first example, we should ensure that the output layer, in this case the classification head, is
|
||||
also updated. Looking at the end of the list printed above, we can see that it's named `'head.fc'`. With that in mind,
|
||||
here is our LoRA config:
|
||||
|
||||
```python
|
||||
config = LoraConfig(target_modules=r".*\.mlp\.fc\d", modules_to_save=["head.fc"])
|
||||
```
|
||||
|
||||
Then we only need to create the PEFT model by passing our base model and the config to `get_peft_model`:
|
||||
|
||||
```python
|
||||
peft_model = get_peft_model(model, config)
|
||||
peft_model.print_trainable_parameters()
|
||||
# prints trainable params: 1,064,454 || all params: 56,467,974 || trainable%: 1.88505789139876
|
||||
```
|
||||
|
||||
This shows us that we only need to train less than 2% of all parameters, which is a huge efficiency gain.
|
||||
|
||||
For a complete example, check out [this notebook](https://github.com/huggingface/peft/blob/main/examples/image_classification/image_classification_timm_peft_lora.ipynb).
|
||||
|
||||
## New transformers architectures
|
||||
|
||||
When new popular transformers architectures are released, we do our best to quickly add them to PEFT. If you come across a transformers model that is not supported out of the box, don't worry, it will most likely still work if the config is set correctly. Specifically, you have to identify the layers that should be adapted and set them correctly when initializing the corresponding config class, e.g. `LoraConfig`. Here are some tips to help with this.
|
||||
|
||||
As a first step, it is a good idea to check the existing models for inspiration. You can find them inside of [constants.py](https://github.com/huggingface/peft/blob/main/src/peft/utils/constants.py) in the PEFT repository. Often, you'll find a similar architecture that uses the same names. For example, if the new model architecture is a variation of the "mistral" model and you want to apply LoRA, you can see that the entry for "mistral" in `TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING` contains `["q_proj", "v_proj"]`. This tells you that for "mistral" models, the `target_modules` for LoRA should be `["q_proj", "v_proj"]`:
|
||||
|
||||
```python
|
||||
from peft import LoraConfig, get_peft_model
|
||||
|
||||
my_mistral_model = ...
|
||||
config = LoraConfig(
|
||||
target_modules=["q_proj", "v_proj"],
|
||||
..., # other LoRA arguments
|
||||
)
|
||||
peft_model = get_peft_model(my_mistral_model, config)
|
||||
```
|
||||
|
||||
If that doesn't help, check the existing modules in your model architecture with the `named_modules` method and try to identify the attention layers, especially the key, query, and value layers. Those will often have names such as `c_attn`, `query`, `q_proj`, etc. The key layer is not always adapted, and ideally, you should check whether including it results in better performance.
|
||||
|
||||
Additionally, linear layers are common targets to be adapted (e.g. in [QLoRA paper](https://huggingface.co/papers/2305.14314), authors suggest to adapt them as well). Their names will often contain the strings `fc` or `dense`.
|
||||
|
||||
If you want to add a new model to PEFT, please create an entry in [constants.py](https://github.com/huggingface/peft/blob/main/src/peft/utils/constants.py) and open a pull request on the [repository](https://github.com/huggingface/peft/pulls). Don't forget to update the [README](https://github.com/huggingface/peft#models-support-matrix) as well.
|
||||
|
||||
## Verify parameters and layers
|
||||
|
||||
You can verify whether you've correctly applied a PEFT method to your model in a few ways.
|
||||
|
||||
* Check the fraction of parameters that are trainable with the [`~PeftModel.print_trainable_parameters`] method. If this number is lower or higher than expected, check the model `repr` by printing the model. This shows the names of all the layer types in the model. Ensure that only the intended target layers are replaced by the adapter layers. For example, if LoRA is applied to `nn.Linear` layers, then you should only see `lora.Linear` layers being used.
|
||||
|
||||
```py
|
||||
peft_model.print_trainable_parameters()
|
||||
```
|
||||
|
||||
* Another way you can view the adapted layers is to use the `targeted_module_names` attribute to list the name of each module that was adapted.
|
||||
|
||||
```python
|
||||
print(peft_model.targeted_module_names)
|
||||
```
|
||||
|
||||
## Unsupported module types
|
||||
|
||||
Methods like LoRA only work if the target modules are supported by PEFT. For example, it's possible to apply LoRA to `nn.Linear` and `nn.Conv2d` layers, but not, for instance, to `nn.LSTM`. If you find a layer class you want to apply PEFT to is not supported, you can:
|
||||
|
||||
- define a custom mapping to dynamically dispatch custom modules in LoRA
|
||||
- open an [issue](https://github.com/huggingface/peft/issues) and request the feature where maintainers will implement it or guide you on how to implement it yourself if demand for this module type is sufficiently high
|
||||
|
||||
### Experimental support for dynamic dispatch of custom modules in LoRA
|
||||
|
||||
> [!WARNING]
|
||||
> This feature is experimental and subject to change, depending on its reception by the community. We will introduce a public and stable API if there is significant demand for it.
|
||||
|
||||
PEFT supports an experimental API for custom module types for LoRA. Let's assume you have a LoRA implementation for LSTMs. Normally, you would not be able to tell PEFT to use it, even if it would theoretically work with PEFT. However, this is possible with dynamic dispatch of custom layers.
|
||||
|
||||
The experimental API currently looks like this:
|
||||
|
||||
```python
|
||||
class MyLoraLSTMLayer:
|
||||
...
|
||||
|
||||
base_model = ... # load the base model that uses LSTMs
|
||||
|
||||
# add the LSTM layer names to target_modules
|
||||
config = LoraConfig(..., target_modules=["lstm"])
|
||||
# define a mapping from base layer type to LoRA layer type
|
||||
custom_module_mapping = {nn.LSTM: MyLoraLSTMLayer}
|
||||
# register the new mapping
|
||||
config._register_custom_module(custom_module_mapping)
|
||||
# after registration, create the PEFT model
|
||||
peft_model = get_peft_model(base_model, config)
|
||||
# do training
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> When you call [`get_peft_model`], you will see a warning because PEFT does not recognize the targeted module type. In this case, you can ignore this warning.
|
||||
|
||||
By supplying a custom mapping, PEFT first checks the base model's layers against the custom mapping and dispatches to the custom LoRA layer type if there is a match. If there is no match, PEFT checks the built-in LoRA layer types for a match.
|
||||
|
||||
Therefore, this feature can also be used to override existing dispatch logic, e.g. if you want to use your own LoRA layer for `nn.Linear` instead of using the one provided by PEFT.
|
||||
|
||||
When creating your custom LoRA module, please follow the same rules as the [existing LoRA modules](https://github.com/huggingface/peft/blob/main/src/peft/tuners/lora/layer.py). Some important constraints to consider:
|
||||
|
||||
- The custom module should inherit from `nn.Module` and `peft.tuners.lora.layer.LoraLayer`.
|
||||
- The `__init__` method of the custom module should have the positional arguments `base_layer` and `adapter_name`. After this, there are additional `**kwargs` that you are free to use or ignore.
|
||||
- The learnable parameters should be stored in an `nn.ModuleDict` or `nn.ParameterDict`, where the key corresponds to the name of the specific adapter (remember that a model can have more than one adapter at a time).
|
||||
- The name of these learnable parameter attributes should start with `"lora_"`, e.g. `self.lora_new_param = ...`.
|
||||
- Some methods are optional, e.g. you only need to implement `merge` and `unmerge` if you want to support weight merging.
|
||||
|
||||
Currently, the information about the custom module does not persist when you save the model. When loading the model, you have to register the custom modules again.
|
||||
|
||||
```python
|
||||
# saving works as always and includes the parameters of the custom modules
|
||||
peft_model.save_pretrained(<model-path>)
|
||||
|
||||
# loading the model later:
|
||||
base_model = ...
|
||||
# load the LoRA config that you saved earlier
|
||||
config = LoraConfig.from_pretrained(<model-path>)
|
||||
# register the custom module again, the same way as the first time
|
||||
custom_module_mapping = {nn.LSTM: MyLoraLSTMLayer}
|
||||
config._register_custom_module(custom_module_mapping)
|
||||
# pass the config instance to from_pretrained:
|
||||
peft_model = PeftModel.from_pretrained(model, tmp_path / "lora-custom-module", config=config)
|
||||
```
|
||||
|
||||
If you use this feature and find it useful, or if you encounter problems, let us know by creating an issue or a discussion on GitHub. This allows us to estimate the demand for this feature and add a public API if it is sufficiently high.
|
||||
@@ -0,0 +1,148 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Adapter injection
|
||||
|
||||
With PEFT, you can inject trainable adapters into any `torch` module which allows you to use adapter methods without relying on the modeling classes in PEFT. This works for all adapters except for those based on prompt learning (e.g. prefix tuning or p-tuning).
|
||||
|
||||
Check the table below to see when you should inject adapters.
|
||||
|
||||
| Pros | Cons |
|
||||
|---|---|
|
||||
| the model is modified inplace, keeping all the original attributes and methods | manually write the `from_pretrained` and `save_pretrained` utility functions from Hugging Face to save and load adapters |
|
||||
| works for any `torch` module and modality | doesn't work with any of the utility methods provided by `PeftModel` such as disabling and merging adapters |
|
||||
|
||||
## Creating a new PEFT model
|
||||
|
||||
To perform the adapter injection, use the [`inject_adapter_in_model`] method. This method takes 3 arguments, the PEFT config, the model, and an optional adapter name. You can also attach multiple adapters to the model if you call [`inject_adapter_in_model`] multiple times with different adapter names.
|
||||
|
||||
For example, to inject LoRA adapters into the `linear` submodule of the `DummyModel` module:
|
||||
|
||||
```python
|
||||
import torch
|
||||
from peft import inject_adapter_in_model, LoraConfig
|
||||
|
||||
class DummyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.embedding = torch.nn.Embedding(10, 10)
|
||||
self.linear = torch.nn.Linear(10, 10)
|
||||
self.lm_head = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, input_ids):
|
||||
x = self.embedding(input_ids)
|
||||
x = self.linear(x)
|
||||
x = self.lm_head(x)
|
||||
return x
|
||||
|
||||
|
||||
lora_config = LoraConfig(
|
||||
lora_alpha=16,
|
||||
lora_dropout=0.1,
|
||||
r=64,
|
||||
bias="none",
|
||||
target_modules=["linear"],
|
||||
)
|
||||
|
||||
model = DummyModel()
|
||||
model = inject_adapter_in_model(lora_config, model)
|
||||
|
||||
dummy_inputs = torch.LongTensor([[0, 1, 2, 3, 4, 5, 6, 7]])
|
||||
dummy_outputs = model(dummy_inputs)
|
||||
```
|
||||
|
||||
Print the model to see that the adapters have been correctly injected.
|
||||
|
||||
```bash
|
||||
DummyModel(
|
||||
(embedding): Embedding(10, 10)
|
||||
(linear): Linear(
|
||||
in_features=10, out_features=10, bias=True
|
||||
(lora_dropout): ModuleDict(
|
||||
(default): Dropout(p=0.1, inplace=False)
|
||||
)
|
||||
(lora_A): ModuleDict(
|
||||
(default): Linear(in_features=10, out_features=64, bias=False)
|
||||
)
|
||||
(lora_B): ModuleDict(
|
||||
(default): Linear(in_features=64, out_features=10, bias=False)
|
||||
)
|
||||
(lora_embedding_A): ParameterDict()
|
||||
(lora_embedding_B): ParameterDict()
|
||||
)
|
||||
(lm_head): Linear(in_features=10, out_features=10, bias=True)
|
||||
)
|
||||
```
|
||||
|
||||
### Injection based on a `state_dict`
|
||||
|
||||
Sometimes, it is possible that there is a PEFT adapter checkpoint but the corresponding PEFT config is not known for whatever reason. To inject the PEFT layers for this checkpoint, you would usually have to reverse-engineer the corresponding PEFT config, most notably the `target_modules` argument, based on the `state_dict` from the checkpoint. This can be cumbersome and error prone. To avoid this, it is also possible to call [`inject_adapter_in_model`] and pass the loaded `state_dict` as an argument:
|
||||
|
||||
```python
|
||||
from safetensors.torch import load_file
|
||||
|
||||
model = ...
|
||||
state_dict = load_file(<path-to-safetensors-file>)
|
||||
lora_config = LoraConfig(...)
|
||||
model = inject_adapter_in_model(lora_config, model, state_dict=state_dict)
|
||||
```
|
||||
|
||||
In this case, PEFT will use the `state_dict` as reference for which layers to target instead of using the PEFT config. As a user, you don't have to set the exact `target_modules` of the PEFT config for this to work. However, you should still pass a PEFT config of the right type, in this example `LoraConfig`, you can leave the `target_modules` as `None`.
|
||||
|
||||
Be aware that this still only creates the uninitialized PEFT layers, the values from the `state_dict` are not used to populate the model weights. To populate the weights, proceed with calling [`set_peft_model_state_dict`] as described below.
|
||||
|
||||
⚠️ Note that if there is a mismatch between what is configured in the PEFT config and what is found in the `state_dict`, PEFT will warn you about this. You can ignore the warning if you know that the PEFT config is not correctly specified.
|
||||
|
||||
> [!WARNING]
|
||||
> If the original PEFT adapters was using `target_parameters` instead of `target_modules`, injecting from a `state_dict` will not work correctly. In this case, it is mandatory to use the correct PEFT config for injection.
|
||||
|
||||
## Saving the model
|
||||
|
||||
To only save the adapter, use the [`get_peft_model_state_dict`] function:
|
||||
|
||||
```python
|
||||
from peft import get_peft_model_state_dict
|
||||
|
||||
peft_state_dict = get_peft_model_state_dict(model)
|
||||
print(peft_state_dict)
|
||||
```
|
||||
|
||||
Otherwise, `model.state_dict()` returns the full state dict of the model.
|
||||
|
||||
## Loading the model
|
||||
|
||||
After loading the saved `state_dict`, it can be applied using the [`set_peft_model_state_dict`] function:
|
||||
|
||||
```python
|
||||
from peft import set_peft_model_state_dict
|
||||
|
||||
model = DummyModel()
|
||||
model = inject_adapter_in_model(lora_config, model)
|
||||
outcome = set_peft_model_state_dict(model, peft_state_dict)
|
||||
# check that there were no wrong keys
|
||||
print(outcome.unexpected_keys)
|
||||
```
|
||||
|
||||
If injecting the adapter is slow or you need to load a large number of adapters, you may use an optimization that allows to create an "empty" adapter on meta device and only fills the weights with real weights when the [`set_peft_model_state_dict`] is called. To do this, pass `low_cpu_mem_usage=True` to both [`inject_adapter_in_model`] and [`set_peft_model_state_dict`].
|
||||
|
||||
```python
|
||||
model = DummyModel()
|
||||
model = inject_adapter_in_model(lora_config, model, low_cpu_mem_usage=True)
|
||||
|
||||
print(model.linear.lora_A["default"].weight.device.type == "meta") # should be True
|
||||
set_peft_model_state_dict(model, peft_state_dict, low_cpu_mem_usage=True)
|
||||
print(model.linear.lora_A["default"].weight.device.type == "cpu") # should be True
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
<!--Copyright 2026-present The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Memory Efficient Training
|
||||
|
||||
🤗 PEFT makes fine-tuning parameter efficient, but not automatically memory efficient. This overview collects tips for cutting training memory and links to the detailed guides.
|
||||
|
||||
> [!TIP]
|
||||
> Always consider the basics of choosing a smaller base model, smaller batch size or shorter sequence length to lower your memory usage.
|
||||
|
||||
## Training memory overview
|
||||
|
||||
Let's dissect the distribution of training memory so we can reason about potential countermeasures. We will use a large language-model trained with the Adam optimizer as an example. When doing full fine-tuning, we will have the following positions taking up memory:
|
||||
|
||||
1. base model parameters: the memory consumption highly depends on the chosen `dtype`. The less bits per parameter (16 for float16), the less memory this will take up. A 1B model in float16 (16 bit/2 byte per parameter) will roughly take `1e9 × 2 byte = 1.863 GiB` of memory.
|
||||
2. all trainable base model parameters ×3 for gradients (1×) and Adam optimizer states (2×), therefore 5.59GB of memory
|
||||
3. memory for the intermediate activations between layers, these are hard to predict but mostly depend on the used compute dtype and sequence length / batch size.
|
||||
|
||||
A smaller base model or a using a smaller compute dtype will reduce all points while using shorter sequences or smaller batches mainly affects gradients and activation memory. Employing PEFT methods will reduce the number of trainable parameters and therefore significantly reduce both gradients and optimizer state, saving a lot of memory.
|
||||
|
||||
## Choosing the right method
|
||||
|
||||
Not every PEFT method is built equally and some formulations are easier to build in a memory efficient manner. If you are on a memory budget it makes sense to check out the [PEFT method comparison suite](https://huggingface.co/spaces/peft-internal-testing/PEFT-method-comparison) and filter for **maximum** accelerator memory usage. Average accelerator memory usage can be fairly equal across methods but not every method scales equally with activations and sequence length; some methods are more prone to memory spikes than others.
|
||||
|
||||
Consider [using trainable tokens](troubleshooting#using-trainable-tokens) when targeting large layers like language modeling heads or embedding layers to fine-tune specific tokens.
|
||||
|
||||
## Quantization
|
||||
|
||||
Quantization is one of the best ways to reduce memory consumption *of the base model* and will, depending on the employed quantization, also reduce activation memory. Since the PEFT methods will only take up a small portion of the total number of parameters, PEFT defaults to use a higher precision than the base model. This can also have the effect that adapters can mitigate some of the quality loss incurred by quantization methods. Read the [PEFT quantization guide](quantization).
|
||||
|
||||
## Compilation
|
||||
|
||||
The models we train are composed of operations like matrix multiplications, sums and assignments where each operation produces a new result and, subsequently, needs to take up memory. If those intermediate results are not needed we can fuse these operations and save up on memory. This is just one of many optimizations that `torch.compile` can do for you, so check out the [PEFT torch.compile guide](torch_compile).
|
||||
|
||||
## Gradient Checkpointing
|
||||
|
||||
You can trade memory with computation by only saving every nth gradient between layers and computing the rest on the fly. Check out the [gradient checkpointing](https://huggingface.co/docs/transformers/grad_checkpointing) documentation of Transformers to learn more.
|
||||
|
||||
> [!NOTE]
|
||||
> When not using Diffusers or Transformers you may need to implement your own gradient checkpointing logic, depending on the training framework that you are using.
|
||||
|
||||
## Chunked NLL loss
|
||||
|
||||
Using [`NLLLoss`](https://docs.pytorch.org/docs/stable/generated/torch.nn.NLLLoss.html) is very common when training language models (or classification tasks). You allocate a matrix of size `batch × sequence × vocabulary`. With particularly long sequences or vocabularies this can get expensive fast.
|
||||
|
||||
When using [TRL](https://huggingface.co/docs/trl) you can either use the [Liger kernel integration](https://huggingface.co/docs/trl/liger_kernel_integration) or use [Chunked NLLLoss](https://huggingface.co/docs/trl/v1.5.1/en/reducing_memory_usage#chunked-cross-entropy-for-reducing-peak-memory-usage). The latter will split the sequence in chunks of size 256 to keep the maximum memory consumption constant.
|
||||
|
||||

|
||||
|
||||
In case the default chunk size is not optimal for your setting, look in the [original TRL PR](https://github.com/huggingface/trl/pull/5575) for more information on how to tune the chunk size.
|
||||
@@ -0,0 +1,37 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
-->
|
||||
|
||||
# Mixed adapter types
|
||||
|
||||
Normally, it isn't possible to mix different adapter types in 🤗 PEFT. You can create a PEFT model with two different LoRA adapters (which can have different config options), but it is not possible to combine a LoRA and LoHa adapter. With [`PeftMixedModel`] however, this works as long as the adapter types are compatible. The main purpose of allowing mixed adapter types is to combine trained adapters for inference. While it is possible to train a mixed adapter model, this has not been tested and is not recommended.
|
||||
|
||||
To load different adapter types into a PEFT model, use [`PeftMixedModel`] instead of [`PeftModel`]:
|
||||
|
||||
```py
|
||||
from peft import PeftMixedModel
|
||||
|
||||
base_model = ... # load the base model, e.g. from transformers
|
||||
# load first adapter, which will be called "default"
|
||||
peft_model = PeftMixedModel.from_pretrained(base_model, <path_to_adapter1>)
|
||||
peft_model.load_adapter(<path_to_adapter2>, adapter_name="other")
|
||||
peft_model.set_adapter(["default", "other"])
|
||||
```
|
||||
|
||||
The [`~PeftMixedModel.set_adapter`] method is necessary to activate both adapters, otherwise only the first adapter would be active. You can keep adding more adapters by calling [`~PeftModel.add_adapter`] repeatedly.
|
||||
|
||||
[`PeftMixedModel`] does not support saving and loading mixed adapters. The adapters should already be trained, and loading the model requires a script to be run each time.
|
||||
|
||||
## Tips
|
||||
|
||||
- Not all adapter types can be combined. See [`peft.tuners.mixed.COMPATIBLE_TUNER_TYPES`](https://github.com/huggingface/peft/blob/1c1c7fdaa6e6abaa53939b865dee1eded82ad032/src/peft/tuners/mixed/model.py#L35) for a list of compatible types. An error will be raised if you try to combine incompatible adapter types.
|
||||
- It is possible to mix multiple adapters of the same type which can be useful for combining adapters with very different configs.
|
||||
- If you want to combine a lot of different adapters, the most performant way to do it is to consecutively add the same adapter types. For example, add LoRA1, LoRA2, LoHa1, LoHa2 in this order, instead of LoRA1, LoHa1, LoRA2, and LoHa2. While the order can affect the output, there is no inherently *best* order, so it is best to choose the fastest one.
|
||||
@@ -0,0 +1,164 @@
|
||||
<!--Copyright 2024 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Model merging
|
||||
|
||||
Training a model for each task can be costly, take up storage space, and the models aren't able to learn new information to improve their performance. Multitask learning can overcome some of these limitations by training a model to learn several tasks, but it is expensive to train and designing a dataset for it is challenging. *Model merging* offers a solution to these challenges by combining multiple pretrained models into one model, giving it the combined abilities of each individual model without any additional training.
|
||||
|
||||
PEFT provides several methods for merging models like a linear or SVD combination. This guide focuses on two methods that are more efficient for merging LoRA adapters by eliminating redundant parameters:
|
||||
|
||||
* [TIES](https://hf.co/papers/2306.01708) - TrIm, Elect, and Merge (TIES) is a three-step method for merging models. First, redundant parameters are trimmed, then conflicting signs are resolved into an aggregated vector, and finally the parameters whose signs are the same as the aggregate sign are averaged. This method takes into account that some values (redundant and sign disagreement) can degrade performance in the merged model.
|
||||
* [DARE](https://hf.co/papers/2311.03099) - Drop And REscale is a method that can be used to prepare for other model merging methods like TIES. It works by randomly dropping parameters according to a drop rate and rescaling the remaining parameters. This helps to reduce the number of redundant and potentially interfering parameters among multiple models.
|
||||
|
||||
Models are merged with the [`~LoraModel.add_weighted_adapter`] method, and the specific model merging method is specified in the `combination_type` parameter.
|
||||
|
||||
## Merge method
|
||||
|
||||
With TIES and DARE, merging is enabled by setting `combination_type` and `density` to a value of the weights to keep from the individual models. For example, let's merge three finetuned [TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T](https://huggingface.co/TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T) models: [tinyllama_lora_nobots](https://huggingface.co/smangrul/tinyllama_lora_norobots), [tinyllama_lora_sql](https://huggingface.co/smangrul/tinyllama_lora_sql), and [tinyllama_lora_adcopy](https://huggingface.co/smangrul/tinyllama_lora_adcopy).
|
||||
|
||||
<Tip warning={true}>
|
||||
|
||||
When you're attempting to merge fully trained models with TIES, you should be aware of any special tokens each model may have added to the embedding layer which are not a part of the original checkpoint's vocabulary. This may cause an issue because each model may have added a special token to the same embedding position. If this is the case, you should use the [`~transformers.PreTrainedModel.resize_token_embeddings`] method to avoid merging the special tokens at the same embedding index.
|
||||
|
||||
<br>
|
||||
|
||||
This shouldn't be an issue if you're only merging LoRA adapters trained from the same base model.
|
||||
|
||||
</Tip>
|
||||
|
||||
Load a base model and can use the [`~PeftModel.load_adapter`] method to load and assign each adapter a name:
|
||||
|
||||
```py
|
||||
from peft import PeftConfig, PeftModel
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
import torch
|
||||
|
||||
config = PeftConfig.from_pretrained("smangrul/tinyllama_lora_norobots")
|
||||
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path, load_in_4bit=True, device_map="auto").eval()
|
||||
tokenizer = AutoTokenizer.from_pretrained("smangrul/tinyllama_lora_norobots")
|
||||
|
||||
model.config.vocab_size = 32005
|
||||
model.resize_token_embeddings(32005)
|
||||
|
||||
model = PeftModel.from_pretrained(model, "smangrul/tinyllama_lora_norobots", adapter_name="norobots")
|
||||
_ = model.load_adapter("smangrul/tinyllama_lora_sql", adapter_name="sql")
|
||||
_ = model.load_adapter("smangrul/tinyllama_lora_adcopy", adapter_name="adcopy")
|
||||
```
|
||||
|
||||
Set the adapters, weights, `adapter_name`, `combination_type`, and `density` with the [`~LoraModel.add_weighted_adapter`] method.
|
||||
|
||||
<hfoptions id="merge-method">
|
||||
<hfoption id="TIES">
|
||||
|
||||
Weight values greater than `1.0` typically produce better results because they preserve the correct scale. A good default starting value for the weights is to set all values to `1.0`.
|
||||
|
||||
```py
|
||||
adapters = ["norobots", "adcopy", "sql"]
|
||||
weights = [2.0, 1.0, 1.0]
|
||||
adapter_name = "merge"
|
||||
density = 0.2
|
||||
model.add_weighted_adapter(adapters, weights, adapter_name, combination_type="ties", density=density)
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="DARE">
|
||||
|
||||
```py
|
||||
adapters = ["norobots", "adcopy", "sql"]
|
||||
weights = [2.0, 0.3, 0.7]
|
||||
adapter_name = "merge"
|
||||
density = 0.2
|
||||
model.add_weighted_adapter(adapters, weights, adapter_name, combination_type="dare_ties", density=density)
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
Set the newly merged model as the active model with the [`~LoraModel.set_adapter`] method.
|
||||
|
||||
```py
|
||||
model.set_adapter("merge")
|
||||
```
|
||||
|
||||
Now you can use the merged model as an instruction-tuned model to write ad copy or SQL queries!
|
||||
|
||||
<hfoptions id="ties">
|
||||
<hfoption id="instruct">
|
||||
|
||||
```py
|
||||
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
|
||||
messages = [
|
||||
{"role": "user", "content": "Write an essay about Generative AI."},
|
||||
]
|
||||
text = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
|
||||
inputs = tokenizer(text, return_tensors="pt")
|
||||
inputs = {k: v.to(device) for k, v in inputs.items()}
|
||||
outputs = model.generate(**inputs, max_new_tokens=256, do_sample=True, top_p=0.95, temperature=0.2, repetition_penalty=1.2, eos_token_id=tokenizer.eos_token_id)
|
||||
print(tokenizer.decode(outputs[0]))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="ad copy">
|
||||
|
||||
```py
|
||||
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
|
||||
messages = [
|
||||
{"role": "system", "content": "Create a text ad given the following product and description."},
|
||||
{"role": "user", "content": "Product: Sony PS5 PlayStation Console\nDescription: The PS5 console unleashes new gaming possibilities that you never anticipated."},
|
||||
]
|
||||
text = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
|
||||
inputs = tokenizer(text, return_tensors="pt")
|
||||
inputs = {k: v.to(device) for k, v in inputs.items()}
|
||||
outputs = model.generate(**inputs, max_new_tokens=128, do_sample=True, top_p=0.95, temperature=0.2, repetition_penalty=1.2, eos_token_id=tokenizer.eos_token_id)
|
||||
print(tokenizer.decode(outputs[0]))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="SQL">
|
||||
|
||||
```py
|
||||
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
|
||||
|
||||
text = """Table: 2-11365528-2
|
||||
Columns: ['Team', 'Head Coach', 'President', 'Home Ground', 'Location']
|
||||
Natural Query: Who is the Head Coach of the team whose President is Mario Volarevic?
|
||||
SQL Query:"""
|
||||
|
||||
inputs = tokenizer(text, return_tensors="pt")
|
||||
inputs = {k: v.to(device) for k, v in inputs.items()}
|
||||
outputs = model.generate(**inputs, max_new_tokens=64, repetition_penalty=1.1, eos_token_id=tokenizer("</s>").input_ids[-1])
|
||||
print(tokenizer.decode(outputs[0]))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
|
||||
## Merging (IA)³ Models
|
||||
The (IA)³ models facilitate linear merging of adapters. To merge adapters in an (IA)³ model, utilize the `add_weighted_adapter` method from the `IA3Model` class. This method is analogous to the `add_weighted_adapter` method used in `LoraModel`, with the key difference being the absence of the `combination_type` parameter. For example, to merge three (IA)³ adapters into a PEFT model, you would proceed as follows:
|
||||
|
||||
```py
|
||||
adapters = ["adapter1", "adapter2", "adapter3"]
|
||||
weights = [0.4, 0.3, 0.3]
|
||||
adapter_name = "merge"
|
||||
model.add_weighted_adapter(adapters, weights, adapter_name)
|
||||
```
|
||||
|
||||
It is recommended that the weights sum to 1.0 to preserve the scale of the model. The merged model can then be set as the active model using the `set_adapter` method:
|
||||
|
||||
```py
|
||||
model.set_adapter("merge")
|
||||
```
|
||||
@@ -0,0 +1,356 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Quantization
|
||||
|
||||
Quantization represents data with fewer bits, making it a useful technique for reducing memory-usage and accelerating inference especially when it comes to large language models (LLMs). There are several ways to quantize a model including:
|
||||
|
||||
* optimizing which model weights are quantized with the [AWQ](https://hf.co/papers/2306.00978) algorithm
|
||||
* independently quantizing each row of a weight matrix with the [GPTQ](https://hf.co/papers/2210.17323) algorithm
|
||||
* quantizing to 8-bit and 4-bit precision with the [bitsandbytes](https://github.com/TimDettmers/bitsandbytes) library
|
||||
* quantizing to as low as 2-bit precision with the [AQLM](https://huggingface.co/papers/2401.06118) algorithm
|
||||
|
||||
However, after a model is quantized it isn't typically further trained for downstream tasks because training can be unstable due to the lower precision of the weights and activations. But since PEFT methods only add *extra* trainable parameters, this allows you to train a quantized model with a PEFT adapter on top! Combining quantization with PEFT can be a good strategy for training even the largest models on a single GPU. For example, [QLoRA](https://hf.co/papers/2305.14314) is a method that quantizes a model to 4-bits and then trains it with LoRA. This method allows you to finetune a 65B parameter model on a single 48GB GPU!
|
||||
|
||||
In this guide, you'll see how to quantize a model to 4-bits and train it with LoRA.
|
||||
|
||||
## Quantize a model
|
||||
|
||||
[bitsandbytes](https://github.com/TimDettmers/bitsandbytes) is a quantization library with a Transformers integration. With this integration, you can quantize a model to 8 or 4-bits and enable many other options by configuring the [`~transformers.BitsAndBytesConfig`] class. For example, you can:
|
||||
|
||||
* set `load_in_4bit=True` to quantize the model to 4-bits when you load it
|
||||
* set `bnb_4bit_quant_type="nf4"` to use a special 4-bit data type for weights initialized from a normal distribution
|
||||
* set `bnb_4bit_use_double_quant=True` to use a nested quantization scheme to quantize the already quantized weights
|
||||
* set `bnb_4bit_compute_dtype=torch.bfloat16` to use bfloat16 for faster computation
|
||||
|
||||
```py
|
||||
import torch
|
||||
from transformers import BitsAndBytesConfig
|
||||
|
||||
config = BitsAndBytesConfig(
|
||||
load_in_4bit=True,
|
||||
bnb_4bit_quant_type="nf4",
|
||||
bnb_4bit_use_double_quant=True,
|
||||
bnb_4bit_compute_dtype=torch.bfloat16,
|
||||
)
|
||||
```
|
||||
|
||||
Pass the `config` to the [`~transformers.AutoModelForCausalLM.from_pretrained`] method.
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)
|
||||
```
|
||||
|
||||
Next, you should call the [`~peft.utils.prepare_model_for_kbit_training`] function to preprocess the quantized model for training.
|
||||
|
||||
```py
|
||||
from peft import prepare_model_for_kbit_training
|
||||
|
||||
model = prepare_model_for_kbit_training(model)
|
||||
```
|
||||
|
||||
Now that the quantized model is ready, let's set up a configuration.
|
||||
|
||||
## LoraConfig
|
||||
|
||||
Create a [`LoraConfig`] with the following parameters (or choose your own):
|
||||
|
||||
```py
|
||||
from peft import LoraConfig
|
||||
|
||||
config = LoraConfig(
|
||||
r=16,
|
||||
lora_alpha=8,
|
||||
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
|
||||
lora_dropout=0.05,
|
||||
bias="none",
|
||||
task_type="CAUSAL_LM"
|
||||
)
|
||||
```
|
||||
|
||||
Then use the [`get_peft_model`] function to create a [`PeftModel`] from the quantized model and configuration.
|
||||
|
||||
```py
|
||||
from peft import get_peft_model
|
||||
|
||||
model = get_peft_model(model, config)
|
||||
```
|
||||
|
||||
You're all set for training with whichever training method you prefer!
|
||||
|
||||
### LoftQ initialization
|
||||
|
||||
[LoftQ](https://hf.co/papers/2310.08659) initializes LoRA weights such that the quantization error is minimized, and it can improve performance when training quantized models. To get started, follow [these instructions](https://github.com/huggingface/peft/tree/main/examples/loftq_finetuning).
|
||||
|
||||
In general, for LoftQ to work best, it is recommended to target as many layers with LoRA as possible, since those not targeted cannot have LoftQ applied. This means that passing `LoraConfig(..., target_modules="all-linear")` will most likely give the best results. Also, you should use `nf4` as quant type in your quantization config when using 4bit quantization, i.e. `BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4")`.
|
||||
|
||||
In general, for LoftQ to work best, it is recommended to target as many layers with LoRA as possible, since those not targeted cannot have LoftQ applied. This means that passing `LoraConfig(..., target_modules="all-linear")` will most likely give the best results. Also, you should use `nf4` as quant type in your quantization config when using 4bit quantization, i.e. `BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4")`.
|
||||
|
||||
There are currently two intended ways of applying LoftQ:
|
||||
|
||||
1. load the non-quantized base model, apply LoftQ with your intended quantization level (e.g., `bits=4` for nf4) and
|
||||
save the resulting adapter. Then quantize the base model using, for example,
|
||||
`BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4")` and load the LoftQ-initialized adapter on top
|
||||
2. load the quantized base model, initialize a PEFT model with LoRA (no loftq) and use `replace_lora_weights_loftq` to
|
||||
apply LoftQ initialization by streaming the reference weights of the non-quantized base model from the weights file
|
||||
(explained further in the next section)
|
||||
|
||||
#### Using `replace_lora_weights_loftq` for on-the-fly LoftQ application
|
||||
|
||||
An easier but more limited way to apply LoftQ initialization is to use the convenience function `replace_lora_weights_loftq`. This takes the quantized PEFT model as input and replaces the LoRA weights in-place with their LoftQ-initialized counterparts.
|
||||
|
||||
```python
|
||||
from peft import replace_lora_weights_loftq
|
||||
from transformers import BitsAndBytesConfig
|
||||
|
||||
bnb_config = BitsAndBytesConfig(load_in_4bit=True, ...)
|
||||
base_model = AutoModelForCausalLM.from_pretrained(..., quantization_config=bnb_config)
|
||||
# note: don't pass init_lora_weights="loftq" or loftq_config!
|
||||
lora_config = LoraConfig(task_type="CAUSAL_LM")
|
||||
peft_model = get_peft_model(base_model, lora_config)
|
||||
replace_lora_weights_loftq(peft_model)
|
||||
```
|
||||
|
||||
`replace_lora_weights_loftq` also allows you to pass a `callback` argument to give you more control over which layers should be modified or not, which empirically can improve the results quite a lot. To see a more elaborate example of this, check out [this notebook](https://github.com/huggingface/peft/blob/main/examples/loftq_finetuning/LoftQ_weight_replacement.ipynb).
|
||||
|
||||
`replace_lora_weights_loftq` implements only one iteration step of LoftQ. This means that only the LoRA weights are updated, instead of iteratively updating LoRA weights and quantized base model weights. This may lead to lower performance but has the advantage that we can use the original quantized weights derived from the base model, instead of having to keep an extra copy of modified quantized weights. Whether this tradeoff is worthwhile depends on the use case.
|
||||
|
||||
At the moment, `replace_lora_weights_loftq` has these additional limitations:
|
||||
|
||||
- Model files must be stored as a `safetensors` file.
|
||||
- Only bitsandbytes 4bit quantization is supported.
|
||||
|
||||
### QLoRA-style training
|
||||
|
||||
QLoRA adds trainable weights to all the linear layers in the transformer architecture. Since the attribute names for these linear layers can vary across architectures, set `target_modules` to `"all-linear"` to add LoRA to all the linear layers:
|
||||
|
||||
```py
|
||||
config = LoraConfig(target_modules="all-linear", ...)
|
||||
```
|
||||
|
||||
## GPTQ quantization
|
||||
|
||||
You can learn more about GPTQ-based `[2, 3, 4, 8]` bit quantization at [GPT-QModel](https://github.com/ModelCloud/GPTQModel) and in the Transformers [GPTQ](https://huggingface.co/docs/transformers/quantization/gptq) documentation. PEFT supports GPTQ post-training through GPT-QModel.
|
||||
|
||||
```bash
|
||||
# GPT-QModel install
|
||||
pip install "gptqmodel>=7.0.0"
|
||||
```
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig
|
||||
|
||||
model_id = "facebook/opt-125m"
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||||
|
||||
gptq_config = GPTQConfig(bits=4, group_size=128, dataset="wikitext2", tokenizer=tokenizer)
|
||||
|
||||
quantized_model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", quantization_config=gptq_config)
|
||||
|
||||
# save quantized model
|
||||
quantized_model.save_pretrained("./opt-125m-gptq")
|
||||
tokenizer.save_pretrained("./opt-125m-gptq")
|
||||
```
|
||||
|
||||
Once quantized, you can post-train GPTQ models with PEFT APIs.
|
||||
|
||||
## AQLM quantization
|
||||
|
||||
Additive Quantization of Language Models ([AQLM](https://huggingface.co/papers/2401.06118)) is a Large Language Models compression method. It quantizes multiple weights together and takes advantage of interdependencies between them. AQLM represents groups of 8-16 weights as a sum of multiple vector codes. This allows it to compress models down to as low as 2-bit with considerably low accuracy losses.
|
||||
|
||||
Since the AQLM quantization process is computationally expensive, the use of prequantized models is recommended. A partial list of available models can be found in the official aqlm [repository](https://github.com/Vahe1994/AQLM).
|
||||
|
||||
The models support LoRA adapter tuning. To tune the quantized model you'll need to install the `aqlm` inference library: `pip install aqlm>=1.0.2`. Finetuned LoRA adapters shall be saved separately, as merging them with AQLM quantized weights is not possible.
|
||||
|
||||
```py
|
||||
quantized_model = AutoModelForCausalLM.from_pretrained(
|
||||
"BlackSamorez/Mixtral-8x7b-AQLM-2Bit-1x16-hf-test-dispatch",
|
||||
dtype="auto", device_map="auto", low_cpu_mem_usage=True,
|
||||
)
|
||||
|
||||
peft_config = LoraConfig(...)
|
||||
|
||||
quantized_model = get_peft_model(quantized_model, peft_config)
|
||||
```
|
||||
|
||||
You can refer to the [Google Colab](https://colab.research.google.com/drive/12GTp1FCj5_0SnnNQH18h_2XFh9vS_guX?usp=sharing) example for an overview of AQLM+LoRA finetuning.
|
||||
|
||||
## EETQ quantization
|
||||
|
||||
You can also perform LoRA fine-tuning on EETQ quantized models. [EETQ](https://github.com/NetEase-FuXi/EETQ) package offers simple and efficient way to perform 8-bit quantization, which is claimed to be faster than the `LLM.int8()` algorithm. First, make sure that you have a transformers version that is compatible with EETQ (e.g. by installing it from latest pypi or from source).
|
||||
|
||||
```py
|
||||
import torch
|
||||
from transformers import EetqConfig
|
||||
|
||||
config = EetqConfig("int8")
|
||||
```
|
||||
|
||||
Pass the `config` to the [`~transformers.AutoModelForCausalLM.from_pretrained`] method.
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)
|
||||
```
|
||||
|
||||
and create a `LoraConfig` and pass it to `get_peft_model`:
|
||||
|
||||
```py
|
||||
from peft import LoraConfig, get_peft_model
|
||||
|
||||
config = LoraConfig(
|
||||
r=16,
|
||||
lora_alpha=8,
|
||||
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
|
||||
lora_dropout=0.05,
|
||||
bias="none",
|
||||
task_type="CAUSAL_LM"
|
||||
)
|
||||
|
||||
model = get_peft_model(model, config)
|
||||
```
|
||||
|
||||
## HQQ quantization
|
||||
|
||||
The models that are quantized using Half-Quadratic Quantization of Large Machine Learning Models ([HQQ](https://mobiusml.github.io/hqq_blog/)) support LoRA adapter tuning. To tune the quantized model, you'll need to install the `hqq` library with: `pip install hqq`.
|
||||
|
||||
```python
|
||||
from hqq.engine.hf import HQQModelForCausalLM
|
||||
|
||||
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
|
||||
|
||||
quantized_model = HQQModelForCausalLM.from_quantized(save_dir_or_hfhub, device=device)
|
||||
peft_config = LoraConfig(...)
|
||||
quantized_model = get_peft_model(quantized_model, peft_config)
|
||||
```
|
||||
|
||||
Or using transformers version that is compatible with HQQ (e.g. by installing it from latest pypi or from source).
|
||||
|
||||
```python
|
||||
from transformers import HqqConfig, AutoModelForCausalLM
|
||||
|
||||
quant_config = HqqConfig(nbits=4, group_size=64)
|
||||
quantized_model = AutoModelForCausalLM.from_pretrained(save_dir_or_hfhub, device_map=device_map, quantization_config=quant_config)
|
||||
peft_config = LoraConfig(...)
|
||||
quantized_model = get_peft_model(quantized_model, peft_config)
|
||||
```
|
||||
|
||||
## torchao (PyTorch Architecture Optimization)
|
||||
|
||||
PEFT supports models quantized with [torchao](https://github.com/pytorch/ao) ("ao") for int8 quantization.
|
||||
|
||||
```python
|
||||
from peft import LoraConfig, get_peft_model
|
||||
from transformers import AutoModelForCausalLM, TorchAoConfig
|
||||
from torchao.quantization import Int8WeightOnlyConfig
|
||||
|
||||
model_id = ...
|
||||
quantization_config = TorchAoConfig(quant_type=Int8WeightOnlyConfig())
|
||||
base_model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=quantization_config)
|
||||
peft_config = LoraConfig(...)
|
||||
model = get_peft_model(base_model, peft_config)
|
||||
```
|
||||
|
||||
### Caveats:
|
||||
|
||||
- Use the most recent versions of torchao (>= v0.4.0) and transformers (> 4.42).
|
||||
- Only linear layers are currently supported.
|
||||
- `quant_type = "int4_weight_only"` is currently not supported.
|
||||
- `NF4` is not implemented in transformers as of yet and is thus also not supported.
|
||||
- DoRA only works with `quant_type = "int8_weight_only"` at the moment.
|
||||
- There is explicit support for torchao when used with LoRA. However, when torchao quantizes a layer, its class does not change, only the type of the underlying tensor. For this reason, PEFT methods other than LoRA will generally also work with torchao, even if not explicitly supported. Be aware, however, that **merging only works correctly with LoRA and with `quant_type = "int8_weight_only"`**. If you use a different PEFT method or dtype, merging will likely result in an error, and even it doesn't, the results will still be incorrect.
|
||||
|
||||
## INC quantization
|
||||
|
||||
Intel Neural Compressor ([INC](https://github.com/intel/neural-compressor)) enables model quantization for various devices,
|
||||
including Intel Gaudi accelerators (also known as HPU devices). You can perform LoRA fine-tuning on models that have been
|
||||
quantized using INC. To use INC with PyTorch models, install the library with: `pip install neural-compressor[pt]`.
|
||||
Quantizing a model to FP8 precision for HPU devices can be done with the following single-step quantization workflow:
|
||||
|
||||
```python
|
||||
import torch
|
||||
from neural_compressor.torch.quantization import FP8Config, convert, finalize_calibration, prepare
|
||||
quant_configs = {
|
||||
...
|
||||
}
|
||||
config = FP8Config(**quant_configs)
|
||||
```
|
||||
|
||||
Pass the config to the `prepare` method, run inference to gather calibration stats, and call `finalize_calibration`
|
||||
and `convert` methods to quantize model to FP8 precision:
|
||||
|
||||
```python
|
||||
model = prepare(model, config)
|
||||
# Run inference to collect calibration statistics
|
||||
...
|
||||
# Finalize calibration and convert the model to FP8 precision
|
||||
finalize_calibration(model)
|
||||
model = convert(model)
|
||||
# Load PEFT LoRA adapter as usual
|
||||
...
|
||||
```
|
||||
|
||||
An example demonstrating how to load a PEFT LoRA adapter into an INC-quantized FLUX text-to-image model for HPU
|
||||
devices is provided [here](https://github.com/huggingface/peft/blob/main/examples/stable_diffusion/inc_flux_lora_hpu.py).
|
||||
|
||||
|
||||
### Caveats:
|
||||
|
||||
- `merge()` and `unmerge()` methods are currently not supported for INC-quantized models.
|
||||
- Currently, only **Linear** INC-quantized layers are supported when loading PEFT adapters.
|
||||
|
||||
## Other Supported PEFT Methods
|
||||
|
||||
Besides LoRA, the following PEFT methods also support quantization:
|
||||
|
||||
- **VeRA** (supports bitsandbytes quantization)
|
||||
- **AdaLoRA** (supports both bitsandbytes and GPTQ quantization)
|
||||
- **(IA)³** (supports bitsandbytes quantization)
|
||||
|
||||
## Transformer Engine (TE) LoRA
|
||||
|
||||
PEFT supports LoRA adapters on top of [NVIDIA Transformer Engine](https://docs.nvidia.com/deeplearning/transformer-engine/) layers (`te.pytorch.Linear`, `te.pytorch.LayerNormLinear`, and `te.pytorch.LayerNormMLP`). TE layers use FP8 and fused kernels to accelerate transformer training, and attaching LoRA adapters lets you parameter-efficiently fine-tune TE-accelerated models.
|
||||
|
||||
```python
|
||||
from peft import LoraConfig, get_peft_model
|
||||
|
||||
config = LoraConfig(
|
||||
r=8,
|
||||
lora_alpha=16,
|
||||
target_modules=["q_proj", "v_proj"],
|
||||
task_type="TOKEN_CLS",
|
||||
)
|
||||
model = get_peft_model(te_model, config)
|
||||
```
|
||||
|
||||
When Transformer Engine is installed, PEFT automatically dispatches matching layers to the `TeLinear` adapter — no extra configuration is needed.
|
||||
|
||||
### Caveats
|
||||
|
||||
- `merge()` and `unmerge()` are not yet supported for TE layers.
|
||||
- DoRA is not supported with TE layers.
|
||||
|
||||
A complete ESM2 token-classification example is available under `examples/lora_finetuning_transformer_engine/`.
|
||||
|
||||
## Next steps
|
||||
|
||||
If you're interested in learning more about quantization, the following may be helpful:
|
||||
|
||||
* Learn more details about QLoRA and check out some benchmarks on its impact in the [Making LLMs even more accessible with bitsandbytes, 4-bit quantization and QLoRA](https://huggingface.co/blog/4bit-transformers-bitsandbytes) blog post.
|
||||
* Read more about different quantization schemes in the Transformers [Quantization](https://hf.co/docs/transformers/main/quantization) guide.
|
||||
@@ -0,0 +1,70 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# torch.compile
|
||||
|
||||
In PEFT, [torch.compile](https://pytorch.org/tutorials/intermediate/torch_compile_tutorial.html) works for some but not all features. The reason why it won't always work is because PEFT is highly dynamic in certain places (loading and switching between multiple adapters, for instance), which can cause trouble for `torch.compile`. In other places, `torch.compile` may work, but won't be as fast as expected because of graph breaks.
|
||||
|
||||
If you don't see an error, it doesn't necessarily mean that `torch.compile` worked correctly. It might give you an output, but the output is incorrect. This guide describes what works with `torch.compile` and what doesn't. For your own testing, we recommend using the latest PyTorch version, as `torch.compile` is constantly being improved.
|
||||
|
||||
> [!TIP]
|
||||
> Unless indicated otherwise, the default `torch.compile` settings were used.
|
||||
|
||||
## Training and inference with `torch.compile`
|
||||
|
||||
These features **work** with `torch.compile`. Everything listed below was tested with a causal LM:
|
||||
|
||||
- Training with `Trainer` from 🤗 transformers
|
||||
- Training with a custom PyTorch loop
|
||||
- Inference
|
||||
- Generation
|
||||
|
||||
The following adapters were tested successfully:
|
||||
|
||||
- AdaLoRA
|
||||
- BOFT
|
||||
- IA³
|
||||
- Layer Norm Tuning
|
||||
- LoHa
|
||||
- LoKr
|
||||
- LoRA
|
||||
- LoRA + DoRA
|
||||
- LoRA applied to embedding layers
|
||||
- OFT
|
||||
- VeRA
|
||||
- HRA
|
||||
|
||||
## Advanced PEFT features with `torch.compile`
|
||||
|
||||
Below are some of the more advanced PEFT features that **work**. They were all tested with LoRA.
|
||||
|
||||
- `modules_to_save` (i.e. `config = LoraConfig(..., modules_to_save=...)`)
|
||||
- Merging adapters (one or multiple)
|
||||
- Merging multiple adapters into one adapter (i.e. calling `model.add_weighted_adapter(...)`)
|
||||
- Using PEFT adapters with quantization (bitsandbytes)
|
||||
- Disabling adapters (i.e. using `with model.disable_adapter()`)
|
||||
- Unloading (i.e. calling `model.merge_and_unload()`)
|
||||
- Mixed adapter batches (i.e. calling `model(batch, adapter_names=["__base__", "default", "other", ...])`)
|
||||
- Inference with multiple adapters (i.e. using `model.add_adapter` or `model.load_adapter` to load more than 1 adapter); for this, only call `torch.compile` _after_ loading all adapters
|
||||
|
||||
Generally, we can expect that if a feature works correctly with LoRA and is also supported by other adapter types, it should also work for that adapter type.
|
||||
|
||||
## Test cases
|
||||
|
||||
All the use cases listed above are tested inside of [`peft/tests/test_torch_compile.py`](https://github.com/huggingface/peft/blob/main/tests/test_torch_compile.py). If you want to check in more detail how we tested a certain feature, please go to that file and check the test that corresponds to your use case.
|
||||
|
||||
> [!TIP]
|
||||
> If you have another use case where you know that `torch.compile` does or does not work with PEFT, please contribute by letting us know or by opening a PR to add this use case to the covered test cases.
|
||||
@@ -0,0 +1,458 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
If you encounter any issue when using PEFT, please check the following list of common issues and their solutions.
|
||||
|
||||
## Examples don't work
|
||||
|
||||
Examples often rely on the most recent package versions, so please ensure they're up-to-date. In particular, check the following package versions:
|
||||
|
||||
- `peft`
|
||||
- `transformers`
|
||||
- `accelerate`
|
||||
- `torch`
|
||||
|
||||
In general, you can update the package version by running this command inside your Python environment:
|
||||
|
||||
```bash
|
||||
python -m pip install -U <package_name>
|
||||
```
|
||||
|
||||
Installing PEFT from source is useful for keeping up with the latest developments:
|
||||
|
||||
```bash
|
||||
python -m pip install git+https://github.com/huggingface/peft
|
||||
```
|
||||
|
||||
## Dtype-related issues
|
||||
|
||||
### ValueError: Attempting to unscale FP16 gradients
|
||||
|
||||
This error probably occurred because the model was loaded with `dtype=torch.float16` and then used in an automatic mixed precision (AMP) context, e.g. by setting `fp16=True` in the [`~transformers.Trainer`] class from 🤗 Transformers. The reason is that when using AMP, trainable weights should never use fp16. To make this work without loading the whole model in fp32, add the following to your code:
|
||||
|
||||
```python
|
||||
peft_model = get_peft_model(...)
|
||||
|
||||
# add this:
|
||||
for param in model.parameters():
|
||||
if param.requires_grad:
|
||||
param.data = param.data.float()
|
||||
|
||||
# proceed as usual
|
||||
trainer = Trainer(model=peft_model, fp16=True, ...)
|
||||
trainer.train()
|
||||
```
|
||||
|
||||
Alternatively, you can use the [`~utils.cast_mixed_precision_params`] function to correctly cast the weights:
|
||||
|
||||
```python
|
||||
from peft import cast_mixed_precision_params
|
||||
|
||||
peft_model = get_peft_model(...)
|
||||
cast_mixed_precision_params(peft_model, dtype=torch.float16)
|
||||
|
||||
# proceed as usual
|
||||
trainer = Trainer(model=peft_model, fp16=True, ...)
|
||||
trainer.train()
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Starting from PEFT version v0.12.0, PEFT automatically promotes the dtype of adapter weights from `torch.float16` and `torch.bfloat16` to `torch.float32` where appropriate. To _prevent_ this behavior, you can pass `autocast_adapter_dtype=False` to [`~get_peft_model`], to [`~PeftModel.from_pretrained`], and to [`~PeftModel.load_adapter`].
|
||||
|
||||
### Selecting the dtype of the adapter
|
||||
|
||||
Most PEFT methods, like LoRA, work by adding trainable adapter weights. By default, those weights are stored in float32 dtype (fp32), i.e. at a relatively high precision. Therefore, even if the base model is loaded in float16 (fp16) or bfloat16 (bf16), the adapter weights are float32. When the adapter results are calculated during the forward pass, the input will typically be in the dtype of the base model, thus it will be upcast to float32 if necessary, then cast back to the original dtype.
|
||||
|
||||
If you prefer to have the adapter weights in the lower precision of the base model, i.e. in float16 or bfloat16, you can pass `autocast_adapter_dtype=False` when creating the model ([`~get_peft_model`]) or loading the model ([`~PeftModel.from_pretrained`]). There are some advantages and disadvantages to this:
|
||||
|
||||
Advantages of half precision adapter:
|
||||
- computation slightly faster
|
||||
- slightly less memory
|
||||
- smaller file size of checkpoint (half the size)
|
||||
|
||||
Disadvantages of half precision adapter:
|
||||
- slightly worse loss
|
||||
- higher risk of overflow or underflow
|
||||
|
||||
Note that for most use cases, overall runtime and memory cost will be determined by the size of the base model and by the dataset, while the dtype of the PEFT adapter will only have a small impact.
|
||||
|
||||
## Bad results from a loaded PEFT model
|
||||
|
||||
There can be several reasons for getting a poor result from a loaded PEFT model which are listed below. If you're still unable to troubleshoot the problem, see if anyone else had a similar [issue](https://github.com/huggingface/peft/issues) on GitHub, and if you can't find any, open a new issue.
|
||||
|
||||
When opening an issue, it helps a lot if you provide a minimal code example that reproduces the issue. Also, please report if the loaded model performs at the same level as the model did before fine-tuning, if it performs at a random level, or if it is only slightly worse than expected. This information helps us identify the problem more quickly.
|
||||
|
||||
### Random deviations
|
||||
|
||||
If your model outputs are not exactly the same as previous runs, there could be an issue with random elements. For example:
|
||||
|
||||
1. please ensure it is in `.eval()` mode, which is important, for instance, if the model uses dropout
|
||||
2. if you use [`~transformers.GenerationMixin.generate`] on a language model, there could be random sampling, so obtaining the same result requires setting a random seed
|
||||
3. if you used quantization and merged the weights, small deviations are expected due to rounding errors
|
||||
|
||||
### Incorrectly loaded model
|
||||
|
||||
Please ensure that you load the model correctly. A common error is trying to load a _trained_ model with [`get_peft_model`] which is incorrect. Instead, the loading code should look like this:
|
||||
|
||||
```python
|
||||
from peft import PeftModel, PeftConfig
|
||||
|
||||
base_model = ... # to load the base model, use the same code as when you trained it
|
||||
config = PeftConfig.from_pretrained(peft_model_id)
|
||||
peft_model = PeftModel.from_pretrained(base_model, peft_model_id)
|
||||
```
|
||||
|
||||
### Randomly initialized layers
|
||||
|
||||
For some tasks, it is important to correctly configure `modules_to_save` in the config to account for randomly initialized layers.
|
||||
|
||||
As an example, this is necessary if you use LoRA to fine-tune a language model for sequence classification because 🤗 Transformers adds a randomly initialized classification head on top of the model. If you do not add this layer to `modules_to_save`, the classification head won't be saved. The next time you load the model, you'll get a _different_ randomly initialized classification head, resulting in completely different results.
|
||||
|
||||
PEFT tries to correctly guess the `modules_to_save` if you provide the `task_type` argument in the config. This should work for transformers models that follow the standard naming scheme. It is always a good idea to double check though because we can't guarantee all models follow the naming scheme.
|
||||
|
||||
When you load a transformers model that has randomly initialized layers, you should see a warning along the lines of:
|
||||
|
||||
```
|
||||
Some weights of <MODEL> were not initialized from the model checkpoint at <ID> and are newly initialized: [<LAYER_NAMES>].
|
||||
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
|
||||
```
|
||||
|
||||
The mentioned layers should be added to `modules_to_save` in the config to avoid the described problem.
|
||||
|
||||
> [!TIP]
|
||||
> As an example, when loading a model that is using the DeBERTa architecture for sequence classification, you'll see a warning that the following weights are newly initialized: `['classifier.bias', 'classifier.weight', 'pooler.dense.bias', 'pooler.dense.weight']`. From this, it follows that the `classifier` and `pooler` layers should be added to: `modules_to_save=["classifier", "pooler"]`.
|
||||
|
||||
### Extending the vocabulary
|
||||
|
||||
For many language fine-tuning tasks, extending the model's vocabulary is necessary since new tokens are being introduced. This requires extending the embedding layer to account for the new tokens and, depending on the fine-tuning method, also storing the embedding layer in addition to the adapter weights when saving the adapter. There are a few ways of achieving this ordered by parameter effectiveness:
|
||||
|
||||
- [trainable tokens](../package_reference/trainable_tokens), train only the specified tokens, optionally store only the updated values
|
||||
- training an adapter on the embedding matrix, optionally store only the updated values
|
||||
- full-finetuning of the embedding layer
|
||||
|
||||
#### Using trainable tokens
|
||||
|
||||
Let's start with trainable tokens, in this case its [LoRA integration](../package_reference/lora#efficiently-train-tokens-alongside-lora). If you're interested in only training the new embeddings and nothing else, refer to the [standalone documentation](../package_reference/trainable_tokens).
|
||||
|
||||
To enable selective token training of the embedding layer, you'll need to supply the token ids of your newly added tokens via the `trainable_token_indices` parameter. Optionally you can specify which layer to target if there is more than one embedding layer. For a Mistral model this could look like this:
|
||||
|
||||
```python
|
||||
new_tokens = ['<think>', '</think>']
|
||||
tokenizer.add_tokens(new_tokens)
|
||||
base_model.resize_token_embeddings(len(tokenizer))
|
||||
|
||||
lora_config = LoraConfig(
|
||||
...,
|
||||
trainable_token_indices={'embed_tokens': tokenizer.convert_tokens_to_ids(new_tokens)},
|
||||
)
|
||||
```
|
||||
|
||||
If your model uses tied weights (such as the `lm_head`), trainable tokens will try to resolve those and keep them updated as well, so in that case there should be no need for adding `modules_to_save=["lm_head"]`. This only works if the model uses the Transformers convention for tying weights.
|
||||
|
||||
Saving the model with `model.save_pretrained` may save the full embedding matrix instead of
|
||||
only the difference as a precaution because the embedding matrix was resized. To save space you can disable this behavior by setting `save_embedding_layers=False` when calling `save_pretrained`. This is safe to do as long as you don't modify the embedding matrix through other means as well, as such changes will be not tracked by trainable tokens.
|
||||
|
||||
#### Using an adapter, e.g. LoRA
|
||||
|
||||
Prepare the embedding layer by adding it to the `target_modules` of your adapter config. For example, the Mistral config could look like this:
|
||||
|
||||
```python
|
||||
config = LoraConfig(..., target_modules=["embed_tokens", "lm_head", "q_proj", "v_proj"])
|
||||
```
|
||||
|
||||
Once added to `target_modules`, PEFT automatically stores the embedding layer when saving the adapter if the model has the [`~transformers.PreTrainedModel.get_input_embeddings`] and [`~transformers.PreTrainedModel.get_output_embeddings`]. This is generally the case for Transformers models.
|
||||
|
||||
If the model's embedding layer doesn't follow the Transformer's naming scheme but nevertheless implements `get_input_embeddings`, you can still save it by manually passing `save_embedding_layers=True` when saving the adapter:
|
||||
|
||||
```python
|
||||
model = get_peft_model(...)
|
||||
# train the model
|
||||
model.save_pretrained("my_adapter", save_embedding_layers=True)
|
||||
```
|
||||
|
||||
For inference, load the base model first and resize it the same way you did before you trained the model. After you've resized the base model, you can load the PEFT checkpoint.
|
||||
|
||||
For a complete example, please check out [this notebook](https://github.com/huggingface/peft/blob/main/examples/causal_language_modeling/peft_lora_clm_with_additional_tokens.ipynb).
|
||||
|
||||
#### Full fine-tuning
|
||||
|
||||
Full fine-tuning is more costly in terms of VRAM or storage space but if all else fails, you can fall back to this and see if it works for you. Achieve it by adding the name of the embedding layer to `modules_to_save`. Note that you need to add tied layers as well, e.g. `lm_head`. Example for a Mistral model with LoRA:
|
||||
|
||||
```python
|
||||
config = LoraConfig(..., modules_to_save=["embed_tokens", "lm_head"], target_modules=["q_proj", "v_proj"])
|
||||
```
|
||||
|
||||
### Getting a warning about "weights not being initialized from the model checkpoint"
|
||||
|
||||
When you load your PEFT model which has been trained on a task (for example, classification), you may get a warning like:
|
||||
|
||||
> Some weights of LlamaForSequenceClassification were not initialized from the model checkpoint at meta-llama/Llama-3.2-1B and are newly initialized: ['score.weight']. You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
|
||||
|
||||
Although this looks scary, it is most likely nothing to worry about. This warning comes from Transformers, and it isn't a PEFT specific warning. It lets you know that a randomly initialized classification head (`score`) is attached to the base model, and the head must be trained to produce sensible predictions.
|
||||
|
||||
When you get this warning _before_ training the model, PEFT automatically takes care of making the classification head trainable if you correctly passed the `task_type` argument to the PEFT config.
|
||||
|
||||
```python
|
||||
from peft import LoraConfig, TaskType
|
||||
|
||||
lora_config = LoraConfig(..., task_type=TaskType.SEQ_CLS)
|
||||
```
|
||||
|
||||
If your classification head does not follow the usual naming conventions from Transformers (which is rare), you have to explicitly tell PEFT the name of the head in `modules_to_save`.
|
||||
|
||||
```python
|
||||
lora_config = LoraConfig(..., modules_to_save=["name-of-classification-head"])
|
||||
```
|
||||
|
||||
To check the name of the classification head, print the model and it should be the last module.
|
||||
|
||||
If you get this warning from your inference code, i.e. _after_ training the model, when you load the PEFT model, you always have to load the Transformers model first. Since Transformers does not know that you will load PEFT weights afterwards, it still gives the warning.
|
||||
|
||||
As always, it is best practice to ensure the model works correctly for inference by running some validation on it.
|
||||
|
||||
### Check layer and model status
|
||||
|
||||
Sometimes a PEFT model can end up in a bad state, especially when handling multiple adapters. There can be some confusion around what adapters exist, which one is active, which one is merged, etc. To help investigate this issue, call the [`~peft.PeftModel.get_layer_status`] and the [`~peft.PeftModel.get_model_status`] methods.
|
||||
|
||||
The [`~peft.PeftModel.get_layer_status`] method gives you a detailed overview of each targeted layer's active, merged, and available adapters.
|
||||
|
||||
```python
|
||||
>>> from transformers import AutoModel
|
||||
>>> from peft import get_peft_model, LoraConfig
|
||||
|
||||
>>> model_id = "google/flan-t5-small"
|
||||
>>> model = AutoModel.from_pretrained(model_id)
|
||||
>>> model = get_peft_model(model, LoraConfig())
|
||||
|
||||
>>> model.get_layer_status()
|
||||
[TunerLayerStatus(name='model.encoder.block.0.layer.0.SelfAttention.q',
|
||||
module_type='lora.Linear',
|
||||
enabled=True,
|
||||
active_adapters=['default'],
|
||||
merged_adapters=[],
|
||||
requires_grad={'default': True},
|
||||
available_adapters=['default']),
|
||||
TunerLayerStatus(name='model.encoder.block.0.layer.0.SelfAttention.v',
|
||||
module_type='lora.Linear',
|
||||
enabled=True,
|
||||
active_adapters=['default'],
|
||||
merged_adapters=[],
|
||||
requires_grad={'default': True},
|
||||
available_adapters=['default']),
|
||||
...]
|
||||
|
||||
>>> model.get_model_status()
|
||||
TunerModelStatus(
|
||||
base_model_type='T5Model',
|
||||
adapter_model_type='LoraModel',
|
||||
peft_types={'default': 'LORA'},
|
||||
trainable_params=344064,
|
||||
total_params=60855680,
|
||||
num_adapter_layers=48,
|
||||
enabled=True,
|
||||
active_adapters=['default'],
|
||||
merged_adapters=[],
|
||||
requires_grad={'default': True},
|
||||
available_adapters=['default'],
|
||||
)
|
||||
```
|
||||
|
||||
In the model state output, you should look out for entries that say `"irregular"`. This means PEFT detected an inconsistent state in the model. For instance, if `merged_adapters="irregular"`, it means that for at least one adapter, it was merged on some target modules but not on others. The inference results will most likely be incorrect as a result.
|
||||
|
||||
The best way to resolve this issue is to reload the whole model and adapter checkpoint(s). Ensure that you don't perform any incorrect operations on the model, e.g. manually merging adapters on some modules but not others.
|
||||
|
||||
Convert the layer status into a pandas `DataFrame` for an easier visual inspection.
|
||||
|
||||
```python
|
||||
from dataclasses import asdict
|
||||
import pandas as pd
|
||||
|
||||
df = pd.DataFrame(asdict(layer) for layer in model.get_layer_status())
|
||||
```
|
||||
|
||||
It is possible to get this information for non-PEFT models if they are using PEFT layers under the hood, but some information like the `base_model_type` or the `peft_types` cannot be determined in that case. As an example, you can call this on a [diffusers](https://huggingface.co/docs/diffusers/index) model like so:
|
||||
|
||||
```python
|
||||
>>> import torch
|
||||
>>> from diffusers import StableDiffusionPipeline
|
||||
>>> from peft import get_model_status, get_layer_status
|
||||
|
||||
>>> path = "runwayml/stable-diffusion-v1-5"
|
||||
>>> lora_id = "takuma104/lora-test-text-encoder-lora-target"
|
||||
>>> pipe = StableDiffusionPipeline.from_pretrained(path, dtype=torch.float16)
|
||||
>>> pipe.load_lora_weights(lora_id, adapter_name="adapter-1")
|
||||
>>> pipe.load_lora_weights(lora_id, adapter_name="adapter-2")
|
||||
>>> pipe.set_lora_device(["adapter-2"], "cuda")
|
||||
>>> get_layer_status(pipe.text_encoder)
|
||||
[TunerLayerStatus(name='text_model.encoder.layers.0.self_attn.k_proj',
|
||||
module_type='lora.Linear',
|
||||
enabled=True,
|
||||
active_adapters=['adapter-2'],
|
||||
merged_adapters=[],
|
||||
requires_grad={'adapter-1': False, 'adapter-2': True},
|
||||
available_adapters=['adapter-1', 'adapter-2'],
|
||||
devices={'adapter-1': ['cpu'], 'adapter-2': ['cuda']}),
|
||||
TunerLayerStatus(name='text_model.encoder.layers.0.self_attn.v_proj',
|
||||
module_type='lora.Linear',
|
||||
enabled=True,
|
||||
active_adapters=['adapter-2'],
|
||||
merged_adapters=[],
|
||||
requires_grad={'adapter-1': False, 'adapter-2': True},
|
||||
devices={'adapter-1': ['cpu'], 'adapter-2': ['cuda']}),
|
||||
...]
|
||||
|
||||
>>> get_model_status(pipe.unet)
|
||||
TunerModelStatus(
|
||||
base_model_type='other',
|
||||
adapter_model_type='None',
|
||||
peft_types={},
|
||||
trainable_params=797184,
|
||||
total_params=861115332,
|
||||
num_adapter_layers=128,
|
||||
enabled=True,
|
||||
active_adapters=['adapter-2'],
|
||||
merged_adapters=[],
|
||||
requires_grad={'adapter-1': False, 'adapter-2': True},
|
||||
available_adapters=['adapter-1', 'adapter-2'],
|
||||
devices={'adapter-1': ['cpu'], 'adapter-2': ['cuda']},
|
||||
)
|
||||
```
|
||||
|
||||
## Speed
|
||||
|
||||
### Loading adapter weights is slow
|
||||
|
||||
Loading adapters like LoRA weights should generally be fast compared to loading the base model. However, there can be use cases where the adapter weights are quite large or where users need to load a large number of adapters -- the loading time can add up in this case. The reason for this is that the adapter weights are first initialized and then overridden by the loaded weights, which is wasteful. To speed up the loading time, you can pass the `low_cpu_mem_usage=True` argument to [`~PeftModel.from_pretrained`] and [`~PeftModel.load_adapter`].
|
||||
|
||||
> [!TIP]
|
||||
> If this option works well across different use cases, it may become the default for adapter loading in the future.
|
||||
|
||||
|
||||
## Reproducibility
|
||||
|
||||
### Models using batch norm
|
||||
|
||||
When loading a trained PEFT model where the base model uses batch norm (e.g. `torch.nn.BatchNorm1d` or `torch.nn.BatchNorm2d`), you may find that you cannot reproduce the exact same outputs. This is because the batch norm layers keep track of running stats during training, but these stats are not part of the PEFT checkpoint. Therefore, when you load the PEFT model, the running stats of the base model will be used (i.e. from before training with PEFT).
|
||||
|
||||
Depending on your use case, this may not be a big deal. If, however, you need your outputs to be 100% reproducible, you can achieve this by adding the batch norm layers to `modules_to_save`. Below is an example of this using resnet and LoRA. Notice that we set `modules_to_save=["classifier", "normalization"]`. We need the `"classifier"` argument because our task is image classification, and we add the `"normalization"` argument to ensure that the batch norm layers are saved in the PEFT checkpoint.
|
||||
|
||||
```python
|
||||
from transformers import AutoModelForImageClassification
|
||||
from peft import LoraConfig, get_peft_model
|
||||
|
||||
model_id = "microsoft/resnet-18"
|
||||
base_model = AutoModelForImageClassification.from_pretrained(self.model_id)
|
||||
config = LoraConfig(
|
||||
target_modules=["convolution"],
|
||||
modules_to_save=["classifier", "normalization"],
|
||||
),
|
||||
```
|
||||
|
||||
Depending on the type of model you use, the batch norm layers could have different names than `"normalization"`, so please ensure that the name matches your model architecture.
|
||||
|
||||
## Version mismatch
|
||||
|
||||
### Error while loading the config because of an unexpected keyword argument
|
||||
|
||||
When you encounter an error like the one shown below, it means the adapter you're trying to load was trained with a more recent version of PEFT than the version you have installed on your system.
|
||||
|
||||
```
|
||||
TypeError: LoraConfig.__init__() got an unexpected keyword argument <argument-name>
|
||||
```
|
||||
|
||||
The best way to resolve this issue is to install the latest PEFT version:
|
||||
|
||||
```sh
|
||||
python -m pip install -U PEFT
|
||||
```
|
||||
|
||||
If the adapter was trained from a source install of PEFT (an unreleased version of PEFT), then you also need to install PEFT from source.
|
||||
|
||||
```sh
|
||||
python -m pip install -U git+https://github.com/huggingface/peft.git
|
||||
```
|
||||
|
||||
If it is not possible for you to upgrade PEFT, there is a workaround you can try.
|
||||
|
||||
Assume the error message says that the unknown keyword argument is named `foobar`. Search inside the `adapter_config.json` of this PEFT adapter for the `foobar` entry and delete it from the file. Then save the file and try loading the model again.
|
||||
|
||||
This solution works most of the time. As long as it is the default value for `foobar`, it can be ignored. However, when it is set to some other value, you will get incorrect results. Upgrading PEFT is the recommended solution.
|
||||
|
||||
## Adapter handling
|
||||
|
||||
### Using multiple adapters at the same time
|
||||
|
||||
PEFT allows you to create more than one adapter on the same model. This can be useful in many situations. For example, for inference, you may want to serve two fine-tuned models from the same base model instead of loading the base model once for each fine-tuned model, which would cost more memory. However, multiple adapters can be activated at the same time. This way, the model may leverage the learnings from all those adapters at the same time. As an example, if you have a diffusion model, you may want to use one LoRA adapter to change the style and a different one to change the subject.
|
||||
|
||||
Activating multiple adapters at the same time is generally possible on all PEFT methods (LoRA, LoHa, IA³, etc.) except for prompt learning methods (p-tuning, prefix tuning, etc.). The following example illustrates how to achieve this:
|
||||
|
||||
```python
|
||||
from transformers import AutoModelForCausalLM
|
||||
from peft import PeftModel
|
||||
|
||||
model_id = ...
|
||||
base_model = AutoModelForCausalLM.from_pretrained(model_id)
|
||||
model = PeftModel.from_pretrained(base_model, lora_path_0) # default adapter_name is 'default'
|
||||
model.load_adapter(lora_path_1, adapter_name="other")
|
||||
# the 'other' adapter was loaded but it's not active yet, so to activate both adapters:
|
||||
model.base_model.set_adapter(["default", "other"])
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> In the example above, you can see that we need to call `model.base_model.set_adapter(["default", "other"])`. Why can we not call `model.set_adapter(["default", "other"])`? This is unfortunately not possible because, as explained earlier, some PEFT methods don't support activating more than one adapter at a time.
|
||||
|
||||
It is also possible to train two adapters at the same time, but you should be careful to ensure that the weights of both adapters are known to the optimizer. Otherwise, only one adapter will receive updates.
|
||||
|
||||
```python
|
||||
from transformers import AutoModelForCausalLM
|
||||
from peft import LoraConfig, get_peft_model
|
||||
|
||||
model_id = ...
|
||||
base_model = AutoModelForCausalLM.from_pretrained(model_id)
|
||||
lora_config_0 = LoraConfig(...)
|
||||
lora_config_1 = LoraConfig(...)
|
||||
model = get_peft_model(base_model, lora_config_0)
|
||||
model.add_adapter(adapter_name="other", peft_config=lora_config_1)
|
||||
```
|
||||
|
||||
If we would now call:
|
||||
|
||||
```python
|
||||
from transformers import Trainer
|
||||
|
||||
trainer = Trainer(model=model, ...)
|
||||
trainer.train()
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```python
|
||||
optimizer = torch.optim.AdamW([param for param in model.parameters() if param.requires_grad], ...)
|
||||
```
|
||||
|
||||
then the second LoRA adapter (`"other"`) would not be trained. This is because it is inactive at this moment, which means the `requires_grad` attribute on its parameters is set to `False` and the optimizer will ignore it. Therefore, make sure to activate all adapters that should be trained _before_ initializing the optimizer:
|
||||
|
||||
```python
|
||||
# activate all adapters
|
||||
model.base_model.set_adapter(["default", "other"])
|
||||
trainer = Trainer(model=model, ...)
|
||||
trainer.train()
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> This section deals with using multiple adapters _of the same type_ on the same model, for example, using multiple LoRA adapters at the same time. It does not apply to using _different types_ of adapters on the same model, for example one LoRA adapter and one LoHa adapter. For this, please check [`PeftMixedModel`](https://huggingface.co/docs/peft/developer_guides/mixed_models).
|
||||
@@ -0,0 +1,152 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# PEFT integrations
|
||||
|
||||
PEFT's practical benefits extends to other Hugging Face libraries like [Diffusers](https://hf.co/docs/diffusers) and [Transformers](https://hf.co/docs/transformers). One of the main benefits of PEFT is that an adapter file generated by a PEFT method is a lot smaller than the original model, which makes it super easy to manage and use multiple adapters. You can use one pretrained base model for multiple tasks by simply loading a new adapter finetuned for the task you're solving. Or you can combine multiple adapters with a text-to-image diffusion model to create new effects.
|
||||
|
||||
This tutorial will show you how PEFT can help you manage adapters in Diffusers and Transformers.
|
||||
|
||||
## Diffusers
|
||||
|
||||
Diffusers is a generative AI library for creating images and videos from text or images with diffusion models. LoRA is an especially popular training method for diffusion models because you can very quickly train and share diffusion models to generate images in new styles. To make it easier to use and try multiple LoRA models, Diffusers uses the PEFT library to help manage different adapters for inference.
|
||||
|
||||
For example, load a base model and then load the [artificialguybr/3DRedmond-V1](https://huggingface.co/artificialguybr/3DRedmond-V1) adapter for inference with the [`load_lora_weights`](https://huggingface.co/docs/diffusers/v0.24.0/en/api/loaders/lora#diffusers.loaders.LoraLoaderMixin.load_lora_weights) method. The `adapter_name` argument in the loading method is enabled by PEFT and allows you to set a name for the adapter so it is easier to reference.
|
||||
|
||||
```py
|
||||
import torch
|
||||
from diffusers import DiffusionPipeline
|
||||
|
||||
pipeline = DiffusionPipeline.from_pretrained(
|
||||
"stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16
|
||||
).to("cuda")
|
||||
pipeline.load_lora_weights(
|
||||
"peft-internal-testing/artificialguybr__3DRedmond-V1",
|
||||
weight_name="3DRedmond-3DRenderStyle-3DRenderAF.safetensors",
|
||||
adapter_name="3d"
|
||||
)
|
||||
image = pipeline("sushi rolls shaped like kawaii cat faces").images[0]
|
||||
image
|
||||
```
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/ybelkada/documentation-images/resolve/main/test-lora-diffusers.png"/>
|
||||
</div>
|
||||
|
||||
Now let's try another cool LoRA model, [ostris/super-cereal-sdxl-lora](https://huggingface.co/ostris/super-cereal-sdxl-lora). All you need to do is load and name this new adapter with `adapter_name`, and use the [`set_adapters`](https://huggingface.co/docs/diffusers/api/loaders/unet#diffusers.loaders.UNet2DConditionLoadersMixin.set_adapters) method to set it as the currently active adapter.
|
||||
|
||||
```py
|
||||
pipeline.load_lora_weights(
|
||||
"ostris/super-cereal-sdxl-lora",
|
||||
weight_name="cereal_box_sdxl_v1.safetensors",
|
||||
adapter_name="cereal"
|
||||
)
|
||||
pipeline.set_adapters("cereal")
|
||||
image = pipeline("sushi rolls shaped like kawaii cat faces").images[0]
|
||||
image
|
||||
```
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/ybelkada/documentation-images/resolve/main/test-lora-diffusers-2.png"/>
|
||||
</div>
|
||||
|
||||
Finally, you can call the [`disable_lora`](https://huggingface.co/docs/diffusers/api/loaders/unet#diffusers.loaders.UNet2DConditionLoadersMixin.disable_lora) method to restore the base model.
|
||||
|
||||
```py
|
||||
pipeline.disable_lora()
|
||||
```
|
||||
|
||||
Learn more about how PEFT supports Diffusers in the [Inference with PEFT](https://huggingface.co/docs/diffusers/tutorials/using_peft_for_inference) tutorial.
|
||||
|
||||
## Transformers
|
||||
|
||||
🤗 [Transformers](https://hf.co/docs/transformers) is a collection of pretrained models for all types of tasks in all modalities. You can load these models for training or inference. Many of the models are large language models (LLMs), so it makes sense to integrate PEFT with Transformers to manage and train adapters.
|
||||
|
||||
Load a base pretrained model to train.
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained("facebook/opt-350m")
|
||||
```
|
||||
|
||||
Next, add an adapter configuration to specify how to adapt the model parameters. Call the [`~PeftModel.add_adapter`] method to add the configuration to the base model.
|
||||
|
||||
```py
|
||||
from peft import LoraConfig
|
||||
|
||||
peft_config = LoraConfig(
|
||||
lora_alpha=16,
|
||||
lora_dropout=0.1,
|
||||
r=64,
|
||||
bias="none",
|
||||
task_type="CAUSAL_LM"
|
||||
)
|
||||
model.add_adapter(peft_config)
|
||||
```
|
||||
|
||||
Now you can train the model with Transformer's [`~transformers.Trainer`] class or whichever training framework you prefer.
|
||||
|
||||
To use the newly trained model for inference, the [`~transformers.AutoModel`] class uses PEFT on the backend to load the adapter weights and configuration file into a base pretrained model.
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained("peft-internal-testing/opt-350m-lora")
|
||||
```
|
||||
|
||||
Alternatively, you can use transformers [Pipelines](https://huggingface.co/docs/transformers/en/main_classes/pipelines) to load the model for conveniently running inference:
|
||||
|
||||
```py
|
||||
from transformers import pipeline
|
||||
|
||||
model = pipeline("text-generation", "peft-internal-testing/opt-350m-lora")
|
||||
print(model("Hello World"))
|
||||
```
|
||||
|
||||
If you're interested in comparing or using more than one adapter, you can call the [`~PeftModel.add_adapter`] method to add the adapter configuration to the base model. The only requirement is the adapter type must be the same (you can't mix a LoRA and LoHa adapter).
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM
|
||||
from peft import LoraConfig
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained("facebook/opt-350m")
|
||||
model.add_adapter(lora_config_1, adapter_name="adapter_1")
|
||||
```
|
||||
|
||||
Call [`~PeftModel.add_adapter`] again to attach a new adapter to the base model.
|
||||
|
||||
```py
|
||||
model.add_adapter(lora_config_2, adapter_name="adapter_2")
|
||||
```
|
||||
|
||||
Then you can use [`~PeftModel.set_adapter`] to set the currently active adapter.
|
||||
|
||||
```py
|
||||
model.set_adapter("adapter_1")
|
||||
output = model.generate(**inputs)
|
||||
print(tokenizer.decode(output_disabled[0], skip_special_tokens=True))
|
||||
```
|
||||
|
||||
To disable the adapter, call the [disable_adapters](https://github.com/huggingface/transformers/blob/4e3490f79b40248c53ee54365a9662611e880892/src/transformers/integrations/peft.py#L313) method.
|
||||
|
||||
```py
|
||||
model.disable_adapters()
|
||||
```
|
||||
|
||||
The [enable_adapters](https://github.com/huggingface/transformers/blob/4e3490f79b40248c53ee54365a9662611e880892/src/transformers/integrations/peft.py#L336) can be used to enable the adapters again.
|
||||
|
||||
If you're curious, check out the [Load and train adapters with PEFT](https://huggingface.co/docs/transformers/main/peft) tutorial to learn more.
|
||||
@@ -0,0 +1,179 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# PEFT configurations and models
|
||||
|
||||
The sheer size of today's large pretrained models - which commonly have billions of parameters - presents a significant training challenge because they require more storage space and more computational power to crunch all those calculations. You'll need access to powerful GPUs or TPUs to train these large pretrained models which is expensive, not widely accessible to everyone, not environmentally friendly, and not very practical. PEFT methods address many of these challenges. There are several types of PEFT methods (soft prompting, matrix decomposition, adapters), but they all focus on the same thing, reduce the number of trainable parameters. This makes it more accessible to train and store large models on consumer hardware.
|
||||
|
||||
The PEFT library is designed to help you quickly train large models on free or low-cost GPUs, and in this tutorial, you'll learn how to setup a configuration to apply a PEFT method to a pretrained base model for training. Once the PEFT configuration is setup, you can use any training framework you like (Transformer's [`~transformers.Trainer`] class, [Accelerate](https://hf.co/docs/accelerate), a custom PyTorch training loop).
|
||||
|
||||
## PEFT configurations
|
||||
|
||||
> [!TIP]
|
||||
> Learn more about the parameters you can configure for each PEFT method in their respective API reference page.
|
||||
|
||||
A configuration stores important parameters that specify how a particular PEFT method should be applied.
|
||||
|
||||
For example, take a look at the following `LoraConfig` for applying LoRA and `PromptEncoderConfig` for applying p-tuning (these configuration files are already JSON-serialized). Whenever you load a PEFT adapter, it is a good idea to check whether it has an associated `adapter_config.json` file which is required.
|
||||
|
||||
<hfoptions id="config">
|
||||
<hfoption id="LoraConfig">
|
||||
|
||||
```json
|
||||
{
|
||||
"base_model_name_or_path": "facebook/opt-350m", #base model to apply LoRA to
|
||||
"bias": "none",
|
||||
"fan_in_fan_out": false,
|
||||
"inference_mode": true,
|
||||
"init_lora_weights": true,
|
||||
"layers_pattern": null,
|
||||
"layers_to_transform": null,
|
||||
"lora_alpha": 32,
|
||||
"lora_dropout": 0.05,
|
||||
"modules_to_save": null,
|
||||
"peft_type": "LORA", #PEFT method type
|
||||
"r": 16,
|
||||
"revision": null,
|
||||
"target_modules": [
|
||||
"q_proj", #model modules to apply LoRA to (query and value projection layers)
|
||||
"v_proj"
|
||||
],
|
||||
"task_type": "CAUSAL_LM" #type of task to train model on
|
||||
}
|
||||
```
|
||||
|
||||
You can create your own configuration for training by initializing a [`LoraConfig`].
|
||||
|
||||
```py
|
||||
from peft import LoraConfig, TaskType
|
||||
|
||||
lora_config = LoraConfig(
|
||||
r=16,
|
||||
target_modules=["q_proj", "v_proj"],
|
||||
task_type=TaskType.CAUSAL_LM,
|
||||
lora_alpha=32,
|
||||
lora_dropout=0.05
|
||||
)
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="PromptEncoderConfig">
|
||||
|
||||
```json
|
||||
{
|
||||
"base_model_name_or_path": "roberta-large", #base model to apply p-tuning to
|
||||
"encoder_dropout": 0.0,
|
||||
"encoder_hidden_size": 128,
|
||||
"encoder_num_layers": 2,
|
||||
"encoder_reparameterization_type": "MLP",
|
||||
"inference_mode": true,
|
||||
"num_attention_heads": 16,
|
||||
"num_layers": 24,
|
||||
"num_transformer_submodules": 1,
|
||||
"num_virtual_tokens": 20,
|
||||
"peft_type": "P_TUNING", #PEFT method type
|
||||
"task_type": "SEQ_CLS", #type of task to train model on
|
||||
"token_dim": 1024
|
||||
}
|
||||
```
|
||||
|
||||
You can create your own configuration for training by initializing a [`PromptEncoderConfig`].
|
||||
|
||||
```py
|
||||
from peft import PromptEncoderConfig, TaskType
|
||||
|
||||
p_tuning_config = PromptEncoderConfig(
|
||||
encoder_reparameterization_type="MLP",
|
||||
encoder_hidden_size=128,
|
||||
num_attention_heads=16,
|
||||
num_layers=24,
|
||||
num_transformer_submodules=1,
|
||||
num_virtual_tokens=20,
|
||||
token_dim=1024,
|
||||
task_type=TaskType.SEQ_CLS
|
||||
)
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
## PEFT models
|
||||
|
||||
With a PEFT configuration in hand, you can now apply it to any pretrained model to create a [`PeftModel`]. Choose from any of the state-of-the-art models from the [Transformers](https://hf.co/docs/transformers) library, a custom model, and even new and unsupported transformer architectures.
|
||||
|
||||
For this tutorial, load a base [facebook/opt-350m](https://huggingface.co/facebook/opt-350m) model to finetune.
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained("facebook/opt-350m")
|
||||
```
|
||||
|
||||
Use the [`get_peft_model`] function to create a [`PeftModel`] from the base facebook/opt-350m model and the `lora_config` you created earlier.
|
||||
|
||||
```py
|
||||
from peft import get_peft_model
|
||||
|
||||
lora_model = get_peft_model(model, lora_config)
|
||||
lora_model.print_trainable_parameters()
|
||||
"trainable params: 1,572,864 || all params: 332,769,280 || trainable%: 0.472659014678278"
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> When calling [`get_peft_model`], the base model will be modified *in-place*. That means, when calling [`get_peft_model`] on a model that was already modified in the same way before, this model will be further mutated. Therefore, if you would like to modify your PEFT configuration after having called [`get_peft_model()`] before, you would first have to unload the model with [`~LoraModel.unload`] and then call [`get_peft_model()`] with your new configuration. Alternatively, you can re-initialize the model to ensure a fresh, unmodified state before applying a new PEFT configuration.
|
||||
|
||||
Now you can train the [`PeftModel`] with your preferred training framework! After training, you can save your model locally with [`~PeftModel.save_pretrained`] or upload it to the Hub with the [`~transformers.PreTrainedModel.push_to_hub`] method.
|
||||
|
||||
```py
|
||||
# save locally
|
||||
lora_model.save_pretrained("your-name/opt-350m-lora")
|
||||
|
||||
# push to Hub
|
||||
lora_model.push_to_hub("your-name/opt-350m-lora")
|
||||
```
|
||||
|
||||
To load a [`PeftModel`] for inference, you'll need to provide the [`PeftConfig`] used to create it and the base model it was trained from.
|
||||
|
||||
```py
|
||||
from peft import PeftModel, PeftConfig
|
||||
|
||||
config = PeftConfig.from_pretrained("ybelkada/opt-350m-lora")
|
||||
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
|
||||
lora_model = PeftModel.from_pretrained(model, "ybelkada/opt-350m-lora")
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> By default, the [`PeftModel`] is set for inference, but if you'd like to train the adapter some more you can set `is_trainable=True`.
|
||||
>
|
||||
> ```py
|
||||
> lora_model = PeftModel.from_pretrained(model, "ybelkada/opt-350m-lora", is_trainable=True)
|
||||
> ```
|
||||
|
||||
The [`PeftModel.from_pretrained`] method is the most flexible way to load a [`PeftModel`] because it doesn't matter what model framework was used (Transformers, timm, a generic PyTorch model). Other classes, like [`AutoPeftModel`], are just a convenient wrapper around the base [`PeftModel`], and makes it easier to load PEFT models directly from the Hub or locally where the PEFT weights are stored.
|
||||
|
||||
```py
|
||||
from peft import AutoPeftModelForCausalLM
|
||||
|
||||
lora_model = AutoPeftModelForCausalLM.from_pretrained("ybelkada/opt-350m-lora")
|
||||
```
|
||||
|
||||
Take a look at the [AutoPeftModel](../package_reference/auto_class) API reference to learn more about the [`AutoPeftModel`] classes.
|
||||
|
||||
## Next steps
|
||||
|
||||
With the appropriate [`PeftConfig`], you can apply it to any pretrained model to create a [`PeftModel`] and train large powerful models faster on freely available GPUs! To learn more about PEFT configurations and models, the following guide may be helpful:
|
||||
|
||||
* Learn how to configure a PEFT method for models that aren't from Transformers in the [Working with custom models](../developer_guides/custom_models) guide.
|
||||
@@ -0,0 +1,42 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# PEFT
|
||||
|
||||
🤗 PEFT (Parameter-Efficient Fine-Tuning) is a library for efficiently adapting large pretrained models to various downstream applications without fine-tuning all of a model's parameters because it is prohibitively costly. PEFT methods only fine-tune a small number of (extra) model parameters - significantly decreasing computational and storage costs - while yielding performance comparable to a fully fine-tuned model. This makes it more accessible to train and store large language models (LLMs) and other big models on consumer hardware.
|
||||
|
||||
PEFT is integrated with the Transformers, Diffusers, and Accelerate libraries to provide a faster and easier way to load, train, and use large models for inference.
|
||||
|
||||
<div class="flex justify-center">
|
||||
<div class="flex flex-col basis-1/4">
|
||||
There are numerous methods to "adapt" existing models, often extensively integrating into the model. PEFT can be thought of as a framework for arbitrary methods of model adaption (modifying weights, wrapping layers, manipulating KV-caches, ...) while also serving as a reference implementation for many fine-tuning methods.
|
||||
</div>
|
||||
<div class="flex flex-col basis-3/4 pl-10 pr-10"><img style="border: 0;box-shadow: none;border-radius: 0;" src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/adapter_installation.png" width="100%"></div>
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
<div class="w-full flex flex-col space-y-2 md:space-y-0 md:grid md:grid-cols-2 md:gap-y-4 md:gap-x-5">
|
||||
<a class="!no-underline border dark:border-gray-700 p-5 rounded-lg shadow hover:shadow-lg" href="quicktour"
|
||||
><div class="w-full text-center bg-gradient-to-br from-blue-400 to-blue-500 rounded-lg py-1.5 font-semibold mb-5 text-white text-lg leading-relaxed">Quicktour</div>
|
||||
<p class="text-gray-700">Start here if you're new to 🤗 PEFT to get an overview of the library's main features, and how to train a model with a PEFT method.</p>
|
||||
</a>
|
||||
<a class="!no-underline border dark:border-gray-700 p-5 rounded-lg shadow hover:shadow-lg" href="./methods/overview"
|
||||
><div class="w-full text-center bg-gradient-to-br from-indigo-400 to-indigo-500 rounded-lg py-1.5 font-semibold mb-5 text-white text-lg leading-relaxed">Method overview</div>
|
||||
<p class="text-gray-700">Learn about the different categories of PEFT methods to get an orientation what to use with your model.</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Installation
|
||||
|
||||
Before you start, you will need to setup your environment, install the appropriate packages, and configure 🤗 PEFT. 🤗 PEFT is tested on **Python 3.9+**.
|
||||
|
||||
🤗 PEFT is available on PyPI, as well as GitHub:
|
||||
|
||||
## PyPI
|
||||
|
||||
To install 🤗 PEFT from PyPI:
|
||||
|
||||
```bash
|
||||
pip install peft
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
New features that haven't been released yet are added every day, which also means there may be some bugs. To try them out, install from the GitHub repository:
|
||||
|
||||
```bash
|
||||
pip install git+https://github.com/huggingface/peft
|
||||
```
|
||||
|
||||
If you wish to play with the source code and see live results as you run the code, an editable version
|
||||
can be installed from a locally-cloned version of the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/huggingface/peft
|
||||
cd peft
|
||||
pip install -e ".[test]"
|
||||
```
|
||||
|
||||
If you're planning to contribute to PEFT, follow the [contributing guide](developer_guides/contributing#installation) instead, which also covers forking, adding the upstream remote, and creating a working branch.
|
||||
@@ -0,0 +1,65 @@
|
||||
<!--Copyright 2026-present The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
# Parameter efficient fine-tuning methods
|
||||
|
||||
PEFT methods train as few parameters as possible while aiming for performance comparable to full fine-tuning. Fewer trainable parameters are less expressive, so the same performance isn't guaranteed. In exchange you use less memory, often less compute, and gain features like fast hot-swapping between expert adapters and less forgetting of prior knowledge.
|
||||
|
||||
Giving general advice for training large models is hard but for generative models, especially language models, you can follow these steps:
|
||||
|
||||
1. use prompting (e.g. few-shot examples in the prompt) to see if the model is already capable of the task. If the model solves your problem, great! You can now use [Prompt-based methods](#prompt-based-methods) to learn the prompt and save precious tokens.
|
||||
2. If prompt-based methods are not sufficient you can use [layer tuning](#layer-tuning) and [adapter methods](#adapter-methods). These methods are generally more expressive than prompt-based methods and get closer to full-finetuning.
|
||||
3. Make sure to measure retention of already learnt knowledge since each fine-tuning step is potentially unlearning past knowledge.
|
||||
|
||||
The [PEFT method comparison suite](https://huggingface.co/spaces/peft-internal-testing/PEFT-method-comparison) aims to give a rough overview of (most) implemented methods on selected benchmarks and models.
|
||||
|
||||
> [!NOTE]
|
||||
> Not all PEFT methods are created equal and there are differences between capabilities:
|
||||
> * Quantization: not all methods support quantized base models
|
||||
> * Features: not all features are supported for all methods (e.g., multiple adapters, mixed adapter inference, merging/unmerging)
|
||||
> * Layer types: linear layers are generally supported, but not all adapter methods support embedding (important for extending vocabulary) or convolutional layers (important for some image models)
|
||||
> * Runtime: PEFT methods generally add runtime overhead but some of that can be mitigated (e.g., by [merging the adapter weights](../developer_guides/checkpoint#merge-the-weights))
|
||||
|
||||
## Prompt-based methods
|
||||
|
||||
Prompting primes a frozen pretrained model for a specific downstream task by including a text prompt that describes the task or even demonstrates an example of the task. With prompting, you can avoid fully training a separate model for each downstream task, and use the same frozen pretrained model instead. This is a lot easier because you can use the same model for several different tasks, and it is significantly more efficient to train and store a smaller set of prompt parameters than to train all the model's parameters.
|
||||
|
||||
There are two categories of prompting methods:
|
||||
|
||||
- hard prompts are manually handcrafted text prompts with discrete input tokens; the downside is that it requires a lot of effort to create a good prompt
|
||||
- soft prompts are learnable tensors concatenated with the input embeddings that can be optimized to a dataset; the downside is that they aren't human readable because you aren't matching these "virtual tokens" to the embeddings of a real word
|
||||
|
||||
The PEFT library supports several types of prompting methods (p-tuning, prefix tuning, prompt tuning, ...), explore the table of contents for a full listing of soft prompt methods.
|
||||
If you're interested in applying these methods to other tasks and use cases, take a look at our [notebook collection](https://huggingface.co/spaces/PEFT/soft-prompting)!
|
||||
|
||||
> [!TIP]
|
||||
> Some familiarity with the general process of training a causal language model would be really helpful and allow you to focus on the soft prompting methods. If you're new, we recommend taking a look at the [Causal language modeling](https://huggingface.co/docs/transformers/tasks/language_modeling) guide first from the Transformers documentation. When you're ready, come back and see how easy it is to drop PEFT into your training!
|
||||
|
||||
## Layer Tuning
|
||||
|
||||
Layer Tuning categorizes methods that target one type of layer or one aspect of a layer specifically, for example [LayerNorm Tuning](../package_reference/layernorm_tuning) targets only [`LayerNorm`](https://docs.pytorch.org/docs/stable/generated/torch.nn.LayerNorm.html) layers and [TrainableTokens](../package_reference/trainable_tokens) only targets specific tokens in the embedding matrix. This contrasts prompt-based methods which work with the model input or adapter methods which extend the existing weights and are generally more independent of the layer type, targeting linear or convolutional layers.
|
||||
|
||||
## Adapter methods
|
||||
|
||||
Adapter methods can be seen as ways of adding relatively small, trainable matrices to existing models for fine-tuning. The goal is to introduce few trainable parameters to steer the big model in the direction of the task that needs fine-tuning to save on resources, such as memory or compute.
|
||||
|
||||
A popular way to realize adapters is to insert smaller trainable matrices that are a low-rank decomposition of the adapted weight's layout to save on memory. There are several different ways to express the weight matrix as a low-rank decomposition, but [Low-Rank Adaptation (LoRA)](../package_reference/lora) is the most common method. The PEFT library supports several other variations of this formulation - some are direct variants of LoRA and are documented under LoRA, some are different enough to count as their own methods, such as [Low-Rank Hadamard Product (LoHa)](../package_reference/loha), [Low-Rank Kronecker Product (LoKr)](../package_reference/lokr), and [Adaptive Low-Rank Adaptation (AdaLoRA)](../package_reference/adalora). If you're interested in applying these methods to other tasks and use cases like semantic segmentation, token classification, take a look at our [notebook collection](https://huggingface.co/collections/PEFT/notebooks-6573b28b33e5a4bf5b157fc1)!
|
||||
|
||||
> [!TIP]
|
||||
> LoRA is one of the most popular PEFT methods and a good starting point if you're just getting started with PEFT. It was originally developed for large language models but it is a tremendously popular training method for diffusion models because of its efficiency and effectiveness.
|
||||
|
||||
Low-rank adapters are only one possible adapter formulation, PEFT implements many other types of adapters as well. For example, Orthogonal Fine-Tuning methods ([OFT](../package_reference/oft), [BOFT](../package_reference/boft), ...) use orthogonal decompositions of the adapter weights to achieve small size. Methods like [MiSS](../package_reference/miss) shard matrices and share these shards to save on memory. [IA3](../package_reference/ia3) introduces learned vectors that rescale the key, value, and feed-forward activations.
|
||||
@@ -0,0 +1,79 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# AdaLoRA
|
||||
|
||||
[AdaLoRA](https://hf.co/papers/2303.10512) (Adaptive LoRA) is a method for optimizing the number of trainable parameters to assign to weight matrices and layers, unlike LoRA, which distributes parameters evenly across all modules. More parameters are budgeted for important weight matrices and layers while less important ones receive fewer parameters. You can control the average desired *rank* or `r` of the matrices, and which modules to apply AdaLoRA to with `target_modules`. Other important parameters to set are `lora_alpha` (scaling factor), and `modules_to_save` (the modules apart from the AdaLoRA layers to be trained and saved). All of these parameters - and more - are found in the [`AdaLoraConfig`].
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Fine-tuning large pre-trained language models on downstream tasks has become an important paradigm in NLP. However, common practice fine-tunes all of the parameters in a pre-trained model, which becomes prohibitive when a large number of downstream tasks are present. Therefore, many fine-tuning methods are proposed to learn incremental updates of pre-trained weights in a parameter efficient way, e.g., low-rank increments. These methods often evenly distribute the budget of incremental updates across all pre-trained weight matrices, and overlook the varying importance of different weight parameters. As a consequence, the fine-tuning performance is suboptimal. To bridge this gap, we propose AdaLoRA, which adaptively allocates the parameter budget among weight matrices according to their importance score. In particular, AdaLoRA parameterizes the incremental updates in the form of singular value decomposition. Such a novel approach allows us to effectively prune the singular values of unimportant updates, which is essentially to reduce their parameter budget but circumvent intensive exact SVD computations. We conduct extensive experiments with several pre-trained models on natural language processing, question answering, and natural language generation to validate the effectiveness of AdaLoRA. Results demonstrate that AdaLoRA manifests notable improvement over baselines, especially in the low budget settings. Our code is publicly available at https://github.com/QingruZhang/AdaLoRA*.
|
||||
|
||||
> [!WARNING]
|
||||
> AdaLoRA has an [`~AdaLoraModel.update_and_allocate`] method that should be called at each training step to update the parameter budget and mask, otherwise the adaptation step is not performed. This requires writing a custom training loop or subclassing the [`~transformers.Trainer`] to incorporate this method. As an example, take a look at this [custom training loop](https://github.com/huggingface/peft/blob/912ad41e96e03652cabf47522cd876076f7a0c4f/examples/conditional_generation/peft_adalora_seq2seq.py#L120).
|
||||
|
||||
AdaLoRA manages the parameter budget introduced from LoRA by allocating more parameters - in other words, a higher rank `r` - for important weight matrices that are better adapted for a task and pruning less important ones. The rank is controlled by a method similar to singular value decomposition (SVD). The $\Delta W$ is parameterized with two orthogonal matrices and a diagonal matrix which contains singular values. This parametrization method avoids iteratively applying SVD which is computationally expensive. Based on this method, the rank of $\Delta W$ is adjusted according to an importance score. $\Delta W$ is divided into triplets and each triplet is scored according to its contribution to model performance. Triplets with low importance scores are pruned and triplets with high importance scores are kept for finetuning.
|
||||
|
||||
Training with AdaLoRA has three phases: the init phase, the budgeting phase and the final phase. In the initial phase, no budgeting is applied, therefore the ranks are not touched. During the budgeting phase the process described above is applied and the rank is redistributed according to a budget, aiming to give more important adapters more rank and less important layers less. When reaching the final phase, budgeting has ended, the ranks are redistributed but we may continue training for a while with the redistributed ranks to further improve performance.
|
||||
|
||||
> [!NOTE]
|
||||
> **Contributions welcome**: This section needs clarification.
|
||||
>
|
||||
> It is unclear how importance is measured. The explanations are also a bit redundant and could benefit from consolidation.
|
||||
> See [here](../developer_guides/contributing#documentation-improvements) on how to contribute.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=ADALORA"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
```py
|
||||
from peft import AdaLoraConfig, get_peft_model
|
||||
|
||||
config = AdaLoraConfig(
|
||||
r=8,
|
||||
init_r=12,
|
||||
tinit=200,
|
||||
tfinal=1000,
|
||||
deltaT=10,
|
||||
target_modules=["query", "value"],
|
||||
modules_to_save=["classifier"],
|
||||
)
|
||||
model = get_peft_model(model, config)
|
||||
model.print_trainable_parameters()
|
||||
"trainable params: 520,325 || all params: 87,614,722 || trainable%: 0.5938785036606062"
|
||||
|
||||
[... training code ...]
|
||||
|
||||
model.update_and_allocate(step_idx)
|
||||
```
|
||||
|
||||
# API
|
||||
|
||||
## AdaLoraConfig
|
||||
|
||||
[[autodoc]] tuners.adalora.config.AdaLoraConfig
|
||||
|
||||
## AdaLoraModel
|
||||
|
||||
[[autodoc]] tuners.adalora.model.AdaLoraModel
|
||||
@@ -0,0 +1,48 @@
|
||||
<!--Copyright 2026 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# AdaMSS
|
||||
|
||||
[AdaMSS](https://openreview.net/forum?id=8ZdWmpYxT0) (AdaMSS: Adaptive Multi-Subspace Approach for Parameter-Efficient Fine-Tuning) is a parameter-efficient fine-tuning method that decomposes weight matrices using SVD and clusters the decomposed space into multiple trainable subspaces. Each subspace learns independent low-rank updates while the original weights remain frozen. AdaMSS also supports Adaptive Subspace Allocation (ASA), which dynamically prunes less important subspaces during training based on gradient information.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
> We propose AdaMSS, an adaptive multi-subspace approach for parameter-efficient fine-tuning of large models. Unlike traditional parameterefficient fine-tuning methods that operate within a large single subspace of the network weights, AdaMSS leverages subspace segmentation to obtain multiple smaller subspaces and adaptively reduces the number of trainable parameters during training, ultimately updating only those associated with a small subset of subspaces most relevant to the target downstream task. By using the lowest-rank representation, AdaMSS achieves more compact expressiveness and finer tuning of the model parameters. Theoretical analyses demonstrate that AdaMSS has better generalization guarantee than LoRA, PiSSA, and other single-subspace low-rankbased methods. Extensive experiments across image classification, natural language understanding, and natural language generation tasks show that AdaMSS achieves comparable performance to full fine-tuning and outperforms other parameterefficient fine-tuning methods in most cases, all while requiring fewer trainable parameters. Notably, on the ViT-Large model, AdaMSS achieves 4.7% higher average accuracy than LoRA across seven tasks, using just 15.4% of the trainable parameters. On RoBERTa-Large, AdaMSS outperforms PiSSA by 7% in average accuracy across six tasks while reducing the number of trainable parameters by approximately 94.4%. These results demonstrate the effectiveness of AdaMSS in parameter-efficient fine-tuning. The code for AdaMSS is available at https: //github.com/jzheng20/AdaMSS.
|
||||
|
||||
AdaMSS currently has the following constraints:
|
||||
- Only `nn.Linear` layers are supported.
|
||||
- Requires scikit-learn for the KMeans clustering step.
|
||||
|
||||
If these constraints don't work for your use case, consider other methods instead.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=ADAMSS"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## AdamssConfig
|
||||
|
||||
[[autodoc]] tuners.adamss.config.AdamssConfig
|
||||
|
||||
## AdamssModel
|
||||
|
||||
[[autodoc]] tuners.adamss.model.AdamssModel
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# LyCORIS
|
||||
|
||||
[LyCORIS](https://hf.co/papers/2309.14859) (Lora beYond Conventional methods, Other Rank adaptation Implementations for Stable diffusion) are LoRA-like matrix decomposition adapters that modify the cross-attention layer of the UNet. The [LoHa](loha) and [LoKr](lokr) methods inherit from the `Lycoris` classes here.
|
||||
|
||||
## LycorisConfig
|
||||
|
||||
[[autodoc]] tuners.lycoris_utils.LycorisConfig
|
||||
|
||||
## LycorisLayer
|
||||
|
||||
[[autodoc]] tuners.lycoris_utils.LycorisLayer
|
||||
|
||||
## LycorisTuner
|
||||
|
||||
[[autodoc]] tuners.lycoris_utils.LycorisTuner
|
||||
@@ -0,0 +1,48 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# AutoPeftModels
|
||||
|
||||
The `AutoPeftModel` classes loads the appropriate PEFT model for the task type by automatically inferring it from the configuration file. They are designed to quickly and easily load a PEFT model in a single line of code without having to worry about which exact model class you need or manually loading a [`PeftConfig`].
|
||||
|
||||
## AutoPeftModel
|
||||
|
||||
[[autodoc]] auto.AutoPeftModel
|
||||
- from_pretrained
|
||||
|
||||
## AutoPeftModelForCausalLM
|
||||
|
||||
[[autodoc]] auto.AutoPeftModelForCausalLM
|
||||
|
||||
## AutoPeftModelForSeq2SeqLM
|
||||
|
||||
[[autodoc]] auto.AutoPeftModelForSeq2SeqLM
|
||||
|
||||
## AutoPeftModelForSequenceClassification
|
||||
|
||||
[[autodoc]] auto.AutoPeftModelForSequenceClassification
|
||||
|
||||
## AutoPeftModelForTokenClassification
|
||||
|
||||
[[autodoc]] auto.AutoPeftModelForTokenClassification
|
||||
|
||||
## AutoPeftModelForQuestionAnswering
|
||||
|
||||
[[autodoc]] auto.AutoPeftModelForQuestionAnswering
|
||||
|
||||
## AutoPeftModelForFeatureExtraction
|
||||
|
||||
[[autodoc]] auto.AutoPeftModelForFeatureExtraction
|
||||
@@ -0,0 +1,52 @@
|
||||
<!--Copyright 2026 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# BEFT: Bias-Efficient Fine-Tuning of Language Models in Low-Data Regimes
|
||||
|
||||
[BEFT](https://arxiv.org/abs/2509.15974) is a parameter efficient fine-tuning algorithm (PEFT) that only fine-tunes the added bias terms of value projections from pretrained transformer models. BEFT demonstrates that fine-tuning the added bias terms of value projections from pretrained transformers generally leads to a higher downstream performance in low-data regimes than fine-tuning the added bias terms of query/key projections.
|
||||
|
||||
BEFT currently has the following tradeoffs:
|
||||
|
||||
Pros:
|
||||
- BEFT requires far fewer parameters than LoRA, while maintaining competitive or superior performance across tasks in low-data regimes.
|
||||
|
||||
Cons:
|
||||
- In high-data regimes, BEFT may show limited effectiveness compared to LoRA and full-parameters fine-tuning.
|
||||
|
||||
If your use case belongs to the high-data regime, consider other PEFT methods such as LoRA.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Fine-tuning the bias terms of large language models (LLMs) has the potential to achieve unprecedented parameter efficiency while maintaining competitive performance, particularly in low-data regimes. However, the link between fine-tuning different bias terms (i.e., **b**<sub>q</sub>, **b**<sub>k</sub>, and **b**<sub>v</sub> in the query, key, or value projections) and downstream performance remains largely unclear to date. In this paper, we investigate the link between fine-tuning **b**<sub>q</sub>, **b**<sub>k</sub>, and **b**<sub>v</sub> with the performance of the downstream task. Our key finding is that directly fine-tuning **b**<sub>v</sub> generally leads to higher downstream performance in low-data regimes, in comparison to **b**<sub>q</sub> and **b**<sub>k</sub>. We extensively evaluate this unique property across a wide range of LLMs spanning encoder-only and decoder-only architectures up to 6.7B parameters (including bias-free LLMs). Our results provide strong evidence for the effectiveness of directly fine-tuning **b**<sub>v</sub> across various downstream tasks*.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=BEFT"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## BeftConfig
|
||||
|
||||
[[autodoc]] tuners.beft.config.BeftConfig
|
||||
|
||||
## BeftModel
|
||||
|
||||
[[autodoc]] tuners.beft.model.BeftModel
|
||||
@@ -0,0 +1,92 @@
|
||||
<!--Copyright 2023-present The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# BOFT
|
||||
|
||||
[Orthogonal Butterfly (BOFT)](https://hf.co/papers/2311.06243) is a generic method designed for finetuning foundation models. It improves the parameter efficiency of the finetuning paradigm -- Orthogonal Finetuning (OFT), by taking inspiration from Cooley-Tukey fast Fourier transform, showing favorable results across finetuning different foundation models, including large vision transformers, large language models and text-to-image diffusion models.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Large foundation models are becoming ubiquitous, but training them from scratch is prohibitively expensive. Thus, efficiently adapting these powerful models to downstream tasks is increasingly important. In this paper, we study a principled finetuning paradigm -- Orthogonal Finetuning (OFT) -- for downstream task adaptation. Despite demonstrating good generalizability, OFT still uses a fairly large number of trainable parameters due to the high dimensionality of orthogonal matrices. To address this, we start by examining OFT from an information transmission perspective, and then identify a few key desiderata that enable better parameter-efficiency. Inspired by how the Cooley-Tukey fast Fourier transform algorithm enables efficient information transmission, we propose an efficient orthogonal parameterization using butterfly structures. We apply this parameterization to OFT, creating a novel parameter-efficient finetuning method, called Orthogonal Butterfly (BOFT). By subsuming OFT as a special case, BOFT introduces a generalized orthogonal finetuning framework. Finally, we conduct an extensive empirical study of adapting large vision transformers, large language models, and text-to-image diffusion models to various downstream tasks in vision and language*.
|
||||
|
||||
BOFT focuses on preserving a pretrained model's generative capabilities while being significantly more parameter-efficient than standard [OFT](./oft). Like OFT, BOFT maintains the same cosine similarity ([hyperspherical energy](https://huggingface.co/papers/1805.09298)) between all pairwise neurons in a layer by applying an orthogonal transformation to the pretrained weight matrix, ensuring the semantic relationships among neurons are preserved.
|
||||
|
||||
Instead of using a block-diagonal orthogonal matrix, BOFT factorizes the orthogonal transformation into a product of **sparse butterfly matrices** (originally introduced in the [Cooley–Tukey FFT](https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm)). Unlike OFT's block-diagonal rotations, which only mix inputs within each block, the butterfly structure guarantees that every input can influence every output, producing a **dense connectivity** with just `O(d log d)` parameters. This factorization preserves expressivity while drastically reducing the parameter count compared to OFT (at the expense of computation time).
|
||||
|
||||
In practice, BOFT multiplies each pretrained weight matrix by a sequence of butterfly-structured orthogonal factors, enabling efficient and expressive neuron rotations. This makes BOFT well-suited for controllable generation and tasks where maintaining the pretrained model's subject representation is critical, while also scaling to larger models with lower memory and compute overhead.
|
||||
|
||||
BOFT can be applied to any subset of weight matrices in a neural network to reduce the number of trainable parameters. Given the target layers for injecting BOFT parameters, the number of trainable parameters can be determined based on the size of the weight matrices.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=BOFT"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
## Merge BOFT weights into the base model
|
||||
|
||||
Similar to LoRA, the weights learned by BOFT can be integrated into the pretrained weight matrices using the [`~BOFTModel.merge_and_unload()` function. This function merges the adapter weights with the base model which allows you to effectively use the newly merged model as a standalone model.
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://raw.githubusercontent.com/wy1iu/butterfly-oft/main/assets/boft_merge.png"/>
|
||||
</div>
|
||||
|
||||
This works because during training, the orthogonal weight matrix (R in the diagram above) and the pretrained weight matrices are separate. But once training is complete, these weights can actually be merged (multiplied) into a new weight matrix that is equivalent.
|
||||
|
||||
## BOFT Example Usage
|
||||
|
||||
For an example of the BOFT method application to various downstream tasks, please refer to the following guides:
|
||||
|
||||
Take a look at the following step-by-step guides on how to finetune a model with BOFT:
|
||||
- [Dreambooth finetuning with BOFT](https://github.com/huggingface/peft/blob/main/examples/boft_dreambooth/boft_dreambooth.md)
|
||||
- [Controllable generation finetuning with BOFT (ControlNet)](https://github.com/huggingface/peft/blob/main/examples/boft_controlnet/boft_controlnet.md)
|
||||
|
||||
For the task of image classification, one can initialize the BOFT config for a DinoV2 model as follows:
|
||||
|
||||
```py
|
||||
import transformers
|
||||
from transformers import AutoModelForSeq2SeqLM, BOFTConfig
|
||||
from peft import BOFTConfig, get_peft_model
|
||||
|
||||
config = BOFTConfig(
|
||||
boft_block_size=4,
|
||||
boft_n_butterfly_factor=2,
|
||||
target_modules=["query", "value", "key", "output.dense", "mlp.fc1", "mlp.fc2"],
|
||||
boft_dropout=0.1,
|
||||
bias="boft_only",
|
||||
modules_to_save=["classifier"],
|
||||
)
|
||||
|
||||
model = transformers.Dinov2ForImageClassification.from_pretrained(
|
||||
"facebook/dinov2-large",
|
||||
num_labels=100,
|
||||
)
|
||||
|
||||
boft_model = get_peft_model(model, config)
|
||||
```
|
||||
|
||||
# API
|
||||
|
||||
## BOFTConfig
|
||||
|
||||
[[autodoc]] tuners.boft.config.BOFTConfig
|
||||
|
||||
## BOFTModel
|
||||
|
||||
[[autodoc]] tuners.boft.model.BOFTModel
|
||||
@@ -0,0 +1,55 @@
|
||||
<!--Copyright 2025 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# C3A: Parameter-Efficient Fine-Tuning via Circular Convolution
|
||||
|
||||
[C3A](https://huggingface.co/papers/2407.19342) is a parameter-efficient fine-tuning technique that leverages Circular Convolution to achieve high rank adaptation within reasonable resource limits.
|
||||
|
||||
Note that you should use a much larger learning rate (LR) for C3A than for other methods. For example, a LR of 1e-1 for C3A is a good starting point. Besides, a much smaller weight decay should be used. You can refer to the `method_comparison` folder for more details.
|
||||
|
||||
For the `block_size`, it affects tunable parameters and performance. To start with, you can choose a $\mathrm{gcd}(d_1,d_2)$ near $\frac{\sqrt{d_1 \times d_2}}{r}$, where $r$ is the rank for LoRA you would use for this task.
|
||||
|
||||
C3A currently has the following constraints:
|
||||
|
||||
- Only `nn.Linear` layers are supported.
|
||||
- Quantized layers are not supported.
|
||||
- The block size should be a common divisor of both the input and output sizes of target layers.
|
||||
|
||||
If these constraints don't work for your use case, consider other methods instead.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
> Low-Rank Adaptation (LoRA) has gained popularity for fine-tuning large foundation models, leveraging low-rank matrices $\mathbf{A}$ and $\mathbf{B}$ to represent weight changes (i.e., $\Delta \mathbf{W} = \mathbf{B} \mathbf{A}$). This method reduces trainable parameters and mitigates heavy memory consumption associated with full delta matrices by sequentially multiplying $\mathbf{A}$ and $\mathbf{B}$ with the activation. Despite its success, the intrinsic low-rank characteristic may limit its performance. Although several variants have been proposed to address this issue, they often overlook the crucial computational and memory efficiency brought by LoRA. In this paper, we propose Circular Convolution Adaptation (C3A), which not only achieves high-rank adaptation with enhanced performance but also excels in both computational power and memory utilization. Extensive experiments demonstrate that C3A consistently outperforms LoRA and its variants across various fine-tuning tasks.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=C3A"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
|
||||
# API
|
||||
|
||||
## C3AConfig
|
||||
|
||||
[[autodoc]] tuners.c3a.config.C3AConfig
|
||||
|
||||
## C3AModel
|
||||
|
||||
[[autodoc]] tuners.c3a.model.C3AModel
|
||||
@@ -0,0 +1,93 @@
|
||||
<!--Copyright 2025 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Cartridges
|
||||
|
||||
Cartridges are a prompt-learning method that stores a compressed long-context representation as a parameterized KV-cache
|
||||
prefix. The core idea comes from the paper
|
||||
[Cartridges: Lightweight and general-purpose long context representations via self-study](https://huggingface.co/papers/2506.06266).
|
||||
|
||||
For a high-level overview and motivation, see the blog post
|
||||
[Cartridges: Storing long contexts in tiny caches with self-study](https://hazyresearch.stanford.edu/blog/2025-06-08-cartridges).
|
||||
|
||||
## How Cartridges differ from Prefix Tuning
|
||||
|
||||
Both Prefix Tuning and Cartridges are served by injecting `past_key_values` (a prefix KV cache) into the base model.
|
||||
|
||||
- Prefix Tuning learns virtual token embeddings (and optionally an MLP projection) and produces a KV prefix.
|
||||
- Cartridges learn the KV prefix itself directly (the per-layer key/value vectors for `p` virtual tokens), and are
|
||||
designed to be initialized from real prefill KV (for example, the first `p` tokens of a corpus/system prompt).
|
||||
|
||||
The paper also recommends freezing the first token as an attention sink for stability (`num_frozen_tokens=1` is the
|
||||
default).
|
||||
|
||||
## Usage (inference)
|
||||
|
||||
Load a trained CARTRIDGE adapter and run generation:
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
|
||||
from peft import PeftModel
|
||||
|
||||
model_id = "Qwen/Qwen2.5-0.5B-Instruct"
|
||||
adapter_path = "path/to/cartridge_adapter"
|
||||
|
||||
base = AutoModelForCausalLM.from_pretrained(model_id)
|
||||
model = PeftModel.from_pretrained(base, adapter_path)
|
||||
|
||||
tok = AutoTokenizer.from_pretrained(model_id)
|
||||
if tok.pad_token is None:
|
||||
tok.pad_token = tok.eos_token
|
||||
|
||||
out = model.generate(**tok("Question about the corpus:", return_tensors="pt"), max_new_tokens=64)
|
||||
print(tok.decode(out[0], skip_special_tokens=True))
|
||||
```
|
||||
|
||||
If you need to create and initialize a cartridge before training, see the initialization options below.
|
||||
|
||||
## Initialization options
|
||||
|
||||
The paper discusses a few practical initialization strategies:
|
||||
|
||||
- Random KV (default): create a `CartridgeConfig` and start training. This initializes the KV prefix randomly.
|
||||
- KV from the first tokens of a prompt/corpus: use `initialize_kv_prefix_from_text(model, tokenizer, text=...)`. This
|
||||
runs a prefill on `text` and copies the resulting KV cache for the first `num_virtual_tokens` into the adapter.
|
||||
- KV from an existing cache: use `initialize_kv_prefix_from_past_key_values(model, past_key_values=...)` if you already
|
||||
have a `past_key_values` object from a base-model prefill.
|
||||
|
||||
## Training
|
||||
|
||||
The Cartridges paper proposes a SELF-STUDY distillation objective (a frozen base model provides teacher logits; the
|
||||
CARTRIDGE adapter is trained so the student matches the teacher’s next-token distribution over the target segment).
|
||||
PEFT keeps training logic out of the core library; see
|
||||
`https://github.com/huggingface/peft/tree/main/examples/cartridge_self_study` for a reference workflow.
|
||||
The example scripts use the frozen base model as the teacher and the adapted model as the student, so both share the
|
||||
same underlying checkpoint.
|
||||
|
||||
## Composition
|
||||
|
||||
To concatenate independently trained cartridges into a single adapter, use `compose_cartridge_adapters(...)`.
|
||||
|
||||
# API
|
||||
|
||||
## CartridgeConfig
|
||||
|
||||
[[autodoc]] tuners.cartridge.config.CartridgeConfig
|
||||
|
||||
## CartridgeEncoder
|
||||
|
||||
[[autodoc]] tuners.cartridge.model.CartridgeEncoder
|
||||
@@ -0,0 +1,22 @@
|
||||
<!--⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Configuration
|
||||
|
||||
[`PeftConfigMixin`] is the base configuration class for storing the adapter configuration of a [`PeftModel`], and [`PromptLearningConfig`] is the base configuration class for soft prompt methods (p-tuning, prefix tuning, and prompt tuning). These base classes contain methods for saving and loading model configurations from the Hub, specifying the PEFT method to use, type of task to perform, and model configurations like number of layers and number of attention heads.
|
||||
|
||||
## PeftConfigMixin
|
||||
|
||||
[[autodoc]] config.PeftConfigMixin
|
||||
- all
|
||||
|
||||
## PeftConfig
|
||||
|
||||
[[autodoc]] PeftConfig
|
||||
- all
|
||||
|
||||
## PromptLearningConfig
|
||||
|
||||
[[autodoc]] PromptLearningConfig
|
||||
- all
|
||||
@@ -0,0 +1,48 @@
|
||||
<!-- Copyright 2024 The HuggingFace Team. 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
|
||||
|
||||
|
||||
⚠️ Note that this file is in Markdown but contains specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Context-aware Prompt Tuning: Advancing In-Context Learning with Adversarial Methods
|
||||
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/cpt.png"/>
|
||||
</div>
|
||||
<small>CPT optimizing only specific token embeddings while keeping the rest of the model frozen <a href="https://huggingface.co/papers/2410.17222">(image source)</a>.</small>
|
||||
|
||||
[Context-Aware Prompt Tuning (CPT)](https://huggingface.co/papers/2410.17222) is designed to enhance few-shot classification by refining only context embeddings.
|
||||
This approach combines ideas from In-Context Learning (ICL), [Prompt Tuning](../package_reference/prompt_tuning) (PT), and adversarial optimization, focusing on making model adaptation both parameter-efficient and effective.
|
||||
In CPT, only specific context token embeddings are optimized, while the rest of the model remains frozen.
|
||||
To prevent overfitting and maintain stability, CPT uses controlled perturbations to limit the allowed changes to context embeddings within a defined range.
|
||||
Additionally, to address the phenomenon of recency bias—where examples near the end of the context tend to be prioritized over earlier ones—CPT applies a decay loss factor.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
> Large Language Models (LLMs) can perform few-shot learning using either optimization-based approaches or In-Context Learning (ICL). Optimization-based methods often suffer from overfitting, as they require updating a large number of parameters with limited data. In contrast, ICL avoids overfitting but typically underperforms compared to optimization-based methods and is highly sensitive to the selection, order, and format of demonstration examples. To overcome these challenges, we introduce Context-aware Prompt Tuning (CPT), a method inspired by ICL, Prompt Tuning (PT), and adversarial attacks. CPT builds on the ICL strategy of concatenating examples before the input, extending it by incorporating PT-like learning to refine the context embedding through iterative optimization, extracting deeper insights from the training examples. Our approach carefully modifies specific context tokens, considering the unique structure of the examples within the context. In addition to updating the context with PT-like optimization, CPT draws inspiration from adversarial attacks, adjusting the input based on the labels present in the context while preserving the inherent value of the user-provided data. To ensure robustness and stability during optimization, we employ a projected gradient descent algorithm, constraining token embeddings to remain close to their original values and safeguarding the quality of the context. Our method has demonstrated superior accuracy across multiple classification tasks using various LLM models, outperforming existing baselines and effectively addressing the overfitting challenge in few-shot learning.
|
||||
|
||||
Take a look at [Example](https://github.com/huggingface/peft/blob/main/examples/cpt_finetuning/README.md) for a step-by-step guide on how to train a model with CPT.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
There is no benchmark for this method yet. Feel free to contribute an experiment
|
||||
configuration but make sure to first create an issue
|
||||
[here](https://github.com/huggingface/peft/issues).
|
||||
|
||||
# API
|
||||
|
||||
## CPTConfig
|
||||
|
||||
[[autodoc]] tuners.cpt.config.CPTConfig
|
||||
|
||||
## CPTEmbedding
|
||||
|
||||
[[autodoc]] tuners.cpt.model.CPTEmbedding
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<!--Copyright 2026 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# DEFT: Decompositional Efficient Fine-Tuning for Text-to-Image Models
|
||||
|
||||
[DEFT](https://proceedings.neurips.cc/paper_files/paper/2025/hash/93a34a7138bdad95e874018d5f491cc6-Abstract-Conference.html)
|
||||
(Decompositional Efficient Fine-Tuning) is a parameter-efficient fine-tuning method for text-to-image models. It
|
||||
decomposes the update of a frozen weight matrix `W` into two trainable components: a projection that removes a low-rank
|
||||
subspace from `W`, and a low-rank update that injects new content into that subspace. This formulation is designed to
|
||||
balance aligning with a target distribution, learning new concepts from a few images (personalization), and preserving
|
||||
the pretrained model's instruction-following ability and editability.
|
||||
|
||||
Concretely, DEFT combines two trainable low-rank components: (1) a projection onto the complement of a low-rank
|
||||
subspace spanned by a low-rank matrix, and (2) a low-rank update. The first low-rank matrix defines the subspace, while
|
||||
the second enables flexible parameter adaptation within that subspace.
|
||||
|
||||
When to use DEFT: it is best suited to adapting a model to new data or concepts while **retaining and even improving the
|
||||
base model's instruction-following ability** and keeping **forgetting of its previous capabilities to a minimum**.
|
||||
|
||||
Per target layer, DEFT learns a projection direction `P` (shape `out_features x r`) and an injection matrix `R` (shape
|
||||
`r x in_features`). The effective weight is the residual projection
|
||||
|
||||
```
|
||||
W' = (I - P_proj) @ W + Q_P @ R
|
||||
```
|
||||
|
||||
The projector `P_proj` is derived from `P` according to `decomposition_method`:
|
||||
|
||||
- `"relu"` (default): `Q_P = P`, `P_proj = P @ relu(P).T` — a non-orthogonal projection.
|
||||
- `"qr"`: `Q_P = qr(P)`, `P_proj = Q_P @ Q_P.T` — an orthogonal projection.
|
||||
|
||||
The `(I - P_proj) @ W` term removes a sub-space of the pretrained weight while `Q_P @ R` injects new content into it.
|
||||
By default (`init_weights=True`) `R` is initialized so that the update is an exact identity at initialization
|
||||
(`W' == W`), so training starts from the pretrained weights and learns the injection. The update is equivalent to a
|
||||
low-rank additive delta `Q_P @ (R - right.T @ W)`, which is computed without ever forming the `out x out`
|
||||
projection matrix and can be merged into the base weights for inference-free deployment.
|
||||
|
||||
Setting `para=True` selects the
|
||||
[PaRa](https://proceedings.iclr.cc/paper_files/paper/2025/hash/f09e8dd9274cb7c2dd0dc65ffc6f427a-Abstract-Conference.html)
|
||||
(Parameter Rank Reduction) variant: a removal-only update `W' = (I - P_proj) @ W` that keeps just the subspace-removal
|
||||
term and drops the injection. Only the projection `P` is trained (no injection matrix `R`), so the adapter is not an
|
||||
identity at initialization. PaRa was introduced for personalizing text-to-image diffusion models and is available here
|
||||
as a special case of DEFT.
|
||||
|
||||
DEFT is currently implemented for `torch.nn.Linear` and `Conv1D` (e.g. gpt-2, via `fan_in_fan_out`) layers. The original implementation and the experiments from the
|
||||
paper (Dreambooth, Dreambench Plus, InsDet, VisualCloze, on Stable Diffusion and a unified model) are available at
|
||||
[github.com/MAXNORM8650/DEFT](https://github.com/MAXNORM8650/DEFT).
|
||||
|
||||
If you use DEFT in your work, please cite the paper:
|
||||
|
||||
```bibtex
|
||||
@article{kumar2026deft,
|
||||
title={DEFT: Decompositional Efficient Fine-Tuning for Text-to-Image Models},
|
||||
author={Kumar, Komal and Anwer, Rao and Shahbaz Khan, Fahad and Khan, Salman and Laptev, Ivan and Cholakkal, Hisham},
|
||||
journal={Advances in Neural Information Processing Systems},
|
||||
volume={38},
|
||||
pages={102009--102035},
|
||||
year={2026}
|
||||
}
|
||||
```
|
||||
|
||||
If you use the PaRa variant (`para=True`), please also cite:
|
||||
|
||||
```bibtex
|
||||
@inproceedings{chen2025personalizing,
|
||||
title={Para: Personalizing text-to-image diffusion via parameter rank reduction},
|
||||
author={Chen, Shangyu and Pan, Zizheng and Cai, Jianfei and Phung, Dinh},
|
||||
booktitle={International Conference on Learning Representations},
|
||||
year={2025}
|
||||
}
|
||||
```
|
||||
|
||||
## DeftConfig
|
||||
|
||||
[[autodoc]] tuners.deft.config.DeftConfig
|
||||
|
||||
## DeftModel
|
||||
|
||||
[[autodoc]] tuners.deft.model.DeftModel
|
||||
@@ -0,0 +1,51 @@
|
||||
<!--Copyright 2025 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# DeLoRA: Decoupled Low-rank Adaptation
|
||||
[DeLoRA](https://huggingface.co/papers/2503.18225) is a parameter-efficient fine-tuning technique that implicitly maintains a Frobenius boundary with respect to the pretrained weights by normalizing and scaling learnable low-rank matrices. This effectively decouples the learning of directions (BA term) and magnitude (boundary term) of the weight updates, avoiding catastrophic shifts in the adapted weights and enhancing robustness to hyperparameter choices.
|
||||
|
||||
Note:
|
||||
- use a learning rate 10-100x larger than for standard LoRA variants (typical values from 1e-3/1e-2/..)
|
||||
- ensure the initial boundary parameter lambda is not too small (typical values around 10/15/..). Setting different lambdas to different layers is possible
|
||||
|
||||
DeLoRA currently has the following constraints:
|
||||
- Only nn.Linear layers are supported.
|
||||
- Quantized layers are not supported.
|
||||
|
||||
If these constraints don't work for your use case, consider other methods instead.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
> Parameter-Efficient FineTuning (PEFT) methods have recently gained significant popularity thanks to the widespread availability of large-scale pretrained models. These methods allow for quick adaptation to downstream tasks with minimal computational cost. However, popular finetuning methods such as LoRA exhibit limited robustness when it comes to hyperparameter choices or extended training regimes, preventing optimal out-of-the-box performance. In contrast, bounded approaches, such as ETHER, provide greater robustness but are limited to extremely low-rank adaptations and fixed-strength transformations, reducing their adaptation expressive power. In this work, we propose Decoupled Low-rank Adaptation (DeLoRA), a novel finetuning method that normalizes and scales learnable low-rank matrices. By bounding the distance of the transformation, DeLoRA effectively decouples the angular learning from the adaptation strength, enhancing robustness without compromising performance. Through evaluations on subject-driven image generation, natural language understanding, and instruction tuning, we show that DeLoRA matches or surpasses performance of competing PEFT methods, while exhibiting stronger robustness.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=DELORA"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## DeloraConfig
|
||||
|
||||
[[autodoc]] tuners.delora.config.DeloraConfig
|
||||
|
||||
## DeloraModel
|
||||
|
||||
[[autodoc]] tuners.delora.model.DeloraModel
|
||||
@@ -0,0 +1,49 @@
|
||||
<!--Copyright 2024 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# FourierFT: Discrete Fourier Transformation Fine-Tuning
|
||||
|
||||
[FourierFT](https://huggingface.co/papers/2405.03003) is a parameter-efficient fine-tuning technique that leverages Discrete Fourier Transform to compress the model's tunable weights. This method outperforms LoRA in the GLUE benchmark and common ViT classification tasks using much less parameters.
|
||||
|
||||
FourierFT currently has the following constraints:
|
||||
|
||||
- Only `nn.Linear` layers are supported.
|
||||
- Quantized layers are not supported.
|
||||
|
||||
If these constraints don't work for your use case, consider other methods instead.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
> Low-rank adaptation (LoRA) has recently gained much interest in fine-tuning foundation models. It effectively reduces the number of trainable parameters by incorporating low-rank matrices A and B to represent the weight change, i.e., Delta W=BA. Despite LoRA's progress, it faces storage challenges when handling extensive customization adaptations or larger base models. In this work, we aim to further compress trainable parameters by enjoying the powerful expressiveness of the Fourier transform. Specifically, we introduce FourierFT, which treats Delta W as a matrix in the spatial domain and learns only a small fraction of its spectral coefficients. With the trained spectral coefficients, we implement the inverse discrete Fourier transform to recover Delta W. Empirically, our FourierFT method shows comparable or better performance with fewer parameters than LoRA on various tasks, including natural language understanding, natural language generation, instruction tuning, and image classification. For example, when performing instruction tuning on the LLaMA2-7B model, FourierFT surpasses LoRA with only 0.064M trainable parameters, compared to LoRA's 33.5M.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=FOURIERFT"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## FourierFTConfig
|
||||
|
||||
[[autodoc]] tuners.fourierft.config.FourierFTConfig
|
||||
|
||||
## FourierFTModel
|
||||
|
||||
[[autodoc]] tuners.fourierft.model.FourierFTModel
|
||||
@@ -0,0 +1,75 @@
|
||||
<!--Copyright 2026 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# FRoD: Full-Rank Efficient Fine-Tuning with Rotational Degrees
|
||||
|
||||
FRoD is a parameter-efficient fine-tuning method that combines a shared full-rank basis with sparse learnable
|
||||
rotational degrees. The adapter update is expressed through fixed projection tensors and trainable coefficients, which
|
||||
allows FRoD to apply full-rank updates while keeping the number of trained parameters small.
|
||||
|
||||
Paper: [Full-Rank Efficient Fine-Tuning with Rotational Degrees](https://doi.org/10.1609/aaai.v40i31.39813).
|
||||
|
||||
When saving the adapter parameters, it is possible to avoid storing the projection tensors by setting
|
||||
`save_projection=False` on the `FrodConfig`. In that case, the projections are restored from the base model weights and
|
||||
the fixed random seed from `projection_prng_key`. This reduces checkpoint size, but the default is
|
||||
`save_projection=True` to make checkpoint loading independent of regeneration details.
|
||||
|
||||
Compared to LoRA, FRoD can express a full-rank update in each adapted linear layer while training only the diagonal
|
||||
coefficients and a sparse set of off-diagonal rotation coefficients. This can be useful when a low-rank update is too
|
||||
restrictive. The trade-off is that FRoD computes fixed projection tensors from the base weights during adapter
|
||||
injection, which makes setup more expensive and the implementation less broadly supported than LoRA.
|
||||
|
||||
Projection initialization can be slow on large models because FRoD runs matrix decompositions over the target module
|
||||
categories before injecting the adapters. A progress bar is shown by default and can be disabled with
|
||||
`FrodConfig(progressbar=False)`.
|
||||
|
||||
For memory-constrained training, `runtime_offload_base_weight=True` keeps target base weights on CPU when the active
|
||||
FRoD path does not need them. This is opt-in because PEFT methods usually keep all base parameters on the accelerator
|
||||
after moving the model and after forward passes.
|
||||
|
||||
FRoD currently has the following constraint:
|
||||
|
||||
- Only `nn.Linear` and `transformers.pytorch_utils.Conv1D` layers are supported.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```python
|
||||
from transformers import AutoModelForSequenceClassification
|
||||
|
||||
from peft import FrodConfig, TaskType, get_peft_model
|
||||
|
||||
model = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-uncased", num_labels=2)
|
||||
|
||||
peft_config = FrodConfig(
|
||||
task_type=TaskType.SEQ_CLS,
|
||||
target_modules=["query", "value"],
|
||||
modules_to_save=["classifier"],
|
||||
sparse_rate=0.02,
|
||||
frod_dropout=0.0,
|
||||
runtime_offload_base_weight=True,
|
||||
)
|
||||
|
||||
model = get_peft_model(model, peft_config)
|
||||
model.print_trainable_parameters()
|
||||
```
|
||||
|
||||
## FrodConfig
|
||||
|
||||
[[autodoc]] tuners.frod.config.FrodConfig
|
||||
|
||||
## FrodModel
|
||||
|
||||
[[autodoc]] tuners.frod.model.FrodModel
|
||||
@@ -0,0 +1,37 @@
|
||||
<!--⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Functions for PEFT integration
|
||||
|
||||
A collection of functions that could be useful for non-PeftModel models, e.g. transformers or diffusers integration
|
||||
|
||||
The functions provided here can be considered "public API" of PEFT and hence are safe to be used by packages that provide PEFT integrations.
|
||||
|
||||
## Cast the adapter weight dtypes
|
||||
[[autodoc]] functional.cast_adapter_dtype
|
||||
- all
|
||||
|
||||
## Delete the PEFT adapter from model
|
||||
[[autodoc]] functional.delete_adapter
|
||||
- all
|
||||
|
||||
## Get the state dict of the PEFT adapter
|
||||
[[autodoc]] functional.get_peft_model_state_dict
|
||||
- all
|
||||
|
||||
## Inject a PEFT adapter into the model based on a PEFT config
|
||||
[[autodoc]] functional.inject_adapter_in_model
|
||||
- all
|
||||
|
||||
## Set the active PEFT adapter(s) of the model
|
||||
[[autodoc]] functional.set_adapter
|
||||
- all
|
||||
|
||||
## Set the `requires_grad` attribute of the specified adapters
|
||||
[[autodoc]] functional.set_requires_grad
|
||||
- all
|
||||
|
||||
## Load the weights of the PEFT state dict into the model
|
||||
[[autodoc]] functional.set_peft_model_state_dict
|
||||
- all
|
||||
@@ -0,0 +1,108 @@
|
||||
<!--Copyright 2026 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contains specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# GLoRA
|
||||
|
||||
Generalized Low-Rank Adaptation ([GLoRA](https://huggingface.co/papers/2306.07967)) is a PEFT method that generalizes LoRA and related approaches. GLoRA decomposes updates into configurable paths (A, B, C, D, E), where each path can use low-rank, vector, constant, or disabled parameterization depending on the path.
|
||||
|
||||
Each path supports one of four parameterization modes. They trade off **parameter count** against **expressiveness** (how rich the update can be):
|
||||
|
||||
- `"lora"`: Low-rank decomposition (like standard LoRA). Uses `r * (out + in)` parameters and can express rank-`r` corrections. Most expressive, most parameters.
|
||||
- `"vector"`: A single vector (e.g. shape `(out, 1)`), broadcast across the matrix. Uses `O(out)` parameters; only per-channel scaling or shifts.
|
||||
- `"constant"`: A single scalar shared across all elements. Uses 1 parameter; least expressive among the trainable options.
|
||||
- `"none"`: Zeros with no trainable parameters; disables that path entirely.
|
||||
|
||||
Not every path accepts every mode (for example, `config_D_E` does not support `"lora"`). Choosing `"lora"` on more paths increases capacity and trainable parameters; `"vector"`, `"constant"`, or `"none"` reduce both.
|
||||
|
||||
GLoRA is especially useful for research and advanced applications where you want to experiment with structured update patterns and combine multiple adaptation mechanisms in a single layer.
|
||||
|
||||
At a high level, GLoRA modifies a frozen linear layer with:
|
||||
|
||||
$$
|
||||
W_{\mathrm{eff}} = W_0 + W_0 \odot A + B
|
||||
$$
|
||||
|
||||
$$
|
||||
b_{\mathrm{eff}} = b_0 + b_0 \odot D + E + W_0 C
|
||||
$$
|
||||
|
||||
where each path is independently parameterized.
|
||||
|
||||
## GloraConfig
|
||||
|
||||
[[autodoc]] tuners.glora.config.GloraConfig
|
||||
|
||||
### Key Configuration Options
|
||||
- `r`: Rank used when a path is configured as `"lora"` (default: `8`).
|
||||
- `target_modules`: List or regex of module names to adapt (e.g., `["q_proj", "v_proj"]`).
|
||||
- `config_A_B`: Path type for A and B ("lora", "vector", "constant", "none").
|
||||
- `config_C`: Path type for C ("lora", "vector", "none").
|
||||
- `config_D_E`: Path type for D and E ("constant", "vector", "none").
|
||||
- `bias`: Bias handling (`"none"`, `"all"`, or `"glora_only"`).
|
||||
- `init_weights`: If `True` (default), GLoRA is initialized as a no-op. If `False`, uses kaiming initialization.
|
||||
|
||||
Notes:
|
||||
- `config_D_E` does not support `"lora"`.
|
||||
- `target_modules` can be omitted for supported model types (PEFT default mappings are used).
|
||||
|
||||
## GloraModel
|
||||
|
||||
[[autodoc]] tuners.glora.model.GloraModel
|
||||
|
||||
- Wraps a base model and injects GLoRA adapters into the specified modules.
|
||||
- Supports multiple adapters, adapter switching, merging/unmerging, and mixed-batch inference.
|
||||
- Use `set_adapter`, `merge_and_unload`, and related methods for adapter management.
|
||||
|
||||
## GloraLayer and GloraLinear
|
||||
|
||||
[[autodoc]] tuners.glora.layer.GloraLayer
|
||||
[[autodoc]] tuners.glora.layer.GloraLinear
|
||||
|
||||
- `GloraLayer` is the core logic for generalized low-rank adaptation, supporting multiple adapters and flexible path configs.
|
||||
- `GloraLinear` is a drop-in replacement for `nn.Linear` with GLoRA support.
|
||||
- GLoRA currently supports plain `torch.nn.Linear` base layers.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
from transformers import AutoModelForCausalLM
|
||||
from peft import GloraConfig, get_peft_model
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained("your-model-id")
|
||||
glora_config = GloraConfig(
|
||||
r=8,
|
||||
target_modules=["q_proj", "v_proj"],
|
||||
config_A_B="lora",
|
||||
config_C="vector",
|
||||
config_D_E="constant",
|
||||
task_type="CAUSAL_LM",
|
||||
)
|
||||
model = get_peft_model(model, glora_config)
|
||||
model.print_trainable_parameters()
|
||||
|
||||
# Switch adapters, merge, etc.
|
||||
model.set_adapter("default")
|
||||
model.merge_and_unload()
|
||||
```
|
||||
|
||||
## Notes
|
||||
- GLoRA is a superset of LoRA: setting all paths to "lora" recovers standard LoRA.
|
||||
- You can use different path types for A/B/C/D/E to experiment with new adaptation strategies.
|
||||
- GLoRA supports all standard PEFT adapter management features (add, delete, switch, merge, etc).
|
||||
|
||||
## See Also
|
||||
- [Adapter conceptual guide](../conceptual_guides/adapter.md)
|
||||
- [LoRA reference](./lora.md)
|
||||
- [Paper: https://huggingface.co/papers/2306.07967](https://huggingface.co/papers/2306.07967)
|
||||
@@ -0,0 +1,50 @@
|
||||
# GraLoRA
|
||||
|
||||
[**Granular Low-Rank Adaptation (GraLoRA)**](https://huggingface.co/papers/2505.20355) is a PEFT method designed to enhance the **expressivity** of low-rank adaptation while improving **robustness to outlier** activations, based on insights from well-known issues in quantization.
|
||||
|
||||

|
||||
|
||||
Unlike standard LoRA, which applies a single low-rank adapter across the entire feature space, GraLoRA introduces a structured and fine-grained adaptation scheme. It divides the adaptation space into a grid of $𝑘^2$ smaller, independent adapter pairs, each responsible for a localized subset of the input and output dimensions. As a result, each adapter operates on a subspace that is $k$ times smaller in both dimensions than the original LoRA adapter.
|
||||
|
||||
This granular decomposition enables spatially localized and context-aware updates, effectively increasing representational capacity without additional parameters or computational cost. By isolating the influence of extreme activations within smaller subspaces, GraLoRA mitigates gradient distortion and preserves inter-channel balance during adaptation.
|
||||
|
||||
---
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Low-Rank Adaptation (LoRA) is a popular method for parameter-efficient fine-
|
||||
tuning (PEFT) of generative models, valued for its simplicity and effectiveness.
|
||||
Despite recent enhancements, LoRA still suffers from a fundamental limitation:
|
||||
overfitting when the bottleneck is widened. It performs best at ranks 32–64, yet its
|
||||
accuracy stagnates or declines at higher ranks, still falling short of full fine-tuning
|
||||
(FFT) performance. We identify the root cause as LoRA’s structural bottleneck,
|
||||
which introduces gradient entanglement to the unrelated input channels and distorts
|
||||
gradient propagation. To address this, we introduce a novel structure, Granular
|
||||
Low-Rank Adaptation (GraLoRA) that partitions weight matrices into sub-blocks,
|
||||
each with its own low-rank adapter. With negligible computational or storage cost,
|
||||
GraLoRA overcomes LoRA’s limitations, effectively increases the representational
|
||||
capacity, and more closely approximates FFT behavior. Experiments on code
|
||||
generation, commonsense reasoning, mathematical reasoning, general language
|
||||
understanding, and image generation benchmarks show that GraLoRA consistently
|
||||
outperforms LoRA and other baselines, achieving up to +8.5% absolute gain in
|
||||
Pass@1 on HumanEval+. These improvements hold across model sizes and rank
|
||||
settings, making GraLoRA a scalable and robust solution for PEFT.*
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=GRALORA"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## GraloraConfig
|
||||
|
||||
[[autodoc]] tuners.gralora.config.GraloraConfig
|
||||
|
||||
## GraloraModel
|
||||
|
||||
[[autodoc]] tuners.gralora.model.GraloraModel
|
||||
@@ -0,0 +1,41 @@
|
||||
<!--⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Helper methods
|
||||
|
||||
A collection of helper functions for PEFT.
|
||||
|
||||
## Checking if a model is a PEFT model
|
||||
|
||||
[[autodoc]] helpers.check_if_peft_model
|
||||
- all
|
||||
|
||||
## Temporarily Rescaling Adapter Scale in LoraLayer Modules
|
||||
|
||||
[[autodoc]] helpers.rescale_adapter_scale
|
||||
- all
|
||||
|
||||
## Context manager to disable input dtype casting in the `forward` method of LoRA layers
|
||||
|
||||
[[autodoc]] helpers.disable_input_dtype_casting
|
||||
- all
|
||||
|
||||
## Context manager to enable DoRA caching (faster at inference time but requires more memory)
|
||||
|
||||
[[autodoc]] helpers.DoraCaching
|
||||
- all
|
||||
|
||||
## KappaTune target selection
|
||||
|
||||
`KappaTuneSelector` and `find_kappa_target_modules` implement a general target selection process from the [KappaTune paper](https://arxiv.org/abs/2506.16289).
|
||||
|
||||
The method identifies modules with higher flexibility (higher output differential entropy) and lower specialization (lower sensitivity to specific input directions).
|
||||
|
||||
These properties make the selected modules good candidates for mitigating catastrophic forgetting in any adaptation method that adds trainable parameters, including LoRA, DoRA, LoHa, AdaLoRA, and even direct fine-tuning of the original weights.
|
||||
|
||||
[[autodoc]] helpers.KappaTuneSelector
|
||||
- all
|
||||
|
||||
[[autodoc]] helpers.find_kappa_target_modules
|
||||
- all
|
||||
@@ -0,0 +1,89 @@
|
||||
# HiRA
|
||||
|
||||
High-Rank Adaptation ([HiRA](https://openreview.net/pdf?id=TwJrTz9cRS)) is a PEFT method that extends the LoRA approach by applying an element-wise modulation on the original weight matrix. Instead of adding a low-rank update directly, HiRA computes:
|
||||
|
||||
$$
|
||||
W' = W_0 + W_0 \odot (B A)
|
||||
$$
|
||||
|
||||
where $W_0$ is the base weight, and $A, B$ are low-rank factors with rank $r \ll \min( \text{in_features}, \text{out_features})$. This formulation allows HiRA to adapt existing weights with a multiplicative, input-dependent modulation, often improving fine-tuning efficiency on downstream tasks.
|
||||
|
||||
The abstract from the HiRA paper is:
|
||||
|
||||
> *We propose Hadamard High-Rank Adaptation (HiRA), a parameter-efficient fine-tuning (PEFT) method that enhances the adaptability of Large Language Models (LLMs). While Low-rank Adaptation (LoRA) is widely used to reduce resource demands, its low-rank updates may limit its expressiveness for new tasks. HiRA addresses this by using a Hadamard product to retain high-rank update parameters, improving the model capacity. Empirically, HiRA outperforms LoRA and its variants on several tasks, with extensive ablation studies validating its effectiveness.*
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
from peft import get_peft_model
|
||||
from peft.tuners.hira import HiraConfig
|
||||
|
||||
# Example 1: HiRA on opt-125m for causal language modeling
|
||||
model_id = "facebook/opt-125m"
|
||||
base_model = AutoModelForCausalLM.from_pretrained(model_id)
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||||
|
||||
# Define HiRA configuration: apply to the MLP dense layers in each transformer block
|
||||
hira_config = HiraConfig(
|
||||
r=32,
|
||||
target_modules=["k_proj", "q_proj", "v_proj", "fc1", "fc2"],
|
||||
hira_dropout=0.0,
|
||||
init_weights=True,
|
||||
)
|
||||
peft_model = get_peft_model(base_model, hira_config)
|
||||
|
||||
peft_model.print_trainable_parameters()
|
||||
# trainable params: 4,718,592 || all params: 129,957,888 || trainable%: 3.6309
|
||||
```
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=HIRA"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
## Citation:
|
||||
|
||||
If you found HiRA is useful, please cite HiRA as:
|
||||
```
|
||||
@inproceedings{
|
||||
huang2025hira,
|
||||
title={Hi{RA}: Parameter-Efficient Hadamard High-Rank Adaptation for Large Language Models},
|
||||
author={Qiushi Huang and Tom Ko and Zhan Zhuang and Lilian Tang and Yu Zhang},
|
||||
booktitle={The Thirteenth International Conference on Learning Representations},
|
||||
year={2025},
|
||||
url={https://openreview.net/forum?id=TwJrTz9cRS}
|
||||
}
|
||||
```
|
||||
|
||||
# API
|
||||
|
||||
## HiraConfig
|
||||
|
||||
[[autodoc]] tuners.hira.config.HiraConfig
|
||||
|
||||
## Core Layers
|
||||
|
||||
### HiraLayer
|
||||
|
||||
[[autodoc]] tuners.hira.layer.HiraLayer
|
||||
|
||||
### Linear Adapter
|
||||
|
||||
[[autodoc]] tuners.hira.layer.Linear
|
||||
|
||||
### Embedding Adapter
|
||||
|
||||
[[autodoc]] tuners.hira.layer.Embedding
|
||||
|
||||
### Convolutional Adapters
|
||||
|
||||
[[autodoc]] tuners.hira.layer.Conv1d [[autodoc]] tuners.hira.layer.Conv2d [[autodoc]] tuners.hira.layer.ConvNd
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<!--⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Hotswapping adapters
|
||||
|
||||
The idea of hotswapping an adapter is the following: We can already load multiple adapters, e.g. two LoRAs, at the same time. But sometimes, we want to load one LoRA and then replace its weights in-place with the LoRA weights of another adapter. This is now possible the `hotswap_adapter` function.
|
||||
|
||||
In general, this should be faster than deleting one adapter and loading the adapter in its place, which would be the how to achieve the same final outcome without hotswapping. Another advantage of hotswapping is that it prevents re-compilation in case the PEFT model is already compiled using `torch.compile`. This can save quite a lot of time.
|
||||
|
||||
## Example without `torch.compile`
|
||||
|
||||
```python
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM
|
||||
from peft import PeftModel
|
||||
from peft.utils.hotswap import hotswap_adapter
|
||||
|
||||
model_id = ...
|
||||
inputs = ...
|
||||
device = ...
|
||||
model = AutoModelForCausalLM.from_pretrained(model_id).to(device)
|
||||
|
||||
# load lora 0
|
||||
model = PeftModel.from_pretrained(model, <path-adapter-0>)
|
||||
with torch.inference_mode():
|
||||
output_adapter_0 = model(inputs)
|
||||
|
||||
# replace the "default" lora adapter with the new one
|
||||
hotswap_adapter(model, <path-adapter-1>, adapter_name="default", torch_device=device)
|
||||
with torch.inference_mode():
|
||||
output_adapter_1 = model(inputs).logits
|
||||
```
|
||||
|
||||
## Example with `torch.compile`
|
||||
|
||||
```python
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM
|
||||
from peft import PeftModel
|
||||
from peft.utils.hotswap import hotswap_adapter, prepare_model_for_compiled_hotswap
|
||||
|
||||
model_id = ...
|
||||
inputs = ...
|
||||
device = ...
|
||||
max_rank = ... # maximum rank among all LoRA adapters that will be used
|
||||
model = AutoModelForCausalLM.from_pretrained(model_id).to(device)
|
||||
|
||||
# load lora 0
|
||||
model = PeftModel.from_pretrained(model, <path-adapter-0>)
|
||||
# Prepare the model to allow hotswapping even if ranks/scalings of 2nd adapter differ.
|
||||
# You can skip this step if all ranks and scalings are identical.
|
||||
prepare_model_for_compiled_hotswap(model, target_rank=max_rank)
|
||||
model = torch.compile(model)
|
||||
with torch.inference_mode():
|
||||
output_adapter_0 = model(inputs)
|
||||
|
||||
# replace the "default" lora adapter with the new one
|
||||
hotswap_adapter(model, <path-adapter-1>, adapter_name="default", torch_device=device)
|
||||
with torch.inference_mode():
|
||||
output_adapter_1 = model(inputs).logits
|
||||
```
|
||||
|
||||
Note that if you want to hotswap weights that were added through `target_parameters`, i.e. that directly target an `nn.Parameter`, re-compilation and/or graph breaks cannot be prevented. Therefore, it is recommended to avoid using `target_parameters` together with compiled models and hotswapping.
|
||||
|
||||
## Caveats
|
||||
|
||||
Hotswapping works with transformers models and diffusers models. However, there are some caveats:
|
||||
|
||||
- Right now, only LoRA is properly supported.
|
||||
- It only works for the same PEFT method, so no swapping LoRA and LoHa, for example.
|
||||
- The adapter that is being swapped in must target the same layers as the previous adapter or a subset of those layers. It cannot target new layers. Therefore, if possible, start with the adapter that targets most layers.
|
||||
|
||||
## API
|
||||
|
||||
[[autodoc]] utils.hotswap.hotswap_adapter
|
||||
- all
|
||||
|
||||
[[autodoc]] utils.hotswap.hotswap_adapter_from_state_dict
|
||||
- all
|
||||
@@ -0,0 +1,40 @@
|
||||
<!--Copyright 2024 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Bridging The Gap between Low-rank and Orthogonal Adaptation via Householder Reflection Adaptation (HRA)
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/hra.png"/>
|
||||
</div>
|
||||
<small><a href="https://huggingface.co/papers/2405.17484">Bridging The Gap between Low-rank and Orthogonal Adaptation via Householder Reflection Adaptation</a></small>
|
||||
|
||||
[HRA](https://huggingface.co/papers/2405.17484) provides a new perspective connecting LoRA to OFT, which means it can harness the advantages of both strategies, by leveraging [Householder reflections](https://en.wikipedia.org/wiki/Householder_transformation) to reduce parameters and computation costs while penalizing the loss of pre-training knowledge. It consistently achieves better performance with fewer trainable parameters and outperforms state-of-the-art adapters across different models, including large language models (LLMs) and conditional image generators.
|
||||
|
||||
HRA constructs a chain of `r` trainable Householder reflections (HRs). Because the Householder reflection matrix is an orthogonal matrix and the product of orthogonal matrices is also an orthogonal matrix, HRA satisfies the theoretical guarantee of Orthogonal Finetuning (OFT). Meanwhile, HRA can also be viewed as a low-rank fine-tuning adapter. The higher `r`, the more trainable parameters, resulting in a larger model capacity and better performance. Besides, due to the chain structure, the orthogonality of HR planes impacts the capacity and regularity of HRA. To achieve a trade-off between the model capacity and regularity, an orthogonality regularizer of the HR planes is added to the loss function. The weight \\(\lambda\\) can control the strength of the regularizer.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
> While following different technical routes, both low-rank and orthogonal adaptation techniques can efficiently adapt large-scale pre-training models in specific tasks or domains based on a small piece of trainable parameters. In this study, we bridge the gap between these two techniques, proposing a simple but effective adaptation method based on Householder reflections. Given a pre-trained model, our method fine-tunes its layers by multiplying each frozen weight matrix with an orthogonal matrix constructed by a chain of learnable Householder reflections (HRs). This HR-based orthogonal fine-tuning is equivalent to an adaptive low-rank adaptation. Moreover, we show that the orthogonality of the reflection planes corresponding to the HRs impacts the model capacity and regularity. The analysis motivates us to regularize the orthogonality of the HRs, leading to different implementations of the proposed Householder reflection adaptation (HRA) method. Compared with state-of-the-art methods, HRA achieves superior performance with fewer learnable parameters when adapting large language models and conditional image generators. The code is available at [peft](https://github.com/huggingface/peft/tree/main/src/peft/tuners/hra) and [HRA](https://github.com/DaShenZi721/HRA).
|
||||
|
||||
# API
|
||||
|
||||
## HRAConfig
|
||||
|
||||
[[autodoc]] tuners.hra.config.HRAConfig
|
||||
|
||||
## HRAModel
|
||||
|
||||
[[autodoc]] tuners.hra.model.HRAModel
|
||||
@@ -0,0 +1,79 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# IA3
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/ia3.png"/>
|
||||
</div>
|
||||
<small>IA3 introduces three vectors, lv, lk and lff to scale value, key and feed-forward activations <a href="https://hf.co/papers/2205.05638">(image source)</a>.</small>
|
||||
|
||||
Infused Adapter by Inhibiting and Amplifying Inner Activations, or [IA3](https://hf.co/papers/2205.05638), is a method that adds three learned vectors to rescale the keys and values of the self-attention and encoder-decoder attention layers, and the intermediate activation of the position-wise feed-forward network.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Few-shot in-context learning (ICL) enables pre-trained language models to perform a previously-unseen task without any gradient-based training by feeding a small number of training examples as part of the input. ICL incurs substantial computational, memory, and storage costs because it involves processing all of the training examples every time a prediction is made. Parameter-efficient fine-tuning (PEFT) (e.g. adapter modules, prompt tuning, sparse update methods, etc.) offers an alternative paradigm where a small set of parameters are trained to enable a model to perform the new task. In this paper, we rigorously compare few-shot ICL and PEFT and demonstrate that the latter offers better accuracy as well as dramatically lower computational costs. Along the way, we introduce a new PEFT method called (IA)^3 that scales activations by learned vectors, attaining stronger performance while only introducing a relatively tiny amount of new parameters. We also propose a simple recipe based on the T0 model called T-Few that can be applied to new tasks without task-specific tuning or modifications. We validate the effectiveness of T-Few on completely unseen tasks by applying it to the RAFT benchmark, attaining super-human performance for the first time and outperforming the state-of-the-art by 6% absolute. All of the code used in our experiments is publicly available*.
|
||||
|
||||
To make fine-tuning more efficient, IA3 (Infused Adapter by Inhibiting and Amplifying Inner Activations)
|
||||
rescales inner activations with learned vectors. These learned vectors are injected in the attention and feedforward modules
|
||||
in a typical transformer-based architecture. These learned vectors are the only trainable parameters during fine-tuning, and thus the original
|
||||
weights remain frozen. Dealing with learned vectors (as opposed to learned low-rank updates to a weight matrix like LoRA)
|
||||
keeps the number of trainable parameters much smaller.
|
||||
|
||||
Being similar to [LoRA](./lora), IA3 carries many of the same advantages:
|
||||
|
||||
* IA3 makes fine-tuning more efficient by drastically reducing the number of trainable parameters. (For T0, an IA3 model only has about 0.01% trainable parameters, while even LoRA has > 0.1%)
|
||||
* The original pre-trained weights are kept frozen, which means you can have multiple lightweight and portable IA3 models for various downstream tasks built on top of them.
|
||||
* Performance of models fine-tuned using IA3 is comparable to the performance of fully fine-tuned models.
|
||||
* IA3 does not add any inference latency because adapter weights can be merged with the base model.
|
||||
|
||||
In principle, IA3 can be applied to any subset of weight matrices in a neural network to reduce the number of trainable
|
||||
parameters. Following the authors' implementation, IA3 weights are added to the key, value and feedforward layers
|
||||
of a Transformer model. To be specific, for transformer models, IA3 weights are added to the outputs of key and value layers, and to the input of the second feedforward layer
|
||||
in each transformer block.
|
||||
|
||||
Given the target layers for injecting IA3 parameters, the number of trainable parameters
|
||||
can be determined based on the size of the weight matrices.
|
||||
|
||||
## Usage
|
||||
|
||||
For the task of sequence classification, one can initialize the IA3 config for a Llama model as follows:
|
||||
|
||||
```py
|
||||
peft_config = IA3Config(
|
||||
task_type=TaskType.SEQ_CLS, target_modules=["k_proj", "v_proj", "down_proj"], feedforward_modules=["down_proj"]
|
||||
)
|
||||
```
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=IA3"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
|
||||
# API
|
||||
|
||||
## IA3Config
|
||||
|
||||
[[autodoc]] tuners.ia3.config.IA3Config
|
||||
|
||||
## IA3Model
|
||||
|
||||
[[autodoc]] tuners.ia3.model.IA3Model
|
||||
@@ -0,0 +1,45 @@
|
||||
<!--Copyright 2024 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# LayerNorm Tuning
|
||||
|
||||
LayerNorm Tuning ([LN Tuning](https://huggingface.co/papers/2312.11420)) is a PEFT method that only fine-tunes the parameters of the LayerNorm layers in a model.
|
||||
The paper has tested the performance of this method on large language models and has shown that it can achieve strong performance with a significant reduction in the number of trainable parameters and GPU memory usage.
|
||||
However, the method is not limited to language models and can be applied to any model that uses LayerNorm layers.
|
||||
In this implementation, the default is that all layernorm layers inside a model is finetuned, but it could be used to target other layer types such as `MLP` or `Attention` layers, this can be done by specifying the `target_modules` in the `LNTuningConfig`.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*This paper introduces an efficient strategy to transform Large Language Models (LLMs) into Multi-Modal Large Language Models (MLLMs). By conceptualizing this transformation as a domain adaptation process, i.e., transitioning from text understanding to embracing multiple modalities, we intriguingly note that, within each attention block, tuning LayerNorm suffices to yield strong performance. Moreover, when benchmarked against other tuning approaches like full parameter finetuning or LoRA, its benefits on efficiency are substantial. For example, when compared to LoRA on a 13B model scale, performance can be enhanced by an average of over 20% across five multi-modal tasks, and meanwhile, results in a significant reduction of trainable parameters by 41.9% and a decrease in GPU memory usage by 17.6%. On top of this LayerNorm strategy, we showcase that selectively tuning only with conversational data can improve efficiency further. Beyond these empirical outcomes, we provide a comprehensive analysis to explore the role of LayerNorm in adapting LLMs to the multi-modal domain and improving the expressive power of the model.*
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=LN_TUNING"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## LNTuningConfig
|
||||
|
||||
[[autodoc]] tuners.ln_tuning.config.LNTuningConfig
|
||||
|
||||
## LNTuningModel
|
||||
|
||||
[[autodoc]] tuners.ln_tuning.model.LNTuningModel
|
||||
@@ -0,0 +1,52 @@
|
||||
<!--Copyright 2026 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Lily: Low-Rank Interconnected Adaptation across Layers
|
||||
|
||||
[Lily](https://huggingface.co/papers/2407.09946) is a parameter-efficient fine-tuning technique that introduces cross-layer weight sharing for adapter matrices. Instead of learning an independent AB pair per layer as in LoRA, Lily uses **locally shared A adapters** (each A is shared across a block of `stride_A` consecutive layers) and **globally shared B experts** (a small pool of `num_B` B adapters is shared across all layers). At each forward pass, a lightweight data-dependent router computes a softmax-weighted combination of the B experts to produce the effective B for that layer and input.
|
||||
|
||||
This sharing can reduce the total number of adapter matrices from `2N` (standard LoRA) to `N / stride_A + num_B`, freeing up the parameter budget to use a **much larger rank `r`** — typically `2×`–`4×` what you would use in LoRA. Higher rank and better interconnectivity increase the effective rank of the weight update `ΔW = A × combined_B`, leading to better adaptation performance.
|
||||
|
||||
Because the B combination is **data-dependent** (the router weights depend on the input activations at runtime), `merge` and `unmerge` are **not supported**. If weight merging is required for your deployment, consider other methods such as LoRA instead.
|
||||
|
||||
Lily currently has the following additional constraints:
|
||||
- Only `nn.Linear` layers are supported.
|
||||
- Quantized layers are not supported.
|
||||
|
||||
If these constraints don't work for your use case, consider other methods instead.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
> Low-rank adaptation (LoRA) is a widely used parameter-efficient fine-tuning (PEFT) method that learns weight updates ΔW = AB for pretrained weights W through low-rank adapters A and B. While LoRA ensures hardware efficiency, its low-rank weight updates limit adaptation performance. In this paper, we propose low-rank interconnected adaptation across layers (Lily), a novel PEFT method that introduces an interconnected framework with locally shared A and globally shared B experts. This structure eliminates redundant per-layer AB pairs, enabling higher-rank ΔW with equal or fewer parameters. To enhance expressiveness, we use data-dependent routers to determine A-B interconnections, preventing B experts from converging to the same behavior and improving representational power across domains. Experiments across modalities, architectures, and model sizes demonstrate Lily's superior performance and efficiency.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=LILY"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## LilyConfig
|
||||
|
||||
[[autodoc]] tuners.lily.config.LilyConfig
|
||||
|
||||
## LilyModel
|
||||
|
||||
[[autodoc]] tuners.lily.model.LilyModel
|
||||
@@ -0,0 +1,47 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Llama-Adapter
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/llama-adapter.png"/>
|
||||
</div>
|
||||
<small><a href="https://hf.co/papers/2303.16199">LLaMA-Adapter: Efficient Fine-tuning of Language Models with Zero-init Attention</a></small>
|
||||
|
||||
[Llama-Adapter](https://hf.co/papers/2303.16199) is a PEFT method specifically designed for turning Llama into an instruction-following model. The Llama model is frozen and only a set of adaptation prompts prefixed to the input instruction tokens are learned. Since randomly initialized modules inserted into the model can cause the model to lose some of its existing knowledge, Llama-Adapter uses zero-initialized attention with zero gating to progressively add the instructional prompts to the model.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*We present LLaMA-Adapter, a lightweight adaption method to efficiently fine-tune LLaMA into an instruction-following model. Using 52K self-instruct demonstrations, LLaMA-Adapter only introduces 1.2M learnable parameters upon the frozen LLaMA 7B model, and costs less than one hour for fine-tuning on 8 A100 GPUs. Specifically, we adopt a set of learnable adaption prompts, and prepend them to the input text tokens at higher transformer layers. Then, a zero-init attention mechanism with zero gating is proposed, which adaptively injects the new instructional cues into LLaMA, while effectively preserves its pre-trained knowledge. With efficient training, LLaMA-Adapter generates high-quality responses, comparable to Alpaca with fully fine-tuned 7B parameters. Furthermore, our approach can be simply extended to multi-modal input, e.g., images, for image-conditioned LLaMA, which achieves superior reasoning capacity on ScienceQA. We release our code at https://github.com/ZrrSkywalker/LLaMA-Adapter*.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=ADAPTION_PROMPT"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## AdaptionPromptConfig
|
||||
|
||||
[[autodoc]] tuners.adaption_prompt.config.AdaptionPromptConfig
|
||||
|
||||
## AdaptionPromptModel
|
||||
|
||||
[[autodoc]] tuners.adaption_prompt.model.AdaptionPromptModel
|
||||
@@ -0,0 +1,90 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# LoHa
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/lora.png"/>
|
||||
</div>
|
||||
<small><a href="https://hf.co/papers/2103.10385">Navigating Text-To-Image Customization: From LyCORIS Fine-Tuning to Model Evaluation</a></small>
|
||||
|
||||
Low-Rank Hadamard Product ([LoHa](https://huggingface.co/papers/2108.06098)), is similar to LoRA except it approximates the large weight matrix with more low-rank matrices and combines them with the Hadamard product. This method is even more parameter-efficient than LoRA and achieves comparable performance. LoHa was originally proposed for federated learning (FedPara) but works well as a general-purpose PEFT method, and is especially popular for fine-tuning image generation models such as Stable Diffusion.
|
||||
|
||||
> **Note:** LoHa is part of the [LyCORIS](./adapter_utils) family of adapters. Its close relative [LoKr](./lokr) uses the Kronecker product instead of the Hadamard product.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*In this work, we propose a communication-efficient parameterization, FedPara, for federated learning (FL) to overcome the burdens on frequent model uploads and downloads. Our method re-parameterizes weight parameters of layers using low-rank weights followed by the Hadamard product. Compared to the conventional low-rank parameterization, our FedPara method is not restricted to low-rank constraints, and thereby it has a far larger capacity. This property enables to achieve comparable performance while requiring 3 to 10 times lower communication costs than the model with the original layers, which is not achievable by the traditional low-rank methods. The efficiency of our method can be further improved by combining with other efficient FL optimizers. In addition, we extend our method to a personalized FL application, pFedPara, which separates parameters into global and local ones. We show that pFedPara outperforms competing personalized FL methods with more than three times fewer parameters.*
|
||||
|
||||
Low-rank decomposition can impact performance because the weight updates are limited to the low-rank space, which can constrain a model's expressiveness. However, you don't necessarily want to use a larger rank because it increases the number of trainable parameters. To address this, LoHa was applied to diffusion models where the ability to generate diverse images is an important consideration. LoHa should also work with general model types, but support for embedding layers isn't currently implemented in PEFT.
|
||||
|
||||
LoHa uses the [Hadamard product](https://en.wikipedia.org/wiki/Hadamard_product_(matrices)) (element-wise product) instead of the matrix product. $\Delta W$ is represented by four smaller matrices instead of two - like in LoRA - and each pair of these low-rank matrices are combined with the Hadamard product. As a result, $\Delta W$ can have the same number of trainable parameters but a higher rank and expressivity.
|
||||
|
||||
## When to use LoHa
|
||||
|
||||
LoHa is a good choice when:
|
||||
|
||||
- You are fine-tuning **image generation models** (Stable Diffusion UNet or text encoder), where it is most widely used.
|
||||
- You want **higher effective rank** than LoRA for the same number of trainable parameters, since the Hadamard product of two low-rank matrices spans a larger subspace than a single low-rank product.
|
||||
- You want to **combine different PEFT methods** at inference time using [`PeftMixedModel`](./peft_model#peft.PeftMixedModel), for example LoHa together with LoKr.
|
||||
|
||||
LoHa supports linear and Conv2d layers. For tasks that additionally require embedding layer adaptation, consider [LoRA](./lora) instead.
|
||||
|
||||
## Usage
|
||||
|
||||
```python
|
||||
from diffusers import StableDiffusionPipeline
|
||||
from peft import LoHaConfig, get_peft_model
|
||||
|
||||
config_unet = LoHaConfig(
|
||||
r=8,
|
||||
alpha=8,
|
||||
target_modules=[
|
||||
"to_k",
|
||||
"to_q",
|
||||
"to_v",
|
||||
"to_out.0",
|
||||
"proj_in",
|
||||
"proj_out",
|
||||
],
|
||||
rank_dropout=0.0,
|
||||
module_dropout=0.0,
|
||||
use_effective_conv2d=True,
|
||||
)
|
||||
|
||||
pipeline = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5")
|
||||
pipeline.unet = get_peft_model(pipeline.unet, config_unet)
|
||||
pipeline.unet.print_trainable_parameters()
|
||||
```
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=LOHA"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## LoHaConfig
|
||||
|
||||
[[autodoc]] tuners.loha.config.LoHaConfig
|
||||
|
||||
## LoHaModel
|
||||
|
||||
[[autodoc]] tuners.loha.model.LoHaModel
|
||||
@@ -0,0 +1,64 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# LoKr
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/lora.png"/>
|
||||
</div>
|
||||
<small><a href="https://hf.co/papers/2103.10385">Navigating Text-To-Image Customization: From LyCORIS Fine-Tuning to Model Evaluation</a></small>
|
||||
|
||||
Low-Rank Kronecker Product ([LoKr](https://hf.co/papers/2309.14859)), is a LoRA-variant method that approximates the large weight matrix with two low-rank matrices and combines them with the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product). LoKr also provides an optional third low-rank matrix to provide better control during fine-tuning. By expresseing the weight update matrix as a decomposition of a Kronecker product, creating a block matrix, LoKr is able to preserve the rank of the original weight matrix. The size of the smaller matrices are determined by its *rank* or `r`. Another benefit of the Kronecker product is that it can be vectorized by stacking the matrix columns. This can speed up the process because you're avoiding fully reconstructing ∆W.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Text-to-image generative models have garnered immense attention for their ability to produce high-fidelity images from text prompts. Among these, Stable Diffusion distinguishes itself as a leading open-source model in this fast-growing field. However, the intricacies of fine-tuning these models pose multiple challenges from new methodology integration to systematic evaluation. Addressing these issues, this paper introduces LyCORIS [Lora beYond Conventional methods, Other Rank adaptation Implementations for Stable diffusion](https://github.com/KohakuBlueleaf/LyCORIS), an open-source library that offers a wide selection of fine-tuning methodologies for Stable Diffusion. Furthermore, we present a thorough framework for the systematic assessment of varied fine-tuning techniques. This framework employs a diverse suite of metrics and delves into multiple facets of fine-tuning, including hyperparameter adjustments and the evaluation with different prompt types across various concept categories. Through this comprehensive approach, our work provides essential insights into the nuanced effects of fine-tuning parameters, bridging the gap between state-of-the-art research and practical application.*
|
||||
|
||||
## Usage
|
||||
|
||||
```py
|
||||
from peft import LoKrConfig, get_peft_model
|
||||
|
||||
config = LoKrConfig(
|
||||
r=16,
|
||||
alpha=16,
|
||||
target_modules=["query", "value"],
|
||||
module_dropout=0.1,
|
||||
modules_to_save=["classifier"],
|
||||
)
|
||||
model = get_peft_model(model, config)
|
||||
model.print_trainable_parameters()
|
||||
"trainable params: 116,069 || all params: 87,172,042 || trainable%: 0.13314934162033282"
|
||||
```
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=LOKR"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## LoKrConfig
|
||||
|
||||
[[autodoc]] tuners.lokr.config.LoKrConfig
|
||||
|
||||
## LoKrModel
|
||||
|
||||
[[autodoc]] tuners.lokr.model.LoKrModel
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,155 @@
|
||||
<!--⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# LoRA conversion
|
||||
|
||||
Functions that allow to convert non-LoRA PEFT models to LoRA models.
|
||||
|
||||
## Description
|
||||
|
||||
PEFT supports dozens of different parameter effficient fine-tuning techniques. The most popular one by far is LoRA. This means that many other packages support LoRA too. For example, [Diffusers](https://huggingface.co/docs/diffusers/main/en/api/loaders/lora) allows to load LoRA adapters to change the capabilities of diffusion models. [vLLM](https://docs.vllm.ai/en/stable/features/lora/) allows serving models with LoRA adapters. This is nice but unfortunately, all the other, non-LoRA PEFT methods are rarely supported. Therefore, even if another PEFT method would work better for your specific use case, you may be prevented from using it because downstream packages offer no support.
|
||||
|
||||
Here we present a potential solution. PEFT offers two functions, [`save_as_lora`] and [`convert_to_lora`], which allow to convert a PEFT adapter into a LoRA adapter. Not all PEFT methods support this for now, but if they do, it means you can start with the PEFT method that works best for you and then later use it as if it were a LoRA adapter.
|
||||
|
||||
## Example
|
||||
|
||||
The LoRA rank for the converted adapter can either be set to a fixed rank by passing an int > 0 to the `rank` argument, or a dynamic rank, which adapts to each layer, by passing a float between 0 and 1 to the `rank` argument. Dynamic ranks can potentially be more efficient (same performance with fewer parameters).
|
||||
|
||||
### Fixed LoRA rank
|
||||
|
||||
The usage of [`save_as_lora`] is relatively straightforward:
|
||||
|
||||
```python
|
||||
from peft import get_peft_model, save_as_lora
|
||||
|
||||
# first load and train your non-LoRA PEFT model as normal
|
||||
base_model = ...
|
||||
non_lora_config = ...
|
||||
model = get_peft_model(base_model, non_lora_config)
|
||||
# check that this PEFT method can indeed be converted to LoRA
|
||||
assert model.supports_lora_conversion()
|
||||
... # train the model
|
||||
|
||||
# the rank of the LoRA adapter that you want to convert to
|
||||
target_rank = 64
|
||||
# save as a LoRA checkpoint
|
||||
save_as_lora(output_path, model, rank=target_rank)
|
||||
```
|
||||
|
||||
This will create a LoRA checkpoint at `output_path` that you can load like any other LoRA adapter, or use in downstream packages such as Diffusers or vLLM.
|
||||
|
||||
The [`convert_to_lora`] function is useful if you don't want to save the converted LoRA adapter but instead want to use the converted weights right away, for example to perform evaluations:
|
||||
|
||||
```python
|
||||
from peft import convert_to_lora, get_peft_model, set_peft_model_state_dict
|
||||
|
||||
base_model = ...
|
||||
non_lora_config = ...
|
||||
model = get_peft_model(base_model, non_lora_config)
|
||||
... # train the model
|
||||
|
||||
# get the lora config and state dict of the converted lora model
|
||||
lora_config, lora_state_dict = convert_to_lora(model, rank=target_rank)
|
||||
# reload the base model, or use model.unload()
|
||||
base_model = ...
|
||||
# apply the lora config to the base model
|
||||
lora_model = get_peft_model(base_model, lora_config)
|
||||
# load the LoRA weights onto the base model
|
||||
set_peft_model_state_dict(lora_model, state_dict)
|
||||
```
|
||||
|
||||
### Dynamic LoRA rank
|
||||
|
||||
In the examples above, we used a fixed LoRA rank for conversion. However, it is conceivable that some layers don't require a high rank to be accurately converted, while other layers require a higher rank. To accommodate this, PEFT offers the option to pass a float between 0 and 1 as the `rank` argument. Let's say you pass `rank=0.5`. This means that for each layer, the rank for the LoRA adapter is chosen such that the LoRA adapter explains 50% of the variance in weight introduced by original adapter. In more technical terms, under the hood we perform a [Singular Value Decomposition](https://en.wikipedia.org/wiki/Singular_value_decomposition) on the weight contribution of the adapter and then take the top singular values that, when normalized, sum up to the passed value.
|
||||
|
||||
```python
|
||||
# set a dynamic rank by passing a float
|
||||
threshold = 0.7
|
||||
# save as a LoRA checkpoint
|
||||
save_as_lora(output_path, model, rank=threshold)
|
||||
# get the lora config and state dict directly:
|
||||
lora_config, lora_state_dict = convert_to_lora(model, rank=threshold)
|
||||
# inspect the different ranks per layer:
|
||||
print(lora_config.rank_pattern)
|
||||
```
|
||||
|
||||
Using this type of dynamic LoRA rank can be useful if the contribution of the different layers varies a lot. The disadvantage is that it could mean that some layers will have a very high LoRA rank, which can lead to memory spikes. Please test what works best for your use case.
|
||||
|
||||
### Compiling the model
|
||||
|
||||
For large models, doing the conversion may take some time; for instance each PEFT module has to go through an SVD computation. By passing `compile_kwargs` to [`save_as_lora`] or [`convert_to_lora`], you can apply [`torch.compile`](https://docs.pytorch.org/docs/stable/generated/torch.compile.html) to the conversion function and potentially speed up the process. The `compile_kwargs` are a dict of keyword arguments that are passed to `torch.compile` (empty dict also works). Below is an example:
|
||||
|
||||
```python
|
||||
compile_kwargs = {"dynamic": True, "mode": "max-autotune-no-cudagraphs", "fullgraph": True}
|
||||
save_as_lora(output_path, model, rank=rank, compile_kwargs=compile_kwargs)
|
||||
```
|
||||
|
||||
### LoRA to LoRA conversion
|
||||
|
||||
It is also possible to convert a LoRA adapter into another LoRA adapter. Why would you want to do that? There is one reason, namely if you want to reduce the rank of the LoRA adapter. If, after training, you want to shrink the LoRA adapter, use [`save_as_lora`] or [`convert_to_lora`] and pass a smaller rank. This will give you a new LoRA adapter that has a smaller memory and storage footprint.
|
||||
|
||||
## Metrics
|
||||
|
||||
### Non-LoRA to LoRA conversion
|
||||
|
||||
Of course, converting one PEFT adapter into another adapter is a lossy process. The new adapter will most likely not perform as well as the initial adapter. Therefore, it is highly advised to **evaluate the converted LoRA adapter**. This way, you can make sure that the converted adapter performs well enough for your use case. The general rule applies that the higher the rank of the LoRA adapter, the better it will approximate your initial adapter. This means that the converted LoRA adapter may require more parameters than the original adapter to achieve a similar performanace.
|
||||
|
||||
To give an example, here are some numbers that were derived on the [PEFT MetaMathQA benchmark](https://github.com/huggingface/peft/tree/main/method_comparison/MetaMathQA). For this, a [LoHa](https://huggingface.co/docs/peft/package_reference/loha) adapter was used to fine-tune `meta-llama/Llama-3.2-3B` on MetaMathQA and evaluated on GSM8K. The initial LoKr adapter had rank 32, resulting in 18,350,080 trainable parameters and a test accuracy of 41.85%. Evaluation required 12.25 GB of memory. The checkpoint was converted into LoRA with different values for the `rank`. The resulting outcome is:
|
||||
|
||||
| rank | trainable parameters | test accuracy (%) | accuracy change | memory reserved (max, GB) | memory increase |
|
||||
|------|---------------------:|------------------:|----------------:|--------------------------:|----------------:|
|
||||
| 8 | 2293760 | 37.60 | -4.25 | 12.41 | 0.16 |
|
||||
| 16 | 4587520 | 38.89 | -2.96 | 12.15 | -0.10 |
|
||||
| 32 | 9175040 | 40.11 | -1.74 | 12.41 | 0.16 |
|
||||
| 64 | 18350080 | 39.20 | -2.65 | 12.18 | -0.07 |
|
||||
| | | | | | |
|
||||
| 0.4 | 2428928 | 37.60 | -4.25 | 12.41 | 0.16 |
|
||||
| 0.5 | 4761600 | 40.18 | -1.67 | 12.41 | 0.16 |
|
||||
| 0.6 | 8857600 | 39.42 | -2.43 | 12.41 | 0.16 |
|
||||
| 0.7 | 16230400 | 39.04 | -2.81 | 12.15 | -0.10 |
|
||||
|
||||
As you can see, we can attain a test accuracy that comes close to the original LoHa adapter if the rank is sufficiently high. Choosing the right rank is a tradeoff between model performance and model efficiency. To reproduce this experiment, follow the script at https://github.com/huggingface/peft/tree/main/scripts/evaluate-lora-conversion.py.
|
||||
|
||||
Note that the number of trainable parameters cannot be translated one to one into memory usage. Some PEFT methods require more, some less memory, even with the same number of trainable parameters. Therefore, even if after conversion, the LoRA adapter has more parameters than the original one, it could still be more memory efficient when serving.
|
||||
|
||||
### LoRA to LoRA conversion
|
||||
|
||||
Similar to the experiment above, we can also evaluate LoRA to LoRA conversion (i.e. LoRA compression). Here, we start with a LoRA adapter of rank 64 trained on the same setup as above with RS-LoRA. The initial adapter has 18,350,080 trainable parameters, a test accuracy of 52.92%, and requires 12.58 GB of memory for evaluation. The following table shows the results of converting this adapter to LoRA adapters of smaller rank:
|
||||
|
||||
| rank | trainable parameters | test accuracy (%) | accuracy change | memory reserved (max, GB) | memory increase |
|
||||
|------|---------------------:|------------------:|----------------:|--------------------------:|----------------:|
|
||||
| 8 | 2293760 | 43.37 | -9.55 | 12.38 | -0.20 |
|
||||
| 16 | 4587520 | 48.90 | -4.02 | 12.38 | -0.20 |
|
||||
| 32 | 9175040 | 51.48 | -1.44 | 12.49 | -0.09 |
|
||||
| 48 | 13762560 | 52.01 | -0.91 | 12.38 | -0.20 |
|
||||
| | | | | | |
|
||||
| 0.5 | 2150400 | 44.12 | -8.80 | 12.37 | -0.21 |
|
||||
| 0.6 | 3082240 | 47.54 | -5.38 | 12.37 | -0.21 |
|
||||
| 0.7 | 4448256 | 50.49 | -2.43 | 12.37 | -0.21 |
|
||||
| 0.8 | 6510592 | 50.11 | -2.81 | 12.37 | -0.21 |
|
||||
| 0.9 | 10022912 | 51.55 | -1.37 | 12.38 | -0.20 |
|
||||
| 0.95 | 12976128 | 52.62 | -0.30 | 12.39 | -0.19 |
|
||||
|
||||
So for instance for rank 0.95, we can close the accuracy gap to just 0.3 percentage points while reducing the number of parameters by 30%. Also note that these compressed LoRAs can be better than directly training them on the lower rank -- e.g. for rank 32, training directly results in a test accuracy of 48.22%, while conversion from rank 64 results in 51.48%.
|
||||
|
||||
## Caveats
|
||||
|
||||
There are some limitations to the LoRA conversion. As mentioned above, a reduction in performance is expected and the converted LoRA will most likely be less parameter efficient than the original adapter. Moreover, LoRA conversion has these limitations:
|
||||
|
||||
- Right now, only adapters applied to linear layers can be converted.
|
||||
- Not all PEFT methods currently support LoRA conversion.
|
||||
|
||||
If there is a lot of demand to extend LoRA conversion, please let us know by creating a [GitHub discussion](https://github.com/huggingface/peft/discussions) and we will make it work with more layer types and PEFT methods.
|
||||
|
||||
## API
|
||||
|
||||
### Convert a non-LoRA model to a LoRA model, return the `LoraConfig` and `state_dict`
|
||||
|
||||
[[autodoc]] tuners.lora.conversion.convert_to_lora
|
||||
- all
|
||||
|
||||
### Convert a non-LoRA model to a LoRA model, save the adapter checkpoint and config at the given path
|
||||
|
||||
[[autodoc]] tuners.lora.conversion.save_as_lora
|
||||
- all
|
||||
@@ -0,0 +1,20 @@
|
||||
# Block-Diagonal LoRA for Eliminating Communication Overhead in Tensor Parallel LoRA Serving
|
||||
|
||||
Block-Diagonal LoRA (BD-LoRA) is a LoRA variant in which some LoRA factors are constrained to be block-diagonal. This allows faster serving by eliminating communication overheads
|
||||
when running inference on multiple GPUs. Despite the block-diagonal constraint, BD-LoRA is similarly performant to vanilla LoRA at similar parameter counts.
|
||||
|
||||
BD-LoRA is designed to be used with tensor parallelism, which means sharding the weights of a model among multiple GPUs. A popular sharding strategy is the [Megatron Sharding Strategy](https://arxiv.org/abs/1909.08053). For two linear layers $W_1$, $W_2$ that follow each other (for example the up and down projections in a transformer MLP module), we will shard the first layer in a column-parallel way (which requires LoRA B to be block-diagonal) and the second layer in a row-parallel way (which requires LoRA A to be block-diagonal). For the attention module, this can be similarly achieved by taking the Q, K and V projections together as $W_1$ and the out projection as $W_2$, sharding accordingly. This sharding allows a compatible inference engine to distribute each block-diagonal shard over a a different GPU, cutting the need to communicate partial results among GPUs. In the image below, you can see the exact sharding strategy and how this saves computational efforts.
|
||||
|
||||
Paper: https://hf.co/papers/2510.23346
|
||||
|
||||
<div>
|
||||
<img src="https://github.com/huggingface/peft/blob/main/examples/bdlora_finetuning/bdlora-sharding.png?raw=true" width="800"/>
|
||||
</div>
|
||||
|
||||
### Performance, rank and parameter count
|
||||
BD-LoRA achieves similar performance to LoRA (see image below, or the `method_comparison` folder in the peft repository root) at the same parameter count. However, as every other factor in BD-LoRA is block-diagonal, a BD-LoRA adapter will have less parameters than a LoRA adapter at the same rank. The performance of BD-LoRA is only competitive when the rank is then increased accordingly. We provide example code for rank-matching at the end of this example notebook.
|
||||
|
||||
<div>
|
||||
<img src="https://github.com/huggingface/peft/blob/main/examples/bdlora_finetuning/bdlora-performance.png?raw=true" width="600"/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<!--Copyright 2026-present The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contains specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Weight-Decomposed Low-Rank Adaptation (DoRA)
|
||||
|
||||
> [!NOTE]
|
||||
> This is a variant of LoRA and therefore everything that is possible with LoRA is valid for this method except otherwise stated on this page.
|
||||
|
||||
This technique decomposes the updates of the weights into two parts, magnitude and direction. Direction is handled by normal LoRA, whereas the magnitude is handled by a separate learnable parameter. This can improve the performance of LoRA, especially at low ranks. For more information on DoRA, see https://huggingface.co/papers/2402.09353.
|
||||
|
||||
```py
|
||||
from peft import LoraConfig
|
||||
|
||||
config = LoraConfig(use_dora=True, ...)
|
||||
```
|
||||
|
||||
If parts of the model or the DoRA adapter are offloaded to CPU you can get a significant speedup at the cost of some temporary (ephemeral) VRAM overhead by using `ephemeral_gpu_offload=True` in `config.runtime_config`.
|
||||
|
||||
```py
|
||||
from peft import LoraConfig, LoraRuntimeConfig
|
||||
|
||||
config = LoraConfig(use_dora=True, runtime_config=LoraRuntimeConfig(ephemeral_gpu_offload=True), ...)
|
||||
```
|
||||
|
||||
A `PeftModel` with a DoRA adapter can also be loaded with `ephemeral_gpu_offload=True` flag using the `from_pretrained` method as well as the `load_adapter` method.
|
||||
|
||||
```py
|
||||
from peft import PeftModel
|
||||
|
||||
model = PeftModel.from_pretrained(base_model, peft_model_id, ephemeral_gpu_offload=True)
|
||||
```
|
||||
|
||||
## Optimization
|
||||
|
||||
DoRA is optimized (computes faster and takes less memory) for models in the evaluation mode, or when dropout is set to 0. We reuse the
|
||||
base result at those times to get the speedup.
|
||||
Running [dora finetuning](https://github.com/huggingface/peft/blob/main/examples/dora_finetuning/dora_finetuning.py)
|
||||
with `CUDA_VISIBLE_DEVICES=0 ZE_AFFINITY_MASK=0 time python examples/dora_finetuning/dora_finetuning.py --quantize --lora_dropout 0 --batch_size 16 --eval_step 2 --use_dora` on a 4090 with gradient accumulation set to 2 and max step to 20 resulted with the following observations:
|
||||
|
||||
| | Without Optimization | With Optimization |
|
||||
| :--: | :--: | :--: |
|
||||
| train runtime (sec) | 359.7298 | **279.2676** |
|
||||
| train samples per second | 1.779 | **2.292** |
|
||||
| train steps per second | 0.056 | **0.072** |
|
||||
|
||||
Moreover, it is possible to further increase runtime performance of DoRA by using the [`DoraCaching`] helper context. This requires the model to be in `eval` mode:
|
||||
|
||||
```py
|
||||
from peft.helpers import DoraCaching
|
||||
|
||||
model.eval()
|
||||
with DoraCaching():
|
||||
output = model(inputs)
|
||||
```
|
||||
|
||||
For [`meta-llama/Llama-3.1-8B`](https://huggingface.co/meta-llama/Llama-3.1-8B), the [DoRA caching benchmark script](https://github.com/huggingface/peft/blob/main/examples/dora_finetuning/dora-caching.py) shows that, compared to LoRA:
|
||||
|
||||
- DoRA without caching requires 139% more time
|
||||
- DoRA without caching requires 4% more memory
|
||||
- DoRA with caching requires 17% more time
|
||||
- DoRA with caching requires 41% more memory
|
||||
|
||||
Caching can thus make inference with DoRA significantly faster but it also requires significantly more memory. Ideally, if the use case allows it, just merge the DoRA adapter to avoid both memory and runtime overhead.
|
||||
|
||||
## Caveats
|
||||
|
||||
- DoRA only supports embedding, linear, and Conv2d layers at the moment.
|
||||
- DoRA introduces a bigger overhead than pure LoRA, so it is recommended to merge weights for inference, see [`LoraModel.merge_and_unload`].
|
||||
- DoRA should work with weights quantized with bitsandbytes ("QDoRA"). However, issues have been reported when using QDoRA with DeepSpeed Zero2.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# MonteCLoRA (Monte Carlo Low-Rank Adaptation)
|
||||
|
||||
> [!NOTE]
|
||||
> This is a variant of LoRA and therefore everything that is possible with LoRA is valid for this method except otherwise stated on this page.
|
||||
|
||||
MonteCLoRA wraps a standard LoRA adapter with a small variational module that draws Monte Carlo samples of stochastic perturbations on top of the LoRA `A` matrix during training. Concretely, it learns variational parameters (a Wishart-based covariance, a per-sample multivariate-normal noise term, and a Dirichlet weighting over the samples) and adds the resulting averaged perturbation to `lora_A` at every forward pass. A KL-divergence + entropy term is added to the training loss to keep these variational parameters anchored to a sensible prior. At inference time the sampler is disabled and MonteCLoRA behaves exactly like a regular LoRA adapter, so there is **no extra inference cost or extra parameters to merge**. For the full method see https://huggingface.co/papers/2411.04358.
|
||||
|
||||
You may want to consider MonteCLoRA when:
|
||||
|
||||
- You are fine-tuning on a small or noisy dataset and want stronger regularization than vanilla LoRA. The Monte Carlo averaging and the KL term together act as a Bayesian-style regularizer.
|
||||
- You want better uncertainty calibration / robustness from your adapter without paying extra cost at inference time (the variational machinery is training-only).
|
||||
- Vanilla LoRA is overfitting and lowering `r` or increasing `lora_dropout` is not enough.
|
||||
|
||||
You probably do *not* need MonteCLoRA when you have a large, clean dataset and vanilla LoRA already trains stably — in that regime the extra variational parameters mostly add training overhead without much benefit.
|
||||
|
||||
To enable MonteCLoRA, pass a `MontecloraConfig` to `LoraConfig`:
|
||||
|
||||
```py
|
||||
from peft import LoraConfig, MontecloraConfig
|
||||
|
||||
monteclora_config = MontecloraConfig(
|
||||
num_samples=8, # number of Monte Carlo samples per forward pass
|
||||
sample_scaler=1e-4, # magnitude of the variational perturbation
|
||||
kl_loss_weight=1e-5, # weight of the KL term added to the training loss
|
||||
)
|
||||
config = LoraConfig(
|
||||
r=16,
|
||||
lora_alpha=32,
|
||||
target_modules=["q_proj", "v_proj"],
|
||||
monteclora_config=monteclora_config,
|
||||
)
|
||||
```
|
||||
|
||||
During training you must add the variational regularization loss to the task loss. The simplest way is to call [`LoraModel._get_monteclora_loss`] on the underlying `LoraModel`:
|
||||
|
||||
```py
|
||||
task_loss = ... # standard loss returned by your model
|
||||
monteclora_loss = model._get_monteclora_loss() # 0.0 if MonteCLoRA is not used
|
||||
total_loss = task_loss + monteclora_loss
|
||||
total_loss.backward()
|
||||
```
|
||||
|
||||
If you train with the HF `Trainer`, you can simply mix in [`peft.helpers.MontecloraTrainerMixin`] which does this for you in `compute_loss`:
|
||||
|
||||
```py
|
||||
from transformers import Trainer
|
||||
from peft.helpers import MontecloraTrainerMixin
|
||||
|
||||
|
||||
class MontecloraTrainer(MontecloraTrainerMixin, Trainer):
|
||||
pass
|
||||
```
|
||||
|
||||
A complete working example is available at [`examples/monteclora_finetuning`](https://github.com/huggingface/peft/tree/main/examples/monteclora_finetuning).
|
||||
@@ -0,0 +1,52 @@
|
||||
<!--Copyright 2026-present The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contains specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
### VeLoRA
|
||||
|
||||
> [!NOTE]
|
||||
> This is a variant of LoRA and therefore everything that is possible with LoRA is valid for this method except otherwise stated on this page.
|
||||
|
||||
[VeLoRA](https://huggingface.co/papers/2405.17991) is a LoRA variant that reduces training memory by compressing the activations saved for the LoRA in the forward pass and then reconstructing them in the backwards pass to implement the update rules. In PEFT, VeLoRA is configured as a LoRA variant through the `velora_config` argument on [`LoraConfig`].
|
||||
|
||||
```py
|
||||
from peft import LoraConfig, VeloraConfig
|
||||
|
||||
config = LoraConfig(
|
||||
target_modules=["q_proj", "v_proj"],
|
||||
velora_config=VeloraConfig(
|
||||
num_groups=64,
|
||||
scale=0.2,
|
||||
init_type="batch_average",
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
VeLoRA is applied to every LoRA layer selected by `target_modules`. `num_groups` controls how the input activation depth is split before compression. If the activation depth is not evenly divisible by `num_groups`, VeLoRA pads the grouped representation internally and removes the padding after reconstruction. `scale` rescales the reconstructed activations during the backward pass, and `init_type` chooses how the projection is initialized.
|
||||
|
||||
Use `batch_average_once` to initialize the projection from the first training batch, `batch_average` to update it from every training forward pass, or `random` to initialize it immediately from a random normalized vector.
|
||||
|
||||
Below are some results with the [MetaMathQA benchmark](https://github.com/huggingface/peft/tree/main/method_comparison/MetaMathQA).
|
||||
|
||||
| Variant | Training Loss | Max Memory (GiB) | Tokens/sec |
|
||||
|---|---:|---:|---:|
|
||||
| LoRA | 0.5427 | 27.69 | 2366.2 |
|
||||
| LoRA + GC | 0.5426 | 13.17 | 1671.8 |
|
||||
| LoRA+VeLoRA | 0.5427 | 19.94 | 2057.6 |
|
||||
|
||||
#### Caveats
|
||||
|
||||
- VeLoRA is currently supported on standard LoRA linear layers only.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--Copyright 2024 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Model merge
|
||||
|
||||
PEFT provides several internal utilities for [merging LoRA adapters](../developer_guides/model_merging) with the TIES and DARE methods.
|
||||
|
||||
[[autodoc]] utils.merge_utils.prune
|
||||
|
||||
[[autodoc]] utils.merge_utils.calculate_majority_sign_mask
|
||||
|
||||
[[autodoc]] utils.merge_utils.disjoint_merge
|
||||
|
||||
[[autodoc]] utils.merge_utils.task_arithmetic
|
||||
|
||||
[[autodoc]] utils.merge_utils.ties
|
||||
|
||||
[[autodoc]] utils.merge_utils.dare_linear
|
||||
|
||||
[[autodoc]] utils.merge_utils.dare_ties
|
||||
@@ -0,0 +1,102 @@
|
||||
<!--Copyright 2025 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# MiSS
|
||||
|
||||
[MiSS (Matrix Shard Sharing)](https://arxiv.org/abs/2409.15371) is a PEFT method that achieves a good balance between model performance and computational efficiency. It requires only a single trainable matrix and introduces a shard-sharing mechanism distinct from LoRA.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Parameter-Efficient Fine-Tuning (PEFT) methods, particularly Low-Rank Adaptation (LoRA), effectively reduce the number of trainable parameters in Large Language Models (LLMs). However, as model scales continue to grow, the demand for computational resources remains a significant challenge. Existing LoRA variants often struggle to strike an optimal balance between adaptability (model performance and convergence speed) and efficiency (computational overhead, memory usage, and initialization time). This paper introduces MiSS (Matrix Shard Sharing), a novel PEFT approach that addresses this trade-off through a simple shard-sharing mechanism. MiSS leverages the insight that a low-rank adaptation can be achieved by decomposing the weight matrix into multiple fragment matrices and utilizing a shared, trainable common fragment. This method constructs the low-rank update matrix through the replication of these shared, partitioned shards. We also propose a hardware-efficient and broadly applicable implementation for MiSS. Extensive experiments conducted on a range of tasks, alongside a systematic analysis of computational performance, demonstrate MiSS's superiority. The results show that MiSS significantly outperforms standard LoRA and its prominent variants in both model performance metrics and computational efficiency, including initialization speed and training throughput. By effectively balancing expressive power and resource utilization, MiSS offers a compelling solution for efficiently adapting large-scale models.*
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=MISS"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
## When to use MiSS
|
||||
|
||||
MiSS is a good choice when:
|
||||
|
||||
- You want faster initialization and higher training throughput than advanced LoRA initialization schemes that use expensive setups (such as PiSSA, LoRA-GA, or OLoRA).
|
||||
- You want a drop-in alternative to LoRA with minimal configuration changes.
|
||||
|
||||
If you need stronger expressiveness at the cost of some efficiency, consider the `bat` initialization variant (see below).
|
||||
|
||||
## init_weights modes
|
||||
|
||||
MiSS supports three initialization modes via the `init_weights` parameter:
|
||||
|
||||
- `True` (default): Standard MiSS initialization. Best starting point for most use cases.
|
||||
- `"bat"`: Enables nonlinear updates across different shards. Produces better results than standard MiSS but uses more memory and is approximately twice as slow. Use this when performance is the priority over efficiency.
|
||||
- `"mini"`: Uses a smaller rank along the `out_features` dimension, controlled by `mini_r`. This reduces trainable parameters further. When using this mode, `mini_r` must be set and `out_features` must be divisible by `mini_r`.
|
||||
|
||||
## Quick start
|
||||
|
||||
```python
|
||||
import torch
|
||||
from peft import MissConfig, get_peft_model
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained(
|
||||
"meta-llama/Llama-2-7b-hf",
|
||||
torch_dtype=torch.bfloat16,
|
||||
device_map="auto"
|
||||
)
|
||||
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
|
||||
tokenizer.pad_token_id = tokenizer.eos_token_id
|
||||
|
||||
# Standard MiSS
|
||||
config = MissConfig(
|
||||
r=64,
|
||||
miss_dropout=0.01,
|
||||
task_type="CAUSAL_LM"
|
||||
)
|
||||
|
||||
# BAT variant — better performance, more memory
|
||||
# config = MissConfig(
|
||||
# r=64,
|
||||
# init_weights="bat",
|
||||
# task_type="CAUSAL_LM"
|
||||
# )
|
||||
|
||||
# Mini variant — fewer trainable parameters
|
||||
# config = MissConfig(
|
||||
# r=64,
|
||||
# init_weights="mini",
|
||||
# mini_r=8,
|
||||
# task_type="CAUSAL_LM"
|
||||
# )
|
||||
|
||||
model = get_peft_model(model, config)
|
||||
model.print_trainable_parameters()
|
||||
```
|
||||
|
||||
For a full fine-tuning example including training and inference, see the [MiSS fine-tuning example](https://github.com/huggingface/peft/tree/main/examples/miss_finetuning).
|
||||
|
||||
# API
|
||||
|
||||
## MissConfig
|
||||
|
||||
[[autodoc]] tuners.miss.config.MissConfig
|
||||
|
||||
## MissModel
|
||||
|
||||
[[autodoc]] tuners.miss.model.MissModel
|
||||
@@ -0,0 +1,55 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Multitask prompt tuning
|
||||
|
||||
[Multitask prompt tuning](https://huggingface.co/papers/2303.02861) decomposes the soft prompts of each task into a single learned transferable prompt instead of a separate prompt for each task. The single learned prompt can be adapted for each task by multiplicative low rank updates.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Prompt tuning, in which a base pretrained model is adapted to each task via conditioning on learned prompt vectors, has emerged as a promising approach for efficiently adapting large language models to multiple downstream tasks. However, existing methods typically learn soft prompt vectors from scratch, and it has not been clear how to exploit the rich cross-task knowledge with prompt vectors in a multitask learning setting. We propose multitask prompt tuning (MPT), which first learns a single transferable prompt by distilling knowledge from multiple task-specific source prompts. We then learn multiplicative low rank updates to this shared prompt to efficiently adapt it to each downstream target task. Extensive experiments on 23 NLP datasets demonstrate that our proposed approach outperforms the state-of-the-art methods, including the full finetuning baseline in some cases, despite only tuning 0.035% as many task-specific parameters*.
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/mpt.png"/>
|
||||
</div>
|
||||
<small><a href="https://hf.co/papers/2303.02861">Multitask prompt tuning enables parameter-efficient transfer learning</a>.</small>
|
||||
|
||||
MPT consists of two stages:
|
||||
|
||||
1. source training - for each task, its soft prompt is decomposed into task-specific vectors. The task-specific vectors are multiplied together to form another matrix W, and the Hadamard product is used between W and a shared prompt matrix P to generate a task-specific prompt matrix. The task-specific prompts are distilled into a single prompt matrix that is shared across all tasks. This prompt is trained with multitask training.
|
||||
2. target adaptation - to adapt the single prompt for a target task, a target prompt is initialized and expressed as the Hadamard product of the shared prompt matrix and the task-specific low-rank prompt matrix.
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/mpt-decomposition.png"/>
|
||||
</div>
|
||||
<small><a href="https://hf.co/papers/2103.10385">Prompt decomposition</a>.</small>
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
There is no benchmark for this method yet. Feel free to contribute an experiment
|
||||
configuration but make sure to first create an issue
|
||||
[here](https://github.com/huggingface/peft/issues).
|
||||
|
||||
|
||||
# API
|
||||
|
||||
## MultitaskPromptTuningConfig
|
||||
|
||||
[[autodoc]] tuners.multitask_prompt_tuning.config.MultitaskPromptTuningConfig
|
||||
|
||||
## MultitaskPromptEmbedding
|
||||
|
||||
[[autodoc]] tuners.multitask_prompt_tuning.model.MultitaskPromptEmbedding
|
||||
@@ -0,0 +1,98 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# OFT
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/oft.png"/>
|
||||
</div>
|
||||
<small><a href="https://hf.co/papers/2306.07280">Controlling Text-to-Image Diffusion by Orthogonal Finetuning</a></small>
|
||||
|
||||
[Orthogonal Finetuning (OFT)](https://hf.co/papers/2306.07280) and [OFTv2](https://huggingface.co/papers/2506.19847) is a method developed for adapting text-to-image diffusion models. It works by reparameterizing the pretrained weight matrices with its orthogonal matrix to preserve information in the pretrained model. To reduce the number of parameters, OFT introduces a block-diagonal structure in the orthogonal matrix. The method primarily focuses on preserving a pretrained model's generative performance in the finetuned model. It tries to maintain the same cosine similarity ([hyperspherical energy](https://huggingface.co/papers/1805.09298)) between all pairwise neurons in a layer because this better captures the semantic information among neurons. This means OFT is more capable at preserving the subject and it is better for controllable generation (similar to [ControlNet](https://huggingface.co/docs/diffusers/using-diffusers/controlnet)).
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Large text-to-image diffusion models have impressive capabilities in generating photorealistic images from text prompts. How to effectively guide or control these powerful models to perform different downstream tasks becomes an important open problem. To tackle this challenge, we introduce a principled finetuning method -- Orthogonal Finetuning (OFT), for adapting text-to-image diffusion models to downstream tasks. Unlike existing methods, OFT can provably preserve hyperspherical energy which characterizes the pairwise neuron relationship on the unit hypersphere. We find that this property is crucial for preserving the semantic generation ability of text-to-image diffusion models. To improve finetuning stability, we further propose Constrained Orthogonal Finetuning (COFT) which imposes an additional radius constraint to the hypersphere. Specifically, we consider two important finetuning text-to-image tasks: subject-driven generation where the goal is to generate subject-specific images given a few images of a subject and a text prompt, and controllable generation where the goal is to enable the model to take in additional control signals. We empirically show that our OFT framework outperforms existing methods in generation quality and convergence speed*.
|
||||
|
||||
OFT preserves the hyperspherical energy by learning an orthogonal transformation for neurons to keep the cosine similarity between them unchanged, potentially leading to less forgetting of previous learnt knowledge. In practice, this means taking the matrix product of an orthogonal matrix with the pretrained weight matrix. However, to be parameter-efficient, the orthogonal matrix is represented as a block-diagonal matrix with rank `r` blocks. Whereas LoRA reduces the number of trainable parameters with low-rank structures, OFT reduces the number of trainable parameters with a sparse block-diagonal matrix structure.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=OFT"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
## Merge OFT weights into the base model
|
||||
|
||||
Similar to LoRA, the weights learned by OFT can be integrated into the pretrained weight matrices using the [`~OFTModel.merge_and_unload()` function. This function merges the adapter weights with the base model which allows you to effectively use the newly merged model as a standalone model.
|
||||
|
||||
## OFT Example Usage
|
||||
|
||||
For using OFT for quantized finetuning with [TRL](https://github.com/huggingface/trl) for `SFT`, `PPO`, or `DPO` fine-tuning, follow the following outline:
|
||||
|
||||
```py
|
||||
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
|
||||
from trl import SFTTrainer
|
||||
from peft import OFTConfig
|
||||
|
||||
if use_quantization:
|
||||
bnb_config = BitsAndBytesConfig(
|
||||
load_in_4bit=True,
|
||||
bnb_4bit_quant_type="nf4",
|
||||
bnb_4bit_compute_dtype=torch.bfloat16,
|
||||
bnb_4bit_use_double_quant=True,
|
||||
bnb_4bit_quant_storage=torch.bfloat16,
|
||||
)
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained(
|
||||
"model_name",
|
||||
quantization_config=bnb_config
|
||||
)
|
||||
tokenizer = AutoTokenizer.from_pretrained("model_name")
|
||||
|
||||
# Configure OFT
|
||||
peft_config = OFTConfig(
|
||||
oft_block_size=32,
|
||||
use_cayley_neumann=True,
|
||||
target_modules="all-linear",
|
||||
bias="none",
|
||||
task_type="CAUSAL_LM"
|
||||
)
|
||||
|
||||
trainer = SFTTrainer(
|
||||
model=model,
|
||||
train_dataset=ds['train'],
|
||||
peft_config=peft_config,
|
||||
processing_class=tokenizer,
|
||||
args=training_arguments,
|
||||
data_collator=collator,
|
||||
)
|
||||
|
||||
trainer.train()
|
||||
```
|
||||
|
||||
# API
|
||||
|
||||
## OFTConfig
|
||||
|
||||
[[autodoc]] tuners.oft.config.OFTConfig
|
||||
|
||||
## OFTModel
|
||||
|
||||
[[autodoc]] tuners.oft.model.OFTModel
|
||||
@@ -0,0 +1,254 @@
|
||||
<!--Copyright 2025 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# OSF (Orthogonal Subspace Fine-tuning)
|
||||
|
||||
Orthogonal Subspace Fine-tuning ([OSF](https://huggingface.co/papers/2504.07097)) is a PEFT method designed for continual learning that constrains parameter updates to be orthogonal to previously important directions. This approach enables full fine-tuning while preventing catastrophic forgetting without requiring additional parameters or storing previous gradients.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Continual learning in large language models (LLMs) is prone to catastrophic forgetting, where adapting to new tasks significantly degrades performance on previously learned ones. Existing methods typically rely on low-rank, parameter-efficient updates that limit the model's expressivity and introduce additional parameters per task, leading to scalability issues. To address these limitations, we propose a novel continual full fine-tuning approach leveraging adaptive singular value decomposition (SVD). Our method dynamically identifies task-specific low-rank parameter subspaces and constrains updates to be orthogonal to critical directions associated with prior tasks, thus effectively minimizing interference without additional parameter overhead or storing previous task gradients. We evaluate our approach extensively on standard continual learning benchmarks using both encoder-decoder (T5-Large) and decoder-only (LLaMA-2 7B) models, spanning diverse tasks including classification, generation, and reasoning. Empirically, our method achieves state-of-the-art results, up to 7% higher average accuracy than recent baselines like O-LoRA, and notably maintains the model's general linguistic capabilities, instruction-following accuracy, and safety throughout the continual learning process by reducing forgetting to near-negligible levels. Our adaptive SVD framework effectively balances model plasticity and knowledge retention, providing a practical, theoretically grounded, and computationally scalable solution for continual learning scenarios in large language models.*
|
||||
|
||||
## How OSF Works
|
||||
|
||||
OSF decomposes each weight matrix into high-rank (frozen) and low-rank (trainable) components using SVD:
|
||||
|
||||
```
|
||||
W = U_high * S_high * V_high^T + U_low * S_low * V_low^T
|
||||
```
|
||||
|
||||
Where:
|
||||
- `U_high, S_high, V_high`: Preserve important directions from previous tasks (frozen)
|
||||
- `U_low, S_low, V_low`: Allow adaptation to new tasks (trainable)
|
||||
|
||||
During training, gradients are projected to be orthogonal to the high-rank subspace, ensuring updates don't interfere with previously learned knowledge.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```python
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
from peft import OSFConfig, get_peft_model
|
||||
|
||||
# Load base model
|
||||
model = AutoModelForCausalLM.from_pretrained("gpt2")
|
||||
|
||||
# Configure OSF
|
||||
config = OSFConfig(
|
||||
target_modules=["c_attn", "c_proj"], # Target attention layers
|
||||
effective_rank=8, # Default rank for decomposition
|
||||
rank_pattern={"c_attn": 16} # Override rank for specific modules
|
||||
)
|
||||
|
||||
# Apply OSF
|
||||
model = get_peft_model(model, config)
|
||||
|
||||
# Train as usual
|
||||
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained("gpt2")
|
||||
tokenizer.pad_token = tokenizer.eos_token
|
||||
|
||||
inputs = tokenizer("Hello world", return_tensors="pt", padding=True)
|
||||
loss = model(**inputs, labels=inputs.input_ids).loss
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Target Modules
|
||||
|
||||
You can specify target modules in several ways:
|
||||
|
||||
```python
|
||||
# Specific module names
|
||||
config = OSFConfig(target_modules=["q_proj", "k_proj", "v_proj", "o_proj"])
|
||||
|
||||
# All linear layers
|
||||
config = OSFConfig(target_modules="all-linear")
|
||||
|
||||
# Model-specific defaults (automatically detected)
|
||||
config = OSFConfig() # Uses model-appropriate defaults
|
||||
```
|
||||
|
||||
### Effective Rank Configuration
|
||||
|
||||
Control the preserved/trainable subspaces:
|
||||
|
||||
```python
|
||||
# Global preserved rank (applies to all target modules)
|
||||
config = OSFConfig(effective_rank=16) # preserves top-16 singular directions; trains the rest
|
||||
|
||||
# Automatic preserved rank (50% of the smaller matrix dimension per target)
|
||||
config = OSFConfig(effective_rank=None)
|
||||
|
||||
# Per-module preserved-rank overrides
|
||||
config = OSFConfig(
|
||||
effective_rank=8,
|
||||
rank_pattern={
|
||||
"q_proj": 16, # Higher rank for query projection
|
||||
"gate_proj": 4 # Lower rank for gate projection
|
||||
}
|
||||
)
|
||||
|
||||
# Fractional preserved rank is supported (interpreted per-target as fraction * min_dim)
|
||||
config = OSFConfig(effective_rank=0.8) # preserve 80% of min_dim; train remaining 20%
|
||||
config = OSFConfig(rank_pattern={"q_proj": 0.5}) # preserve 50% on q_proj, others use global/default
|
||||
```
|
||||
|
||||
Note: OSF's `effective_rank` is the preserved (frozen) rank, not the trainable rank. The trainable rank equals `min(weight.shape) - effective_rank`. This differs from LoRA's `r`, which directly specifies the trainable rank.
|
||||
|
||||
|
||||
## Training Advice for Continual Learning
|
||||
|
||||
### Sequential Task Learning
|
||||
|
||||
OSF is specifically designed for learning tasks sequentially. Between tasks, recompute the SVD so the preserved subspace reflects the latest weights. One simple way is to re-wrap the updated base model with OSF again:
|
||||
|
||||
```python
|
||||
# Task 1: train on domain A with initial preserved subspace
|
||||
r = 8 # initial effective rank to preserve
|
||||
model = get_peft_model(base_model, OSFConfig(effective_rank=r))
|
||||
train_task(model, task_1_data)
|
||||
|
||||
# Task 2: recompute SVD on updated weights and increase preserved subspace
|
||||
base_model = model.unload() # unwrap base model without assuming internals
|
||||
r += 4 # grow preserved subspace to include Task 1 knowledge
|
||||
model = get_peft_model(base_model, OSFConfig(effective_rank=r))
|
||||
train_task(model, task_2_data)
|
||||
|
||||
# Task 3: recompute again and expand preserved subspace further
|
||||
base_model = model.unload()
|
||||
r += 4
|
||||
model = get_peft_model(base_model, OSFConfig(effective_rank=r))
|
||||
train_task(model, task_3_data)
|
||||
```
|
||||
|
||||
### Budget Allocation for Task Sequences
|
||||
|
||||
When training on a known sequence of n tasks, one effective strategy is to progressively allocate model capacity to balance learning new tasks while preserving previous knowledge:
|
||||
|
||||
- **Task 1**: Use full capacity (train everything)
|
||||
- **Task 2**: Freeze 1/n of model capacity, train remaining (n-1)/n capacity
|
||||
- **Task 3**: Freeze 2/n of model capacity, train remaining (n-2)/n capacity
|
||||
- **Task n**: Freeze (n-1)/n of model capacity, use 1/n capacity for final task
|
||||
|
||||
This approach ensures each task gets adequate learning capacity while progressively preserving more knowledge from previous tasks.
|
||||
|
||||
```python
|
||||
# Example: 4-task sequence with progressive budget allocation
|
||||
n_tasks = 4
|
||||
max_preserved_rank = 512 # Upper bound for preserved rank per target (heuristic)
|
||||
|
||||
for task_id in range(n_tasks):
|
||||
# Freeze increases over time; trainable capacity shrinks
|
||||
preserved_fraction = (task_id + 1) / n_tasks
|
||||
preserved_rank = int(max_preserved_rank * preserved_fraction)
|
||||
|
||||
config = OSFConfig(
|
||||
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
|
||||
effective_rank=preserved_rank,
|
||||
)
|
||||
|
||||
print(
|
||||
f"Task {task_id + 1}: Preserving rank {preserved_rank} "
|
||||
f"({preserved_fraction:.1%} of max_preserved_rank - {max_preserved_rank} frozen); trainable rank = min_dim - preserved_rank"
|
||||
)
|
||||
|
||||
model = get_peft_model(base_model, config)
|
||||
train_task(model, task_data[task_id])
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Effective Rank Selection**: Start with `effective_rank=None` (auto sets rank to 50% of the smaller weight dimension per target module) and adjust based on task complexity
|
||||
2. **Learning Rate**: Use smaller learning rates (1e-5 to 1e-4) compared to standard fine-tuning
|
||||
3. **Task Importance**: Use `rank_pattern` to allocate more capacity to critical modules
|
||||
4. **Model Architecture**: OSF works best with transformer architectures having clear attention and MLP separations
|
||||
5. **Capacity Planning**: For known task sequences, use progressive budget allocation (1/n, 2/n, ..., (n-1)/n freezing) to balance plasticity and stability
|
||||
|
||||
### Memory Considerations
|
||||
|
||||
OSF modifies weights in-place and doesn't add parameters, making it memory-efficient:
|
||||
|
||||
```python
|
||||
# Memory usage remains close to base model
|
||||
print(f"Base model parameters: {base_model.num_parameters():,}")
|
||||
print(f"OSF model parameters: {osf_model.num_parameters():,}") # Similar count
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Custom Target Modules
|
||||
|
||||
For models with non-standard architectures:
|
||||
|
||||
```python
|
||||
config = OSFConfig(
|
||||
target_modules=["dense", "intermediate.dense"], # Custom layer names
|
||||
effective_rank=12,
|
||||
rank_pattern={"dense": 8, "intermediate.dense": 16}
|
||||
)
|
||||
```
|
||||
|
||||
### Integration with Other Methods
|
||||
|
||||
OSF can be combined with other techniques:
|
||||
|
||||
```python
|
||||
# Use with gradient checkpointing for memory efficiency
|
||||
model.gradient_checkpointing_enable()
|
||||
|
||||
# Apply weight decay selectively (regularizes low-rank factors to limit drift/overfitting in continual updates; keep small)
|
||||
optimizer = torch.optim.AdamW([
|
||||
{"params": [p for n, p in model.named_parameters() if "U_low" in n], "weight_decay": 0.01},
|
||||
{"params": [p for n, p in model.named_parameters() if "S_low" in n], "weight_decay": 0.001},
|
||||
{"params": [p for n, p in model.named_parameters() if "V_low" in n], "weight_decay": 0.01},
|
||||
], lr=1e-4)
|
||||
```
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=OSF"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## OSFConfig
|
||||
|
||||
[[autodoc]] tuners.osf.config.OSFConfig
|
||||
|
||||
## OSFModel
|
||||
|
||||
[[autodoc]] tuners.osf.model.OSFModel
|
||||
|
||||
## Utility Functions
|
||||
|
||||
### Weight Decomposition
|
||||
|
||||
[[autodoc]] tuners.osf.utils.decompose_weight_matrix
|
||||
|
||||
[[autodoc]] tuners.osf.utils.reconstruct_weight_matrix
|
||||
|
||||
### Gradient Projection
|
||||
|
||||
[[autodoc]] tuners.osf.utils.project_gradient_to_orthogonal_space
|
||||
@@ -0,0 +1,70 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# P-tuning
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/p-tuning.png"/>
|
||||
</div>
|
||||
<small>Prompt tokens can be inserted anywhere in the input sequence, and they are optimized by a prompt encoder <a href="https://hf.co/papers/2103.10385">(image source)</a>.</small>
|
||||
|
||||
[P-tuning](https://hf.co/papers/2103.10385) is designed for natural language understanding (NLU) tasks and all language models.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*While GPTs with traditional fine-tuning fail to achieve strong results on natural language understanding (NLU), we show that GPTs can be better than or comparable to similar-sized BERTs on NLU tasks with a novel method P-tuning -- which employs trainable continuous prompt embeddings. On the knowledge probing (LAMA) benchmark, the best GPT recovers 64\% (P@1) of world knowledge without any additional text provided during test time, which substantially improves the previous best by 20+ percentage points. On the SuperGlue benchmark, GPTs achieve comparable and sometimes better performance to similar-sized BERTs in supervised learning. Importantly, we find that P-tuning also improves BERTs' performance in both few-shot and supervised settings while largely reducing the need for prompt engineering. Consequently, P-tuning outperforms the state-of-the-art approaches on the few-shot SuperGlue benchmark.*.
|
||||
|
||||
The method adds trainable prompt embeddings to the input that is optimized by a prompt encoder to find a better prompt, eliminating the need to manually design prompts. The prompt tokens can be added anywhere in the input sequence, and p-tuning also introduces anchor tokens for improving performance. A prompt encoder (a bidirectional long-short term memory network or LSTM) is used to optimize the prompt parameters. Unlike prefix tuning:
|
||||
|
||||
- the prompt tokens can be inserted anywhere in the input sequence, and it isn't restricted to only the beginning
|
||||
- the prompt tokens are only added to the input instead of adding them to every layer of the model
|
||||
- introducing *anchor* tokens can improve performance because they indicate characteristics of a component in the input sequence
|
||||
|
||||
The paper's results suggest that P-tuning is more efficient than manually crafting prompts, and it enables GPT-like models to compete with BERT-like models on NLU tasks.
|
||||
|
||||
## Usage
|
||||
|
||||
Create a [`PromptEncoderConfig`] with the task type, the number of virtual tokens to add and learn, and the hidden size of the encoder for learning the prompt parameters.
|
||||
|
||||
```py
|
||||
from peft import PromptEncoderConfig, get_peft_model
|
||||
|
||||
peft_config = PromptEncoderConfig(task_type="CAUSAL_LM", num_virtual_tokens=20, encoder_hidden_size=128)
|
||||
model = get_peft_model(model, peft_config)
|
||||
model.print_trainable_parameters()
|
||||
"trainable params: 300,288 || all params: 559,514,880 || trainable%: 0.05366935013417338"
|
||||
```
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=P_TUNING"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
|
||||
# API
|
||||
|
||||
## PromptEncoderConfig
|
||||
|
||||
[[autodoc]] tuners.p_tuning.config.PromptEncoderConfig
|
||||
|
||||
## PromptEncoder
|
||||
|
||||
[[autodoc]] tuners.p_tuning.model.PromptEncoder
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<!--Copyright 2026 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# PEANuT: Parameter-Efficient Adaptation with Weight-aware Neural Tweakers
|
||||
|
||||
[PEANuT](https://arxiv.org/abs/2410.01870) is a parameter-efficient fine-tuning technique that introduces
|
||||
weight-aware neural tweakers to generate adapter updates from the frozen pretrained weights themselves. Instead of
|
||||
learning a purely linear low-rank update as in LoRA, PEANuT conditions the adapter transformation on the base weight,
|
||||
which makes the update rule more expressive while keeping the number of trainable parameters small.
|
||||
|
||||
PEANuT uses an input projection `A`, an output projection `B`, and optional intermediate residual encoder/decoder
|
||||
pairs with non-linear activations. This makes it possible to model more complex update patterns than weight-agnostic
|
||||
linear adapters while still remaining within the PEFT setting.
|
||||
|
||||
PEANuT currently has the following tradeoffs:
|
||||
|
||||
Pros:
|
||||
- Higher theoretical expressiveness than linear low-rank updates.
|
||||
- Better performance than LoRA on a range of tasks under similar budgets.
|
||||
- Works well in very low-parameter regimes, for example around `0.2M` trainable parameters.
|
||||
|
||||
Cons:
|
||||
- Higher memory usage than LoRA, because `ΔW` is explicitly constructed before being applied.
|
||||
- Slower training and inference than LoRA, and deeper intermediate layers increase the overhead further.
|
||||
- The non-linearity can require more careful hyperparameter tuning, especially learning rate and related optimization settings.
|
||||
|
||||
If these tradeoffs do not fit your use case, consider other PEFT methods such as LoRA.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
> Fine-tuning large pre-trained foundation models often yields excellent downstream performance but is prohibitively expensive when updating all parameters. Parameter-efficient fine-tuning (PEFT) methods such as LoRA alleviate this by introducing lightweight update modules, yet they commonly rely on weight-agnostic linear approximations, limiting their expressiveness. In this work, we propose PEANuT, a novel PEFT framework that introduces weight-aware neural tweakers, compact neural modules that generate task-adaptive updates conditioned on frozen pre-trained weights. PEANuT provides a flexible yet efficient way to capture complex update patterns without full model tuning. We theoretically show that PEANuT achieves equivalent or greater expressivity than existing linear PEFT methods with comparable or fewer parameters. Extensive experiments across four benchmarks with over twenty datasets demonstrate that PEANuT consistently outperforms strong baselines in both NLP and vision tasks, while maintaining low computational overhead.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=PEANUT"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## PeanutConfig
|
||||
|
||||
[[autodoc]] tuners.peanut.config.PeanutConfig
|
||||
|
||||
## PeanutModel
|
||||
|
||||
[[autodoc]] tuners.peanut.model.PeanutModel
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<!--⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Models
|
||||
|
||||
[`PeftModel`] is the base model class for specifying the base Transformer model and configuration to apply a PEFT method to. The base `PeftModel` contains methods for loading and saving models from the Hub.
|
||||
|
||||
## PeftModel
|
||||
|
||||
[[autodoc]] PeftModel
|
||||
- all
|
||||
|
||||
## PeftModelForSequenceClassification
|
||||
|
||||
A `PeftModel` for sequence classification tasks.
|
||||
|
||||
[[autodoc]] PeftModelForSequenceClassification
|
||||
- all
|
||||
|
||||
## PeftModelForTokenClassification
|
||||
|
||||
A `PeftModel` for token classification tasks.
|
||||
|
||||
[[autodoc]] PeftModelForTokenClassification
|
||||
- all
|
||||
|
||||
## PeftModelForCausalLM
|
||||
|
||||
A `PeftModel` for causal language modeling.
|
||||
|
||||
[[autodoc]] PeftModelForCausalLM
|
||||
- all
|
||||
|
||||
## PeftModelForSeq2SeqLM
|
||||
|
||||
A `PeftModel` for sequence-to-sequence language modeling.
|
||||
|
||||
[[autodoc]] PeftModelForSeq2SeqLM
|
||||
- all
|
||||
|
||||
## PeftModelForQuestionAnswering
|
||||
|
||||
A `PeftModel` for question answering.
|
||||
|
||||
[[autodoc]] PeftModelForQuestionAnswering
|
||||
- all
|
||||
|
||||
## PeftModelForFeatureExtraction
|
||||
|
||||
A `PeftModel` for getting extracting features/embeddings from transformer models.
|
||||
|
||||
[[autodoc]] PeftModelForFeatureExtraction
|
||||
- all
|
||||
|
||||
## PeftMixedModel
|
||||
|
||||
A `PeftModel` for mixing different adapter types (e.g. LoRA and LoHa).
|
||||
|
||||
[[autodoc]] PeftMixedModel
|
||||
- all
|
||||
|
||||
## Utilities
|
||||
|
||||
[[autodoc]] utils.cast_mixed_precision_params
|
||||
|
||||
[[autodoc]] get_peft_model
|
||||
|
||||
[[autodoc]] inject_adapter_in_model
|
||||
|
||||
[[autodoc]] utils.get_peft_model_state_dict
|
||||
|
||||
[[autodoc]] utils.prepare_model_for_kbit_training
|
||||
|
||||
[[autodoc]] get_layer_status
|
||||
|
||||
[[autodoc]] get_model_status
|
||||
@@ -0,0 +1,27 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# PEFT types
|
||||
|
||||
[`PeftType`] includes the supported adapters in PEFT, and [`TaskType`] includes PEFT-supported tasks.
|
||||
|
||||
## PeftType
|
||||
|
||||
[[autodoc]] utils.peft_types.PeftType
|
||||
|
||||
## TaskType
|
||||
|
||||
[[autodoc]] utils.peft_types.TaskType
|
||||
@@ -0,0 +1,48 @@
|
||||
<!--Copyright 2024 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Polytropon
|
||||
|
||||
[Polytropon](https://hf.co/papers/2202.13914) is a multitask model with a number of different LoRA adapters in its "inventory". The model learns the correct combination of adapters from the inventory with a routing function to choose the best subset of modules for a specific task. PEFT also supports [Multi-Head Adapter Routing (MHR)](https://hf.co/papers/2211.03831) for Polytropon which builds on and improves the routing function by combining the adapter heads more granularly. The adapter heads are separated into disjoint blocks and a different routing function is learned for each one, allowing for more expressivity.
|
||||
|
||||
<hfoptions id="paper">
|
||||
<hfoption id="Combining Modular Skills in Multitask Learning">
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*A modular design encourages neural models to disentangle and recombine different facets of knowledge to generalise more systematically to new tasks. In this work, we assume that each task is associated with a subset of latent discrete skills from a (potentially small) inventory. In turn, skills correspond to parameter-efficient (sparse / low-rank) model parameterisations. By jointly learning these and a task-skill allocation matrix, the network for each task is instantiated as the average of the parameters of active skills. To favour non-trivial soft partitions of skills across tasks, we experiment with a series of inductive biases, such as an Indian Buffet Process prior and a two-speed learning rate. We evaluate our latent-skill model on two main settings: 1) multitask reinforcement learning for grounded instruction following on 8 levels of the BabyAI platform; and 2) few-shot adaptation of pre-trained text-to-text generative models on CrossFit, a benchmark comprising 160 NLP tasks. We find that the modular design of a network significantly increases sample efficiency in reinforcement learning and few-shot generalisation in supervised learning, compared to baselines with fully shared, task-specific, or conditionally generated parameters where knowledge is entangled across tasks. In addition, we show how discrete skills help interpretability, as they yield an explicit hierarchy of tasks.*
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Multi-Head Adapter Routing for Cross-Task Generalization">
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Parameter-efficient fine-tuning (PEFT) for cross-task generalization consists in pre-training adapters on a multi-task training set before few-shot adaptation to test tasks. Polytropon [Ponti et al., 2023] (Poly) jointly learns an inventory of adapters and a routing function that selects a (variable-size) subset of adapters for each task during both pre-training and few-shot adaptation. In this paper, we investigate the role that adapter routing plays in its success and design new variants based on our findings. First, we build on the intuition that finer-grained routing provides more expressivity. Hence, we propose MHR (Multi-Head Routing), which combines subsets of adapter parameters and outperforms Poly under a comparable parameter budget; by only fine-tuning the routing function and not the adapters (MHR-z), we achieve competitive performance with extreme parameter efficiency. Second, we find that Poly/MHR performance is a result of better multi-task optimization, rather than modular inductive biases that facilitate adapter recombination and local adaptation, as previously hypothesized. In fact, we find that MHR exhibits higher gradient alignment between tasks than any other method. Since this implies that routing is only crucial during multi-task pre-training, we propose MHR-mu, which discards routing and fine-tunes the average of the pre-trained adapters during few-shot adaptation. This establishes MHR-mu as an effective method for single-adapter fine-tuning.*.
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
In case you want to try out routing without training first, you can check out the [Arrow LoRA variant](./lora#Arrow).
|
||||
|
||||
# API
|
||||
|
||||
## PolyConfig
|
||||
|
||||
[[autodoc]] tuners.poly.config.PolyConfig
|
||||
|
||||
## PolyModel
|
||||
|
||||
[[autodoc]] tuners.poly.model.PolyModel
|
||||
@@ -0,0 +1,123 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Prefix tuning
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/prefix-tuning.png"/>
|
||||
</div>
|
||||
<small>Optimize the prefix parameters for each task <a href="https://hf.co/papers/2101.00190">(image source)</a>.</small>
|
||||
|
||||
[Prefix tuning](https://hf.co/papers/2101.00190) prefixes a series of task-specific vectors to the input sequence that can be learned while keeping the pretrained model frozen. The prefix parameters are inserted in all of the model layers.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Fine-tuning is the de facto way to leverage large pretrained language models to perform downstream tasks. However, it modifies all the language model parameters and therefore necessitates storing a full copy for each task. In this paper, we propose prefix-tuning, a lightweight alternative to fine-tuning for natural language generation tasks, which keeps language model parameters frozen, but optimizes a small continuous task-specific vector (called the prefix). Prefix-tuning draws inspiration from prompting, allowing subsequent tokens to attend to this prefix as if it were "virtual tokens". We apply prefix-tuning to GPT-2 for table-to-text generation and to BART for summarization. We find that by learning only 0.1\% of the parameters, prefix-tuning obtains comparable performance in the full data setting, outperforms fine-tuning in low-data settings, and extrapolates better to examples with topics unseen during training*.
|
||||
|
||||
**Note** For encoder-decoder models (seq2seq), the prefix is only applied to the decoder, which does not correspond to the paper specification (see e.g. Figure 2). Prefix tuning can still be fine-tuned on these model architectures but the performance could be sub-par; consider using other PEFT methods for encoder-decoder models.
|
||||
|
||||
Prefix tuning is very similar to [prompt tuning](../package_reference/prompt_tuning). The main difference is that the prefix parameters are inserted in **all** of the model layers, whereas prompt tuning only adds the prompt parameters to the model input embeddings. The prefix parameters are also optimized by a separate feed-forward network (FFN) instead of training directly on the soft prompts because it causes instability and hurts performance. The FFN is discarded after updating the soft prompts.
|
||||
|
||||
As a result, the authors found that prefix tuning demonstrates comparable performance to fully finetuning a model, despite having 1000x fewer parameters, and it performs even better in low-data settings.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Create a [`PrefixTuningConfig`] with the task type and number of virtual tokens to add and learn.
|
||||
|
||||
```py
|
||||
from peft import PrefixTuningConfig, get_peft_model
|
||||
|
||||
peft_config = PrefixTuningConfig(task_type="CAUSAL_LM", num_virtual_tokens=20)
|
||||
model = get_peft_model(model, peft_config)
|
||||
model.print_trainable_parameters()
|
||||
"trainable params: 983,040 || all params: 560,197,632 || trainable%: 0.1754809274167014"
|
||||
```
|
||||
|
||||
## Possible Initializations
|
||||
|
||||
By default, prefix tuning uses randomly initialized virtual tokens. There's also the option to initialize the vectors
|
||||
to be close to a no-op (initialized to zero, it will still shift the probability mass a bit).
|
||||
This means that the KV-cache injected prefixes have less impact from the beginning and reduces the variance in training
|
||||
performance.
|
||||
|
||||
PEFT also provides utilities to initialize a prefix-tuning adapter from an existing KV cache prefix (for example, from
|
||||
the first `p` tokens of a prompt/corpus). This is only supported when `prefix_projection=False` (the default), because
|
||||
in that case the learned parameters are the KV prefix itself.
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
|
||||
from peft import PrefixTuningConfig, get_peft_model, initialize_kv_prefix_from_text
|
||||
|
||||
base = AutoModelForCausalLM.from_pretrained("gpt2")
|
||||
tok = AutoTokenizer.from_pretrained("gpt2")
|
||||
|
||||
peft_cfg = PrefixTuningConfig(task_type="CAUSAL_LM", num_virtual_tokens=20, prefix_projection=False)
|
||||
model = get_peft_model(base, peft_cfg)
|
||||
|
||||
initialize_kv_prefix_from_text(
|
||||
model,
|
||||
tok,
|
||||
text="...a long context with at least num_virtual_tokens tokens...",
|
||||
use_chat_template=False,
|
||||
)m peft import PrefixTuningConfig, get_peft_model, initialize_kv_prefix_from_text
|
||||
|
||||
base = AutoModelForCausalLM.from_pretrained("gpt2")
|
||||
tok = AutoTokenizer.from_pretrained("gpt2")
|
||||
|
||||
peft_cfg = PrefixTuningConfig(task_type="CAUSAL_LM", num_virtual_tokens=20, prefix_projection=False)
|
||||
model = get_peft_model(base, peft_cfg)
|
||||
|
||||
initialize_kv_prefix_from_text(
|
||||
model,
|
||||
tok,
|
||||
text="...a long context with at least num_virtual_tokens tokens...",
|
||||
use_chat_template=False,
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Make sure the text is long enough to produce at least `num_virtual_tokens` tokens, otherwise initialization will fail.
|
||||
|
||||
As a guideline:
|
||||
|
||||
* start with a neutral starting sequence using `initialize_kv_prefix_from_text`, it can be a very short string like
|
||||
"Question: "
|
||||
* if that doesn't help, use a longer sequence with task relevance (i.e. an engineered prompt), giving you more virtual
|
||||
tokens to fit but also more steering of the model
|
||||
* if it is not possible to use an initialization text or you want to quickly check if prefix tuning is viable at all,
|
||||
use a zero init without projection
|
||||
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=PREFIX_TUNING"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
|
||||
# API
|
||||
|
||||
## PrefixTuningConfig
|
||||
|
||||
[[autodoc]] tuners.prefix_tuning.config.PrefixTuningConfig
|
||||
|
||||
## PrefixEncoder
|
||||
|
||||
[[autodoc]] tuners.prefix_tuning.model.PrefixEncoder
|
||||
@@ -0,0 +1,74 @@
|
||||
<!--Copyright 2023 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# Prompt tuning
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/prompt-tuning.png"/>
|
||||
</div>
|
||||
<small>Only train and store a significantly smaller set of task-specific prompt parameters <a href="https://hf.co/papers/2104.08691">(image source)</a>.</small>
|
||||
|
||||
[Prompt tuning](https://hf.co/papers/2104.08691) adds a task-specific, virtual prompt to the input that consists of trainable vectors in the embedding space. The virtual token parameters are updated independently of the pretrained model parameters which are frozen.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*In this work, we explore "prompt tuning", a simple yet effective mechanism for learning "soft prompts" to condition frozen language models to perform specific downstream tasks. Unlike the discrete text prompts used by GPT-3, soft prompts are learned through backpropagation and can be tuned to incorporate signal from any number of labeled examples. Our end-to-end learned approach outperforms GPT-3's "few-shot" learning by a large margin. More remarkably, through ablations on model size using T5, we show that prompt tuning becomes more competitive with scale: as models exceed billions of parameters, our method "closes the gap" and matches the strong performance of model tuning (where all model weights are tuned). This finding is especially relevant in that large models are costly to share and serve, and the ability to reuse one frozen model for multiple downstream tasks can ease this burden. Our method can be seen as a simplification of the recently proposed "prefix tuning" of Li and Liang (2021), and we provide a comparison to this and other similar approaches. Finally, we show that conditioning a frozen model with soft prompts confers benefits in robustness to domain transfer, as compared to full model tuning*.
|
||||
|
||||
In contrast to [prefix tuning](../package_reference/prefix_tuning), only the
|
||||
input of the first layer receives the virtual tokens.
|
||||
|
||||
## Usage
|
||||
|
||||
There are two decisions to take: how many virtual tokens are added to the
|
||||
input of the model (`num_virtual_tokens`) - this will define how many
|
||||
trainable parameters there will be - and how these tokens are initialized.
|
||||
|
||||
Create a [`PromptTuningConfig`] with the task type, the initial prompt tuning text to train the model with, the number of virtual tokens to add and learn, and a tokenizer.
|
||||
|
||||
```py
|
||||
from peft import PromptTuningConfig, PromptTuningInit, get_peft_model
|
||||
|
||||
prompt_tuning_init_text = "Classify if the tweet is a complaint or no complaint.\n"
|
||||
peft_config = PromptTuningConfig(
|
||||
task_type="CAUSAL_LM",
|
||||
prompt_tuning_init=PromptTuningInit.TEXT,
|
||||
num_virtual_tokens=len(tokenizer(prompt_tuning_init_text)["input_ids"]),
|
||||
prompt_tuning_init_text=prompt_tuning_init_text,
|
||||
tokenizer_name_or_path="bigscience/bloomz-560m",
|
||||
)
|
||||
model = get_peft_model(model, peft_config)
|
||||
model.print_trainable_parameters()
|
||||
"trainable params: 8,192 || all params: 559,222,784 || trainable%: 0.0014648902430985358"
|
||||
```
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=PROMPT_TUNING"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## PromptTuningConfig
|
||||
|
||||
[[autodoc]] tuners.prompt_tuning.config.PromptTuningConfig
|
||||
|
||||
## PromptEmbedding
|
||||
|
||||
[[autodoc]] tuners.prompt_tuning.model.PromptEmbedding
|
||||
@@ -0,0 +1,139 @@
|
||||
<!--Copyright 2026 The HuggingFace Team. 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# PSOFT
|
||||
|
||||
[PSOFT](https://hf.co/papers/2505.11235) is an Orthogonal Fine-Tuning (OFT)-based parameter-efficient fine-tuning method that preserves the geometric relationships of pre-trained weight column vectors while achieving a balanced trade-off between performance and multi-dimensional efficiency, including parameter count, memory usage, and computational cost. By restricting orthogonal transformations to a low-rank principal subspace derived from pre-trained weights, PSOFT bridges the gap between LoRA and OFT, providing both theoretical guarantees and practical adaptability. Its effectiveness is validated through extensive evaluations on diverse benchmarks, including GLUE, VTAB-1K, GSM8K, MATH, and commonsense reasoning benchmarks.
|
||||
|
||||
- Only `nn.Linear` layers are supported.
|
||||
- Quantized layers are not supported.
|
||||
|
||||
The abstract from the paper is:
|
||||
|
||||
*Driven by the rapid growth of model parameters, parameter-efficient fine-tuning (PEFT) has become essential for adapting large models to diverse downstream tasks under constrained computational resources. Within this paradigm, orthogonal fine-tuning and its variants preserve semantic representations of pre-trained models, but struggle to achieve both expressiveness and efficiency in terms of parameter counts, memory, and computation. To overcome this limitation, we propose efficient Orthogonal Fine-Tuning with Principal Subspace adaptation (PSOFT), which confines orthogonal transformations to the principal subspace of pre-trained weights. Specifically, PSOFT constructs this subspace via matrix decomposition to enable compatible transformations, establishes a theoretical condition that strictly maintains the geometry of this subspace for essential semantic preservation, and introduces efficient tunable vectors that gradually relax orthogonality during training to enhance adaptability. Extensive experiments on 35 NLP and CV tasks across four representative models demonstrate that PSOFT offers a practical and scalable solution to simultaneously achieve semantic preservation, expressiveness, and multi-dimensional efficiency in PEFT.*
|
||||
|
||||
|
||||
## How PSOFT Works
|
||||
|
||||
PSOFT decomposes each weight matrix $W_{pre}$ into $W_{pri}$ and $W_{res}$ using SVD:
|
||||
$W_{\text{pre}} = U S V^\top$
|
||||
|
||||
The principal subspace $W_{\text{pri}} = U_r S_r V_r^\top = AB$ is constructed from the top-$r$ singular components:
|
||||
|
||||
$W_{\text{pre}} = W_{\text{pri}} + W_{\text{res}} = AB + W_{\text{res}},$
|
||||
|
||||
|
||||
$W_{\text{ps-tuned}} = ARB + W_{\text{res}}.$ (PSOFT-SO: PSOFT with strict orthogonality)
|
||||
|
||||
|
||||
$W_{\text{ps-tuned}} = A \, \mathrm{diag}(\alpha) \, R \, \mathrm{diag}(\beta) \, B + W_{\text{res}}.$ (PSOFT-RO: PSOFT with relaxed orthogonality)
|
||||
|
||||
During training, $A$, $B$, and $W_{\text{res}}$ are frozen, and only $R$ (or $R$ with $\alpha$ and $\beta$) is trainable.
|
||||
|
||||
For compatibility with the PEFT framework (which expects additive weight updates), PSOFT is implemented in the following additive form:
|
||||
$W_{\text{ps-tuned}} = W_{\text{pre}} + A (R - I_r) B$
|
||||
|
||||
|
||||
## Trainable Parameters
|
||||
|
||||
After applying PSOFT:
|
||||
|
||||
- The original model weights ($A$, $B$, and $W_{\text{res}}$) are frozen.
|
||||
- Only the orthogonal matrix $R$ (and optionally $\alpha$, $\beta$) are trainable.
|
||||
- No additional bias parameters are introduced.
|
||||
|
||||
## Basic Usage
|
||||
```python
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
from peft import PsoftConfig, get_peft_model
|
||||
|
||||
# Load base model
|
||||
model_id = "facebook/opt-125m"
|
||||
model = AutoModelForCausalLM.from_pretrained(model_id)
|
||||
|
||||
# Configure PSOFT
|
||||
config = PsoftConfig(
|
||||
r=32, # the dimension of trainable matrix R,
|
||||
psoft_alpha=32, # scaling factor (typically set to r in PSOFT),
|
||||
target_modules=["q_proj", "v_proj"], # target attention projection layers
|
||||
ab_svd_init="psoft_init", # principal subspace initialization
|
||||
psoft_svd="full", # SVD method
|
||||
psoft_orth=True, # enable orthogonal R (Cayley parameterization)
|
||||
psoft_mag_a=True, # enable tunable vector alpha
|
||||
psoft_mag_b=True, # enable tunable vector beta
|
||||
use_cayley_neumann=False, # disable Cayley–Neumann approximation
|
||||
num_cayley_neumann_terms=5, # number of Neumann series terms
|
||||
cayley_neumann_eps=None, # improve numerical stability
|
||||
)
|
||||
|
||||
# Apply PSOFT
|
||||
model = get_peft_model(model, config)
|
||||
model.train()
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||||
tokenizer.pad_token = tokenizer.eos_token
|
||||
|
||||
# Train
|
||||
inputs = tokenizer("Hello world", return_tensors="pt", padding=True)
|
||||
loss = model(**inputs, labels=inputs["input_ids"]).loss
|
||||
loss.backward()
|
||||
|
||||
trainable = [p for p in model.parameters() if p.requires_grad]
|
||||
optimizer = torch.optim.AdamW(trainable, lr=5e-4)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad(set_to_none=True)
|
||||
```
|
||||
## Configuration Options
|
||||
|
||||
### Different Mode
|
||||
|
||||
(PSOFT-SO: PSOFT with strict orthogonality)
|
||||
|
||||
```python
|
||||
config = PsoftConfig(psoft_orth=True,psoft_mag_a=False,psoft_mag_b=False)
|
||||
```
|
||||
|
||||
(PSOFT-RO: PSOFT with relaxed orthogonality)
|
||||
```python
|
||||
config = PsoftConfig(psoft_orth=True,psoft_mag_a=True,psoft_mag_b=True)
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
1. **Rank Choice**: Smaller ranks (e.g., `32–128`) are suitable for simpler tasks, while larger ranks (e.g., `64–256`) provide greater expressiveness for more complex tasks at the cost of increased parameters and computation.
|
||||
2. **Scaling Factor**: The scaling factor is typically set to $r$ in PSOFT.
|
||||
3. **Learning Rate**: Use standard learning rates (e.g., `1e-4` to `5e-3`) for stable training.
|
||||
4. **SVD Initialization**: The `lowrank` option is more memory- and compute-efficient than `full`, making it more suitable for large models.
|
||||
5. **Cayley–Neumann Approximation**: When the rank is large, enabling the Cayley–Neumann approximation can significantly improve computational efficiency, while the benefit is less pronounced for small ranks. In practice, a small number of Neumann series terms (typically `5`) usually provides a good balance between accuracy and efficiency.
|
||||
|
||||
## Benchmark overview
|
||||
|
||||
<iframe
|
||||
src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=PSOFT"
|
||||
frameborder="0"
|
||||
width="850"
|
||||
height="1000"
|
||||
></iframe>
|
||||
|
||||
# API
|
||||
|
||||
## PsoftConfig
|
||||
|
||||
[[autodoc]] tuners.psoft.config.PsoftConfig
|
||||
|
||||
## PsoftModel
|
||||
|
||||
[[autodoc]] tuners.psoft.model.PsoftModel
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user