mirror of
https://github.com/huggingface/accelerate.git
synced 2025-11-13 21:59:16 +08:00
Compare commits
208 Commits
fork-teste
...
v1.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
| d8f314c1d6 | |||
| fbfa53bc5e | |||
| d09040dfc9 | |||
| 828aae4e32 | |||
| f0b030554c | |||
| 80973430ee | |||
| c67d47ae79 | |||
| 8c423cff79 | |||
| 95f34d6243 | |||
| ba90f85627 | |||
| b13aadcb67 | |||
| 58f14364d5 | |||
| 54370d4504 | |||
| d6d3e03cd4 | |||
| acfbf72a7f | |||
| 200c9eb783 | |||
| 7b2edc0bf2 | |||
| b92fb4774f | |||
| 3e62fbb09c | |||
| cb8b7c637a | |||
| aa16d69561 | |||
| f9a2e7902f | |||
| 51fd482d6e | |||
| 60461ff7c4 | |||
| f8c77f0522 | |||
| b626ef5f00 | |||
| dd68af886a | |||
| 11818e657b | |||
| 1f508a6df6 | |||
| 4a100eef43 | |||
| c6f34a060f | |||
| 29be478862 | |||
| e11d3ceff3 | |||
| 08101b9dde | |||
| 5f96369161 | |||
| 069743775e | |||
| 77f2b6235e | |||
| d7b1b368e9 | |||
| 8ad2b3b8e7 | |||
| e724c9a97f | |||
| cf169a1ae6 | |||
| 8ade23cc6a | |||
| c0552c9012 | |||
| bf4572b6ce | |||
| a4a44aca1f | |||
| b0e5fd353c | |||
| 8159c98d43 | |||
| 497eb3cf86 | |||
| 87732a4c32 | |||
| ffbca15979 | |||
| ba7ab93f5e | |||
| 85f35647db | |||
| 2f39575bbd | |||
| 1ace241db4 | |||
| 78e1bdd088 | |||
| 4dda5797bd | |||
| 1f4fbb77a2 | |||
| c809f8e45c | |||
| 39dc2b120f | |||
| 735dfa3018 | |||
| a84327e596 | |||
| 292954b547 | |||
| 0e61127b5a | |||
| 6f79b63b86 | |||
| 1d2ca747f1 | |||
| cba3f2d5e0 | |||
| f1f2b4d1a8 | |||
| fd9880da91 | |||
| 21c994c298 | |||
| 52581c3f01 | |||
| f4ee5a2dc7 | |||
| 55136b8dc4 | |||
| fb68cb9d0e | |||
| 506d732230 | |||
| ae9cb6e4db | |||
| 127818fc27 | |||
| bcc13c00b5 | |||
| d4d6b6e7f5 | |||
| 1077611552 | |||
| cd93e35e08 | |||
| e93b056687 | |||
| 5060574827 | |||
| 018a99e5f6 | |||
| 4305033f80 | |||
| 4617be3760 | |||
| 521eb5bee4 | |||
| 9f9951325c | |||
| e9e5a73fcc | |||
| 79a8426416 | |||
| 8a43837cc9 | |||
| a768b2b753 | |||
| 85b1a03552 | |||
| fc52fa969e | |||
| 3a670bd0da | |||
| b32d8bcb75 | |||
| d5b7b70e06 | |||
| 1ce2eb6385 | |||
| 3fd02e60dc | |||
| ed9a574564 | |||
| 7d3bbe721b | |||
| 4b4c036933 | |||
| e7e01812df | |||
| 5ad982ac51 | |||
| 9d67867ad9 | |||
| 52b3421d8f | |||
| f1ca8ac78f | |||
| ab89fc7e1d | |||
| b5235f21d8 | |||
| 8931e5e48c | |||
| a84859242d | |||
| 758d6243a7 | |||
| b07ad2adf2 | |||
| 1d09a20fc1 | |||
| 3fcc9461c4 | |||
| 939ce400cb | |||
| c2120927b0 | |||
| 654e1d9984 | |||
| 8c3aded21a | |||
| 2789933938 | |||
| 726140cad2 | |||
| 2d4f1dda7e | |||
| c0cf860dc6 | |||
| ad3f574a3b | |||
| 1a6af0bd6d | |||
| 52fae0960c | |||
| 7ffe7662ca | |||
| 5536a3a893 | |||
| 7ec8eab955 | |||
| 589fddd317 | |||
| 99c69aaf73 | |||
| 00785cd9fc | |||
| a452327e8e | |||
| 851cf34351 | |||
| cd5698bb32 | |||
| 90d5023901 | |||
| 3bde615607 | |||
| dc3b5ad82e | |||
| 12a5befdd6 | |||
| 79ca85c27d | |||
| 13d93c4f50 | |||
| d982751aec | |||
| 95edc68cb3 | |||
| 288accc0ec | |||
| 83b0610155 | |||
| 386f7d2825 | |||
| 308a8e9689 | |||
| f35cbd1f02 | |||
| a14260c9da | |||
| 32f368ec3f | |||
| 415eddf1be | |||
| 230857691a | |||
| a5a3e57125 | |||
| 0af1d8b8de | |||
| d16d7371a1 | |||
| 7a5c231b9e | |||
| 4f02bb764a | |||
| 709fd1e42b | |||
| f4f1260a0e | |||
| c6da9f8693 | |||
| 3ebbe573ad | |||
| 24bf5ec546 | |||
| e1247de01e | |||
| 12a007d559 | |||
| 5bdcd7e169 | |||
| 2471eacdd6 | |||
| 167cb5eb20 | |||
| 947f64ee62 | |||
| 8330b375d4 | |||
| 92404fbf5f | |||
| 3a02754915 | |||
| fec1170e35 | |||
| eac206f063 | |||
| 6882ff2bea | |||
| 57a4c7465e | |||
| 404510a5ec | |||
| 3086e26db9 | |||
| 5d5d07abfc | |||
| 5a0b7dc597 | |||
| c799c198e9 | |||
| 1f7a79b428 | |||
| 4cc3530b64 | |||
| 5d4a3beb01 | |||
| 0284f9a9f6 | |||
| 573d22d48f | |||
| 13ca7dccb6 | |||
| 3b5a00e048 | |||
| 3c4eaedd46 | |||
| c0faec766c | |||
| 91a2599f93 | |||
| 5f9235a731 | |||
| 7a36a75c7c | |||
| f62854a281 | |||
| a9869ea0dc | |||
| 6d59614603 | |||
| 2d74c0c077 | |||
| 40007b4e97 | |||
| 7141881b1f | |||
| f0049b2cfb | |||
| 83bad87559 | |||
| 24d8b63fc3 | |||
| 4a83ee5382 | |||
| 05d240af95 | |||
| bad2ce42ed | |||
| 30cb7ece76 | |||
| b7fa2fa956 | |||
| d5d378d64e | |||
| 065e74d11a | |||
| 86b6deaea1 |
@ -21,7 +21,8 @@ jobs:
|
||||
|
||||
version-cpu:
|
||||
name: "Latest Accelerate CPU [version]"
|
||||
runs-on: [self-hosted, intel-cpu, 8-cpu, ci]
|
||||
runs-on:
|
||||
group: aws-general-8-plus
|
||||
needs: get-version
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
@ -41,7 +42,8 @@ jobs:
|
||||
|
||||
version-cuda:
|
||||
name: "Latest Accelerate GPU [version]"
|
||||
runs-on: [self-hosted, single-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
needs: get-version
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
@ -61,7 +63,8 @@ jobs:
|
||||
|
||||
version-cuda-deepspeed:
|
||||
name: "Latest Accelerate GPU DeepSpeed [version]"
|
||||
runs-on: [self-hosted, single-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
needs: get-version
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
@ -79,3 +82,23 @@ jobs:
|
||||
push: true
|
||||
tags: huggingface/accelerate:gpu-deepspeed-release-${{needs.get-version.outputs.version}}
|
||||
|
||||
version-cuda-fp8-transformerengine:
|
||||
name: "Latest Accelerate GPU FP8 TransformerEngine [version]"
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
needs: get-version
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Build and Push GPU
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: docker/accelerate-gpu/Dockerfile
|
||||
push: true
|
||||
tags: huggingface/accelerate:gpu-fp8-transformerengine-release-${{needs.get-version.outputs.version}}
|
||||
37
.github/workflows/build_docker_images.yml
vendored
37
.github/workflows/build_docker_images.yml
vendored
@ -13,7 +13,8 @@ concurrency:
|
||||
jobs:
|
||||
latest-cpu:
|
||||
name: "Latest Accelerate CPU [dev]"
|
||||
runs-on: [self-hosted, intel-cpu, 8-cpu, ci]
|
||||
runs-on:
|
||||
group: aws-general-8-plus
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
@ -29,7 +30,7 @@ jobs:
|
||||
- name: Build and Push CPU
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: docker/accelerate-cpu/Dockerfile
|
||||
file: docker/accelerate-cpu/Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
huggingface/accelerate:cpu-nightly
|
||||
@ -37,7 +38,8 @@ jobs:
|
||||
|
||||
latest-cuda:
|
||||
name: "Latest Accelerate GPU [dev]"
|
||||
runs-on: [self-hosted, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
@ -53,7 +55,7 @@ jobs:
|
||||
- name: Build and Push GPU
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: docker/accelerate-gpu/Dockerfile
|
||||
file: docker/accelerate-gpu/Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
huggingface/accelerate:gpu-nightly
|
||||
@ -61,7 +63,8 @@ jobs:
|
||||
|
||||
latest-cuda-deepspeed:
|
||||
name: "Latest Accelerate GPU DeepSpeed [dev]"
|
||||
runs-on: [self-hosted, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
@ -77,9 +80,31 @@ jobs:
|
||||
- name: Build and Push GPU
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: docker/accelerate-gpu-deepspeed/Dockerfile
|
||||
file: docker/accelerate-gpu-deepspeed/Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
huggingface/accelerate:gpu-deepspeed-nightly
|
||||
huggingface/accelerate:gpu-deepspeed-nightly-${{ env.date }}
|
||||
|
||||
latest-cuda-fp8-transformerengine:
|
||||
name: "Latest Accelerate GPU FP8 TransformerEngine [dev]"
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: |
|
||||
echo "date=$(date '+%Y-%m-%d')" >> $GITHUB_ENV
|
||||
- name: Build and Push GPU
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: benchmarks/fp8/transformer_engine/Dockerfile
|
||||
push: true
|
||||
tags: huggingface/accelerate:gpu-fp8-transformerengine-nightly-${{ env.date }}
|
||||
6
.github/workflows/integration_tests.yml
vendored
6
.github/workflows/integration_tests.yml
vendored
@ -27,10 +27,12 @@ jobs:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- name: Set up python 3.8
|
||||
- name: Set up python 3.9
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'setup.py'
|
||||
|
||||
- name: Install Accelerate from source
|
||||
run: |
|
||||
|
||||
22
.github/workflows/nightly.yml
vendored
22
.github/workflows/nightly.yml
vendored
@ -13,7 +13,8 @@ env:
|
||||
|
||||
jobs:
|
||||
run_core_tests_single_gpu:
|
||||
runs-on: [self-hosted, single-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: "0"
|
||||
TEST_TYPE: "single_gpu"
|
||||
@ -43,7 +44,7 @@ jobs:
|
||||
run: |
|
||||
source activate accelerate
|
||||
make test
|
||||
|
||||
|
||||
- name: Run examples on GPUs
|
||||
working-directory: accelerate
|
||||
if: always()
|
||||
@ -51,7 +52,7 @@ jobs:
|
||||
source activate accelerate
|
||||
pip uninstall comet_ml -y
|
||||
make test_examples
|
||||
|
||||
|
||||
- name: Generate Report
|
||||
working-directory: accelerate
|
||||
if: always()
|
||||
@ -60,7 +61,8 @@ jobs:
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
run_deepspeed_tests_single_gpu:
|
||||
runs-on: [self-hosted, single-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: "0"
|
||||
TEST_TYPE: "single_gpu_deepspeed"
|
||||
@ -105,7 +107,7 @@ jobs:
|
||||
source activate accelerate
|
||||
pip uninstall comet_ml -y
|
||||
make test_examples
|
||||
|
||||
|
||||
- name: Generate Report
|
||||
working-directory: accelerate
|
||||
if: always()
|
||||
@ -114,7 +116,8 @@ jobs:
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
run_core_tests_multi_gpu:
|
||||
runs-on: [self-hosted, multi-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-12xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: "0,1"
|
||||
TEST_TYPE: "multi_gpu"
|
||||
@ -170,7 +173,8 @@ jobs:
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
run_deepspeed_tests_multi_gpu:
|
||||
runs-on: [self-hosted, multi-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-12xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: "0,1"
|
||||
TEST_TYPE: "multi_gpu_deepspeed"
|
||||
@ -223,7 +227,7 @@ jobs:
|
||||
pip install slack_sdk tabulate
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
|
||||
|
||||
run-integration-tests:
|
||||
if: always()
|
||||
uses: ./.github/workflows/self_hosted_integration_tests.yml
|
||||
uses: ./.github/workflows/self_hosted_integration_tests.yml
|
||||
|
||||
6
.github/workflows/quality.yml
vendored
6
.github/workflows/quality.yml
vendored
@ -7,10 +7,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'setup.py'
|
||||
- name: Install Python dependencies
|
||||
run: pip install -e .[quality]
|
||||
- name: Run Quality check
|
||||
|
||||
18
.github/workflows/run_merge_tests.yml
vendored
18
.github/workflows/run_merge_tests.yml
vendored
@ -10,7 +10,8 @@ env:
|
||||
|
||||
jobs:
|
||||
run_core_tests_single_gpu:
|
||||
runs-on: [self-hosted, single-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: "0"
|
||||
container:
|
||||
@ -39,7 +40,7 @@ jobs:
|
||||
run: |
|
||||
source activate accelerate;
|
||||
make test_cli
|
||||
|
||||
|
||||
- name: Run test on GPUs
|
||||
working-directory: accelerate
|
||||
if: always()
|
||||
@ -62,7 +63,8 @@ jobs:
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
run_deepspeed_tests_single_gpu:
|
||||
runs-on: [self-hosted, single-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-4xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: "0"
|
||||
container:
|
||||
@ -85,7 +87,7 @@ jobs:
|
||||
run: |
|
||||
source activate accelerate;
|
||||
pip freeze
|
||||
|
||||
|
||||
- name: Run test on GPUs
|
||||
working-directory: accelerate
|
||||
if: always()
|
||||
@ -101,7 +103,8 @@ jobs:
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
run_core_tests_multi_gpu:
|
||||
runs-on: [self-hosted, multi-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-12xlarge-plus
|
||||
env:
|
||||
CUDA_VISIBLE_DEVICES: 0,1
|
||||
container:
|
||||
@ -147,7 +150,8 @@ jobs:
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
run_deepspeed_tests_multi_gpu:
|
||||
runs-on: [self-hosted, multi-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-12xlarge-plus
|
||||
container:
|
||||
image: huggingface/accelerate:gpu-deepspeed-nightly
|
||||
options: --gpus all --shm-size "16gb"
|
||||
@ -181,4 +185,4 @@ jobs:
|
||||
if: always()
|
||||
run: |
|
||||
pip install tabulate;
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# CI for specifically ensuring integrations work fine (`transformers` mainly) on GPUs
|
||||
# Useful tips:
|
||||
# - `working-directory` should be set to the root of the repo, which is cloned on the actual CI runner.
|
||||
# It follows the directory structure of `actions-runner/_work/{repo_name}/{repo_name}/{cloned_repo} on
|
||||
# It follows the directory structure of `actions-runner/_work/{repo_name}/{repo_name}/{cloned_repo} on
|
||||
# prem, but in Actions setting `working-directory` looks just in the `{repo_name}` level.
|
||||
# - New integrations to test should have its own job, and follow a strategy method where we check both
|
||||
# the pypi and github versions.
|
||||
@ -25,12 +25,13 @@ jobs:
|
||||
container:
|
||||
image: huggingface/accelerate:gpu-deepspeed-nightly
|
||||
options: --gpus all --shm-size "16gb"
|
||||
runs-on: [self-hosted, multi-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-12xlarge-plus
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cuda_visible_devices: [
|
||||
"0",
|
||||
"0",
|
||||
"0,1"
|
||||
]
|
||||
steps:
|
||||
@ -51,7 +52,7 @@ jobs:
|
||||
pip install -e .[testing];
|
||||
pip uninstall comet_ml wandb dvclive -y
|
||||
cd ..;
|
||||
|
||||
|
||||
- name: Show installed libraries
|
||||
run: |
|
||||
source activate accelerate;
|
||||
@ -90,12 +91,13 @@ jobs:
|
||||
container:
|
||||
image: huggingface/accelerate:gpu-nightly
|
||||
options: --gpus all --shm-size "16gb"
|
||||
runs-on: [self-hosted, multi-gpu, nvidia-gpu, t4, ci]
|
||||
runs-on:
|
||||
group: aws-g6-12xlarge-plus
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Install accelerate
|
||||
run:
|
||||
run:
|
||||
source activate accelerate;
|
||||
git clone https://github.com/huggingface/accelerate;
|
||||
cd accelerate;
|
||||
@ -122,4 +124,4 @@ jobs:
|
||||
working-directory: skorch/
|
||||
run: |
|
||||
source activate accelerate;
|
||||
pytest -sv -k TestAccelerate
|
||||
pytest -sv -k TestAccelerate
|
||||
|
||||
9
.github/workflows/stale.yml
vendored
9
.github/workflows/stale.yml
vendored
@ -10,6 +10,9 @@ jobs:
|
||||
name: Close Stale Issues
|
||||
if: github.repository == 'huggingface/accelerate'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
@ -18,11 +21,13 @@ jobs:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'setup.py'
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
pip install PyGithub
|
||||
- name: Close stale issues
|
||||
run: |
|
||||
python utils/stale.py
|
||||
python utils/stale.py
|
||||
|
||||
10
.github/workflows/test.yml
vendored
10
.github/workflows/test.yml
vendored
@ -39,17 +39,19 @@ jobs:
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- name: Set up python 3.8
|
||||
- name: Set up python 3.9
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'setup.py'
|
||||
|
||||
- name: Install the library
|
||||
run: |
|
||||
if [[ ${{ matrix.test-kind }} = test_prod ]]; then pip install -e .[test_prod]; fi
|
||||
if [[ ${{ matrix.test-kind }} != test_prod ]]; then pip install -e .[testing,test_trackers]; fi
|
||||
if [[ ${{ matrix.test-kind }} = test_rest ]]; then pip uninstall comet_ml -y; fi
|
||||
if [[ ${{ matrix.test-kind }} = minimum ]]; then pip install torch==1.10.0; fi
|
||||
if [[ ${{ matrix.pytorch-version }} = minimum ]]; then pip install torchvision==0.18.1 torch==2.3.1; fi
|
||||
pip install pytest-reportlog tabulate setuptools
|
||||
|
||||
- name: Show installed libraries
|
||||
@ -65,4 +67,4 @@ jobs:
|
||||
- name: Generate Report
|
||||
if: always()
|
||||
run: |
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
55
.github/workflows/test_imports.yml
vendored
Normal file
55
.github/workflows/test_imports.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
name: Run Import Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "src/**"
|
||||
- "tests/**"
|
||||
- ".github/**"
|
||||
- "examples/**"
|
||||
- "setup.py"
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
env:
|
||||
HF_HOME: ~/hf_cache
|
||||
TESTING_MOCKED_DATALOADERS: "1"
|
||||
IS_GITHUB_CI: "1"
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pytorch-version: [
|
||||
latest,
|
||||
minimum,
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- name: Set up python 3.9
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'setup.py'
|
||||
|
||||
- name: Install the library
|
||||
run: |
|
||||
pip install -e .
|
||||
pip install pytest-reportlog tabulate setuptools git+https://github.com/muellerzr/import-timer
|
||||
|
||||
- name: Show installed libraries
|
||||
run: |
|
||||
pip freeze
|
||||
|
||||
- name: Run Import Tests
|
||||
env:
|
||||
PYTORCH_VERSION: ${{ matrix.pytorch-version }}
|
||||
run: |
|
||||
pytest -sv tests/test_imports.py
|
||||
|
||||
- name: Generate Report
|
||||
if: always()
|
||||
run: |
|
||||
python utils/log_reports.py >> $GITHUB_STEP_SUMMARY
|
||||
15
.github/workflows/trufflehog.yml
vendored
Normal file
15
.github/workflows/trufflehog.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
on:
|
||||
push:
|
||||
|
||||
name: Secret Leaks
|
||||
|
||||
jobs:
|
||||
trufflehog:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Secret Scanning
|
||||
uses: trufflesecurity/trufflehog@main
|
||||
@ -123,12 +123,15 @@ Follow these steps to start contributing:
|
||||
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 ".[quality]"
|
||||
$ pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
This will install all testing and linting/code quality dependencies for the library (see `quality`, `test_dev`,
|
||||
`test_prod` targets in [`setup.py`](./setup.py)).
|
||||
|
||||
(If accelerate was already installed in the virtual environment, remove
|
||||
it with `pip uninstall accelerate` before reinstalling it in editable
|
||||
mode with the `-e` flag.)
|
||||
mode with the `-e` flag).
|
||||
|
||||
Alternatively, if you are using [Visual Studio Code](https://code.visualstudio.com/Download), the fastest way to get set up is by using
|
||||
the provided Dev Container. Documentation on how to get started with dev containers is available [here](https://code.visualstudio.com/docs/remote/containers).
|
||||
|
||||
24
README.md
24
README.md
@ -22,22 +22,12 @@ limitations under the License.
|
||||
|
||||
<p align="center">
|
||||
<!-- Uncomment when CircleCI is set up
|
||||
<a href="https://circleci.com/gh/huggingface/accelerate">
|
||||
<img alt="Build" src="https://img.shields.io/circleci/build/github/huggingface/transformers/master">
|
||||
</a>
|
||||
<a href="https://circleci.com/gh/huggingface/accelerate"><img alt="Build" src="https://img.shields.io/circleci/build/github/huggingface/transformers/master"></a>
|
||||
-->
|
||||
<a href="https://github.com/huggingface/accelerate/blob/main/LICENSE">
|
||||
<img alt="License" src="https://img.shields.io/github/license/huggingface/accelerate.svg?color=blue">
|
||||
</a>
|
||||
<a href="https://huggingface.co/docs/accelerate/index.html">
|
||||
<img alt="Documentation" src="https://img.shields.io/website/http/huggingface.co/docs/accelerate/index.html.svg?down_color=red&down_message=offline&up_message=online">
|
||||
</a>
|
||||
<a href="https://github.com/huggingface/accelerate/releases">
|
||||
<img alt="GitHub release" src="https://img.shields.io/github/release/huggingface/accelerate.svg">
|
||||
</a>
|
||||
<a href="https://github.com/huggingface/accelerate/blob/main/CODE_OF_CONDUCT.md">
|
||||
<img alt="Contributor Covenant" src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg">
|
||||
</a>
|
||||
<a href="https://github.com/huggingface/accelerate/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/huggingface/accelerate.svg?color=blue"></a>
|
||||
<a href="https://huggingface.co/docs/accelerate/index.html"><img alt="Documentation" src="https://img.shields.io/website/http/huggingface.co/docs/accelerate/index.html.svg?down_color=red&down_message=offline&up_message=online"></a>
|
||||
<a href="https://github.com/huggingface/accelerate/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/huggingface/accelerate.svg"></a>
|
||||
<a href="https://github.com/huggingface/accelerate/blob/main/CODE_OF_CONDUCT.md"><img alt="Contributor Covenant" src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg"></a>
|
||||
</p>
|
||||
|
||||
<h3 align="center">
|
||||
@ -167,6 +157,8 @@ accelerate launch --multi_gpu --num_processes 2 examples/nlp_example.py
|
||||
|
||||
To learn more, check the CLI documentation available [here](https://huggingface.co/docs/accelerate/package_reference/cli).
|
||||
|
||||
Or view the configuration zoo [here](https://github.com/huggingface/accelerate/blob/main/examples/config_yaml_templates/)
|
||||
|
||||
## Launching multi-CPU run using MPI
|
||||
|
||||
🤗 Here is another way to launch multi-CPU run using MPI. You can learn how to install Open MPI on [this page](https://www.open-mpi.org/faq/?category=building#easy-build). You can use Intel MPI or MVAPICH as well.
|
||||
@ -266,7 +258,7 @@ pip install accelerate
|
||||
- multi-GPU on several nodes (machines)
|
||||
- TPU
|
||||
- FP16/BFloat16 mixed precision
|
||||
- FP8 mixed precision with [Transformer Engine](https://github.com/NVIDIA/TransformerEngine)
|
||||
- FP8 mixed precision with [Transformer Engine](https://github.com/NVIDIA/TransformerEngine) or [MS-AMP](https://github.com/Azure/MS-AMP/)
|
||||
- DeepSpeed support (Experimental)
|
||||
- PyTorch Fully Sharded Data Parallel (FSDP) support (Experimental)
|
||||
- Megatron-LM support (Experimental)
|
||||
|
||||
@ -1,46 +1,5 @@
|
||||
# Big model inference benchmarks
|
||||
# Benchmarks
|
||||
|
||||
Running inference with Accelerate on big models.
|
||||
The folders below contain suites to test various functionalities in Accelerate.
|
||||
|
||||
## Setup
|
||||
|
||||
These benchmarks use the `transformers` library:
|
||||
|
||||
```bash
|
||||
pip install transformers
|
||||
```
|
||||
|
||||
To reproduce or test a new setup, run
|
||||
|
||||
```py
|
||||
python inference_acc.py model_name
|
||||
```
|
||||
|
||||
This script supports `gpt-j-6b`, `gpt-neox`, `opt` (30B version) and `T0pp` out of the box, but you can specify any valid checkpoint for `model_name`.
|
||||
|
||||
To force a different `torch_dtype` than the one in the config: `--torch_dtype xxx`.
|
||||
|
||||
If you get an error linked to disk offload, you need to add the option `--disk-offload`
|
||||
|
||||
## Results
|
||||
|
||||
On a setup with two Titan RTXs (24GB of RAM) and 32GB of RAM, we get the following benchmarks (T0pp does not run in float16, which is why it's not included).
|
||||
|
||||
| Model | Model load time | Generation time | dtype | GPU 0 use | GPU 1 use | CPU use | Disk offload |
|
||||
|:-----:|:---------------:|:---------------:|:-----:|:---------:|:---------:|:-------:|:------------:|
|
||||
| GPT-J-6B | 8.7s | 0.05s per token | float16 | 11.7GB | 0GB | 0GB | no |
|
||||
| GPT-J-6B | 12.4s | 0.06s per token | float32 | 21.9GB | 1.5GB | 0GB | no |
|
||||
| GPT-Neo-X-20B | 30.9s | 0.08s per token | float16 | 21.5GB | 18GB | 0GB | no |
|
||||
| GPT-Neo-X-20B | 78.2s | 10.72s per token | float32 | 20.3GB | 22.7 GB | 24.4GB | yes |
|
||||
| T0pp (11B) | 29.4s | 0.05s per token | float32 | 21.1GB | 21.3GB | 0GB | no |
|
||||
| OPT-30B | 34.5s | 2.37s per token | float16 | 20.7GB | 22.3GB | 14.1GB | no |
|
||||
| OPT-30B | 112.3s | 33.9s per token | float32 | 20.2GB | 21.2GB | 23.5GB | yes |
|
||||
|
||||
Note on the results:
|
||||
- using two GPUs instead of one does not slow down generation
|
||||
- using CPU offload slows down a bit (see OPT-30b)
|
||||
- using disk offload slows down a lot (need to implement prefetching)
|
||||
|
||||
You will also note that Accelerate does not use anymore GPU and CPU RAM than necessary:
|
||||
- peak GPU memory is exactly the size of the model put on a given GPU
|
||||
- peak CPU memory is either the size of the biggest checkpoint shard or the part of the model offloaded on CPU, whichever is bigger.
|
||||
See their relevant README.md's for more information.
|
||||
|
||||
46
benchmarks/big_model_inference/README.md
Normal file
46
benchmarks/big_model_inference/README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Big model inference benchmarks
|
||||
|
||||
Running inference with Accelerate on big models.
|
||||
|
||||
## Setup
|
||||
|
||||
These benchmarks use the `transformers` library:
|
||||
|
||||
```bash
|
||||
pip install transformers
|
||||
```
|
||||
|
||||
To reproduce or test a new setup, run
|
||||
|
||||
```py
|
||||
python inference_acc.py model_name
|
||||
```
|
||||
|
||||
This script supports `gpt-j-6b`, `gpt-neox`, `opt` (30B version) and `T0pp` out of the box, but you can specify any valid checkpoint for `model_name`.
|
||||
|
||||
To force a different `torch_dtype` than the one in the config: `--torch_dtype xxx`.
|
||||
|
||||
If you get an error linked to disk offload, you need to add the option `--disk-offload`
|
||||
|
||||
## Results
|
||||
|
||||
On a setup with two Titan RTXs (24GB of RAM) and 32GB of RAM, we get the following benchmarks (T0pp does not run in float16, which is why it's not included).
|
||||
|
||||
| Model | Model load time | Generation time | dtype | GPU 0 use | GPU 1 use | CPU use | Disk offload |
|
||||
|:-----:|:---------------:|:---------------:|:-----:|:---------:|:---------:|:-------:|:------------:|
|
||||
| GPT-J-6B | 8.7s | 0.05s per token | float16 | 11.7GB | 0GB | 0GB | no |
|
||||
| GPT-J-6B | 12.4s | 0.06s per token | float32 | 21.9GB | 1.5GB | 0GB | no |
|
||||
| GPT-Neo-X-20B | 30.9s | 0.08s per token | float16 | 21.5GB | 18GB | 0GB | no |
|
||||
| GPT-Neo-X-20B | 78.2s | 10.72s per token | float32 | 20.3GB | 22.7 GB | 24.4GB | yes |
|
||||
| T0pp (11B) | 29.4s | 0.05s per token | float32 | 21.1GB | 21.3GB | 0GB | no |
|
||||
| OPT-30B | 34.5s | 2.37s per token | float16 | 20.7GB | 22.3GB | 14.1GB | no |
|
||||
| OPT-30B | 112.3s | 33.9s per token | float32 | 20.2GB | 21.2GB | 23.5GB | yes |
|
||||
|
||||
Note on the results:
|
||||
- using two GPUs instead of one does not slow down generation
|
||||
- using CPU offload slows down a bit (see OPT-30b)
|
||||
- using disk offload slows down a lot (need to implement prefetching)
|
||||
|
||||
You will also note that Accelerate does not use anymore GPU and CPU RAM than necessary:
|
||||
- peak GPU memory is exactly the size of the model put on a given GPU
|
||||
- peak CPU memory is either the size of the biggest checkpoint shard or the part of the model offloaded on CPU, whichever is bigger.
|
||||
12
benchmarks/fp8/ms_amp/Dockerfile
Normal file
12
benchmarks/fp8/ms_amp/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM ghcr.io/azure/msamp
|
||||
|
||||
RUN pip install transformers evaluate datasets
|
||||
RUN git clone https://github.com/huggingface/accelerate
|
||||
|
||||
RUN cd accelerate && \
|
||||
pip install -e . && \
|
||||
cd benchmarks/fp8
|
||||
|
||||
CMD ["bash"]
|
||||
|
||||
|
||||
123
benchmarks/fp8/ms_amp/ddp.py
Normal file
123
benchmarks/fp8/ms_amp/ddp.py
Normal file
@ -0,0 +1,123 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
|
||||
"""
|
||||
This script tests to ensure that `accelerate` performs at the same level as raw `MS-AMP`.
|
||||
|
||||
This particular script verifies this for DDP training.
|
||||
"""
|
||||
|
||||
import evaluate
|
||||
import msamp
|
||||
import torch
|
||||
from fp8_utils import evaluate_model, get_training_utilities
|
||||
from torch.nn.parallel import DistributedDataParallel as DDP
|
||||
|
||||
from accelerate import Accelerator
|
||||
from accelerate.state import AcceleratorState
|
||||
from accelerate.utils import FP8RecipeKwargs, get_grad_scaler, set_seed
|
||||
|
||||
|
||||
MODEL_NAME = "bert-base-cased"
|
||||
METRIC = evaluate.load("glue", "mrpc")
|
||||
|
||||
|
||||
def train_baseline(opt_level="O2"):
|
||||
set_seed(42)
|
||||
scaler = get_grad_scaler()
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(MODEL_NAME)
|
||||
accelerator = Accelerator()
|
||||
device = accelerator.device
|
||||
|
||||
model, optimizer = msamp.initialize(model, optimizer, opt_level=opt_level)
|
||||
|
||||
model.to(device)
|
||||
|
||||
# Convert the model to DDP
|
||||
device_ids, output_device = [accelerator.local_process_index], accelerator.local_process_index
|
||||
model = DDP(model, device_ids=device_ids, output_device=output_device)
|
||||
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
|
||||
for i, batch in enumerate(train_dataloader):
|
||||
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
scaler.scale(loss).backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
def train_integration(opt_level="O2"):
|
||||
kwargs_handlers = [FP8RecipeKwargs(backend="msamp", opt_level=opt_level)]
|
||||
AcceleratorState()._reset_state(True)
|
||||
accelerator = Accelerator(mixed_precision="fp8", kwargs_handlers=kwargs_handlers)
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
model, optimizer = accelerator.prepare(model, optimizer)
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
for i, batch in enumerate(train_dataloader):
|
||||
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for opt_level in ["O1", "O2"]:
|
||||
baseline_not_trained, baseline_trained = train_baseline(opt_level)
|
||||
accelerator_not_trained, accelerator_trained = train_integration(opt_level)
|
||||
assert (
|
||||
baseline_not_trained["accuracy"] == accelerator_not_trained["accuracy"]
|
||||
), f'Accuracy not the same for untrained baseline and accelerator using opt_level={opt_level}: {baseline_not_trained["accuracy"]} == {accelerator_not_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_not_trained["f1"] == accelerator_not_trained["f1"]
|
||||
), f'F1 not the same for untrained baseline and accelerator using opt_level={opt_level}: {baseline_not_trained["f1"]} == {accelerator_not_trained["f1"]}'
|
||||
assert (
|
||||
baseline_trained["accuracy"] == accelerator_trained["accuracy"]
|
||||
), f'Accuracy not the same for trained baseline and accelerator using opt_level={opt_level}: {baseline_trained["accuracy"]} == {accelerator_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_trained["f1"] == accelerator_trained["f1"]
|
||||
), f'F1 not the same for trained baseline and accelerator using opt_level={opt_level}: {baseline_trained["f1"]} == {accelerator_trained["f1"]}'
|
||||
161
benchmarks/fp8/ms_amp/distrib_deepspeed.py
Normal file
161
benchmarks/fp8/ms_amp/distrib_deepspeed.py
Normal file
@ -0,0 +1,161 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
|
||||
"""
|
||||
This script tests to ensure that `accelerate` performs at the same level as raw `MS-AMP`.
|
||||
|
||||
This particular script verifies this for DeepSpeed training.
|
||||
|
||||
NOTE: MS-AMP does *not* support ZeRO-3.
|
||||
"""
|
||||
|
||||
# import msamp.deepspeed as msamp_deepspeed
|
||||
import evaluate
|
||||
import torch
|
||||
from fp8_utils import evaluate_model, get_training_utilities
|
||||
from msamp import deepspeed as msamp_deepspeed
|
||||
|
||||
from accelerate import Accelerator, DeepSpeedPlugin
|
||||
from accelerate.state import AcceleratorState
|
||||
from accelerate.utils import set_seed
|
||||
|
||||
|
||||
MODEL_NAME = "bert-base-cased"
|
||||
METRIC = evaluate.load("glue", "mrpc")
|
||||
|
||||
|
||||
def train_baseline(zero_stage: int = 1, opt_level: str = "O1"):
|
||||
set_seed(42)
|
||||
accelerator = Accelerator()
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
|
||||
config = {
|
||||
"train_batch_size": 32,
|
||||
"train_micro_batch_size_per_gpu": 16,
|
||||
"gradient_accumulation_steps": 1,
|
||||
"zero_optimization": {
|
||||
"stage": zero_stage,
|
||||
"offload_optimizer": {"device": "none", "nvme_path": None},
|
||||
"offload_param": {"device": "none", "nvme_path": None},
|
||||
},
|
||||
"gradient_clipping": 1.0,
|
||||
"steps_per_print": np.inf,
|
||||
"bf16": {"enabled": True},
|
||||
"fp16": {"enabled": False},
|
||||
"zero_allow_untested_optimizer": True,
|
||||
"msamp": {
|
||||
"enabled": True,
|
||||
"opt_level": opt_level,
|
||||
},
|
||||
}
|
||||
(
|
||||
model,
|
||||
optimizer,
|
||||
_,
|
||||
_,
|
||||
) = msamp_deepspeed.initialize(
|
||||
model=model,
|
||||
optimizer=optimizer,
|
||||
config_params=config,
|
||||
)
|
||||
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
|
||||
for _ in range(2):
|
||||
for batch in train_dataloader:
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
model.backward(loss)
|
||||
model.step()
|
||||
for _ in range(accelerator.num_processes):
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.destroy()
|
||||
torch.cuda.empty_cache()
|
||||
AcceleratorState()._reset_state(True)
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
def train_integration(zero_stage: int = 1, opt_level: str = "O1"):
|
||||
set_seed(42)
|
||||
deepspeed_plugin = DeepSpeedPlugin(
|
||||
zero_stage=zero_stage,
|
||||
enable_msamp=True,
|
||||
msamp_opt_level=opt_level,
|
||||
)
|
||||
accelerator = Accelerator(mixed_precision="fp8", deepspeed_plugin=deepspeed_plugin)
|
||||
accelerator.state.deepspeed_plugin.deepspeed_config["train_micro_batch_size_per_gpu"] = 16
|
||||
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
model, optimizer, lr_scheduler = accelerator.prepare(model, optimizer, lr_scheduler)
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
for _ in range(2):
|
||||
for batch in train_dataloader:
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
lr_scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.destroy()
|
||||
torch.cuda.empty_cache()
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
AcceleratorState()._reset_state(True)
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for zero_stage in [1, 2]:
|
||||
for opt_level in ["O1", "O2", "O3"]:
|
||||
baseline_not_trained, baseline_trained = train_baseline(zero_stage, opt_level)
|
||||
accelerator_not_trained, accelerator_trained = train_integration(zero_stage, opt_level)
|
||||
assert (
|
||||
baseline_not_trained["accuracy"] == accelerator_not_trained["accuracy"]
|
||||
), f'ZERO stage {zero_stage}, opt_level={opt_level}:\nAccuracy should be the same for the baseline and accelerator: {baseline_not_trained["accuracy"]} == {accelerator_not_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_not_trained["f1"] == accelerator_not_trained["f1"]
|
||||
), f'ZERO stage {zero_stage}, opt_level={opt_level}:\nF1 score should be the same for the baseline and accelerator: {baseline_not_trained["f1"]} == {accelerator_not_trained["f1"]}'
|
||||
assert (
|
||||
baseline_trained["accuracy"] == accelerator_trained["accuracy"]
|
||||
), f'ZERO stage {zero_stage}, opt_level={opt_level}:\nAccuracy should be the same for the baseline and accelerator: {baseline_trained["accuracy"]} == {accelerator_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_trained["f1"] == accelerator_trained["f1"]
|
||||
), f'ZERO stage {zero_stage}, opt_level={opt_level}:\nF1 score should be the same for the baseline and accelerator: {baseline_trained["f1"]} == {accelerator_trained["f1"]}'
|
||||
|
||||
torch.distributed.destroy_process_group()
|
||||
118
benchmarks/fp8/ms_amp/fp8_utils.py
Normal file
118
benchmarks/fp8/ms_amp/fp8_utils.py
Normal file
@ -0,0 +1,118 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
import torch
|
||||
|
||||
|
||||
def get_dataloaders(model_name: str, batch_size: int = 16):
|
||||
from datasets import load_dataset
|
||||
from torch.utils.data import DataLoader
|
||||
from transformers import AutoTokenizer
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||||
datasets = load_dataset("glue", "mrpc")
|
||||
|
||||
def tokenize_function(examples):
|
||||
# max_length=None => use the model max length (it's actually the default)
|
||||
outputs = tokenizer(examples["sentence1"], examples["sentence2"], truncation=True, max_length=None)
|
||||
return outputs
|
||||
|
||||
# Apply the method we just defined to all the examples in all the splits of the dataset
|
||||
# starting with the main process first:
|
||||
tokenized_datasets = datasets.map(
|
||||
tokenize_function,
|
||||
batched=True,
|
||||
remove_columns=["idx", "sentence1", "sentence2"],
|
||||
)
|
||||
|
||||
# We also rename the 'label' column to 'labels' which is the expected name for labels by the models of the
|
||||
# transformers library
|
||||
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
|
||||
|
||||
def collate_fn(examples):
|
||||
return tokenizer.pad(
|
||||
examples,
|
||||
padding="longest",
|
||||
pad_to_multiple_of=16, # Specific for FP8
|
||||
return_tensors="pt",
|
||||
)
|
||||
|
||||
# Instantiate dataloaders.
|
||||
train_dataloader = DataLoader(
|
||||
tokenized_datasets["train"], shuffle=True, collate_fn=collate_fn, batch_size=batch_size, drop_last=True
|
||||
)
|
||||
eval_dataloader = DataLoader(
|
||||
tokenized_datasets["validation"],
|
||||
shuffle=False,
|
||||
collate_fn=collate_fn,
|
||||
batch_size=16,
|
||||
drop_last=True,
|
||||
)
|
||||
|
||||
return train_dataloader, eval_dataloader
|
||||
|
||||
|
||||
def get_training_utilities(model_name: str, batch_size: int = 16, accelerator=None):
|
||||
"""
|
||||
Returns a tuple of:
|
||||
- Model
|
||||
- Optimizer
|
||||
- Train dataloader (prepared)
|
||||
- Eval dataloader (prepared)
|
||||
- LR Scheduler
|
||||
Suitable for training on the MRPC dataset
|
||||
"""
|
||||
from torch.optim import AdamW
|
||||
from transformers import AutoModelForSequenceClassification, get_linear_schedule_with_warmup
|
||||
|
||||
from accelerate import Accelerator
|
||||
|
||||
if accelerator is None:
|
||||
accelerator = Accelerator()
|
||||
model = AutoModelForSequenceClassification.from_pretrained(model_name)
|
||||
train_dataloader, eval_dataloader = get_dataloaders(model_name, batch_size)
|
||||
optimizer = AdamW(model.parameters(), lr=0.0001)
|
||||
lr_scheduler = get_linear_schedule_with_warmup(
|
||||
optimizer=optimizer,
|
||||
num_warmup_steps=100,
|
||||
num_training_steps=len(train_dataloader) * 2,
|
||||
)
|
||||
train_dataloader, eval_dataloader = accelerator.prepare(train_dataloader, eval_dataloader)
|
||||
return model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
|
||||
|
||||
|
||||
def get_named_parameters(model):
|
||||
"""
|
||||
Same thing as `Accelerator.get_named_parameters` Returns a list of the named parameters of the model (extracted
|
||||
from parallel)
|
||||
"""
|
||||
from accelerate.utils import extract_model_from_parallel
|
||||
|
||||
model = extract_model_from_parallel(model)
|
||||
return {n: p for n, p in model.named_parameters()}
|
||||
|
||||
|
||||
def evaluate_model(model, dataloader, metric, accelerator=None):
|
||||
"Turns model to .eval(), runs dataloader, calculates metric, then turns eval back on"
|
||||
model.eval()
|
||||
for step, batch in enumerate(dataloader):
|
||||
with torch.no_grad():
|
||||
# W/ MS-AMP, we need to cast while evaluating
|
||||
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
||||
outputs = model(**batch)
|
||||
predictions = outputs.logits.argmax(dim=-1)
|
||||
references = batch["labels"]
|
||||
if accelerator is not None and accelerator.num_processes > 1:
|
||||
predictions, references = accelerator.gather_for_metrics((predictions, references))
|
||||
metric.add_batch(predictions=predictions, references=references)
|
||||
return metric.compute()
|
||||
118
benchmarks/fp8/ms_amp/non_distributed.py
Normal file
118
benchmarks/fp8/ms_amp/non_distributed.py
Normal file
@ -0,0 +1,118 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
|
||||
"""
|
||||
This script tests to ensure that `accelerate` performs at the same level as raw `MS-AMP`.
|
||||
|
||||
This particular script verifies this for single GPU training.
|
||||
"""
|
||||
|
||||
import evaluate
|
||||
import msamp
|
||||
import torch
|
||||
from fp8_utils import evaluate_model, get_training_utilities
|
||||
|
||||
from accelerate import Accelerator
|
||||
from accelerate.state import AcceleratorState
|
||||
from accelerate.utils import FP8RecipeKwargs, get_grad_scaler, set_seed
|
||||
|
||||
|
||||
MODEL_NAME = "bert-base-cased"
|
||||
METRIC = evaluate.load("glue", "mrpc")
|
||||
|
||||
|
||||
def train_baseline(opt_level="O2"):
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(MODEL_NAME)
|
||||
|
||||
model, optimizer = msamp.initialize(model, optimizer, opt_level=opt_level)
|
||||
model.to("cuda")
|
||||
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC)
|
||||
model.train()
|
||||
scaler = get_grad_scaler()
|
||||
|
||||
for batch in train_dataloader:
|
||||
batch = batch.to("cuda")
|
||||
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
loss = scaler.scale(loss)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
def train_integration(opt_level="O2"):
|
||||
kwargs_handlers = [FP8RecipeKwargs(backend="msamp", opt_level=opt_level)]
|
||||
AcceleratorState()._reset_state(True)
|
||||
accelerator = Accelerator(mixed_precision="fp8", kwargs_handlers=kwargs_handlers)
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
model, optimizer, lr_scheduler = accelerator.prepare(model, optimizer, lr_scheduler)
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC)
|
||||
model.train()
|
||||
|
||||
for batch in train_dataloader:
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for opt_level in ["O1", "O2"]:
|
||||
baseline_not_trained, baseline_trained = train_baseline(opt_level)
|
||||
accelerator_not_trained, accelerator_trained = train_integration(opt_level)
|
||||
|
||||
assert (
|
||||
baseline_not_trained["accuracy"] == accelerator_not_trained["accuracy"]
|
||||
), f'Accuracy should be the same for the baseline and accelerator: {baseline_not_trained["accuracy"]} == {accelerator_not_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_not_trained["f1"] == accelerator_not_trained["f1"]
|
||||
), f'F1 score should be the same for the baseline and accelerator: {baseline_not_trained["f1"]} == {accelerator_not_trained["f1"]}'
|
||||
assert (
|
||||
baseline_trained["accuracy"] == accelerator_trained["accuracy"]
|
||||
), f'Accuracy should be the same for the baseline and accelerator: {baseline_trained["accuracy"]} == {accelerator_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_trained["f1"] == accelerator_trained["f1"]
|
||||
), f'F1 score should be the same for the baseline and accelerator: {baseline_trained["f1"]} == {accelerator_trained["f1"]}'
|
||||
12
benchmarks/fp8/transformer_engine/Dockerfile
Normal file
12
benchmarks/fp8/transformer_engine/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM nvcr.io/nvidia/pytorch:24.07-py3
|
||||
|
||||
RUN pip install transformers evaluate datasets
|
||||
RUN git clone https://github.com/huggingface/accelerate.git
|
||||
|
||||
RUN cd accelerate && \
|
||||
pip install -e . && \
|
||||
cd benchmarks/fp8
|
||||
|
||||
RUN /bin/bash
|
||||
|
||||
|
||||
32
benchmarks/fp8/transformer_engine/README.md
Normal file
32
benchmarks/fp8/transformer_engine/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# FP8 Benchmarks
|
||||
|
||||
Comparing and running [TransformerEngine](https://github.com/NVIDIA/TransformerEngine) FP8 with accelerate
|
||||
|
||||
## Overview
|
||||
|
||||
This repo provides scripts which compare native TransformerEngine model training against `accelerate`'s own integration. Each modeling type is segmented out via a script, supporting the following:
|
||||
|
||||
* Single GPU training (`non_distributed.py`)
|
||||
* Multi-GPU training via DistributedDataParallelism (`ddp.py`)
|
||||
* Fully Sharded Data Parallelism (`fsdp.py`)
|
||||
* DeepSpeed ZeRO 1-3 (`deepspeed.py`)
|
||||
|
||||
To run them, it's recommended to use a docker image (see the attached `Dockerfile`) and not install `TransformerEngine` manually.
|
||||
|
||||
## Running:
|
||||
|
||||
There are official Docker images located at `huggingface/accelerate:gpu-fp8-transformerengine-nightly` which can be used.
|
||||
|
||||
You can run all scripts using the core `accelerate launch` command without any `accelerate config` being needed.
|
||||
|
||||
For single GPU, run it via `python`:
|
||||
|
||||
```bash
|
||||
python non_distributed.py
|
||||
```
|
||||
|
||||
For the rest, run it via `accelerate launch`:
|
||||
|
||||
```bash
|
||||
accelerate launch ddp.py # or distrib_deepspeed.py, ddp.py
|
||||
```
|
||||
144
benchmarks/fp8/transformer_engine/ddp.py
Normal file
144
benchmarks/fp8/transformer_engine/ddp.py
Normal file
@ -0,0 +1,144 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
|
||||
"""
|
||||
This script tests to ensure that `accelerate` performs at the same level as raw `TransformersEngine`.
|
||||
|
||||
This particular script verifies this for DDP training.
|
||||
"""
|
||||
|
||||
import evaluate
|
||||
import torch
|
||||
import transformer_engine.common.recipe as te_recipe
|
||||
import transformer_engine.pytorch as te
|
||||
from fp8_utils import evaluate_model, get_named_parameters, get_training_utilities
|
||||
from torch.nn.parallel import DistributedDataParallel as DDP
|
||||
from transformer_engine.common.recipe import DelayedScaling
|
||||
|
||||
from accelerate import Accelerator
|
||||
from accelerate.state import AcceleratorState
|
||||
from accelerate.utils import FP8RecipeKwargs, set_seed
|
||||
from accelerate.utils.transformer_engine import convert_model
|
||||
|
||||
|
||||
MODEL_NAME = "bert-base-cased"
|
||||
METRIC = evaluate.load("glue", "mrpc")
|
||||
|
||||
|
||||
def train_baseline():
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(MODEL_NAME)
|
||||
accelerator = Accelerator()
|
||||
device = accelerator.device
|
||||
model.to(device)
|
||||
|
||||
# Convert the model to TE
|
||||
old_named_params = get_named_parameters(model)
|
||||
|
||||
with torch.no_grad():
|
||||
convert_model(model)
|
||||
|
||||
FP8_RECIPE_KWARGS = {"fp8_format": te_recipe.Format.HYBRID, "amax_history_len": 32, "amax_compute_algo": "max"}
|
||||
fp8_recipe = DelayedScaling(**FP8_RECIPE_KWARGS)
|
||||
|
||||
new_named_params = get_named_parameters(model)
|
||||
|
||||
# Convert the model to DDP
|
||||
device_ids, output_device = [accelerator.local_process_index], accelerator.local_process_index
|
||||
model = DDP(model, device_ids=device_ids, output_device=output_device)
|
||||
|
||||
mapping = {p: new_named_params[n] for n, p in old_named_params.items()}
|
||||
for param_group in optimizer.param_groups:
|
||||
param_group["params"] = [mapping[p] for p in param_group["params"]]
|
||||
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
|
||||
for _ in range(2):
|
||||
for batch in train_dataloader:
|
||||
with te.fp8_autocast(enabled=True, fp8_recipe=fp8_recipe):
|
||||
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
||||
batch = batch.to(device)
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
def train_integration():
|
||||
FP8_RECIPE_KWARGS = {"fp8_format": "HYBRID", "amax_history_len": 32, "amax_compute_algo": "max"}
|
||||
kwargs_handlers = [FP8RecipeKwargs(backend="TE", **FP8_RECIPE_KWARGS)]
|
||||
AcceleratorState()._reset_state(True)
|
||||
accelerator = Accelerator(mixed_precision="fp8", kwargs_handlers=kwargs_handlers)
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
model, optimizer = accelerator.prepare(model, optimizer)
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
|
||||
for _ in range(2):
|
||||
for batch in train_dataloader:
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
baseline_not_trained, baseline_trained = train_baseline()
|
||||
accelerator_not_trained, accelerator_trained = train_integration()
|
||||
|
||||
assert (
|
||||
baseline_not_trained["accuracy"] == accelerator_not_trained["accuracy"]
|
||||
), f'Accuracy should be the same for the baseline and accelerator: {baseline_not_trained["accuracy"]} == {accelerator_not_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_not_trained["f1"] == accelerator_not_trained["f1"]
|
||||
), f'F1 score should be the same for the baseline and accelerator: {baseline_not_trained["f1"]} == {accelerator_not_trained["f1"]}'
|
||||
assert (
|
||||
baseline_trained["accuracy"] == accelerator_trained["accuracy"]
|
||||
), f'Accuracy should be the same for the baseline and accelerator: {baseline_trained["accuracy"]} == {accelerator_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_trained["f1"] == accelerator_trained["f1"]
|
||||
), f'F1 score should be the same for the baseline and accelerator: {baseline_trained["f1"]} == {accelerator_trained["f1"]}'
|
||||
|
||||
torch.distributed.destroy_process_group()
|
||||
190
benchmarks/fp8/transformer_engine/distrib_deepspeed.py
Normal file
190
benchmarks/fp8/transformer_engine/distrib_deepspeed.py
Normal file
@ -0,0 +1,190 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
|
||||
"""
|
||||
This script tests to ensure that `accelerate` performs at the same level as raw `TransformersEngine`.
|
||||
|
||||
This particular script verifies this for DDP training.
|
||||
"""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import deepspeed
|
||||
import evaluate
|
||||
import torch
|
||||
import transformer_engine.common.recipe as te_recipe
|
||||
import transformer_engine.pytorch as te
|
||||
from fp8_utils import evaluate_model, get_named_parameters, get_training_utilities
|
||||
from transformer_engine.common.recipe import DelayedScaling
|
||||
|
||||
from accelerate import Accelerator, DeepSpeedPlugin
|
||||
from accelerate.state import AcceleratorState
|
||||
from accelerate.utils import FP8RecipeKwargs, set_seed
|
||||
from accelerate.utils.transformer_engine import convert_model
|
||||
|
||||
|
||||
MODEL_NAME = "bert-base-cased"
|
||||
METRIC = evaluate.load("glue", "mrpc")
|
||||
|
||||
|
||||
def train_baseline(zero_stage: int = 1):
|
||||
# This forces transformers to think Zero-3 Init should be used
|
||||
with patch("transformers.integrations.deepspeed.is_deepspeed_zero3_enabled") as mock:
|
||||
mock.return_value = zero_stage == 3
|
||||
set_seed(42)
|
||||
|
||||
accelerator = Accelerator()
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
# Convert the model to TE
|
||||
old_named_params = get_named_parameters(model)
|
||||
|
||||
with torch.no_grad():
|
||||
convert_model(model)
|
||||
new_named_params = get_named_parameters(model)
|
||||
|
||||
mapping = {p: new_named_params[n] for n, p in old_named_params.items()}
|
||||
for param_group in optimizer.param_groups:
|
||||
param_group["params"] = [mapping[p] for p in param_group["params"]]
|
||||
|
||||
FP8_RECIPE_KWARGS = {"fp8_format": te_recipe.Format.HYBRID, "amax_history_len": 32, "amax_compute_algo": "max"}
|
||||
fp8_recipe = DelayedScaling(**FP8_RECIPE_KWARGS)
|
||||
|
||||
import numpy as np
|
||||
|
||||
config = {
|
||||
"train_batch_size": 32,
|
||||
"train_micro_batch_size_per_gpu": 16,
|
||||
"gradient_accumulation_steps": 1,
|
||||
"zero_optimization": {
|
||||
"stage": zero_stage,
|
||||
"offload_optimizer": {"device": "none", "nvme_path": None},
|
||||
"offload_param": {"device": "none", "nvme_path": None},
|
||||
"stage3_gather_16bit_weights_on_model_save": False,
|
||||
},
|
||||
"gradient_clipping": 1.0,
|
||||
"steps_per_print": np.inf,
|
||||
"bf16": {"enabled": True},
|
||||
"fp16": {"enabled": False},
|
||||
"zero_allow_untested_optimizer": True,
|
||||
}
|
||||
|
||||
(
|
||||
model,
|
||||
optimizer,
|
||||
_,
|
||||
_,
|
||||
) = deepspeed.initialize(
|
||||
model=model,
|
||||
optimizer=optimizer,
|
||||
config_params=config,
|
||||
)
|
||||
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
|
||||
model_outputs = []
|
||||
data = []
|
||||
|
||||
for _ in range(2):
|
||||
for batch in train_dataloader:
|
||||
with te.fp8_autocast(enabled=True, fp8_recipe=fp8_recipe):
|
||||
outputs = model(**batch)
|
||||
data.append(batch.to("cpu"))
|
||||
model_outputs.append(outputs.logits.to("cpu"))
|
||||
loss = outputs.loss
|
||||
model.backward(loss)
|
||||
model.step()
|
||||
for _ in range(accelerator.num_processes):
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.destroy()
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results, model_outputs, data
|
||||
|
||||
|
||||
def train_integration(zero_stage: int = 1):
|
||||
set_seed(42)
|
||||
FP8_RECIPE_KWARGS = {"fp8_format": "HYBRID", "amax_history_len": 32, "amax_compute_algo": "max"}
|
||||
kwargs_handlers = [FP8RecipeKwargs(backend="TE", **FP8_RECIPE_KWARGS)]
|
||||
AcceleratorState()._reset_state(True)
|
||||
deepspeed_plugin = DeepSpeedPlugin(
|
||||
zero_stage=zero_stage,
|
||||
zero3_init_flag=zero_stage == 3,
|
||||
)
|
||||
accelerator = Accelerator(
|
||||
mixed_precision="fp8", kwargs_handlers=kwargs_handlers, deepspeed_plugin=deepspeed_plugin
|
||||
)
|
||||
accelerator.state.deepspeed_plugin.deepspeed_config["train_micro_batch_size_per_gpu"] = 16
|
||||
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
model, optimizer, lr_scheduler = accelerator.prepare(model, optimizer, lr_scheduler)
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
model_outputs = []
|
||||
data = []
|
||||
for _ in range(2):
|
||||
for batch in train_dataloader:
|
||||
outputs = model(**batch)
|
||||
data.append(batch.to("cpu"))
|
||||
model_outputs.append(outputs.logits.to("cpu"))
|
||||
loss = outputs.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
lr_scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.destroy()
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results, model_outputs, data
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# for zero_stage in [1, 2, 3]:
|
||||
zero_stage = 1
|
||||
baseline_not_trained, baseline_trained, baseline_outputs, baseline_data = train_baseline(zero_stage)
|
||||
accelerator_not_trained, accelerator_trained, accelerator_outputs, accelerator_data = train_integration(zero_stage)
|
||||
assert (
|
||||
baseline_not_trained["accuracy"] == accelerator_not_trained["accuracy"]
|
||||
), f'ZERO stage {zero_stage}: Accuracy should be the same for the baseline and accelerator: {baseline_not_trained["accuracy"]} == {accelerator_not_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_not_trained["f1"] == accelerator_not_trained["f1"]
|
||||
), f'ZERO stage {zero_stage}: F1 score should be the same for the baseline and accelerator: {baseline_not_trained["f1"]} == {accelerator_not_trained["f1"]}'
|
||||
assert (
|
||||
baseline_trained["accuracy"] == accelerator_trained["accuracy"]
|
||||
), f'ZERO stage {zero_stage}: Accuracy should be the same for the baseline and accelerator: {baseline_trained["accuracy"]} == {accelerator_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_trained["f1"] == accelerator_trained["f1"]
|
||||
), f'ZERO stage {zero_stage}: F1 score should be the same for the baseline and accelerator: {baseline_trained["f1"]} == {accelerator_trained["f1"]}'
|
||||
|
||||
torch.distributed.destroy_process_group()
|
||||
116
benchmarks/fp8/transformer_engine/fp8_utils.py
Normal file
116
benchmarks/fp8/transformer_engine/fp8_utils.py
Normal file
@ -0,0 +1,116 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
import torch
|
||||
|
||||
|
||||
def get_dataloaders(model_name: str, batch_size: int = 16):
|
||||
from datasets import load_dataset
|
||||
from torch.utils.data import DataLoader
|
||||
from transformers import AutoTokenizer
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||||
datasets = load_dataset("glue", "mrpc")
|
||||
|
||||
def tokenize_function(examples):
|
||||
# max_length=None => use the model max length (it's actually the default)
|
||||
outputs = tokenizer(examples["sentence1"], examples["sentence2"], truncation=True, max_length=None)
|
||||
return outputs
|
||||
|
||||
# Apply the method we just defined to all the examples in all the splits of the dataset
|
||||
# starting with the main process first:
|
||||
tokenized_datasets = datasets.map(
|
||||
tokenize_function,
|
||||
batched=True,
|
||||
remove_columns=["idx", "sentence1", "sentence2"],
|
||||
)
|
||||
|
||||
# We also rename the 'label' column to 'labels' which is the expected name for labels by the models of the
|
||||
# transformers library
|
||||
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
|
||||
|
||||
def collate_fn(examples):
|
||||
return tokenizer.pad(
|
||||
examples,
|
||||
padding="longest",
|
||||
pad_to_multiple_of=16, # Specific for FP8
|
||||
return_tensors="pt",
|
||||
)
|
||||
|
||||
# Instantiate dataloaders.
|
||||
train_dataloader = DataLoader(
|
||||
tokenized_datasets["train"], shuffle=True, collate_fn=collate_fn, batch_size=batch_size, drop_last=True
|
||||
)
|
||||
eval_dataloader = DataLoader(
|
||||
tokenized_datasets["validation"],
|
||||
shuffle=False,
|
||||
collate_fn=collate_fn,
|
||||
batch_size=16,
|
||||
drop_last=True,
|
||||
)
|
||||
|
||||
return train_dataloader, eval_dataloader
|
||||
|
||||
|
||||
def get_training_utilities(model_name: str, batch_size: int = 16, accelerator=None):
|
||||
"""
|
||||
Returns a tuple of:
|
||||
- Model
|
||||
- Optimizer
|
||||
- Train dataloader (prepared)
|
||||
- Eval dataloader (prepared)
|
||||
- LR Scheduler
|
||||
Suitable for training on the MRPC dataset
|
||||
"""
|
||||
from torch.optim import AdamW
|
||||
from transformers import AutoModelForSequenceClassification, get_linear_schedule_with_warmup
|
||||
|
||||
from accelerate import Accelerator
|
||||
|
||||
if accelerator is None:
|
||||
accelerator = Accelerator()
|
||||
model = AutoModelForSequenceClassification.from_pretrained(model_name)
|
||||
train_dataloader, eval_dataloader = get_dataloaders(model_name, batch_size)
|
||||
optimizer = AdamW(model.parameters(), lr=0.0001)
|
||||
lr_scheduler = get_linear_schedule_with_warmup(
|
||||
optimizer=optimizer,
|
||||
num_warmup_steps=100,
|
||||
num_training_steps=len(train_dataloader) * 2,
|
||||
)
|
||||
train_dataloader, eval_dataloader = accelerator.prepare(train_dataloader, eval_dataloader)
|
||||
return model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
|
||||
|
||||
|
||||
def get_named_parameters(model):
|
||||
"""
|
||||
Same thing as `Accelerator.get_named_parameters` Returns a list of the named parameters of the model (extracted
|
||||
from parallel)
|
||||
"""
|
||||
from accelerate.utils import extract_model_from_parallel
|
||||
|
||||
model = extract_model_from_parallel(model)
|
||||
return {n: p for n, p in model.named_parameters()}
|
||||
|
||||
|
||||
def evaluate_model(model, dataloader, metric, accelerator=None):
|
||||
"Turns model to .eval(), runs dataloader, calculates metric, then turns eval back on"
|
||||
model.eval()
|
||||
for step, batch in enumerate(dataloader):
|
||||
with torch.no_grad():
|
||||
outputs = model(**batch)
|
||||
predictions = outputs.logits.argmax(dim=-1)
|
||||
references = batch["labels"]
|
||||
if accelerator is not None and accelerator.num_processes > 1:
|
||||
predictions, references = accelerator.gather_for_metrics((predictions, references))
|
||||
metric.add_batch(predictions=predictions, references=references)
|
||||
return metric.compute()
|
||||
161
benchmarks/fp8/transformer_engine/fsdp.py
Normal file
161
benchmarks/fp8/transformer_engine/fsdp.py
Normal file
@ -0,0 +1,161 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
|
||||
"""
|
||||
This script tests to ensure that `accelerate` performs at the same level as raw `TransformersEngine`.
|
||||
|
||||
This particular script verifies this for FSDP training.
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
|
||||
import evaluate
|
||||
import torch
|
||||
import transformer_engine.common.recipe as te_recipe
|
||||
import transformer_engine.pytorch as te
|
||||
from fp8_utils import evaluate_model, get_named_parameters, get_training_utilities
|
||||
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
|
||||
from torch.distributed.fsdp import MixedPrecision
|
||||
from torch.distributed.fsdp.wrap import transformer_auto_wrap_policy
|
||||
from transformer_engine.common.recipe import DelayedScaling
|
||||
from transformers.models.bert import BertLayer
|
||||
|
||||
from accelerate import Accelerator
|
||||
from accelerate import FullyShardedDataParallelPlugin as FSDPPlugin
|
||||
from accelerate.state import AcceleratorState
|
||||
from accelerate.utils import FP8RecipeKwargs, set_seed
|
||||
from accelerate.utils.transformer_engine import convert_model
|
||||
|
||||
|
||||
MODEL_NAME = "bert-base-cased"
|
||||
METRIC = evaluate.load("glue", "mrpc")
|
||||
|
||||
FSDP_WRAP_POLICY = partial(transformer_auto_wrap_policy, transformer_layer_cls={BertLayer})
|
||||
|
||||
|
||||
def train_baseline():
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(MODEL_NAME)
|
||||
accelerator = Accelerator()
|
||||
device = accelerator.device
|
||||
model.to(device)
|
||||
|
||||
# Convert the model to TE
|
||||
old_named_params = get_named_parameters(model)
|
||||
|
||||
with torch.no_grad():
|
||||
convert_model(model)
|
||||
|
||||
FP8_RECIPE_KWARGS = {"fp8_format": te_recipe.Format.HYBRID, "amax_history_len": 32, "amax_compute_algo": "max"}
|
||||
fp8_recipe = DelayedScaling(**FP8_RECIPE_KWARGS)
|
||||
|
||||
new_named_params = get_named_parameters(model)
|
||||
|
||||
# Convert the model to FSDP
|
||||
model = FSDP(
|
||||
model,
|
||||
use_orig_params=True,
|
||||
mixed_precision=MixedPrecision(param_dtype=torch.bfloat16, reduce_dtype=torch.float32),
|
||||
auto_wrap_policy=FSDP_WRAP_POLICY,
|
||||
)
|
||||
|
||||
mapping = {p: new_named_params[n] for n, p in old_named_params.items()}
|
||||
for param_group in optimizer.param_groups:
|
||||
param_group["params"] = [mapping[p] for p in param_group["params"]]
|
||||
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
|
||||
for _ in range(2):
|
||||
for batch in train_dataloader:
|
||||
with te.fp8_autocast(enabled=True, fp8_recipe=fp8_recipe):
|
||||
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
||||
batch = batch.to(device)
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
def train_integration():
|
||||
FP8_RECIPE_KWARGS = {"fp8_format": "HYBRID", "amax_history_len": 32, "amax_compute_algo": "max"}
|
||||
kwargs_handlers = [FP8RecipeKwargs(backend="TE", **FP8_RECIPE_KWARGS)]
|
||||
AcceleratorState()._reset_state(True)
|
||||
fsdp_plugin = FSDPPlugin(
|
||||
auto_wrap_policy=FSDP_WRAP_POLICY,
|
||||
use_orig_params=True,
|
||||
mixed_precision_policy=MixedPrecision(param_dtype=torch.bfloat16, reduce_dtype=torch.float32),
|
||||
)
|
||||
accelerator = Accelerator(mixed_precision="fp8", fsdp_plugin=fsdp_plugin, kwargs_handlers=kwargs_handlers)
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
model, optimizer = accelerator.prepare(model, optimizer)
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
model.train()
|
||||
|
||||
for _ in range(2):
|
||||
for batch in train_dataloader:
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC, accelerator=accelerator)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
baseline_not_trained, baseline_trained = train_baseline()
|
||||
accelerator_not_trained, accelerator_trained = train_integration()
|
||||
|
||||
assert (
|
||||
baseline_not_trained["accuracy"] == accelerator_not_trained["accuracy"]
|
||||
), f'Accuracy should be the same for the baseline and accelerator: {baseline_not_trained["accuracy"]} == {accelerator_not_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_not_trained["f1"] == accelerator_not_trained["f1"]
|
||||
), f'F1 score should be the same for the baseline and accelerator: {baseline_not_trained["f1"]} == {accelerator_not_trained["f1"]}'
|
||||
assert (
|
||||
baseline_trained["accuracy"] == accelerator_trained["accuracy"]
|
||||
), f'Accuracy should be the same for the baseline and accelerator: {baseline_trained["accuracy"]} == {accelerator_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_trained["f1"] == accelerator_trained["f1"]
|
||||
), f'F1 score should be the same for the baseline and accelerator: {baseline_trained["f1"]} == {accelerator_trained["f1"]}'
|
||||
|
||||
torch.distributed.destroy_process_group()
|
||||
132
benchmarks/fp8/transformer_engine/non_distributed.py
Normal file
132
benchmarks/fp8/transformer_engine/non_distributed.py
Normal file
@ -0,0 +1,132 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
|
||||
"""
|
||||
This script tests to ensure that `accelerate` performs at the same level as raw `TransformersEngine`.
|
||||
|
||||
This particular script verifies this for single GPU training.
|
||||
"""
|
||||
|
||||
import evaluate
|
||||
import torch
|
||||
import transformer_engine.common.recipe as te_recipe
|
||||
import transformer_engine.pytorch as te
|
||||
from fp8_utils import evaluate_model, get_named_parameters, get_training_utilities
|
||||
from transformer_engine.common.recipe import DelayedScaling
|
||||
|
||||
from accelerate import Accelerator
|
||||
from accelerate.state import AcceleratorState
|
||||
from accelerate.utils import FP8RecipeKwargs, set_seed
|
||||
from accelerate.utils.transformer_engine import convert_model
|
||||
|
||||
|
||||
MODEL_NAME = "bert-base-cased"
|
||||
METRIC = evaluate.load("glue", "mrpc")
|
||||
|
||||
|
||||
def train_baseline():
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(MODEL_NAME)
|
||||
|
||||
# Convert the model to TE
|
||||
old_named_params = get_named_parameters(model)
|
||||
|
||||
with torch.no_grad():
|
||||
convert_model(model)
|
||||
|
||||
new_named_params = get_named_parameters(model)
|
||||
mapping = {p: new_named_params[n] for n, p in old_named_params.items()}
|
||||
for param_group in optimizer.param_groups:
|
||||
param_group["params"] = [mapping[p] for p in param_group["params"]]
|
||||
|
||||
FP8_RECIPE_KWARGS = {"fp8_format": te_recipe.Format.HYBRID, "amax_history_len": 32, "amax_compute_algo": "max"}
|
||||
fp8_recipe = DelayedScaling(**FP8_RECIPE_KWARGS)
|
||||
|
||||
model.to("cuda")
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC)
|
||||
model.train()
|
||||
|
||||
for batch in train_dataloader:
|
||||
with te.fp8_autocast(enabled=True, fp8_recipe=fp8_recipe):
|
||||
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
||||
batch = batch.to("cuda")
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
def train_integration():
|
||||
FP8_RECIPE_KWARGS = {"fp8_format": "HYBRID", "amax_history_len": 32, "amax_compute_algo": "max"}
|
||||
kwargs_handlers = [FP8RecipeKwargs(backend="TE", **FP8_RECIPE_KWARGS)]
|
||||
AcceleratorState()._reset_state(True)
|
||||
accelerator = Accelerator(mixed_precision="fp8", kwargs_handlers=kwargs_handlers)
|
||||
set_seed(42)
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = get_training_utilities(
|
||||
MODEL_NAME, accelerator=accelerator
|
||||
)
|
||||
|
||||
model, optimizer, lr_scheduler = accelerator.prepare(model, optimizer, lr_scheduler)
|
||||
base_model_results = evaluate_model(model, eval_dataloader, METRIC)
|
||||
model.train()
|
||||
|
||||
for batch in train_dataloader:
|
||||
outputs = model(**batch)
|
||||
loss = outputs.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
lr_scheduler.step()
|
||||
|
||||
trained_model_results = evaluate_model(model, eval_dataloader, METRIC)
|
||||
|
||||
assert (
|
||||
trained_model_results["accuracy"] > base_model_results["accuracy"]
|
||||
), f'Accuracy should be higher for the trained model: {trained_model_results["accuracy"]} > {base_model_results["accuracy"]}'
|
||||
assert (
|
||||
trained_model_results["f1"] > base_model_results["f1"]
|
||||
), f'F1 score should be higher for the trained model: {trained_model_results["f1"]} > {base_model_results["f1"]}'
|
||||
|
||||
return base_model_results, trained_model_results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
baseline_not_trained, baseline_trained = train_baseline()
|
||||
accelerator_not_trained, accelerator_trained = train_integration()
|
||||
|
||||
assert (
|
||||
baseline_not_trained["accuracy"] == accelerator_not_trained["accuracy"]
|
||||
), f'Accuracy should be the same for the baseline and accelerator: {baseline_not_trained["accuracy"]} == {accelerator_not_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_not_trained["f1"] == accelerator_not_trained["f1"]
|
||||
), f'F1 score should be the same for the baseline and accelerator: {baseline_not_trained["f1"]} == {accelerator_not_trained["f1"]}'
|
||||
assert (
|
||||
baseline_trained["accuracy"] == accelerator_trained["accuracy"]
|
||||
), f'Accuracy should be the same for the baseline and accelerator: {baseline_trained["accuracy"]} == {accelerator_trained["accuracy"]}'
|
||||
assert (
|
||||
baseline_trained["f1"] == accelerator_trained["f1"]
|
||||
), f'F1 score should be the same for the baseline and accelerator: {baseline_trained["f1"]} == {accelerator_trained["f1"]}'
|
||||
@ -33,6 +33,7 @@ huggingface/accelerate:{accelerator}-{nightly,release}
|
||||
* `cpu`: Comes compiled off of `python:3.9-slim` and is designed for non-CUDA based workloads.
|
||||
* More to come soon
|
||||
* `gpu-deepspeed`: Comes compiled off of the `nvidia/cuda` image and includes core parts like `bitsandbytes` as well as the latest `deepspeed` version. Runs off python 3.10.
|
||||
* `gpu-fp8-transformerengine`: Comes compiled off of `nvcr.io/nvidia/pytorch` and is specifically for running the `benchmarks/fp8` scripts on devices which support FP8 operations using the `TransformerEngine` library (RTX 4090, H100, etc)
|
||||
|
||||
## Nightlies vs Releases
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Builds CPU-only Docker image of PyTorch
|
||||
# Uses multi-staged approach to reduce size
|
||||
# Stage 1
|
||||
FROM python:3.8-slim as compile-image
|
||||
FROM python:3.9-slim as compile-image
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@ -25,7 +25,7 @@ RUN python3 -m pip install --no-cache-dir \
|
||||
--extra-index-url https://download.pytorch.org/whl/cpu
|
||||
|
||||
# Stage 2
|
||||
FROM python:3.8-slim AS build-image
|
||||
FROM python:3.9-slim AS build-image
|
||||
COPY --from=compile-image /opt/venv /opt/venv
|
||||
RUN useradd -ms /bin/bash user
|
||||
USER user
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
- local: basic_tutorials/tpu
|
||||
title: TPU training
|
||||
- local: basic_tutorials/launch
|
||||
title: Launching distributed code
|
||||
title: Launching Accelerate scripts
|
||||
- local: basic_tutorials/notebook
|
||||
title: Launching distributed training from Jupyter Notebooks
|
||||
title: Tutorials
|
||||
@ -31,8 +31,10 @@
|
||||
title: Model quantization
|
||||
- local: usage_guides/tracking
|
||||
title: Experiment trackers
|
||||
- local: usage_guides/profiler
|
||||
title: Profiler
|
||||
- local: usage_guides/checkpoint
|
||||
title: Save and load training states
|
||||
title: Checkpointing
|
||||
- local: basic_tutorials/troubleshooting
|
||||
title: Troubleshoot
|
||||
- local: usage_guides/training_zoo
|
||||
@ -48,8 +50,12 @@
|
||||
title: Low precision (FP8) training
|
||||
- local: usage_guides/deepspeed
|
||||
title: DeepSpeed
|
||||
- local: usage_guides/deepspeed_multiple_model
|
||||
title: Using multiple models with DeepSpeed
|
||||
- local: usage_guides/ddp_comm_hook
|
||||
title: DDP Communication Hooks
|
||||
- local: usage_guides/fsdp
|
||||
title: Fully Sharded Data Parallelism
|
||||
title: Fully Sharded Data Parallel
|
||||
- local: usage_guides/megatron_lm
|
||||
title: Megatron-LM
|
||||
- local: usage_guides/sagemaker
|
||||
@ -69,7 +75,7 @@
|
||||
title: How to guides
|
||||
- sections:
|
||||
- local: concept_guides/internal_mechanism
|
||||
title: 🤗 Accelerate's internal mechanism
|
||||
title: Accelerate's internal mechanism
|
||||
- local: concept_guides/big_model_inference
|
||||
title: Loading big models into memory
|
||||
- local: concept_guides/performance
|
||||
@ -81,23 +87,23 @@
|
||||
- local: concept_guides/fsdp_and_deepspeed
|
||||
title: FSDP vs DeepSpeed
|
||||
- local: concept_guides/low_precision_training
|
||||
title: How training in low-precision environments is possible (FP8)
|
||||
title: Low precision training methods
|
||||
- local: concept_guides/training_tpu
|
||||
title: TPU best practices
|
||||
title: Training on TPUs
|
||||
title: Concepts and fundamentals
|
||||
- sections:
|
||||
- local: package_reference/accelerator
|
||||
title: Accelerator
|
||||
- local: package_reference/state
|
||||
title: Stateful configuration classes
|
||||
title: Stateful classes
|
||||
- local: package_reference/cli
|
||||
title: The Command Line
|
||||
- local: package_reference/torch_wrappers
|
||||
title: Torch wrapper classes
|
||||
title: DataLoaders, Optimizers, Schedulers
|
||||
- local: package_reference/tracking
|
||||
title: Experiment trackers
|
||||
- local: package_reference/launchers
|
||||
title: Distributed launchers
|
||||
title: Launchers
|
||||
- local: package_reference/deepspeed
|
||||
title: DeepSpeed utilities
|
||||
- local: package_reference/logging
|
||||
@ -105,13 +111,15 @@
|
||||
- local: package_reference/big_modeling
|
||||
title: Working with large models
|
||||
- local: package_reference/inference
|
||||
title: Distributed inference with big models
|
||||
title: Pipeline parallelism
|
||||
- local: package_reference/kwargs
|
||||
title: Kwargs handlers
|
||||
- local: package_reference/fp8
|
||||
title: FP8
|
||||
- local: package_reference/utilities
|
||||
title: Utility functions and classes
|
||||
- local: package_reference/megatron_lm
|
||||
title: Megatron-LM Utilities
|
||||
title: Megatron-LM utilities
|
||||
- local: package_reference/fsdp
|
||||
title: Fully Sharded Data Parallelism Utilities
|
||||
title: Fully Sharded Data Parallel utilities
|
||||
title: "Reference"
|
||||
|
||||
@ -13,31 +13,29 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Installation and Configuration
|
||||
# Installation
|
||||
|
||||
Before you start, you will need to setup your environment, install the appropriate packages, and configure 🤗 Accelerate. 🤗 Accelerate is tested on **Python 3.8+**.
|
||||
Before you start, you will need to setup your environment, install the appropriate packages, and configure Accelerate. Accelerate is tested on **Python 3.8+**.
|
||||
|
||||
## Installing 🤗 Accelerate
|
||||
Accelerate is available on pypi and conda, as well as on GitHub. Details to install from each are below:
|
||||
|
||||
🤗 Accelerate is available on pypi and conda, as well as on GitHub. Details to install from each are below:
|
||||
## pip
|
||||
|
||||
### pip
|
||||
|
||||
To install 🤗 Accelerate from pypi, perform:
|
||||
To install Accelerate from pypi, perform:
|
||||
|
||||
```bash
|
||||
pip install accelerate
|
||||
```
|
||||
|
||||
### conda
|
||||
## conda
|
||||
|
||||
🤗 Accelerate can also be installed with conda with:
|
||||
Accelerate can also be installed with conda with:
|
||||
|
||||
```bash
|
||||
conda install -c conda-forge accelerate
|
||||
```
|
||||
|
||||
### Source
|
||||
## Source
|
||||
|
||||
New features are added every day that haven't been released yet. To try them out yourself, install
|
||||
from the GitHub repository:
|
||||
@ -56,9 +54,9 @@ cd accelerate
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Configuring 🤗 Accelerate
|
||||
## Configuration
|
||||
|
||||
After installing, you need to configure 🤗 Accelerate for how the current system is setup for training.
|
||||
After installing, you need to configure Accelerate for how the current system is setup for training.
|
||||
To do so run the following and answer the questions prompted to you:
|
||||
|
||||
```bash
|
||||
@ -70,7 +68,8 @@ To write a barebones configuration that doesn't include options such as DeepSpee
|
||||
```bash
|
||||
python -c "from accelerate.utils import write_basic_config; write_basic_config(mixed_precision='fp16')"
|
||||
```
|
||||
🤗 Accelerate will automatically utilize the maximum number of GPUs available and set the mixed precision mode.
|
||||
|
||||
Accelerate will automatically utilize the maximum number of GPUs available and set the mixed precision mode.
|
||||
|
||||
To check that your configuration looks fine, run:
|
||||
|
||||
@ -80,23 +79,36 @@ accelerate env
|
||||
|
||||
An example output is shown below, which describes two GPUs on a single machine with no mixed precision being used:
|
||||
|
||||
|
||||
```bash
|
||||
- `Accelerate` version: 0.11.0.dev0
|
||||
- Platform: Linux-5.10.0-15-cloud-amd64-x86_64-with-debian-11.3
|
||||
- Python version: 3.7.12
|
||||
- Numpy version: 1.19.5
|
||||
- PyTorch version (GPU?): 1.12.0+cu102 (True)
|
||||
- `Accelerate` version: 1.2.0.dev0
|
||||
- Platform: Linux-6.8.0-47-generic-x86_64-with-glibc2.35
|
||||
- `accelerate` bash location: /home/zach/miniconda3/envs/accelerate/bin/accelerate
|
||||
- Python version: 3.10.13
|
||||
- Numpy version: 1.26.4
|
||||
- PyTorch version (GPU?): 2.5.1+cu124 (True)
|
||||
- PyTorch XPU available: False
|
||||
- PyTorch NPU available: False
|
||||
- PyTorch MLU available: False
|
||||
- PyTorch MUSA available: False
|
||||
- System RAM: 187.91 GB
|
||||
- GPU type: NVIDIA GeForce RTX 4090
|
||||
- `Accelerate` default config:
|
||||
- compute_environment: LOCAL_MACHINE
|
||||
- distributed_type: MULTI_GPU
|
||||
- mixed_precision: no
|
||||
- use_cpu: False
|
||||
- debug: False
|
||||
- num_processes: 2
|
||||
- machine_rank: 0
|
||||
- num_machines: 1
|
||||
- main_process_ip: None
|
||||
- main_process_port: None
|
||||
- gpu_ids: all
|
||||
- rdzv_backend: static
|
||||
- same_network: True
|
||||
- main_training_function: main
|
||||
- deepspeed_config: {}
|
||||
- fsdp_config: {}
|
||||
```
|
||||
- enable_cpu_affinity: False
|
||||
- downcast_bf16: no
|
||||
- tpu_use_cluster: False
|
||||
- tpu_use_sudo: False
|
||||
- tpu_env: []
|
||||
```
|
||||
|
||||
@ -13,9 +13,9 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Launching your 🤗 Accelerate scripts
|
||||
# Launching Accelerate scripts
|
||||
|
||||
In the previous tutorial, you were introduced to how to modify your current training script to use 🤗 Accelerate.
|
||||
In the previous tutorial, you were introduced to how to modify your current training script to use Accelerate.
|
||||
The final version of that code is shown below:
|
||||
|
||||
```python
|
||||
@ -69,14 +69,14 @@ Next, you need to launch it with `accelerate launch`.
|
||||
<Tip warning={true}>
|
||||
|
||||
It's recommended you run `accelerate config` before using `accelerate launch` to configure your environment to your liking.
|
||||
Otherwise 🤗 Accelerate will use very basic defaults depending on your system setup.
|
||||
Otherwise Accelerate will use very basic defaults depending on your system setup.
|
||||
|
||||
</Tip>
|
||||
|
||||
|
||||
## Using accelerate launch
|
||||
|
||||
🤗 Accelerate has a special CLI command to help you launch your code in your system through `accelerate launch`.
|
||||
Accelerate has a special CLI command to help you launch your code in your system through `accelerate launch`.
|
||||
This command wraps around all of the different commands needed to launch your script on various platforms, without you having to remember what each of them is.
|
||||
|
||||
<Tip>
|
||||
@ -97,11 +97,14 @@ Since this runs the various torch spawn methods, all of the expected environment
|
||||
For example, here is how to use `accelerate launch` with a single GPU:
|
||||
|
||||
```bash
|
||||
# for cuda device:
|
||||
CUDA_VISIBLE_DEVICES="0" accelerate launch {script_name.py} --arg1 --arg2 ...
|
||||
# for xpu device:
|
||||
ZE_AFFINITY_MASK="0" accelerate launch {script_name.py} --arg1 --arg2 ...
|
||||
```
|
||||
|
||||
You can also use `accelerate launch` without performing `accelerate config` first, but you may need to manually pass in the right configuration parameters.
|
||||
In this case, 🤗 Accelerate will make some hyperparameter decisions for you, e.g., if GPUs are available, it will use all of them by default without the mixed precision.
|
||||
In this case, Accelerate will make some hyperparameter decisions for you, e.g., if GPUs are available, it will use all of them by default without the mixed precision.
|
||||
Here is how you would use all GPUs and train with mixed precision disabled:
|
||||
|
||||
```bash
|
||||
@ -129,14 +132,14 @@ accelerate launch -h
|
||||
|
||||
<Tip>
|
||||
|
||||
Even if you are not using 🤗 Accelerate in your code, you can still use the launcher for starting your scripts!
|
||||
Even if you are not using Accelerate in your code, you can still use the launcher for starting your scripts!
|
||||
|
||||
</Tip>
|
||||
|
||||
For a visualization of this difference, that earlier `accelerate launch` on multi-gpu would look something like so with `torchrun`:
|
||||
|
||||
```bash
|
||||
MIXED_PRECISION="fp16" torchrun --nproc_per_node=2 --num_machines=1 {script_name.py} {--arg1} {--arg2} ...
|
||||
MIXED_PRECISION="fp16" torchrun --nproc_per_node=2 --nnodes=1 {script_name.py} {--arg1} {--arg2} ...
|
||||
```
|
||||
|
||||
You can also launch your script utilizing the launch CLI as a python module itself, enabling the ability to pass in other python-specific
|
||||
@ -178,7 +181,7 @@ accelerate launch {script_name.py} {--arg1} {--arg2} ...
|
||||
## Custom Configurations
|
||||
|
||||
As briefly mentioned earlier, `accelerate launch` should be mostly used through combining set configurations
|
||||
made with the `accelerate config` command. These configs are saved to a `default_config.yaml` file in your cache folder for 🤗 Accelerate.
|
||||
made with the `accelerate config` command. These configs are saved to a `default_config.yaml` file in your cache folder for Accelerate.
|
||||
This cache folder is located at (with decreasing order of priority):
|
||||
|
||||
- The content of your environment variable `HF_HOME` suffixed with `accelerate`.
|
||||
@ -211,7 +214,7 @@ accelerate launch --config_file {path/to/config/my_config_file.yaml} {script_nam
|
||||
```
|
||||
|
||||
## Multi-node training
|
||||
Multi-node training with 🤗Accelerate is similar to [multi-node training with torchrun](https://pytorch.org/tutorials/intermediate/ddp_series_multinode.html). The simplest way to launch a multi-node training run is to do the following:
|
||||
Multi-node training with Accelerate is similar to [multi-node training with torchrun](https://pytorch.org/tutorials/intermediate/ddp_series_multinode.html). The simplest way to launch a multi-node training run is to do the following:
|
||||
|
||||
- Copy your codebase and data to all nodes. (or place them on a shared filesystem)
|
||||
- Setup your python packages on all nodes.
|
||||
|
||||
@ -145,7 +145,7 @@ Set the mixed precision type to use in the [`Accelerator`], and then use the [`~
|
||||
```diff
|
||||
+ accelerator = Accelerator(mixed_precision="fp16")
|
||||
+ with accelerator.autocast():
|
||||
loss = complex_loss_function(outputs, target):
|
||||
loss = complex_loss_function(outputs, target)
|
||||
```
|
||||
|
||||
## Save and load
|
||||
@ -219,3 +219,6 @@ During training, you may want to save the current state of the model, optimizer,
|
||||
To further customize where and how states are saved through [`~Accelerator.save_state`], use the [`~utils.ProjectConfiguration`] class. For example, if `automatic_checkpoint_naming` is enabled, each saved checkpoint is stored at `Accelerator.project_dir/checkpoints/checkpoint_{checkpoint_number}`.
|
||||
|
||||
Any other stateful items to be stored should be registered with the [`~Accelerator.register_for_checkpointing`] method so they can be saved and loaded. Every object passed to this method to be stored must have a `load_state_dict` and `state_dict` function.
|
||||
|
||||
> [!TIP]
|
||||
> If you have [`torchdata>=0.8.0`](https://github.com/pytorch/data/tree/main) installed, you can additionally pass `use_stateful_dataloader=True` into your [`~utils.DataLoaderConfiguration`]. This extends Accelerate's DataLoader classes with a `load_state_dict` and `state_dict` function, and makes it so `Accelerator.save_state` and `Accelerator.load_state` also track how far into the training dataset it has read when persisting the model.
|
||||
|
||||
@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Launching Multi-GPU Training from a Jupyter Environment
|
||||
# Launching distributed training from Jupyter Notebooks
|
||||
|
||||
This tutorial teaches you how to fine tune a computer vision model with 🤗 Accelerate from a Jupyter Notebook on a distributed system.
|
||||
You will also learn how to setup a few requirements needed for ensuring your environment is configured properly, your data has been prepared properly, and finally how to launch training.
|
||||
@ -26,13 +26,13 @@ You will also learn how to setup a few requirements needed for ensuring your env
|
||||
|
||||
## Configuring the Environment
|
||||
|
||||
Before any training can be performed, a 🤗 Accelerate config file must exist in the system. Usually this can be done by running the following in a terminal and answering the prompts:
|
||||
Before any training can be performed, a Accelerate config file must exist in the system. Usually this can be done by running the following in a terminal and answering the prompts:
|
||||
|
||||
```bash
|
||||
accelerate config
|
||||
```
|
||||
|
||||
However, if general defaults are fine and you are *not* running on a TPU, 🤗Accelerate has a utility to quickly write your GPU configuration into a config file via [`utils.write_basic_config`].
|
||||
However, if general defaults are fine and you are *not* running on a TPU, Accelerate has a utility to quickly write your GPU configuration into a config file via [`utils.write_basic_config`].
|
||||
|
||||
The following code will restart Jupyter after writing the configuration, as CUDA code was called to perform this.
|
||||
|
||||
@ -327,7 +327,7 @@ def training_loop(mixed_precision="fp16", seed: int = 42, batch_size: int = 64):
|
||||
# Build dataloaders
|
||||
train_dataloader, eval_dataloader = get_dataloaders(batch_size)
|
||||
|
||||
# Instantiate the model (you build the model here so that the seed also controls new weight initaliziations)
|
||||
# Instantiate the model (you build the model here so that the seed also controls new weight initializations)
|
||||
model = create_model("resnet50d", pretrained=True, num_classes=len(label_to_id))
|
||||
|
||||
# Freeze the base model
|
||||
@ -454,7 +454,7 @@ epoch 4: 94.71
|
||||
|
||||
And that's it!
|
||||
|
||||
Please note that [`notebook_launcher`] ignores the 🤗 Accelerate config file, to launch based on the config use:
|
||||
Please note that [`notebook_launcher`] ignores the Accelerate config file, to launch based on the config use:
|
||||
|
||||
```bash
|
||||
accelerate launch
|
||||
|
||||
@ -15,10 +15,10 @@ rendered properly in your Markdown viewer.
|
||||
|
||||
# Overview
|
||||
|
||||
Welcome to the 🤗 Accelerate tutorials! These introductory guides will help catch you up to speed on working with 🤗 Accelerate.
|
||||
Welcome to the Accelerate tutorials! These introductory guides will help catch you up to speed on working with Accelerate.
|
||||
You'll learn how to modify your code to have it work with the API seamlessly, how to launch your script properly,
|
||||
and more!
|
||||
|
||||
These tutorials assume some basic knowledge of Python and familiarity with the PyTorch framework.
|
||||
|
||||
If you have any questions about 🤗 Accelerate, feel free to join and ask the community on our [forum](https://discuss.huggingface.co/c/accelerate/18).
|
||||
If you have any questions about Accelerate, feel free to join and ask the community on our [forum](https://discuss.huggingface.co/c/accelerate/18).
|
||||
@ -111,17 +111,17 @@ Input shapes:
|
||||
|
||||
For early stopping in distributed training, if each process has a specific stopping condition (e.g. validation loss), it may not be synchronized across all processes. As a result, a break can happen on process 0 but not on process 1 which will cause your code to hang indefinitely until a timeout occurs.
|
||||
|
||||
If you have early stopping conditionals, use the `set_breakpoint` and `check_breakpoint` methods to make sure all the processes
|
||||
If you have early stopping conditionals, use the `set_trigger` and `check_trigger` methods to make sure all the processes
|
||||
are ended correctly.
|
||||
|
||||
```py
|
||||
# Assume `should_do_breakpoint` is a custom defined function that returns a conditional,
|
||||
# and that conditional might be true only on process 1
|
||||
if should_do_breakpoint(loss):
|
||||
accelerator.set_breakpoint()
|
||||
accelerator.set_trigger()
|
||||
|
||||
# Later in the training script when we need to check for the breakpoint
|
||||
if accelerator.check_breakpoint():
|
||||
if accelerator.check_trigger():
|
||||
break
|
||||
```
|
||||
|
||||
@ -142,9 +142,9 @@ hostnames for each of the nodes.
|
||||
mpirun -f hostfile -n {number of nodes} -ppn 1 hostname
|
||||
```
|
||||
|
||||
## CUDA Out-of-Memory
|
||||
## Out-of-Memory
|
||||
|
||||
One of the most frustrating errors when it comes to running training scripts is hitting "CUDA Out-of-Memory". The entire script needs to be restarted and any progress is lost.
|
||||
One of the most frustrating errors when it comes to running training scripts is hitting "Out-of-Memory" on devices like CUDA, XPU or CPU. The entire script needs to be restarted and any progress is lost.
|
||||
|
||||
To address this problem, Accelerate provides the [`find_executable_batch_size`] utility that is heavily based on [toma](https://github.com/BlackHC/toma).
|
||||
This utility retries code that fails due to OOM (out-of-memory) conditions and automatically lowers batch sizes. For each OOM condition, the algorithm decreases the batch size by half and retries the code until it succeeds.
|
||||
@ -153,7 +153,7 @@ To use [`find_executable_batch_size`], restructure your training function to inc
|
||||
|
||||
<Tip warning={true}>
|
||||
|
||||
The inner function **must** take batch size as the first parameter, but we do not pass one to it when called. The wrapper will handles this for you. Any object (models, optimizers) that consumes CUDA memory and is passed to the [`Accelerator`] also **must** be declared inside the inner function.
|
||||
The inner function **must** take batch size as the first parameter, but we do not pass one to it when called. The wrapper will handles this for you. Any object (models, optimizers) that consumes device memory and is passed to the [`Accelerator`] also **must** be declared inside the inner function.
|
||||
|
||||
</Tip>
|
||||
|
||||
@ -204,8 +204,8 @@ Vastly different GPUs within the same setup can lead to performance bottlenecks.
|
||||
|
||||
If none of the solutions and advice here helped resolve your issue, you can always reach out to the community and Accelerate team for help.
|
||||
|
||||
- Ask for help on the Hugging Face forums by posting your question in the [🤗 Accelerate category](https://discuss.huggingface.co/c/accelerate/18). Make sure to write a descriptive post with relevant context about your setup and reproducible code to maximize the likelihood that your problem is solved!
|
||||
- Ask for help on the Hugging Face forums by posting your question in the [Accelerate category](https://discuss.huggingface.co/c/accelerate/18). Make sure to write a descriptive post with relevant context about your setup and reproducible code to maximize the likelihood that your problem is solved!
|
||||
|
||||
- Post a question on [Discord](http://hf.co/join/discord), and let the team and the community help you.
|
||||
|
||||
- Create an Issue on the 🤗 Accelerate [GitHub repository](https://github.com/huggingface/accelerate/issues) if you think you've found a bug related to the library. Include context regarding the bug and details about your distributed setup to help us better figure out what's wrong and how we can fix it.
|
||||
- Create an Issue on the Accelerate [GitHub repository](https://github.com/huggingface/accelerate/issues) if you think you've found a bug related to the library. Include context regarding the bug and details about your distributed setup to help us better figure out what's wrong and how we can fix it.
|
||||
|
||||
@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Handling big models for inference
|
||||
# Loading big models into memory
|
||||
|
||||
When loading a pre-trained model in PyTorch, the usual workflow looks like this:
|
||||
|
||||
@ -46,7 +46,7 @@ This API is quite new and still in its experimental stage. While we strive to pr
|
||||
|
||||
### Instantiating an empty model
|
||||
|
||||
The first tool 🤗 Accelerate introduces to help with big models is a context manager [`init_empty_weights`] that helps you initialize a model without using any RAM so that step 1 can be done on models of any size. Here is how it works:
|
||||
The first tool Accelerate introduces to help with big models is a context manager [`init_empty_weights`] that helps you initialize a model without using any RAM so that step 1 can be done on models of any size. Here is how it works:
|
||||
|
||||
```py
|
||||
from accelerate import init_empty_weights
|
||||
@ -74,7 +74,7 @@ initializes an empty model with a bit more than 100B parameters. Behind the scen
|
||||
|
||||
It's possible your model is so big that even a single copy won't fit in RAM. That doesn't mean it can't be loaded: if you have one or several GPUs, this is more memory available to store your model. In this case, it's better if your checkpoint is split into several smaller files that we call checkpoint shards.
|
||||
|
||||
🤗 Accelerate will handle sharded checkpoints as long as you follow the following format: your checkpoint should be in a folder, with several files containing the partial state dicts, and there should be an index in the JSON format that contains a dictionary mapping parameter names to the file containing their weights. You can easily shard your model with [`~Accelerator.save_model`]. For instance, we could have a folder containing:
|
||||
Accelerate will handle sharded checkpoints as long as you follow the following format: your checkpoint should be in a folder, with several files containing the partial state dicts, and there should be an index in the JSON format that contains a dictionary mapping parameter names to the file containing their weights. You can easily shard your model with [`~Accelerator.save_model`]. For instance, we could have a folder containing:
|
||||
|
||||
```bash
|
||||
first_state_dict.bin
|
||||
@ -97,9 +97,9 @@ and `first_state_dict.bin` containing the weights for `"linear1.weight"` and `"l
|
||||
|
||||
### Loading weights
|
||||
|
||||
The second tool 🤗 Accelerate introduces is a function [`load_checkpoint_and_dispatch`], that will allow you to load a checkpoint inside your empty model. This supports full checkpoints (a single file containing the whole state dict) as well as sharded checkpoints. It will also automatically dispatch those weights across the devices you have available (GPUs, CPU RAM), so if you are loading a sharded checkpoint, the maximum RAM usage will be the size of the biggest shard.
|
||||
The second tool Accelerate introduces is a function [`load_checkpoint_and_dispatch`], that will allow you to load a checkpoint inside your empty model. This supports full checkpoints (a single file containing the whole state dict) as well as sharded checkpoints. It will also automatically dispatch those weights across the devices you have available (GPUs, CPU RAM), so if you are loading a sharded checkpoint, the maximum RAM usage will be the size of the biggest shard.
|
||||
|
||||
If you want to use big model inference with 🤗 Transformers models, check out this [documentation](https://huggingface.co/docs/transformers/main/en/main_classes/model#large-model-loading).
|
||||
If you want to use big model inference with Transformers models, check out this [documentation](https://huggingface.co/docs/transformers/main/en/main_classes/model#large-model-loading).
|
||||
|
||||
Here is how we can use this to load the [GPT2-1.5B](https://huggingface.co/marcsun13/gpt2-xl-linear-sharded) model.
|
||||
|
||||
@ -145,7 +145,7 @@ model = load_checkpoint_and_dispatch(
|
||||
)
|
||||
```
|
||||
|
||||
By passing `device_map="auto"`, we tell 🤗 Accelerate to determine automatically where to put each layer of the model depending on the available resources:
|
||||
By passing `device_map="auto"`, we tell Accelerate to determine automatically where to put each layer of the model depending on the available resources:
|
||||
- first, we use the maximum space available on the GPU(s)
|
||||
- if we still need space, we store the remaining weights on the CPU
|
||||
- if there is not enough RAM, we store the remaining weights on the hard drive as memory-mapped tensors
|
||||
@ -159,7 +159,7 @@ include a residual connection of some kind.
|
||||
|
||||
#### The `device_map`
|
||||
|
||||
You can see the `device_map` that 🤗 Accelerate picked by accessing the `hf_device_map` attribute of your model:
|
||||
You can see the `device_map` that Accelerate picked by accessing the `hf_device_map` attribute of your model:
|
||||
|
||||
```py
|
||||
model.hf_device_map
|
||||
@ -210,7 +210,7 @@ outputs = model.generate(x1, max_new_tokens=10, do_sample=False)[0]
|
||||
tokenizer.decode(outputs.cpu().squeeze())
|
||||
```
|
||||
|
||||
Behind the scenes, 🤗 Accelerate added hooks to the model, so that:
|
||||
Behind the scenes, Accelerate added hooks to the model, so that:
|
||||
- at each layer, the inputs are put on the right device (so even if your model is spread across several GPUs, it works)
|
||||
- for the weights offloaded on the CPU, they are put on a GPU just before the forward pass and cleaned up just after
|
||||
- for the weights offloaded on the hard drive, they are loaded in RAM then put on a GPU just before the forward pass and cleaned up just after
|
||||
@ -225,7 +225,7 @@ This way, your model can run for inference even if it doesn't fit on one of the
|
||||
|
||||
### Designing a device map
|
||||
|
||||
You can let 🤗 Accelerate handle the device map computation by setting `device_map` to one of the supported options (`"auto"`, `"balanced"`, `"balanced_low_0"`, `"sequential"`) or create one yourself if you want more control over where each layer should go.
|
||||
You can let Accelerate handle the device map computation by setting `device_map` to one of the supported options (`"auto"`, `"balanced"`, `"balanced_low_0"`, `"sequential"`) or create one yourself if you want more control over where each layer should go.
|
||||
|
||||
<Tip>
|
||||
|
||||
|
||||
@ -13,9 +13,9 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Deferring Executions
|
||||
# Executing and deferring jobs
|
||||
|
||||
When you run your usual script, instructions are executed in order. Using 🤗 Accelerate to deploy your script on several
|
||||
When you run your usual script, instructions are executed in order. Using Accelerate to deploy your script on several
|
||||
GPUs at the same time introduces a complication: while each process executes all instructions in order, some may be
|
||||
faster than others.
|
||||
|
||||
@ -127,4 +127,4 @@ for (x,y) in data_loader:
|
||||
# Later in the training script when we need to check for the breakpoint
|
||||
if accelerator.check_trigger():
|
||||
break
|
||||
```
|
||||
```
|
||||
|
||||
@ -13,15 +13,15 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Moving between FSDP And DeepSpeed
|
||||
# FSDP vs DeepSpeed
|
||||
|
||||
🤗 Accelerate offers flexibilty of training frameworks, by integrating two extremely powerful tools for distributed training, namely [Pytorch FSDP](../usage_guides/fsdp.md) and [Microsoft DeepSpeed](../usage_guides/deepspeed.md). The aim of this tutorial is to draw parallels, as well as to outline potential differences, to empower the user to switch seamlessly between these two frameworks.
|
||||
Accelerate offers flexibilty of training frameworks, by integrating two extremely powerful tools for distributed training, namely [Pytorch FSDP](../usage_guides/fsdp) and [Microsoft DeepSpeed](../usage_guides/deepspeed). The aim of this tutorial is to draw parallels, as well as to outline potential differences, to empower the user to switch seamlessly between these two frameworks.
|
||||
|
||||
<Tip>
|
||||
|
||||
To switch between the frameworks, we recommend launching code 🤗 `accelerate launch` passing in the correct config file with `--config_file`, or passing in the respective arguments directly for [FSDP and DeepSpeed](../package_reference/cli#accelerate-launch) .
|
||||
To switch between the frameworks, we recommend launching code `accelerate launch` passing in the correct config file with `--config_file`, or passing in the respective arguments directly for [FSDP and DeepSpeed](../package_reference/cli#accelerate-launch) .
|
||||
|
||||
Example 🤗 Accelerate configurations can be found here for [DeepSpeed](../usage_guides/deepspeed#accelerate-deepspeed-plugin) and [FSDP](../usage_guides/fsdp#how-it-works-out-of-the-box), or in the [example zoo under "Launch Configurations"](../usage_guides/explore)
|
||||
Example Accelerate configurations can be found here for [DeepSpeed](../usage_guides/deepspeed#accelerate-deepspeed-plugin) and [FSDP](../usage_guides/fsdp#how-it-works-out-of-the-box), or in the [example zoo under "Launch Configurations"](../usage_guides/explore)
|
||||
|
||||
</Tip>
|
||||
|
||||
@ -47,7 +47,7 @@ parameters summoning | FSDP<br>DeepSpeed | `--fsdp_use_orig_params`<br>None | `t
|
||||
parameters syncing | FSDP<br>DeepSpeed | `--fsdp_sync_module_states`<br>None | `true` |
|
||||
training | FSDP<br>DeepSpeed | None<br>`--gradient_accumulation_steps`<br>`--gradient_clipping` | <br>`auto`<br>`auto` | Transparent to user
|
||||
|
||||
For detailed descriptions of the above, refer to [🤗 `Accelerate` launch documentation](../package_reference/cli#accelerate-launch).
|
||||
For detailed descriptions of the above, refer to [`Accelerate` launch documentation](../package_reference/cli#accelerate-launch).
|
||||
|
||||
<Tip>
|
||||
|
||||
@ -94,7 +94,7 @@ FSDP only allows *all-or-nothing* offload (i.e., either offload parameters, grad
|
||||
### Prefetching
|
||||
|
||||
FSDP allows two prefetching configurations `--fsdp_forward_prefetch` and `--fsdp_backward_prefetch` to improve overlap of comms / computation at a cost of extra memory, see [FSDP documentation](https://pytorch.org/docs/stable/fsdp.html).
|
||||
For DeepSpeed, the prefetching will be turned on when needed, and it turns on depending on certain hyper-params like `stage3_param_persistence_threshold`, `stage3_max_reuse_distance`, etc, [that can be configured for Zero3](https://www.deepspeed.ai/docs/config-json/#parameter-offloading); 🤗 `accelerate` may set these hyper-params automatically if you don't set those explicitly in the deepspeed config file.
|
||||
For DeepSpeed, the prefetching will be turned on when needed, and it turns on depending on certain hyper-params like `stage3_param_persistence_threshold`, `stage3_max_reuse_distance`, etc, [that can be configured for Zero3](https://www.deepspeed.ai/docs/config-json/#parameter-offloading); `accelerate` may set these hyper-params automatically if you don't set those explicitly in the deepspeed config file.
|
||||
|
||||
<Tip>
|
||||
|
||||
@ -104,11 +104,11 @@ For DeepSpeed, the prefetching will be turned on when needed, and it turns on de
|
||||
|
||||
### Model Loading
|
||||
|
||||
While FSDP require an explicit `--fsdp_cpu_ram_efficient_loading true` to activate efficient model loading, 🤗 `transformers` will activate the similar feature whenever DeepSpeed Zero3 is used.
|
||||
While FSDP require an explicit `--fsdp_cpu_ram_efficient_loading true` to activate efficient model loading, `transformers` will activate the similar feature whenever DeepSpeed Zero3 is used.
|
||||
|
||||
<Tip>
|
||||
|
||||
For FSDP, whenever setting `--fsdp_cpu_ram_efficient_loading true`, 🤗 `accelerate` will automatically set `sync_module_states` to true.
|
||||
For FSDP, whenever setting `--fsdp_cpu_ram_efficient_loading true`, `accelerate` will automatically set `sync_module_states` to true.
|
||||
For RAM efficient loading the weights will be loaded only in a singe rank, and thus requires `sync_module_states` to broadcast weights to other ranks.
|
||||
|
||||
</Tip>
|
||||
@ -189,4 +189,4 @@ Framework | Model Loading (`torch_dtype`) | Mixed Precision | Preparation (Local
|
||||
--|--|--|--|--|--
|
||||
FSDP | bf16 | default (none) | bf16 | bf16 | bf16
|
||||
FSDP | bf16 | bf16 | fp32 | bf16 | fp32
|
||||
DeepSpeed | bf16 | bf16 | fp32 | bf16 | fp32
|
||||
DeepSpeed | bf16 | bf16 | fp32 | bf16 | fp32
|
||||
|
||||
@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Gradient Synchronization
|
||||
# Gradient synchronization
|
||||
|
||||
PyTorch's distributed module operates by communicating back and forth between all of the GPUs in your system.
|
||||
This communication takes time, and ensuring all processes know the states of each other happens at particular triggerpoints
|
||||
@ -28,7 +28,7 @@ from torch.nn.parallel import DistributedDataParallel
|
||||
model = nn.Linear(10, 10)
|
||||
ddp_model = DistributedDataParallel(model)
|
||||
```
|
||||
In 🤗 Accelerate this conversion happens automatically when calling [`~Accelerator.prepare`] and passing in your model.
|
||||
In Accelerate this conversion happens automatically when calling [`~Accelerator.prepare`] and passing in your model.
|
||||
|
||||
```diff
|
||||
+ from accelerate import Accelerator
|
||||
@ -90,7 +90,7 @@ for index, batch in enumerate(dataloader):
|
||||
optimizer.step()
|
||||
```
|
||||
|
||||
In 🤗 Accelerate to make this an API that can be called no matter the training device (though it may not do anything if you are not in a distributed system!),
|
||||
In Accelerate to make this an API that can be called no matter the training device (though it may not do anything if you are not in a distributed system!),
|
||||
`ddp_model.no_sync` gets replaced with [`~Accelerator.no_sync`] and operates the same way:
|
||||
|
||||
```diff
|
||||
|
||||
@ -13,9 +13,9 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# 🤗 Accelerate's internal mechanisms
|
||||
# Accelerate's internal mechanisms
|
||||
|
||||
Internally, 🤗 Accelerate works by first analyzing the environment in which the script is launched to determine which
|
||||
Internally, Accelerate works by first analyzing the environment in which the script is launched to determine which
|
||||
kind of distributed setup is used, how many different processes there are and which one the current script is in. All
|
||||
that information is stored in the [`~AcceleratorState`].
|
||||
|
||||
@ -69,4 +69,6 @@ setting the same seed in the main random number generator in all processes.
|
||||
|
||||
</Tip>
|
||||
|
||||
If you have [`torchdata>=0.8.0`](https://github.com/pytorch/data/tree/main) installed, and you have passed `use_stateful_dataloader=True` into your [`~utils.DataLoaderConfiguration`], these classes will directly inherit from `StatefulDataLoader` instead, and maintain a `state_dict`.
|
||||
|
||||
For more details about the internals, see the [Internals page](package_reference/torch_wrappers).
|
||||
|
||||
@ -13,12 +13,12 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Low Precision Training Methods
|
||||
# Low precision training methods
|
||||
|
||||
The release of new kinds of hardware led to the emergence of new training paradigms that better utilize them. Currently, this is in the form of training
|
||||
in 8-bit precision using packages such as [TransformersEngine](https://github.com/NVIDIA/TransformerEngine) (TE) or [MS-AMP](https://github.com/Azure/MS-AMP/tree/main).
|
||||
|
||||
For an introduction to the topics discussed today, we recommend reviewing the [low-precision usage guide](../usage_guides/low_precision_training.md) as this documentation will reference it regularly.
|
||||
For an introduction to the topics discussed today, we recommend reviewing the [low-precision usage guide](../usage_guides/low_precision_training) as this documentation will reference it regularly.
|
||||
|
||||
## A Quick Chart
|
||||
|
||||
@ -36,7 +36,7 @@ MS-AMP O3 | FP8 | FP8 | FP8 | FP16 | FP8 | FP8+FP16
|
||||
|
||||
`TransformersEngine` is the first solution to trying to train in 8-bit floating point. It works by using drop-in replacement layers for certain ones in a model that utilizes their FP8-engine to reduce the number of bits (such as 32 to 8) without degrading the final accuracy of the model.
|
||||
|
||||
Specifically, 🤗 Accelerate will find and replace the following layers with `TransformersEngine` versions:
|
||||
Specifically, Accelerate will find and replace the following layers with `TransformersEngine` versions:
|
||||
|
||||
* `nn.LayerNorm` for `te.LayerNorm`
|
||||
* `nn.Linear` for `te.Linear`
|
||||
@ -50,7 +50,7 @@ The `TransformerEngine` can receive many different arguments that customize how
|
||||
|
||||
* `margin`: The margin to use for the gradient scaling.
|
||||
* `interval`: The interval to use for how often the scaling factor is recomputed.
|
||||
* `fp8_format``: The format to use for the FP8 recipe. Must be one of `E4M3` or `HYBRID`.
|
||||
* `fp8_format``: The format to use for the FP8 recipe. Must be one of `HYBRID` or `E4M3`. (Generally `HYBRID` for training, `E4M3` for evaluation)
|
||||
* `amax_history_len`: The length of the history to use for the scaling factor computation
|
||||
* `amax_compute_algo`: The algorithm to use for the scaling factor computation. Must be one of `max` or `most_recent`.
|
||||
* `override_linear_precision`: Whether or not to execute `fprop`, `dgrad`, and `wgrad` GEMMS in higher precision.
|
||||
@ -67,7 +67,7 @@ MS-AMP takes a different approach to `TransformersEngine` by providing three dif
|
||||
|
||||
* The second optimization level (`O2`) improves upon this by also reducing the precision of the optimizer states. One is in FP8 while the other is in FP16. Generally it's been shown that this will only provide a net-gain of no degraded end accuracy, increased training speed, and reduced memory as now every state is either in FP16 or FP8.
|
||||
|
||||
* Finally, MS-AMP has a third optimization level (`O3`) which helps during DDP scenarios such as DeepSpeed. The weights of the model in memory are fully cast to FP8, and the master weights are now stored in FP16. This fully reduces memory by the highest factor as now not only is almost everything in FP8, only two states are left in FP16. Currently, only DeepSpeed versions up through 0.9.2 are supported, so this capability is not included in the 🤗 Accelerate integration
|
||||
* Finally, MS-AMP has a third optimization level (`O3`) which helps during DDP scenarios such as DeepSpeed. The weights of the model in memory are fully cast to FP8, and the master weights are now stored in FP16. This fully reduces memory by the highest factor as now not only is almost everything in FP8, only two states are left in FP16. Currently, only DeepSpeed versions up through 0.9.2 are supported, so this capability is not included in the Accelerate integration
|
||||
|
||||
## Combining the two
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Comparing performance between different device setups
|
||||
# Comparing performance across distributed setups
|
||||
|
||||
Evaluating and comparing the performance from different setups can be quite tricky if you don't know what to look for.
|
||||
For example, you cannot run the same script with the same batch size across TPU, multi-GPU, and single-GPU with Accelerate
|
||||
@ -43,13 +43,13 @@ Why is this important? Under the hood this will set **5** different seed setting
|
||||
random.seed(seed)
|
||||
np.random.seed(seed)
|
||||
torch.manual_seed(seed)
|
||||
torch.cuda.manual_seed_all(seed)
|
||||
torch.cuda.manual_seed_all(seed) # or torch.xpu.manual_seed_all, etc
|
||||
# ^^ safe to call this function even if cuda is not available
|
||||
if is_torch_xla_available():
|
||||
xm.set_rng_state(seed)
|
||||
```
|
||||
|
||||
The random state, numpy's state, torch, torch's cuda state, and if TPUs are available torch_xla's cuda state.
|
||||
The random state, numpy's state, torch, torch's device state, and if TPUs are available torch_xla's cuda state.
|
||||
|
||||
## Observed Batch Sizes
|
||||
|
||||
|
||||
@ -13,9 +13,9 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Training on TPUs with 🤗 Accelerate
|
||||
# Training on TPUs
|
||||
|
||||
Training on TPUs can be slightly different from training on multi-gpu, even with 🤗 Accelerate. This guide aims to show you
|
||||
Training on TPUs can be slightly different from training on multi-gpu, even with Accelerate. This guide aims to show you
|
||||
where you should be careful and why, as well as the best practices in general.
|
||||
|
||||
## Training in a Notebook
|
||||
@ -81,7 +81,7 @@ notebook_launcher(training_function)
|
||||
|
||||
<Tip>
|
||||
|
||||
The `notebook_launcher` will default to 8 processes if 🤗 Accelerate has been configured for a TPU
|
||||
The `notebook_launcher` will default to 8 processes if Accelerate has been configured for a TPU
|
||||
|
||||
</Tip>
|
||||
|
||||
@ -128,10 +128,10 @@ And finally calling the training function with:
|
||||
|
||||
## Mixed Precision and Global Variables
|
||||
|
||||
As mentioned in the [mixed precision tutorial](../usage_guides/mixed_precision), 🤗 Accelerate supports fp16 and bf16, both of which can be used on TPUs.
|
||||
As mentioned in the [mixed precision tutorial](../usage_guides/mixed_precision), Accelerate supports fp16 and bf16, both of which can be used on TPUs.
|
||||
That being said, ideally `bf16` should be utilized as it is extremely efficient to use.
|
||||
|
||||
There are two "layers" when using `bf16` and 🤗 Accelerate on TPUs, at the base level and at the operation level.
|
||||
There are two "layers" when using `bf16` and Accelerate on TPUs, at the base level and at the operation level.
|
||||
|
||||
At the base level, this is enabled when passing `mixed_precision="bf16"` to `Accelerator`, such as:
|
||||
```python
|
||||
|
||||
BIN
docs/source/imgs/profile_export.png
Normal file
BIN
docs/source/imgs/profile_export.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
@ -15,7 +15,7 @@ rendered properly in your Markdown viewer.
|
||||
|
||||
# Accelerate
|
||||
|
||||
🤗 Accelerate is a library that enables the same PyTorch code to be run across any distributed configuration by adding just four lines of code! In short, training and inference at scale made simple, efficient and adaptable.
|
||||
Accelerate is a library that enables the same PyTorch code to be run across any distributed configuration by adding just four lines of code! In short, training and inference at scale made simple, efficient and adaptable.
|
||||
|
||||
```diff
|
||||
+ from accelerate import Accelerator
|
||||
@ -37,7 +37,7 @@ rendered properly in your Markdown viewer.
|
||||
scheduler.step()
|
||||
```
|
||||
|
||||
Built on `torch_xla` and `torch.distributed`, 🤗 Accelerate takes care of the heavy lifting, so you don't have to write any custom code to adapt to these platforms.
|
||||
Built on `torch_xla` and `torch.distributed`, Accelerate takes care of the heavy lifting, so you don't have to write any custom code to adapt to these platforms.
|
||||
Convert existing codebases to utilize [DeepSpeed](usage_guides/deepspeed), perform [fully sharded data parallelism](usage_guides/fsdp), and have automatic support for mixed-precision training!
|
||||
|
||||
<Tip>
|
||||
@ -56,11 +56,11 @@ accelerate launch {my_script.py}
|
||||
<div class="w-full flex flex-col space-y-4 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="./basic_tutorials/overview"
|
||||
><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">Tutorials</div>
|
||||
<p class="text-gray-700">Learn the basics and become familiar with using 🤗 Accelerate. Start here if you are using 🤗 Accelerate for the first time!</p>
|
||||
<p class="text-gray-700">Learn the basics and become familiar with using Accelerate. Start here if you are using Accelerate for the first time!</p>
|
||||
</a>
|
||||
<a class="!no-underline border dark:border-gray-700 p-5 rounded-lg shadow hover:shadow-lg" href="./usage_guides/explore"
|
||||
><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">How-to guides</div>
|
||||
<p class="text-gray-700">Practical guides to help you achieve a specific goal. Take a look at these guides to learn how to use 🤗 Accelerate to solve real-world problems.</p>
|
||||
<p class="text-gray-700">Practical guides to help you achieve a specific goal. Take a look at these guides to learn how to use Accelerate to solve real-world problems.</p>
|
||||
</a>
|
||||
<a class="!no-underline border dark:border-gray-700 p-5 rounded-lg shadow hover:shadow-lg" href="./concept_guides/gradient_synchronization"
|
||||
><div class="w-full text-center bg-gradient-to-br from-pink-400 to-pink-500 rounded-lg py-1.5 font-semibold mb-5 text-white text-lg leading-relaxed">Conceptual guides</div>
|
||||
@ -68,7 +68,7 @@ accelerate launch {my_script.py}
|
||||
</a>
|
||||
<a class="!no-underline border dark:border-gray-700 p-5 rounded-lg shadow hover:shadow-lg" href="./package_reference/accelerator"
|
||||
><div class="w-full text-center bg-gradient-to-br from-purple-400 to-purple-500 rounded-lg py-1.5 font-semibold mb-5 text-white text-lg leading-relaxed">Reference</div>
|
||||
<p class="text-gray-700">Technical descriptions of how 🤗 Accelerate classes and methods work.</p>
|
||||
<p class="text-gray-700">Technical descriptions of how Accelerate classes and methods work.</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -15,33 +15,88 @@ rendered properly in your Markdown viewer.
|
||||
|
||||
# Working with large models
|
||||
|
||||
## Dispatching and Offloading Models
|
||||
## Dispatch and offload
|
||||
|
||||
### init_empty_weights
|
||||
|
||||
[[autodoc]] big_modeling.init_empty_weights
|
||||
|
||||
### cpu_offload
|
||||
|
||||
[[autodoc]] big_modeling.cpu_offload
|
||||
|
||||
### cpu_offload_with_hook
|
||||
|
||||
[[autodoc]] big_modeling.cpu_offload_with_hook
|
||||
|
||||
### disk_offload
|
||||
|
||||
[[autodoc]] big_modeling.disk_offload
|
||||
|
||||
### dispatch_model
|
||||
|
||||
[[autodoc]] big_modeling.dispatch_model
|
||||
|
||||
### load_checkpoint_and_dispatch
|
||||
|
||||
[[autodoc]] big_modeling.load_checkpoint_and_dispatch
|
||||
|
||||
### load_checkpoint_in_model
|
||||
|
||||
[[autodoc]] big_modeling.load_checkpoint_in_model
|
||||
|
||||
### infer_auto_device_map
|
||||
|
||||
[[autodoc]] utils.infer_auto_device_map
|
||||
|
||||
## Model Hooks
|
||||
## Hooks
|
||||
|
||||
### Hook Classes
|
||||
### ModelHook
|
||||
|
||||
[[autodoc]] hooks.ModelHook
|
||||
|
||||
### AlignDevicesHook
|
||||
|
||||
[[autodoc]] hooks.AlignDevicesHook
|
||||
|
||||
### SequentialHook
|
||||
|
||||
[[autodoc]] hooks.SequentialHook
|
||||
|
||||
### Adding Hooks
|
||||
## Adding Hooks
|
||||
|
||||
### add_hook_to_module
|
||||
|
||||
[[autodoc]] hooks.add_hook_to_module
|
||||
|
||||
### attach_execution_device_hook
|
||||
|
||||
[[autodoc]] hooks.attach_execution_device_hook
|
||||
|
||||
### attach_align_device_hook
|
||||
|
||||
[[autodoc]] hooks.attach_align_device_hook
|
||||
|
||||
### attach_align_device_hook_on_blocks
|
||||
|
||||
[[autodoc]] hooks.attach_align_device_hook_on_blocks
|
||||
|
||||
### Removing Hooks
|
||||
## Removing Hooks
|
||||
|
||||
### remove_hook_from_module
|
||||
|
||||
[[autodoc]] hooks.remove_hook_from_module
|
||||
[[autodoc]] hooks.remove_hook_from_submodules
|
||||
|
||||
### remove_hook_from_submodules
|
||||
|
||||
[[autodoc]] hooks.remove_hook_from_submodules
|
||||
|
||||
## Utilities
|
||||
|
||||
### has_offloaded_params
|
||||
|
||||
[[autodoc]] utils.has_offloaded_params
|
||||
|
||||
### align_module_device
|
||||
|
||||
[[autodoc]] utils.align_module_device
|
||||
@ -145,10 +145,11 @@ values. They can also be passed in manually.
|
||||
|
||||
The following arguments are useful for fine-tuning how available hardware should be used
|
||||
|
||||
* `--mixed_precision {no,fp16,bf16}` (`str`) -- Whether or not to use mixed precision training. Choose between FP16 and BF16 (bfloat16) training. BF16 training is only supported on Nvidia Ampere GPUs and PyTorch 1.10 or later.
|
||||
* `--mixed_precision {no,fp16,bf16,fp8}` (`str`) -- Whether or not to use mixed precision training. Choose between FP16 and BF16 (bfloat16) training. BF16 training is only supported on Nvidia Ampere GPUs and PyTorch 1.10 or later.
|
||||
* `--num_processes NUM_PROCESSES` (`int`) -- The total number of processes to be launched in parallel.
|
||||
* `--num_machines NUM_MACHINES` (`int`) -- The total number of machines used in this training.
|
||||
* `--num_cpu_threads_per_process NUM_CPU_THREADS_PER_PROCESS` (`int`) -- The number of CPU threads per process. Can be tuned for optimal performance.
|
||||
* `--enable_cpu_affinity` (`bool`) -- Whether or not CPU affinity and balancing should be enabled. Currently only supported on NVIDIA hardware.
|
||||
|
||||
**Training Paradigm Arguments**:
|
||||
|
||||
@ -165,19 +166,26 @@ The following arguments are only useful when `multi_gpu` is passed or multi-gpu
|
||||
|
||||
* `--gpu_ids` (`str`) -- What GPUs (by id) should be used for training on this machine as a comma-seperated list
|
||||
* `--same_network` (`bool`) -- Whether all machines used for multinode training exist on the same local network.
|
||||
* `--machine_rank MACHINE_RANK` (`int`) -- The rank of the machine on which this script is launched.
|
||||
* `--main_process_ip MAIN_PROCESS_IP` (`str`) -- The IP address of the machine of rank 0.
|
||||
* `--main_process_port MAIN_PROCESS_PORT` (`int`) -- The port to use to communicate with the machine of rank 0.
|
||||
* `--rdzv_backend` (`str`) -- The rendezvous method to use, such as "static" or "c10d"
|
||||
* `--machine_rank` (`int`) -- The rank of the machine on which this script is launched.
|
||||
* `--main_process_ip` (`str`) -- The IP address of the machine of rank 0.
|
||||
* `--main_process_port` (`int`) -- The port to use to communicate with the machine of rank 0.
|
||||
* `-t`, `--tee` (`str`) -- Tee std streams into a log file and also to console.
|
||||
* `--log_dir` (`str`) -- Base directory to use for log files when using torchrun/torch.distributed.run as launcher. Use with --tee to redirect std streams info log files.
|
||||
* `--role` (`str`) -- User-defined role for the workers.
|
||||
* `--rdzv_backend` (`str`) -- The rendezvous method to use, such as 'static' (the default) or 'c10d'
|
||||
* `--rdzv_conf` (`str`) -- Additional rendezvous configuration (<key1>=<value1>,<key2>=<value2>,...).
|
||||
* `--max_restarts` (`int`) -- Maximum number of worker group restarts before failing.
|
||||
* `--monitor_interval` (`float`) -- Interval, in seconds, to monitor the state of workers.
|
||||
* `--monitor_interval` (`int`) -- Interval, in seconds, to monitor the state of workers.
|
||||
|
||||
**TPU Arguments**:
|
||||
|
||||
The following arguments are only useful when `tpu` is passed or TPU training is configured through `accelerate config`:
|
||||
|
||||
* `--main_training_function MAIN_TRAINING_FUNCTION` (`str`) -- The name of the main function to be executed in your script.
|
||||
* `--tpu_cluster` (`bool`) -- Whether to use a GCP TPU pod for training.
|
||||
* `--tpu_use_sudo` (`bool`) -- Whether to use `sudo` when running the TPU training script in each pod.
|
||||
* `--vm` (`str`) -- List of single Compute VM instance names. If not provided we assume usage of instance groups. For TPU pods.
|
||||
* `--env` (`str`) -- List of environment variables to set on the Compute VM instances. For TPU pods.
|
||||
* `--main_training_function` (`str`) -- The name of the main function to be executed in your script (only for TPU training).
|
||||
* `--downcast_bf16` (`bool`) -- Whether when using bf16 precision on TPUs if both float and double tensors are cast to bfloat16 or if double tensors remain as float32.
|
||||
|
||||
**DeepSpeed Arguments**:
|
||||
@ -188,6 +196,7 @@ The following arguments are only useful when `use_deepspeed` is passed or `deeps
|
||||
* `--zero_stage` (`int`) -- DeepSpeed's ZeRO optimization stage.
|
||||
* `--offload_optimizer_device` (`str`) -- Decides where (none|cpu|nvme) to offload optimizer states.
|
||||
* `--offload_param_device` (`str`) -- Decides where (none|cpu|nvme) to offload parameters.
|
||||
* `--offload_optimizer_nvme_path` (`str`) -- Decides Nvme Path to offload optimizer states.
|
||||
* `--gradient_accumulation_steps` (`int`) -- No of gradient_accumulation_steps used in your training script.
|
||||
* `--gradient_clipping` (`float`) -- Gradient clipping value used in your training script.
|
||||
* `--zero3_init_flag` (`str`) -- Decides Whether (true|false) to enable `deepspeed.zero.Init` for constructing massive models. Only applicable with DeepSpeed ZeRO Stage-3.
|
||||
@ -196,6 +205,7 @@ The following arguments are only useful when `use_deepspeed` is passed or `deeps
|
||||
* `--deepspeed_exclusion_filter` (`str`) -- DeepSpeed exclusion filter string when using mutli-node setup.
|
||||
* `--deepspeed_inclusion_filter` (`str`) -- DeepSpeed inclusion filter string when using mutli-node setup.
|
||||
* `--deepspeed_multinode_launcher` (`str`) -- DeepSpeed multi-node launcher to use.
|
||||
* `--deepspeed_moe_layer_cls_names` (`str`) -- comma-separated list of transformer MoE layer class names (case-sensitive) to wrap, e.g, `MixtralSparseMoeBlock` `Qwen2MoeSparseMoeBlock`, `JetMoEAttention,JetMoEBlock`
|
||||
|
||||
**Fully Sharded Data Parallelism Arguments**:
|
||||
|
||||
@ -210,8 +220,9 @@ The following arguments are only useful when `use_fsdp` is passed or Fully Shard
|
||||
* `--fsdp_state_dict_type` (`str`) -- FSDP's state dict type.
|
||||
* `--fsdp_forward_prefetch` (`str`) -- FSDP forward prefetch.
|
||||
* `--fsdp_use_orig_params` (`str`) -- If True, allows non-uniform `requires_grad` mixed in a FSDP unit.
|
||||
* `--fsdp_cpu_ram_efficient_loading` (`str`) - If true, only the first process loads the pretrained model checkoint while all other processes have empty weights. When using this, `--fsdp_sync_module_states` needs to True.
|
||||
* `--fsdp_sync_module_states` (`str`) - If true, each individually wrapped FSDP unit will broadcast module parameters from rank 0.
|
||||
* `--fsdp_cpu_ram_efficient_loading` (`str`) -- If true, only the first process loads the pretrained model checkoint while all other processes have empty weights. When using this, `--fsdp_sync_module_states` needs to True.
|
||||
* `--fsdp_sync_module_states` (`str`) -- If true, each individually wrapped FSDP unit will broadcast module parameters from rank 0.
|
||||
* `--fsdp_activation_checkpointing` (`bool`) -- Decides Whether intermediate activations are freed during the forward pass, and a checkpoint is left as a placeholder
|
||||
|
||||
**Megatron-LM Arguments**:
|
||||
|
||||
@ -225,6 +236,18 @@ The following arguments are only useful when `use_megatron_lm` is passed or Mega
|
||||
* `--megatron_lm_use_distributed_optimizer` (``) -- Decides Whether (true|false) to use distributed optimizer which shards optimizer state and gradients across Data Parallel (DP) ranks.
|
||||
* `--megatron_lm_gradient_clipping` (``) -- Megatron-LM's gradient clipping value based on global L2 Norm (0 to disable).
|
||||
|
||||
**FP8 Arguments**:
|
||||
|
||||
* `--fp8_backend` (`str`) -- Choose a backend to train with FP8 (`te` or `msamp`)
|
||||
* `--fp8_use_autocast_during_eval` (`bool`) -- Whether to use FP8 autocast during eval mode (useful only when `--fp8_backend=te` is passed). Generally better metrics are found when this is not passed.
|
||||
* `--fp8_margin` (`int`) -- The margin to use for the gradient scaling (useful only when `--fp8_backend=te` is passed).
|
||||
* `--fp8_interval` (`int`) -- The interval to use for how often the scaling factor is recomputed (useful only when `--fp8_backend=te` is passed).
|
||||
* `--fp8_format` (`str`) -- The format to use for the FP8 recipe (useful only when `--fp8_backend=te` is passed).
|
||||
* `--fp8_amax_history_len` (`int`) -- The length of the history to use for the scaling factor computation (useful only when `--fp8_backend=te` is passed).
|
||||
* `--fp8_amax_compute_algo` (`str`) -- The algorithm to use for the scaling factor computation. (useful only when `--fp8_backend=te` is passed).
|
||||
* `--fp8_override_linear_precision` (`Tuple[bool, bool, bool]`) -- Whether or not to execute `fprop`, `dgrad`, and `wgrad` GEMMS in higher precision.
|
||||
* `--fp8_opt_level` (`str`) -- What level of 8-bit collective communication should be used with MS-AMP (useful only when `--fp8_backend=msamp` is passed)
|
||||
|
||||
**AWS SageMaker Arguments**:
|
||||
|
||||
The following arguments are only useful when training in SageMaker
|
||||
|
||||
@ -13,16 +13,32 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Utilities for DeepSpeed
|
||||
# DeepSpeed utilities
|
||||
|
||||
## DeepSpeedPlugin
|
||||
|
||||
## get_active_deepspeed_plugin
|
||||
|
||||
[[autodoc]] utils.get_active_deepspeed_plugin
|
||||
|
||||
[[autodoc]] utils.DeepSpeedPlugin
|
||||
|
||||
[[autodoc]] utils.deepspeed.DummyOptim
|
||||
|
||||
[[autodoc]] utils.deepspeed.DummyScheduler
|
||||
|
||||
## DeepSpeedEnginerWrapper
|
||||
|
||||
[[autodoc]] utils.deepspeed.DeepSpeedEngineWrapper
|
||||
|
||||
## DeepSpeedOptimizerWrapper
|
||||
|
||||
[[autodoc]] utils.deepspeed.DeepSpeedOptimizerWrapper
|
||||
|
||||
## DeepSpeedSchedulerWrapper
|
||||
|
||||
[[autodoc]] utils.deepspeed.DeepSpeedSchedulerWrapper
|
||||
|
||||
## DummyOptim
|
||||
|
||||
[[autodoc]] utils.deepspeed.DummyOptim
|
||||
|
||||
## DummyScheduler
|
||||
38
docs/source/package_reference/fp8.md
Normal file
38
docs/source/package_reference/fp8.md
Normal file
@ -0,0 +1,38 @@
|
||||
<!--Copyright 2021 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.
|
||||
-->
|
||||
|
||||
# FP8
|
||||
|
||||
Below are functions and classes relative to the underlying FP8 implementation
|
||||
|
||||
## FP8RecipeKwargs
|
||||
|
||||
[[autodoc]] utils.FP8RecipeKwargs
|
||||
|
||||
## convert_model
|
||||
|
||||
[[autodoc]] utils.convert_model
|
||||
|
||||
## has_transformer_engine_layers
|
||||
|
||||
[[autodoc]] utils.has_transformer_engine_layers
|
||||
|
||||
## contextual_fp8_autocast
|
||||
|
||||
[[autodoc]] utils.contextual_fp8_autocast
|
||||
|
||||
## apply_fp8_autowrap
|
||||
|
||||
[[autodoc]] utils.apply_fp8_autowrap
|
||||
@ -13,8 +13,20 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Utilities for Fully Sharded Data Parallelism
|
||||
# Fully Sharded Data Parallel utilities
|
||||
|
||||
## enable_fsdp_ram_efficient_loading
|
||||
|
||||
[[autodoc]] utils.enable_fsdp_ram_efficient_loading
|
||||
|
||||
## disable_fsdp_ram_efficient_loading
|
||||
|
||||
[[autodoc]] utils.disable_fsdp_ram_efficient_loading
|
||||
|
||||
## merge_fsdp_weights
|
||||
|
||||
[[autodoc]] utils.merge_fsdp_weights
|
||||
|
||||
[[autodoc]] utils.FullyShardedDataParallelPlugin
|
||||
## FullyShardedDataParallelPlugin
|
||||
|
||||
[[autodoc]] utils.FullyShardedDataParallelPlugin
|
||||
|
||||
@ -13,8 +13,10 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# The inference API
|
||||
# Pipeline parallelism
|
||||
|
||||
These docs refer to the [PiPPy](https://github.com/PyTorch/PiPPy) integration.
|
||||
Accelerate supports pipeline parallelism for large-scale training with the PyTorch [torch.distributed.pipelining](https://pytorch.org/docs/stable/distributed.pipelining.html) API.
|
||||
|
||||
## prepare_pippy
|
||||
|
||||
[[autodoc]] inference.prepare_pippy
|
||||
|
||||
@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Kwargs Handlers
|
||||
# Kwargs handlers
|
||||
|
||||
The following objects can be passed to the main [`Accelerator`] to customize how some PyTorch objects
|
||||
related to distributed training or mixed precision are created.
|
||||
@ -30,6 +30,10 @@ related to distributed training or mixed precision are created.
|
||||
|
||||
[[autodoc]] utils.FP8RecipeKwargs
|
||||
|
||||
## ProfileKwargs
|
||||
|
||||
[[autodoc]] utils.ProfileKwargs
|
||||
|
||||
## GradScalerKwargs
|
||||
|
||||
[[autodoc]] GradScalerKwargs
|
||||
|
||||
@ -17,6 +17,10 @@ rendered properly in your Markdown viewer.
|
||||
|
||||
Functions for launching training on distributed processes.
|
||||
|
||||
## notebook_launcher
|
||||
|
||||
[[autodoc]] accelerate.notebook_launcher
|
||||
|
||||
## debug_launcher
|
||||
|
||||
[[autodoc]] accelerate.debug_launcher
|
||||
@ -13,9 +13,9 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Logging with Accelerate
|
||||
# Logging
|
||||
|
||||
Refer to the [Troubleshooting guide](../usage_guides/troubleshooting#logging) or to the example below to learn
|
||||
how to use 🤗 Accelerate's logger.
|
||||
how to use Accelerate's logger.
|
||||
|
||||
[[autodoc]] logging.get_logger
|
||||
@ -13,20 +13,36 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Utilities for Megatron-LM
|
||||
# Megatron-LM utilities
|
||||
|
||||
## MegatronLMPlugin
|
||||
|
||||
[[autodoc]] utils.MegatronLMPlugin
|
||||
|
||||
## MegatronLMDummyScheduler
|
||||
|
||||
[[autodoc]] utils.MegatronLMDummyScheduler
|
||||
|
||||
## MegatronLMDummyDataLoader
|
||||
|
||||
[[autodoc]] utils.MegatronLMDummyDataLoader
|
||||
|
||||
## AbstractTrainStep
|
||||
|
||||
[[autodoc]] utils.AbstractTrainStep
|
||||
|
||||
## GPTTrainStep
|
||||
|
||||
[[autodoc]] utils.GPTTrainStep
|
||||
|
||||
## BertTrainStep
|
||||
|
||||
[[autodoc]] utils.BertTrainStep
|
||||
|
||||
## T5TrainStep
|
||||
|
||||
[[autodoc]] utils.T5TrainStep
|
||||
|
||||
## avg_losses_across_data_parallel_group
|
||||
|
||||
[[autodoc]] utils.avg_losses_across_data_parallel_group
|
||||
|
||||
@ -21,8 +21,14 @@ instances share the same state, which is initialized on the first instantiation.
|
||||
These classes are immutable and store information about certain configurations or
|
||||
states.
|
||||
|
||||
## PartialState
|
||||
|
||||
[[autodoc]] state.PartialState
|
||||
|
||||
## AcceleratorState
|
||||
|
||||
[[autodoc]] state.AcceleratorState
|
||||
|
||||
## GradientState
|
||||
|
||||
[[autodoc]] state.GradientState
|
||||
@ -13,25 +13,36 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Wrapper classes for torch Dataloaders, Optimizers, and Schedulers
|
||||
# DataLoaders, Optimizers, and Schedulers
|
||||
|
||||
The internal classes Accelerate uses to prepare objects for distributed training
|
||||
when calling [`~Accelerator.prepare`].
|
||||
|
||||
## Datasets and DataLoaders
|
||||
## DataLoader utilities
|
||||
|
||||
[[autodoc]] data_loader.prepare_data_loader
|
||||
[[autodoc]] data_loader.skip_first_batches
|
||||
|
||||
## BatchSamplerShard
|
||||
|
||||
[[autodoc]] data_loader.BatchSamplerShard
|
||||
|
||||
## IterableDatasetShard
|
||||
|
||||
[[autodoc]] data_loader.IterableDatasetShard
|
||||
|
||||
## DataLoaderShard
|
||||
|
||||
[[autodoc]] data_loader.DataLoaderShard
|
||||
|
||||
## DataLoaderDispatcher
|
||||
|
||||
[[autodoc]] data_loader.DataLoaderDispatcher
|
||||
|
||||
## Optimizers
|
||||
## AcceleratedOptimizer
|
||||
|
||||
[[autodoc]] optimizer.AcceleratedOptimizer
|
||||
|
||||
## Schedulers
|
||||
## AcceleratedScheduler
|
||||
|
||||
[[autodoc]] scheduler.AcceleratedScheduler
|
||||
@ -13,23 +13,38 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Experiment Tracking
|
||||
# Experiment Trackers
|
||||
|
||||
## The Base Tracker Class
|
||||
## GeneralTracker
|
||||
|
||||
[[autodoc]] tracking.GeneralTracker
|
||||
|
||||
## Integrated Trackers
|
||||
## TensorBoardTracker
|
||||
|
||||
[[autodoc]] tracking.TensorBoardTracker
|
||||
- __init__
|
||||
|
||||
## WandBTracker
|
||||
|
||||
[[autodoc]] tracking.WandBTracker
|
||||
- __init__
|
||||
|
||||
## CometMLTracker
|
||||
|
||||
[[autodoc]] tracking.CometMLTracker
|
||||
- __init__
|
||||
|
||||
## AimTracker
|
||||
|
||||
[[autodoc]] tracking.AimTracker
|
||||
- __init__
|
||||
|
||||
## MLflowTracker
|
||||
|
||||
[[autodoc]] tracking.MLflowTracker
|
||||
- __init__
|
||||
|
||||
## ClearMLTracker
|
||||
|
||||
[[autodoc]] tracking.ClearMLTracker
|
||||
- __init__
|
||||
|
||||
@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Helpful Utilities
|
||||
# Utility functions and classes
|
||||
|
||||
Below are a variety of utility functions that 🤗 Accelerate provides, broken down by use-case.
|
||||
|
||||
@ -126,6 +126,10 @@ These include data operations that mimic the same `torch` ops but can be used on
|
||||
|
||||
[[autodoc]] utils.gather_object
|
||||
|
||||
[[autodoc]] utils.get_grad_scaler
|
||||
|
||||
[[autodoc]] utils.get_mixed_precision_context_manager
|
||||
|
||||
[[autodoc]] utils.listify
|
||||
|
||||
[[autodoc]] utils.pad_across_processes
|
||||
@ -170,6 +174,8 @@ When setting up 🤗 Accelerate for the first time, rather than running `acceler
|
||||
|
||||
[[autodoc]] utils.environment.override_numa_affinity
|
||||
|
||||
[[autodoc]] utils.purge_accelerate_environment
|
||||
|
||||
## Memory
|
||||
|
||||
[[autodoc]] utils.find_executable_batch_size
|
||||
@ -202,8 +208,6 @@ These utilities relate to interacting with PyTorch models
|
||||
|
||||
[[autodoc]] utils.set_module_tensor_to_device
|
||||
|
||||
[[autodoc]] utils.shard_checkpoint
|
||||
|
||||
|
||||
## Parallel
|
||||
|
||||
@ -213,6 +217,8 @@ These include general utilities that should be used when working in parallel.
|
||||
|
||||
[[autodoc]] utils.save
|
||||
|
||||
[[autodoc]] utils.load
|
||||
|
||||
[[autodoc]] utils.wait_for_everyone
|
||||
|
||||
|
||||
|
||||
@ -53,6 +53,8 @@ accelerate launch path_to_script.py --args_for_the_script
|
||||
|
||||
To learn more, check out the [Launch distributed code](basic_tutorials/launch) tutorial for more information about launching your scripts.
|
||||
|
||||
We also have a [configuration zoo](https://github.com/huggingface/accelerate/blob/main/examples/config_yaml_templates) which showcases a number of premade **minimal** example configurations for a variety of setups you can run.
|
||||
|
||||
## Adapt training code
|
||||
|
||||
The next main feature of Accelerate is the [`Accelerator`] class which adapts your PyTorch code to run on different distributed setups.
|
||||
@ -171,8 +173,9 @@ The `device_map` parameter determines where to place each model layer, and speci
|
||||
```py
|
||||
from accelerate import load_checkpoint_and_dispatch
|
||||
|
||||
model_checkpoint = "your-local-model-folder"
|
||||
model = load_checkpoint_and_dispatch(
|
||||
model, checkpoint="mistralai/Mixtral-8x7B-Instruct-v0.1", device_map="auto", no_split_module_classes=['Block']
|
||||
model, checkpoint=model_checkpoint, device_map="auto", no_split_module_classes=['Block']
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@ -13,15 +13,15 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Handling big models for inference
|
||||
# Big Model Inference
|
||||
|
||||
One of the biggest advancements 🤗 Accelerate provides is the concept of [large model inference](../concept_guides/big_model_inference) wherein you can perform *inference* on models that cannot fully fit on your graphics card.
|
||||
One of the biggest advancements Accelerate provides is [Big Model Inference](../concept_guides/big_model_inference), which allows you to perform inference with models that don't fully fit on your graphics card.
|
||||
|
||||
This tutorial will be broken down into two parts showcasing how to use both 🤗 Accelerate and 🤗 Transformers (a higher API-level) to make use of this idea.
|
||||
This tutorial will show you how to use Big Model Inference in Accelerate and the Hugging Face ecosystem.
|
||||
|
||||
## Using 🤗 Accelerate
|
||||
## Accelerate
|
||||
|
||||
For these tutorials, we'll assume a typical workflow for loading your model in such that:
|
||||
A typical workflow for loading a PyTorch model is shown below. `ModelClass` is a model that exceeds the GPU memory of your device (mps or cuda or xpu).
|
||||
|
||||
```py
|
||||
import torch
|
||||
@ -31,9 +31,7 @@ state_dict = torch.load(checkpoint_file)
|
||||
my_model.load_state_dict(state_dict)
|
||||
```
|
||||
|
||||
Note that here we assume that `ModelClass` is a model that takes up more video-card memory than what can fit on your device (be it `mps` or `cuda`).
|
||||
|
||||
The first step is to init an empty skeleton of the model which won't take up any RAM using the [`init_empty_weights`] context manager:
|
||||
With Big Model Inference, the first step is to init an empty skeleton of the model with the `init_empty_weights` context manager. This doesn't require any memory because `my_model` is "parameterless".
|
||||
|
||||
```py
|
||||
from accelerate import init_empty_weights
|
||||
@ -41,22 +39,14 @@ with init_empty_weights():
|
||||
my_model = ModelClass(...)
|
||||
```
|
||||
|
||||
With this `my_model` currently is "parameterless", hence leaving the smaller footprint than what one would normally get loading this onto the CPU directly.
|
||||
Next, the weights are loaded into the model for inference.
|
||||
|
||||
Next we need to load in the weights to our model so we can perform inference.
|
||||
The [`load_checkpoint_and_dispatch`] method loads a checkpoint inside your empty model and dispatches the weights for each layer across all available devices, starting with the fastest devices (GPU, MPS, XPU, NPU, MLU, MUSA) first before moving to the slower ones (CPU and hard drive).
|
||||
|
||||
For this we will use [`load_checkpoint_and_dispatch`], which as the name implies will load a checkpoint inside your empty model and dispatch the weights for each layer across all the devices you have available (GPU/MPS and CPU RAM).
|
||||
Setting `device_map="auto"` automatically fills all available space on the GPU(s) first, then the CPU, and finally, the hard drive (the absolute slowest option) if there is still not enough memory.
|
||||
|
||||
To determine how this `dispatch` can be performed, generally specifying `device_map="auto"` will be good enough as 🤗 Accelerate
|
||||
will attempt to fill all the space in your GPU(s), then loading them to the CPU, and finally if there is not enough RAM it will be loaded to the disk (the absolute slowest option).
|
||||
|
||||
<Tip>
|
||||
|
||||
For more details on designing your own device map, see this section of the [concept guide](../concept_guides/big_model_inference#designing-a-device-map)
|
||||
|
||||
</Tip>
|
||||
|
||||
See an example below:
|
||||
> [!TIP]
|
||||
> Refer to the [Designing a device map](../concept_guides/big_model_inference#designing-a-device-map) guide for more details on how to design your own device map.
|
||||
|
||||
```py
|
||||
from accelerate import load_checkpoint_and_dispatch
|
||||
@ -66,42 +56,29 @@ model = load_checkpoint_and_dispatch(
|
||||
)
|
||||
```
|
||||
|
||||
<Tip>
|
||||
If there are certain “chunks” of layers that shouldn’t be split, pass them to `no_split_module_classes` (see [here](../concept_guides/big_model_inference#loading-weights) for more details).
|
||||
|
||||
If there are certain "chunks" of layers that shouldn't be split, you can pass them in as `no_split_module_classes`. Read more about it [here](../concept_guides/big_model_inference#loading-weights)
|
||||
A models weights can also be sharded into multiple checkpoints to save memory, such as when the `state_dict` doesn't fit in memory (see [here](../concept_guides/big_model_inference#sharded-checkpoints) for more details).
|
||||
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
|
||||
Also to save on memory (such as if the `state_dict` will not fit in RAM), a model's weights can be divided and split into multiple checkpoint files. Read more about it [here](../concept_guides/big_model_inference#sharded-checkpoints)
|
||||
|
||||
</Tip>
|
||||
|
||||
Now that the model is dispatched fully, you can perform inference as normal with the model:
|
||||
Now that the model is fully dispatched, you can perform inference.
|
||||
|
||||
```py
|
||||
input = torch.randn(2,3)
|
||||
input = input.to("cuda")
|
||||
device_type = next(iter(model.parameters())).device.type
|
||||
input = input.to(device_type)
|
||||
output = model(input)
|
||||
```
|
||||
|
||||
What will happen now is each time the input gets passed through a layer, it will be sent from the CPU to the GPU (or disk to CPU to GPU), the output is calculated, and then the layer is pulled back off the GPU going back down the line. While this adds some overhead to the inference being performed, through this method it is possible to run **any size model** on your system, as long as the largest layer is capable of fitting on your GPU.
|
||||
Each time an input is passed through a layer, it is sent from the CPU to the GPU (or disk to CPU to GPU), the output is calculated, and the layer is removed from the GPU going back down the line. While this adds some overhead to inference, it enables you to run any size model on your system, as long as the largest layer fits on your GPU.
|
||||
|
||||
<Tip>
|
||||
Multiple GPUs, or "model parallelism", can be utilized but only one GPU will be active at any given moment. This forces the GPU to wait for the previous GPU to send it the output. You should launch your script normally with Python instead of other tools like torchrun and accelerate launch.
|
||||
|
||||
Multiple GPUs can be utilized, however this is considered "model parallelism" and as a result only one GPU will be active at a given moment, waiting for the prior one to send it the output. You should launch your script normally with `python`
|
||||
and not need `torchrun`, `accelerate launch`, etc.
|
||||
> [!TIP]
|
||||
> You may also be interested in *pipeline parallelism* which utilizes all available GPUs at once, instead of only having one GPU active at a time. This approach is less flexbile though. For more details, refer to the [Memory-efficient pipeline parallelism](./distributed_inference#memory-efficient-pipeline-parallelism-experimental) guide.
|
||||
|
||||
</Tip>
|
||||
<Youtube id="MWCSGj9jEAo"/>
|
||||
|
||||
For a visual representation of this, check out the animation below:
|
||||
|
||||
<Youtube id="MWCSGj9jEAo" />
|
||||
|
||||
### Complete Example
|
||||
|
||||
Below is the full example showcasing what we performed above:
|
||||
Take a look at a full example of Big Model Inference below.
|
||||
|
||||
```py
|
||||
import torch
|
||||
@ -115,17 +92,18 @@ model = load_checkpoint_and_dispatch(
|
||||
)
|
||||
|
||||
input = torch.randn(2,3)
|
||||
input = input.to("cuda")
|
||||
device_type = next(iter(model.parameters())).device.type
|
||||
input = input.to(device_type)
|
||||
output = model(input)
|
||||
```
|
||||
|
||||
## Using 🤗 Transformers, 🤗 Diffusers, and other 🤗 Open Source Libraries
|
||||
## Hugging Face ecosystem
|
||||
|
||||
Libraries that support 🤗 Accelerate big model inference include all of the earlier logic in their `from_pretrained` constructors.
|
||||
Other libraries in the Hugging Face ecosystem, like Transformers or Diffusers, supports Big Model Inference in their [`~transformers.PreTrainedModel.from_pretrained`] constructors.
|
||||
|
||||
These operate by specifying a string representing the model to download from the [🤗 Hub](https://hf.co/models) and then denoting `device_map="auto"` along with a few extra parameters.
|
||||
You just need to add `device_map="auto"` in [`~transformers.PreTrainedModel.from_pretrained`] to enable Big Model Inference.
|
||||
|
||||
As a brief example, we will look at using `transformers` and loading in Big Science's T0pp model.
|
||||
For example, load Big Sciences T0pp 11 billion parameter model with Big Model Inference.
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForSeq2SeqLM
|
||||
@ -133,9 +111,7 @@ from transformers import AutoModelForSeq2SeqLM
|
||||
model = AutoModelForSeq2SeqLM.from_pretrained("bigscience/T0pp", device_map="auto")
|
||||
```
|
||||
|
||||
After loading the model in, the initial steps from before to prepare a model have all been done and the model is fully
|
||||
ready to make use of all the resources in your machine. Through these constructors, you can also save *more* memory by
|
||||
specifying the precision the model is loaded into as well, through the `torch_dtype` parameter, such as:
|
||||
After loading the model, the empty init and smart dispatch steps from before are executed and the model is fully ready to make use of all the resources in your machine. Through these constructors, you can also save more memory by specifying the `torch_dtype` parameter to load a model in a lower precision.
|
||||
|
||||
```py
|
||||
from transformers import AutoModelForSeq2SeqLM
|
||||
@ -143,8 +119,6 @@ from transformers import AutoModelForSeq2SeqLM
|
||||
model = AutoModelForSeq2SeqLM.from_pretrained("bigscience/T0pp", device_map="auto", torch_dtype=torch.float16)
|
||||
```
|
||||
|
||||
To learn more about this, check out the 🤗 Transformers documentation available [here](https://huggingface.co/docs/transformers/main/en/main_classes/model#large-model-loading).
|
||||
## Next steps
|
||||
|
||||
## Where to go from here
|
||||
|
||||
For a much more detailed look at big model inference, be sure to check out the [Conceptual Guide on it](../concept_guides/big_model_inference)
|
||||
For a more detailed explanation of Big Model Inference, make sure to check out the [conceptual guide](../concept_guides/big_model_inference)!
|
||||
|
||||
@ -15,8 +15,8 @@ rendered properly in your Markdown viewer.
|
||||
|
||||
# Checkpointing
|
||||
|
||||
When training a PyTorch model with 🤗 Accelerate, you may often want to save and continue a state of training. Doing so requires
|
||||
saving and loading the model, optimizer, RNG generators, and the GradScaler. Inside 🤗 Accelerate are two convenience functions to achieve this quickly:
|
||||
When training a PyTorch model with Accelerate, you may often want to save and continue a state of training. Doing so requires
|
||||
saving and loading the model, optimizer, RNG generators, and the GradScaler. Inside Accelerate are two convenience functions to achieve this quickly:
|
||||
- Use [`~Accelerator.save_state`] for saving everything mentioned above to a folder location
|
||||
- Use [`~Accelerator.load_state`] for loading everything stored from an earlier `save_state`
|
||||
|
||||
|
||||
325
docs/source/usage_guides/ddp_comm_hook.md
Normal file
325
docs/source/usage_guides/ddp_comm_hook.md
Normal file
@ -0,0 +1,325 @@
|
||||
<!--
|
||||
Copyright 2022 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.
|
||||
-->
|
||||
|
||||
# DDP Communication Hooks
|
||||
|
||||
Distributed Data Parallel (DDP) communication hooks provide a generic interface to control how gradients are communicated across workers by overriding the vanilla allreduce in `DistributedDataParallel`. A few built-in communication hooks are provided, and users can easily apply any of these hooks to optimize communication.
|
||||
|
||||
|
||||
- **FP16 Compression Hook**: Compresses gradients by casting them to half-precision floating-point format (`torch.float16`), reducing communication overhead.
|
||||
- **BF16 Compression Hook**: Similar to FP16, but uses the Brain Floating Point format (`torch.bfloat16`), which can be more efficient on certain hardware.
|
||||
- **PowerSGD Hook**: An advanced gradient compression algorithm that provides high compression rates and can accelerate bandwidth-bound distributed training.
|
||||
|
||||
In this tutorial, you will see how to quickly set up DDP communication hooks and perform training with the utilities provided in Accelerate, which can be as simple as adding just one new line of code! This demonstrates how to use DDP communication hooks to optimize gradient communication in distributed training with the Accelerate library.
|
||||
|
||||
## FP16 Compression Hook
|
||||
|
||||
<hfoptions id="fp16">
|
||||
<hfoption id="PyTorch">
|
||||
|
||||
```python
|
||||
import torch
|
||||
from torch.nn.parallel import DistributedDataParallel as DDP
|
||||
from torch.distributed.algorithms.ddp_comm_hooks import default_hooks
|
||||
|
||||
class MyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layer = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, x):
|
||||
return self.layer(x)
|
||||
|
||||
model = MyModel()
|
||||
model = DDP(model, device_ids=[torch.cuda.current_device()])
|
||||
model.register_comm_hook(state=None, hook=default_hooks.fp16_compress_hook)
|
||||
|
||||
# Training loop
|
||||
for data, targets in data_loader:
|
||||
outputs = model(data)
|
||||
loss = criterion(outputs, targets)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Accelerate">
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator, DDPCommunicationHookType, DistributedDataParallelKwargs
|
||||
import torch
|
||||
|
||||
class MyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layer = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, x):
|
||||
return self.layer(x)
|
||||
|
||||
# DDP Communication Hook setup
|
||||
ddp_kwargs = DistributedDataParallelKwargs(comm_hook=DDPCommunicationHookType.FP16)
|
||||
accelerator = Accelerator(kwargs_handlers=[ddp_kwargs])
|
||||
|
||||
model = MyModel()
|
||||
optimizer = torch.optim.Adam(model.parameters())
|
||||
data_loader = DataLoader(dataset, batch_size=16)
|
||||
|
||||
model, optimizer, data_loader = accelerator.prepare(model, optimizer, data_loader)
|
||||
|
||||
# Training loop
|
||||
for data, targets in data_loader:
|
||||
outputs = model(data)
|
||||
loss = criterion(outputs, targets)
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
### BF16 Compression Hook
|
||||
|
||||
<Tip warning={true}>
|
||||
|
||||
BF16 Compression Hook API is experimental, and it requires NCCL version later than 2.9.6.
|
||||
|
||||
</Tip>
|
||||
|
||||
<hfoptions id="bf16">
|
||||
<hfoption id="PyTorch">
|
||||
|
||||
```python
|
||||
import torch
|
||||
from torch.nn.parallel import DistributedDataParallel as DDP
|
||||
from torch.distributed.algorithms.ddp_comm_hooks import default_hooks
|
||||
|
||||
class MyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layer = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, x):
|
||||
return self.layer(x)
|
||||
|
||||
model = MyModel()
|
||||
model = DDP(model, device_ids=[torch.cuda.current_device()])
|
||||
model.register_comm_hook(state=None, hook=default_hooks.bf16_compress_hook)
|
||||
|
||||
# Training loop
|
||||
for data, targets in data_loader:
|
||||
outputs = model(data)
|
||||
loss = criterion(outputs, targets)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Accelerate">
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator, DDPCommunicationHookType, DistributedDataParallelKwargs
|
||||
import torch
|
||||
|
||||
class MyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layer = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, x):
|
||||
return self.layer(x)
|
||||
|
||||
# DDP Communication Hook setup
|
||||
ddp_kwargs = DistributedDataParallelKwargs(comm_hook=DDPCommunicationHookType.BF16)
|
||||
accelerator = Accelerator(kwargs_handlers=[ddp_kwargs])
|
||||
|
||||
model = MyModel()
|
||||
optimizer = torch.optim.Adam(model.parameters())
|
||||
data_loader = DataLoader(dataset, batch_size=16)
|
||||
|
||||
model, optimizer, data_loader = accelerator.prepare(model, optimizer, data_loader)
|
||||
|
||||
# Training loop
|
||||
for data, targets in data_loader:
|
||||
outputs = model(data)
|
||||
loss = criterion(outputs, targets)
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
### PowerSGD Hook
|
||||
|
||||
<Tip warning={true}>
|
||||
|
||||
PowerSGD typically requires extra memory of the same size as the model’s gradients to enable error feedback, which can compensate for biased compressed communication and improve accuracy.
|
||||
|
||||
</Tip>
|
||||
|
||||
<hfoptions id="powerSGD">
|
||||
<hfoption id="PyTorch">
|
||||
|
||||
```python
|
||||
import torch
|
||||
from torch.nn.parallel import DistributedDataParallel as DDP
|
||||
from torch.distributed.algorithms.ddp_comm_hooks import powerSGD_hook
|
||||
|
||||
class MyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layer = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, x):
|
||||
return self.layer(x)
|
||||
|
||||
model = MyModel()
|
||||
model = DDP(model, device_ids=[torch.cuda.current_device()])
|
||||
state = powerSGD_hook.PowerSGDState(process_group=None)
|
||||
model.register_comm_hook(state=state, hook=powerSGD_hook.powerSGD_hook)
|
||||
|
||||
# Training loop
|
||||
for data, targets in data_loader:
|
||||
outputs = model(data)
|
||||
loss = criterion(outputs, targets)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Accelerate">
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator, DDPCommunicationHookType, DistributedDataParallelKwargs
|
||||
import torch
|
||||
|
||||
class MyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layer = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, x):
|
||||
return self.layer(x)
|
||||
|
||||
# DDP Communication Hook setup
|
||||
ddp_kwargs = DistributedDataParallelKwargs(comm_hook=DDPCommunicationHookType.POWER_SGD)
|
||||
accelerator = Accelerator(kwargs_handlers=[ddp_kwargs])
|
||||
|
||||
model = MyModel()
|
||||
optimizer = torch.optim.Adam(model.parameters())
|
||||
data_loader = DataLoader(dataset, batch_size=16)
|
||||
|
||||
model, optimizer, data_loader = accelerator.prepare(model, optimizer, data_loader)
|
||||
|
||||
# Training loop
|
||||
for data, targets in data_loader:
|
||||
outputs = model(data)
|
||||
loss = criterion(outputs, targets)
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
## DDP Communication Hooks utilities
|
||||
|
||||
There are two additional utilities for supporting optional functionalities with the communication hooks.
|
||||
|
||||
### comm_wrapper
|
||||
|
||||
`comm_wrapper` is an option to wrap a communication hook with additional functionality. For example, it can be used to combine FP16 compression with other communication strategies. Currently supported wrappers are `no`, `fp16`, and `bf16`.
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator, DDPCommunicationHookType, DistributedDataParallelKwargs
|
||||
import torch
|
||||
|
||||
class MyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layer = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, x):
|
||||
return self.layer(x)
|
||||
|
||||
# DDP Communication Hook setup
|
||||
ddp_kwargs = DistributedDataParallelKwargs(
|
||||
comm_hook=DDPCommunicationHookType.POWER_SGD,
|
||||
comm_wrapper=DDPCommunicationHookType.FP16
|
||||
)
|
||||
accelerator = Accelerator(kwargs_handlers=[ddp_kwargs])
|
||||
|
||||
model = MyModel()
|
||||
optimizer = torch.optim.Adam(model.parameters())
|
||||
data_loader = DataLoader(dataset, batch_size=16)
|
||||
|
||||
model, optimizer, data_loader = accelerator.prepare(model, optimizer, data_loader)
|
||||
|
||||
# Training loop
|
||||
for data, targets in data_loader:
|
||||
outputs = model(data)
|
||||
loss = criterion(outputs, targets)
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
### comm_state_option
|
||||
|
||||
`comm_state_option` allows you to pass additional state information required by certain communication hooks. This is particularly useful for stateful hooks like `PowerSGD`, which require maintaining hyperparameters and internal states across training steps. Below is an example showcasing the use of `comm_state_option` with the `PowerSGD` hook.
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator, DDPCommunicationHookType, DistributedDataParallelKwargs
|
||||
import torch
|
||||
|
||||
class MyModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.layer = torch.nn.Linear(10, 10)
|
||||
|
||||
def forward(self, x):
|
||||
return self.layer(x)
|
||||
|
||||
# DDP Communication Hook setup
|
||||
ddp_kwargs = DistributedDataParallelKwargs(
|
||||
comm_hook=DDPCommunicationHookType.POWER_SGD,
|
||||
comm_state_option={"matrix_approximation_rank": 2}
|
||||
)
|
||||
accelerator = Accelerator(kwargs_handlers=[ddp_kwargs])
|
||||
|
||||
model = MyModel()
|
||||
optimizer = torch.optim.Adam(model.parameters())
|
||||
data_loader = DataLoader(dataset, batch_size=16)
|
||||
|
||||
model, optimizer, data_loader = accelerator.prepare(model, optimizer, data_loader)
|
||||
|
||||
# Training loop
|
||||
for data, targets in data_loader:
|
||||
outputs = model(data)
|
||||
loss = criterion(outputs, targets)
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
For more advanced usage and additional hooks, refer to the [PyTorch DDP Communication Hooks documentation](https://pytorch.org/docs/stable/ddp_comm_hooks.html).
|
||||
@ -33,7 +33,7 @@ DeepSpeed ZeRO-2 is primarily used only for training, as its features are of no
|
||||
DeepSpeed ZeRO-3 can be used for inference as well since it allows huge models to be loaded on multiple GPUs, which
|
||||
won't be possible on a single GPU.
|
||||
|
||||
🤗 Accelerate integrates [DeepSpeed](https://github.com/microsoft/DeepSpeed) via 2 options:
|
||||
Accelerate integrates [DeepSpeed](https://github.com/microsoft/DeepSpeed) via 2 options:
|
||||
|
||||
1. Integration of the DeepSpeed features via `deepspeed config file` specification in `accelerate config` . You just supply your custom config file or use our template. Most of
|
||||
this document is focused on this feature. This supports all the core features of DeepSpeed and gives user a lot of flexibility.
|
||||
@ -45,7 +45,7 @@ won't be possible on a single GPU.
|
||||
|
||||
Training:
|
||||
|
||||
1. 🤗 Accelerate integrates all features of DeepSpeed ZeRO. This includes all the ZeRO stages 1, 2 and 3 as well as ZeRO-Offload, ZeRO-Infinity (which can offload to disk/NVMe) and ZeRO++.
|
||||
1. Accelerate integrates all features of DeepSpeed ZeRO. This includes all the ZeRO stages 1, 2 and 3 as well as ZeRO-Offload, ZeRO-Infinity (which can offload to disk/NVMe) and ZeRO++.
|
||||
Below is a short description of Data Parallelism using ZeRO - Zero Redundancy Optimizer along with diagram from this [blog post](https://www.microsoft.com/en-us/research/blog/zero-deepspeed-new-system-optimizations-enable-training-models-with-over-100-billion-parameters/)
|
||||

|
||||
|
||||
@ -433,7 +433,7 @@ Only the `auto` fields specified in above examples are handled by `prepare` meth
|
||||
The `auto` values are calculated as:
|
||||
|
||||
- `reduce_bucket_size`: `hidden_size * hidden_size`
|
||||
- `stage3_prefetch_bucket_size`: `0.9 * hidden_size * hidden_size`
|
||||
- `stage3_prefetch_bucket_size`: `int(0.9 * hidden_size * hidden_size)`
|
||||
- `stage3_param_persistence_threshold`: `10 * hidden_size`
|
||||
|
||||
For the `auto` feature to work for these 3 config entries - Accelerate will use `model.config.hidden_size` or `max(model.config.hidden_sizes)` as `hidden_size`. If neither of these is available, the launching will fail and you will have to set these 3 config entries manually. Remember the first 2 config entries are the communication buffers - the larger they are the more efficient the comms will be, and the larger they are the more GPU memory they will consume, so it's a tunable performance trade-off.
|
||||
@ -727,12 +727,12 @@ Papers:
|
||||
- [ZeRO++: Extremely Efficient Collective Communication for Giant Model Training](https://arxiv.org/abs/2306.10209)
|
||||
|
||||
|
||||
Finally, please, remember that 🤗 `Accelerate` only integrates DeepSpeed, therefore if you
|
||||
Finally, please, remember that `Accelerate` only integrates DeepSpeed, therefore if you
|
||||
have any problems or questions with regards to DeepSpeed usage, please, file an issue with [DeepSpeed GitHub](https://github.com/microsoft/DeepSpeed/issues).
|
||||
|
||||
|
||||
<Tip>
|
||||
|
||||
For those interested in the similarities and differences between FSDP and DeepSpeed, please check out the [concept guide here](../concept_guides/fsdp_and_deepspeed.md)!
|
||||
For those interested in the similarities and differences between FSDP and DeepSpeed, please check out the [concept guide here](../concept_guides/fsdp_and_deepspeed)!
|
||||
|
||||
</Tip>
|
||||
246
docs/source/usage_guides/deepspeed_multiple_model.md
Normal file
246
docs/source/usage_guides/deepspeed_multiple_model.md
Normal file
@ -0,0 +1,246 @@
|
||||
<!--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 contains specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Using multiple models with DeepSpeed
|
||||
|
||||
<Tip warning={true}>
|
||||
|
||||
This guide assumes that you have read and understood the [DeepSpeed usage guide](./deepspeed.md).
|
||||
|
||||
</Tip>
|
||||
|
||||
Running multiple models with Accelerate and DeepSpeed is useful for:
|
||||
|
||||
* Knowledge distillation
|
||||
* Post-training techniques like RLHF (see the [TRL](https://github.com/huggingface/trl) library for more examples)
|
||||
* Training multiple models at once
|
||||
|
||||
Currently, Accelerate has a **very experimental API** to help you use multiple models.
|
||||
|
||||
This tutorial will focus on two common use cases:
|
||||
|
||||
1. Knowledge distillation, where a smaller student model is trained to mimic a larger, better-performing teacher. If the student model fits on a single GPU, we can use ZeRO-2 for training and ZeRO-3 to shard the teacher for inference. This is significantly faster than using ZeRO-3 for both models.
|
||||
2. Training multiple *disjoint* models at once.
|
||||
|
||||
## Knowledge distillation
|
||||
|
||||
Knowledge distillation is a good example of using multiple models, but only training one of them.
|
||||
|
||||
Normally, you would use a single [`utils.DeepSpeedPlugin`] for both models. However, in this case, there are two separate configurations. Accelerate allows you to create and use multiple plugins **if and only if** they are in a `dict` so that you can reference and enable the proper plugin when needed.
|
||||
|
||||
```python
|
||||
from accelerate.utils import DeepSpeedPlugin
|
||||
|
||||
zero2_plugin = DeepSpeedPlugin(hf_ds_config="zero2_config.json")
|
||||
zero3_plugin = DeepSpeedPlugin(hf_ds_config="zero3_config.json")
|
||||
|
||||
deepspeed_plugins = {"student": zero2_plugin, "teacher": zero3_plugin}
|
||||
```
|
||||
|
||||
The `zero2_config.json` should be configured for full training (so specify `scheduler` and `optimizer` if you are not utilizing your own), while `zero3_config.json` should only be configured for the inference model, as shown in the example below.
|
||||
|
||||
```json
|
||||
{
|
||||
"bf16": {
|
||||
"enabled": "auto"
|
||||
},
|
||||
"zero_optimization": {
|
||||
"stage": 3,
|
||||
"overlap_comm": true,
|
||||
"reduce_bucket_size": "auto",
|
||||
"stage3_prefetch_bucket_size": "auto",
|
||||
"stage3_param_persistence_threshold": "auto",
|
||||
"stage3_max_live_parameters": "auto",
|
||||
"stage3_max_reuse_distance": "auto",
|
||||
},
|
||||
"train_micro_batch_size_per_gpu": 1
|
||||
}
|
||||
```
|
||||
|
||||
An example `zero2_config.json` configuration is shown below.
|
||||
|
||||
```json
|
||||
{
|
||||
"bf16": {
|
||||
"enabled": "auto"
|
||||
},
|
||||
"optimizer": {
|
||||
"type": "AdamW",
|
||||
"params": {
|
||||
"lr": "auto",
|
||||
"weight_decay": "auto",
|
||||
"torch_adam": true,
|
||||
"adam_w_mode": true
|
||||
}
|
||||
},
|
||||
"scheduler": {
|
||||
"type": "WarmupLR",
|
||||
"params": {
|
||||
"warmup_min_lr": "auto",
|
||||
"warmup_max_lr": "auto",
|
||||
"warmup_num_steps": "auto"
|
||||
}
|
||||
},
|
||||
"zero_optimization": {
|
||||
"stage": 2,
|
||||
"offload_optimizer": {
|
||||
"device": "cpu",
|
||||
"pin_memory": true
|
||||
},
|
||||
},
|
||||
"gradient_accumulation_steps": 1,
|
||||
"gradient_clipping": "auto",
|
||||
"train_batch_size": "auto",
|
||||
"train_micro_batch_size_per_gpu": "auto",
|
||||
}
|
||||
```
|
||||
|
||||
<Tip>
|
||||
|
||||
DeepSpeed will raise an error if `train_micro_batch_size_per_gpu` isn't specified, even if this particular model isn't being trained.
|
||||
|
||||
</Tip>
|
||||
|
||||
From here, create a single [`Accelerator`] and pass in both configurations.
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator
|
||||
|
||||
accelerator = Accelerator(deepspeed_plugins=deepspeed_plugins)
|
||||
```
|
||||
|
||||
Now let's see how to use them.
|
||||
|
||||
### Student model
|
||||
|
||||
By default, Accelerate sets the first item in the `dict` as the default or enabled plugin (`"student"` plugin). Verify this by using the [`utils.deepspeed.get_active_deepspeed_plugin`] function to see which plugin is enabled.
|
||||
|
||||
```python
|
||||
active_plugin = get_active_deepspeed_plugin(accelerator.state)
|
||||
assert active_plugin is deepspeed_plugins["student"]
|
||||
```
|
||||
|
||||
[`AcceleratorState`] also keeps the active DeepSpeed plugin saved in `state.deepspeed_plugin`.
|
||||
```python
|
||||
assert active_plugin is accelerator.deepspeed_plugin
|
||||
```
|
||||
|
||||
Since `student` is the currently active plugin, let's go ahead and prepare the model, optimizer, and scheduler.
|
||||
|
||||
```python
|
||||
student_model, optimizer, scheduler = ...
|
||||
student_model, optimizer, scheduler, train_dataloader = accelerator.prepare(student_model, optimizer, scheduler, train_dataloader)
|
||||
```
|
||||
|
||||
Now it's time to deal with the teacher model.
|
||||
|
||||
### Teacher model
|
||||
|
||||
First, you need to specify in [`Accelerator`] that the `zero3_config.json` configuration should be used.
|
||||
|
||||
```python
|
||||
accelerator.state.select_deepspeed_plugin("teacher")
|
||||
```
|
||||
|
||||
This disables the `"student"` plugin and enables the `"teacher"` plugin instead. The
|
||||
DeepSpeed stateful config inside of Transformers is updated, and it changes which plugin configuration gets called when using
|
||||
`deepspeed.initialize()`. This allows you to use the automatic `deepspeed.zero.Init` context manager integration Transformers provides.
|
||||
|
||||
```python
|
||||
teacher_model = AutoModel.from_pretrained(...)
|
||||
teacher_model = accelerator.prepare(teacher_model)
|
||||
```
|
||||
|
||||
Otherwise, you should manually initialize the model with `deepspeed.zero.Init`.
|
||||
```python
|
||||
with deepspeed.zero.Init(accelerator.deepspeed_plugin.config):
|
||||
model = MyModel(...)
|
||||
```
|
||||
|
||||
### Training
|
||||
|
||||
From here, your training loop can be whatever you like, as long as `teacher_model` is never being trained on.
|
||||
|
||||
```python
|
||||
teacher_model.eval()
|
||||
student_model.train()
|
||||
for batch in train_dataloader:
|
||||
with torch.no_grad():
|
||||
output_teacher = teacher_model(**batch)
|
||||
output_student = student_model(**batch)
|
||||
# Combine the losses or modify it in some way
|
||||
loss = output_teacher.loss + output_student.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
## Train multiple disjoint models
|
||||
|
||||
Training multiple models is a more complicated scenario.
|
||||
In its current state, we assume each model is **completely disjointed** from the other during training.
|
||||
|
||||
This scenario still requires two [`utils.DeepSpeedPlugin`]'s to be made. However, you also need a second [`Accelerator`], since different `deepspeed` engines are being called at different times. A single [`Accelerator`] can only carry one instance at a time.
|
||||
|
||||
Since the [`state.AcceleratorState`] is a stateful object though, it is already aware of both [`utils.DeepSpeedPlugin`]'s available. You can just instantiate a second [`Accelerator`] with no extra arguments.
|
||||
|
||||
```python
|
||||
first_accelerator = Accelerator(deepspeed_plugins=deepspeed_plugins)
|
||||
second_accelerator = Accelerator()
|
||||
```
|
||||
|
||||
You can call either `first_accelerator.state.select_deepspeed_plugin()` to enable or disable
|
||||
a particular plugin, and then call [`prepare`].
|
||||
|
||||
```python
|
||||
# can be `accelerator_0`, `accelerator_1`, or by calling `AcceleratorState().select_deepspeed_plugin(...)`
|
||||
first_accelerator.state.select_deepspeed_plugin("first_model")
|
||||
first_model = AutoModel.from_pretrained(...)
|
||||
# For this example, `get_training_items` is a nonexistent function that gets the setup we need for training
|
||||
first_optimizer, first_scheduler, train_dl, eval_dl = get_training_items(model1)
|
||||
first_model, first_optimizer, first_scheduler, train_dl, eval_dl = accelerator.prepare(
|
||||
first_model, first_optimizer, first_scheduler, train_dl, eval_dl
|
||||
)
|
||||
|
||||
second_accelerator.state.select_deepspeed_plugin("second_model")
|
||||
second_model = AutoModel.from_pretrained(...)
|
||||
# For this example, `get_training_items` is a nonexistent function that gets the setup we need for training
|
||||
second_optimizer, second_scheduler, _, _ = get_training_items(model2)
|
||||
second_model, second_optimizer, second_scheduler = accelerator.prepare(
|
||||
second_model, second_optimizer, second_scheduler
|
||||
)
|
||||
```
|
||||
|
||||
And now you can train:
|
||||
|
||||
```python
|
||||
for batch in dl:
|
||||
outputs1 = first_model(**batch)
|
||||
first_accelerator.backward(outputs1.loss)
|
||||
first_optimizer.step()
|
||||
first_scheduler.step()
|
||||
first_optimizer.zero_grad()
|
||||
|
||||
outputs2 = model2(**batch)
|
||||
second_accelerator.backward(outputs2.loss)
|
||||
second_optimizer.step()
|
||||
second_scheduler.step()
|
||||
second_optimizer.zero_grad()
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
To see more examples, please check out the [related tests](https://github.com/huggingface/accelerate/blob/main/src/accelerate/test_utils/scripts/external_deps/test_ds_multiple_model.py) currently in [Accelerate].
|
||||
@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Distributed Inference with 🤗 Accelerate
|
||||
# Distributed inference
|
||||
|
||||
Distributed inference can fall into three brackets:
|
||||
|
||||
@ -56,13 +56,13 @@ def run_inference(rank, world_size):
|
||||
```
|
||||
One will notice how we have to check the rank to know what prompt to send, which can be a bit tedious.
|
||||
|
||||
A user might then also think that with 🤗 Accelerate, using the `Accelerator` to prepare a dataloader for such a task might also be
|
||||
A user might then also think that with Accelerate, using the `Accelerator` to prepare a dataloader for such a task might also be
|
||||
a simple way to manage this. (To learn more, check out the relevant section in the [Quick Tour](../quicktour#distributed-evaluation))
|
||||
|
||||
Can it manage it? Yes. Does it add unneeded extra code however: also yes.
|
||||
|
||||
|
||||
With 🤗 Accelerate, we can simplify this process by using the [`Accelerator.split_between_processes`] context manager (which also exists in `PartialState` and `AcceleratorState`).
|
||||
With Accelerate, we can simplify this process by using the [`Accelerator.split_between_processes`] context manager (which also exists in `PartialState` and `AcceleratorState`).
|
||||
This function will automatically split whatever data you pass to it (be it a prompt, a set of tensors, a dictionary of the prior data, etc.) across all the processes (with a potential
|
||||
to be padded) for you to use right away.
|
||||
|
||||
@ -82,7 +82,7 @@ with distributed_state.split_between_processes(["a dog", "a cat"]) as prompt:
|
||||
result.save(f"result_{distributed_state.process_index}.png")
|
||||
```
|
||||
|
||||
And then to launch the code, we can use the 🤗 Accelerate:
|
||||
And then to launch the code, we can use the Accelerate:
|
||||
|
||||
If you have generated a config file to be used using `accelerate config`:
|
||||
|
||||
@ -144,22 +144,20 @@ You can find more complex examples [here](https://github.com/huggingface/acceler
|
||||
|
||||
## Memory-efficient pipeline parallelism (experimental)
|
||||
|
||||
This next part will discuss using *pipeline parallelism*. This is an **experimental** API utilizing the [PiPPy library by PyTorch](https://github.com/pytorch/PiPPy/) as a native solution.
|
||||
This next part will discuss using *pipeline parallelism*. This is an **experimental** API that utilizes [torch.distributed.pipelining](https://pytorch.org/docs/stable/distributed.pipelining.html#) as a native solution.
|
||||
|
||||
The general idea with pipeline parallelism is: say you have 4 GPUs and a model big enough it can be *split* on four GPUs using `device_map="auto"`. With this method you can send in 4 inputs at a time (for example here, any amount works) and each model chunk will work on an input, then receive the next input once the prior chunk finished, making it *much* more efficient **and faster** than the method described earlier. Here's a visual taken from the PyTorch repository:
|
||||
|
||||

|
||||

|
||||
|
||||
To illustrate how you can use this with Accelerate, we have created an [example zoo](https://github.com/huggingface/accelerate/tree/main/examples/inference) showcasing a number of different models and situations. In this tutorial, we'll show this method for GPT2 across two GPUs.
|
||||
|
||||
Before you proceed, please make sure you have the latest pippy installed by running the following:
|
||||
Before you proceed, please make sure you have the latest PyTorch version installed by running the following:
|
||||
|
||||
```bash
|
||||
pip install torchpippy
|
||||
pip install torch
|
||||
```
|
||||
|
||||
We require at least version 0.2.0. To confirm that you have the correct version, run `pip show torchpippy`.
|
||||
|
||||
Start by creating the model on the CPU:
|
||||
|
||||
```{python}
|
||||
@ -170,7 +168,7 @@ model = GPT2ForSequenceClassification(config)
|
||||
model.eval()
|
||||
```
|
||||
|
||||
Next you'll need to create some example inputs to use. These help PiPPy trace the model.
|
||||
Next you'll need to create some example inputs to use. These help `torch.distributed.pipelining` trace the model.
|
||||
|
||||
<Tip warning={true}>
|
||||
However you make this example will determine the relative batch size that will be used/passed
|
||||
|
||||
@ -13,14 +13,14 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Learning how to incorporate 🤗 Accelerate features quickly!
|
||||
# Start Here!
|
||||
|
||||
Please use the interactive tool below to help you get started with learning about a particular
|
||||
feature of 🤗 Accelerate and how to utilize it! It will provide you with a code diff, an explanation
|
||||
feature of Accelerate and how to utilize it! It will provide you with a code diff, an explanation
|
||||
towards what is going on, as well as provide you with some useful links to explore more within
|
||||
the documentation!
|
||||
|
||||
Most code examples start from the following python code before integrating 🤗 Accelerate in some way:
|
||||
Most code examples start from the following python code before integrating Accelerate in some way:
|
||||
|
||||
```python
|
||||
for batch in dataloader:
|
||||
|
||||
@ -79,7 +79,7 @@ Currently, `Accelerate` supports the following config through the CLI:
|
||||
|
||||
`fsdp_auto_wrap_policy`: [1] TRANSFORMER_BASED_WRAP, [2] SIZE_BASED_WRAP, [3] NO_WRAP
|
||||
|
||||
`fsdp_transformer_layer_cls_to_wrap`: Only applicable for 🤗 Transformers. When using `fsdp_auto_wrap_policy=TRANSFORMER_BASED_WRAP`, a user may provide a comma-separated string of transformer layer class names (case-sensitive) to wrap, e.g., `BertLayer`, `GPTJBlock`, `T5Block`, `BertLayer,BertEmbeddings,BertSelfOutput`. This is important because submodules that share weights (e.g., embedding layers) should not end up in different FSDP wrapped units. Using this policy, wrapping happens for each block containing Multi-Head Attention followed by a couple of MLP layers. Remaining layers including the shared embeddings are conveniently wrapped in same outermost FSDP unit. Therefore, use this for transformer-based models. You can use the `model._no_split_modules` for 🤗 Transformer models by answering `yes` to `Do you want to use the model's `_no_split_modules` to wrap. It will try to use `model._no_split_modules` when possible.
|
||||
`fsdp_transformer_layer_cls_to_wrap`: Only applicable for Transformers. When using `fsdp_auto_wrap_policy=TRANSFORMER_BASED_WRAP`, a user may provide a comma-separated string of transformer layer class names (case-sensitive) to wrap, e.g., `BertLayer`, `GPTJBlock`, `T5Block`, `BertLayer,BertEmbeddings,BertSelfOutput`. This is important because submodules that share weights (e.g., embedding layers) should not end up in different FSDP wrapped units. Using this policy, wrapping happens for each block containing Multi-Head Attention followed by a couple of MLP layers. Remaining layers including the shared embeddings are conveniently wrapped in same outermost FSDP unit. Therefore, use this for transformer-based models. You can use the `model._no_split_modules` for Transformer models by answering `yes` to `Do you want to use the model's `_no_split_modules` to wrap. It will try to use `model._no_split_modules` when possible.
|
||||
|
||||
`fsdp_min_num_params`: minimum number of parameters when using `fsdp_auto_wrap_policy=SIZE_BASED_WRAP`.
|
||||
|
||||
@ -91,7 +91,7 @@ Currently, `Accelerate` supports the following config through the CLI:
|
||||
|
||||
`fsdp_use_orig_params`: If True, allows non-uniform `requires_grad` during init, which means support for interspersed frozen and trainable parameters. This setting is useful in cases such as parameter-efficient fine-tuning as discussed in [this post](https://dev-discuss.pytorch.org/t/rethinking-pytorch-fully-sharded-data-parallel-fsdp-from-first-principles/1019). This option also allows one to have multiple optimizer param groups. This should be `True` when creating an optimizer before preparing/wrapping the model with FSDP.
|
||||
|
||||
`fsdp_cpu_ram_efficient_loading`: Only applicable for 🤗 Transformers models. If True, only the first process loads the pretrained model checkpoint while all other processes have empty weights. This should be set to False if you experience errors when loading the pretrained 🤗 Transformers model via `from_pretrained` method. When this setting is True `fsdp_sync_module_states` also must to be True, otherwise all the processes except the main process would have random weights leading to unexpected behaviour during training. For this to work, make sure the distributed process group is initialized before calling Transformers `from_pretrained` method. When using 🤗 Trainer API, the distributed process group is initialized when you create an instance of `TrainingArguments` class.
|
||||
`fsdp_cpu_ram_efficient_loading`: Only applicable for Transformers models. If True, only the first process loads the pretrained model checkpoint while all other processes have empty weights. This should be set to False if you experience errors when loading the pretrained Transformers model via `from_pretrained` method. When this setting is True `fsdp_sync_module_states` also must to be True, otherwise all the processes except the main process would have random weights leading to unexpected behaviour during training. For this to work, make sure the distributed process group is initialized before calling Transformers `from_pretrained` method. When using Trainer API, the distributed process group is initialized when you create an instance of `TrainingArguments` class.
|
||||
|
||||
`fsdp_sync_module_states`: If True, each individually wrapped FSDP unit will broadcast module parameters from rank 0.
|
||||
|
||||
@ -187,7 +187,7 @@ accelerate merge-weights pytorch_model_fsdp_0/ output_path
|
||||
## A few caveats to be aware of
|
||||
|
||||
- In case of multiple models, pass the optimizers to the prepare call in the same order as corresponding models else `accelerator.save_state()` and `accelerator.load_state()` will result in wrong/unexpected behaviour.
|
||||
- This feature is incompatible with `--predict_with_generate` in the `run_translation.py` script of 🤗 `Transformers` library.
|
||||
- This feature is incompatible with `--predict_with_generate` in the `run_translation.py` script of `Transformers` library.
|
||||
|
||||
For more control, users can leverage the `FullyShardedDataParallelPlugin`. After creating an instance of this class, users can pass it to the Accelerator class instantiation.
|
||||
For more information on these options, please refer to the PyTorch [FullyShardedDataParallel](https://github.com/pytorch/pytorch/blob/0df2e863fbd5993a7b9e652910792bd21a516ff3/torch/distributed/fsdp/fully_sharded_data_parallel.py#L236) code.
|
||||
@ -195,6 +195,6 @@ For more information on these options, please refer to the PyTorch [FullySharded
|
||||
|
||||
<Tip>
|
||||
|
||||
For those interested in the similarities and differences between FSDP and DeepSpeed, please check out the [concept guide here](../concept_guides/fsdp_and_deepspeed.md)!
|
||||
For those interested in the similarities and differences between FSDP and DeepSpeed, please check out the [concept guide here](../concept_guides/fsdp_and_deepspeed)!
|
||||
|
||||
</Tip>
|
||||
@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Performing gradient accumulation with 🤗 Accelerate
|
||||
# Performing gradient accumulation with Accelerate
|
||||
|
||||
Gradient accumulation is a technique where you can train on bigger batch sizes than
|
||||
your machine would normally be able to fit into memory. This is done by accumulating gradients over
|
||||
@ -22,7 +22,7 @@ several batches, and only stepping the optimizer after a certain number of batch
|
||||
While technically standard gradient accumulation code would work fine in a distributed setup, it is not the most efficient
|
||||
method for doing so and you may experience considerable slowdowns!
|
||||
|
||||
In this tutorial you will see how to quickly setup gradient accumulation and perform it with the utilities provided in 🤗 Accelerate,
|
||||
In this tutorial you will see how to quickly setup gradient accumulation and perform it with the utilities provided in Accelerate,
|
||||
which can total to adding just one new line of code!
|
||||
|
||||
This example will use a very simplistic PyTorch training loop that performs gradient accumulation every two batches:
|
||||
@ -47,9 +47,9 @@ for index, batch in enumerate(training_dataloader):
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
## Converting it to 🤗 Accelerate
|
||||
## Converting it to Accelerate
|
||||
|
||||
First the code shown earlier will be converted to utilize 🤗 Accelerate without the special gradient accumulation helper:
|
||||
First the code shown earlier will be converted to utilize Accelerate without the special gradient accumulation helper:
|
||||
|
||||
```diff
|
||||
+ from accelerate import Accelerator
|
||||
@ -79,9 +79,9 @@ First the code shown earlier will be converted to utilize 🤗 Accelerate withou
|
||||
|
||||
</Tip>
|
||||
|
||||
## Letting 🤗 Accelerate handle gradient accumulation
|
||||
## Letting Accelerate handle gradient accumulation
|
||||
|
||||
All that is left now is to let 🤗 Accelerate handle the gradient accumulation for us. To do so you should pass in a `gradient_accumulation_steps` parameter to [`Accelerator`], dictating the number
|
||||
All that is left now is to let Accelerate handle the gradient accumulation for us. To do so you should pass in a `gradient_accumulation_steps` parameter to [`Accelerator`], dictating the number
|
||||
of steps to perform before each call to `step()` and how to automatically adjust the loss during the call to [`~Accelerator.backward`]:
|
||||
|
||||
```diff
|
||||
@ -120,7 +120,7 @@ As you can see the [`Accelerator`] is able to keep track of the batch number you
|
||||
<Tip>
|
||||
|
||||
Typically with gradient accumulation, you would need to adjust the number of steps to reflect the change in total batches you are
|
||||
training on. 🤗 Accelerate automagically does this for you by default. Behind the scenes we instantiate a [`GradientAccumulationPlugin`] configured to do this.
|
||||
training on. Accelerate automagically does this for you by default. Behind the scenes we instantiate a [`GradientAccumulationPlugin`] configured to do this.
|
||||
|
||||
</Tip>
|
||||
|
||||
@ -140,7 +140,7 @@ accelerator = Accelerator(..., gradient_accumulation_plugin=plugin)
|
||||
|
||||
## The finished code
|
||||
|
||||
Below is the finished implementation for performing gradient accumulation with 🤗 Accelerate
|
||||
Below is the finished implementation for performing gradient accumulation with Accelerate
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator
|
||||
@ -171,7 +171,7 @@ To learn more about what magic this wraps around, read the [Gradient Synchroniza
|
||||
|
||||
## Self-contained example
|
||||
|
||||
Here is a self-contained example that you can run to see gradient accumulation in action with 🤗 Accelerate:
|
||||
Here is a self-contained example that you can run to see gradient accumulation in action with Accelerate:
|
||||
|
||||
```python
|
||||
import torch
|
||||
@ -187,38 +187,46 @@ set_seed(0)
|
||||
x = torch.tensor([1., 2., 3., 4., 5., 6., 7., 8.])
|
||||
y = torch.tensor([2., 4., 6., 8., 10., 12., 14., 16.])
|
||||
gradient_accumulation_steps = 4
|
||||
batch_size = len(x) // gradient_accumulation_steps
|
||||
per_device_batch_size = len(x) // gradient_accumulation_steps
|
||||
|
||||
# define dataset and dataloader
|
||||
dataset = TensorDataset(x, y)
|
||||
dataloader = DataLoader(dataset, batch_size=batch_size)
|
||||
dataloader = DataLoader(dataset, batch_size=per_device_batch_size)
|
||||
|
||||
# define model, optimizer and loss function
|
||||
model = torch.zeros((1, 1), requires_grad=True)
|
||||
class SimpleLinearModel(torch.nn.Module):
|
||||
def __init__(self):
|
||||
super(SimpleLinearModel, self).__init__()
|
||||
self.weight = torch.nn.Parameter(torch.zeros((1, 1)))
|
||||
|
||||
def forward(self, inputs):
|
||||
return inputs @ self.weight
|
||||
|
||||
model = SimpleLinearModel()
|
||||
model_clone = copy.deepcopy(model)
|
||||
criterion = torch.nn.MSELoss()
|
||||
model_optimizer = torch.optim.SGD([model], lr=0.02)
|
||||
model_optimizer = torch.optim.SGD(model.parameters(), lr=0.02)
|
||||
accelerator = Accelerator(gradient_accumulation_steps=gradient_accumulation_steps)
|
||||
model, model_optimizer, dataloader = accelerator.prepare(model, model_optimizer, dataloader)
|
||||
model_clone_optimizer = torch.optim.SGD([model_clone], lr=0.02)
|
||||
print(f"initial model weight is {model.mean().item():.5f}")
|
||||
print(f"initial model weight is {model_clone.mean().item():.5f}")
|
||||
model_clone_optimizer = torch.optim.SGD(model_clone.parameters(), lr=0.02)
|
||||
print(f"initial model weight is {model.weight.mean().item():.5f}")
|
||||
print(f"initial model weight is {model_clone.weight.mean().item():.5f}")
|
||||
for i, (inputs, labels) in enumerate(dataloader):
|
||||
with accelerator.accumulate(model):
|
||||
inputs = inputs.view(-1, 1)
|
||||
print(i, inputs.flatten())
|
||||
labels = labels.view(-1, 1)
|
||||
outputs = inputs @ model
|
||||
outputs = model(inputs)
|
||||
loss = criterion(outputs, labels)
|
||||
accelerator.backward(loss)
|
||||
model_optimizer.step()
|
||||
model_optimizer.zero_grad()
|
||||
loss = criterion(x.view(-1, 1) @ model_clone, y.view(-1, 1))
|
||||
loss = criterion(x.view(-1, 1) @ model_clone.weight, y.view(-1, 1))
|
||||
model_clone_optimizer.zero_grad()
|
||||
loss.backward()
|
||||
model_clone_optimizer.step()
|
||||
print(f"w/ accumulation, the final model weight is {model.mean().item():.5f}")
|
||||
print(f"w/o accumulation, the final model weight is {model_clone.mean().item():.5f}")
|
||||
print(f"w/ accumulation, the final model weight is {model.weight.mean().item():.5f}")
|
||||
print(f"w/o accumulation, the final model weight is {model_clone.weight.mean().item():.5f}")
|
||||
```
|
||||
```
|
||||
initial model weight is 0.00000
|
||||
@ -230,3 +238,233 @@ initial model weight is 0.00000
|
||||
w/ accumulation, the final model weight is 2.04000
|
||||
w/o accumulation, the final model weight is 2.04000
|
||||
```
|
||||
|
||||
## Gradient accumulation on training samples of variable size
|
||||
|
||||
As was pointed out in this [blog-post](https://huggingface.co/blog/gradient_accumulation), which points out a common error that occurs when performing gradient accumulation on training samples of variable size:
|
||||
|
||||
> [...] for gradient accumulation across token-level tasks like causal LM training, the correct loss should be computed by the **total loss across all batches in a gradient accumulation step** divided by the **total number of all non padding tokens in those batches**. This is not the same as the average of the per-batch loss values.
|
||||
|
||||
In other words, some adjustements must be made on losses that operate on a token-level basis.
|
||||
|
||||
### Skeleton code
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator
|
||||
import math
|
||||
import contextlib
|
||||
|
||||
gradient_accumulation_steps = 2
|
||||
accelerator = Accelerator(gradient_accumulation_steps=gradient_accumulation_steps)
|
||||
model, optimizer, training_dataloader, scheduler = accelerator.prepare(
|
||||
model, optimizer, training_dataloader, scheduler
|
||||
)
|
||||
|
||||
training_iterator = iter(training_dataloader)
|
||||
num_samples_in_epoch = len(training_dataloader)
|
||||
remainder = num_samples_in_epoch % gradient_accumulation_steps
|
||||
remainder = remainder if remainder != 0 else gradient_accumulation_steps
|
||||
total_updates = math.ceil(num_samples_in_epoch / gradient_accumulation_steps)
|
||||
|
||||
|
||||
total_batched_samples = 0
|
||||
for update_step in range(total_updates):
|
||||
# In order to correctly the total number of non-padded tokens on which we'll compute the cross-entropy loss
|
||||
# we need to pre-load the full local batch - i.e the next per_device_batch_size * accumulation_steps samples
|
||||
batch_samples = []
|
||||
num_batches_in_step = gradient_accumulation_steps if update_step != (total_updates - 1) else remainder
|
||||
for _ in range(num_batches_in_step):
|
||||
batch_samples += [next(training_iterator)]
|
||||
|
||||
# get local num items in batch
|
||||
num_items_in_batch = sum([(batch["labels"].ne(-100)).sum() for batch in batch_samples])
|
||||
# to compute it correctly in a multi-device DDP training, we need to gather the total number of items in the full batch.
|
||||
num_items_in_batch = accelerator.gather(num_items_in_batch).sum().item()
|
||||
|
||||
for i, batch in enumerate(batch_samples):
|
||||
# if we perform gradient accumulation in a multi-devices set-up, we want to avoid unecessary communications when accumulating
|
||||
# cf: https://muellerzr.github.io/blog/gradient_accumulation.html
|
||||
if (i < len(batch_samples) - 1 and accelerator.num_processes > 1):
|
||||
ctx = model.no_sync
|
||||
else:
|
||||
ctx = contextlib.nullcontext
|
||||
|
||||
total_batched_samples += 1
|
||||
|
||||
with ctx():
|
||||
inputs, targets = batch
|
||||
outputs = model(inputs)
|
||||
loss = loss_function(outputs, targets) # the loss function shoud sum over samples rather than averaging
|
||||
|
||||
# We multiply by num_processes because the DDP calculates the average gradient across all devices whereas dividing by num_items_in_batch already takes into account all devices
|
||||
# Same reason for gradient_accumulation_steps, but this times it's Accelerate that calculate the average gradient across the accumulated steps
|
||||
loss = (loss * gradient_accumulation_steps * accelerator.num_processes) / num_items_in_batch
|
||||
|
||||
accelerator.backward(loss)
|
||||
|
||||
# Sync gradients and perform optimization steps once every gradient_accumulation_steps
|
||||
optimizer.step()
|
||||
scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
### Self-contained causal LM example
|
||||
|
||||
```py
|
||||
import torch
|
||||
import copy
|
||||
from accelerate import Accelerator
|
||||
from accelerate.utils import set_seed
|
||||
from accelerate.logging import get_logger
|
||||
from torch.utils.data import Dataset, DataLoader
|
||||
import math
|
||||
import contexlib
|
||||
|
||||
# seed
|
||||
set_seed(0)
|
||||
logger = get_logger(__name__)
|
||||
|
||||
class MyDataset(Dataset):
|
||||
def __init__(self, num_samples):
|
||||
super().__init__()
|
||||
self.len = num_samples
|
||||
|
||||
def __getitem__(self, index):
|
||||
input_ids = torch.arange(1, index+2, dtype=torch.float32)
|
||||
labels = torch.remainder(input_ids, 2)
|
||||
return {"input_ids": input_ids, "labels": labels}
|
||||
|
||||
def __len__(self):
|
||||
return self.len
|
||||
|
||||
def collate_fn(features):
|
||||
input_ids = torch.nn.utils.rnn.pad_sequence([f["input_ids"] for f in features], batch_first=True, padding_value=-100)
|
||||
labels = torch.nn.utils.rnn.pad_sequence([f["labels"] for f in features], batch_first=True, padding_value=-100)
|
||||
return {"input_ids": input_ids[..., None], "labels": labels[..., None]}
|
||||
|
||||
# define toy inputs and labels
|
||||
gradient_accumulation_steps = 2
|
||||
per_device_batch_size = 4
|
||||
|
||||
# define accelerator
|
||||
accelerator = Accelerator(gradient_accumulation_steps=gradient_accumulation_steps)
|
||||
|
||||
# define dataset and dataloader
|
||||
# for this toy example, we'll compute gradient descent over one single global batch
|
||||
dataset = MyDataset(per_device_batch_size*gradient_accumulation_steps*accelerator.num_processes)
|
||||
dataloader = DataLoader(dataset, batch_size=per_device_batch_size, collate_fn=collate_fn)
|
||||
|
||||
# define model, model_optimizer and loss function
|
||||
model = torch.nn.Linear(1, 2, bias=False)
|
||||
model_clone = copy.deepcopy(model)
|
||||
criterion = torch.nn.CrossEntropyLoss(reduction="sum") # must sum over samples rather than averaging
|
||||
model_optimizer = torch.optim.SGD(model.parameters(), lr=0.08)
|
||||
|
||||
|
||||
logger.warning(f"initial model weight is {model.weight.detach().cpu().squeeze()}")
|
||||
logger.warning(f"initial model clone weight is {model_clone.weight.detach().cpu().squeeze()}")
|
||||
|
||||
# prepare artifacts - accelerator handles device placement and dataloader splitting
|
||||
model, model_optimizer = accelerator.prepare(model, model_optimizer)
|
||||
dataloader = accelerator.prepare_data_loader(dataloader, device_placement=True)
|
||||
training_iterator = iter(dataloader)
|
||||
|
||||
num_samples_in_epoch = len(dataloader)
|
||||
remainder = num_samples_in_epoch % gradient_accumulation_steps
|
||||
remainder = remainder if remainder != 0 else gradient_accumulation_steps
|
||||
total_gradient_updates = math.ceil(num_samples_in_epoch / gradient_accumulation_steps)
|
||||
|
||||
total_batched_samples = 0
|
||||
for update_step in range(total_gradient_updates):
|
||||
# In order to correctly the total number of non-padded tokens on which we'll compute the cross-entropy loss
|
||||
# we need to pre-load the full local batch - i.e the next per_device_batch_size * accumulation_steps samples
|
||||
batch_samples = []
|
||||
num_batches_in_step = gradient_accumulation_steps if update_step != (total_gradient_updates - 1) else remainder
|
||||
for _ in range(num_batches_in_step):
|
||||
batch_samples += [next(training_iterator)]
|
||||
|
||||
# get local num items in batch
|
||||
local_num_items_in_batch = sum([(batch["labels"].ne(-100)).sum() for batch in batch_samples])
|
||||
logger.warning(f"Step {update_step} - Device {accelerator.process_index} - num items in the local batch {local_num_items_in_batch}", main_process_only=False)
|
||||
|
||||
# to compute it correctly in a multi-device DDP training, we need to gather the total number of items in the full batch.
|
||||
num_items_in_batch = accelerator.gather(local_num_items_in_batch).sum().item()
|
||||
logger.warning(f"Total num items {num_items_in_batch}")
|
||||
|
||||
for i, batch in enumerate(batch_samples):
|
||||
inputs, labels = batch["input_ids"], batch["labels"]
|
||||
total_batched_samples += 1
|
||||
# if we perform gradient accumulation in a multi-devices set-up, we want to avoid unecessary communications when accumulating
|
||||
# cf: https://muellerzr.github.io/blog/gradient_accumulation.html
|
||||
if (i < len(batch_samples) - 1 and accelerator.num_processes > 1):
|
||||
ctx = model.no_sync
|
||||
else:
|
||||
ctx = contextlib.nullcontext
|
||||
with ctx():
|
||||
|
||||
outputs = model(inputs)
|
||||
loss = criterion(outputs.view(-1, 2), labels.view(-1).to(torch.int64))
|
||||
|
||||
# We multiply by num_processes because the DDP calculates the average gradient across all devices whereas dividing by num_items_in_batch already takes into account all devices
|
||||
# Same reason for gradient_accumulation_steps, but this times it's Accelerate that calculate the average gradient across the accumulated steps
|
||||
loss = (loss * gradient_accumulation_steps * accelerator.num_processes) / num_items_in_batch
|
||||
accelerator.backward(loss)
|
||||
model_optimizer.step()
|
||||
model_optimizer.zero_grad()
|
||||
|
||||
|
||||
logger.warning(f"Device {accelerator.process_index} - w/ accumulation, the final model weight is {accelerator.unwrap_model(model).weight.detach().cpu().squeeze()}", main_process_only=False)
|
||||
|
||||
# We know do the same operation but on a single device and without gradient accumulation
|
||||
|
||||
if accelerator.is_main_process:
|
||||
# prepare one single entire batch
|
||||
dataloader = DataLoader(dataset, batch_size=len(dataset), collate_fn=collate_fn)
|
||||
full_batch_without_accum = next(iter(dataloader))
|
||||
total_inputs, total_labels = full_batch_without_accum["input_ids"], full_batch_without_accum["labels"]
|
||||
model_clone_optimizer = torch.optim.SGD(model_clone.parameters(), lr=0.08)
|
||||
|
||||
# train the cloned model
|
||||
loss = torch.nn.CrossEntropyLoss(reduction="mean")(model_clone(total_inputs).view(-1, 2), total_labels.view(-1).to(torch.int64))
|
||||
model_clone_optimizer.zero_grad()
|
||||
loss.backward()
|
||||
model_clone_optimizer.step()
|
||||
|
||||
# We should have the same final weights.
|
||||
logger.warning(f"w/o accumulation, the final model weight is {model_clone.weight.detach().cpu().squeeze()}")
|
||||
|
||||
```
|
||||
|
||||
Results on a single device - gradient accumulation steps set to 1 and batch_size set to 8:
|
||||
```
|
||||
initial model weight is tensor([-0.0075, 0.5364])
|
||||
initial model clone weight is tensor([-0.0075, 0.5364])
|
||||
Step 0 - Device 0 - num items in the local batch 36
|
||||
Total num items 36
|
||||
Device 0 - w/ accumulation, the final model weight is tensor([0.0953, 0.4337])
|
||||
w/o accumulation, the final model weight is tensor([0.0953, 0.4337])
|
||||
```
|
||||
|
||||
Results on a two devices set-up - gradient accumulation steps set to 2 and batch_size set to 4.
|
||||
```
|
||||
initial model weight is tensor([-0.0075, 0.5364])
|
||||
initial model clone weight is tensor([-0.0075, 0.5364])
|
||||
Step 0 - Device 0 - num items in the local batch 52
|
||||
Step 0 - Device 1 - num items in the local batch 84
|
||||
Total num items 136
|
||||
Device 1 - w/ accumulation, the final model weight is tensor([0.2117, 0.3172])
|
||||
Device 0 - w/ accumulation, the final model weight is tensor([0.2117, 0.3172])
|
||||
w/o accumulation, the final model weight is tensor([0.2117, 0.3172])
|
||||
```
|
||||
|
||||
### To go further:
|
||||
|
||||
Please find a complete example script on a real world training run in the examples folder at the path [`accelerate/examples/by_feature/gradient_accumulation_for_autoregressive_models.py`](https://github.com/huggingface/accelerate/blob/main/examples/by_feature/gradient_accumulation_for_autoregressive_models.py).
|
||||
|
||||
Running it on several training configurations with constant global batch size equal to 32 gives the following graph:
|
||||
|
||||
<div style="text-align: center">
|
||||
<img src="https://huggingface.co/datasets/hf-audio/gradient_accumulation_example/resolve/main/training_losses.png">
|
||||
</div>
|
||||
|
||||
Note that the training losses are exactly the same up to training step 20. The small deviation after this training step occurs at the very end of the first epoch, because, by [default](https://huggingface.co/docs/accelerate/en/package_reference/torch_wrappers#accelerate.data_loader.prepare_data_loader.even_batches), the dataloader duplicates the samples at the beginning of the dataset when the total batch size doesn't exactly divide the dataset.
|
||||
|
||||
@ -40,7 +40,7 @@ Check more approaches for [IPEX installation](https://intel.github.io/intel-exte
|
||||
|
||||
## How It Works For Training optimization in CPU
|
||||
|
||||
🤗 Accelerate has integrated [IPEX](https://github.com/intel/intel-extension-for-pytorch), all you need to do is enabling it through the config.
|
||||
Accelerate has integrated [IPEX](https://github.com/intel/intel-extension-for-pytorch), all you need to do is enabling it through the config.
|
||||
|
||||
**Scenario 1**: Acceleration of No distributed CPU training
|
||||
|
||||
|
||||
@ -13,12 +13,12 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Using Local SGD with 🤗 Accelerate
|
||||
# Using Local SGD with Accelerate
|
||||
|
||||
Local SGD is a technique for distributed training where gradients are not synchronized every step. Thus, each process updates its own version of the model weights and after a given number of steps these weights are synchronized by averaging across all processes. This improves communication efficiency and can lead to substantial training speed up especially when a computer lacks a faster interconnect such as NVLink.
|
||||
Unlike gradient accumulation (where improving communication efficiency requires increasing the effective batch size), Local SGD does not require changing a batch size or a learning rate / schedule. However, if necessary, Local SGD can be combined with gradient accumulation as well.
|
||||
|
||||
In this tutorial you will see how to quickly setup Local SGD 🤗 Accelerate. Compared to a standard Accelerate setup, this requires only two extra lines of code.
|
||||
In this tutorial you will see how to quickly setup Local SGD Accelerate. Compared to a standard Accelerate setup, this requires only two extra lines of code.
|
||||
|
||||
This example will use a very simplistic PyTorch training loop that performs gradient accumulation every two batches:
|
||||
|
||||
@ -42,9 +42,9 @@ for index, batch in enumerate(training_dataloader):
|
||||
optimizer.zero_grad()
|
||||
```
|
||||
|
||||
## Converting it to 🤗 Accelerate
|
||||
## Converting it to Accelerate
|
||||
|
||||
First the code shown earlier will be converted to use 🤗 Accelerate with neither a LocalSGD or a gradient accumulation helper:
|
||||
First the code shown earlier will be converted to use Accelerate with neither a LocalSGD or a gradient accumulation helper:
|
||||
|
||||
```diff
|
||||
+ from accelerate import Accelerator
|
||||
@ -67,9 +67,9 @@ First the code shown earlier will be converted to use 🤗 Accelerate with neit
|
||||
scheduler.step()
|
||||
```
|
||||
|
||||
## Letting 🤗 Accelerate handle model synchronization
|
||||
## Letting Accelerate handle model synchronization
|
||||
|
||||
All that is left now is to let 🤗 Accelerate handle model parameter synchronization **and** the gradient accumulation for us. For simplicity let us assume we need to synchronize every 8 steps. This is
|
||||
All that is left now is to let Accelerate handle model parameter synchronization **and** the gradient accumulation for us. For simplicity let us assume we need to synchronize every 8 steps. This is
|
||||
achieved by adding one `with LocalSGD` statement and one call `local_sgd.step()` after every optimizer step:
|
||||
|
||||
```diff
|
||||
|
||||
@ -15,11 +15,11 @@ rendered properly in your Markdown viewer.
|
||||
|
||||
# Low Precision Training Methods
|
||||
|
||||
🤗 Accelerate provides integrations to train on lower precision methods using specified supported hardware through the `TransformersEngine` and `MS-AMP` packages. This documentation will help guide you through what hardware is supported, how to configure your [`Accelerator`] to leverage the low precision methods, and what you can expect when training.
|
||||
Accelerate provides integrations to train on lower precision methods using specified supported hardware through the `TransformersEngine` and `MS-AMP` packages. This documentation will help guide you through what hardware is supported, how to configure your [`Accelerator`] to leverage the low precision methods, and what you can expect when training.
|
||||
|
||||
## What training on FP8 means
|
||||
|
||||
To explore more of the nitty-gritty in training in FP8 with PyTorch and 🤗 Accelerate, check out the [concept_guide](../concept_guides/low_precision_training.md) on why this can be difficult. But essentially rather than training in BF16, some (or all) aspects of training a model can be performed using 8 bits instead of 16. The challenge is doing so without degrading final performance.
|
||||
To explore more of the nitty-gritty in training in FP8 with PyTorch and Accelerate, check out the [concept_guide](../concept_guides/low_precision_training) on why this can be difficult. But essentially rather than training in BF16, some (or all) aspects of training a model can be performed using 8 bits instead of 16. The challenge is doing so without degrading final performance.
|
||||
|
||||
This is only enabled on specific NVIDIA hardware, namely:
|
||||
|
||||
@ -39,7 +39,7 @@ from accelerate import Accelerator
|
||||
accelerator = Accelerator(mixed_precision="fp8")
|
||||
```
|
||||
|
||||
By default, if `MS-AMP` is available in your environment, 🤗 Accelerate will automatically utilize it as a backend. To specify it yourself (and customize other parts of the FP8 mixed precision setup), you can utilize the [`utils.FP8RecipeKwargs`]:
|
||||
By default, if `MS-AMP` is available in your environment, Accelerate will automatically utilize it as a backend. To specify it yourself (and customize other parts of the FP8 mixed precision setup), you can utilize the [`utils.FP8RecipeKwargs`] or clarify it in your config `yaml`/during `accelerate launch`:
|
||||
|
||||
```{python}
|
||||
from accelerate import Accelerator
|
||||
@ -50,11 +50,24 @@ kwargs = [FP8RecipeKwargs(backend="msamp")]
|
||||
accelerator = Accelerator(mixed_precision="fp8", kwarg_handlers=kwargs)
|
||||
```
|
||||
|
||||
```{yaml}
|
||||
mixed_precision: fp8
|
||||
fp8_config:
|
||||
amax_compute_algorithm: max
|
||||
amax_history_length: 1024
|
||||
backend: TE
|
||||
fp8_format: HYBRID
|
||||
interval: 1
|
||||
margin: 0
|
||||
override_linear_precision: false
|
||||
use_autocast_during_eval: false
|
||||
```
|
||||
|
||||
## Configuring MS-AMP
|
||||
|
||||
Of the two, `MS-AMP` is traditionally the easier one to configure as there is only a single argument: the optimization level.
|
||||
|
||||
Currently two levels of optimization are supported in the 🤗 Accelerate integration, `"O1"` and `"O2"` (using the letter 'o', not zero).
|
||||
Currently two levels of optimization are supported in the Accelerate integration, `"O1"` and `"O2"` (using the letter 'o', not zero).
|
||||
|
||||
* `"O1"` will cast the weight gradients and `all_reduce` communications to happen in 8-bit, while the rest are done in 16 bit. This reduces the general GPU memory usage and speeds up communication bandwidths.
|
||||
* `"O2"` will also cast first-order optimizer states into 8 bit, while the second order states are in FP16. (Currently just the `Adam` optimizer is supported). This tries its best to minimize final accuracy degradation and will save the highest potential memory.
|
||||
@ -68,11 +81,22 @@ kwargs = [FP8RecipeKwargs(backend="msamp", optimization_level="O2")]
|
||||
accelerator = Accelerator(mixed_precision="fp8", kwarg_handlers=kwargs)
|
||||
```
|
||||
|
||||
Or during `accelerate launch` via `--fp8_backend=msamp --fp8_opt_level=O2`
|
||||
|
||||
Similarly this can be set in your `config.yaml`:
|
||||
|
||||
```{yaml}
|
||||
mixed_precision: fp8
|
||||
fp8_config:
|
||||
backend: MSAMP
|
||||
opt_level: O2
|
||||
```
|
||||
|
||||
## Configuring TransformersEngine
|
||||
|
||||
TransformersEngine has much more available for customizing how and what FP8 calculations are performed. A full list of supported arguments and what they mean are available in [NVIDIA's documentation](https://docs.nvidia.com/deeplearning/transformer-engine/user-guide/api/common.html), however they are restated as part of [`FP8KwargsHandler`]'s docstring for your convenience.
|
||||
|
||||
🤗 Accelerate tries to set sensible defaults, but exploring and tweaking the various parameters yourself can lead to better performance potentially.
|
||||
Accelerate tries to set sensible defaults, but exploring and tweaking the various parameters yourself can lead to better performance potentially.
|
||||
|
||||
To use it, specify `backend="te"` and modify any of the arguments you want as part of your kwarg handler:
|
||||
|
||||
@ -83,10 +107,39 @@ kwargs = [FP8RecipeKwargs(backend="te", ...)]
|
||||
accelerator = Accelerator(mixed_precision="fp8", kwarg_handlers=kwargs)
|
||||
```
|
||||
|
||||
Or during `accelerate launch` via `--fp8_backend=te ...`. Use `accelerate launch --fp8_backend=te -h` to see relevent arguments.
|
||||
|
||||
Similarly this can be set in your `config.yaml`:
|
||||
|
||||
```{yaml}
|
||||
mixed_precision: fp8
|
||||
fp8_config:
|
||||
amax_compute_algorithm: max
|
||||
amax_history_length: 1024
|
||||
backend: TE
|
||||
fp8_format: HYBRID
|
||||
interval: 1
|
||||
margin: 0
|
||||
override_linear_precision: false
|
||||
use_autocast_during_eval: false
|
||||
```
|
||||
|
||||
## Example Zoo
|
||||
|
||||
We have examples showcasing training with FP8 both with accelerate and its underlying implementation available in the accelerate repo.
|
||||
Currently we support scripts showcasing:
|
||||
|
||||
* Single GPU
|
||||
* Distributed Data Parallelism (Multi-GPU)
|
||||
* Fully Sharded Data Parallelism
|
||||
* DeepSpeed ZeRO 1 through 3
|
||||
|
||||
Find out more [here](https://github.com/huggingface/accelerate/tree/main/benchmarks/fp8)
|
||||
|
||||
## Further Reading
|
||||
|
||||
To learn more about training in FP8 please check out the following resources:
|
||||
|
||||
* [Our concept guide](../concept_guides/low_precision_training.md) detailing into more about both TransformersEngine and MS-AMP
|
||||
* [Our concept guide](../concept_guides/low_precision_training) detailing into more about both TransformersEngine and MS-AMP
|
||||
* [The `transformers-engine` documentation](https://docs.nvidia.com/deeplearning/transformer-engine/user-guide/api/common.html)
|
||||
* [The `MS-AMP` documentation](https://azure.github.io/MS-AMP/docs/)
|
||||
|
||||
@ -32,7 +32,7 @@ independently and in parallel by each shard followed by syncing across all GPUs
|
||||
In a simple transformer layer, this leads to 2 `all-reduces` in the forward path and 2 in the backward path.
|
||||
For more details, please refer research paper [Megatron-LM: Training Multi-Billion Parameter Language Models Using
|
||||
Model Parallelism](https://arxiv.org/pdf/1909.08053.pdf) and
|
||||
this section of 🤗 blogpost [The Technology Behind BLOOM Training](https://huggingface.co/blog/bloom-megatron-deepspeed#tensor-parallelism).
|
||||
this section of blogpost [The Technology Behind BLOOM Training](https://huggingface.co/blog/bloom-megatron-deepspeed#tensor-parallelism).
|
||||
|
||||
|
||||
b. **Pipeline Parallelism (PP)**: Reduces memory footprint and enables large scale training via inter-node parallelization.
|
||||
@ -41,7 +41,7 @@ Layers are distributed uniformly across PP stages. For example, if a model has `
|
||||
pipeline parallelism, each GPU will have `6` layers (24/4). For more details on schedules to reduce the idle time of PP,
|
||||
please refer to the research paper [Efficient Large-Scale Language Model Training on GPU Clusters
|
||||
Using Megatron-LM](https://arxiv.org/pdf/2104.04473.pdf) and
|
||||
this section of 🤗 blogpost [The Technology Behind BLOOM Training](https://huggingface.co/blog/bloom-megatron-deepspeed#pipeline-parallelism).
|
||||
this section of blogpost [The Technology Behind BLOOM Training](https://huggingface.co/blog/bloom-megatron-deepspeed#pipeline-parallelism).
|
||||
|
||||
c. **Sequence Parallelism (SP)**: Reduces memory footprint without any additional communication. Only applicable when using TP.
|
||||
It reduces activation memory required as it prevents the same copies to be on the tensor parallel ranks
|
||||
@ -57,7 +57,7 @@ d. **Data Parallelism (DP)** via Distributed Optimizer: Reduces the memory footp
|
||||
For example, when using Adam optimizer with mixed-precision training, each parameter accounts for 12 bytes of memory.
|
||||
This gets distributed equally across the GPUs, i.e., each parameter would account for 3 bytes (12/4) if we have 4 GPUs.
|
||||
For more details, please refer the research paper [ZeRO: Memory Optimizations Toward Training Trillion
|
||||
Parameter Models](https://arxiv.org/pdf/1910.02054.pdf) and following section of 🤗 blog
|
||||
Parameter Models](https://arxiv.org/pdf/1910.02054.pdf) and following section of blog
|
||||
[The Technology Behind BLOOM Training](https://huggingface.co/blog/bloom-megatron-deepspeed#zero-data-parallelism).
|
||||
|
||||
e. **Selective Activation Recomputation**: Reduces the memory footprint of activations significantly via smart activation checkpointing.
|
||||
@ -72,9 +72,9 @@ PyTorch JIT compiled Fused GeLU and Fused Bias+Dropout+Residual addition.
|
||||
g. **Support for Indexed datasets**: Efficient binary format of datasets for large scale training. Support for the `mmap`, `cached` index file and the `lazy` loader format.
|
||||
|
||||
h. **Checkpoint reshaping and interoperability**: Utility for reshaping Megatron-LM checkpoints of variable
|
||||
tensor and pipeline parallel sizes to the beloved 🤗 Transformers sharded checkpoints as it has great support with plethora of tools
|
||||
such as 🤗 Accelerate Big Model Inference, Megatron-DeepSpeed Inference etc.
|
||||
Support is also available for converting 🤗 Transformers sharded checkpoints to Megatron-LM checkpoint of variable tensor and pipeline parallel sizes
|
||||
tensor and pipeline parallel sizes to the beloved Transformers sharded checkpoints as it has great support with plethora of tools
|
||||
such as Accelerate Big Model Inference, Megatron-DeepSpeed Inference etc.
|
||||
Support is also available for converting Transformers sharded checkpoints to Megatron-LM checkpoint of variable tensor and pipeline parallel sizes
|
||||
for large scale training.
|
||||
|
||||
|
||||
@ -107,7 +107,10 @@ cd ..
|
||||
4. Installing Megatron-LM
|
||||
|
||||
```
|
||||
pip install git+https://github.com/huggingface/Megatron-LM.git
|
||||
git clone https://github.com/NVIDIA/Megatron-LM.git
|
||||
cd Megatron-LM
|
||||
git checkout core_r0.5.0
|
||||
pip install --no-use-pep517 -e .
|
||||
```
|
||||
|
||||
## Accelerate Megatron-LM Plugin
|
||||
@ -356,7 +359,7 @@ def main():
|
||||
2. For using the Megatron-LM datasets, a few more changes are required. Dataloaders for these datasets
|
||||
are available only on rank 0 of each tensor parallel group. As such, there are rank where dataloader won't be
|
||||
available and this requires tweaks to the training loop. Being able to do all this shows how
|
||||
flexible and extensible 🤗 Accelerate is. The changes required are as follows.
|
||||
flexible and extensible Accelerate is. The changes required are as follows.
|
||||
|
||||
a. For Megatron-LM indexed datasets, we need to use `MegatronLMDummyDataLoader`
|
||||
and pass the required dataset args to it such as `data_path`, `seq_length` etc.
|
||||
@ -388,7 +391,7 @@ c. Changes to training and evaluation loops as dataloader is only available on t
|
||||
So, we need to iterate only if the dataloader isn't `None` else provide empty dict
|
||||
As such, we loop using `while` loop and break when `completed_steps` is equal to `args.max_train_steps`
|
||||
This is similar to the Megatron-LM setup wherein user has to provide `max_train_steps` when using Megaton-LM indexed datasets.
|
||||
This displays how flexible and extensible 🤗 Accelerate is.
|
||||
This displays how flexible and extensible Accelerate is.
|
||||
|
||||
```python
|
||||
while completed_steps < args.max_train_steps:
|
||||
@ -411,10 +414,10 @@ while completed_steps < args.max_train_steps:
|
||||
|
||||
## Utility for Checkpoint reshaping and interoperability
|
||||
|
||||
1. The scripts for these are present in 🤗 Transformers library under respective models.
|
||||
1. The scripts for these are present in Transformers library under respective models.
|
||||
Currently, it is available for GPT model [checkpoint_reshaping_and_interoperability.py](https://github.com/huggingface/transformers/blob/main/src/transformers/models/megatron_gpt2/checkpoint_reshaping_and_interoperability.py)
|
||||
|
||||
2. Below is an example of conversion of checkpoint from Megatron-LM to universal 🤗 Transformers sharded checkpoint.
|
||||
2. Below is an example of conversion of checkpoint from Megatron-LM to universal Transformers sharded checkpoint.
|
||||
```bash
|
||||
python checkpoint_reshaping_and_interoperability.py \
|
||||
--convert_checkpoint_from_megatron_to_transformers \
|
||||
@ -566,18 +569,18 @@ setting is synonymous with gradient accumulation.
|
||||
|
||||
7. When using Megatron-LM, use `accelerator.save_state` and `accelerator.load_state` for saving and loading checkpoints.
|
||||
|
||||
8. Below are the mapping from Megatron-LM model architectures to the the equivalent 🤗 transformers model architectures.
|
||||
Only these 🤗 transformers model architectures are supported.
|
||||
8. Below are the mapping from Megatron-LM model architectures to the the equivalent transformers model architectures.
|
||||
Only these transformers model architectures are supported.
|
||||
|
||||
a. Megatron-LM [BertModel](https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/bert_model.py) :
|
||||
🤗 transformers models with `megatron-bert` in config's model type, e.g.,
|
||||
transformers models with `megatron-bert` in config's model type, e.g.,
|
||||
[MegatronBERT](https://huggingface.co/docs/transformers/model_doc/megatron-bert)
|
||||
|
||||
b. Megatron-LM [GPTModel](https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py) :
|
||||
🤗 transformers models with `gpt2` in config's model type, e.g.,
|
||||
transformers models with `gpt2` in config's model type, e.g.,
|
||||
[OpenAI GPT2](https://huggingface.co/docs/transformers/model_doc/gpt2)
|
||||
|
||||
c. Megatron-LM [T5Model](https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/t5_model.py) :
|
||||
🤗 transformers models with `t5` in config's model type, e.g.,
|
||||
transformers models with `t5` in config's model type, e.g.,
|
||||
[T5](https://huggingface.co/docs/transformers/model_doc/t5) and
|
||||
[MT5](https://huggingface.co/docs/transformers/model_doc/mt5)
|
||||
|
||||
@ -13,12 +13,12 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Understanding how big of a model can fit on your machine
|
||||
# Model memory estimator
|
||||
|
||||
One very difficult aspect when exploring potential models to use on your machine is knowing just how big of a model will *fit* into memory with your current graphics card (such as loading the model onto CUDA).
|
||||
|
||||
To help alleviate this, 🤗 Accelerate has a CLI interface through `accelerate estimate-memory`. This tutorial will
|
||||
help walk you through using it, what to expect, and at the end link to the interactive demo hosted on the 🤗 Hub which will
|
||||
To help alleviate this, Accelerate has a CLI interface through `accelerate estimate-memory`. This tutorial will
|
||||
help walk you through using it, what to expect, and at the end link to the interactive demo hosted on the Hub which will
|
||||
even let you post those results directly on the model repo!
|
||||
|
||||
Currently we support searching for models that can be used in `timm` and `transformers`.
|
||||
|
||||
@ -44,11 +44,8 @@ accelerate launch /examples/cv_example.py --data_dir images
|
||||
|
||||
## A few caveats to be aware of
|
||||
|
||||
1. We strongly recommend to install PyTorch >= 1.13 (nightly version at the time of writing) on your MacOS machine.
|
||||
It has major fixes related to model correctness and performance improvements for transformer based models.
|
||||
Please refer to https://github.com/pytorch/pytorch/issues/82707 for more details.
|
||||
2. Distributed setups `gloo` and `nccl` are not working with `mps` device.
|
||||
1. Distributed setups `gloo` and `nccl` are not working with `mps` device.
|
||||
This means that currently only single GPU of `mps` device type can be used.
|
||||
|
||||
Finally, please, remember that, 🤗 `Accelerate` only integrates MPS backend, therefore if you
|
||||
Finally, please, remember that, `Accelerate` only integrates MPS backend, therefore if you
|
||||
have any problems or questions with regards to MPS backend usage, please, file an issue with [PyTorch GitHub](https://github.com/pytorch/pytorch/issues).
|
||||
337
docs/source/usage_guides/profiler.md
Normal file
337
docs/source/usage_guides/profiler.md
Normal file
@ -0,0 +1,337 @@
|
||||
<!--
|
||||
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 contains specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Profiler
|
||||
|
||||
Profiler is a tool that allows the collection of performance metrics during training and inference. Profiler’s context manager API can be used to better understand what model operators are the most expensive, examine their input shapes and stack traces, study device kernel activity, and visualize the execution trace. It provides insights into the performance of your model, allowing you to optimize and improve it.
|
||||
|
||||
This guide explains how to use PyTorch Profiler to measure the time and memory consumption of the model’s operators and how to integrate this with Accelerate. We will cover various use cases and provide examples for each.
|
||||
|
||||
## Using profiler to analyze execution time
|
||||
|
||||
Profiler allows one to check which operators were called during the execution of a code range wrapped with a profiler context manager.
|
||||
|
||||
Let’s see how we can use profiler to analyze the execution time:
|
||||
|
||||
<hfoptions id="cpu execution time">
|
||||
<hfoption id="PyTorch">
|
||||
|
||||
```python
|
||||
import torch
|
||||
import torchvision.models as models
|
||||
from torch.profiler import profile, record_function, ProfilerActivity
|
||||
|
||||
model = models.resnet18()
|
||||
inputs = torch.randn(5, 3, 224, 224)
|
||||
|
||||
with profile(activities=[ProfilerActivity.CPU], record_shapes=True) as prof:
|
||||
model(inputs)
|
||||
|
||||
print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Accelerate">
|
||||
|
||||
```python
|
||||
from accelerate import Accelerator, ProfileKwargs
|
||||
import torch
|
||||
import torchvision.models as models
|
||||
|
||||
model = models.resnet18()
|
||||
inputs = torch.randn(5, 3, 224, 224)
|
||||
|
||||
profile_kwargs = ProfileKwargs(
|
||||
activities=["cpu"],
|
||||
record_shapes=True
|
||||
)
|
||||
|
||||
accelerator = Accelerator(cpu=True, kwargs_handlers=[profile_kwargs])
|
||||
model = accelerator.prepare(model)
|
||||
|
||||
with accelerator.profile() as prof:
|
||||
with torch.no_grad():
|
||||
model(inputs)
|
||||
|
||||
print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
The resulting table output (omitting some columns):
|
||||
|
||||
```
|
||||
--------------------------------- ------------ ------------ ------------ ------------
|
||||
Name Self CPU CPU total CPU time avg # of Calls
|
||||
--------------------------------- ------------ ------------ ------------ ------------
|
||||
aten::conv2d 171.000us 52.260ms 2.613ms 20
|
||||
aten::convolution 227.000us 52.089ms 2.604ms 20
|
||||
aten::_convolution 270.000us 51.862ms 2.593ms 20
|
||||
aten::mkldnn_convolution 51.273ms 51.592ms 2.580ms 20
|
||||
aten::batch_norm 118.000us 7.059ms 352.950us 20
|
||||
aten::_batch_norm_impl_index 315.000us 6.941ms 347.050us 20
|
||||
aten::native_batch_norm 6.305ms 6.599ms 329.950us 20
|
||||
aten::max_pool2d 40.000us 4.008ms 4.008ms 1
|
||||
aten::max_pool2d_with_indices 3.968ms 3.968ms 3.968ms 1
|
||||
aten::add_ 780.000us 780.000us 27.857us 28
|
||||
--------------------------------- ------------ ------------ ------------ ------------
|
||||
Self CPU time total: 67.016ms
|
||||
```
|
||||
|
||||
To get a finer granularity of results and include operator input shapes, pass `group_by_input_shape=True` (note: this requires running the profiler with `record_shapes=True`):
|
||||
|
||||
```python
|
||||
print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10))
|
||||
```
|
||||
|
||||
## Using profiler to analyze memory consumption
|
||||
|
||||
Profiler can also show the amount of memory (used by the model’s tensors) that was allocated (or released) during the execution of the model’s operators. To enable memory profiling functionality pass `profile_memory=True`.
|
||||
|
||||
<hfoptions id="memory consumption">
|
||||
<hfoption id="PyTorch">
|
||||
|
||||
```python
|
||||
model = models.resnet18()
|
||||
inputs = torch.randn(5, 3, 224, 224)
|
||||
|
||||
with profile(activities=[ProfilerActivity.CPU],
|
||||
profile_memory=True, record_shapes=True) as prof:
|
||||
model(inputs)
|
||||
|
||||
print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Accelerate">
|
||||
|
||||
```python
|
||||
model = models.resnet18()
|
||||
inputs = torch.randn(5, 3, 224, 224)
|
||||
|
||||
profile_kwargs = ProfileKwargs(
|
||||
activities=["cpu"],
|
||||
profile_memory=True,
|
||||
record_shapes=True
|
||||
)
|
||||
|
||||
accelerator = Accelerator(cpu=True, kwargs_handlers=[profile_kwargs])
|
||||
model = accelerator.prepare(model)
|
||||
|
||||
with accelerator.profile() as prof:
|
||||
model(inputs)
|
||||
|
||||
print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
The resulting table output (omitting some columns):
|
||||
|
||||
```
|
||||
--------------------------------- ------------ ------------ ------------
|
||||
Name CPU Mem Self CPU Mem # of Calls
|
||||
--------------------------------- ------------ ------------ ------------
|
||||
aten::empty 94.85 Mb 94.85 Mb 205
|
||||
aten::max_pool2d_with_indices 11.48 Mb 11.48 Mb 1
|
||||
aten::addmm 19.53 Kb 19.53 Kb 1
|
||||
aten::mean 10.00 Kb 10.00 Kb 1
|
||||
aten::empty_strided 492 b 492 b 5
|
||||
aten::cat 240 b 240 b 6
|
||||
aten::abs 480 b 240 b 4
|
||||
aten::masked_select 120 b 112 b 1
|
||||
aten::ne 61 b 53 b 3
|
||||
aten::eq 30 b 30 b 1
|
||||
--------------------------------- ------------ ------------ ------------
|
||||
Self CPU time total: 69.332ms
|
||||
```
|
||||
|
||||
|
||||
## Exporting chrome trace
|
||||
|
||||
You can examine the sequence of profiled operators and CUDA kernels in Chrome trace viewer (`chrome://tracing`):
|
||||
|
||||

|
||||
|
||||
<hfoptions id="exporting chrome trace">
|
||||
<hfoption id="PyTorch">
|
||||
|
||||
```python
|
||||
model = models.resnet18().cuda()
|
||||
inputs = torch.randn(5, 3, 224, 224).cuda()
|
||||
|
||||
with profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA]) as prof:
|
||||
model(inputs)
|
||||
|
||||
prof.export_chrome_trace("trace.json")
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Accelerate">
|
||||
|
||||
```python
|
||||
model = models.resnet18()
|
||||
inputs = torch.randn(5, 3, 224, 224).cuda()
|
||||
profile_kwargs = ProfileKwargs(
|
||||
activities=["cpu", "cuda"],
|
||||
output_trace_dir="trace"
|
||||
)
|
||||
|
||||
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
|
||||
model = accelerator.prepare(model)
|
||||
|
||||
with accelerator.profile() as prof:
|
||||
model(inputs)
|
||||
|
||||
# The trace will be saved to the specified directory
|
||||
```
|
||||
For other hardware accelerators, e.g. XPU, you can change `cuda` to `xpu` in the above example code.
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
## Using Profiler to Analyze Long-Running Jobs
|
||||
|
||||
Profiler offers an additional API to handle long-running jobs (such as training loops). Tracing all of the execution can be slow and result in very large trace files. To avoid this, use optional arguments:
|
||||
|
||||
- `schedule_option`: Scheduling options allow you to control when profiling is active. This is useful for long-running jobs to avoid collecting too much data. Available keys are `wait`, `warmup`, `active`, `repeat` and `skip_first`. The profiler will skip the first `skip_first` steps, then wait for `wait` steps, then do the warmup for the next `warmup` steps, then do the active recording for the next `active` steps and then repeat the cycle starting with `wait` steps. The optional number of cycles is specified with the `repeat` parameter, the zero value means that the cycles will continue until the profiling is finished.
|
||||
- `on_trace_ready`: specifies a function that takes a reference to the profiler as an input and is called by the profiler each time the new trace is ready.
|
||||
|
||||
To illustrate how the API works, consider the following example:
|
||||
|
||||
<hfoptions id="custom handler">
|
||||
<hfoption id="PyTorch">
|
||||
|
||||
```python
|
||||
from torch.profiler import schedule
|
||||
|
||||
my_schedule = schedule(
|
||||
skip_first=1,
|
||||
wait=5,
|
||||
warmup=1,
|
||||
active=3,
|
||||
repeat=2
|
||||
)
|
||||
|
||||
def trace_handler(p):
|
||||
output = p.key_averages().table(sort_by="self_cuda_time_total", row_limit=10)
|
||||
print(output)
|
||||
p.export_chrome_trace("/tmp/trace_" + str(p.step_num) + ".json")
|
||||
|
||||
with profile(
|
||||
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
|
||||
schedule=my_schedule,
|
||||
on_trace_ready=trace_handler
|
||||
) as p:
|
||||
for idx in range(8):
|
||||
model(inputs)
|
||||
p.step()
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Accelerate">
|
||||
|
||||
```python
|
||||
def trace_handler(p):
|
||||
output = p.key_averages().table(sort_by="self_cuda_time_total", row_limit=10)
|
||||
print(output)
|
||||
p.export_chrome_trace("/tmp/trace_" + str(p.step_num) + ".json")
|
||||
|
||||
profile_kwargs = ProfileKwargs(
|
||||
activities=["cpu", "cuda"],
|
||||
schedule_option={"wait": 5, "warmup": 1, "active": 3, "repeat": 2, "skip_first": 1},
|
||||
on_trace_ready=trace_handler
|
||||
)
|
||||
|
||||
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
|
||||
model = accelerator.prepare(model)
|
||||
|
||||
with accelerator.profile() as prof:
|
||||
for idx in range(8):
|
||||
model(inputs)
|
||||
prof.step()
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
## FLOPS
|
||||
|
||||
Use formula to estimate the FLOPs (floating point operations) of specific operators (matrix multiplication and 2D convolution).
|
||||
|
||||
To measure floating-point operations (FLOPS):
|
||||
|
||||
<hfoptions id="FLOPS">
|
||||
<hfoption id="PyTorch">
|
||||
|
||||
```python
|
||||
with profile(
|
||||
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
|
||||
with_flops=True
|
||||
) as prof:
|
||||
model(inputs)
|
||||
|
||||
print(prof.key_averages().table(sort_by="flops", row_limit=10))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
<hfoption id="Accelerate">
|
||||
|
||||
```python
|
||||
profile_kwargs = ProfileKwargs(
|
||||
with_flops=True
|
||||
)
|
||||
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
|
||||
|
||||
with accelerator.profile() as prof:
|
||||
model(inputs)
|
||||
|
||||
print(prof.key_averages().table(sort_by="flops", row_limit=10))
|
||||
```
|
||||
|
||||
</hfoption>
|
||||
</hfoptions>
|
||||
|
||||
The resulting table output (omitting some columns):
|
||||
|
||||
```
|
||||
------------------------------------------------------- ------------ ------------ ------------
|
||||
Name Self CPU Self CUDA Total FLOPs
|
||||
------------------------------------------------------- ------------ ------------ ------------
|
||||
aten::conv2d 197.000us 0.000us 18135613440.000
|
||||
aten::addmm 103.000us 17.000us 5120000.000
|
||||
aten::mul 29.000us 2.000us 30.000
|
||||
aten::convolution 409.000us 0.000us --
|
||||
aten::_convolution 253.000us 0.000us --
|
||||
aten::cudnn_convolution 5.465ms 2.970ms --
|
||||
cudaEventRecord 138.000us 0.000us --
|
||||
cudaStreamIsCapturing 43.000us 0.000us --
|
||||
cudaStreamGetPriority 40.000us 0.000us --
|
||||
cudaDeviceGetStreamPriorityRange 10.000us 0.000us --
|
||||
------------------------------------------------------- ------------ ------------ ------------
|
||||
Self CPU time total: 21.938ms
|
||||
Self CUDA time total: 4.165ms
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Conclusion and Further Information
|
||||
|
||||
PyTorch Profiler is a powerful tool for analyzing the performance of your models. By integrating it with Accelerate, you can easily profile your models and gain insights into their performance, helping you to optimize and improve them.
|
||||
|
||||
For more detailed information, refer to the [PyTorch Profiler documentation](https://pytorch.org/docs/stable/profiler.html).
|
||||
@ -13,13 +13,13 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Quantization
|
||||
# Model quantization
|
||||
|
||||
## `bitsandbytes` Integration
|
||||
|
||||
🤗 Accelerate brings `bitsandbytes` quantization to your model. You can now load any pytorch model in 8-bit or 4-bit with a few lines of code.
|
||||
Accelerate brings `bitsandbytes` quantization to your model. You can now load any pytorch model in 8-bit or 4-bit with a few lines of code.
|
||||
|
||||
If you want to use 🤗 Transformers models with `bitsandbytes`, you should follow this [documentation](https://huggingface.co/docs/transformers/main_classes/quantization).
|
||||
If you want to use Transformers models with `bitsandbytes`, you should follow this [documentation](https://huggingface.co/docs/transformers/main_classes/quantization).
|
||||
|
||||
To learn more about how the `bitsandbytes` quantization works, check out the blog posts on [8-bit quantization](https://huggingface.co/blog/hf-bitsandbytes-integration) and [4-bit quantization](https://huggingface.co/blog/4bit-transformers-bitsandbytes).
|
||||
|
||||
@ -30,6 +30,8 @@ You will need to install the following requirements:
|
||||
```bash
|
||||
pip install bitsandbytes
|
||||
```
|
||||
For non-cuda devices, you can refer to the bitsandbytes installation guide [here](https://huggingface.co/docs/bitsandbytes/main/en/installation#multi-backend).
|
||||
|
||||
- Install latest `accelerate` from source
|
||||
```bash
|
||||
pip install git+https://github.com/huggingface/accelerate.git
|
||||
@ -127,10 +129,10 @@ device_map = {
|
||||
|
||||
It is not possible to perform pure 8bit or 4bit training on these models. However, you can train these models by leveraging parameter efficient fine tuning methods (PEFT) and train for example adapters on top of them. Please have a look at [peft](https://github.com/huggingface/peft) library for more details.
|
||||
|
||||
Currently, you can't add adapters on top of any quantized model. However, with the official support of adapters with 🤗 Transformers models, you can fine-tune quantized models. If you want to finetune a 🤗 Transformers model , follow this [documentation](https://huggingface.co/docs/transformers/main_classes/quantization) instead. Check out this [demo](https://colab.research.google.com/drive/1VoYNfYDKcKRQRor98Zbf2-9VQTtGJ24k?usp=sharing) on how to fine-tune a 4-bit 🤗 Transformers model.
|
||||
Currently, you can't add adapters on top of any quantized model. However, with the official support of adapters with Transformers models, you can fine-tune quantized models. If you want to finetune a Transformers model , follow this [documentation](https://huggingface.co/docs/transformers/main_classes/quantization) instead. Check out this [demo](https://colab.research.google.com/drive/1VoYNfYDKcKRQRor98Zbf2-9VQTtGJ24k?usp=sharing) on how to fine-tune a 4-bit Transformers model.
|
||||
|
||||
Note that you don’t need to pass `device_map` when loading the model for training. It will automatically load your model on your GPU. Please note that `device_map=auto` should be used for inference only.
|
||||
|
||||
### Example demo - running GPT2 1.5b on a Google Colab
|
||||
|
||||
Check out the Google Colab [demo](https://colab.research.google.com/drive/1T1pOgewAWVpR9gKpaEWw4orOrzPFb3yM?usp=sharing) for running quantized models on a GTP2 model. The GPT2-1.5B model checkpoint is in FP32 which uses 6GB of memory. After quantization, it uses 1.6GB with 8-bit modules and 1.2GB with 4-bit modules.
|
||||
Check out the Google Colab [demo](https://colab.research.google.com/drive/1T1pOgewAWVpR9gKpaEWw4orOrzPFb3yM?usp=sharing) for running quantized models on a GPT2 model. The GPT2-1.5B model checkpoint is in FP32 which uses 6GB of memory. After quantization, it uses 1.6GB with 8-bit modules and 1.2GB with 4-bit modules.
|
||||
|
||||
@ -23,17 +23,16 @@ make it easier than ever to train Hugging Face Transformer models in [Amazon Sag
|
||||
### Setup & Installation
|
||||
|
||||
|
||||
Before you can run your 🤗 Accelerate scripts on Amazon SageMaker you need to sign up for an AWS account. If you do not
|
||||
Before you can run your Accelerate scripts on Amazon SageMaker you need to sign up for an AWS account. If you do not
|
||||
have an AWS account yet learn more [here](https://docs.aws.amazon.com/sagemaker/latest/dg/gs-set-up.html).
|
||||
|
||||
After you have your AWS Account you need to install the `sagemaker` sdk for 🤗 Accelerate with:
|
||||
After you have your AWS Account you need to install the `sagemaker` sdk for Accelerate with:
|
||||
|
||||
```bash
|
||||
pip install "accelerate[sagemaker]" --upgrade
|
||||
```
|
||||
|
||||
🤗 Accelerate currently uses the 🤗 DLCs, with `transformers`, `datasets` and `tokenizers` pre-installed. 🤗
|
||||
Accelerate is not in the DLC yet (will soon be added!) so to use it within Amazon SageMaker you need to create a
|
||||
Accelerate currently uses the DLCs, with `transformers`, `datasets` and `tokenizers` pre-installed. Accelerate is not in the DLC yet (will soon be added!) so to use it within Amazon SageMaker you need to create a
|
||||
`requirements.txt` in the same directory where your training script is located and add it as dependency:
|
||||
|
||||
```
|
||||
@ -43,25 +42,25 @@ accelerate
|
||||
You should also add any other dependencies you have to this `requirements.txt`.
|
||||
|
||||
|
||||
### Configure 🤗 Accelerate
|
||||
### Configure Accelerate
|
||||
|
||||
You can configure the launch configuration for Amazon SageMaker the same as you do for non SageMaker training jobs with
|
||||
the 🤗 Accelerate CLI:
|
||||
the Accelerate CLI:
|
||||
|
||||
```bash
|
||||
accelerate config
|
||||
# In which compute environment are you running? ([0] This machine, [1] AWS (Amazon SageMaker)): 1
|
||||
```
|
||||
|
||||
🤗 Accelerate will go through a questionnaire about your Amazon SageMaker setup and create a config file you can edit.
|
||||
Accelerate will go through a questionnaire about your Amazon SageMaker setup and create a config file you can edit.
|
||||
|
||||
<Tip>
|
||||
|
||||
🤗 Accelerate is not saving any of your credentials.
|
||||
Accelerate is not saving any of your credentials.
|
||||
|
||||
</Tip>
|
||||
|
||||
### Prepare a 🤗 Accelerate fine-tuning script
|
||||
### Prepare a Accelerate fine-tuning script
|
||||
|
||||
The training script is very similar to a training script you might run outside of SageMaker, but to save your model
|
||||
after training you need to specify either `/opt/ml/model` or use `os.environ["SM_MODEL_DIR"]` as your save
|
||||
@ -82,7 +81,7 @@ directory. After training, artifacts in this directory are uploaded to S3:
|
||||
|
||||
### Launch Training
|
||||
|
||||
You can launch your training with 🤗 Accelerate CLI with:
|
||||
You can launch your training with Accelerate CLI with:
|
||||
|
||||
```
|
||||
accelerate launch path_to_script.py --args_to_the_script
|
||||
@ -146,8 +145,8 @@ image_uri: null
|
||||
mixed_precision: fp16
|
||||
num_machines: 1
|
||||
profile: xxxxx
|
||||
py_version: py38
|
||||
pytorch_version: 1.10.2
|
||||
py_version: py10
|
||||
pytorch_version: 2.5.0
|
||||
region: us-east-1
|
||||
transformers_version: 4.17.0
|
||||
use_cpu: false
|
||||
@ -159,7 +158,7 @@ use_cpu: false
|
||||
|
||||
### Python packages and dependencies
|
||||
|
||||
🤗 Accelerate currently uses the 🤗 DLCs, with `transformers`, `datasets` and `tokenizers` pre-installed. If you
|
||||
Accelerate currently uses the DLCs, with `transformers`, `datasets` and `tokenizers` pre-installed. If you
|
||||
want to use different/other Python packages you can do this by adding them to the `requirements.txt`. These packages
|
||||
will be installed before your training script is started.
|
||||
|
||||
@ -198,7 +197,7 @@ additional_args:
|
||||
max_wait: 86400
|
||||
```
|
||||
|
||||
*Note: Spot Instances are subject to be terminated and training to be continued from a checkpoint. This is not handled in 🤗 Accelerate out of the box. Contact us if you would like this feature.*
|
||||
*Note: Spot Instances are subject to be terminated and training to be continued from a checkpoint. This is not handled in Accelerate out of the box. Contact us if you would like this feature.*
|
||||
|
||||
### Remote scripts: Use scripts located on Github
|
||||
|
||||
|
||||
@ -13,10 +13,10 @@ specific language governing permissions and limitations under the License.
|
||||
rendered properly in your Markdown viewer.
|
||||
-->
|
||||
|
||||
# Tracking
|
||||
# Experiment trackers
|
||||
|
||||
There are a large number of experiment tracking API's available, however getting them all to work with in a multi-processing environment can oftentimes be complex.
|
||||
🤗 Accelerate provides a general tracking API that can be used to log useful items during your script through [`Accelerator.log`]
|
||||
There are a large number of experiment tracking APIs available, however getting them all to work in a multi-processing environment can oftentimes be complex.
|
||||
Accelerate provides a general tracking API that can be used to log useful items during your script through [`Accelerator.log`]
|
||||
|
||||
## Integrated Trackers
|
||||
|
||||
@ -71,12 +71,12 @@ config = {
|
||||
|
||||
accelerator.init_trackers("example_project", config=config)
|
||||
|
||||
my_model, my_optimizer, my_training_dataloader = accelerate.prepare(my_model, my_optimizer, my_training_dataloader)
|
||||
my_model, my_optimizer, my_training_dataloader = accelerator.prepare(my_model, my_optimizer, my_training_dataloader)
|
||||
device = accelerator.device
|
||||
my_model.to(device)
|
||||
|
||||
for iteration in config["num_iterations"]:
|
||||
for step, batch in my_training_dataloader:
|
||||
for iteration in range(config["num_iterations"]):
|
||||
for step, batch in enumerate(my_training_dataloader):
|
||||
my_optimizer.zero_grad()
|
||||
inputs, targets = batch
|
||||
inputs = inputs.to(device)
|
||||
@ -184,7 +184,7 @@ wandb_tracker = accelerator.get_tracker("wandb")
|
||||
From there you can interact with `wandb`'s `run` object like normal:
|
||||
|
||||
```python
|
||||
wandb_run.log_artifact(some_artifact_to_log)
|
||||
wandb_tracker.log_artifact(some_artifact_to_log)
|
||||
```
|
||||
|
||||
<Tip>
|
||||
@ -208,10 +208,10 @@ if accelerator.is_main_process:
|
||||
If a library has an API that does not follow a strict `.log` with an overall dictionary such as Neptune.AI, logging can be done manually under an `if accelerator.is_main_process` statement:
|
||||
```diff
|
||||
from accelerate import Accelerator
|
||||
+ import neptune.new as neptune
|
||||
+ import neptune
|
||||
|
||||
accelerator = Accelerator()
|
||||
+ run = neptune.init(...)
|
||||
+ run = neptune.init_run(...)
|
||||
|
||||
my_model, my_optimizer, my_training_dataloader = accelerate.prepare(my_model, my_optimizer, my_training_dataloader)
|
||||
device = accelerator.device
|
||||
|
||||
@ -15,7 +15,7 @@ rendered properly in your Markdown viewer.
|
||||
|
||||
# Example Zoo
|
||||
|
||||
Below contains a non-exhaustive list of tutorials and scripts showcasing 🤗 Accelerate
|
||||
Below contains a non-exhaustive list of tutorials and scripts showcasing Accelerate.
|
||||
|
||||
## Official Accelerate Examples:
|
||||
|
||||
@ -68,7 +68,7 @@ These examples showcase every feature in Accelerate at once that was shown in "F
|
||||
|
||||
## Integration Examples
|
||||
|
||||
These are tutorials from libraries that integrate with 🤗 Accelerate:
|
||||
These are tutorials from libraries that integrate with Accelerate:
|
||||
|
||||
> Don't find your integration here? Make a PR to include it!
|
||||
|
||||
@ -85,7 +85,7 @@ These are tutorials from libraries that integrate with 🤗 Accelerate:
|
||||
|
||||
- [Fine-tuning DALLE2](https://github.com/lucidrains/DALLE2-pytorch#usage)
|
||||
|
||||
### 🤗 diffusers
|
||||
### Diffusers
|
||||
|
||||
- [Performing textual inversion with diffusers](https://github.com/huggingface/diffusers/tree/main/examples/textual_inversion)
|
||||
- [Training DreamBooth with diffusers](https://github.com/huggingface/diffusers/tree/main/examples/dreambooth)
|
||||
@ -134,7 +134,7 @@ These are tutorials from libraries that integrate with 🤗 Accelerate:
|
||||
|
||||
## In Science
|
||||
|
||||
Below contains a non-exhaustive list of papers utilizing 🤗 Accelerate.
|
||||
Below contains a non-exhaustive list of papers utilizing Accelerate.
|
||||
|
||||
> Don't find your paper here? Make a PR to include it!
|
||||
|
||||
|
||||
@ -208,23 +208,13 @@ To run it in each of these various modes, use the following commands:
|
||||
|
||||
- [huggan project](https://github.com/huggingface/community-events/tree/main/huggan)
|
||||
|
||||
|
||||
### Using AWS SageMaker integration
|
||||
- [Examples showcasing AWS SageMaker integration of 🤗 Accelerate.](https://github.com/pacman100/accelerate-aws-sagemaker)
|
||||
|
||||
|
||||
## Simple Multi-GPU Hardware Launcher
|
||||
|
||||
[multigpu_remote_launcher.py](./multigpu_remote_launcher.py) is a minimal script that demonstrates launching accelerate
|
||||
on multiple remote GPUs, and with automatic hardware environment and dependency setup for reproducibility. You can
|
||||
easily customize the training function used, training arguments, hyperparameters, and type of compute hardware, and then
|
||||
run the script to automatically launch multi GPU training on remote hardware.
|
||||
|
||||
This script uses [Runhouse](https://github.com/run-house/runhouse) to launch on self-hosted hardware (e.g. in your own
|
||||
cloud account or on-premise cluster) but there are other options for running remotely as well. Runhouse can be installed
|
||||
with `pip install runhouse`, and you can refer to
|
||||
[hardware setup](https://runhouse-docs.readthedocs-hosted.com/en/latest/api/python/cluster.html#hardware-setup)
|
||||
for hardware setup instructions, or this
|
||||
[Colab tutorial](https://colab.research.google.com/drive/1qVwYyLTCPYPSdz9ZX7BZl9Qm0A3j7RJe) for a more in-depth walkthrough.
|
||||
## Configuration zoo
|
||||
In [/config_yaml_templates](./config_yaml_templates/) we have a variety of *minimal* `config.yaml` templates and examples to help you learn
|
||||
how to create your own configuration files depending on the scenario.
|
||||
|
||||
## SLURM Scripts
|
||||
In [/slurm/submit_multigpu.sh](./slurm/submit_multigpu.sh) and [/slurm/submit_multinode.sh](./slurm/submit_multinode.sh) we present two scripts for running the examples on a machine with [SLURM](https://slurm.schedmd.com/documentation.html) workload manager.
|
||||
@ -233,6 +223,8 @@ In [/slurm/submit_multigpu.sh](./slurm/submit_multigpu.sh) the only parameter in
|
||||
|
||||
In [/slurm/submit_multinode.sh](./slurm/submit_multinode.sh) we must specify the number of nodes that will be part of the training (`--num_machines`), how many GPUs we will use in total (`--num_processes`), the [`backend`](https://pytorch.org/docs/stable/elastic/run.html#note-on-rendezvous-backend), `--main_process_ip` which will be the address the master node and the `--main_process_port`.
|
||||
|
||||
In [/slurm/submit_multicpu.sh](./slurm/submit_multicpu.sh) we must specify the number of nodes that will be part of the training (`--num_machines`), how many CPU processes we will use in total (`--num_processes`), the [`backend`](https://pytorch.org/docs/stable/elastic/run.html#note-on-rendezvous-backend), `--main_process_ip` which will be the address the master node and the `--main_process_port`. `mpirun_hostfile` specifies to run the job using MPIRun.
|
||||
|
||||
In both scripts, we run `activateEnviroment.sh` at the beginning. This script should contain the necessary instructions to initialize the environment for execution. Below, we show an example that loads the necessary libraries ([Environment modules](https://github.com/cea-hpc/modules)), activates the Python environment, and sets up various environment variables, most of them to run the scripts in offline mode in case we don't have internet connection from the cluster.
|
||||
|
||||
```bash
|
||||
@ -249,6 +241,20 @@ export PYTHONPATH=/home/nct01/nct01328/transformers-in-supercomputers:$PYTHONPAT
|
||||
export GPUS_PER_NODE=4
|
||||
```
|
||||
|
||||
## Simple Multi-GPU Hardware Launcher (using an external platform)
|
||||
|
||||
[multigpu_remote_launcher.py](./multigpu_remote_launcher.py) is a minimal script that demonstrates launching accelerate
|
||||
on multiple remote GPUs, and with automatic hardware environment and dependency setup for reproducibility. You can
|
||||
easily customize the training function used, training arguments, hyperparameters, and type of compute hardware, and then
|
||||
run the script to automatically launch multi GPU training on remote hardware.
|
||||
|
||||
This script uses [Runhouse](https://github.com/run-house/runhouse) to launch on self-hosted hardware (e.g. in your own
|
||||
cloud account or on-premise cluster) but there are other options for running remotely as well. Runhouse can be installed
|
||||
with `pip install runhouse`, and you can refer to
|
||||
[hardware setup](https://runhouse-docs.readthedocs-hosted.com/en/latest/api/python/cluster.html#hardware-setup)
|
||||
for hardware setup instructions, or this
|
||||
[Colab tutorial](https://colab.research.google.com/drive/1qVwYyLTCPYPSdz9ZX7BZl9Qm0A3j7RJe) for a more in-depth walkthrough.
|
||||
|
||||
## Finer Examples
|
||||
|
||||
While the first two scripts are extremely barebones when it comes to what you can do with accelerate, more advanced features are documented in two other locations.
|
||||
|
||||
@ -88,4 +88,34 @@ These arguments should be added at the end of any method for starting the python
|
||||
accelerate launch ./local_sgd.py --local_sgd_steps 4
|
||||
```
|
||||
|
||||
### DDP Communication Hook (`ddp_comm_hook.py`)
|
||||
|
||||
- Shows how to use DDP Communication Hooks to control and optimize gradient communication across workers in a DistributedDataParallel setup.
|
||||
- Arguments available:
|
||||
- `ddp_comm_hook`, the type of DDP communication hook to use. Choose between `no`, `fp16`, `bf16`, `power_sgd`, and `batched_power_sgd`.
|
||||
|
||||
These arguments should be added at the end of any method for starting the python script (such as `accelerate launch`, `python -m torch.distributed.run`), such as:
|
||||
|
||||
```bash
|
||||
accelerate launch ./ddp_comm_hook.py --mixed_precision fp16 --ddp_comm_hook power_sgd
|
||||
```
|
||||
|
||||
### Profiler (`profiler.py`)
|
||||
|
||||
- Shows how to use the profiling capabilities of `Accelerate` to profile PyTorch models during training.
|
||||
- Uses the `ProfileKwargs` handler to customize profiling options, including activities, scheduling, and additional profiling options.
|
||||
- Can generate and save profiling traces in JSON format for visualization in Chrome's tracing tool.
|
||||
|
||||
Arguments available:
|
||||
- `--record_shapes`: If passed, records shapes for profiling.
|
||||
- `--profile_memory`: If passed, profiles memory usage.
|
||||
- `--with_stack`: If passed, profiles stack traces.
|
||||
- `--with_flops`: If passed, profiles floating point operations (FLOPS).
|
||||
- `--output_trace_dir`: If specified, saves the profiling trace to the given dir in JSON format.
|
||||
- `--cpu`: If passed, trains on the CPU instead of GPU.
|
||||
|
||||
These arguments should be added at the end of any method for starting the Python script (such as `python`, `accelerate launch`, `python -m torchrun`), such as:
|
||||
|
||||
```bash
|
||||
accelerate launch ./profiler.py --record_shapes --profile_memory --with_flops --output_trace_dir "profiler"
|
||||
```
|
||||
|
||||
@ -217,6 +217,7 @@ def training_function(config, args):
|
||||
# And call it at the end with no arguments
|
||||
# Note: You could also refactor this outside of your training loop function
|
||||
inner_training_loop()
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -19,9 +19,10 @@ import torch
|
||||
from datasets import load_dataset
|
||||
from torch.optim import AdamW
|
||||
from torch.utils.data import DataLoader
|
||||
from transformers import AutoModelForSequenceClassification, AutoTokenizer, get_linear_schedule_with_warmup, set_seed
|
||||
from transformers import AutoModelForSequenceClassification, AutoTokenizer, get_linear_schedule_with_warmup
|
||||
|
||||
from accelerate import Accelerator, DistributedType
|
||||
from accelerate import Accelerator, DataLoaderConfiguration, DistributedType
|
||||
from accelerate.utils import set_seed
|
||||
|
||||
|
||||
########################################################################
|
||||
@ -125,7 +126,8 @@ def training_function(config, args):
|
||||
if os.environ.get("TESTING_MOCKED_DATALOADERS", None) == "1":
|
||||
config["num_epochs"] = 2
|
||||
# Initialize accelerator
|
||||
accelerator = Accelerator(cpu=args.cpu, mixed_precision=args.mixed_precision)
|
||||
dataloader_config = DataLoaderConfiguration(use_stateful_dataloader=args.use_stateful_dataloader)
|
||||
accelerator = Accelerator(cpu=args.cpu, mixed_precision=args.mixed_precision, dataloader_config=dataloader_config)
|
||||
# Sample hyper-parameters for learning rate, batch size, seed and a few other HPs
|
||||
lr = config["lr"]
|
||||
num_epochs = int(config["num_epochs"])
|
||||
@ -217,8 +219,11 @@ def training_function(config, args):
|
||||
model.train()
|
||||
# New Code #
|
||||
if args.resume_from_checkpoint and epoch == starting_epoch and resume_step is not None:
|
||||
# We need to skip steps until we reach the resumed step
|
||||
active_dataloader = accelerator.skip_first_batches(train_dataloader, resume_step)
|
||||
# We need to skip steps until we reach the resumed step only if we are not using a stateful dataloader
|
||||
if not args.use_stateful_dataloader:
|
||||
active_dataloader = accelerator.skip_first_batches(train_dataloader, resume_step)
|
||||
else:
|
||||
active_dataloader = train_dataloader
|
||||
overall_step += resume_step
|
||||
else:
|
||||
# After the first iteration though, we need to go back to the original dataloader
|
||||
@ -248,7 +253,6 @@ def training_function(config, args):
|
||||
if args.output_dir is not None:
|
||||
output_dir = os.path.join(args.output_dir, output_dir)
|
||||
accelerator.save_state(output_dir)
|
||||
|
||||
model.eval()
|
||||
for step, batch in enumerate(eval_dataloader):
|
||||
# We could avoid this line since we set the accelerator with `device_placement=True` (the default).
|
||||
@ -261,7 +265,6 @@ def training_function(config, args):
|
||||
predictions=predictions,
|
||||
references=references,
|
||||
)
|
||||
|
||||
eval_metric = metric.compute()
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:", eval_metric)
|
||||
@ -276,6 +279,7 @@ def training_function(config, args):
|
||||
if args.output_dir is not None:
|
||||
output_dir = os.path.join(args.output_dir, output_dir)
|
||||
accelerator.save_state(output_dir)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
@ -308,6 +312,11 @@ def main():
|
||||
default=None,
|
||||
help="If the training should continue from a checkpoint folder.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--use_stateful_dataloader",
|
||||
action="store_true",
|
||||
help="If the dataloader should be a resumable stateful dataloader.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
config = {"lr": 2e-5, "num_epochs": 3, "seed": 42, "batch_size": 16}
|
||||
training_function(config, args)
|
||||
|
||||
@ -255,6 +255,7 @@ def training_function(config, args):
|
||||
preds = torch.stack(test_predictions, dim=0).sum(dim=0).div(int(args.num_folds)).argmax(dim=-1)
|
||||
test_metric = metric.compute(predictions=preds, references=test_references)
|
||||
accelerator.print("Average test metrics from all folds:", test_metric)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
232
examples/by_feature/ddp_comm_hook.py
Normal file
232
examples/by_feature/ddp_comm_hook.py
Normal file
@ -0,0 +1,232 @@
|
||||
# Copyright 2021 The HuggingFace Inc. 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.
|
||||
import argparse
|
||||
import os
|
||||
|
||||
import evaluate
|
||||
import torch
|
||||
from datasets import load_dataset
|
||||
from torch.optim import AdamW
|
||||
from torch.utils.data import DataLoader
|
||||
from transformers import AutoModelForSequenceClassification, AutoTokenizer, get_linear_schedule_with_warmup, set_seed
|
||||
|
||||
from accelerate import Accelerator, DistributedType
|
||||
from accelerate.utils import DDPCommunicationHookType, DistributedDataParallelKwargs
|
||||
|
||||
|
||||
########################################################################
|
||||
# This is a fully working simple example to use Accelerate
|
||||
# and perform ddp communication hook
|
||||
#
|
||||
# This example trains a Bert base model on GLUE MRPC
|
||||
# in any of the following settings (with the same script):
|
||||
# - single CPU or single GPU
|
||||
# - multi GPUS (using PyTorch distributed mode)
|
||||
# - (multi) TPUs
|
||||
# - fp16 (mixed-precision) or fp32 (normal precision)
|
||||
#
|
||||
# To run it in each of these various modes, follow the instructions
|
||||
# in the readme for examples:
|
||||
# https://github.com/huggingface/accelerate/tree/main/examples
|
||||
#
|
||||
########################################################################
|
||||
|
||||
|
||||
MAX_GPU_BATCH_SIZE = 16
|
||||
EVAL_BATCH_SIZE = 32
|
||||
|
||||
|
||||
def get_dataloaders(accelerator: Accelerator, batch_size: int = 16):
|
||||
"""
|
||||
Creates a set of `DataLoader`s for the `glue` dataset,
|
||||
using "bert-base-cased" as the tokenizer.
|
||||
|
||||
Args:
|
||||
accelerator (`Accelerator`):
|
||||
An `Accelerator` object
|
||||
batch_size (`int`, *optional*):
|
||||
The batch size for the train and validation DataLoaders.
|
||||
"""
|
||||
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
|
||||
datasets = load_dataset("glue", "mrpc")
|
||||
|
||||
def tokenize_function(examples):
|
||||
# max_length=None => use the model max length (it's actually the default)
|
||||
outputs = tokenizer(examples["sentence1"], examples["sentence2"], truncation=True, max_length=None)
|
||||
return outputs
|
||||
|
||||
# Apply the method we just defined to all the examples in all the splits of the dataset
|
||||
# starting with the main process first:
|
||||
with accelerator.main_process_first():
|
||||
tokenized_datasets = datasets.map(
|
||||
tokenize_function,
|
||||
batched=True,
|
||||
remove_columns=["idx", "sentence1", "sentence2"],
|
||||
)
|
||||
|
||||
# We also rename the 'label' column to 'labels' which is the expected name for labels by the models of the
|
||||
# transformers library
|
||||
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
|
||||
|
||||
def collate_fn(examples):
|
||||
# On TPU it's best to pad everything to the same length or training will be very slow.
|
||||
max_length = 128 if accelerator.distributed_type == DistributedType.XLA else None
|
||||
# When using mixed precision we want round multiples of 8/16
|
||||
if accelerator.mixed_precision == "fp8":
|
||||
pad_to_multiple_of = 16
|
||||
elif accelerator.mixed_precision != "no":
|
||||
pad_to_multiple_of = 8
|
||||
else:
|
||||
pad_to_multiple_of = None
|
||||
|
||||
return tokenizer.pad(
|
||||
examples,
|
||||
padding="longest",
|
||||
max_length=max_length,
|
||||
pad_to_multiple_of=pad_to_multiple_of,
|
||||
return_tensors="pt",
|
||||
)
|
||||
|
||||
# Instantiate dataloaders.
|
||||
train_dataloader = DataLoader(
|
||||
tokenized_datasets["train"], shuffle=True, collate_fn=collate_fn, batch_size=batch_size
|
||||
)
|
||||
eval_dataloader = DataLoader(
|
||||
tokenized_datasets["validation"], shuffle=False, collate_fn=collate_fn, batch_size=EVAL_BATCH_SIZE
|
||||
)
|
||||
|
||||
return train_dataloader, eval_dataloader
|
||||
|
||||
|
||||
# For testing only
|
||||
if os.environ.get("TESTING_MOCKED_DATALOADERS", None) == "1":
|
||||
from accelerate.test_utils.training import mocked_dataloaders
|
||||
|
||||
get_dataloaders = mocked_dataloaders # noqa: F811
|
||||
|
||||
|
||||
def training_function(config, args):
|
||||
# For testing only
|
||||
if os.environ.get("TESTING_MOCKED_DATALOADERS", None) == "1":
|
||||
config["num_epochs"] = 2
|
||||
# New Code #
|
||||
ddp_comm_hook_type = DDPCommunicationHookType(args.ddp_comm_hook)
|
||||
ddp_comm_wrapper = DDPCommunicationHookType(args.ddp_comm_wrapper)
|
||||
ddp_kwargs = DistributedDataParallelKwargs(comm_hook=ddp_comm_hook_type, comm_wrapper=ddp_comm_wrapper)
|
||||
# Initialize accelerator
|
||||
accelerator = Accelerator(cpu=args.cpu, mixed_precision=args.mixed_precision, kwargs_handlers=[ddp_kwargs])
|
||||
# Sample hyper-parameters for learning rate, batch size, seed and a few other HPs
|
||||
lr = config["lr"]
|
||||
num_epochs = int(config["num_epochs"])
|
||||
seed = int(config["seed"])
|
||||
batch_size = int(config["batch_size"])
|
||||
|
||||
metric = evaluate.load("glue", "mrpc")
|
||||
|
||||
set_seed(seed)
|
||||
train_dataloader, eval_dataloader = get_dataloaders(accelerator, batch_size)
|
||||
# Instantiate the model (we build the model here so that the seed also control new weights initialization)
|
||||
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", return_dict=True)
|
||||
|
||||
# We could avoid this line since the accelerator is set with `device_placement=True` (default value).
|
||||
# Note that if you are placing tensors on devices manually, this line absolutely needs to be before the optimizer
|
||||
# creation otherwise training will not work on TPU (`accelerate` will kindly throw an error to make us aware of that).
|
||||
model = model.to(accelerator.device)
|
||||
|
||||
# Instantiate optimizer
|
||||
optimizer = AdamW(params=model.parameters(), lr=lr)
|
||||
|
||||
# Instantiate scheduler
|
||||
lr_scheduler = get_linear_schedule_with_warmup(
|
||||
optimizer=optimizer,
|
||||
num_warmup_steps=100,
|
||||
num_training_steps=(len(train_dataloader) * num_epochs),
|
||||
)
|
||||
|
||||
# Prepare everything
|
||||
# There is no specific order to remember, we just need to unpack the objects in the same order we gave them to the
|
||||
# prepare method.
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
|
||||
)
|
||||
|
||||
# Now we train the model
|
||||
for epoch in range(num_epochs):
|
||||
model.train()
|
||||
for step, batch in enumerate(train_dataloader):
|
||||
# We could avoid this line since we set the accelerator with `device_placement=True`.
|
||||
batch.to(accelerator.device)
|
||||
# We use the new `accumulate` context manager to perform gradient accumulation
|
||||
with accelerator.accumulate(model):
|
||||
output = model(**batch)
|
||||
loss = output.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
lr_scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
|
||||
model.eval()
|
||||
for step, batch in enumerate(eval_dataloader):
|
||||
# We could avoid this line since we set the accelerator with `device_placement=True`.
|
||||
batch.to(accelerator.device)
|
||||
with torch.no_grad():
|
||||
outputs = model(**batch)
|
||||
predictions = outputs.logits.argmax(dim=-1)
|
||||
predictions, references = accelerator.gather_for_metrics((predictions, batch["labels"]))
|
||||
metric.add_batch(
|
||||
predictions=predictions,
|
||||
references=references,
|
||||
)
|
||||
|
||||
eval_metric = metric.compute()
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:", eval_metric)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Simple example of training script.")
|
||||
parser.add_argument(
|
||||
"--mixed_precision",
|
||||
type=str,
|
||||
default=None,
|
||||
choices=["no", "fp16", "bf16", "fp8"],
|
||||
help="Whether to use mixed precision. Choose"
|
||||
"between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >= 1.10."
|
||||
"and an Nvidia Ampere GPU.",
|
||||
)
|
||||
# New Code #
|
||||
parser.add_argument(
|
||||
"--ddp_comm_hook",
|
||||
type=str,
|
||||
default="no",
|
||||
choices=["no", "fp16", "bf16", "power_sgd", "batched_power_sgd"],
|
||||
help="DDP Communication hook to use. Choose between `no`, `fp16`, `bf16`, `power_sgd`, and `batched_power_sgd`.",
|
||||
)
|
||||
# New Code #
|
||||
parser.add_argument(
|
||||
"--ddp_comm_wrapper",
|
||||
type=str,
|
||||
default="no",
|
||||
choices=["no", "fp16", "bf16"],
|
||||
help="DDP Communication wrapper to use. Choose between `no`, `fp16`, and `bf16`.",
|
||||
)
|
||||
parser.add_argument("--cpu", action="store_true", help="If passed, will train on the CPU.")
|
||||
args = parser.parse_args()
|
||||
config = {"lr": 2e-5, "num_epochs": 3, "seed": 42, "batch_size": 16}
|
||||
training_function(config, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -716,6 +716,7 @@ def main():
|
||||
|
||||
with open(os.path.join(args.output_dir, "all_results.json"), "w") as f:
|
||||
json.dump({"perplexity": perplexity, "eval_loss": eval_loss.item()}, f)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -222,6 +222,7 @@ def training_function(config, args):
|
||||
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:", eval_metric)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -399,8 +399,7 @@ def training_function(config, args):
|
||||
step=epoch,
|
||||
)
|
||||
|
||||
if args.with_tracking:
|
||||
accelerator.end_training()
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -197,6 +197,7 @@ def training_function(config, args):
|
||||
eval_metric = metric.compute()
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:", eval_metric)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -0,0 +1,341 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
import argparse
|
||||
import contextlib
|
||||
import math
|
||||
import os
|
||||
|
||||
import torch
|
||||
from datasets import load_dataset
|
||||
from torch.optim import AdamW
|
||||
from torch.utils.data import DataLoader
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer, get_constant_schedule, set_seed
|
||||
|
||||
from accelerate import Accelerator, DistributedType
|
||||
|
||||
|
||||
########################################################################
|
||||
# This is a fully working simple example to use Accelerate
|
||||
# and perform gradient accumulation on samples of variable size
|
||||
#
|
||||
# This example trains a SmolLM base model on WikiText-2 v1
|
||||
# in any of the following settings (with the same script):
|
||||
# - single CPU or single GPU
|
||||
# - multi GPUS (using PyTorch distributed mode)
|
||||
# - (multi) TPUs
|
||||
# - fp16 (mixed-precision) or fp32 (normal precision)
|
||||
#
|
||||
# To run it in each of these various modes, follow the instructions
|
||||
# in the readme for examples:
|
||||
# https://github.com/huggingface/accelerate/tree/main/examples
|
||||
#
|
||||
########################################################################
|
||||
|
||||
|
||||
EVAL_BATCH_SIZE = 32
|
||||
|
||||
|
||||
def get_dataloaders(accelerator: Accelerator, batch_size: int = 16, max_training_samples=500):
|
||||
"""
|
||||
Creates a set of `DataLoader`s for the `Salesforce/wikitext` dataset,
|
||||
using "HuggingFaceTB/SmolLM-360M" as the tokenizer.
|
||||
|
||||
Args:
|
||||
accelerator (`Accelerator`):
|
||||
An `Accelerator` object
|
||||
batch_size (`int`, *optional*):
|
||||
The batch size for the train and validation DataLoaders.
|
||||
"""
|
||||
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM-360M")
|
||||
tokenizer.pad_token = tokenizer.eos_token
|
||||
with accelerator.local_main_process_first():
|
||||
datasets = load_dataset("Salesforce/wikitext", "wikitext-2-v1")
|
||||
datasets["train"] = datasets["train"].select(range(max_training_samples))
|
||||
|
||||
def tokenize_function(examples):
|
||||
# max_length=None => use the model max length (it's actually the default)
|
||||
outputs = tokenizer(examples["text"], truncation=True, max_length=None, return_attention_mask=False)
|
||||
return outputs
|
||||
|
||||
# Filter out empty texts
|
||||
with accelerator.main_process_first():
|
||||
datasets = datasets.filter(
|
||||
lambda x: len(x) > 0,
|
||||
input_columns="text",
|
||||
)
|
||||
|
||||
# Apply the method we just defined to all the examples in all the splits of the dataset
|
||||
# starting with the main process first:
|
||||
with accelerator.main_process_first():
|
||||
tokenized_datasets = datasets.map(
|
||||
tokenize_function,
|
||||
batched=True,
|
||||
remove_columns=["text"],
|
||||
)
|
||||
|
||||
# Filter out empty samples
|
||||
with accelerator.main_process_first():
|
||||
tokenized_datasets = tokenized_datasets.filter(
|
||||
lambda x: len(x) > 0,
|
||||
input_columns="input_ids",
|
||||
)
|
||||
|
||||
def collate_fn(examples):
|
||||
# On TPU it's best to pad everything to the same length or training will be very slow.
|
||||
max_length = (
|
||||
128
|
||||
if accelerator.distributed_type == DistributedType.XLA
|
||||
else max([len(e["input_ids"]) for e in examples])
|
||||
)
|
||||
# When using mixed precision we want round multiples of 8/16
|
||||
if accelerator.mixed_precision == "fp8":
|
||||
pad_to_multiple_of = 16
|
||||
elif accelerator.mixed_precision != "no":
|
||||
pad_to_multiple_of = 8
|
||||
else:
|
||||
pad_to_multiple_of = None
|
||||
|
||||
batch = tokenizer.pad(
|
||||
examples,
|
||||
padding="max_length",
|
||||
max_length=max_length + 1,
|
||||
pad_to_multiple_of=pad_to_multiple_of,
|
||||
return_tensors="pt",
|
||||
)
|
||||
|
||||
batch["labels"] = batch["input_ids"][:, 1:]
|
||||
batch["input_ids"] = batch["input_ids"][:, :-1]
|
||||
|
||||
batch["labels"] = torch.where(batch["labels"] == tokenizer.pad_token_id, -100, batch["labels"])
|
||||
|
||||
return batch
|
||||
|
||||
# Instantiate dataloaders.
|
||||
train_dataloader = DataLoader(
|
||||
tokenized_datasets["train"], shuffle=False, collate_fn=collate_fn, batch_size=batch_size
|
||||
)
|
||||
eval_dataloader = DataLoader(
|
||||
tokenized_datasets["validation"], shuffle=False, collate_fn=collate_fn, batch_size=EVAL_BATCH_SIZE
|
||||
)
|
||||
|
||||
return train_dataloader, eval_dataloader
|
||||
|
||||
|
||||
# For testing only
|
||||
if os.environ.get("TESTING_MOCKED_DATALOADERS", None) == "1":
|
||||
from accelerate.test_utils.training import mocked_dataloaders_for_autoregressive_models
|
||||
|
||||
get_dataloaders = mocked_dataloaders_for_autoregressive_models # noqa: F811
|
||||
|
||||
|
||||
def training_function(config, args):
|
||||
# For testing only
|
||||
if os.environ.get("TESTING_MOCKED_DATALOADERS", None) == "1":
|
||||
config["num_epochs"] = 2
|
||||
|
||||
gradient_accumulation_steps = int(args.gradient_accumulation_steps)
|
||||
# Initialize accelerator
|
||||
if args.with_wandb_tracking:
|
||||
accelerator = Accelerator(
|
||||
cpu=args.cpu,
|
||||
mixed_precision=args.mixed_precision,
|
||||
gradient_accumulation_steps=gradient_accumulation_steps,
|
||||
log_with="wandb",
|
||||
)
|
||||
else:
|
||||
accelerator = Accelerator(
|
||||
cpu=args.cpu, mixed_precision=args.mixed_precision, gradient_accumulation_steps=gradient_accumulation_steps
|
||||
)
|
||||
if accelerator.distributed_type == DistributedType.XLA and gradient_accumulation_steps > 1:
|
||||
raise NotImplementedError(
|
||||
"Gradient accumulation on TPUs is currently not supported. Pass `gradient_accumulation_steps=1`"
|
||||
)
|
||||
# Sample hyper-parameters for learning rate, batch size, seed and a few other HPs
|
||||
lr = config["lr"]
|
||||
num_epochs = int(config["num_epochs"])
|
||||
seed = int(config["seed"])
|
||||
batch_size = int(config["batch_size"])
|
||||
max_grad_norm = config["max_grad_norm"]
|
||||
|
||||
# We need to initialize the trackers we use, and also store our configuration
|
||||
if args.with_wandb_tracking:
|
||||
run = os.path.split(__file__)[-1].split(".")[0]
|
||||
run_name = f"{accelerator.num_processes}GPU-grad{gradient_accumulation_steps}-bs{batch_size}"
|
||||
accelerator.init_trackers(
|
||||
run,
|
||||
config,
|
||||
init_kwargs={"wandb": {"name": run_name}},
|
||||
)
|
||||
|
||||
set_seed(seed)
|
||||
train_dataloader, eval_dataloader = get_dataloaders(accelerator, batch_size)
|
||||
# Instantiate the model (we build the model here so that the seed also control new weights initialization)
|
||||
model = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM-360M")
|
||||
|
||||
# We could avoid this line since the accelerator is set with `device_placement=True` (default value).
|
||||
# Note that if you are placing tensors on devices manually, this line absolutely needs to be before the optimizer
|
||||
# creation otherwise training will not work on TPU (`accelerate` will kindly throw an error to make us aware of that).
|
||||
model = model.to(accelerator.device)
|
||||
|
||||
# Instantiate optimizer
|
||||
optimizer = AdamW(params=model.parameters(), lr=lr)
|
||||
|
||||
# Instantiate scheduler
|
||||
lr_scheduler = get_constant_schedule(
|
||||
optimizer=optimizer,
|
||||
)
|
||||
|
||||
# Prepare everything
|
||||
# There is no specific order to remember, we just need to unpack the objects in the same order we gave them to the
|
||||
# prepare method.
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
|
||||
)
|
||||
|
||||
num_samples_in_epoch = len(train_dataloader)
|
||||
remainder = num_samples_in_epoch % gradient_accumulation_steps
|
||||
remainder = remainder if remainder != 0 else gradient_accumulation_steps
|
||||
total_gradient_updates = math.ceil(num_samples_in_epoch / gradient_accumulation_steps)
|
||||
|
||||
total_batched_samples = 0
|
||||
# Now we train the model
|
||||
for epoch in range(num_epochs):
|
||||
model.train()
|
||||
training_iterator = iter(train_dataloader)
|
||||
for update_step in range(total_gradient_updates):
|
||||
# In order to correctly the total number of non-padded tokens on which we'll compute the cross-entropy loss
|
||||
# we need to pre-load the full local batch - i.e the next per_device_batch_size * accumulation_steps samples
|
||||
batch_samples = []
|
||||
num_batches_in_step = (
|
||||
gradient_accumulation_steps if update_step != (total_gradient_updates - 1) else remainder
|
||||
)
|
||||
for _ in range(num_batches_in_step):
|
||||
batch_samples += [next(training_iterator)]
|
||||
# get local num items in batch
|
||||
local_num_items_in_batch = sum([(batch["labels"].ne(-100)).sum() for batch in batch_samples])
|
||||
|
||||
# to compute it correctly in a multi-device DDP training, we need to gather the total number of items in the full batch.
|
||||
num_items_in_batch = accelerator.gather(local_num_items_in_batch).sum().item()
|
||||
losses = []
|
||||
for i, batch in enumerate(batch_samples):
|
||||
# if we perform gradient accumulation in a multi-devices set-up, we want to avoid unecessary communications when accumulating
|
||||
# cf: https://muellerzr.github.io/blog/gradient_accumulation.html
|
||||
ctx = (
|
||||
model.no_sync
|
||||
if (i < len(batch_samples) - 1 and accelerator.num_processes > 1)
|
||||
else contextlib.nullcontext
|
||||
)
|
||||
with ctx():
|
||||
total_batched_samples += 1
|
||||
|
||||
outputs = model(**batch, use_cache=False, num_items_in_batch=num_items_in_batch)
|
||||
loss = outputs.loss
|
||||
|
||||
# We multiply by num_processes because the DDP calculates the average gradient across all devices whereas dividing by num_items_in_batch already takes into account all devices
|
||||
# Same reason for gradient_accumulation_steps, but this times it's Accelerate that calculate the average gradient across the accumulated steps
|
||||
# Because the loss is already divided by `num_items_in_batch` in the `transformers` code, we don't need to do it again
|
||||
loss = loss * gradient_accumulation_steps * accelerator.num_processes
|
||||
accelerator.backward(loss)
|
||||
losses.append(loss.detach())
|
||||
|
||||
# Sync gradients and perform optimization steps once every gradient_accumulation_steps
|
||||
grad_norm = accelerator.clip_grad_norm_(model.parameters(), max_grad_norm)
|
||||
optimizer.step()
|
||||
lr_scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
|
||||
losses = accelerator.gather(sum(losses)).sum().item() / (
|
||||
accelerator.num_processes * gradient_accumulation_steps
|
||||
)
|
||||
|
||||
grad_norm = grad_norm.detach().item() if isinstance(grad_norm, torch.Tensor) else grad_norm
|
||||
accelerator.print(
|
||||
f"epoch {epoch} - update step {update_step}:: grad norm: {grad_norm} ::train loss: {losses}"
|
||||
)
|
||||
if args.with_wandb_tracking:
|
||||
accelerator.log(
|
||||
{
|
||||
"train/grad_norm": grad_norm,
|
||||
"train/epoch": epoch,
|
||||
"train/loss": losses,
|
||||
},
|
||||
step=update_step + total_gradient_updates * epoch,
|
||||
)
|
||||
model.eval()
|
||||
losses = []
|
||||
for step, batch in enumerate(eval_dataloader):
|
||||
with torch.no_grad():
|
||||
outputs = model(**batch, use_cache=False)
|
||||
eval_loss = outputs.loss
|
||||
losses.append(accelerator.gather_for_metrics(loss.repeat(EVAL_BATCH_SIZE)))
|
||||
|
||||
losses = torch.cat(losses)
|
||||
try:
|
||||
eval_loss = torch.mean(losses)
|
||||
perplexity = math.exp(eval_loss)
|
||||
except OverflowError:
|
||||
perplexity = float("inf")
|
||||
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:: eval perplexity: {perplexity} eval_loss: {eval_loss}")
|
||||
if args.with_wandb_tracking:
|
||||
accelerator.log(
|
||||
{
|
||||
"eval/perplexity": perplexity,
|
||||
"eval/loss": eval_loss,
|
||||
"eval/epoch": epoch,
|
||||
},
|
||||
step=update_step + total_gradient_updates * epoch,
|
||||
)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Simple example of training script.")
|
||||
parser.add_argument(
|
||||
"--mixed_precision",
|
||||
type=str,
|
||||
default=None,
|
||||
choices=["no", "fp16", "bf16", "fp8"],
|
||||
help="Whether to use mixed precision. Choose"
|
||||
"between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >= 1.10."
|
||||
"and an Nvidia Ampere GPU.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--gradient_accumulation_steps",
|
||||
type=int,
|
||||
default=1,
|
||||
help="The number of minibatches to be ran before gradients are accumulated.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--per_device_batch_size",
|
||||
type=int,
|
||||
default=2,
|
||||
help="The size of each minibatch",
|
||||
)
|
||||
|
||||
parser.add_argument("--cpu", action="store_true", help="If passed, will train on the CPU.")
|
||||
parser.add_argument(
|
||||
"--with_wandb_tracking",
|
||||
action="store_true",
|
||||
help="Whether to load in wandb from the environment and use them for logging.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
config = {"lr": 2e-5, "num_epochs": 3, "seed": 42, "batch_size": args.per_device_batch_size, "max_grad_norm": 1.0}
|
||||
training_function(config, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -202,6 +202,7 @@ def training_function(config, args):
|
||||
eval_metric = metric.compute()
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:", eval_metric)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -252,7 +252,7 @@ def main():
|
||||
|
||||
if args.with_tracking:
|
||||
accelerator_log_kwargs["log_with"] = args.report_to
|
||||
accelerator_log_kwargs["logging_dir"] = args.output_dir
|
||||
accelerator_log_kwargs["project_dir"] = args.output_dir
|
||||
|
||||
accelerator = Accelerator(gradient_accumulation_steps=args.gradient_accumulation_steps, **accelerator_log_kwargs)
|
||||
|
||||
@ -703,6 +703,7 @@ def main():
|
||||
|
||||
with open(os.path.join(args.output_dir, "all_results.json"), "w") as f:
|
||||
json.dump({"perplexity": perplexity}, f)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -210,6 +210,7 @@ def training_function(config, args):
|
||||
# And call it at the end with no arguments
|
||||
# Note: You could also refactor this outside of your training loop function
|
||||
inner_training_loop()
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -214,6 +214,7 @@ def training_function(config, args):
|
||||
eval_metric = metric.compute()
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:", eval_metric)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
255
examples/by_feature/profiler.py
Normal file
255
examples/by_feature/profiler.py
Normal file
@ -0,0 +1,255 @@
|
||||
# Copyright 2024 The HuggingFace Inc. 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.
|
||||
import argparse
|
||||
import os
|
||||
|
||||
import evaluate
|
||||
import torch
|
||||
from datasets import load_dataset
|
||||
from torch.optim import AdamW
|
||||
from torch.utils.data import DataLoader
|
||||
from transformers import AutoModelForSequenceClassification, AutoTokenizer, get_linear_schedule_with_warmup, set_seed
|
||||
|
||||
from accelerate import Accelerator, DistributedType
|
||||
from accelerate.utils import ProfileKwargs
|
||||
|
||||
|
||||
########################################################################
|
||||
# This is a fully working simple example to use Accelerate
|
||||
# and perform profiling
|
||||
#
|
||||
# This example trains a Bert base model on GLUE MRPC
|
||||
# in any of the following settings (with the same script):
|
||||
# - single CPU or single GPU
|
||||
# - multi GPUS (using PyTorch distributed mode)
|
||||
# - (multi) TPUs
|
||||
# - fp16 (mixed-precision) or fp32 (normal precision)
|
||||
#
|
||||
# To run it in each of these various modes, follow the instructions
|
||||
# in the readme for examples:
|
||||
# https://github.com/huggingface/accelerate/tree/main/examples
|
||||
#
|
||||
########################################################################
|
||||
|
||||
|
||||
MAX_GPU_BATCH_SIZE = 16
|
||||
EVAL_BATCH_SIZE = 32
|
||||
|
||||
|
||||
def get_dataloaders(accelerator: Accelerator, batch_size: int = 16):
|
||||
"""
|
||||
Creates a set of `DataLoader`s for the `glue` dataset,
|
||||
using "bert-base-cased" as the tokenizer.
|
||||
|
||||
Args:
|
||||
accelerator (`Accelerator`):
|
||||
An `Accelerator` object
|
||||
batch_size (`int`, *optional*):
|
||||
The batch size for the train and validation DataLoaders.
|
||||
"""
|
||||
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
|
||||
datasets = load_dataset("glue", "mrpc")
|
||||
|
||||
def tokenize_function(examples):
|
||||
# max_length=None => use the model max length (it's actually the default)
|
||||
outputs = tokenizer(examples["sentence1"], examples["sentence2"], truncation=True, max_length=None)
|
||||
return outputs
|
||||
|
||||
# Apply the method we just defined to all the examples in all the splits of the dataset
|
||||
# starting with the main process first:
|
||||
with accelerator.main_process_first():
|
||||
tokenized_datasets = datasets.map(
|
||||
tokenize_function,
|
||||
batched=True,
|
||||
remove_columns=["idx", "sentence1", "sentence2"],
|
||||
)
|
||||
|
||||
# We also rename the 'label' column to 'labels' which is the expected name for labels by the models of the
|
||||
# transformers library
|
||||
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
|
||||
|
||||
def collate_fn(examples):
|
||||
# On TPU it's best to pad everything to the same length or training will be very slow.
|
||||
max_length = 128 if accelerator.distributed_type == DistributedType.XLA else None
|
||||
# When using mixed precision we want round multiples of 8/16
|
||||
if accelerator.mixed_precision == "fp8":
|
||||
pad_to_multiple_of = 16
|
||||
elif accelerator.mixed_precision != "no":
|
||||
pad_to_multiple_of = 8
|
||||
else:
|
||||
pad_to_multiple_of = None
|
||||
|
||||
return tokenizer.pad(
|
||||
examples,
|
||||
padding="longest",
|
||||
max_length=max_length,
|
||||
pad_to_multiple_of=pad_to_multiple_of,
|
||||
return_tensors="pt",
|
||||
)
|
||||
|
||||
# Instantiate dataloaders.
|
||||
train_dataloader = DataLoader(
|
||||
tokenized_datasets["train"], shuffle=True, collate_fn=collate_fn, batch_size=batch_size
|
||||
)
|
||||
eval_dataloader = DataLoader(
|
||||
tokenized_datasets["validation"], shuffle=False, collate_fn=collate_fn, batch_size=EVAL_BATCH_SIZE
|
||||
)
|
||||
|
||||
return train_dataloader, eval_dataloader
|
||||
|
||||
|
||||
# For testing only
|
||||
if os.environ.get("TESTING_MOCKED_DATALOADERS", None) == "1":
|
||||
from accelerate.test_utils.training import mocked_dataloaders
|
||||
|
||||
get_dataloaders = mocked_dataloaders # noqa: F811
|
||||
|
||||
|
||||
def training_function(config, args):
|
||||
# For testing only
|
||||
if os.environ.get("TESTING_MOCKED_DATALOADERS", None) == "1":
|
||||
config["num_epochs"] = 2
|
||||
# New Code #
|
||||
profile_kwargs = ProfileKwargs(
|
||||
record_shapes=args.record_shapes,
|
||||
profile_memory=args.profile_memory,
|
||||
with_flops=args.with_flops,
|
||||
output_trace_dir=args.output_trace_dir,
|
||||
)
|
||||
# Initialize accelerator
|
||||
accelerator = Accelerator(cpu=args.cpu, mixed_precision=args.mixed_precision, kwargs_handlers=[profile_kwargs])
|
||||
# Sample hyper-parameters for learning rate, batch size, seed and a few other HPs
|
||||
lr = config["lr"]
|
||||
num_epochs = int(config["num_epochs"])
|
||||
seed = int(config["seed"])
|
||||
batch_size = int(config["batch_size"])
|
||||
|
||||
metric = evaluate.load("glue", "mrpc")
|
||||
|
||||
set_seed(seed)
|
||||
train_dataloader, eval_dataloader = get_dataloaders(accelerator, batch_size)
|
||||
# Instantiate the model (we build the model here so that the seed also control new weights initialization)
|
||||
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", return_dict=True)
|
||||
|
||||
# We could avoid this line since the accelerator is set with `device_placement=True` (default value).
|
||||
# Note that if you are placing tensors on devices manually, this line absolutely needs to be before the optimizer
|
||||
# creation otherwise training will not work on TPU (`accelerate` will kindly throw an error to make us aware of that).
|
||||
model = model.to(accelerator.device)
|
||||
|
||||
# Instantiate optimizer
|
||||
optimizer = AdamW(params=model.parameters(), lr=lr)
|
||||
|
||||
# Instantiate scheduler
|
||||
lr_scheduler = get_linear_schedule_with_warmup(
|
||||
optimizer=optimizer,
|
||||
num_warmup_steps=100,
|
||||
num_training_steps=(len(train_dataloader) * num_epochs),
|
||||
)
|
||||
|
||||
# Prepare everything
|
||||
# There is no specific order to remember, we just need to unpack the objects in the same order we gave them to the
|
||||
# prepare method.
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
|
||||
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
|
||||
)
|
||||
|
||||
# Now we train the model
|
||||
for epoch in range(num_epochs):
|
||||
model.train()
|
||||
# New Code #
|
||||
with accelerator.profile() as prof:
|
||||
for step, batch in enumerate(train_dataloader):
|
||||
# We could avoid this line since we set the accelerator with `device_placement=True`.
|
||||
batch.to(accelerator.device)
|
||||
# We use the new `accumulate` context manager to perform gradient accumulation
|
||||
with accelerator.accumulate(model):
|
||||
output = model(**batch)
|
||||
loss = output.loss
|
||||
accelerator.backward(loss)
|
||||
optimizer.step()
|
||||
lr_scheduler.step()
|
||||
optimizer.zero_grad()
|
||||
# New Code #
|
||||
accelerator.print(
|
||||
prof.key_averages().table(
|
||||
sort_by="self_cpu_time_total" if args.cpu else "self_cuda_time_total", row_limit=-1
|
||||
)
|
||||
)
|
||||
|
||||
model.eval()
|
||||
for step, batch in enumerate(eval_dataloader):
|
||||
# We could avoid this line since we set the accelerator with `device_placement=True`.
|
||||
batch.to(accelerator.device)
|
||||
with torch.no_grad():
|
||||
outputs = model(**batch)
|
||||
predictions = outputs.logits.argmax(dim=-1)
|
||||
predictions, references = accelerator.gather_for_metrics((predictions, batch["labels"]))
|
||||
metric.add_batch(
|
||||
predictions=predictions,
|
||||
references=references,
|
||||
)
|
||||
|
||||
eval_metric = metric.compute()
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:", eval_metric)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Simple example of training script.")
|
||||
parser.add_argument(
|
||||
"--mixed_precision",
|
||||
type=str,
|
||||
default=None,
|
||||
choices=["no", "fp16", "bf16", "fp8"],
|
||||
help="Whether to use mixed precision. Choose"
|
||||
"between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >= 1.10."
|
||||
"and an Nvidia Ampere GPU.",
|
||||
)
|
||||
# New Code #
|
||||
parser.add_argument(
|
||||
"--record_shapes",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="If passed, will record shapes for profiling.",
|
||||
)
|
||||
# New Code #
|
||||
parser.add_argument(
|
||||
"--profile_memory",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="If passed, will profile memory.",
|
||||
)
|
||||
# New Code #
|
||||
parser.add_argument(
|
||||
"--with_flops",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="If passed, will profile flops.",
|
||||
)
|
||||
# New Code #
|
||||
parser.add_argument(
|
||||
"--output_trace_dir",
|
||||
type=str,
|
||||
default=None,
|
||||
help="If passed, will save a json trace to the specified path.",
|
||||
)
|
||||
parser.add_argument("--cpu", action="store_true", help="If passed, will train on the CPU.")
|
||||
args = parser.parse_args()
|
||||
config = {"lr": 2e-5, "num_epochs": 3, "seed": 42, "batch_size": 16}
|
||||
training_function(config, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -202,6 +202,7 @@ def training_function(config, args):
|
||||
eval_metric = metric.compute()
|
||||
# Use accelerator.print to print only on the main process.
|
||||
accelerator.print(f"epoch {epoch}:", eval_metric)
|
||||
accelerator.end_training()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user