Upload folder using huggingface_hub (part 4)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +92 -0
- venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/INSTALLER +1 -0
- venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/LICENSE +201 -0
- venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/METADATA +380 -0
- venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/RECORD +95 -0
- venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/REQUESTED +0 -0
- venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/WHEEL +5 -0
- venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/entry_points.txt +6 -0
- venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/top_level.txt +1 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_ddp_comm_hook.py +85 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_distributed_data_loop.py +410 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_merge_weights.py +162 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_notebook.py +118 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_ops.py +181 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_script.py +901 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_sync.py +410 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/testing.py +814 -0
- venv/lib/python3.11/site-packages/accelerate/test_utils/training.py +162 -0
- venv/lib/python3.11/site-packages/accelerate/tracking.py +1089 -0
- venv/lib/python3.11/site-packages/accelerate/utils/__init__.py +291 -0
- venv/lib/python3.11/site-packages/accelerate/utils/ao.py +139 -0
- venv/lib/python3.11/site-packages/accelerate/utils/bnb.py +470 -0
- venv/lib/python3.11/site-packages/accelerate/utils/constants.py +92 -0
- venv/lib/python3.11/site-packages/accelerate/utils/dataclasses.py +0 -0
- venv/lib/python3.11/site-packages/accelerate/utils/deepspeed.py +371 -0
- venv/lib/python3.11/site-packages/accelerate/utils/environment.py +421 -0
- venv/lib/python3.11/site-packages/accelerate/utils/fsdp_utils.py +629 -0
- venv/lib/python3.11/site-packages/accelerate/utils/imports.py +546 -0
- venv/lib/python3.11/site-packages/accelerate/utils/launch.py +709 -0
- venv/lib/python3.11/site-packages/accelerate/utils/megatron_lm.py +1424 -0
- venv/lib/python3.11/site-packages/accelerate/utils/memory.py +199 -0
- venv/lib/python3.11/site-packages/accelerate/utils/modeling.py +2140 -0
- venv/lib/python3.11/site-packages/accelerate/utils/offload.py +213 -0
- venv/lib/python3.11/site-packages/accelerate/utils/operations.py +862 -0
- venv/lib/python3.11/site-packages/accelerate/utils/other.py +373 -0
- venv/lib/python3.11/site-packages/accelerate/utils/random.py +156 -0
- venv/lib/python3.11/site-packages/accelerate/utils/rich.py +24 -0
- venv/lib/python3.11/site-packages/accelerate/utils/torch_xla.py +51 -0
- venv/lib/python3.11/site-packages/accelerate/utils/tqdm.py +43 -0
- venv/lib/python3.11/site-packages/accelerate/utils/transformer_engine.py +160 -0
- venv/lib/python3.11/site-packages/accelerate/utils/versions.py +56 -0
- venv/lib/python3.11/site-packages/ada92cb5d92a588d1b93__mypyc.cpython-311-x86_64-linux-gnu.so +3 -0
- venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/INSTALLER +1 -0
- venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/METADATA +145 -0
- venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/RECORD +11 -0
- venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/WHEEL +4 -0
- venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/entry_points.txt +4 -0
- venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/licenses/LICENSE +21 -0
- venv/lib/python3.11/site-packages/annotated_doc/__init__.py +3 -0
- venv/lib/python3.11/site-packages/annotated_doc/main.py +36 -0
.gitattributes
CHANGED
|
@@ -51,3 +51,95 @@ venv/lib/python3.11/site-packages/PIL/_imagingft.cpython-311-x86_64-linux-gnu.so
|
|
| 51 |
venv/lib/python3.11/site-packages/PIL/_imagingmath.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 52 |
venv/lib/python3.11/site-packages/PIL/_webp.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 53 |
venv/lib/python3.11/site-packages/_brotli.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
venv/lib/python3.11/site-packages/PIL/_imagingmath.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 52 |
venv/lib/python3.11/site-packages/PIL/_webp.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 53 |
venv/lib/python3.11/site-packages/_brotli.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
venv/lib/python3.11/site-packages/ada92cb5d92a588d1b93__mypyc.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
venv/lib/python3.11/site-packages/av/_core.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
venv/lib/python3.11/site-packages/av/attachments/stream.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
venv/lib/python3.11/site-packages/av/audio/codeccontext.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
venv/lib/python3.11/site-packages/av/audio/fifo.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
venv/lib/python3.11/site-packages/av/audio/format.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
venv/lib/python3.11/site-packages/av/audio/frame.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
venv/lib/python3.11/site-packages/av/audio/layout.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
venv/lib/python3.11/site-packages/av/audio/plane.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
venv/lib/python3.11/site-packages/av/audio/resampler.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
venv/lib/python3.11/site-packages/av/audio/stream.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 65 |
+
venv/lib/python3.11/site-packages/av/bitstream.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 66 |
+
venv/lib/python3.11/site-packages/av/buffer.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 67 |
+
venv/lib/python3.11/site-packages/av/bytesource.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 68 |
+
venv/lib/python3.11/site-packages/av/codec/codec.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 69 |
+
venv/lib/python3.11/site-packages/av/codec/context.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 70 |
+
venv/lib/python3.11/site-packages/av/container/core.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 71 |
+
venv/lib/python3.11/site-packages/av/container/input.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 72 |
+
venv/lib/python3.11/site-packages/av/container/output.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 73 |
+
venv/lib/python3.11/site-packages/av/container/pyio.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 74 |
+
venv/lib/python3.11/site-packages/av/container/streams.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 75 |
+
venv/lib/python3.11/site-packages/av/data/stream.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 76 |
+
venv/lib/python3.11/site-packages/av/descriptor.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 77 |
+
venv/lib/python3.11/site-packages/av/dictionary.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 78 |
+
venv/lib/python3.11/site-packages/av/error.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 79 |
+
venv/lib/python3.11/site-packages/av/filter/context.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 80 |
+
venv/lib/python3.11/site-packages/av/filter/filter.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 81 |
+
venv/lib/python3.11/site-packages/av/filter/graph.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 82 |
+
venv/lib/python3.11/site-packages/av/filter/link.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 83 |
+
venv/lib/python3.11/site-packages/av/filter/loudnorm.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 84 |
+
venv/lib/python3.11/site-packages/av/filter/pad.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 85 |
+
venv/lib/python3.11/site-packages/av/format.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 86 |
+
venv/lib/python3.11/site-packages/av/frame.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 87 |
+
venv/lib/python3.11/site-packages/av/logging.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 88 |
+
venv/lib/python3.11/site-packages/av/opaque.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 89 |
+
venv/lib/python3.11/site-packages/av/option.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 90 |
+
venv/lib/python3.11/site-packages/av/packet.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 91 |
+
venv/lib/python3.11/site-packages/av/plane.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 92 |
+
venv/lib/python3.11/site-packages/av/sidedata/motionvectors.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 93 |
+
venv/lib/python3.11/site-packages/av/sidedata/sidedata.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 94 |
+
venv/lib/python3.11/site-packages/av/stream.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 95 |
+
venv/lib/python3.11/site-packages/av/subtitles/codeccontext.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 96 |
+
venv/lib/python3.11/site-packages/av/subtitles/stream.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 97 |
+
venv/lib/python3.11/site-packages/av/subtitles/subtitle.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 98 |
+
venv/lib/python3.11/site-packages/av/utils.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 99 |
+
venv/lib/python3.11/site-packages/av/video/codeccontext.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 100 |
+
venv/lib/python3.11/site-packages/av/video/format.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 101 |
+
venv/lib/python3.11/site-packages/av/video/frame.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 102 |
+
venv/lib/python3.11/site-packages/av/video/plane.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 103 |
+
venv/lib/python3.11/site-packages/av/video/reformatter.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 104 |
+
venv/lib/python3.11/site-packages/av/video/stream.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 105 |
+
venv/lib/python3.11/site-packages/av.libs/libaom-e9efed4a.so.3.2.0 filter=lfs diff=lfs merge=lfs -text
|
| 106 |
+
venv/lib/python3.11/site-packages/av.libs/libavcodec-fb6c662d.so.61.19.100 filter=lfs diff=lfs merge=lfs -text
|
| 107 |
+
venv/lib/python3.11/site-packages/av.libs/libavfilter-3235a7c8.so.10.4.100 filter=lfs diff=lfs merge=lfs -text
|
| 108 |
+
venv/lib/python3.11/site-packages/av.libs/libavformat-071c54bd.so.61.7.100 filter=lfs diff=lfs merge=lfs -text
|
| 109 |
+
venv/lib/python3.11/site-packages/av.libs/libavutil-2749f1ba.so.59.39.100 filter=lfs diff=lfs merge=lfs -text
|
| 110 |
+
venv/lib/python3.11/site-packages/av.libs/libdav1d-1b53ef2f.so.7.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 111 |
+
venv/lib/python3.11/site-packages/av.libs/libgmp-a4b719d5.so.10.5.0 filter=lfs diff=lfs merge=lfs -text
|
| 112 |
+
venv/lib/python3.11/site-packages/av.libs/libgnutls-b9b94016.so.30.36.0 filter=lfs diff=lfs merge=lfs -text
|
| 113 |
+
venv/lib/python3.11/site-packages/av.libs/libhogweed-9544c08c.so.6.8 filter=lfs diff=lfs merge=lfs -text
|
| 114 |
+
venv/lib/python3.11/site-packages/av.libs/liblzma-af70179d.so.5.4.4 filter=lfs diff=lfs merge=lfs -text
|
| 115 |
+
venv/lib/python3.11/site-packages/av.libs/libmp3lame-3ecc6556.so.0.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 116 |
+
venv/lib/python3.11/site-packages/av.libs/libnettle-ecd2e589.so.8.8 filter=lfs diff=lfs merge=lfs -text
|
| 117 |
+
venv/lib/python3.11/site-packages/av.libs/libopencore-amrnb-393dbae2.so.0.0.3 filter=lfs diff=lfs merge=lfs -text
|
| 118 |
+
venv/lib/python3.11/site-packages/av.libs/libopus-59fcdf85.so.0.9.0 filter=lfs diff=lfs merge=lfs -text
|
| 119 |
+
venv/lib/python3.11/site-packages/av.libs/libspeex-2370356a.so.1.5.2 filter=lfs diff=lfs merge=lfs -text
|
| 120 |
+
venv/lib/python3.11/site-packages/av.libs/libswresample-da7d062e.so.5.3.100 filter=lfs diff=lfs merge=lfs -text
|
| 121 |
+
venv/lib/python3.11/site-packages/av.libs/libswscale-9212cf18.so.8.3.100 filter=lfs diff=lfs merge=lfs -text
|
| 122 |
+
venv/lib/python3.11/site-packages/av.libs/libtwolame-72d74ef7.so.0.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 123 |
+
venv/lib/python3.11/site-packages/av.libs/libunistring-214e3d6e.so.5.1.0 filter=lfs diff=lfs merge=lfs -text
|
| 124 |
+
venv/lib/python3.11/site-packages/av.libs/libvorbis-f4a9a6fd.so.0.4.9 filter=lfs diff=lfs merge=lfs -text
|
| 125 |
+
venv/lib/python3.11/site-packages/av.libs/libvorbisenc-0d9d5bdf.so.2.0.12 filter=lfs diff=lfs merge=lfs -text
|
| 126 |
+
venv/lib/python3.11/site-packages/av.libs/libvpx-832f6f52.so.9.0.0 filter=lfs diff=lfs merge=lfs -text
|
| 127 |
+
venv/lib/python3.11/site-packages/av.libs/libwebp-e16038c7.so.7.1.9 filter=lfs diff=lfs merge=lfs -text
|
| 128 |
+
venv/lib/python3.11/site-packages/av.libs/libx264-2a4c6f6d.so.164 filter=lfs diff=lfs merge=lfs -text
|
| 129 |
+
venv/lib/python3.11/site-packages/av.libs/libx265-d8690e8d.so.199 filter=lfs diff=lfs merge=lfs -text
|
| 130 |
+
venv/lib/python3.11/site-packages/av.libs/libxcb-65da195c.so.1.1.0 filter=lfs diff=lfs merge=lfs -text
|
| 131 |
+
venv/lib/python3.11/site-packages/av.libs/libxml2-cb941fce.so.2.9.13 filter=lfs diff=lfs merge=lfs -text
|
| 132 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda118.so filter=lfs diff=lfs merge=lfs -text
|
| 133 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda121.so filter=lfs diff=lfs merge=lfs -text
|
| 134 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda124.so filter=lfs diff=lfs merge=lfs -text
|
| 135 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda126.so filter=lfs diff=lfs merge=lfs -text
|
| 136 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda128.so filter=lfs diff=lfs merge=lfs -text
|
| 137 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda130.so filter=lfs diff=lfs merge=lfs -text
|
| 138 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda132.so filter=lfs diff=lfs merge=lfs -text
|
| 139 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_rocm64.so filter=lfs diff=lfs merge=lfs -text
|
| 140 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_rocm70.so filter=lfs diff=lfs merge=lfs -text
|
| 141 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_rocm71.so filter=lfs diff=lfs merge=lfs -text
|
| 142 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_rocm714.so filter=lfs diff=lfs merge=lfs -text
|
| 143 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_rocm72.so filter=lfs diff=lfs merge=lfs -text
|
| 144 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_xpu2025.so filter=lfs diff=lfs merge=lfs -text
|
| 145 |
+
venv/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_xpu2026.so filter=lfs diff=lfs merge=lfs -text
|
venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.1
|
| 2 |
+
Name: accelerate
|
| 3 |
+
Version: 1.6.0
|
| 4 |
+
Summary: Accelerate
|
| 5 |
+
Home-page: https://github.com/huggingface/accelerate
|
| 6 |
+
Author: The HuggingFace team
|
| 7 |
+
Author-email: zach.mueller@huggingface.co
|
| 8 |
+
License: Apache
|
| 9 |
+
Keywords: deep learning
|
| 10 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 11 |
+
Classifier: Intended Audience :: Developers
|
| 12 |
+
Classifier: Intended Audience :: Education
|
| 13 |
+
Classifier: Intended Audience :: Science/Research
|
| 14 |
+
Classifier: License :: OSI Approved :: Apache Software License
|
| 15 |
+
Classifier: Operating System :: OS Independent
|
| 16 |
+
Classifier: Programming Language :: Python :: 3
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 18 |
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
| 19 |
+
Requires-Python: >=3.9.0
|
| 20 |
+
Description-Content-Type: text/markdown
|
| 21 |
+
License-File: LICENSE
|
| 22 |
+
Requires-Dist: numpy<3.0.0,>=1.17
|
| 23 |
+
Requires-Dist: packaging>=20.0
|
| 24 |
+
Requires-Dist: psutil
|
| 25 |
+
Requires-Dist: pyyaml
|
| 26 |
+
Requires-Dist: torch>=2.0.0
|
| 27 |
+
Requires-Dist: huggingface-hub>=0.21.0
|
| 28 |
+
Requires-Dist: safetensors>=0.4.3
|
| 29 |
+
Provides-Extra: deepspeed
|
| 30 |
+
Requires-Dist: deepspeed; extra == "deepspeed"
|
| 31 |
+
Provides-Extra: dev
|
| 32 |
+
Requires-Dist: black~=23.1; extra == "dev"
|
| 33 |
+
Requires-Dist: hf-doc-builder>=0.3.0; extra == "dev"
|
| 34 |
+
Requires-Dist: ruff~=0.11.2; extra == "dev"
|
| 35 |
+
Requires-Dist: pytest<=8.0.0,>=7.2.0; extra == "dev"
|
| 36 |
+
Requires-Dist: pytest-xdist; extra == "dev"
|
| 37 |
+
Requires-Dist: pytest-subtests; extra == "dev"
|
| 38 |
+
Requires-Dist: parameterized; extra == "dev"
|
| 39 |
+
Requires-Dist: pytest-order; extra == "dev"
|
| 40 |
+
Requires-Dist: datasets; extra == "dev"
|
| 41 |
+
Requires-Dist: diffusers; extra == "dev"
|
| 42 |
+
Requires-Dist: evaluate; extra == "dev"
|
| 43 |
+
Requires-Dist: torchdata>=0.8.0; extra == "dev"
|
| 44 |
+
Requires-Dist: torchpippy>=0.2.0; extra == "dev"
|
| 45 |
+
Requires-Dist: transformers; extra == "dev"
|
| 46 |
+
Requires-Dist: scipy; extra == "dev"
|
| 47 |
+
Requires-Dist: scikit-learn; extra == "dev"
|
| 48 |
+
Requires-Dist: tqdm; extra == "dev"
|
| 49 |
+
Requires-Dist: bitsandbytes; extra == "dev"
|
| 50 |
+
Requires-Dist: timm; extra == "dev"
|
| 51 |
+
Requires-Dist: rich; extra == "dev"
|
| 52 |
+
Provides-Extra: docs
|
| 53 |
+
Provides-Extra: quality
|
| 54 |
+
Requires-Dist: black~=23.1; extra == "quality"
|
| 55 |
+
Requires-Dist: hf-doc-builder>=0.3.0; extra == "quality"
|
| 56 |
+
Requires-Dist: ruff~=0.11.2; extra == "quality"
|
| 57 |
+
Provides-Extra: rich
|
| 58 |
+
Requires-Dist: rich; extra == "rich"
|
| 59 |
+
Provides-Extra: sagemaker
|
| 60 |
+
Requires-Dist: sagemaker; extra == "sagemaker"
|
| 61 |
+
Provides-Extra: test_dev
|
| 62 |
+
Requires-Dist: datasets; extra == "test-dev"
|
| 63 |
+
Requires-Dist: diffusers; extra == "test-dev"
|
| 64 |
+
Requires-Dist: evaluate; extra == "test-dev"
|
| 65 |
+
Requires-Dist: torchdata>=0.8.0; extra == "test-dev"
|
| 66 |
+
Requires-Dist: torchpippy>=0.2.0; extra == "test-dev"
|
| 67 |
+
Requires-Dist: transformers; extra == "test-dev"
|
| 68 |
+
Requires-Dist: scipy; extra == "test-dev"
|
| 69 |
+
Requires-Dist: scikit-learn; extra == "test-dev"
|
| 70 |
+
Requires-Dist: tqdm; extra == "test-dev"
|
| 71 |
+
Requires-Dist: bitsandbytes; extra == "test-dev"
|
| 72 |
+
Requires-Dist: timm; extra == "test-dev"
|
| 73 |
+
Provides-Extra: test_prod
|
| 74 |
+
Requires-Dist: pytest<=8.0.0,>=7.2.0; extra == "test-prod"
|
| 75 |
+
Requires-Dist: pytest-xdist; extra == "test-prod"
|
| 76 |
+
Requires-Dist: pytest-subtests; extra == "test-prod"
|
| 77 |
+
Requires-Dist: parameterized; extra == "test-prod"
|
| 78 |
+
Requires-Dist: pytest-order; extra == "test-prod"
|
| 79 |
+
Provides-Extra: test_trackers
|
| 80 |
+
Requires-Dist: wandb; extra == "test-trackers"
|
| 81 |
+
Requires-Dist: comet-ml; extra == "test-trackers"
|
| 82 |
+
Requires-Dist: tensorboard; extra == "test-trackers"
|
| 83 |
+
Requires-Dist: dvclive; extra == "test-trackers"
|
| 84 |
+
Requires-Dist: mlflow; extra == "test-trackers"
|
| 85 |
+
Requires-Dist: matplotlib; extra == "test-trackers"
|
| 86 |
+
Provides-Extra: testing
|
| 87 |
+
Requires-Dist: pytest<=8.0.0,>=7.2.0; extra == "testing"
|
| 88 |
+
Requires-Dist: pytest-xdist; extra == "testing"
|
| 89 |
+
Requires-Dist: pytest-subtests; extra == "testing"
|
| 90 |
+
Requires-Dist: parameterized; extra == "testing"
|
| 91 |
+
Requires-Dist: pytest-order; extra == "testing"
|
| 92 |
+
Requires-Dist: datasets; extra == "testing"
|
| 93 |
+
Requires-Dist: diffusers; extra == "testing"
|
| 94 |
+
Requires-Dist: evaluate; extra == "testing"
|
| 95 |
+
Requires-Dist: torchdata>=0.8.0; extra == "testing"
|
| 96 |
+
Requires-Dist: torchpippy>=0.2.0; extra == "testing"
|
| 97 |
+
Requires-Dist: transformers; extra == "testing"
|
| 98 |
+
Requires-Dist: scipy; extra == "testing"
|
| 99 |
+
Requires-Dist: scikit-learn; extra == "testing"
|
| 100 |
+
Requires-Dist: tqdm; extra == "testing"
|
| 101 |
+
Requires-Dist: bitsandbytes; extra == "testing"
|
| 102 |
+
Requires-Dist: timm; extra == "testing"
|
| 103 |
+
|
| 104 |
+
<!---
|
| 105 |
+
Copyright 2021 The HuggingFace Team. All rights reserved.
|
| 106 |
+
|
| 107 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 108 |
+
you may not use this file except in compliance with the License.
|
| 109 |
+
You may obtain a copy of the License at
|
| 110 |
+
|
| 111 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 112 |
+
|
| 113 |
+
Unless required by applicable law or agreed to in writing, software
|
| 114 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 115 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 116 |
+
See the License for the specific language governing permissions and
|
| 117 |
+
limitations under the License.
|
| 118 |
+
-->
|
| 119 |
+
|
| 120 |
+
<p align="center">
|
| 121 |
+
<br>
|
| 122 |
+
<img src="https://raw.githubusercontent.com/huggingface/accelerate/main/docs/source/imgs/accelerate_logo.png" width="400"/>
|
| 123 |
+
<br>
|
| 124 |
+
<p>
|
| 125 |
+
|
| 126 |
+
<p align="center">
|
| 127 |
+
<!-- Uncomment when CircleCI is set up
|
| 128 |
+
<a href="https://circleci.com/gh/huggingface/accelerate"><img alt="Build" src="https://img.shields.io/circleci/build/github/huggingface/transformers/master"></a>
|
| 129 |
+
-->
|
| 130 |
+
<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>
|
| 131 |
+
<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>
|
| 132 |
+
<a href="https://github.com/huggingface/accelerate/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/huggingface/accelerate.svg"></a>
|
| 133 |
+
<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>
|
| 134 |
+
</p>
|
| 135 |
+
|
| 136 |
+
<h3 align="center">
|
| 137 |
+
<p>Run your *raw* PyTorch training script on any kind of device
|
| 138 |
+
</h3>
|
| 139 |
+
|
| 140 |
+
<h3 align="center">
|
| 141 |
+
<a href="https://hf.co/course"><img src="https://raw.githubusercontent.com/huggingface/accelerate/main/docs/source/imgs/course_banner.png"></a>
|
| 142 |
+
</h3>
|
| 143 |
+
|
| 144 |
+
## Easy to integrate
|
| 145 |
+
|
| 146 |
+
🤗 Accelerate was created for PyTorch users who like to write the training loop of PyTorch models but are reluctant to write and maintain the boilerplate code needed to use multi-GPUs/TPU/fp16.
|
| 147 |
+
|
| 148 |
+
🤗 Accelerate abstracts exactly and only the boilerplate code related to multi-GPUs/TPU/fp16 and leaves the rest of your code unchanged.
|
| 149 |
+
|
| 150 |
+
Here is an example:
|
| 151 |
+
|
| 152 |
+
```diff
|
| 153 |
+
import torch
|
| 154 |
+
import torch.nn.functional as F
|
| 155 |
+
from datasets import load_dataset
|
| 156 |
+
+ from accelerate import Accelerator
|
| 157 |
+
|
| 158 |
+
+ accelerator = Accelerator()
|
| 159 |
+
- device = 'cpu'
|
| 160 |
+
+ device = accelerator.device
|
| 161 |
+
|
| 162 |
+
model = torch.nn.Transformer().to(device)
|
| 163 |
+
optimizer = torch.optim.Adam(model.parameters())
|
| 164 |
+
|
| 165 |
+
dataset = load_dataset('my_dataset')
|
| 166 |
+
data = torch.utils.data.DataLoader(dataset, shuffle=True)
|
| 167 |
+
|
| 168 |
+
+ model, optimizer, data = accelerator.prepare(model, optimizer, data)
|
| 169 |
+
|
| 170 |
+
model.train()
|
| 171 |
+
for epoch in range(10):
|
| 172 |
+
for source, targets in data:
|
| 173 |
+
source = source.to(device)
|
| 174 |
+
targets = targets.to(device)
|
| 175 |
+
|
| 176 |
+
optimizer.zero_grad()
|
| 177 |
+
|
| 178 |
+
output = model(source)
|
| 179 |
+
loss = F.cross_entropy(output, targets)
|
| 180 |
+
|
| 181 |
+
- loss.backward()
|
| 182 |
+
+ accelerator.backward(loss)
|
| 183 |
+
|
| 184 |
+
optimizer.step()
|
| 185 |
+
```
|
| 186 |
+
|
| 187 |
+
As you can see in this example, by adding 5-lines to any standard PyTorch training script you can now run on any kind of single or distributed node setting (single CPU, single GPU, multi-GPUs and TPUs) as well as with or without mixed precision (fp8, fp16, bf16).
|
| 188 |
+
|
| 189 |
+
In particular, the same code can then be run without modification on your local machine for debugging or your training environment.
|
| 190 |
+
|
| 191 |
+
🤗 Accelerate even handles the device placement for you (which requires a few more changes to your code, but is safer in general), so you can even simplify your training loop further:
|
| 192 |
+
|
| 193 |
+
```diff
|
| 194 |
+
import torch
|
| 195 |
+
import torch.nn.functional as F
|
| 196 |
+
from datasets import load_dataset
|
| 197 |
+
+ from accelerate import Accelerator
|
| 198 |
+
|
| 199 |
+
- device = 'cpu'
|
| 200 |
+
+ accelerator = Accelerator()
|
| 201 |
+
|
| 202 |
+
- model = torch.nn.Transformer().to(device)
|
| 203 |
+
+ model = torch.nn.Transformer()
|
| 204 |
+
optimizer = torch.optim.Adam(model.parameters())
|
| 205 |
+
|
| 206 |
+
dataset = load_dataset('my_dataset')
|
| 207 |
+
data = torch.utils.data.DataLoader(dataset, shuffle=True)
|
| 208 |
+
|
| 209 |
+
+ model, optimizer, data = accelerator.prepare(model, optimizer, data)
|
| 210 |
+
|
| 211 |
+
model.train()
|
| 212 |
+
for epoch in range(10):
|
| 213 |
+
for source, targets in data:
|
| 214 |
+
- source = source.to(device)
|
| 215 |
+
- targets = targets.to(device)
|
| 216 |
+
|
| 217 |
+
optimizer.zero_grad()
|
| 218 |
+
|
| 219 |
+
output = model(source)
|
| 220 |
+
loss = F.cross_entropy(output, targets)
|
| 221 |
+
|
| 222 |
+
- loss.backward()
|
| 223 |
+
+ accelerator.backward(loss)
|
| 224 |
+
|
| 225 |
+
optimizer.step()
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
Want to learn more? Check out the [documentation](https://huggingface.co/docs/accelerate) or have a look at our [examples](https://github.com/huggingface/accelerate/tree/main/examples).
|
| 229 |
+
|
| 230 |
+
## Launching script
|
| 231 |
+
|
| 232 |
+
🤗 Accelerate also provides an optional CLI tool that allows you to quickly configure and test your training environment before launching the scripts. No need to remember how to use `torch.distributed.run` or to write a specific launcher for TPU training!
|
| 233 |
+
On your machine(s) just run:
|
| 234 |
+
|
| 235 |
+
```bash
|
| 236 |
+
accelerate config
|
| 237 |
+
```
|
| 238 |
+
|
| 239 |
+
and answer the questions asked. This will generate a config file that will be used automatically to properly set the default options when doing
|
| 240 |
+
|
| 241 |
+
```bash
|
| 242 |
+
accelerate launch my_script.py --args_to_my_script
|
| 243 |
+
```
|
| 244 |
+
|
| 245 |
+
For instance, here is how you would run the GLUE example on the MRPC task (from the root of the repo):
|
| 246 |
+
|
| 247 |
+
```bash
|
| 248 |
+
accelerate launch examples/nlp_example.py
|
| 249 |
+
```
|
| 250 |
+
|
| 251 |
+
This CLI tool is **optional**, and you can still use `python my_script.py` or `python -m torchrun my_script.py` at your convenience.
|
| 252 |
+
|
| 253 |
+
You can also directly pass in the arguments you would to `torchrun` as arguments to `accelerate launch` if you wish to not run` accelerate config`.
|
| 254 |
+
|
| 255 |
+
For example, here is how to launch on two GPUs:
|
| 256 |
+
|
| 257 |
+
```bash
|
| 258 |
+
accelerate launch --multi_gpu --num_processes 2 examples/nlp_example.py
|
| 259 |
+
```
|
| 260 |
+
|
| 261 |
+
To learn more, check the CLI documentation available [here](https://huggingface.co/docs/accelerate/package_reference/cli).
|
| 262 |
+
|
| 263 |
+
Or view the configuration zoo [here](https://github.com/huggingface/accelerate/blob/main/examples/config_yaml_templates/)
|
| 264 |
+
|
| 265 |
+
## Launching multi-CPU run using MPI
|
| 266 |
+
|
| 267 |
+
🤗 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.
|
| 268 |
+
Once you have MPI setup on your cluster, just run:
|
| 269 |
+
```bash
|
| 270 |
+
accelerate config
|
| 271 |
+
```
|
| 272 |
+
Answer the questions that are asked, selecting to run using multi-CPU, and answer "yes" when asked if you want accelerate to launch mpirun.
|
| 273 |
+
Then, use `accelerate launch` with your script like:
|
| 274 |
+
```bash
|
| 275 |
+
accelerate launch examples/nlp_example.py
|
| 276 |
+
```
|
| 277 |
+
Alternatively, you can use mpirun directly, without using the CLI like:
|
| 278 |
+
```bash
|
| 279 |
+
mpirun -np 2 python examples/nlp_example.py
|
| 280 |
+
```
|
| 281 |
+
|
| 282 |
+
## Launching training using DeepSpeed
|
| 283 |
+
|
| 284 |
+
🤗 Accelerate supports training on single/multiple GPUs using DeepSpeed. To use it, you don't need to change anything in your training code; you can set everything using just `accelerate config`. However, if you desire to tweak your DeepSpeed related args from your Python script, we provide you the `DeepSpeedPlugin`.
|
| 285 |
+
|
| 286 |
+
```python
|
| 287 |
+
from accelerate import Accelerator, DeepSpeedPlugin
|
| 288 |
+
|
| 289 |
+
# deepspeed needs to know your gradient accumulation steps beforehand, so don't forget to pass it
|
| 290 |
+
# Remember you still need to do gradient accumulation by yourself, just like you would have done without deepspeed
|
| 291 |
+
deepspeed_plugin = DeepSpeedPlugin(zero_stage=2, gradient_accumulation_steps=2)
|
| 292 |
+
accelerator = Accelerator(mixed_precision='fp16', deepspeed_plugin=deepspeed_plugin)
|
| 293 |
+
|
| 294 |
+
# How to save your 🤗 Transformer?
|
| 295 |
+
accelerator.wait_for_everyone()
|
| 296 |
+
unwrapped_model = accelerator.unwrap_model(model)
|
| 297 |
+
unwrapped_model.save_pretrained(save_dir, save_function=accelerator.save, state_dict=accelerator.get_state_dict(model))
|
| 298 |
+
```
|
| 299 |
+
|
| 300 |
+
Note: DeepSpeed support is experimental for now. In case you get into some problem, please open an issue.
|
| 301 |
+
|
| 302 |
+
## Launching your training from a notebook
|
| 303 |
+
|
| 304 |
+
🤗 Accelerate also provides a `notebook_launcher` function you can use in a notebook to launch a distributed training. This is especially useful for Colab or Kaggle notebooks with a TPU backend. Just define your training loop in a `training_function` then in your last cell, add:
|
| 305 |
+
|
| 306 |
+
```python
|
| 307 |
+
from accelerate import notebook_launcher
|
| 308 |
+
|
| 309 |
+
notebook_launcher(training_function)
|
| 310 |
+
```
|
| 311 |
+
|
| 312 |
+
An example can be found in [this notebook](https://github.com/huggingface/notebooks/blob/main/examples/accelerate_examples/simple_nlp_example.ipynb). [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/examples/accelerate_examples/simple_nlp_example.ipynb)
|
| 313 |
+
|
| 314 |
+
## Why should I use 🤗 Accelerate?
|
| 315 |
+
|
| 316 |
+
You should use 🤗 Accelerate when you want to easily run your training scripts in a distributed environment without having to renounce full control over your training loop. This is not a high-level framework above PyTorch, just a thin wrapper so you don't have to learn a new library. In fact, the whole API of 🤗 Accelerate is in one class, the `Accelerator` object.
|
| 317 |
+
|
| 318 |
+
## Why shouldn't I use 🤗 Accelerate?
|
| 319 |
+
|
| 320 |
+
You shouldn't use 🤗 Accelerate if you don't want to write a training loop yourself. There are plenty of high-level libraries above PyTorch that will offer you that, 🤗 Accelerate is not one of them.
|
| 321 |
+
|
| 322 |
+
## Frameworks using 🤗 Accelerate
|
| 323 |
+
|
| 324 |
+
If you like the simplicity of 🤗 Accelerate but would prefer a higher-level abstraction around its capabilities, some frameworks and libraries that are built on top of 🤗 Accelerate are listed below:
|
| 325 |
+
|
| 326 |
+
* [Amphion](https://github.com/open-mmlab/Amphion) is a toolkit for Audio, Music, and Speech Generation. Its purpose is to support reproducible research and help junior researchers and engineers get started in the field of audio, music, and speech generation research and development.
|
| 327 |
+
* [Animus](https://github.com/Scitator/animus) is a minimalistic framework to run machine learning experiments. Animus highlights common "breakpoints" in ML experiments and provides a unified interface for them within [IExperiment](https://github.com/Scitator/animus/blob/main/animus/core.py#L76).
|
| 328 |
+
* [Catalyst](https://github.com/catalyst-team/catalyst#getting-started) is a PyTorch framework for Deep Learning Research and Development. It focuses on reproducibility, rapid experimentation, and codebase reuse so you can create something new rather than write yet another train loop. Catalyst provides a [Runner](https://catalyst-team.github.io/catalyst/api/core.html#runner) to connect all parts of the experiment: hardware backend, data transformations, model training, and inference logic.
|
| 329 |
+
* [fastai](https://github.com/fastai/fastai#installing) is a PyTorch framework for Deep Learning that simplifies training fast and accurate neural nets using modern best practices. fastai provides a [Learner](https://docs.fast.ai/learner.html#Learner) to handle the training, fine-tuning, and inference of deep learning algorithms.
|
| 330 |
+
* [Finetuner](https://github.com/jina-ai/finetuner) is a service that enables models to create higher-quality embeddings for semantic search, visual similarity search, cross-modal text<->image search, recommendation systems, clustering, duplication detection, anomaly detection, or other uses.
|
| 331 |
+
* [InvokeAI](https://github.com/invoke-ai/InvokeAI) is a creative engine for Stable Diffusion models, offering industry-leading WebUI, terminal usage support, and serves as the foundation for many commercial products.
|
| 332 |
+
* [Kornia](https://kornia.readthedocs.io/en/latest/get-started/introduction.html) is a differentiable library that allows classical computer vision to be integrated into deep learning models. Kornia provides a [Trainer](https://kornia.readthedocs.io/en/latest/x.html#kornia.x.Trainer) with the specific purpose to train and fine-tune the supported deep learning algorithms within the library.
|
| 333 |
+
* [Open Assistant](https://projects.laion.ai/Open-Assistant/) is a chat-based assistant that understands tasks, can interact with their party systems, and retrieve information dynamically to do so.
|
| 334 |
+
* [pytorch-accelerated](https://github.com/Chris-hughes10/pytorch-accelerated) is a lightweight training library, with a streamlined feature set centered around a general-purpose [Trainer](https://pytorch-accelerated.readthedocs.io/en/latest/trainer.html), that places a huge emphasis on simplicity and transparency; enabling users to understand exactly what is going on under the hood, but without having to write and maintain the boilerplate themselves!
|
| 335 |
+
* [Stable Diffusion web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) is an open-source browser-based easy-to-use interface based on the Gradio library for Stable Diffusion.
|
| 336 |
+
* [torchkeras](https://github.com/lyhue1991/torchkeras) is a simple tool for training pytorch model just in a keras style, a dynamic and beautiful plot is provided in notebook to monitor your loss or metric.
|
| 337 |
+
* [transformers](https://github.com/huggingface/transformers) as a tool for helping train state-of-the-art machine learning models in PyTorch, Tensorflow, and JAX. (Accelerate is the backend for the PyTorch side).
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
## Installation
|
| 341 |
+
|
| 342 |
+
This repository is tested on Python 3.8+ and PyTorch 1.10.0+
|
| 343 |
+
|
| 344 |
+
You should install 🤗 Accelerate in a [virtual environment](https://docs.python.org/3/library/venv.html). If you're unfamiliar with Python virtual environments, check out the [user guide](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/).
|
| 345 |
+
|
| 346 |
+
First, create a virtual environment with the version of Python you're going to use and activate it.
|
| 347 |
+
|
| 348 |
+
Then, you will need to install PyTorch: refer to the [official installation page](https://pytorch.org/get-started/locally/#start-locally) regarding the specific install command for your platform. Then 🤗 Accelerate can be installed using pip as follows:
|
| 349 |
+
|
| 350 |
+
```bash
|
| 351 |
+
pip install accelerate
|
| 352 |
+
```
|
| 353 |
+
|
| 354 |
+
## Supported integrations
|
| 355 |
+
|
| 356 |
+
- CPU only
|
| 357 |
+
- multi-CPU on one node (machine)
|
| 358 |
+
- multi-CPU on several nodes (machines)
|
| 359 |
+
- single GPU
|
| 360 |
+
- multi-GPU on one node (machine)
|
| 361 |
+
- multi-GPU on several nodes (machines)
|
| 362 |
+
- TPU
|
| 363 |
+
- FP16/BFloat16 mixed precision
|
| 364 |
+
- FP8 mixed precision with [Transformer Engine](https://github.com/NVIDIA/TransformerEngine) or [MS-AMP](https://github.com/Azure/MS-AMP/)
|
| 365 |
+
- DeepSpeed support (Experimental)
|
| 366 |
+
- PyTorch Fully Sharded Data Parallel (FSDP) support (Experimental)
|
| 367 |
+
- Megatron-LM support (Experimental)
|
| 368 |
+
|
| 369 |
+
## Citing 🤗 Accelerate
|
| 370 |
+
|
| 371 |
+
If you use 🤗 Accelerate in your publication, please cite it by using the following BibTeX entry.
|
| 372 |
+
|
| 373 |
+
```bibtex
|
| 374 |
+
@Misc{accelerate,
|
| 375 |
+
title = {Accelerate: Training and inference at scale made simple, efficient and adaptable.},
|
| 376 |
+
author = {Sylvain Gugger and Lysandre Debut and Thomas Wolf and Philipp Schmid and Zachary Mueller and Sourab Mangrulkar and Marc Sun and Benjamin Bossan},
|
| 377 |
+
howpublished = {\url{https://github.com/huggingface/accelerate}},
|
| 378 |
+
year = {2022}
|
| 379 |
+
}
|
| 380 |
+
```
|
venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/accelerate,sha256=yGR8aKf_gKSS8x9pCBUGuD7rCpYLVZLhIyVaQrvwtWE,327
|
| 2 |
+
../../../bin/accelerate-config,sha256=dUFEPDzU0dxtO1aioRHSqdcaCFuHDWurFs1a1A0pd5s,319
|
| 3 |
+
../../../bin/accelerate-estimate-memory,sha256=1x5il15x5leo8DeYfFmUgzfQRVUVSUgcRQn1rioSBKk,321
|
| 4 |
+
../../../bin/accelerate-launch,sha256=_Er34YoJFY7jHgz-unzt4xu-p-8x13fnFQtvuwW1YgY,319
|
| 5 |
+
../../../bin/accelerate-merge-weights,sha256=H8qIBwDIWnizGG4GmuXRZ0K3iFjN9kl9orgQOh3zzlU,318
|
| 6 |
+
accelerate-1.6.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 7 |
+
accelerate-1.6.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
| 8 |
+
accelerate-1.6.0.dist-info/METADATA,sha256=zT5ADQHZZeLT4qEiGMNSG4cT7hCnQplwyshDyeDyZNo,19421
|
| 9 |
+
accelerate-1.6.0.dist-info/RECORD,,
|
| 10 |
+
accelerate-1.6.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 11 |
+
accelerate-1.6.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
| 12 |
+
accelerate-1.6.0.dist-info/entry_points.txt,sha256=Vpy8gUGfZ-1VnM2229fb8CpJNLBdMH_wtJ9PQ7b_2tQ,296
|
| 13 |
+
accelerate-1.6.0.dist-info/top_level.txt,sha256=esVfdxTidsjQ90zsN_rPpjLFJ4ijRlx4mnLrG09hlt4,11
|
| 14 |
+
accelerate/__init__.py,sha256=r3I-pArsQK9ZrH3XgnjeCoXo4l-DEFOWQhjj3BguTZc,1504
|
| 15 |
+
accelerate/accelerator.py,sha256=G952noNHGPrl-poK6qAj1OY32kGjmN5S13v8zy7H63E,173175
|
| 16 |
+
accelerate/big_modeling.py,sha256=IMiAtiuZQpwSyk2jQsoYC2uWzfRUSpCg7FiThSvjfKw,29702
|
| 17 |
+
accelerate/checkpointing.py,sha256=BaDOrpQzRI2U1BvN2vK4lepTRNNqbxGd4QPa1zOShoc,13612
|
| 18 |
+
accelerate/commands/__init__.py,sha256=m1PPTDT4ziIAvM0-FDSgIMIZ69Konn126s6LwuzH6v8,606
|
| 19 |
+
accelerate/commands/accelerate_cli.py,sha256=SkwFad6Z1ZsGjtm7TiXFq8je-akshp_0WxX_6rGSBw8,1972
|
| 20 |
+
accelerate/commands/config/__init__.py,sha256=iJK8dgj3pc5Vdr1E7UuGoFu-BlybyXLxYDoTg9gXngE,1645
|
| 21 |
+
accelerate/commands/config/cluster.py,sha256=w0L3zTyZp4sjDpCrM3NxOjxZ0kyPJZmzi06pFZmbM2c,37472
|
| 22 |
+
accelerate/commands/config/config.py,sha256=FuRlQvOjgATEtyqOSsGD-KEtOCvACOHjs2C-krrtldk,3035
|
| 23 |
+
accelerate/commands/config/config_args.py,sha256=xn6M8iJnlFycosDlbM0BE86r9RxfdDwHtIlk-UUq7UM,10082
|
| 24 |
+
accelerate/commands/config/config_utils.py,sha256=mdvZE9fpllfD8S4Blhqk3nLqQ5m14WJ0jQ1yh768H10,3177
|
| 25 |
+
accelerate/commands/config/default.py,sha256=sPgQVt_0zk68KlupQFqt8B6JUoPMFPxXmXr7xFM-EN8,6212
|
| 26 |
+
accelerate/commands/config/sagemaker.py,sha256=GjHE2-h4tRr1P_PFtMF3miiAtJlzkbHbMb6kFXqn8eo,10341
|
| 27 |
+
accelerate/commands/config/update.py,sha256=NXW1J7GkUHpg71QlIXsmMB_0z8S8IZo2FWax5POwrhc,2395
|
| 28 |
+
accelerate/commands/env.py,sha256=-B3FPX4S705A-P_tyLKm_JzGpz-TeKqFNPdNWDAdGIM,4156
|
| 29 |
+
accelerate/commands/estimate.py,sha256=Qduq4xudVyIede37BMEe1rNhXf-rfW-MHV2KtwxdfEA,12585
|
| 30 |
+
accelerate/commands/launch.py,sha256=7DI42Uw4kf_peOpY5TUA1V2yz7cuSO3cYnLgiI5G1Vs,47496
|
| 31 |
+
accelerate/commands/menu/__init__.py,sha256=uqSlBM0TFHBwzdv3p3SXfpAk1lZFp4h1a7mbBdscPHs,645
|
| 32 |
+
accelerate/commands/menu/cursor.py,sha256=-lmpJVAzvNc0c3EOtSuLoKB59zqylVCbYyWLPnrOmvQ,2028
|
| 33 |
+
accelerate/commands/menu/helpers.py,sha256=KrSB5fJjH4MUEUAQJ6bYaN16AYcnl9UalDrPD3DYeeg,1483
|
| 34 |
+
accelerate/commands/menu/input.py,sha256=T8Mdd-Y_OURgqfDV9qZh4Wf6hmT22AneNtJzj4JA1Rk,2512
|
| 35 |
+
accelerate/commands/menu/keymap.py,sha256=eXj-suyYs1m5dEHoUKN4mKAMLc8DWHnwhP6G6JSU0jQ,4086
|
| 36 |
+
accelerate/commands/menu/selection_menu.py,sha256=bxy-DHaKKC6SCToOlMBv5_z0MdUzylEg6Sio9OuV3GM,4921
|
| 37 |
+
accelerate/commands/merge.py,sha256=quDKckN3vKn9nsGjdwfoojnfTMFdKRRUkY1DYuuNNmc,2388
|
| 38 |
+
accelerate/commands/test.py,sha256=YrPYEaAACOGZ6btn2MV6NbMSEdBUcMWADLbQWaZSHtk,2149
|
| 39 |
+
accelerate/commands/to_fsdp2.py,sha256=gfbhoUT4qFB3LVDMNmckElgLG0yWm8aj_aofszeiJmM,5991
|
| 40 |
+
accelerate/commands/tpu.py,sha256=KyxDP7IuveidZrbW4rx2s8Ku3o_ptI6tzwr_R7ck0os,5548
|
| 41 |
+
accelerate/commands/utils.py,sha256=aT8xUCe2pCkFII7yZxcfaohEjgBAzMUM7WiD4UuWSOY,4150
|
| 42 |
+
accelerate/data_loader.py,sha256=yArisKhfuIJzDD7vuOgZAqEJNUC8tgl2L8ay92rgtfY,64551
|
| 43 |
+
accelerate/hooks.py,sha256=lYtYSIqEQnZOImgj2UMTngQPkcQDEHS2klwak1oHD6w,32248
|
| 44 |
+
accelerate/inference.py,sha256=NLANdzXm5PwmDWbPYkFmoRoQSLLvuhfvIG33xfpapT0,7668
|
| 45 |
+
accelerate/launchers.py,sha256=QIqUVkDc-oTmWf00L8kas7u2RBEwOYoRi8M2Our0DAs,13721
|
| 46 |
+
accelerate/local_sgd.py,sha256=aCj_yqXK_FhhZRWEpzXIkgXBERH6fC3HyrC3nsOj1uA,4160
|
| 47 |
+
accelerate/logging.py,sha256=4XcgY_BV7Qn_enh2tZ-8fNtuaE_3n-LsYJbgwhRx_PI,5042
|
| 48 |
+
accelerate/memory_utils.py,sha256=3R5LoeHl6GgTZ-IMPrDZMdaEehWarGdPqODushb-6pg,862
|
| 49 |
+
accelerate/optimizer.py,sha256=QfgCkQ5dA-fLSi_Z7CBPRCObXA1rL9zxHg4tyKCEg2A,8113
|
| 50 |
+
accelerate/scheduler.py,sha256=des_4M_Tt1W8gCYZZbLla0GHBEgJY3Wx2EGBQPTzeiY,4238
|
| 51 |
+
accelerate/state.py,sha256=YYpuPqXeNjz5_Y71h0zmCu13cBuDmQ8lw6fAmoSWUFk,55457
|
| 52 |
+
accelerate/test_utils/__init__.py,sha256=8xikmLMAM6_6CwVF6tsdsv4XzgWkHAk2tZBdV9DxIH8,1749
|
| 53 |
+
accelerate/test_utils/examples.py,sha256=IN4n2lxA95hexE2rojsyyjhpXLbXnbmjTzd8UTws5_4,7257
|
| 54 |
+
accelerate/test_utils/scripts/__init__.py,sha256=m1PPTDT4ziIAvM0-FDSgIMIZ69Konn126s6LwuzH6v8,606
|
| 55 |
+
accelerate/test_utils/scripts/external_deps/__init__.py,sha256=m1PPTDT4ziIAvM0-FDSgIMIZ69Konn126s6LwuzH6v8,606
|
| 56 |
+
accelerate/test_utils/scripts/external_deps/test_checkpointing.py,sha256=XHaNRmnrARd1izXFjWGi5UjYGas-4vqayW51jAHBPCA,10699
|
| 57 |
+
accelerate/test_utils/scripts/external_deps/test_ds_multiple_model.py,sha256=Cg4-h0B4UcOQ5CxXjIdrsPVR5fFsWCv24DqZGjXEwW8,13790
|
| 58 |
+
accelerate/test_utils/scripts/external_deps/test_metrics.py,sha256=Ev2XKaiwmznoxKujskAAuISGChW646MOiyf0CXEPb9Y,12168
|
| 59 |
+
accelerate/test_utils/scripts/external_deps/test_peak_memory_usage.py,sha256=9Yn9Rc7d-yWr1fU0RagASPG5l8vrKeHVYbuYABbA-fU,12498
|
| 60 |
+
accelerate/test_utils/scripts/external_deps/test_performance.py,sha256=Di6LT19bCBLlWmCBSu_jjdqR2EqngXpvUOGDBx8GfZE,10432
|
| 61 |
+
accelerate/test_utils/scripts/external_deps/test_pippy.py,sha256=ocZntbmAduln2ma4LeEA9o-S8hla3YXCJ_A8hEcWHgs,4762
|
| 62 |
+
accelerate/test_utils/scripts/external_deps/test_zero3_integration.py,sha256=P9alBOHZ9Lfqs5LoRP7bCbXl-tnsNrBkvJZGseibBeA,1665
|
| 63 |
+
accelerate/test_utils/scripts/test_cli.py,sha256=qfk1aYFtdvYFCYPkl05602SNGvk08QTv0xZVVcFVtzM,833
|
| 64 |
+
accelerate/test_utils/scripts/test_ddp_comm_hook.py,sha256=k_-2MBjLKNdMGIcneTbuGd84K05Wp1GEQX6DUVF9UBw,3566
|
| 65 |
+
accelerate/test_utils/scripts/test_distributed_data_loop.py,sha256=RUWTwd7DIpr2fl7JtKOsvTjMiJioTxO8FdSr2Lw_5uI,15137
|
| 66 |
+
accelerate/test_utils/scripts/test_merge_weights.py,sha256=dssMnAoZt291vNLbPhPOTQUooh0leg_0erQh0uZH6aU,6125
|
| 67 |
+
accelerate/test_utils/scripts/test_notebook.py,sha256=qfIy3IvH74-kGn8nadBn_k7qrviqvsxy5ijsnUhuY6o,3894
|
| 68 |
+
accelerate/test_utils/scripts/test_ops.py,sha256=Bcs-h8EMJwULTfbizlFN5qkv3JraWEpoSZWMn-HswiI,6265
|
| 69 |
+
accelerate/test_utils/scripts/test_script.py,sha256=8-53hIVQXD28HQT4h2Ijy6yGCHfTWDAf1-HOi4UtDng,34219
|
| 70 |
+
accelerate/test_utils/scripts/test_sync.py,sha256=PDe8sYZLCL2LKjj_L9b-Bh2BjAjeii9EZ8sZNfuYx5s,18817
|
| 71 |
+
accelerate/test_utils/testing.py,sha256=x9RK70VgAMyHlo5xut7P85j-9kdAnlfQe_4jwSPpMv4,27807
|
| 72 |
+
accelerate/test_utils/training.py,sha256=jO5YEIr34jAcnJ_9WNp_x3zuHzSam_I6IgMvmcGm7yI,6456
|
| 73 |
+
accelerate/tracking.py,sha256=ucpsoYAT3pVXgOfwDdXf6qTugY2-tk-EINvZtfmRitM,42756
|
| 74 |
+
accelerate/utils/__init__.py,sha256=wjpXyvFxS-ed3Stwm_IHIlBmsmP7KRyAljQ_Qss-OWw,7802
|
| 75 |
+
accelerate/utils/ao.py,sha256=koMiji7AG1kJMRMkJnwSnpuycfx4lPY3CNnpNx2ZqzM,4736
|
| 76 |
+
accelerate/utils/bnb.py,sha256=KCbg6LUt4eXvPHVnKh7rSVcPwDnzxY_Ii7yYmK5bNGw,20737
|
| 77 |
+
accelerate/utils/constants.py,sha256=hc24V0pgxWdBQwS6SXxDKwuIni2pCnzdfvMOX1XI9Os,3264
|
| 78 |
+
accelerate/utils/dataclasses.py,sha256=E7CnCbfskpzxzSorst95Via_XE39t0NP_UGYgJUris0,131486
|
| 79 |
+
accelerate/utils/deepspeed.py,sha256=QYIXv5LwHXw7wBFFo-7a0t86MbwNAfieJkkBaLGA6wI,14064
|
| 80 |
+
accelerate/utils/environment.py,sha256=h0zacbBkAp9szltTf5-aTr5NcbVsQp7wl6DFWp8XNuI,15257
|
| 81 |
+
accelerate/utils/fsdp_utils.py,sha256=Q2tc9EakwBjuYlyXvQrBLV97r6cdReRft6KeS1P_Vb4,28938
|
| 82 |
+
accelerate/utils/imports.py,sha256=YI1ebPJAuxarclENTfzvDPPGf6jeEKnVQ42taFPuqh0,16759
|
| 83 |
+
accelerate/utils/launch.py,sha256=nN4ykAtnEL3oITLTejABltdpS3OivcE2COmX-BnWuY4,31195
|
| 84 |
+
accelerate/utils/megatron_lm.py,sha256=FnIF-niZjvdMk9ymafZWEPjDho_Q_P98C69qc9g5r_E,58059
|
| 85 |
+
accelerate/utils/memory.py,sha256=lDHqW7Ue_CPmw_DWgNxX_B3HY71_srAFdgR10XiVRSM,6960
|
| 86 |
+
accelerate/utils/modeling.py,sha256=_xSTiH7zSsffZULSTJuzcDK6IaWImEMOcbq1xqeI7GY,92319
|
| 87 |
+
accelerate/utils/offload.py,sha256=VFaL8oSJzqZ_47VuUQ69xZi9bF2heRSFoOSnnOxbGXc,7825
|
| 88 |
+
accelerate/utils/operations.py,sha256=VWPYvtrO4UGX5JmisanXzLLUbhAeL8kQk0yYc66bQ2M,31055
|
| 89 |
+
accelerate/utils/other.py,sha256=iiLZcKEAlK2Sj_wt03gAEGKrk7_NZFwbmy9cgEppRPw,13231
|
| 90 |
+
accelerate/utils/random.py,sha256=Xv_ZJm9eaC2Q7rgZy9OpOunKuTingMiDQCH00qhNVxE,6220
|
| 91 |
+
accelerate/utils/rich.py,sha256=8JZX_uGMQX-BufdXxJpdne7BWd1KyLHSgbiGxrDMYr8,847
|
| 92 |
+
accelerate/utils/torch_xla.py,sha256=Pq1tuqN0X_pWDVza6YgjfO45uoJdoRVRForLeLQzFus,1908
|
| 93 |
+
accelerate/utils/tqdm.py,sha256=k8e9JnieTEQHCCNBaiBys7hPxWlEbyRASdIma-qy_X8,1657
|
| 94 |
+
accelerate/utils/transformer_engine.py,sha256=498Y3z2BkbybYLtBiuF_TJgt8Iii943s4wgRAV8FDC4,6372
|
| 95 |
+
accelerate/utils/versions.py,sha256=UgmcbjBm--6CIx1ZamSAMjAK_B_2l48LbeaNygqej8M,2149
|
venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/REQUESTED
ADDED
|
File without changes
|
venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (75.1.0)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
accelerate = accelerate.commands.accelerate_cli:main
|
| 3 |
+
accelerate-config = accelerate.commands.config:main
|
| 4 |
+
accelerate-estimate-memory = accelerate.commands.estimate:main
|
| 5 |
+
accelerate-launch = accelerate.commands.launch:main
|
| 6 |
+
accelerate-merge-weights = accelerate.commands.merge:main
|
venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
accelerate
|
venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_ddp_comm_hook.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
import torch
|
| 15 |
+
|
| 16 |
+
from accelerate import Accelerator, DDPCommunicationHookType, DistributedDataParallelKwargs, PartialState
|
| 17 |
+
from accelerate.utils import is_hpu_available
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class MockModel(torch.nn.Module):
|
| 21 |
+
def __init__(self):
|
| 22 |
+
super().__init__()
|
| 23 |
+
torch.manual_seed(0)
|
| 24 |
+
self.p = torch.nn.Parameter(torch.randn(40, 20))
|
| 25 |
+
|
| 26 |
+
def forward(self, x, rank):
|
| 27 |
+
return self.p * (x ** (1 + rank))
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _run_and_get_grads(model, rank):
|
| 31 |
+
torch.manual_seed(2024)
|
| 32 |
+
input = torch.randn(40, 20)
|
| 33 |
+
output = model(input, rank)
|
| 34 |
+
output.mean().backward()
|
| 35 |
+
param = next(model.parameters())
|
| 36 |
+
return param.grad
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_ddp_comm_hook(comm_hook, comm_wrapper, comm_state_option):
|
| 40 |
+
ddp_kwargs = DistributedDataParallelKwargs(
|
| 41 |
+
comm_hook=comm_hook,
|
| 42 |
+
comm_wrapper=comm_wrapper,
|
| 43 |
+
comm_state_option=comm_state_option,
|
| 44 |
+
)
|
| 45 |
+
accelerator = Accelerator(kwargs_handlers=[ddp_kwargs])
|
| 46 |
+
|
| 47 |
+
model = accelerator.prepare(MockModel())
|
| 48 |
+
hook_grads = _run_and_get_grads(model, accelerator.local_process_index)
|
| 49 |
+
|
| 50 |
+
reference_model = torch.nn.parallel.DistributedDataParallel(
|
| 51 |
+
MockModel().to(accelerator.device),
|
| 52 |
+
device_ids=[accelerator.local_process_index],
|
| 53 |
+
output_device=accelerator.local_process_index,
|
| 54 |
+
)
|
| 55 |
+
reference_grads = _run_and_get_grads(reference_model, accelerator.local_process_index)
|
| 56 |
+
|
| 57 |
+
torch.testing.assert_close(hook_grads, reference_grads, rtol=1e-2, atol=1e-2)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def main():
|
| 61 |
+
for comm_hook, comm_wrapper, comm_state_option in [
|
| 62 |
+
(DDPCommunicationHookType.NO, DDPCommunicationHookType.NO, {}),
|
| 63 |
+
(DDPCommunicationHookType.FP16, DDPCommunicationHookType.NO, {}),
|
| 64 |
+
(DDPCommunicationHookType.BF16, DDPCommunicationHookType.NO, {}),
|
| 65 |
+
(DDPCommunicationHookType.POWER_SGD, DDPCommunicationHookType.NO, {}),
|
| 66 |
+
(DDPCommunicationHookType.POWER_SGD, DDPCommunicationHookType.FP16, {}),
|
| 67 |
+
(DDPCommunicationHookType.POWER_SGD, DDPCommunicationHookType.BF16, {}),
|
| 68 |
+
(DDPCommunicationHookType.POWER_SGD, DDPCommunicationHookType.NO, {"matrix_approximation_rank": 2}),
|
| 69 |
+
(DDPCommunicationHookType.BATCHED_POWER_SGD, DDPCommunicationHookType.NO, {}),
|
| 70 |
+
(DDPCommunicationHookType.BATCHED_POWER_SGD, DDPCommunicationHookType.FP16, {}),
|
| 71 |
+
(DDPCommunicationHookType.BATCHED_POWER_SGD, DDPCommunicationHookType.BF16, {}),
|
| 72 |
+
]:
|
| 73 |
+
if is_hpu_available():
|
| 74 |
+
HPU_UNSUPPORTED_COMM_HOOKS = {DDPCommunicationHookType.FP16, DDPCommunicationHookType.BF16}
|
| 75 |
+
if comm_hook in HPU_UNSUPPORTED_COMM_HOOKS or comm_wrapper in HPU_UNSUPPORTED_COMM_HOOKS:
|
| 76 |
+
print(f"Skipping test DDP comm hook: {comm_hook}, comm wrapper: {comm_wrapper} on HPU")
|
| 77 |
+
continue
|
| 78 |
+
|
| 79 |
+
print(f"Test DDP comm hook: {comm_hook}, comm wrapper: {comm_wrapper}")
|
| 80 |
+
test_ddp_comm_hook(comm_hook, comm_wrapper, comm_state_option)
|
| 81 |
+
PartialState().destroy_process_group()
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
if __name__ == "__main__":
|
| 85 |
+
main()
|
venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_distributed_data_loop.py
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
|
| 3 |
+
# Copyright 2021 The HuggingFace Team. All rights reserved.
|
| 4 |
+
#
|
| 5 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 6 |
+
# you may not use this file except in compliance with the License.
|
| 7 |
+
# You may obtain a copy of the License at
|
| 8 |
+
#
|
| 9 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 10 |
+
#
|
| 11 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 12 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 13 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 14 |
+
# See the License for the specific language governing permissions and
|
| 15 |
+
# limitations under the License.
|
| 16 |
+
|
| 17 |
+
import pickle
|
| 18 |
+
import tempfile
|
| 19 |
+
import warnings
|
| 20 |
+
from unittest.mock import Mock
|
| 21 |
+
|
| 22 |
+
import torch
|
| 23 |
+
from torch.utils.data import (
|
| 24 |
+
BatchSampler,
|
| 25 |
+
DataLoader,
|
| 26 |
+
Dataset,
|
| 27 |
+
IterableDataset,
|
| 28 |
+
RandomSampler,
|
| 29 |
+
TensorDataset,
|
| 30 |
+
default_collate,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
from accelerate.accelerator import Accelerator, DataLoaderConfiguration
|
| 34 |
+
from accelerate.utils.dataclasses import DistributedType
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
NUM_ELEMENTS = 22
|
| 38 |
+
NUM_WORKERS = 4
|
| 39 |
+
BATCH_SIZE = 4
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class DummyDataset(Dataset):
|
| 43 |
+
def __len__(self):
|
| 44 |
+
return NUM_ELEMENTS
|
| 45 |
+
|
| 46 |
+
def __getitem__(self, index):
|
| 47 |
+
squeeze = False
|
| 48 |
+
|
| 49 |
+
if isinstance(index, int):
|
| 50 |
+
index = [index]
|
| 51 |
+
squeeze = True
|
| 52 |
+
elif isinstance(index, slice):
|
| 53 |
+
index = list(range(*index.indices(self.size)))
|
| 54 |
+
else:
|
| 55 |
+
index = list(index)
|
| 56 |
+
|
| 57 |
+
batch = [{"index": i, "label": i % 2, "random_augmentation": torch.rand(1).item()} for i in index]
|
| 58 |
+
|
| 59 |
+
if squeeze:
|
| 60 |
+
batch = batch[0]
|
| 61 |
+
|
| 62 |
+
return batch
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class DummyIterableDataset(IterableDataset):
|
| 66 |
+
def __init__(self, data):
|
| 67 |
+
self.data = data
|
| 68 |
+
|
| 69 |
+
def __iter__(self):
|
| 70 |
+
yield from self.data
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def create_accelerator(even_batches=True):
|
| 74 |
+
dataloader_config = DataLoaderConfiguration(even_batches=even_batches)
|
| 75 |
+
accelerator = Accelerator(dataloader_config=dataloader_config)
|
| 76 |
+
assert accelerator.num_processes == 2, "this script expects that two GPUs are available"
|
| 77 |
+
return accelerator
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def create_dataloader(
|
| 81 |
+
accelerator: Accelerator, dataset_size: int, batch_size: int, iterable: bool = False, shuffle: bool = False
|
| 82 |
+
):
|
| 83 |
+
"""
|
| 84 |
+
Create a simple DataLoader to use during the test cases
|
| 85 |
+
"""
|
| 86 |
+
values = torch.as_tensor(range(dataset_size))
|
| 87 |
+
if shuffle:
|
| 88 |
+
values = values[torch.randperm(values.size(0))]
|
| 89 |
+
if iterable:
|
| 90 |
+
dataset = DummyIterableDataset(values)
|
| 91 |
+
else:
|
| 92 |
+
dataset = TensorDataset(torch.as_tensor(range(dataset_size)))
|
| 93 |
+
|
| 94 |
+
dl = DataLoader(dataset, batch_size=batch_size)
|
| 95 |
+
dl = accelerator.prepare(dl)
|
| 96 |
+
|
| 97 |
+
return dl
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def verify_dataloader_batch_sizes(
|
| 101 |
+
accelerator: Accelerator,
|
| 102 |
+
dataset_size: int,
|
| 103 |
+
batch_size: int,
|
| 104 |
+
process_0_expected_batch_sizes: list[int],
|
| 105 |
+
process_1_expected_batch_sizes: list[int],
|
| 106 |
+
):
|
| 107 |
+
"""
|
| 108 |
+
A helper function for verifying the batch sizes coming from a prepared dataloader in each process
|
| 109 |
+
"""
|
| 110 |
+
dl = create_dataloader(accelerator=accelerator, dataset_size=dataset_size, batch_size=batch_size)
|
| 111 |
+
|
| 112 |
+
batch_sizes = [len(batch[0]) for batch in dl]
|
| 113 |
+
|
| 114 |
+
if accelerator.process_index == 0:
|
| 115 |
+
assert batch_sizes == process_0_expected_batch_sizes
|
| 116 |
+
elif accelerator.process_index == 1:
|
| 117 |
+
assert batch_sizes == process_1_expected_batch_sizes
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def test_default_ensures_even_batch_sizes():
|
| 121 |
+
accelerator = create_accelerator()
|
| 122 |
+
|
| 123 |
+
# without padding, we would expect a different number of batches
|
| 124 |
+
verify_dataloader_batch_sizes(
|
| 125 |
+
accelerator,
|
| 126 |
+
dataset_size=3,
|
| 127 |
+
batch_size=1,
|
| 128 |
+
process_0_expected_batch_sizes=[1, 1],
|
| 129 |
+
process_1_expected_batch_sizes=[1, 1],
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
# without padding, we would expect the same number of batches, but different sizes
|
| 133 |
+
verify_dataloader_batch_sizes(
|
| 134 |
+
accelerator,
|
| 135 |
+
dataset_size=7,
|
| 136 |
+
batch_size=2,
|
| 137 |
+
process_0_expected_batch_sizes=[2, 2],
|
| 138 |
+
process_1_expected_batch_sizes=[2, 2],
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def test_can_disable_even_batches():
|
| 143 |
+
accelerator = create_accelerator(even_batches=False)
|
| 144 |
+
|
| 145 |
+
verify_dataloader_batch_sizes(
|
| 146 |
+
accelerator,
|
| 147 |
+
dataset_size=3,
|
| 148 |
+
batch_size=1,
|
| 149 |
+
process_0_expected_batch_sizes=[1, 1],
|
| 150 |
+
process_1_expected_batch_sizes=[1],
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
verify_dataloader_batch_sizes(
|
| 154 |
+
accelerator,
|
| 155 |
+
dataset_size=7,
|
| 156 |
+
batch_size=2,
|
| 157 |
+
process_0_expected_batch_sizes=[2, 2],
|
| 158 |
+
process_1_expected_batch_sizes=[2, 1],
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def test_can_join_uneven_inputs():
|
| 163 |
+
accelerator = create_accelerator(even_batches=False)
|
| 164 |
+
|
| 165 |
+
model = torch.nn.Linear(1, 1)
|
| 166 |
+
ddp_model = accelerator.prepare(model)
|
| 167 |
+
|
| 168 |
+
dl = create_dataloader(accelerator, dataset_size=3, batch_size=1)
|
| 169 |
+
|
| 170 |
+
batch_idxs = []
|
| 171 |
+
with accelerator.join_uneven_inputs([ddp_model]):
|
| 172 |
+
for batch_idx, batch in enumerate(dl):
|
| 173 |
+
output = ddp_model(batch[0].float())
|
| 174 |
+
loss = output.sum()
|
| 175 |
+
loss.backward()
|
| 176 |
+
batch_idxs.append(batch_idx)
|
| 177 |
+
|
| 178 |
+
accelerator.wait_for_everyone()
|
| 179 |
+
|
| 180 |
+
if accelerator.process_index == 0:
|
| 181 |
+
assert batch_idxs == [0, 1]
|
| 182 |
+
elif accelerator.process_index == 1:
|
| 183 |
+
assert batch_idxs == [0]
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def test_join_raises_warning_for_non_ddp_distributed(accelerator):
|
| 187 |
+
with warnings.catch_warnings(record=True) as w:
|
| 188 |
+
with accelerator.join_uneven_inputs([Mock()]):
|
| 189 |
+
pass
|
| 190 |
+
|
| 191 |
+
assert issubclass(w[-1].category, UserWarning)
|
| 192 |
+
assert "only supported for multi-GPU" in str(w[-1].message)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def test_join_can_override_even_batches():
|
| 196 |
+
default_even_batches = True
|
| 197 |
+
overridden_even_batches = False
|
| 198 |
+
accelerator = create_accelerator(even_batches=default_even_batches)
|
| 199 |
+
model = torch.nn.Linear(1, 1)
|
| 200 |
+
ddp_model = accelerator.prepare(model)
|
| 201 |
+
train_dl = create_dataloader(accelerator, dataset_size=3, batch_size=1)
|
| 202 |
+
valid_dl = create_dataloader(accelerator, dataset_size=3, batch_size=1)
|
| 203 |
+
|
| 204 |
+
with accelerator.join_uneven_inputs([ddp_model], even_batches=overridden_even_batches):
|
| 205 |
+
train_dl_overridden_value = train_dl.batch_sampler.even_batches
|
| 206 |
+
valid_dl_overridden_value = valid_dl.batch_sampler.even_batches
|
| 207 |
+
|
| 208 |
+
assert train_dl_overridden_value == overridden_even_batches
|
| 209 |
+
assert valid_dl_overridden_value == overridden_even_batches
|
| 210 |
+
assert train_dl.batch_sampler.even_batches == default_even_batches
|
| 211 |
+
assert valid_dl.batch_sampler.even_batches == default_even_batches
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def test_join_can_override_for_mixed_type_dataloaders():
|
| 215 |
+
default_even_batches = True
|
| 216 |
+
overridden_even_batches = False
|
| 217 |
+
accelerator = create_accelerator(even_batches=default_even_batches)
|
| 218 |
+
model = torch.nn.Linear(1, 1)
|
| 219 |
+
ddp_model = accelerator.prepare(model)
|
| 220 |
+
create_dataloader(accelerator, dataset_size=3, batch_size=1, iterable=True)
|
| 221 |
+
batch_dl = create_dataloader(accelerator, dataset_size=3, batch_size=1)
|
| 222 |
+
|
| 223 |
+
with warnings.catch_warnings():
|
| 224 |
+
warnings.filterwarnings("ignore")
|
| 225 |
+
try:
|
| 226 |
+
with accelerator.join_uneven_inputs([ddp_model], even_batches=overridden_even_batches):
|
| 227 |
+
batch_dl_overridden_value = batch_dl.batch_sampler.even_batches
|
| 228 |
+
except AttributeError:
|
| 229 |
+
# ensure attribute error is not raised when processing iterable dl
|
| 230 |
+
raise AssertionError
|
| 231 |
+
|
| 232 |
+
assert batch_dl_overridden_value == overridden_even_batches
|
| 233 |
+
assert batch_dl.batch_sampler.even_batches == default_even_batches
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
def test_join_raises_warning_for_iterable_when_overriding_even_batches():
|
| 237 |
+
accelerator = create_accelerator()
|
| 238 |
+
model = torch.nn.Linear(1, 1)
|
| 239 |
+
ddp_model = accelerator.prepare(model)
|
| 240 |
+
create_dataloader(accelerator, dataset_size=3, batch_size=1, iterable=True)
|
| 241 |
+
|
| 242 |
+
with warnings.catch_warnings(record=True) as w:
|
| 243 |
+
with accelerator.join_uneven_inputs([ddp_model], even_batches=False):
|
| 244 |
+
pass
|
| 245 |
+
|
| 246 |
+
assert issubclass(w[-1].category, UserWarning)
|
| 247 |
+
assert "only supported for map-style datasets" in str(w[-1].message)
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def test_pickle_accelerator():
|
| 251 |
+
accelerator = create_accelerator()
|
| 252 |
+
data_loader = create_dataloader(accelerator, dataset_size=32, batch_size=4)
|
| 253 |
+
_ = accelerator.prepare(data_loader)
|
| 254 |
+
pickled_accelerator = pickle.dumps(accelerator)
|
| 255 |
+
unpickled_accelerator = pickle.loads(pickled_accelerator)
|
| 256 |
+
# TODO: Maybe this should be implemented as __eq__ for AcceleratorState?
|
| 257 |
+
assert accelerator.state.__dict__ == unpickled_accelerator.state.__dict__
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
def test_data_loader(data_loader, accelerator):
|
| 261 |
+
# Prepare the DataLoader
|
| 262 |
+
data_loader = accelerator.prepare(data_loader)
|
| 263 |
+
|
| 264 |
+
all_examples = []
|
| 265 |
+
for i, batch in enumerate(data_loader):
|
| 266 |
+
index, _ = accelerator.gather_for_metrics((batch["index"], batch["label"]))
|
| 267 |
+
all_examples.extend(index.detach().cpu().numpy().tolist())
|
| 268 |
+
|
| 269 |
+
# Sort the examples
|
| 270 |
+
sorted_all_examples = sorted(all_examples)
|
| 271 |
+
|
| 272 |
+
# Check if all elements are present in the sorted list of iterated samples
|
| 273 |
+
assert len(set(sorted_all_examples)) == NUM_ELEMENTS, (
|
| 274 |
+
"Not all the dataset elements have been iterated in an epoch due to duplication of samples across processes."
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def test_stateful_dataloader(accelerator):
|
| 279 |
+
"""
|
| 280 |
+
Tests that a stateful dataloader can be iterated over, saved after a few batches using `load_state_dict`, and then
|
| 281 |
+
resumed from the saved state.
|
| 282 |
+
|
| 283 |
+
The result should be the same as the rest of the data that iterated over after saving.
|
| 284 |
+
"""
|
| 285 |
+
old_dataloader_config = accelerator.dataloader_config
|
| 286 |
+
try:
|
| 287 |
+
accelerator.dataloader_config = DataLoaderConfiguration(use_stateful_dataloader=True)
|
| 288 |
+
prepared_dl = create_dataloader(
|
| 289 |
+
accelerator, dataset_size=32 * accelerator.num_processes, batch_size=4, iterable=True, shuffle=True
|
| 290 |
+
)
|
| 291 |
+
untrained_batches = []
|
| 292 |
+
# Calculate what step that will be
|
| 293 |
+
total_batches = 32 * accelerator.num_processes // (4 * accelerator.num_processes)
|
| 294 |
+
last_batch_num = total_batches - 1
|
| 295 |
+
for step, batch in enumerate(prepared_dl):
|
| 296 |
+
# Step just before
|
| 297 |
+
if step == last_batch_num - 1:
|
| 298 |
+
state_dict = prepared_dl.state_dict()
|
| 299 |
+
if step >= last_batch_num:
|
| 300 |
+
# Otherwise grab the "unseen" batches
|
| 301 |
+
untrained_batches.append(batch)
|
| 302 |
+
not_skipped_batches = accelerator.gather(untrained_batches)
|
| 303 |
+
prepared_dl.load_state_dict(state_dict)
|
| 304 |
+
resumed_batches = []
|
| 305 |
+
for batch in prepared_dl:
|
| 306 |
+
resumed_batches.append(batch)
|
| 307 |
+
resumed_batches = accelerator.gather(resumed_batches)
|
| 308 |
+
for b1, b2 in zip(not_skipped_batches, resumed_batches):
|
| 309 |
+
for v1, v2 in zip(b1, b2):
|
| 310 |
+
assert torch.equal(v1, v2), f"Batch {b1} and {b2} are not equal"
|
| 311 |
+
finally:
|
| 312 |
+
accelerator.dataloader_config = old_dataloader_config
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def test_stateful_dataloader_save_state(accelerator):
|
| 316 |
+
"""
|
| 317 |
+
Tests that a stateful dataloader can be iterated over, saved after a few batches using `Accelerator.save_state`,
|
| 318 |
+
and then resumed from the saved state.
|
| 319 |
+
|
| 320 |
+
The result should be the same as the rest of the data that iterated over after saving.
|
| 321 |
+
"""
|
| 322 |
+
old_dataloader_config = accelerator.dataloader_config
|
| 323 |
+
try:
|
| 324 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 325 |
+
accelerator.dataloader_config = DataLoaderConfiguration(use_stateful_dataloader=True)
|
| 326 |
+
prepared_dl = create_dataloader(
|
| 327 |
+
accelerator, dataset_size=32 * accelerator.num_processes, batch_size=4, iterable=True, shuffle=True
|
| 328 |
+
)
|
| 329 |
+
untrained_batches = []
|
| 330 |
+
# Calculate what step that will be
|
| 331 |
+
total_batches = 32 * accelerator.num_processes // (4 * accelerator.num_processes)
|
| 332 |
+
last_batch_num = total_batches - 1
|
| 333 |
+
for step, batch in enumerate(prepared_dl):
|
| 334 |
+
# Step just before
|
| 335 |
+
if step == last_batch_num - 1:
|
| 336 |
+
accelerator.save_state(tmpdir)
|
| 337 |
+
if step >= last_batch_num:
|
| 338 |
+
# Otherwise grab the "unseen" batches
|
| 339 |
+
untrained_batches.append(batch)
|
| 340 |
+
not_skipped_batches = accelerator.gather(untrained_batches)
|
| 341 |
+
accelerator.load_state(tmpdir)
|
| 342 |
+
resumed_batches = []
|
| 343 |
+
for batch in prepared_dl:
|
| 344 |
+
resumed_batches.append(batch)
|
| 345 |
+
resumed_batches = accelerator.gather(resumed_batches)
|
| 346 |
+
for b1, b2 in zip(not_skipped_batches, resumed_batches):
|
| 347 |
+
for v1, v2 in zip(b1, b2):
|
| 348 |
+
assert torch.equal(v1, v2), f"Batch {b1} and {b2} are not equal"
|
| 349 |
+
finally:
|
| 350 |
+
accelerator.dataloader_config = old_dataloader_config
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def main():
|
| 354 |
+
accelerator = create_accelerator()
|
| 355 |
+
torch.manual_seed(accelerator.process_index)
|
| 356 |
+
|
| 357 |
+
accelerator.print("Test that even_batches variable ensures uniform batches across processes")
|
| 358 |
+
test_default_ensures_even_batch_sizes()
|
| 359 |
+
|
| 360 |
+
accelerator.print("Run tests with even_batches disabled")
|
| 361 |
+
test_can_disable_even_batches()
|
| 362 |
+
|
| 363 |
+
accelerator.print("Test joining uneven inputs")
|
| 364 |
+
test_can_join_uneven_inputs()
|
| 365 |
+
|
| 366 |
+
accelerator.print("Test overriding even_batches when joining uneven inputs")
|
| 367 |
+
test_join_can_override_even_batches()
|
| 368 |
+
|
| 369 |
+
accelerator.print("Test overriding even_batches for mixed dataloader types")
|
| 370 |
+
test_join_can_override_for_mixed_type_dataloaders()
|
| 371 |
+
|
| 372 |
+
accelerator.print("Test overriding even_batches raises a warning for iterable dataloaders")
|
| 373 |
+
test_join_raises_warning_for_iterable_when_overriding_even_batches()
|
| 374 |
+
|
| 375 |
+
accelerator.print("Test join with non DDP distributed raises warning")
|
| 376 |
+
original_state = accelerator.state.distributed_type
|
| 377 |
+
accelerator.state.distributed_type = DistributedType.FSDP
|
| 378 |
+
test_join_raises_warning_for_non_ddp_distributed(accelerator)
|
| 379 |
+
accelerator.state.distributed_type = original_state
|
| 380 |
+
|
| 381 |
+
accelerator.print("Test pickling an accelerator")
|
| 382 |
+
test_pickle_accelerator()
|
| 383 |
+
|
| 384 |
+
dataset = DummyDataset()
|
| 385 |
+
|
| 386 |
+
accelerator.print("Test DataLoader with shuffle=False")
|
| 387 |
+
loader = DataLoader(dataset, shuffle=False, batch_size=BATCH_SIZE, num_workers=NUM_WORKERS)
|
| 388 |
+
test_data_loader(loader, accelerator)
|
| 389 |
+
|
| 390 |
+
accelerator.print("Test DataLoader with shuffle=True")
|
| 391 |
+
loader = DataLoader(dataset, shuffle=True, batch_size=BATCH_SIZE, num_workers=NUM_WORKERS)
|
| 392 |
+
test_data_loader(loader, accelerator)
|
| 393 |
+
|
| 394 |
+
accelerator.print("Test DataLoader with batch_sampler")
|
| 395 |
+
sampler = BatchSampler(RandomSampler(dataset), batch_size=BATCH_SIZE, drop_last=False)
|
| 396 |
+
loader = DataLoader(dataset, batch_sampler=sampler, num_workers=NUM_WORKERS)
|
| 397 |
+
test_data_loader(loader, accelerator)
|
| 398 |
+
|
| 399 |
+
accelerator.print("Test DataLoader with sampler as an instance of `BatchSampler`")
|
| 400 |
+
sampler = BatchSampler(RandomSampler(dataset), batch_size=BATCH_SIZE, drop_last=False)
|
| 401 |
+
loader = DataLoader(dataset, sampler=sampler, batch_size=None, collate_fn=default_collate, num_workers=NUM_WORKERS)
|
| 402 |
+
test_data_loader(loader, accelerator)
|
| 403 |
+
test_stateful_dataloader(accelerator)
|
| 404 |
+
test_stateful_dataloader_save_state(accelerator)
|
| 405 |
+
|
| 406 |
+
accelerator.end_training()
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
if __name__ == "__main__":
|
| 410 |
+
main()
|
venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_merge_weights.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import gc
|
| 16 |
+
import logging
|
| 17 |
+
import shutil
|
| 18 |
+
from pathlib import Path
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
from safetensors.torch import load_file
|
| 22 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import ShardingStrategy, StateDictType
|
| 23 |
+
from torch.utils.data import DataLoader
|
| 24 |
+
|
| 25 |
+
from accelerate import Accelerator, FullyShardedDataParallelPlugin
|
| 26 |
+
from accelerate.commands.merge import merge_command, merge_command_parser
|
| 27 |
+
from accelerate.state import AcceleratorState
|
| 28 |
+
from accelerate.test_utils import torch_device
|
| 29 |
+
from accelerate.test_utils.training import RegressionDataset
|
| 30 |
+
from accelerate.utils import merge_fsdp_weights, patch_environment, save_fsdp_model
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
logging.basicConfig(level=logging.INFO)
|
| 34 |
+
|
| 35 |
+
parser = merge_command_parser()
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class TinyModel(torch.nn.Module):
|
| 39 |
+
def __init__(self):
|
| 40 |
+
super().__init__()
|
| 41 |
+
self.linear1 = torch.nn.Linear(16, 16)
|
| 42 |
+
self.activation = torch.nn.ReLU()
|
| 43 |
+
self.linear2 = torch.nn.Linear(16, 16)
|
| 44 |
+
self.softmax = torch.nn.Softmax()
|
| 45 |
+
|
| 46 |
+
def forward(self, x):
|
| 47 |
+
return self.linear2(self.activation(self.linear1(x)))
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def setup():
|
| 51 |
+
if AcceleratorState._shared_state != {}:
|
| 52 |
+
AcceleratorState()._reset_state()
|
| 53 |
+
plugin = FullyShardedDataParallelPlugin(
|
| 54 |
+
sharding_strategy=ShardingStrategy.FULL_SHARD, state_dict_type=StateDictType.SHARDED_STATE_DICT
|
| 55 |
+
)
|
| 56 |
+
model = TinyModel()
|
| 57 |
+
with patch_environment(fsdp_auto_wrap_policy="SIZE_BASED_WRAP"):
|
| 58 |
+
plugin.set_auto_wrap_policy(model)
|
| 59 |
+
accelerator = Accelerator(fsdp_plugin=plugin)
|
| 60 |
+
model = accelerator.prepare(model)
|
| 61 |
+
return model, plugin, accelerator
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def mock_training(accelerator, model):
|
| 65 |
+
train_set = RegressionDataset(length=128, seed=42)
|
| 66 |
+
train_dl = DataLoader(train_set, batch_size=16, shuffle=False)
|
| 67 |
+
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
|
| 68 |
+
|
| 69 |
+
train_dl, model, optimizer = accelerator.prepare(train_dl, model, optimizer)
|
| 70 |
+
for _ in range(3):
|
| 71 |
+
for batch in train_dl:
|
| 72 |
+
model.zero_grad()
|
| 73 |
+
output = model(batch["x"])
|
| 74 |
+
loss = torch.nn.functional.mse_loss(output, batch["y"])
|
| 75 |
+
accelerator.backward(loss)
|
| 76 |
+
optimizer.step()
|
| 77 |
+
return model
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def check_weights(operation, state_1, state_2):
|
| 81 |
+
for weight_1, weight_2 in zip(state_1.values(), state_2.values()):
|
| 82 |
+
if str(weight_1.device) != torch_device:
|
| 83 |
+
weight_1 = weight_1.to(torch_device)
|
| 84 |
+
if str(weight_2.device) != torch_device:
|
| 85 |
+
weight_2 = weight_2.to(torch_device)
|
| 86 |
+
if operation == "same":
|
| 87 |
+
assert torch.allclose(weight_1, weight_2)
|
| 88 |
+
else:
|
| 89 |
+
assert not torch.allclose(weight_1, weight_2)
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def check_safetensors_weights(path, model):
|
| 93 |
+
safe_state_dict = load_file(path / "model.safetensors")
|
| 94 |
+
safe_loaded_model = TinyModel()
|
| 95 |
+
check_weights("diff", model.state_dict(), safe_loaded_model.state_dict())
|
| 96 |
+
safe_loaded_model.load_state_dict(safe_state_dict)
|
| 97 |
+
check_weights("same", model.state_dict(), safe_loaded_model.state_dict())
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def check_pytorch_weights(path, model):
|
| 101 |
+
nonsafe_state_dict = torch.load(path / "pytorch_model.bin")
|
| 102 |
+
nonsafe_loaded_model = TinyModel()
|
| 103 |
+
check_weights("diff", model.state_dict(), nonsafe_loaded_model.state_dict())
|
| 104 |
+
nonsafe_loaded_model.load_state_dict(nonsafe_state_dict)
|
| 105 |
+
check_weights("same", model.state_dict(), nonsafe_loaded_model.state_dict())
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def test_merge_weights_safetensors(model, path):
|
| 109 |
+
# Should now be saved at `path/merged.safetensors`
|
| 110 |
+
merge_fsdp_weights(path / "pytorch_model_fsdp_0", path, safe_serialization=True)
|
| 111 |
+
check_safetensors_weights(path, model)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def test_merge_weights_command_safetensors(model, path):
|
| 115 |
+
args = parser.parse_args([str(path / "pytorch_model_fsdp_0"), str(path)])
|
| 116 |
+
merge_command(args)
|
| 117 |
+
check_safetensors_weights(path, model)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def test_merge_weights_pytorch(model, path):
|
| 121 |
+
# Should now be saved at `path/merged.bin`
|
| 122 |
+
merge_fsdp_weights(path / "pytorch_model_fsdp_0", path, safe_serialization=False)
|
| 123 |
+
check_pytorch_weights(path, model)
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def test_merge_weights_command_pytorch(model, path):
|
| 127 |
+
args = parser.parse_args([str(path / "pytorch_model_fsdp_0"), str(path), "--unsafe_serialization"])
|
| 128 |
+
merge_command(args)
|
| 129 |
+
check_pytorch_weights(path, model)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
if __name__ == "__main__":
|
| 133 |
+
# Note this test requires at least two accelerators!
|
| 134 |
+
model, plugin, accelerator = setup()
|
| 135 |
+
if accelerator.num_processes > 1:
|
| 136 |
+
try:
|
| 137 |
+
# Initial setup for things
|
| 138 |
+
out_path = Path("test_merge_weights_fsdp_weights")
|
| 139 |
+
if not out_path.exists():
|
| 140 |
+
out_path.mkdir(parents=True, exist_ok=True)
|
| 141 |
+
|
| 142 |
+
# Train briefly once weights aren't the baseline
|
| 143 |
+
model = mock_training(accelerator, model)
|
| 144 |
+
accelerator.wait_for_everyone()
|
| 145 |
+
|
| 146 |
+
gc.collect() # Needed for some lingering refs after training
|
| 147 |
+
save_fsdp_model(plugin, accelerator, model, out_path)
|
| 148 |
+
accelerator.wait_for_everyone()
|
| 149 |
+
|
| 150 |
+
# Finally we can test
|
| 151 |
+
test_merge_weights_safetensors(model, out_path)
|
| 152 |
+
test_merge_weights_command_safetensors(model, out_path)
|
| 153 |
+
test_merge_weights_pytorch(model, out_path)
|
| 154 |
+
test_merge_weights_command_pytorch(model, out_path)
|
| 155 |
+
except Exception:
|
| 156 |
+
raise
|
| 157 |
+
finally:
|
| 158 |
+
# Cleanup in case of any failures
|
| 159 |
+
if accelerator.is_main_process:
|
| 160 |
+
shutil.rmtree(out_path)
|
| 161 |
+
accelerator.wait_for_everyone()
|
| 162 |
+
accelerator.end_training()
|
venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_notebook.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""
|
| 15 |
+
Test file to ensure that in general certain situational setups for notebooks work.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
import os
|
| 19 |
+
import time
|
| 20 |
+
from multiprocessing import Queue
|
| 21 |
+
|
| 22 |
+
from pytest import mark, raises
|
| 23 |
+
from torch.distributed.elastic.multiprocessing.errors import ChildFailedError
|
| 24 |
+
|
| 25 |
+
from accelerate import PartialState, notebook_launcher
|
| 26 |
+
from accelerate.test_utils import require_bnb
|
| 27 |
+
from accelerate.utils import is_bnb_available
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def basic_function():
|
| 31 |
+
# Just prints the PartialState
|
| 32 |
+
print(f"PartialState:\n{PartialState()}")
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def tough_nut_function(queue: Queue):
|
| 36 |
+
if queue.empty():
|
| 37 |
+
return
|
| 38 |
+
trial = queue.get()
|
| 39 |
+
if trial > 0:
|
| 40 |
+
queue.put(trial - 1)
|
| 41 |
+
raise RuntimeError("The nut hasn't cracked yet! Try again.")
|
| 42 |
+
|
| 43 |
+
print(f"PartialState:\n{PartialState()}")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def bipolar_sleep_function(sleep_sec: int):
|
| 47 |
+
state = PartialState()
|
| 48 |
+
if state.process_index % 2 == 0:
|
| 49 |
+
raise RuntimeError("I'm an even process. I don't like to sleep.")
|
| 50 |
+
else:
|
| 51 |
+
time.sleep(sleep_sec)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
NUM_PROCESSES = int(os.environ.get("ACCELERATE_NUM_PROCESSES", 1))
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def test_can_initialize():
|
| 58 |
+
notebook_launcher(basic_function, (), num_processes=NUM_PROCESSES)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
@mark.skipif(NUM_PROCESSES < 2, reason="Need at least 2 processes to test static rendezvous backends")
|
| 62 |
+
def test_static_rdzv_backend():
|
| 63 |
+
notebook_launcher(basic_function, (), num_processes=NUM_PROCESSES, rdzv_backend="static")
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
@mark.skipif(NUM_PROCESSES < 2, reason="Need at least 2 processes to test c10d rendezvous backends")
|
| 67 |
+
def test_c10d_rdzv_backend():
|
| 68 |
+
notebook_launcher(basic_function, (), num_processes=NUM_PROCESSES, rdzv_backend="c10d")
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
@mark.skipif(NUM_PROCESSES < 2, reason="Need at least 2 processes to test fault tolerance")
|
| 72 |
+
def test_fault_tolerant(max_restarts: int = 3):
|
| 73 |
+
queue = Queue()
|
| 74 |
+
queue.put(max_restarts)
|
| 75 |
+
notebook_launcher(tough_nut_function, (queue,), num_processes=NUM_PROCESSES, max_restarts=max_restarts)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
@mark.skipif(NUM_PROCESSES < 2, reason="Need at least 2 processes to test monitoring")
|
| 79 |
+
def test_monitoring(monitor_interval: float = 0.01, sleep_sec: int = 100):
|
| 80 |
+
start_time = time.time()
|
| 81 |
+
with raises(ChildFailedError, match="I'm an even process. I don't like to sleep."):
|
| 82 |
+
notebook_launcher(
|
| 83 |
+
bipolar_sleep_function,
|
| 84 |
+
(sleep_sec,),
|
| 85 |
+
num_processes=NUM_PROCESSES,
|
| 86 |
+
monitor_interval=monitor_interval,
|
| 87 |
+
)
|
| 88 |
+
assert time.time() - start_time < sleep_sec, "Monitoring did not stop the process in time."
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@require_bnb
|
| 92 |
+
def test_problematic_imports():
|
| 93 |
+
with raises(RuntimeError, match="Please keep these imports"):
|
| 94 |
+
import bitsandbytes as bnb # noqa: F401
|
| 95 |
+
|
| 96 |
+
notebook_launcher(basic_function, (), num_processes=NUM_PROCESSES)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def main():
|
| 100 |
+
print("Test basic notebook can be ran")
|
| 101 |
+
test_can_initialize()
|
| 102 |
+
print("Test static rendezvous backend")
|
| 103 |
+
test_static_rdzv_backend()
|
| 104 |
+
print("Test c10d rendezvous backend")
|
| 105 |
+
test_c10d_rdzv_backend()
|
| 106 |
+
print("Test fault tolerant")
|
| 107 |
+
test_fault_tolerant()
|
| 108 |
+
print("Test monitoring")
|
| 109 |
+
test_monitoring()
|
| 110 |
+
if is_bnb_available():
|
| 111 |
+
print("Test problematic imports (bnb)")
|
| 112 |
+
test_problematic_imports()
|
| 113 |
+
if NUM_PROCESSES > 1:
|
| 114 |
+
PartialState().destroy_process_group()
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
if __name__ == "__main__":
|
| 118 |
+
main()
|
venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_ops.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
|
| 3 |
+
# Copyright 2023 The HuggingFace Team. All rights reserved.
|
| 4 |
+
#
|
| 5 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 6 |
+
# you may not use this file except in compliance with the License.
|
| 7 |
+
# You may obtain a copy of the License at
|
| 8 |
+
#
|
| 9 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 10 |
+
#
|
| 11 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 12 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 13 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 14 |
+
# See the License for the specific language governing permissions and
|
| 15 |
+
# limitations under the License.
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from accelerate import PartialState
|
| 20 |
+
from accelerate.test_utils.testing import assert_exception
|
| 21 |
+
from accelerate.utils.dataclasses import DistributedType
|
| 22 |
+
from accelerate.utils.operations import (
|
| 23 |
+
DistributedOperationException,
|
| 24 |
+
broadcast,
|
| 25 |
+
copy_tensor_to_devices,
|
| 26 |
+
gather,
|
| 27 |
+
gather_object,
|
| 28 |
+
pad_across_processes,
|
| 29 |
+
reduce,
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def create_tensor(state):
|
| 34 |
+
return (torch.arange(state.num_processes) + 1.0 + (state.num_processes * state.process_index)).to(state.device)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def test_gather(state):
|
| 38 |
+
tensor = create_tensor(state)
|
| 39 |
+
gathered_tensor = gather(tensor)
|
| 40 |
+
assert gathered_tensor.tolist() == list(range(1, state.num_processes**2 + 1))
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_gather_object(state):
|
| 44 |
+
# Gather objects in TorchXLA is not supported.
|
| 45 |
+
if state.distributed_type == DistributedType.XLA:
|
| 46 |
+
return
|
| 47 |
+
obj = [state.process_index]
|
| 48 |
+
gathered_obj = gather_object(obj)
|
| 49 |
+
assert len(gathered_obj) == state.num_processes, f"{gathered_obj}, {len(gathered_obj)} != {state.num_processes}"
|
| 50 |
+
assert gathered_obj == list(range(state.num_processes)), f"{gathered_obj} != {list(range(state.num_processes))}"
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def test_gather_non_contigous(state):
|
| 54 |
+
# Skip this test because the 'is_contiguous' function of XLA tensor always returns True.
|
| 55 |
+
if state.distributed_type == DistributedType.XLA:
|
| 56 |
+
return
|
| 57 |
+
|
| 58 |
+
# Create a non-contiguous tensor (enforce non-contiguity after device memory allocation)
|
| 59 |
+
tensor = torch.arange(12, device=state.device).view(4, 3).t()
|
| 60 |
+
assert not tensor.is_contiguous()
|
| 61 |
+
# Shouldn't error out
|
| 62 |
+
_ = gather(tensor)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def test_broadcast(state):
|
| 66 |
+
tensor = create_tensor(state)
|
| 67 |
+
broadcasted_tensor = broadcast(tensor)
|
| 68 |
+
assert broadcasted_tensor.shape == torch.Size([state.num_processes])
|
| 69 |
+
assert broadcasted_tensor.tolist() == list(range(1, state.num_processes + 1))
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def test_pad_across_processes(state):
|
| 73 |
+
# We need to pad the tensor with one more element if we are the main process
|
| 74 |
+
# to ensure that we can pad
|
| 75 |
+
if state.is_main_process:
|
| 76 |
+
tensor = torch.arange(state.num_processes + 1).to(state.device)
|
| 77 |
+
else:
|
| 78 |
+
tensor = torch.arange(state.num_processes).to(state.device)
|
| 79 |
+
padded_tensor = pad_across_processes(tensor)
|
| 80 |
+
assert padded_tensor.shape == torch.Size([state.num_processes + 1])
|
| 81 |
+
if not state.is_main_process:
|
| 82 |
+
assert padded_tensor.tolist() == list(range(0, state.num_processes)) + [0]
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def test_reduce_sum(state):
|
| 86 |
+
# For now runs on only two processes
|
| 87 |
+
if state.num_processes != 2:
|
| 88 |
+
return
|
| 89 |
+
tensor = create_tensor(state)
|
| 90 |
+
reduced_tensor = reduce(tensor, "sum")
|
| 91 |
+
truth_tensor = torch.tensor([4.0, 6]).to(state.device)
|
| 92 |
+
assert torch.allclose(reduced_tensor, truth_tensor), f"{reduced_tensor} != {truth_tensor}"
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def test_reduce_mean(state):
|
| 96 |
+
# For now runs on only two processes
|
| 97 |
+
if state.num_processes != 2:
|
| 98 |
+
return
|
| 99 |
+
tensor = create_tensor(state)
|
| 100 |
+
reduced_tensor = reduce(tensor, "mean")
|
| 101 |
+
truth_tensor = torch.tensor([2.0, 3]).to(state.device)
|
| 102 |
+
assert torch.allclose(reduced_tensor, truth_tensor), f"{reduced_tensor} != {truth_tensor}"
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def test_op_checker(state):
|
| 106 |
+
# Must be in a distributed state, and gathering is currently not supported in TorchXLA.
|
| 107 |
+
if state.distributed_type in [DistributedType.NO, DistributedType.XLA]:
|
| 108 |
+
return
|
| 109 |
+
state.debug = True
|
| 110 |
+
# `pad_across_processes`
|
| 111 |
+
if state.process_index == 0:
|
| 112 |
+
data = {"tensor": torch.tensor([[0.0, 1, 2, 3, 4]]).to(state.device)}
|
| 113 |
+
else:
|
| 114 |
+
data = {"tensor": torch.tensor([[[0.0, 1, 2, 3, 4, 5]]]).to(state.device)}
|
| 115 |
+
|
| 116 |
+
with assert_exception(DistributedOperationException):
|
| 117 |
+
pad_across_processes(data, dim=0)
|
| 118 |
+
|
| 119 |
+
# `reduce`
|
| 120 |
+
if state.process_index == 0:
|
| 121 |
+
data = {"tensor": torch.tensor([[0.0, 1, 2, 3, 4]]).to(state.device)}
|
| 122 |
+
else:
|
| 123 |
+
data = {"tensor": torch.tensor([[[0.0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]]).to(state.device)}
|
| 124 |
+
|
| 125 |
+
with assert_exception(DistributedOperationException):
|
| 126 |
+
reduce(data)
|
| 127 |
+
|
| 128 |
+
# `broadcast`
|
| 129 |
+
if state.process_index == 0:
|
| 130 |
+
data = {"tensor": torch.tensor([[0.0, 1, 2, 3, 4]]).to(state.device)}
|
| 131 |
+
else:
|
| 132 |
+
data = {"tensor": torch.tensor([[[0.0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]]).to(state.device)}
|
| 133 |
+
|
| 134 |
+
with assert_exception(DistributedOperationException):
|
| 135 |
+
broadcast(data)
|
| 136 |
+
|
| 137 |
+
state.debug = False
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def test_copy_tensor_to_devices(state):
|
| 141 |
+
if state.distributed_type not in [DistributedType.MULTI_GPU, DistributedType.XLA]:
|
| 142 |
+
return
|
| 143 |
+
if state.is_main_process:
|
| 144 |
+
tensor = torch.tensor([1, 2, 3], dtype=torch.int).to(state.device)
|
| 145 |
+
else:
|
| 146 |
+
tensor = None
|
| 147 |
+
tensor = copy_tensor_to_devices(tensor)
|
| 148 |
+
assert torch.allclose(tensor, torch.tensor([1, 2, 3], dtype=torch.int, device=state.device))
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def _mp_fn(index):
|
| 152 |
+
# For xla_spawn (TPUs)
|
| 153 |
+
main()
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def main():
|
| 157 |
+
state = PartialState()
|
| 158 |
+
state.print(f"State: {state}")
|
| 159 |
+
state.print("testing gather")
|
| 160 |
+
test_gather(state)
|
| 161 |
+
state.print("testing gather_object")
|
| 162 |
+
test_gather_object(state)
|
| 163 |
+
state.print("testing gather non-contigous")
|
| 164 |
+
test_gather_non_contigous(state)
|
| 165 |
+
state.print("testing broadcast")
|
| 166 |
+
test_broadcast(state)
|
| 167 |
+
state.print("testing pad_across_processes")
|
| 168 |
+
test_pad_across_processes(state)
|
| 169 |
+
state.print("testing reduce_sum")
|
| 170 |
+
test_reduce_sum(state)
|
| 171 |
+
state.print("testing reduce_mean")
|
| 172 |
+
test_reduce_mean(state)
|
| 173 |
+
state.print("testing op_checker")
|
| 174 |
+
test_op_checker(state)
|
| 175 |
+
state.print("testing sending tensors across devices")
|
| 176 |
+
test_copy_tensor_to_devices(state)
|
| 177 |
+
state.destroy_process_group()
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
if __name__ == "__main__":
|
| 181 |
+
main()
|
venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_script.py
ADDED
|
@@ -0,0 +1,901 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
|
| 3 |
+
# Copyright 2021 The HuggingFace Team. All rights reserved.
|
| 4 |
+
#
|
| 5 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 6 |
+
# you may not use this file except in compliance with the License.
|
| 7 |
+
# You may obtain a copy of the License at
|
| 8 |
+
#
|
| 9 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 10 |
+
#
|
| 11 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 12 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 13 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 14 |
+
# See the License for the specific language governing permissions and
|
| 15 |
+
# limitations under the License.
|
| 16 |
+
|
| 17 |
+
import contextlib
|
| 18 |
+
import io
|
| 19 |
+
import math
|
| 20 |
+
import time
|
| 21 |
+
from copy import deepcopy
|
| 22 |
+
from pathlib import Path
|
| 23 |
+
|
| 24 |
+
import numpy as np
|
| 25 |
+
import torch
|
| 26 |
+
from torch.utils.data import DataLoader, Dataset
|
| 27 |
+
|
| 28 |
+
from accelerate import Accelerator
|
| 29 |
+
from accelerate.data_loader import SeedableRandomSampler, prepare_data_loader
|
| 30 |
+
from accelerate.state import AcceleratorState
|
| 31 |
+
from accelerate.test_utils import RegressionDataset, are_the_same_tensors
|
| 32 |
+
from accelerate.utils import (
|
| 33 |
+
DataLoaderConfiguration,
|
| 34 |
+
DistributedType,
|
| 35 |
+
gather,
|
| 36 |
+
is_bf16_available,
|
| 37 |
+
is_datasets_available,
|
| 38 |
+
is_fp16_available,
|
| 39 |
+
is_hpu_available,
|
| 40 |
+
is_ipex_available,
|
| 41 |
+
is_pytest_available,
|
| 42 |
+
is_xpu_available,
|
| 43 |
+
set_seed,
|
| 44 |
+
synchronize_rng_states,
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# TODO: remove RegressionModel4XPU once ccl support empty buffer in broadcasting.
|
| 49 |
+
if is_xpu_available():
|
| 50 |
+
from accelerate.test_utils import RegressionModel4XPU as RegressionModel
|
| 51 |
+
else:
|
| 52 |
+
from accelerate.test_utils import RegressionModel
|
| 53 |
+
|
| 54 |
+
if is_hpu_available():
|
| 55 |
+
ATOL = 1e-3
|
| 56 |
+
RTOL = 1e-3
|
| 57 |
+
else:
|
| 58 |
+
ATOL = 1e-6
|
| 59 |
+
RTOL = 1e-6
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def generate_baseline_dataloader(train_set, generator, batch_size, use_seedable_sampler=False):
|
| 63 |
+
"Creates a dataloader that can also use the `SeedableRandomSampler`"
|
| 64 |
+
if use_seedable_sampler:
|
| 65 |
+
# The SeedableRandomSampler is needed during distributed setups
|
| 66 |
+
# for full reproducibility across processes with the `DataLoader`
|
| 67 |
+
sampler = SeedableRandomSampler(
|
| 68 |
+
generator=generator,
|
| 69 |
+
data_source=train_set,
|
| 70 |
+
num_samples=len(train_set),
|
| 71 |
+
)
|
| 72 |
+
return DataLoader(train_set, batch_size=batch_size, sampler=sampler)
|
| 73 |
+
else:
|
| 74 |
+
return DataLoader(train_set, batch_size=batch_size, shuffle=True, generator=generator)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def print_main(state):
|
| 78 |
+
print(f"Printing from the main process {state.process_index}")
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def print_local_main(state):
|
| 82 |
+
print(f"Printing from the local main process {state.local_process_index}")
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def print_last(state):
|
| 86 |
+
print(f"Printing from the last process {state.process_index}")
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def print_on(state, process_idx):
|
| 90 |
+
print(f"Printing from process {process_idx}: {state.process_index}")
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def process_execution_check():
|
| 94 |
+
accelerator = Accelerator()
|
| 95 |
+
num_processes = accelerator.num_processes
|
| 96 |
+
# Test main_process_first context manager
|
| 97 |
+
path = Path("check_main_process_first.txt")
|
| 98 |
+
with accelerator.main_process_first():
|
| 99 |
+
if accelerator.is_main_process:
|
| 100 |
+
time.sleep(0.1) # ensure main process takes longest
|
| 101 |
+
with open(path, "a+") as f:
|
| 102 |
+
f.write("Currently in the main process\n")
|
| 103 |
+
else:
|
| 104 |
+
with open(path, "a+") as f:
|
| 105 |
+
f.write("Now on another process\n")
|
| 106 |
+
accelerator.wait_for_everyone()
|
| 107 |
+
|
| 108 |
+
if accelerator.is_main_process:
|
| 109 |
+
with open(path) as f:
|
| 110 |
+
text = "".join(f.readlines())
|
| 111 |
+
try:
|
| 112 |
+
assert text.startswith("Currently in the main process\n"), "Main process was not first"
|
| 113 |
+
if num_processes > 1:
|
| 114 |
+
assert text.endswith("Now on another process\n"), "Main process was not first"
|
| 115 |
+
assert text.count("Now on another process\n") == accelerator.num_processes - 1, (
|
| 116 |
+
f"Only wrote to file {text.count('Now on another process') + 1} times, not {accelerator.num_processes}"
|
| 117 |
+
)
|
| 118 |
+
except AssertionError:
|
| 119 |
+
path.unlink()
|
| 120 |
+
raise
|
| 121 |
+
|
| 122 |
+
if accelerator.is_main_process and path.exists():
|
| 123 |
+
path.unlink()
|
| 124 |
+
accelerator.wait_for_everyone()
|
| 125 |
+
# Test the decorators
|
| 126 |
+
f = io.StringIO()
|
| 127 |
+
with contextlib.redirect_stdout(f):
|
| 128 |
+
accelerator.on_main_process(print_main)(accelerator.state)
|
| 129 |
+
result = f.getvalue().rstrip()
|
| 130 |
+
if accelerator.is_main_process:
|
| 131 |
+
assert result == "Printing from the main process 0", f"{result} != Printing from the main process 0"
|
| 132 |
+
else:
|
| 133 |
+
assert f.getvalue().rstrip() == "", f'{result} != ""'
|
| 134 |
+
f.truncate(0)
|
| 135 |
+
f.seek(0)
|
| 136 |
+
|
| 137 |
+
with contextlib.redirect_stdout(f):
|
| 138 |
+
accelerator.on_local_main_process(print_local_main)(accelerator.state)
|
| 139 |
+
if accelerator.is_local_main_process:
|
| 140 |
+
assert f.getvalue().rstrip() == "Printing from the local main process 0"
|
| 141 |
+
else:
|
| 142 |
+
assert f.getvalue().rstrip() == ""
|
| 143 |
+
f.truncate(0)
|
| 144 |
+
f.seek(0)
|
| 145 |
+
|
| 146 |
+
with contextlib.redirect_stdout(f):
|
| 147 |
+
accelerator.on_last_process(print_last)(accelerator.state)
|
| 148 |
+
if accelerator.is_last_process:
|
| 149 |
+
assert f.getvalue().rstrip() == f"Printing from the last process {accelerator.state.num_processes - 1}"
|
| 150 |
+
else:
|
| 151 |
+
assert f.getvalue().rstrip() == ""
|
| 152 |
+
f.truncate(0)
|
| 153 |
+
f.seek(0)
|
| 154 |
+
|
| 155 |
+
for process_idx in range(num_processes):
|
| 156 |
+
with contextlib.redirect_stdout(f):
|
| 157 |
+
accelerator.on_process(print_on, process_index=process_idx)(accelerator.state, process_idx)
|
| 158 |
+
if accelerator.process_index == process_idx:
|
| 159 |
+
assert f.getvalue().rstrip() == f"Printing from process {process_idx}: {accelerator.process_index}"
|
| 160 |
+
else:
|
| 161 |
+
assert f.getvalue().rstrip() == ""
|
| 162 |
+
f.truncate(0)
|
| 163 |
+
f.seek(0)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def init_state_check():
|
| 167 |
+
# Test we can instantiate this twice in a row.
|
| 168 |
+
state = AcceleratorState()
|
| 169 |
+
if state.local_process_index == 0:
|
| 170 |
+
print("Testing, testing. 1, 2, 3.")
|
| 171 |
+
print(state)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def rng_sync_check():
|
| 175 |
+
state = AcceleratorState()
|
| 176 |
+
synchronize_rng_states(["torch"])
|
| 177 |
+
assert are_the_same_tensors(torch.get_rng_state()), "RNG states improperly synchronized on CPU."
|
| 178 |
+
if state.distributed_type == DistributedType.MULTI_GPU:
|
| 179 |
+
synchronize_rng_states(["cuda"])
|
| 180 |
+
assert are_the_same_tensors(torch.cuda.get_rng_state()), "RNG states improperly synchronized on GPU."
|
| 181 |
+
elif state.distributed_type == DistributedType.MULTI_XPU:
|
| 182 |
+
synchronize_rng_states(["xpu"])
|
| 183 |
+
assert are_the_same_tensors(torch.xpu.get_rng_state()), "RNG states improperly synchronized on XPU."
|
| 184 |
+
generator = torch.Generator()
|
| 185 |
+
synchronize_rng_states(["generator"], generator=generator)
|
| 186 |
+
assert are_the_same_tensors(generator.get_state()), "RNG states improperly synchronized in generator."
|
| 187 |
+
|
| 188 |
+
if state.local_process_index == 0:
|
| 189 |
+
print("All rng are properly synched.")
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def dl_preparation_check():
|
| 193 |
+
state = AcceleratorState()
|
| 194 |
+
length = 32 * state.num_processes
|
| 195 |
+
|
| 196 |
+
dl = DataLoader(range(length), batch_size=8)
|
| 197 |
+
dl = prepare_data_loader(dl, state.device, state.num_processes, state.process_index, put_on_device=True)
|
| 198 |
+
result = []
|
| 199 |
+
for batch in dl:
|
| 200 |
+
result.append(gather(batch))
|
| 201 |
+
result = torch.cat(result)
|
| 202 |
+
|
| 203 |
+
assert torch.equal(result.cpu(), torch.arange(0, length).long()), "Wrong non-shuffled dataloader result."
|
| 204 |
+
|
| 205 |
+
dl = DataLoader(range(length), batch_size=8)
|
| 206 |
+
dl = prepare_data_loader(
|
| 207 |
+
dl,
|
| 208 |
+
state.device,
|
| 209 |
+
state.num_processes,
|
| 210 |
+
state.process_index,
|
| 211 |
+
put_on_device=True,
|
| 212 |
+
split_batches=True,
|
| 213 |
+
)
|
| 214 |
+
result = []
|
| 215 |
+
for batch in dl:
|
| 216 |
+
result.append(gather(batch))
|
| 217 |
+
result = torch.cat(result)
|
| 218 |
+
assert torch.equal(result.cpu(), torch.arange(0, length).long()), "Wrong non-shuffled dataloader result."
|
| 219 |
+
|
| 220 |
+
if state.process_index == 0:
|
| 221 |
+
print("Non-shuffled dataloader passing.")
|
| 222 |
+
|
| 223 |
+
dl = DataLoader(range(length), batch_size=8, shuffle=True)
|
| 224 |
+
dl = prepare_data_loader(dl, state.device, state.num_processes, state.process_index, put_on_device=True)
|
| 225 |
+
result = []
|
| 226 |
+
for batch in dl:
|
| 227 |
+
result.append(gather(batch))
|
| 228 |
+
result = torch.cat(result).tolist()
|
| 229 |
+
result.sort()
|
| 230 |
+
assert result == list(range(length)), "Wrong shuffled dataloader result."
|
| 231 |
+
|
| 232 |
+
dl = DataLoader(range(length), batch_size=8, shuffle=True)
|
| 233 |
+
dl = prepare_data_loader(
|
| 234 |
+
dl,
|
| 235 |
+
state.device,
|
| 236 |
+
state.num_processes,
|
| 237 |
+
state.process_index,
|
| 238 |
+
put_on_device=True,
|
| 239 |
+
split_batches=True,
|
| 240 |
+
)
|
| 241 |
+
result = []
|
| 242 |
+
for batch in dl:
|
| 243 |
+
result.append(gather(batch))
|
| 244 |
+
result = torch.cat(result).tolist()
|
| 245 |
+
result.sort()
|
| 246 |
+
assert result == list(range(length)), "Wrong shuffled dataloader result."
|
| 247 |
+
|
| 248 |
+
if state.local_process_index == 0:
|
| 249 |
+
print("Shuffled dataloader passing.")
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def central_dl_preparation_check():
|
| 253 |
+
state = AcceleratorState()
|
| 254 |
+
length = 32 * state.num_processes
|
| 255 |
+
|
| 256 |
+
dl = DataLoader(range(length), batch_size=8)
|
| 257 |
+
dl = prepare_data_loader(
|
| 258 |
+
dl, state.device, state.num_processes, state.process_index, put_on_device=True, dispatch_batches=True
|
| 259 |
+
)
|
| 260 |
+
result = []
|
| 261 |
+
for batch in dl:
|
| 262 |
+
result.append(gather(batch))
|
| 263 |
+
result = torch.cat(result)
|
| 264 |
+
assert torch.equal(result.cpu(), torch.arange(0, length).long()), "Wrong non-shuffled dataloader result."
|
| 265 |
+
|
| 266 |
+
dl = DataLoader(range(length), batch_size=8)
|
| 267 |
+
dl = prepare_data_loader(
|
| 268 |
+
dl,
|
| 269 |
+
state.device,
|
| 270 |
+
state.num_processes,
|
| 271 |
+
state.process_index,
|
| 272 |
+
put_on_device=True,
|
| 273 |
+
split_batches=True,
|
| 274 |
+
dispatch_batches=True,
|
| 275 |
+
)
|
| 276 |
+
result = []
|
| 277 |
+
for batch in dl:
|
| 278 |
+
result.append(gather(batch))
|
| 279 |
+
result = torch.cat(result)
|
| 280 |
+
assert torch.equal(result.cpu(), torch.arange(0, length).long()), "Wrong non-shuffled dataloader result."
|
| 281 |
+
|
| 282 |
+
if state.process_index == 0:
|
| 283 |
+
print("Non-shuffled central dataloader passing.")
|
| 284 |
+
|
| 285 |
+
dl = DataLoader(range(length), batch_size=8, shuffle=True)
|
| 286 |
+
dl = prepare_data_loader(
|
| 287 |
+
dl, state.device, state.num_processes, state.process_index, put_on_device=True, dispatch_batches=True
|
| 288 |
+
)
|
| 289 |
+
result = []
|
| 290 |
+
for batch in dl:
|
| 291 |
+
result.append(gather(batch))
|
| 292 |
+
result = torch.cat(result).tolist()
|
| 293 |
+
result.sort()
|
| 294 |
+
assert result == list(range(length)), "Wrong shuffled dataloader result."
|
| 295 |
+
|
| 296 |
+
dl = DataLoader(range(length), batch_size=8, shuffle=True)
|
| 297 |
+
dl = prepare_data_loader(
|
| 298 |
+
dl,
|
| 299 |
+
state.device,
|
| 300 |
+
state.num_processes,
|
| 301 |
+
state.process_index,
|
| 302 |
+
put_on_device=True,
|
| 303 |
+
split_batches=True,
|
| 304 |
+
dispatch_batches=True,
|
| 305 |
+
)
|
| 306 |
+
result = []
|
| 307 |
+
for batch in dl:
|
| 308 |
+
result.append(gather(batch))
|
| 309 |
+
result = torch.cat(result).tolist()
|
| 310 |
+
result.sort()
|
| 311 |
+
assert result == list(range(length)), "Wrong shuffled dataloader result."
|
| 312 |
+
|
| 313 |
+
if state.local_process_index == 0:
|
| 314 |
+
print("Shuffled central dataloader passing.")
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
def custom_sampler_check():
|
| 318 |
+
state = AcceleratorState()
|
| 319 |
+
|
| 320 |
+
class CustomDataset(Dataset):
|
| 321 |
+
def __init__(self, data):
|
| 322 |
+
self.data = data
|
| 323 |
+
|
| 324 |
+
def __len__(self):
|
| 325 |
+
return len(self.data)
|
| 326 |
+
|
| 327 |
+
def __getitem__(self, index):
|
| 328 |
+
return self.data[index]
|
| 329 |
+
|
| 330 |
+
class CustomBatchSampler:
|
| 331 |
+
def __init__(self, dataset_length: int, batch_size: int, shuffle: bool = True):
|
| 332 |
+
self.batch_size = batch_size
|
| 333 |
+
self.data_index = np.arange(dataset_length)
|
| 334 |
+
self.shuffle = shuffle
|
| 335 |
+
|
| 336 |
+
def __iter__(self):
|
| 337 |
+
num_batches = len(self)
|
| 338 |
+
if self.shuffle:
|
| 339 |
+
index = np.random.permutation(self.data_index)
|
| 340 |
+
else:
|
| 341 |
+
index = self.data_index
|
| 342 |
+
output = np.array_split(index, num_batches)
|
| 343 |
+
yield from output
|
| 344 |
+
|
| 345 |
+
def __len__(self):
|
| 346 |
+
return math.ceil(len(self.data_index) / self.batch_size)
|
| 347 |
+
|
| 348 |
+
dataset = CustomDataset(range(32 * state.num_processes))
|
| 349 |
+
sampler = CustomBatchSampler(len(dataset), batch_size=8)
|
| 350 |
+
dl = DataLoader(dataset, batch_sampler=sampler)
|
| 351 |
+
dl = prepare_data_loader(dl, state.device, state.num_processes, state.process_index)
|
| 352 |
+
# We need just ensure that `dl.batch_sampler` (or `dl.batch_sampler.batch_sampler` is indeed the old batch sampler
|
| 353 |
+
if hasattr(dl.batch_sampler, "batch_sampler"):
|
| 354 |
+
assert isinstance(dl.batch_sampler.batch_sampler, CustomBatchSampler), (
|
| 355 |
+
"Custom sampler was changed after calling `prepare_data_loader`"
|
| 356 |
+
)
|
| 357 |
+
else:
|
| 358 |
+
assert isinstance(dl.batch_sampler, CustomBatchSampler), (
|
| 359 |
+
"Custom sampler was changed after calling `prepare_data_loader`"
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def check_seedable_sampler():
|
| 364 |
+
# Set seed
|
| 365 |
+
set_seed(42)
|
| 366 |
+
train_set = RegressionDataset(length=10, seed=42)
|
| 367 |
+
train_dl = DataLoader(train_set, batch_size=2, shuffle=True)
|
| 368 |
+
|
| 369 |
+
config = DataLoaderConfiguration(use_seedable_sampler=True)
|
| 370 |
+
accelerator = Accelerator(dataloader_config=config)
|
| 371 |
+
train_dl = accelerator.prepare(train_dl)
|
| 372 |
+
original_items = []
|
| 373 |
+
for _ in range(3):
|
| 374 |
+
for batch in train_dl:
|
| 375 |
+
original_items.append(batch["x"])
|
| 376 |
+
original_items = torch.cat(original_items)
|
| 377 |
+
|
| 378 |
+
# Set seed again and the epoch
|
| 379 |
+
set_seed(42)
|
| 380 |
+
train_dl.set_epoch(0)
|
| 381 |
+
new_items = []
|
| 382 |
+
for _ in range(3):
|
| 383 |
+
for batch in train_dl:
|
| 384 |
+
new_items.append(batch["x"])
|
| 385 |
+
new_items = torch.cat(new_items)
|
| 386 |
+
assert torch.allclose(original_items, new_items), "Did not obtain the same items with the same seed and epoch."
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
def check_seedable_sampler_in_batch_sampler_shard():
|
| 390 |
+
set_seed(42)
|
| 391 |
+
|
| 392 |
+
config = DataLoaderConfiguration(use_seedable_sampler=True)
|
| 393 |
+
accelerator = Accelerator(dataloader_config=config)
|
| 394 |
+
assert accelerator.num_processes > 1, "This test requires more than one process."
|
| 395 |
+
|
| 396 |
+
dataloader = DataLoader(list(range(10)), batch_size=1, shuffle=True)
|
| 397 |
+
prepared_data_loader = prepare_data_loader(
|
| 398 |
+
dataloader=dataloader,
|
| 399 |
+
use_seedable_sampler=True,
|
| 400 |
+
)
|
| 401 |
+
|
| 402 |
+
target_sampler = prepared_data_loader.batch_sampler.batch_sampler.sampler
|
| 403 |
+
assert isinstance(target_sampler, SeedableRandomSampler), (
|
| 404 |
+
"Sampler in BatchSamplerShard is not SeedableRandomSampler."
|
| 405 |
+
)
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
def check_seedable_sampler_with_data_seed():
|
| 409 |
+
# Set seed
|
| 410 |
+
set_seed(42)
|
| 411 |
+
data_seed = 42
|
| 412 |
+
train_set = RegressionDataset(length=10, seed=42)
|
| 413 |
+
train_dl = DataLoader(train_set, batch_size=2, shuffle=True)
|
| 414 |
+
|
| 415 |
+
config = DataLoaderConfiguration(use_seedable_sampler=True, data_seed=data_seed)
|
| 416 |
+
accelerator = Accelerator(dataloader_config=config)
|
| 417 |
+
prepared_dl = accelerator.prepare(train_dl)
|
| 418 |
+
original_items = []
|
| 419 |
+
for _ in range(3):
|
| 420 |
+
for batch in prepared_dl:
|
| 421 |
+
original_items.append(batch["x"])
|
| 422 |
+
original_items = torch.cat(original_items)
|
| 423 |
+
|
| 424 |
+
# Set new data seed
|
| 425 |
+
config.data_seed = 43
|
| 426 |
+
accelerator = Accelerator(dataloader_config=config)
|
| 427 |
+
prepared_dl = accelerator.prepare(train_dl)
|
| 428 |
+
new_items = []
|
| 429 |
+
for _ in range(3):
|
| 430 |
+
for batch in prepared_dl:
|
| 431 |
+
new_items.append(batch["x"])
|
| 432 |
+
new_items = torch.cat(new_items)
|
| 433 |
+
assert not torch.allclose(original_items, new_items), "Obtained the same items with different data seed."
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
def mock_training(length, batch_size, generator, use_seedable_sampler=False):
|
| 437 |
+
set_seed(42)
|
| 438 |
+
generator.manual_seed(42)
|
| 439 |
+
train_set = RegressionDataset(length=length, seed=42)
|
| 440 |
+
|
| 441 |
+
train_dl = generate_baseline_dataloader(train_set, generator, batch_size, use_seedable_sampler)
|
| 442 |
+
model = RegressionModel()
|
| 443 |
+
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
|
| 444 |
+
for epoch in range(3):
|
| 445 |
+
for batch in train_dl:
|
| 446 |
+
model.zero_grad()
|
| 447 |
+
output = model(batch["x"])
|
| 448 |
+
loss = torch.nn.functional.mse_loss(output, batch["y"])
|
| 449 |
+
loss.backward()
|
| 450 |
+
optimizer.step()
|
| 451 |
+
return train_set, model
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
def training_check(use_seedable_sampler=False):
|
| 455 |
+
state = AcceleratorState()
|
| 456 |
+
generator = torch.Generator()
|
| 457 |
+
batch_size = 8
|
| 458 |
+
length = batch_size * 4 * state.num_processes
|
| 459 |
+
|
| 460 |
+
train_set, old_model = mock_training(length, batch_size * state.num_processes, generator, use_seedable_sampler)
|
| 461 |
+
assert are_the_same_tensors(old_model.a), "Did not obtain the same model on both processes."
|
| 462 |
+
assert are_the_same_tensors(old_model.b), "Did not obtain the same model on both processes."
|
| 463 |
+
|
| 464 |
+
accelerator = Accelerator()
|
| 465 |
+
train_dl = generate_baseline_dataloader(train_set, generator, batch_size, use_seedable_sampler)
|
| 466 |
+
model = RegressionModel()
|
| 467 |
+
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
|
| 468 |
+
|
| 469 |
+
train_dl, model, optimizer = accelerator.prepare(train_dl, model, optimizer)
|
| 470 |
+
set_seed(42)
|
| 471 |
+
generator.manual_seed(42)
|
| 472 |
+
for _ in range(3):
|
| 473 |
+
for batch in train_dl:
|
| 474 |
+
model.zero_grad()
|
| 475 |
+
output = model(batch["x"])
|
| 476 |
+
loss = torch.nn.functional.mse_loss(output, batch["y"])
|
| 477 |
+
accelerator.backward(loss)
|
| 478 |
+
optimizer.step()
|
| 479 |
+
|
| 480 |
+
model = accelerator.unwrap_model(model).cpu()
|
| 481 |
+
torch.testing.assert_close(
|
| 482 |
+
old_model.a,
|
| 483 |
+
model.a,
|
| 484 |
+
atol=ATOL,
|
| 485 |
+
rtol=RTOL,
|
| 486 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 487 |
+
)
|
| 488 |
+
torch.testing.assert_close(
|
| 489 |
+
old_model.b,
|
| 490 |
+
model.b,
|
| 491 |
+
atol=ATOL,
|
| 492 |
+
rtol=RTOL,
|
| 493 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 494 |
+
)
|
| 495 |
+
|
| 496 |
+
accelerator.print("Training yielded the same results on one CPU or distributed setup with no batch split.")
|
| 497 |
+
|
| 498 |
+
dataloader_config = DataLoaderConfiguration(split_batches=True, use_seedable_sampler=use_seedable_sampler)
|
| 499 |
+
accelerator = Accelerator(dataloader_config=dataloader_config)
|
| 500 |
+
train_dl = generate_baseline_dataloader(
|
| 501 |
+
train_set, generator, batch_size * state.num_processes, use_seedable_sampler
|
| 502 |
+
)
|
| 503 |
+
model = RegressionModel()
|
| 504 |
+
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
|
| 505 |
+
|
| 506 |
+
train_dl, model, optimizer = accelerator.prepare(train_dl, model, optimizer)
|
| 507 |
+
set_seed(42)
|
| 508 |
+
generator.manual_seed(42)
|
| 509 |
+
for _ in range(3):
|
| 510 |
+
for batch in train_dl:
|
| 511 |
+
model.zero_grad()
|
| 512 |
+
output = model(batch["x"])
|
| 513 |
+
loss = torch.nn.functional.mse_loss(output, batch["y"])
|
| 514 |
+
accelerator.backward(loss)
|
| 515 |
+
optimizer.step()
|
| 516 |
+
|
| 517 |
+
model = accelerator.unwrap_model(model).cpu()
|
| 518 |
+
torch.testing.assert_close(
|
| 519 |
+
old_model.a,
|
| 520 |
+
model.a,
|
| 521 |
+
atol=ATOL,
|
| 522 |
+
rtol=RTOL,
|
| 523 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 524 |
+
)
|
| 525 |
+
torch.testing.assert_close(
|
| 526 |
+
old_model.b,
|
| 527 |
+
model.b,
|
| 528 |
+
atol=ATOL,
|
| 529 |
+
rtol=RTOL,
|
| 530 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 531 |
+
)
|
| 532 |
+
|
| 533 |
+
accelerator.print("Training yielded the same results on one CPU or distributed setup with batch split.")
|
| 534 |
+
|
| 535 |
+
# FP32 wrapper check
|
| 536 |
+
if torch.cuda.is_available():
|
| 537 |
+
# Mostly a test that model.forward will have autocast when running unwrap_model(model, keep_fp32_wrapper=True)
|
| 538 |
+
print("Keep fp32 wrapper check.")
|
| 539 |
+
AcceleratorState._reset_state()
|
| 540 |
+
accelerator = Accelerator(mixed_precision="fp16")
|
| 541 |
+
|
| 542 |
+
model = torch.nn.Linear(2, 4)
|
| 543 |
+
model = accelerator.prepare(model)
|
| 544 |
+
model_with_fp32_wrapper = accelerator.unwrap_model(model, keep_fp32_wrapper=True)
|
| 545 |
+
|
| 546 |
+
# Run forward with fp16 as input.
|
| 547 |
+
# When the model is with mixed precision wrapper, no error will be raised.
|
| 548 |
+
input_tensor = torch.Tensor([1, 2]).to(dtype=torch.float16, device=accelerator.device)
|
| 549 |
+
output = model_with_fp32_wrapper(input_tensor)
|
| 550 |
+
|
| 551 |
+
# BF16 support
|
| 552 |
+
if is_bf16_available():
|
| 553 |
+
# Mostly a test that BF16 doesn't crash as the operation inside the model is not converted to BF16
|
| 554 |
+
print("BF16 training check.")
|
| 555 |
+
AcceleratorState._reset_state()
|
| 556 |
+
dataloader_config = DataLoaderConfiguration(use_seedable_sampler=use_seedable_sampler)
|
| 557 |
+
accelerator = Accelerator(mixed_precision="bf16", dataloader_config=dataloader_config)
|
| 558 |
+
train_dl = generate_baseline_dataloader(train_set, generator, batch_size, use_seedable_sampler)
|
| 559 |
+
model = RegressionModel()
|
| 560 |
+
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
|
| 561 |
+
|
| 562 |
+
train_dl, model, optimizer = accelerator.prepare(train_dl, model, optimizer)
|
| 563 |
+
set_seed(42)
|
| 564 |
+
generator.manual_seed(42)
|
| 565 |
+
for _ in range(3):
|
| 566 |
+
for batch in train_dl:
|
| 567 |
+
model.zero_grad()
|
| 568 |
+
output = model(batch["x"])
|
| 569 |
+
loss = torch.nn.functional.mse_loss(output, batch["y"])
|
| 570 |
+
accelerator.backward(loss)
|
| 571 |
+
optimizer.step()
|
| 572 |
+
|
| 573 |
+
model = accelerator.unwrap_model(model).cpu()
|
| 574 |
+
torch.testing.assert_close(
|
| 575 |
+
old_model.a,
|
| 576 |
+
model.a,
|
| 577 |
+
atol=ATOL,
|
| 578 |
+
rtol=RTOL,
|
| 579 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 580 |
+
)
|
| 581 |
+
torch.testing.assert_close(
|
| 582 |
+
old_model.b,
|
| 583 |
+
model.b,
|
| 584 |
+
atol=ATOL,
|
| 585 |
+
rtol=RTOL,
|
| 586 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
# FP16 support (HPU fp16 model seems to be off by 10% from the CPU, which is a lot of numerical error)
|
| 590 |
+
if is_fp16_available() and not is_hpu_available():
|
| 591 |
+
# Mostly a test that FP16 doesn't crash as the operation inside the model is not converted to FP16
|
| 592 |
+
print("FP16 training check.")
|
| 593 |
+
AcceleratorState._reset_state()
|
| 594 |
+
dataloader_config = DataLoaderConfiguration(use_seedable_sampler=use_seedable_sampler)
|
| 595 |
+
accelerator = Accelerator(mixed_precision="fp16", dataloader_config=dataloader_config)
|
| 596 |
+
train_dl = generate_baseline_dataloader(train_set, generator, batch_size, use_seedable_sampler)
|
| 597 |
+
model = RegressionModel()
|
| 598 |
+
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
|
| 599 |
+
|
| 600 |
+
train_dl, model, optimizer = accelerator.prepare(train_dl, model, optimizer)
|
| 601 |
+
set_seed(42)
|
| 602 |
+
generator.manual_seed(42)
|
| 603 |
+
for _ in range(3):
|
| 604 |
+
for batch in train_dl:
|
| 605 |
+
model.zero_grad()
|
| 606 |
+
output = model(batch["x"])
|
| 607 |
+
loss = torch.nn.functional.mse_loss(output, batch["y"])
|
| 608 |
+
accelerator.backward(loss)
|
| 609 |
+
optimizer.step()
|
| 610 |
+
|
| 611 |
+
model = accelerator.unwrap_model(model).cpu()
|
| 612 |
+
torch.testing.assert_close(
|
| 613 |
+
old_model.a,
|
| 614 |
+
model.a,
|
| 615 |
+
atol=ATOL,
|
| 616 |
+
rtol=RTOL,
|
| 617 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 618 |
+
)
|
| 619 |
+
torch.testing.assert_close(
|
| 620 |
+
old_model.b,
|
| 621 |
+
model.b,
|
| 622 |
+
atol=ATOL,
|
| 623 |
+
rtol=RTOL,
|
| 624 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 625 |
+
)
|
| 626 |
+
|
| 627 |
+
# IPEX support is only for CPU
|
| 628 |
+
if is_ipex_available():
|
| 629 |
+
print("ipex BF16 training check.")
|
| 630 |
+
AcceleratorState._reset_state()
|
| 631 |
+
dataloader_config = DataLoaderConfiguration(use_seedable_sampler=use_seedable_sampler)
|
| 632 |
+
accelerator = Accelerator(mixed_precision="bf16", cpu=True, dataloader_config=dataloader_config)
|
| 633 |
+
train_dl = generate_baseline_dataloader(train_set, generator, batch_size, use_seedable_sampler)
|
| 634 |
+
model = RegressionModel()
|
| 635 |
+
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
|
| 636 |
+
|
| 637 |
+
train_dl, model, optimizer = accelerator.prepare(train_dl, model, optimizer)
|
| 638 |
+
set_seed(42)
|
| 639 |
+
generator.manual_seed(42)
|
| 640 |
+
for _ in range(3):
|
| 641 |
+
for batch in train_dl:
|
| 642 |
+
model.zero_grad()
|
| 643 |
+
output = model(batch["x"])
|
| 644 |
+
loss = torch.nn.functional.mse_loss(output, batch["y"])
|
| 645 |
+
accelerator.backward(loss)
|
| 646 |
+
optimizer.step()
|
| 647 |
+
|
| 648 |
+
model = accelerator.unwrap_model(model).cpu()
|
| 649 |
+
torch.testing.assert_close(
|
| 650 |
+
old_model.a,
|
| 651 |
+
model.a,
|
| 652 |
+
atol=ATOL,
|
| 653 |
+
rtol=RTOL,
|
| 654 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 655 |
+
)
|
| 656 |
+
torch.testing.assert_close(
|
| 657 |
+
old_model.b,
|
| 658 |
+
model.b,
|
| 659 |
+
atol=ATOL,
|
| 660 |
+
rtol=RTOL,
|
| 661 |
+
msg=lambda msg: f"Did not obtain the same model on CPU or distributed training.\n{msg}",
|
| 662 |
+
)
|
| 663 |
+
|
| 664 |
+
|
| 665 |
+
def test_split_between_processes_dataset(datasets_Dataset):
|
| 666 |
+
state = AcceleratorState()
|
| 667 |
+
data = datasets_Dataset.from_list([dict(k=v) for v in range(2 * state.num_processes)])
|
| 668 |
+
with state.split_between_processes(data, apply_padding=False) as results:
|
| 669 |
+
assert len(results) == 2, (
|
| 670 |
+
f"Each process did not have two items. Process index: {state.process_index}; Length: {len(results)}"
|
| 671 |
+
)
|
| 672 |
+
|
| 673 |
+
data = datasets_Dataset.from_list([dict(k=v) for v in range(2 * state.num_processes - 1)])
|
| 674 |
+
with state.split_between_processes(data, apply_padding=False) as results:
|
| 675 |
+
if state.is_last_process:
|
| 676 |
+
assert len(results) == 1, (
|
| 677 |
+
f"Last process did not receive a single item. Process index: {state.process_index}; Length: {len(results)}"
|
| 678 |
+
)
|
| 679 |
+
else:
|
| 680 |
+
assert len(results) == 2, (
|
| 681 |
+
f"One of the intermediate processes did not receive two items. Process index: {state.process_index}; Length: {len(results)}"
|
| 682 |
+
)
|
| 683 |
+
|
| 684 |
+
data = datasets_Dataset.from_list([dict(k=v) for v in range(2 * state.num_processes - 1)])
|
| 685 |
+
with state.split_between_processes(data, apply_padding=True) as results:
|
| 686 |
+
if state.num_processes == 1:
|
| 687 |
+
assert len(results) == 1, (
|
| 688 |
+
f"Single process did not receive a single item. Process index: {state.process_index}; Length: {len(results)}"
|
| 689 |
+
)
|
| 690 |
+
else:
|
| 691 |
+
assert len(results) == 2, (
|
| 692 |
+
f"Each process did not have two items. Process index: {state.process_index}; Length: {len(results)}"
|
| 693 |
+
)
|
| 694 |
+
|
| 695 |
+
state.wait_for_everyone()
|
| 696 |
+
|
| 697 |
+
|
| 698 |
+
def test_split_between_processes_list():
|
| 699 |
+
state = AcceleratorState()
|
| 700 |
+
data = list(range(0, 2 * state.num_processes))
|
| 701 |
+
with state.split_between_processes(data) as results:
|
| 702 |
+
assert len(results) == 2, (
|
| 703 |
+
f"Each process did not have two items. Process index: {state.process_index}; Length: {len(results)}"
|
| 704 |
+
)
|
| 705 |
+
|
| 706 |
+
data = list(range(0, (3 * state.num_processes) - 1))
|
| 707 |
+
with state.split_between_processes(data, apply_padding=True) as results:
|
| 708 |
+
if state.is_last_process:
|
| 709 |
+
# Test that the last process gets the extra item(s)
|
| 710 |
+
num_samples_per_device = math.ceil(len(data) / state.num_processes)
|
| 711 |
+
assert len(results) == num_samples_per_device, (
|
| 712 |
+
f"Last process did not get the extra item(s). Process index: {state.process_index}; Length: {len(results)}"
|
| 713 |
+
)
|
| 714 |
+
state.wait_for_everyone()
|
| 715 |
+
|
| 716 |
+
|
| 717 |
+
def test_split_between_processes_nested_dict():
|
| 718 |
+
state = AcceleratorState()
|
| 719 |
+
a = [1, 2, 3, 4, 5, 6, 7, 8]
|
| 720 |
+
b = ["a", "b", "c", "d", "e", "f", "g", "h"]
|
| 721 |
+
c = torch.tensor([1, 2, 3, 4, 5, 6, 7, 8])
|
| 722 |
+
if state.num_processes in (1, 2, 4):
|
| 723 |
+
data = {"a": a, "b": b, "c": c}
|
| 724 |
+
data_copy = deepcopy(data)
|
| 725 |
+
with state.split_between_processes(data) as results:
|
| 726 |
+
if state.process_index == 0:
|
| 727 |
+
assert results["a"] == data_copy["a"][: 8 // state.num_processes]
|
| 728 |
+
elif state.num_processes == 2:
|
| 729 |
+
assert results["a"] == data_copy["a"][4:]
|
| 730 |
+
elif state.process_index == 3:
|
| 731 |
+
# We return a list each time
|
| 732 |
+
assert results["a"] == data_copy["a"][-2:], f"Expected: {data_copy['a'][-2]}, Actual: {results['a']}"
|
| 733 |
+
if state.process_index == 0:
|
| 734 |
+
assert results["b"] == data_copy["b"][: 8 // state.num_processes]
|
| 735 |
+
elif state.num_processes == 2:
|
| 736 |
+
assert results["b"] == data_copy["b"][4:]
|
| 737 |
+
elif state.process_index == 3:
|
| 738 |
+
assert results["b"] == data_copy["b"][-2:]
|
| 739 |
+
if state.process_index == 0:
|
| 740 |
+
assert torch.allclose(results["c"], data_copy["c"][: 8 // state.num_processes]), (
|
| 741 |
+
f"Did not obtain expected values on process 0, expected `{data['c'][: 8 // state.num_processes]}`, received: {results['c']}"
|
| 742 |
+
)
|
| 743 |
+
elif state.num_processes == 2:
|
| 744 |
+
assert torch.allclose(results["c"], data_copy["c"][4:]), (
|
| 745 |
+
f"Did not obtain expected values on process 2, expected `{data['c'][4:]}`, received: {results['c']}"
|
| 746 |
+
)
|
| 747 |
+
elif state.process_index == 3:
|
| 748 |
+
assert torch.allclose(results["c"], data_copy["c"][-2:]), (
|
| 749 |
+
f"Did not obtain expected values on process 4, expected `{data['c'][-2:]}`, received: {results['c']}"
|
| 750 |
+
)
|
| 751 |
+
|
| 752 |
+
state.wait_for_everyone()
|
| 753 |
+
|
| 754 |
+
|
| 755 |
+
def test_split_between_processes_tensor():
|
| 756 |
+
state = AcceleratorState()
|
| 757 |
+
if state.num_processes > 1:
|
| 758 |
+
data = torch.tensor([[0, 1, 2, 3], [4, 5, 6, 7]]).to(state.device)
|
| 759 |
+
with state.split_between_processes(data) as results:
|
| 760 |
+
if state.process_index == 0:
|
| 761 |
+
expected = torch.tensor([[0, 1, 2, 3]]).to(state.device)
|
| 762 |
+
else:
|
| 763 |
+
expected = torch.tensor([[4, 5, 6, 7]]).to(state.device)
|
| 764 |
+
torch.testing.assert_close(results, expected)
|
| 765 |
+
state.wait_for_everyone()
|
| 766 |
+
|
| 767 |
+
|
| 768 |
+
def test_split_between_processes_evenly():
|
| 769 |
+
state = AcceleratorState()
|
| 770 |
+
if state.num_processes in (1, 2, 4, 8):
|
| 771 |
+
data = list(range(17))
|
| 772 |
+
num_samples_per_process = len(data) // state.num_processes
|
| 773 |
+
num_extras = len(data) % state.num_processes
|
| 774 |
+
with state.split_between_processes(data) as results:
|
| 775 |
+
if state.process_index < num_extras:
|
| 776 |
+
assert len(results) == num_samples_per_process + 1, (
|
| 777 |
+
f"Each Process should have even elements. Expected: {num_samples_per_process + 1}, Actual: {len(results)}"
|
| 778 |
+
)
|
| 779 |
+
else:
|
| 780 |
+
assert len(results) == num_samples_per_process, (
|
| 781 |
+
f"Each Process should have even elements. Expected: {num_samples_per_process}, Actual: {len(results)}"
|
| 782 |
+
)
|
| 783 |
+
state.wait_for_everyone()
|
| 784 |
+
|
| 785 |
+
|
| 786 |
+
def test_trigger():
|
| 787 |
+
accelerator = Accelerator()
|
| 788 |
+
# should start with being false
|
| 789 |
+
assert accelerator.check_trigger() is False
|
| 790 |
+
|
| 791 |
+
# set a breakpoint on the main process
|
| 792 |
+
if accelerator.is_main_process:
|
| 793 |
+
accelerator.set_trigger()
|
| 794 |
+
|
| 795 |
+
# check it's been activated across all processes
|
| 796 |
+
# calls `all_reduce` and triggers a sync
|
| 797 |
+
assert accelerator.check_trigger() is True
|
| 798 |
+
|
| 799 |
+
# check it's been reset after the sync
|
| 800 |
+
assert accelerator.check_trigger() is False
|
| 801 |
+
|
| 802 |
+
|
| 803 |
+
def test_reinstantiated_state():
|
| 804 |
+
import pytest
|
| 805 |
+
|
| 806 |
+
AcceleratorState._reset_state()
|
| 807 |
+
simple_model = torch.nn.Linear(1, 1)
|
| 808 |
+
# First define an accelerator
|
| 809 |
+
accelerator = Accelerator()
|
| 810 |
+
# Then call `reset_state`, breaking the state existing in the accelerator
|
| 811 |
+
AcceleratorState._reset_state()
|
| 812 |
+
# Now try and prepare a simple model, should raise the custom error early
|
| 813 |
+
with pytest.raises(AttributeError) as cm:
|
| 814 |
+
accelerator.prepare(simple_model)
|
| 815 |
+
assert "`AcceleratorState` object has no attribute" in str(cm.value.args[0])
|
| 816 |
+
assert "This happens if `AcceleratorState._reset_state()`" in str(cm.value.args[0])
|
| 817 |
+
|
| 818 |
+
|
| 819 |
+
def main():
|
| 820 |
+
accelerator = Accelerator()
|
| 821 |
+
state = accelerator.state
|
| 822 |
+
if state.local_process_index == 0:
|
| 823 |
+
print("**Initialization**")
|
| 824 |
+
init_state_check()
|
| 825 |
+
state.wait_for_everyone()
|
| 826 |
+
|
| 827 |
+
if state.distributed_type == DistributedType.MULTI_GPU:
|
| 828 |
+
num_processes_per_node = torch.cuda.device_count()
|
| 829 |
+
else:
|
| 830 |
+
num_processes_per_node = state.num_processes
|
| 831 |
+
|
| 832 |
+
# We only run this test on non-multinode
|
| 833 |
+
if num_processes_per_node == state.num_processes:
|
| 834 |
+
if state.process_index == 0:
|
| 835 |
+
print("\n**Test process execution**")
|
| 836 |
+
process_execution_check()
|
| 837 |
+
|
| 838 |
+
if state.process_index == 0:
|
| 839 |
+
print("\n**Test split between processes as a list**")
|
| 840 |
+
test_split_between_processes_list()
|
| 841 |
+
|
| 842 |
+
if state.process_index == 0:
|
| 843 |
+
print("\n**Test split between processes as a dict**")
|
| 844 |
+
test_split_between_processes_nested_dict()
|
| 845 |
+
|
| 846 |
+
if state.process_index == 0:
|
| 847 |
+
print("\n**Test split between processes as a tensor**")
|
| 848 |
+
test_split_between_processes_tensor()
|
| 849 |
+
|
| 850 |
+
if state.process_index == 0:
|
| 851 |
+
print("\n**Test split between processes evenly**")
|
| 852 |
+
test_split_between_processes_evenly()
|
| 853 |
+
|
| 854 |
+
if state.process_index == 0:
|
| 855 |
+
print("\n**Test split between processes as a datasets.Dataset**")
|
| 856 |
+
if is_datasets_available():
|
| 857 |
+
from datasets import Dataset as datasets_Dataset
|
| 858 |
+
|
| 859 |
+
test_split_between_processes_dataset(datasets_Dataset)
|
| 860 |
+
else:
|
| 861 |
+
print("Skipped because Hugging Face datasets is not available")
|
| 862 |
+
|
| 863 |
+
if state.local_process_index == 0:
|
| 864 |
+
print("\n**Test random number generator synchronization**")
|
| 865 |
+
rng_sync_check()
|
| 866 |
+
|
| 867 |
+
if state.local_process_index == 0:
|
| 868 |
+
print("\n**DataLoader integration test**")
|
| 869 |
+
dl_preparation_check()
|
| 870 |
+
if state.distributed_type != DistributedType.XLA:
|
| 871 |
+
central_dl_preparation_check()
|
| 872 |
+
custom_sampler_check()
|
| 873 |
+
check_seedable_sampler()
|
| 874 |
+
check_seedable_sampler_with_data_seed()
|
| 875 |
+
|
| 876 |
+
if state.num_processes > 1:
|
| 877 |
+
check_seedable_sampler_in_batch_sampler_shard()
|
| 878 |
+
|
| 879 |
+
# Trainings are not exactly the same in DeepSpeed and CPU mode
|
| 880 |
+
if state.distributed_type == DistributedType.DEEPSPEED:
|
| 881 |
+
return
|
| 882 |
+
|
| 883 |
+
if state.local_process_index == 0:
|
| 884 |
+
print("\n**Training integration test**")
|
| 885 |
+
training_check(use_seedable_sampler=False)
|
| 886 |
+
training_check(use_seedable_sampler=True)
|
| 887 |
+
|
| 888 |
+
if state.local_process_index == 0:
|
| 889 |
+
print("\n**Breakpoint trigger test**")
|
| 890 |
+
test_trigger()
|
| 891 |
+
|
| 892 |
+
if is_pytest_available():
|
| 893 |
+
if state.local_process_index == 0:
|
| 894 |
+
print("\n**Test reinstantiated state**")
|
| 895 |
+
test_reinstantiated_state()
|
| 896 |
+
|
| 897 |
+
state.destroy_process_group()
|
| 898 |
+
|
| 899 |
+
|
| 900 |
+
if __name__ == "__main__":
|
| 901 |
+
main()
|
venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_sync.py
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from copy import deepcopy
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
import torch.nn.functional as F
|
| 19 |
+
from torch.optim import AdamW
|
| 20 |
+
from torch.optim.lr_scheduler import LambdaLR
|
| 21 |
+
from torch.utils.data import DataLoader
|
| 22 |
+
|
| 23 |
+
from accelerate.accelerator import Accelerator, DataLoaderConfiguration, GradientAccumulationPlugin
|
| 24 |
+
from accelerate.state import GradientState
|
| 25 |
+
from accelerate.test_utils import RegressionDataset, RegressionModel
|
| 26 |
+
from accelerate.utils import DistributedType, set_seed
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def check_model_parameters(model_a, model_b, did_step, iteration, **kwargs):
|
| 30 |
+
for param, grad_param in zip(model_a.parameters(), model_b.parameters()):
|
| 31 |
+
if not param.requires_grad:
|
| 32 |
+
continue
|
| 33 |
+
if not did_step:
|
| 34 |
+
# Grads should not be in sync
|
| 35 |
+
assert torch.allclose(param.grad, grad_param.grad, **kwargs) is False, (
|
| 36 |
+
f"Gradients in sync when they should not be at iteration {iteration}:\nmodel_a grad ({param.grad}) == model_b grad ({grad_param.grad})"
|
| 37 |
+
)
|
| 38 |
+
else:
|
| 39 |
+
# Grads should be in sync
|
| 40 |
+
assert torch.allclose(param.grad, grad_param.grad, **kwargs) is True, (
|
| 41 |
+
f"Gradients not in sync when they should be at iteration {iteration}:\nmodel_a grad ({param.grad}) != model_b grad ({grad_param.grad})"
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def step_model(model, input, target, accelerator, do_backward=True):
|
| 46 |
+
model.train()
|
| 47 |
+
output = model(input)
|
| 48 |
+
loss = F.mse_loss(output, target.to(output.device))
|
| 49 |
+
if not do_backward:
|
| 50 |
+
loss /= accelerator.gradient_accumulation_steps
|
| 51 |
+
loss.backward()
|
| 52 |
+
else:
|
| 53 |
+
accelerator.backward(loss)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def get_training_setup(accelerator, sched=False):
|
| 57 |
+
"Returns everything needed to perform basic training"
|
| 58 |
+
set_seed(42)
|
| 59 |
+
model = RegressionModel()
|
| 60 |
+
ddp_model = deepcopy(model)
|
| 61 |
+
dset = RegressionDataset(length=80)
|
| 62 |
+
dataloader = DataLoader(dset, batch_size=16)
|
| 63 |
+
model.to(accelerator.device)
|
| 64 |
+
if sched:
|
| 65 |
+
opt = AdamW(params=model.parameters(), lr=1e-3)
|
| 66 |
+
ddp_opt = AdamW(params=ddp_model.parameters(), lr=1e-3)
|
| 67 |
+
sched = LambdaLR(opt, lr_lambda=lambda epoch: epoch**0.65)
|
| 68 |
+
ddp_sched = LambdaLR(ddp_opt, lr_lambda=lambda epoch: epoch**0.65)
|
| 69 |
+
# Make a copy of `model`
|
| 70 |
+
if sched:
|
| 71 |
+
ddp_model, ddp_opt, ddp_sched, dataloader = accelerator.prepare(ddp_model, ddp_opt, ddp_sched, dataloader)
|
| 72 |
+
else:
|
| 73 |
+
ddp_model, dataloader = accelerator.prepare(ddp_model, dataloader)
|
| 74 |
+
if sched:
|
| 75 |
+
return (model, opt, sched, dataloader, ddp_model, ddp_opt, ddp_sched)
|
| 76 |
+
return model, ddp_model, dataloader
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def test_noop_sync(accelerator):
|
| 80 |
+
# Test when on a single CPU or GPU that the context manager does nothing
|
| 81 |
+
model, ddp_model, dataloader = get_training_setup(accelerator)
|
| 82 |
+
# Use a single batch
|
| 83 |
+
ddp_input, ddp_target = next(iter(dataloader)).values()
|
| 84 |
+
for iteration in range(3):
|
| 85 |
+
# Gather the distributed inputs and targs for the base model
|
| 86 |
+
input, target = accelerator.gather((ddp_input, ddp_target))
|
| 87 |
+
input, target = input.to(accelerator.device), target.to(accelerator.device)
|
| 88 |
+
# Perform our initial ground truth step in non "DDP"
|
| 89 |
+
step_model(model, input, target, accelerator)
|
| 90 |
+
# Do "gradient accumulation" (noop)
|
| 91 |
+
if iteration % 2 == 0:
|
| 92 |
+
# Accumulate grads locally
|
| 93 |
+
with accelerator.no_sync(ddp_model):
|
| 94 |
+
step_model(ddp_model, ddp_input, ddp_target, accelerator)
|
| 95 |
+
else:
|
| 96 |
+
# Sync grads
|
| 97 |
+
step_model(ddp_model, ddp_input, ddp_target, accelerator)
|
| 98 |
+
|
| 99 |
+
# Since `no_sync` is a noop, `ddp_model` and `model` grads should always be in sync
|
| 100 |
+
check_model_parameters(model, ddp_model, True, iteration)
|
| 101 |
+
for param, ddp_param in zip(model.parameters(), ddp_model.parameters()):
|
| 102 |
+
if not param.requires_grad:
|
| 103 |
+
continue
|
| 104 |
+
assert torch.allclose(param.grad, ddp_param.grad), (
|
| 105 |
+
f"Gradients not in sync when they should be:\nModel grad ({param.grad}) != DDP grad ({ddp_param.grad})"
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
# Shuffle ddp_input on each iteration
|
| 109 |
+
torch.manual_seed(1337 + iteration)
|
| 110 |
+
ddp_input = ddp_input[torch.randperm(len(ddp_input))]
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def test_distributed_sync(accelerator):
|
| 114 |
+
# Test on distributed setup that context manager behaves properly
|
| 115 |
+
model, ddp_model, dataloader = get_training_setup(accelerator)
|
| 116 |
+
# Use a single batch
|
| 117 |
+
ddp_input, ddp_target = next(iter(dataloader)).values()
|
| 118 |
+
for iteration in range(3):
|
| 119 |
+
# Gather the distributed inputs and targs for the base model
|
| 120 |
+
input, target = accelerator.gather((ddp_input, ddp_target))
|
| 121 |
+
input, target = input.to(accelerator.device), target.to(accelerator.device)
|
| 122 |
+
# Perform our initial ground truth step in non "DDP"
|
| 123 |
+
step_model(model, input, target, accelerator)
|
| 124 |
+
# Do "gradient accumulation" (noop)
|
| 125 |
+
if iteration % 2 == 0:
|
| 126 |
+
# Accumulate grads locally
|
| 127 |
+
with accelerator.no_sync(ddp_model):
|
| 128 |
+
step_model(ddp_model, ddp_input, ddp_target, accelerator)
|
| 129 |
+
else:
|
| 130 |
+
# Sync grads
|
| 131 |
+
step_model(ddp_model, ddp_input, ddp_target, accelerator)
|
| 132 |
+
|
| 133 |
+
# DDP model and model should only be in sync when not (iteration % 2 == 0)
|
| 134 |
+
for param, ddp_param in zip(model.parameters(), ddp_model.parameters()):
|
| 135 |
+
if not param.requires_grad:
|
| 136 |
+
continue
|
| 137 |
+
if iteration % 2 == 0:
|
| 138 |
+
# Grads should not be in sync
|
| 139 |
+
assert torch.allclose(param.grad, ddp_param.grad) is False, (
|
| 140 |
+
f"Gradients in sync when they should not be:\nModel grad ({param.grad}) == DDP grad ({ddp_param.grad})"
|
| 141 |
+
)
|
| 142 |
+
else:
|
| 143 |
+
# Grads should be in sync
|
| 144 |
+
assert torch.allclose(param.grad, ddp_param.grad) is True, (
|
| 145 |
+
f"Gradients not in sync when they should be:\nModel grad ({param.grad}) != DDP grad ({ddp_param.grad})"
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
# Shuffle ddp_input on each iteration
|
| 149 |
+
torch.manual_seed(1337 + iteration)
|
| 150 |
+
ddp_input = ddp_input[torch.randperm(len(ddp_input))]
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def test_distributed_sync_multiple_fwd(accelerator):
|
| 154 |
+
# Test on distributed setup that context manager behaves properly when used with multiple forwards followed by multiple backwards
|
| 155 |
+
model, ddp_model, dataloader = get_training_setup(accelerator)
|
| 156 |
+
# Do multiple forwards
|
| 157 |
+
losses = []
|
| 158 |
+
num_iterations = 3
|
| 159 |
+
for iteration in range(num_iterations):
|
| 160 |
+
ddp_input, ddp_target = next(iter(dataloader)).values()
|
| 161 |
+
|
| 162 |
+
# Gather the distributed inputs and targs for the base model
|
| 163 |
+
input, target = accelerator.gather((ddp_input, ddp_target))
|
| 164 |
+
input, target = input.to(accelerator.device), target.to(accelerator.device)
|
| 165 |
+
|
| 166 |
+
# Perform our initial ground truth step in non "DDP"
|
| 167 |
+
step_model(model, input, target, accelerator)
|
| 168 |
+
|
| 169 |
+
# Accumulate grads locally
|
| 170 |
+
with accelerator.no_sync(ddp_model):
|
| 171 |
+
ddp_output = ddp_model(ddp_input)
|
| 172 |
+
loss = F.mse_loss(ddp_output, ddp_target.to(ddp_output.device))
|
| 173 |
+
losses.append(loss)
|
| 174 |
+
|
| 175 |
+
# Do multiple backwards and sync only at the last backward
|
| 176 |
+
for iteration in range(num_iterations):
|
| 177 |
+
loss = losses[iteration]
|
| 178 |
+
|
| 179 |
+
if iteration < num_iterations - 1:
|
| 180 |
+
# Accumulate grads locally
|
| 181 |
+
accelerator.backward(loss)
|
| 182 |
+
|
| 183 |
+
# DDP model and model should only be in sync after last backward
|
| 184 |
+
for param, ddp_param in zip(model.parameters(), ddp_model.parameters()):
|
| 185 |
+
if not param.requires_grad:
|
| 186 |
+
continue
|
| 187 |
+
# Grads should not be in sync
|
| 188 |
+
assert torch.allclose(param.grad, ddp_param.grad) is False, (
|
| 189 |
+
f"Gradients in sync when they should not be:\nModel grad ({param.grad}) == DDP grad ({ddp_param.grad})"
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
else:
|
| 193 |
+
# Sync grads if last backward
|
| 194 |
+
with accelerator.trigger_sync_in_backward(ddp_model):
|
| 195 |
+
accelerator.backward(loss)
|
| 196 |
+
|
| 197 |
+
# DDP model and model should only be in sync after last backward
|
| 198 |
+
for param, ddp_param in zip(model.parameters(), ddp_model.parameters()):
|
| 199 |
+
if not param.requires_grad:
|
| 200 |
+
continue
|
| 201 |
+
# Grads should be in sync
|
| 202 |
+
assert torch.allclose(param.grad, ddp_param.grad) is True, (
|
| 203 |
+
f"Gradients not in sync when they should be:\nModel grad ({param.grad}) != DDP grad ({ddp_param.grad})"
|
| 204 |
+
)
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def test_gradient_accumulation(split_batches=False, dispatch_batches=False, sync_each_batch=False):
|
| 208 |
+
gradient_accumulation_plugin = GradientAccumulationPlugin(num_steps=2, sync_each_batch=sync_each_batch)
|
| 209 |
+
dataloader_config = DataLoaderConfiguration(split_batches=split_batches, dispatch_batches=dispatch_batches)
|
| 210 |
+
accelerator = Accelerator(
|
| 211 |
+
dataloader_config=dataloader_config,
|
| 212 |
+
gradient_accumulation_plugin=gradient_accumulation_plugin,
|
| 213 |
+
)
|
| 214 |
+
# Test that context manager behaves properly
|
| 215 |
+
model, ddp_model, dataloader = get_training_setup(accelerator)
|
| 216 |
+
for iteration, batch in enumerate(dataloader):
|
| 217 |
+
ddp_input, ddp_target = batch.values()
|
| 218 |
+
# Gather the distributed inputs and targs for the base model
|
| 219 |
+
input, target = accelerator.gather((ddp_input, ddp_target))
|
| 220 |
+
input, target = input.to(accelerator.device), target.to(accelerator.device)
|
| 221 |
+
# Perform our initial ground truth step in non "DDP"
|
| 222 |
+
step_model(model, input, target, accelerator, False)
|
| 223 |
+
# Do "gradient accumulation" (noop)
|
| 224 |
+
with accelerator.accumulate(ddp_model):
|
| 225 |
+
step_model(ddp_model, ddp_input, ddp_target, accelerator)
|
| 226 |
+
|
| 227 |
+
# DDP model and model should only be in sync when not (iteration % 2 == 0)
|
| 228 |
+
for param, ddp_param in zip(model.parameters(), ddp_model.parameters()):
|
| 229 |
+
if not param.requires_grad:
|
| 230 |
+
continue
|
| 231 |
+
if ((iteration + 1) % 2 == 0) or (iteration == len(dataloader) - 1) or sync_each_batch:
|
| 232 |
+
# Grads should be in sync
|
| 233 |
+
assert torch.allclose(param.grad, ddp_param.grad) is True, (
|
| 234 |
+
f"Gradients not in sync when they should be at iteration {iteration}:\nModel grad ({param.grad}) != DDP grad ({ddp_param.grad})"
|
| 235 |
+
)
|
| 236 |
+
else:
|
| 237 |
+
# Grads should not be in sync
|
| 238 |
+
assert torch.allclose(param.grad, ddp_param.grad) is False, (
|
| 239 |
+
f"Gradients in sync when they should not be at iteration {iteration}:\nModel grad ({param.grad}) == DDP grad ({ddp_param.grad})"
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
# Shuffle ddp_input on each iteration
|
| 243 |
+
torch.manual_seed(1337 + iteration)
|
| 244 |
+
ddp_input = ddp_input[torch.randperm(len(ddp_input))]
|
| 245 |
+
GradientState._reset_state()
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def test_gradient_accumulation_with_opt_and_scheduler(
|
| 249 |
+
split_batches=False, dispatch_batches=False, sync_each_batch=False
|
| 250 |
+
):
|
| 251 |
+
gradient_accumulation_plugin = GradientAccumulationPlugin(num_steps=2, sync_each_batch=sync_each_batch)
|
| 252 |
+
dataloader_config = DataLoaderConfiguration(split_batches=split_batches, dispatch_batches=dispatch_batches)
|
| 253 |
+
accelerator = Accelerator(
|
| 254 |
+
dataloader_config=dataloader_config,
|
| 255 |
+
gradient_accumulation_plugin=gradient_accumulation_plugin,
|
| 256 |
+
)
|
| 257 |
+
# Test that context manager behaves properly
|
| 258 |
+
model, opt, sched, dataloader, ddp_model, ddp_opt, ddp_sched = get_training_setup(accelerator, True)
|
| 259 |
+
for iteration, batch in enumerate(dataloader):
|
| 260 |
+
ddp_input, ddp_target = batch.values()
|
| 261 |
+
# Gather the distributed inputs and targs for the base model
|
| 262 |
+
input, target = accelerator.gather((ddp_input, ddp_target))
|
| 263 |
+
input, target = input.to(accelerator.device), target.to(accelerator.device)
|
| 264 |
+
# Perform our initial ground truth step in non "DDP"
|
| 265 |
+
model.train()
|
| 266 |
+
ddp_model.train()
|
| 267 |
+
step_model(model, input, target, accelerator, False)
|
| 268 |
+
opt.step()
|
| 269 |
+
|
| 270 |
+
if ((iteration + 1) % 2 == 0) or ((iteration + 1) == len(dataloader)):
|
| 271 |
+
if split_batches:
|
| 272 |
+
sched.step()
|
| 273 |
+
else:
|
| 274 |
+
for _ in range(accelerator.num_processes):
|
| 275 |
+
sched.step()
|
| 276 |
+
|
| 277 |
+
# Perform gradient accumulation under wrapper
|
| 278 |
+
with accelerator.accumulate(ddp_model):
|
| 279 |
+
step_model(ddp_model, ddp_input, ddp_target, accelerator)
|
| 280 |
+
ddp_opt.step()
|
| 281 |
+
ddp_sched.step()
|
| 282 |
+
|
| 283 |
+
# Learning rates should be the same
|
| 284 |
+
assert opt.param_groups[0]["lr"] == ddp_opt.param_groups[0]["lr"], (
|
| 285 |
+
f"Learning rates found in each optimizer did not align\nopt: {opt.param_groups[0]['lr']}\nDDP opt: {ddp_opt.param_groups[0]['lr']}\n"
|
| 286 |
+
)
|
| 287 |
+
did_step = (((iteration + 1) % 2) == 0) or ((iteration + 1) == len(dataloader))
|
| 288 |
+
if accelerator.num_processes > 1:
|
| 289 |
+
check_model_parameters(
|
| 290 |
+
model,
|
| 291 |
+
ddp_model,
|
| 292 |
+
did_step or sync_each_batch, # syncs at each grad_accum interval of if sync_each_batch==True
|
| 293 |
+
iteration,
|
| 294 |
+
rtol=1e-3, # needs a relative tolerance due to roundoff errors
|
| 295 |
+
)
|
| 296 |
+
|
| 297 |
+
if did_step:
|
| 298 |
+
opt.zero_grad() # flush gradients every accum step
|
| 299 |
+
ddp_opt.zero_grad()
|
| 300 |
+
|
| 301 |
+
# Shuffle ddp_input on each iteration
|
| 302 |
+
torch.manual_seed(1337 + iteration)
|
| 303 |
+
GradientState._reset_state()
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
def test_dataloader_break():
|
| 307 |
+
accelerator = Accelerator()
|
| 308 |
+
first_dset = RegressionDataset(length=80)
|
| 309 |
+
first_dataloader = DataLoader(first_dset, batch_size=16)
|
| 310 |
+
second_dset = RegressionDataset(length=96)
|
| 311 |
+
second_dataloader = DataLoader(second_dset, batch_size=16)
|
| 312 |
+
first_dataloader, second_dataloader = accelerator.prepare(first_dataloader, second_dataloader)
|
| 313 |
+
|
| 314 |
+
assert accelerator.gradient_state.active_dataloader is None
|
| 315 |
+
for iteration, _ in enumerate(first_dataloader):
|
| 316 |
+
assert id(accelerator.gradient_state.active_dataloader) == id(first_dataloader)
|
| 317 |
+
if iteration < len(first_dataloader) - 1:
|
| 318 |
+
assert not accelerator.gradient_state.end_of_dataloader
|
| 319 |
+
if iteration == 1:
|
| 320 |
+
for batch_num, _ in enumerate(second_dataloader):
|
| 321 |
+
assert id(accelerator.gradient_state.active_dataloader) == id(second_dataloader)
|
| 322 |
+
if batch_num < len(second_dataloader) - 1:
|
| 323 |
+
assert not accelerator.gradient_state.end_of_dataloader
|
| 324 |
+
else:
|
| 325 |
+
assert accelerator.gradient_state.end_of_dataloader
|
| 326 |
+
else:
|
| 327 |
+
assert accelerator.gradient_state.end_of_dataloader
|
| 328 |
+
assert accelerator.gradient_state.active_dataloader is None
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
def main():
|
| 332 |
+
accelerator = Accelerator()
|
| 333 |
+
state = accelerator.state
|
| 334 |
+
if state.local_process_index == 0:
|
| 335 |
+
print("**Test `accumulate` gradient accumulation with dataloader break**")
|
| 336 |
+
if state.distributed_type != DistributedType.XLA:
|
| 337 |
+
test_dataloader_break()
|
| 338 |
+
if state.distributed_type == DistributedType.NO:
|
| 339 |
+
if state.local_process_index == 0:
|
| 340 |
+
print("**Test NOOP `no_sync` context manager**")
|
| 341 |
+
test_noop_sync(accelerator)
|
| 342 |
+
if state.distributed_type in (
|
| 343 |
+
DistributedType.MULTI_GPU,
|
| 344 |
+
DistributedType.MULTI_NPU,
|
| 345 |
+
DistributedType.MULTI_MLU,
|
| 346 |
+
DistributedType.MULTI_SDAA,
|
| 347 |
+
DistributedType.MULTI_MUSA,
|
| 348 |
+
DistributedType.MULTI_CPU,
|
| 349 |
+
DistributedType.MULTI_HPU,
|
| 350 |
+
):
|
| 351 |
+
if state.local_process_index == 0:
|
| 352 |
+
print("**Test Distributed `no_sync` context manager**")
|
| 353 |
+
test_distributed_sync(accelerator)
|
| 354 |
+
if state.local_process_index == 0:
|
| 355 |
+
print("**Test Distributed `no_sync` context manager with multiple forwards**")
|
| 356 |
+
test_distributed_sync_multiple_fwd(accelerator)
|
| 357 |
+
if state.distributed_type in (
|
| 358 |
+
DistributedType.MULTI_GPU,
|
| 359 |
+
DistributedType.MULTI_NPU,
|
| 360 |
+
DistributedType.MULTI_MLU,
|
| 361 |
+
DistributedType.MULTI_SDAA,
|
| 362 |
+
DistributedType.MULTI_MUSA,
|
| 363 |
+
DistributedType.MULTI_HPU,
|
| 364 |
+
):
|
| 365 |
+
for split_batch in [True, False]:
|
| 366 |
+
for dispatch_batches in [True, False]:
|
| 367 |
+
for sync_each_batch in [True, False]:
|
| 368 |
+
if state.local_process_index == 0:
|
| 369 |
+
print(
|
| 370 |
+
"**Test `accumulate` gradient accumulation, ",
|
| 371 |
+
f"`split_batches={split_batch}` and `dispatch_batches={dispatch_batches}` and `sync_each_batch={sync_each_batch}`**",
|
| 372 |
+
)
|
| 373 |
+
test_gradient_accumulation(split_batch, dispatch_batches, sync_each_batch)
|
| 374 |
+
|
| 375 |
+
# Currently will break on torch 2.0 +, need to investigate why
|
| 376 |
+
if state.local_process_index == 0:
|
| 377 |
+
print(
|
| 378 |
+
"**Test `accumulate` gradient accumulation with optimizer and scheduler, ",
|
| 379 |
+
"`split_batches=False`, `dispatch_batches=False`, `sync_each_batch=False`**",
|
| 380 |
+
)
|
| 381 |
+
test_gradient_accumulation_with_opt_and_scheduler()
|
| 382 |
+
if state.distributed_type in (
|
| 383 |
+
DistributedType.MULTI_GPU,
|
| 384 |
+
DistributedType.MULTI_NPU,
|
| 385 |
+
DistributedType.MULTI_MLU,
|
| 386 |
+
DistributedType.MULTI_SDAA,
|
| 387 |
+
DistributedType.MULTI_MUSA,
|
| 388 |
+
DistributedType.MULTI_HPU,
|
| 389 |
+
):
|
| 390 |
+
for split_batch in [True, False]:
|
| 391 |
+
for dispatch_batches in [True, False]:
|
| 392 |
+
for sync_each_batch in [True, False]:
|
| 393 |
+
if not split_batch and not dispatch_batches and not sync_each_batch:
|
| 394 |
+
continue
|
| 395 |
+
if state.local_process_index == 0:
|
| 396 |
+
print(
|
| 397 |
+
"**Test `accumulate` gradient accumulation with optimizer and scheduler, ",
|
| 398 |
+
f"`split_batches={split_batch}` and `dispatch_batches={dispatch_batches}` and `sync_each_batch={sync_each_batch}`**",
|
| 399 |
+
)
|
| 400 |
+
test_gradient_accumulation_with_opt_and_scheduler(split_batch, dispatch_batches, sync_each_batch)
|
| 401 |
+
state.destroy_process_group()
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def _mp_fn(index):
|
| 405 |
+
# For xla_spawn (TPUs)
|
| 406 |
+
main()
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
if __name__ == "__main__":
|
| 410 |
+
main()
|
venv/lib/python3.11/site-packages/accelerate/test_utils/testing.py
ADDED
|
@@ -0,0 +1,814 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import asyncio
|
| 16 |
+
import inspect
|
| 17 |
+
import io
|
| 18 |
+
import os
|
| 19 |
+
import shutil
|
| 20 |
+
import subprocess
|
| 21 |
+
import sys
|
| 22 |
+
import tempfile
|
| 23 |
+
import unittest
|
| 24 |
+
from contextlib import contextmanager
|
| 25 |
+
from functools import partial
|
| 26 |
+
from pathlib import Path
|
| 27 |
+
from typing import Union
|
| 28 |
+
from unittest import mock
|
| 29 |
+
|
| 30 |
+
import torch
|
| 31 |
+
|
| 32 |
+
import accelerate
|
| 33 |
+
|
| 34 |
+
from ..state import AcceleratorState
|
| 35 |
+
from ..utils import (
|
| 36 |
+
check_cuda_fp8_capability,
|
| 37 |
+
gather,
|
| 38 |
+
is_bnb_available,
|
| 39 |
+
is_clearml_available,
|
| 40 |
+
is_comet_ml_available,
|
| 41 |
+
is_cuda_available,
|
| 42 |
+
is_datasets_available,
|
| 43 |
+
is_deepspeed_available,
|
| 44 |
+
is_dvclive_available,
|
| 45 |
+
is_fp8_available,
|
| 46 |
+
is_fp16_available,
|
| 47 |
+
is_habana_gaudi1,
|
| 48 |
+
is_hpu_available,
|
| 49 |
+
is_import_timer_available,
|
| 50 |
+
is_matplotlib_available,
|
| 51 |
+
is_mlflow_available,
|
| 52 |
+
is_mlu_available,
|
| 53 |
+
is_mps_available,
|
| 54 |
+
is_musa_available,
|
| 55 |
+
is_npu_available,
|
| 56 |
+
is_pandas_available,
|
| 57 |
+
is_pippy_available,
|
| 58 |
+
is_pytest_available,
|
| 59 |
+
is_schedulefree_available,
|
| 60 |
+
is_sdaa_available,
|
| 61 |
+
is_tensorboard_available,
|
| 62 |
+
is_timm_available,
|
| 63 |
+
is_torch_version,
|
| 64 |
+
is_torch_xla_available,
|
| 65 |
+
is_torchao_available,
|
| 66 |
+
is_torchdata_stateful_dataloader_available,
|
| 67 |
+
is_torchvision_available,
|
| 68 |
+
is_transformer_engine_available,
|
| 69 |
+
is_transformers_available,
|
| 70 |
+
is_triton_available,
|
| 71 |
+
is_wandb_available,
|
| 72 |
+
is_xpu_available,
|
| 73 |
+
str_to_bool,
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def get_backend():
|
| 78 |
+
if is_torch_xla_available():
|
| 79 |
+
return "xla", torch.cuda.device_count(), torch.cuda.memory_allocated
|
| 80 |
+
elif is_cuda_available():
|
| 81 |
+
return "cuda", torch.cuda.device_count(), torch.cuda.memory_allocated
|
| 82 |
+
elif is_mps_available(min_version="2.0"):
|
| 83 |
+
return "mps", 1, torch.mps.current_allocated_memory
|
| 84 |
+
elif is_mps_available():
|
| 85 |
+
return "mps", 1, lambda: 0
|
| 86 |
+
elif is_mlu_available():
|
| 87 |
+
return "mlu", torch.mlu.device_count(), torch.mlu.memory_allocated
|
| 88 |
+
elif is_sdaa_available():
|
| 89 |
+
return "sdaa", torch.sdaa.device_count(), torch.sdaa.memory_allocated
|
| 90 |
+
elif is_musa_available():
|
| 91 |
+
return "musa", torch.musa.device_count(), torch.musa.memory_allocated
|
| 92 |
+
elif is_npu_available():
|
| 93 |
+
return "npu", torch.npu.device_count(), torch.npu.memory_allocated
|
| 94 |
+
elif is_xpu_available():
|
| 95 |
+
return "xpu", torch.xpu.device_count(), torch.xpu.memory_allocated
|
| 96 |
+
elif is_hpu_available():
|
| 97 |
+
return "hpu", torch.hpu.device_count(), torch.hpu.memory_allocated
|
| 98 |
+
else:
|
| 99 |
+
return "cpu", 1, lambda: 0
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
torch_device, device_count, memory_allocated_func = get_backend()
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def get_launch_command(**kwargs) -> list:
|
| 106 |
+
"""
|
| 107 |
+
Wraps around `kwargs` to help simplify launching from `subprocess`.
|
| 108 |
+
|
| 109 |
+
Example:
|
| 110 |
+
```python
|
| 111 |
+
# returns ['accelerate', 'launch', '--num_processes=2', '--device_count=2']
|
| 112 |
+
get_launch_command(num_processes=2, device_count=2)
|
| 113 |
+
```
|
| 114 |
+
"""
|
| 115 |
+
command = ["accelerate", "launch"]
|
| 116 |
+
for k, v in kwargs.items():
|
| 117 |
+
if isinstance(v, bool) and v:
|
| 118 |
+
command.append(f"--{k}")
|
| 119 |
+
elif v is not None:
|
| 120 |
+
command.append(f"--{k}={v}")
|
| 121 |
+
return command
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
DEFAULT_LAUNCH_COMMAND = get_launch_command(num_processes=device_count, monitor_interval=0.1)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def parse_flag_from_env(key, default=False):
|
| 128 |
+
try:
|
| 129 |
+
value = os.environ[key]
|
| 130 |
+
except KeyError:
|
| 131 |
+
# KEY isn't set, default to `default`.
|
| 132 |
+
_value = default
|
| 133 |
+
else:
|
| 134 |
+
# KEY is set, convert it to True or False.
|
| 135 |
+
try:
|
| 136 |
+
_value = str_to_bool(value)
|
| 137 |
+
except ValueError:
|
| 138 |
+
# More values are supported, but let's keep the message simple.
|
| 139 |
+
raise ValueError(f"If set, {key} must be yes or no.")
|
| 140 |
+
return _value
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
_run_slow_tests = parse_flag_from_env("RUN_SLOW", default=False)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def skip(test_case):
|
| 147 |
+
"Decorator that skips a test unconditionally"
|
| 148 |
+
return unittest.skip("Test was skipped")(test_case)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def slow(test_case):
|
| 152 |
+
"""
|
| 153 |
+
Decorator marking a test as slow. Slow tests are skipped by default. Set the RUN_SLOW environment variable to a
|
| 154 |
+
truthy value to run them.
|
| 155 |
+
"""
|
| 156 |
+
return unittest.skipUnless(_run_slow_tests, "test is slow")(test_case)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def require_cpu(test_case):
|
| 160 |
+
"""
|
| 161 |
+
Decorator marking a test that must be only ran on the CPU. These tests are skipped when a GPU is available.
|
| 162 |
+
"""
|
| 163 |
+
return unittest.skipUnless(torch_device == "cpu", "test requires only a CPU")(test_case)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def require_non_cpu(test_case):
|
| 167 |
+
"""
|
| 168 |
+
Decorator marking a test that requires a hardware accelerator backend. These tests are skipped when there are no
|
| 169 |
+
hardware accelerator available.
|
| 170 |
+
"""
|
| 171 |
+
return unittest.skipUnless(torch_device != "cpu", "test requires a GPU")(test_case)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def require_cuda(test_case):
|
| 175 |
+
"""
|
| 176 |
+
Decorator marking a test that requires CUDA. These tests are skipped when there are no GPU available or when
|
| 177 |
+
TorchXLA is available.
|
| 178 |
+
"""
|
| 179 |
+
return unittest.skipUnless(is_cuda_available() and not is_torch_xla_available(), "test requires a GPU")(test_case)
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def require_cuda_or_hpu(test_case):
|
| 183 |
+
"""
|
| 184 |
+
Decorator marking a test that requires CUDA or HPU. These tests are skipped when there are no GPU available or when
|
| 185 |
+
TorchXLA is available.
|
| 186 |
+
"""
|
| 187 |
+
return unittest.skipUnless(
|
| 188 |
+
(is_cuda_available() and not is_torch_xla_available()) or is_hpu_available(), "test requires a GPU or HPU"
|
| 189 |
+
)(test_case)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def require_xpu(test_case):
|
| 193 |
+
"""
|
| 194 |
+
Decorator marking a test that requires XPU. These tests are skipped when there are no XPU available.
|
| 195 |
+
"""
|
| 196 |
+
return unittest.skipUnless(is_xpu_available(), "test requires a XPU")(test_case)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def require_cuda_or_xpu(test_case):
|
| 200 |
+
"""
|
| 201 |
+
Decorator marking a test that requires CUDA or XPU. These tests are skipped when there are no GPU available or when
|
| 202 |
+
TorchXLA is available.
|
| 203 |
+
"""
|
| 204 |
+
cuda_condition = is_cuda_available() and not is_torch_xla_available()
|
| 205 |
+
xpu_condition = is_xpu_available()
|
| 206 |
+
return unittest.skipUnless(cuda_condition or xpu_condition, "test requires a CUDA GPU or XPU")(test_case)
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def require_non_xpu(test_case):
|
| 210 |
+
"""
|
| 211 |
+
Decorator marking a test that should be skipped for XPU.
|
| 212 |
+
"""
|
| 213 |
+
return unittest.skipUnless(torch_device != "xpu", "test requires a non-XPU")(test_case)
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def require_non_hpu(test_case):
|
| 217 |
+
"""
|
| 218 |
+
Decorator marking a test that should be skipped for HPU.
|
| 219 |
+
"""
|
| 220 |
+
return unittest.skipUnless(torch_device != "hpu", "test requires a non-HPU")(test_case)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def require_fp16(test_case):
|
| 224 |
+
"""
|
| 225 |
+
Decorator marking a test that requires FP16. These tests are skipped when FP16 is not supported.
|
| 226 |
+
"""
|
| 227 |
+
|
| 228 |
+
return unittest.skipUnless(is_fp16_available(), "test requires FP16 support")(test_case)
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def require_fp8(test_case):
|
| 232 |
+
"""
|
| 233 |
+
Decorator marking a test that requires FP8. These tests are skipped when FP8 is not supported.
|
| 234 |
+
"""
|
| 235 |
+
|
| 236 |
+
# is_fp8_available only checks for libraries
|
| 237 |
+
# ideally it should check for device capability as well
|
| 238 |
+
fp8_is_available = is_fp8_available()
|
| 239 |
+
|
| 240 |
+
if torch.cuda.is_available() and not check_cuda_fp8_capability():
|
| 241 |
+
fp8_is_available = False
|
| 242 |
+
|
| 243 |
+
if is_hpu_available() and is_habana_gaudi1():
|
| 244 |
+
fp8_is_available = False
|
| 245 |
+
|
| 246 |
+
return unittest.skipUnless(fp8_is_available, "test requires FP8 support")(test_case)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def require_mlu(test_case):
|
| 250 |
+
"""
|
| 251 |
+
Decorator marking a test that requires MLU. These tests are skipped when there are no MLU available.
|
| 252 |
+
"""
|
| 253 |
+
return unittest.skipUnless(is_mlu_available(), "test require a MLU")(test_case)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def require_sdaa(test_case):
|
| 257 |
+
"""
|
| 258 |
+
Decorator marking a test that requires SDAA. These tests are skipped when there are no SDAA available.
|
| 259 |
+
"""
|
| 260 |
+
return unittest.skipUnless(is_sdaa_available(), "test require a SDAA")(test_case)
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def require_musa(test_case):
|
| 264 |
+
"""
|
| 265 |
+
Decorator marking a test that requires MUSA. These tests are skipped when there are no MUSA available.
|
| 266 |
+
"""
|
| 267 |
+
return unittest.skipUnless(is_musa_available(), "test require a MUSA")(test_case)
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
def require_npu(test_case):
|
| 271 |
+
"""
|
| 272 |
+
Decorator marking a test that requires NPU. These tests are skipped when there are no NPU available.
|
| 273 |
+
"""
|
| 274 |
+
return unittest.skipUnless(is_npu_available(), "test require a NPU")(test_case)
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def require_mps(test_case):
|
| 278 |
+
"""
|
| 279 |
+
Decorator marking a test that requires MPS backend. These tests are skipped when torch doesn't support `mps`
|
| 280 |
+
backend.
|
| 281 |
+
"""
|
| 282 |
+
return unittest.skipUnless(is_mps_available(), "test requires a `mps` backend support in `torch`")(test_case)
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
def require_huggingface_suite(test_case):
|
| 286 |
+
"""
|
| 287 |
+
Decorator marking a test that requires transformers and datasets. These tests are skipped when they are not.
|
| 288 |
+
"""
|
| 289 |
+
return unittest.skipUnless(
|
| 290 |
+
is_transformers_available() and is_datasets_available(),
|
| 291 |
+
"test requires the Hugging Face suite",
|
| 292 |
+
)(test_case)
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def require_transformers(test_case):
|
| 296 |
+
"""
|
| 297 |
+
Decorator marking a test that requires transformers. These tests are skipped when they are not.
|
| 298 |
+
"""
|
| 299 |
+
return unittest.skipUnless(is_transformers_available(), "test requires the transformers library")(test_case)
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def require_timm(test_case):
|
| 303 |
+
"""
|
| 304 |
+
Decorator marking a test that requires timm. These tests are skipped when they are not.
|
| 305 |
+
"""
|
| 306 |
+
return unittest.skipUnless(is_timm_available(), "test requires the timm library")(test_case)
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def require_torchvision(test_case):
|
| 310 |
+
"""
|
| 311 |
+
Decorator marking a test that requires torchvision. These tests are skipped when they are not.
|
| 312 |
+
"""
|
| 313 |
+
return unittest.skipUnless(is_torchvision_available(), "test requires the torchvision library")(test_case)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def require_triton(test_case):
|
| 317 |
+
"""
|
| 318 |
+
Decorator marking a test that requires triton. These tests are skipped when they are not.
|
| 319 |
+
"""
|
| 320 |
+
return unittest.skipUnless(is_triton_available(), "test requires the triton library")(test_case)
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
def require_schedulefree(test_case):
|
| 324 |
+
"""
|
| 325 |
+
Decorator marking a test that requires schedulefree. These tests are skipped when they are not.
|
| 326 |
+
"""
|
| 327 |
+
return unittest.skipUnless(is_schedulefree_available(), "test requires the schedulefree library")(test_case)
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def require_bnb(test_case):
|
| 331 |
+
"""
|
| 332 |
+
Decorator marking a test that requires bitsandbytes. These tests are skipped when they are not.
|
| 333 |
+
"""
|
| 334 |
+
return unittest.skipUnless(is_bnb_available(), "test requires the bitsandbytes library")(test_case)
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def require_tpu(test_case):
|
| 338 |
+
"""
|
| 339 |
+
Decorator marking a test that requires TPUs. These tests are skipped when there are no TPUs available.
|
| 340 |
+
"""
|
| 341 |
+
return unittest.skipUnless(is_torch_xla_available(check_is_tpu=True), "test requires TPU")(test_case)
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
def require_non_torch_xla(test_case):
|
| 345 |
+
"""
|
| 346 |
+
Decorator marking a test as requiring an environment without TorchXLA. These tests are skipped when TorchXLA is
|
| 347 |
+
available.
|
| 348 |
+
"""
|
| 349 |
+
return unittest.skipUnless(not is_torch_xla_available(), "test requires an env without TorchXLA")(test_case)
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
def require_single_device(test_case):
|
| 353 |
+
"""
|
| 354 |
+
Decorator marking a test that requires a single device. These tests are skipped when there is no hardware
|
| 355 |
+
accelerator available or number of devices is more than one.
|
| 356 |
+
"""
|
| 357 |
+
return unittest.skipUnless(
|
| 358 |
+
torch_device != "cpu" and device_count == 1, "test requires a single device accelerator"
|
| 359 |
+
)(test_case)
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
def require_single_gpu(test_case):
|
| 363 |
+
"""
|
| 364 |
+
Decorator marking a test that requires CUDA on a single GPU. These tests are skipped when there are no GPU
|
| 365 |
+
available or number of GPUs is more than one.
|
| 366 |
+
"""
|
| 367 |
+
return unittest.skipUnless(torch.cuda.device_count() == 1, "test requires a GPU")(test_case)
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
def require_single_xpu(test_case):
|
| 371 |
+
"""
|
| 372 |
+
Decorator marking a test that requires CUDA on a single XPU. These tests are skipped when there are no XPU
|
| 373 |
+
available or number of xPUs is more than one.
|
| 374 |
+
"""
|
| 375 |
+
return unittest.skipUnless(torch.xpu.device_count() == 1, "test requires a XPU")(test_case)
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
def require_multi_device(test_case):
|
| 379 |
+
"""
|
| 380 |
+
Decorator marking a test that requires a multi-device setup. These tests are skipped on a machine without multiple
|
| 381 |
+
devices.
|
| 382 |
+
"""
|
| 383 |
+
return unittest.skipUnless(device_count > 1, "test requires multiple hardware accelerators")(test_case)
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def require_multi_gpu(test_case):
|
| 387 |
+
"""
|
| 388 |
+
Decorator marking a test that requires a multi-GPU setup. These tests are skipped on a machine without multiple
|
| 389 |
+
GPUs.
|
| 390 |
+
"""
|
| 391 |
+
return unittest.skipUnless(torch.cuda.device_count() > 1, "test requires multiple GPUs")(test_case)
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
def require_multi_xpu(test_case):
|
| 395 |
+
"""
|
| 396 |
+
Decorator marking a test that requires a multi-XPU setup. These tests are skipped on a machine without multiple
|
| 397 |
+
XPUs.
|
| 398 |
+
"""
|
| 399 |
+
return unittest.skipUnless(torch.xpu.device_count() > 1, "test requires multiple XPUs")(test_case)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
def require_multi_gpu_or_xpu(test_case):
|
| 403 |
+
"""
|
| 404 |
+
Decorator marking a test that requires a multi-GPU setup. These tests are skipped on a machine without multiple
|
| 405 |
+
GPUs or XPUs.
|
| 406 |
+
"""
|
| 407 |
+
return unittest.skipUnless(
|
| 408 |
+
(is_cuda_available() or is_xpu_available()) and device_count > 1, "test requires multiple GPUs or XPUs"
|
| 409 |
+
)(test_case)
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
def require_deepspeed(test_case):
|
| 413 |
+
"""
|
| 414 |
+
Decorator marking a test that requires DeepSpeed installed. These tests are skipped when DeepSpeed isn't installed
|
| 415 |
+
"""
|
| 416 |
+
return unittest.skipUnless(is_deepspeed_available(), "test requires DeepSpeed")(test_case)
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def require_tp(test_case):
|
| 420 |
+
"""
|
| 421 |
+
Decorator marking a test that requires TP installed. These tests are skipped when TP isn't installed
|
| 422 |
+
"""
|
| 423 |
+
return unittest.skipUnless(is_torch_version(">=", "2.3.0"), "test requires torch version >= 2.3.0")(test_case)
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
def require_torch_min_version(test_case=None, version=None):
|
| 427 |
+
"""
|
| 428 |
+
Decorator marking that a test requires a particular torch version to be tested. These tests are skipped when an
|
| 429 |
+
installed torch version is less than the required one.
|
| 430 |
+
"""
|
| 431 |
+
if test_case is None:
|
| 432 |
+
return partial(require_torch_min_version, version=version)
|
| 433 |
+
return unittest.skipUnless(is_torch_version(">=", version), f"test requires torch version >= {version}")(test_case)
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
def require_tensorboard(test_case):
|
| 437 |
+
"""
|
| 438 |
+
Decorator marking a test that requires tensorboard installed. These tests are skipped when tensorboard isn't
|
| 439 |
+
installed
|
| 440 |
+
"""
|
| 441 |
+
return unittest.skipUnless(is_tensorboard_available(), "test requires Tensorboard")(test_case)
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
def require_wandb(test_case):
|
| 445 |
+
"""
|
| 446 |
+
Decorator marking a test that requires wandb installed. These tests are skipped when wandb isn't installed
|
| 447 |
+
"""
|
| 448 |
+
return unittest.skipUnless(is_wandb_available(), "test requires wandb")(test_case)
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
def require_comet_ml(test_case):
|
| 452 |
+
"""
|
| 453 |
+
Decorator marking a test that requires comet_ml installed. These tests are skipped when comet_ml isn't installed
|
| 454 |
+
"""
|
| 455 |
+
return unittest.skipUnless(is_comet_ml_available(), "test requires comet_ml")(test_case)
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def require_clearml(test_case):
|
| 459 |
+
"""
|
| 460 |
+
Decorator marking a test that requires clearml installed. These tests are skipped when clearml isn't installed
|
| 461 |
+
"""
|
| 462 |
+
return unittest.skipUnless(is_clearml_available(), "test requires clearml")(test_case)
|
| 463 |
+
|
| 464 |
+
|
| 465 |
+
def require_dvclive(test_case):
|
| 466 |
+
"""
|
| 467 |
+
Decorator marking a test that requires dvclive installed. These tests are skipped when dvclive isn't installed
|
| 468 |
+
"""
|
| 469 |
+
return unittest.skipUnless(is_dvclive_available(), "test requires dvclive")(test_case)
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
def require_pandas(test_case):
|
| 473 |
+
"""
|
| 474 |
+
Decorator marking a test that requires pandas installed. These tests are skipped when pandas isn't installed
|
| 475 |
+
"""
|
| 476 |
+
return unittest.skipUnless(is_pandas_available(), "test requires pandas")(test_case)
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
def require_mlflow(test_case):
|
| 480 |
+
"""
|
| 481 |
+
Decorator marking a test that requires mlflow installed. These tests are skipped when mlflow isn't installed
|
| 482 |
+
"""
|
| 483 |
+
return unittest.skipUnless(is_mlflow_available(), "test requires mlflow")(test_case)
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
def require_pippy(test_case):
|
| 487 |
+
"""
|
| 488 |
+
Decorator marking a test that requires pippy installed. These tests are skipped when pippy isn't installed It is
|
| 489 |
+
also checked if the test is running on a Gaudi1 device which doesn't support pippy.
|
| 490 |
+
"""
|
| 491 |
+
return unittest.skipUnless(is_pippy_available() and not is_habana_gaudi1(), "test requires pippy")(test_case)
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
def require_import_timer(test_case):
|
| 495 |
+
"""
|
| 496 |
+
Decorator marking a test that requires tuna interpreter installed. These tests are skipped when tuna isn't
|
| 497 |
+
installed
|
| 498 |
+
"""
|
| 499 |
+
return unittest.skipUnless(is_import_timer_available(), "test requires tuna interpreter")(test_case)
|
| 500 |
+
|
| 501 |
+
|
| 502 |
+
def require_transformer_engine(test_case):
|
| 503 |
+
"""
|
| 504 |
+
Decorator marking a test that requires transformers engine installed. These tests are skipped when transformers
|
| 505 |
+
engine isn't installed
|
| 506 |
+
"""
|
| 507 |
+
return unittest.skipUnless(is_transformer_engine_available(), "test requires transformers engine")(test_case)
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
def require_torchao(test_case):
|
| 511 |
+
"""
|
| 512 |
+
Decorator marking a test that requires torchao installed. These tests are skipped when torchao isn't installed
|
| 513 |
+
"""
|
| 514 |
+
return unittest.skipUnless(is_torchao_available(), "test requires torchao")(test_case)
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
def require_matplotlib(test_case):
|
| 518 |
+
"""
|
| 519 |
+
Decorator marking a test that requires matplotlib installed. These tests are skipped when matplotlib isn't
|
| 520 |
+
installed
|
| 521 |
+
"""
|
| 522 |
+
return unittest.skipUnless(is_matplotlib_available(), "test requires matplotlib")(test_case)
|
| 523 |
+
|
| 524 |
+
|
| 525 |
+
_atleast_one_tracker_available = (
|
| 526 |
+
any([is_wandb_available(), is_tensorboard_available()]) and not is_comet_ml_available()
|
| 527 |
+
)
|
| 528 |
+
|
| 529 |
+
|
| 530 |
+
def require_trackers(test_case):
|
| 531 |
+
"""
|
| 532 |
+
Decorator marking that a test requires at least one tracking library installed. These tests are skipped when none
|
| 533 |
+
are installed
|
| 534 |
+
"""
|
| 535 |
+
return unittest.skipUnless(
|
| 536 |
+
_atleast_one_tracker_available,
|
| 537 |
+
"test requires at least one tracker to be available and for `comet_ml` to not be installed",
|
| 538 |
+
)(test_case)
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
def require_torchdata_stateful_dataloader(test_case):
|
| 542 |
+
"""
|
| 543 |
+
Decorator marking a test that requires torchdata.stateful_dataloader.
|
| 544 |
+
|
| 545 |
+
These tests are skipped when torchdata with stateful_dataloader module isn't installed.
|
| 546 |
+
|
| 547 |
+
"""
|
| 548 |
+
return unittest.skipUnless(
|
| 549 |
+
is_torchdata_stateful_dataloader_available(), "test requires torchdata.stateful_dataloader"
|
| 550 |
+
)(test_case)
|
| 551 |
+
|
| 552 |
+
|
| 553 |
+
def run_first(test_case):
|
| 554 |
+
"""
|
| 555 |
+
Decorator marking a test with order(1). When pytest-order plugin is installed, tests marked with this decorator are
|
| 556 |
+
garanteed to run first.
|
| 557 |
+
|
| 558 |
+
This is especially useful in some test settings like on a Gaudi instance where a Gaudi device can only be used by a
|
| 559 |
+
single process at a time. So we make sure all tests that run in a subprocess are launched first, to avoid device
|
| 560 |
+
allocation conflicts.
|
| 561 |
+
|
| 562 |
+
If pytest is not installed, test will be returned as is.
|
| 563 |
+
"""
|
| 564 |
+
|
| 565 |
+
if is_pytest_available():
|
| 566 |
+
import pytest
|
| 567 |
+
|
| 568 |
+
return pytest.mark.order(1)(test_case)
|
| 569 |
+
return test_case
|
| 570 |
+
|
| 571 |
+
|
| 572 |
+
class TempDirTestCase(unittest.TestCase):
|
| 573 |
+
"""
|
| 574 |
+
A TestCase class that keeps a single `tempfile.TemporaryDirectory` open for the duration of the class, wipes its
|
| 575 |
+
data at the start of a test, and then destroyes it at the end of the TestCase.
|
| 576 |
+
|
| 577 |
+
Useful for when a class or API requires a single constant folder throughout it's use, such as Weights and Biases
|
| 578 |
+
|
| 579 |
+
The temporary directory location will be stored in `self.tmpdir`
|
| 580 |
+
"""
|
| 581 |
+
|
| 582 |
+
clear_on_setup = True
|
| 583 |
+
|
| 584 |
+
@classmethod
|
| 585 |
+
def setUpClass(cls):
|
| 586 |
+
"Creates a `tempfile.TemporaryDirectory` and stores it in `cls.tmpdir`"
|
| 587 |
+
cls.tmpdir = Path(tempfile.mkdtemp())
|
| 588 |
+
|
| 589 |
+
@classmethod
|
| 590 |
+
def tearDownClass(cls):
|
| 591 |
+
"Remove `cls.tmpdir` after test suite has finished"
|
| 592 |
+
if os.path.exists(cls.tmpdir):
|
| 593 |
+
shutil.rmtree(cls.tmpdir)
|
| 594 |
+
|
| 595 |
+
def setUp(self):
|
| 596 |
+
"Destroy all contents in `self.tmpdir`, but not `self.tmpdir`"
|
| 597 |
+
if self.clear_on_setup:
|
| 598 |
+
for path in self.tmpdir.glob("**/*"):
|
| 599 |
+
if path.is_file():
|
| 600 |
+
path.unlink()
|
| 601 |
+
elif path.is_dir():
|
| 602 |
+
shutil.rmtree(path)
|
| 603 |
+
|
| 604 |
+
|
| 605 |
+
class AccelerateTestCase(unittest.TestCase):
|
| 606 |
+
"""
|
| 607 |
+
A TestCase class that will reset the accelerator state at the end of every test. Every test that checks or utilizes
|
| 608 |
+
the `AcceleratorState` class should inherit from this to avoid silent failures due to state being shared between
|
| 609 |
+
tests.
|
| 610 |
+
"""
|
| 611 |
+
|
| 612 |
+
def tearDown(self):
|
| 613 |
+
super().tearDown()
|
| 614 |
+
# Reset the state of the AcceleratorState singleton.
|
| 615 |
+
AcceleratorState._reset_state(True)
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
class MockingTestCase(unittest.TestCase):
|
| 619 |
+
"""
|
| 620 |
+
A TestCase class designed to dynamically add various mockers that should be used in every test, mimicking the
|
| 621 |
+
behavior of a class-wide mock when defining one normally will not do.
|
| 622 |
+
|
| 623 |
+
Useful when a mock requires specific information available only initialized after `TestCase.setUpClass`, such as
|
| 624 |
+
setting an environment variable with that information.
|
| 625 |
+
|
| 626 |
+
The `add_mocks` function should be ran at the end of a `TestCase`'s `setUp` function, after a call to
|
| 627 |
+
`super().setUp()` such as:
|
| 628 |
+
```python
|
| 629 |
+
def setUp(self):
|
| 630 |
+
super().setUp()
|
| 631 |
+
mocks = mock.patch.dict(os.environ, {"SOME_ENV_VAR", "SOME_VALUE"})
|
| 632 |
+
self.add_mocks(mocks)
|
| 633 |
+
```
|
| 634 |
+
"""
|
| 635 |
+
|
| 636 |
+
def add_mocks(self, mocks: Union[mock.Mock, list[mock.Mock]]):
|
| 637 |
+
"""
|
| 638 |
+
Add custom mocks for tests that should be repeated on each test. Should be called during
|
| 639 |
+
`MockingTestCase.setUp`, after `super().setUp()`.
|
| 640 |
+
|
| 641 |
+
Args:
|
| 642 |
+
mocks (`mock.Mock` or list of `mock.Mock`):
|
| 643 |
+
Mocks that should be added to the `TestCase` after `TestCase.setUpClass` has been run
|
| 644 |
+
"""
|
| 645 |
+
self.mocks = mocks if isinstance(mocks, (tuple, list)) else [mocks]
|
| 646 |
+
for m in self.mocks:
|
| 647 |
+
m.start()
|
| 648 |
+
self.addCleanup(m.stop)
|
| 649 |
+
|
| 650 |
+
|
| 651 |
+
def are_the_same_tensors(tensor):
|
| 652 |
+
state = AcceleratorState()
|
| 653 |
+
tensor = tensor[None].clone().to(state.device)
|
| 654 |
+
tensors = gather(tensor).cpu()
|
| 655 |
+
tensor = tensor[0].cpu()
|
| 656 |
+
for i in range(tensors.shape[0]):
|
| 657 |
+
if not torch.equal(tensors[i], tensor):
|
| 658 |
+
return False
|
| 659 |
+
return True
|
| 660 |
+
|
| 661 |
+
|
| 662 |
+
class _RunOutput:
|
| 663 |
+
def __init__(self, returncode, stdout, stderr):
|
| 664 |
+
self.returncode = returncode
|
| 665 |
+
self.stdout = stdout
|
| 666 |
+
self.stderr = stderr
|
| 667 |
+
|
| 668 |
+
|
| 669 |
+
async def _read_stream(stream, callback):
|
| 670 |
+
while True:
|
| 671 |
+
line = await stream.readline()
|
| 672 |
+
if line:
|
| 673 |
+
callback(line)
|
| 674 |
+
else:
|
| 675 |
+
break
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
async def _stream_subprocess(cmd, env=None, stdin=None, timeout=None, quiet=False, echo=False) -> _RunOutput:
|
| 679 |
+
if echo:
|
| 680 |
+
print("\nRunning: ", " ".join(cmd))
|
| 681 |
+
|
| 682 |
+
p = await asyncio.create_subprocess_exec(
|
| 683 |
+
cmd[0],
|
| 684 |
+
*cmd[1:],
|
| 685 |
+
stdin=stdin,
|
| 686 |
+
stdout=asyncio.subprocess.PIPE,
|
| 687 |
+
stderr=asyncio.subprocess.PIPE,
|
| 688 |
+
env=env,
|
| 689 |
+
)
|
| 690 |
+
|
| 691 |
+
# note: there is a warning for a possible deadlock when using `wait` with huge amounts of data in the pipe
|
| 692 |
+
# https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process.wait
|
| 693 |
+
#
|
| 694 |
+
# If it starts hanging, will need to switch to the following code. The problem is that no data
|
| 695 |
+
# will be seen until it's done and if it hangs for example there will be no debug info.
|
| 696 |
+
# out, err = await p.communicate()
|
| 697 |
+
# return _RunOutput(p.returncode, out, err)
|
| 698 |
+
|
| 699 |
+
out = []
|
| 700 |
+
err = []
|
| 701 |
+
|
| 702 |
+
def tee(line, sink, pipe, label=""):
|
| 703 |
+
line = line.decode("utf-8").rstrip()
|
| 704 |
+
sink.append(line)
|
| 705 |
+
if not quiet:
|
| 706 |
+
print(label, line, file=pipe)
|
| 707 |
+
|
| 708 |
+
# XXX: the timeout doesn't seem to make any difference here
|
| 709 |
+
await asyncio.wait(
|
| 710 |
+
[
|
| 711 |
+
asyncio.create_task(_read_stream(p.stdout, lambda l: tee(l, out, sys.stdout, label="stdout:"))),
|
| 712 |
+
asyncio.create_task(_read_stream(p.stderr, lambda l: tee(l, err, sys.stderr, label="stderr:"))),
|
| 713 |
+
],
|
| 714 |
+
timeout=timeout,
|
| 715 |
+
)
|
| 716 |
+
return _RunOutput(await p.wait(), out, err)
|
| 717 |
+
|
| 718 |
+
|
| 719 |
+
def execute_subprocess_async(cmd: list, env=None, stdin=None, timeout=180, quiet=False, echo=True) -> _RunOutput:
|
| 720 |
+
# Cast every path in `cmd` to a string
|
| 721 |
+
for i, c in enumerate(cmd):
|
| 722 |
+
if isinstance(c, Path):
|
| 723 |
+
cmd[i] = str(c)
|
| 724 |
+
loop = asyncio.get_event_loop()
|
| 725 |
+
result = loop.run_until_complete(
|
| 726 |
+
_stream_subprocess(cmd, env=env, stdin=stdin, timeout=timeout, quiet=quiet, echo=echo)
|
| 727 |
+
)
|
| 728 |
+
|
| 729 |
+
cmd_str = " ".join(cmd)
|
| 730 |
+
if result.returncode > 0:
|
| 731 |
+
stderr = "\n".join(result.stderr)
|
| 732 |
+
raise RuntimeError(
|
| 733 |
+
f"'{cmd_str}' failed with returncode {result.returncode}\n\n"
|
| 734 |
+
f"The combined stderr from workers follows:\n{stderr}"
|
| 735 |
+
)
|
| 736 |
+
|
| 737 |
+
return result
|
| 738 |
+
|
| 739 |
+
|
| 740 |
+
class SubprocessCallException(Exception):
|
| 741 |
+
pass
|
| 742 |
+
|
| 743 |
+
|
| 744 |
+
def run_command(command: list[str], return_stdout=False, env=None):
|
| 745 |
+
"""
|
| 746 |
+
Runs `command` with `subprocess.check_output` and will potentially return the `stdout`. Will also properly capture
|
| 747 |
+
if an error occured while running `command`
|
| 748 |
+
"""
|
| 749 |
+
# Cast every path in `command` to a string
|
| 750 |
+
for i, c in enumerate(command):
|
| 751 |
+
if isinstance(c, Path):
|
| 752 |
+
command[i] = str(c)
|
| 753 |
+
if env is None:
|
| 754 |
+
env = os.environ.copy()
|
| 755 |
+
try:
|
| 756 |
+
output = subprocess.check_output(command, stderr=subprocess.STDOUT, env=env)
|
| 757 |
+
if return_stdout:
|
| 758 |
+
if hasattr(output, "decode"):
|
| 759 |
+
output = output.decode("utf-8")
|
| 760 |
+
return output
|
| 761 |
+
except subprocess.CalledProcessError as e:
|
| 762 |
+
raise SubprocessCallException(
|
| 763 |
+
f"Command `{' '.join(command)}` failed with the following error:\n\n{e.output.decode()}"
|
| 764 |
+
) from e
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
def path_in_accelerate_package(*components: str) -> Path:
|
| 768 |
+
"""
|
| 769 |
+
Get a path within the `accelerate` package's directory.
|
| 770 |
+
|
| 771 |
+
Args:
|
| 772 |
+
*components: Components of the path to join after the package directory.
|
| 773 |
+
|
| 774 |
+
Returns:
|
| 775 |
+
`Path`: The path to the requested file or directory.
|
| 776 |
+
"""
|
| 777 |
+
|
| 778 |
+
accelerate_package_dir = Path(inspect.getfile(accelerate)).parent
|
| 779 |
+
return accelerate_package_dir.joinpath(*components)
|
| 780 |
+
|
| 781 |
+
|
| 782 |
+
@contextmanager
|
| 783 |
+
def assert_exception(exception_class: Exception, msg: str = None) -> bool:
|
| 784 |
+
"""
|
| 785 |
+
Context manager to assert that the right `Exception` class was raised.
|
| 786 |
+
|
| 787 |
+
If `msg` is provided, will check that the message is contained in the raised exception.
|
| 788 |
+
"""
|
| 789 |
+
was_ran = False
|
| 790 |
+
try:
|
| 791 |
+
yield
|
| 792 |
+
was_ran = True
|
| 793 |
+
except Exception as e:
|
| 794 |
+
assert isinstance(e, exception_class), f"Expected exception of type {exception_class} but got {type(e)}"
|
| 795 |
+
if msg is not None:
|
| 796 |
+
assert msg in str(e), f"Expected message '{msg}' to be in exception but got '{str(e)}'"
|
| 797 |
+
if was_ran:
|
| 798 |
+
raise AssertionError(f"Expected exception of type {exception_class} but ran without issue.")
|
| 799 |
+
|
| 800 |
+
|
| 801 |
+
def capture_call_output(func, *args, **kwargs):
|
| 802 |
+
"""
|
| 803 |
+
Takes in a `func` with `args` and `kwargs` and returns the captured stdout as a string
|
| 804 |
+
"""
|
| 805 |
+
captured_output = io.StringIO()
|
| 806 |
+
original_stdout = sys.stdout
|
| 807 |
+
try:
|
| 808 |
+
sys.stdout = captured_output
|
| 809 |
+
func(*args, **kwargs)
|
| 810 |
+
except Exception as e:
|
| 811 |
+
raise e
|
| 812 |
+
finally:
|
| 813 |
+
sys.stdout = original_stdout
|
| 814 |
+
return captured_output.getvalue()
|
venv/lib/python3.11/site-packages/accelerate/test_utils/training.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import numpy as np
|
| 16 |
+
import torch
|
| 17 |
+
from torch.utils.data import DataLoader
|
| 18 |
+
|
| 19 |
+
from accelerate.utils.dataclasses import DistributedType
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class RegressionDataset:
|
| 23 |
+
def __init__(self, a=2, b=3, length=64, seed=None):
|
| 24 |
+
rng = np.random.default_rng(seed)
|
| 25 |
+
self.length = length
|
| 26 |
+
self.x = rng.normal(size=(length,)).astype(np.float32)
|
| 27 |
+
self.y = a * self.x + b + rng.normal(scale=0.1, size=(length,)).astype(np.float32)
|
| 28 |
+
|
| 29 |
+
def __len__(self):
|
| 30 |
+
return self.length
|
| 31 |
+
|
| 32 |
+
def __getitem__(self, i):
|
| 33 |
+
return {"x": self.x[i], "y": self.y[i]}
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class RegressionModel4XPU(torch.nn.Module):
|
| 37 |
+
def __init__(self, a=0, b=0, double_output=False):
|
| 38 |
+
super().__init__()
|
| 39 |
+
self.a = torch.nn.Parameter(torch.tensor([2, 3]).float())
|
| 40 |
+
self.b = torch.nn.Parameter(torch.tensor([2, 3]).float())
|
| 41 |
+
self.first_batch = True
|
| 42 |
+
|
| 43 |
+
def forward(self, x=None):
|
| 44 |
+
if self.first_batch:
|
| 45 |
+
print(f"Model dtype: {self.a.dtype}, {self.b.dtype}. Input dtype: {x.dtype}")
|
| 46 |
+
self.first_batch = False
|
| 47 |
+
return x * self.a[0] + self.b[0]
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class RegressionModel(torch.nn.Module):
|
| 51 |
+
def __init__(self, a=0, b=0, double_output=False):
|
| 52 |
+
super().__init__()
|
| 53 |
+
self.a = torch.nn.Parameter(torch.tensor(a).float())
|
| 54 |
+
self.b = torch.nn.Parameter(torch.tensor(b).float())
|
| 55 |
+
self.first_batch = True
|
| 56 |
+
|
| 57 |
+
def forward(self, x=None):
|
| 58 |
+
if self.first_batch:
|
| 59 |
+
print(f"Model dtype: {self.a.dtype}, {self.b.dtype}. Input dtype: {x.dtype}")
|
| 60 |
+
self.first_batch = False
|
| 61 |
+
return x * self.a + self.b
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def mocked_dataloaders(accelerator, batch_size: int = 16):
|
| 65 |
+
from datasets import load_dataset
|
| 66 |
+
from transformers import AutoTokenizer
|
| 67 |
+
|
| 68 |
+
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
|
| 69 |
+
data_files = {"train": "tests/test_samples/MRPC/train.csv", "validation": "tests/test_samples/MRPC/dev.csv"}
|
| 70 |
+
datasets = load_dataset("csv", data_files=data_files)
|
| 71 |
+
label_list = datasets["train"].unique("label")
|
| 72 |
+
|
| 73 |
+
label_to_id = {v: i for i, v in enumerate(label_list)}
|
| 74 |
+
|
| 75 |
+
def tokenize_function(examples):
|
| 76 |
+
# max_length=None => use the model max length (it's actually the default)
|
| 77 |
+
outputs = tokenizer(
|
| 78 |
+
examples["sentence1"], examples["sentence2"], truncation=True, max_length=None, padding="max_length"
|
| 79 |
+
)
|
| 80 |
+
if "label" in examples:
|
| 81 |
+
outputs["labels"] = [label_to_id[l] for l in examples["label"]]
|
| 82 |
+
return outputs
|
| 83 |
+
|
| 84 |
+
# Apply the method we just defined to all the examples in all the splits of the dataset
|
| 85 |
+
tokenized_datasets = datasets.map(
|
| 86 |
+
tokenize_function,
|
| 87 |
+
batched=True,
|
| 88 |
+
remove_columns=["sentence1", "sentence2", "label"],
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
def collate_fn(examples):
|
| 92 |
+
# On TPU it's best to pad everything to the same length or training will be very slow.
|
| 93 |
+
if accelerator.distributed_type == DistributedType.XLA:
|
| 94 |
+
return tokenizer.pad(examples, padding="max_length", max_length=128, return_tensors="pt")
|
| 95 |
+
return tokenizer.pad(examples, padding="longest", return_tensors="pt")
|
| 96 |
+
|
| 97 |
+
# Instantiate dataloaders.
|
| 98 |
+
train_dataloader = DataLoader(tokenized_datasets["train"], shuffle=True, collate_fn=collate_fn, batch_size=2)
|
| 99 |
+
eval_dataloader = DataLoader(tokenized_datasets["validation"], shuffle=False, collate_fn=collate_fn, batch_size=1)
|
| 100 |
+
|
| 101 |
+
return train_dataloader, eval_dataloader
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def mocked_dataloaders_for_autoregressive_models(accelerator, batch_size: int = 16):
|
| 105 |
+
from datasets import load_dataset
|
| 106 |
+
from transformers import AutoTokenizer
|
| 107 |
+
|
| 108 |
+
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM-360M")
|
| 109 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 110 |
+
|
| 111 |
+
data_files = {"train": "tests/test_samples/MRPC/train.csv", "validation": "tests/test_samples/MRPC/dev.csv"}
|
| 112 |
+
datasets = load_dataset("csv", data_files=data_files)
|
| 113 |
+
|
| 114 |
+
def tokenize_function(examples):
|
| 115 |
+
# max_length=None => use the model max length (it's actually the default)
|
| 116 |
+
outputs = tokenizer(examples["sentence1"], truncation=True, max_length=None, return_attention_mask=False)
|
| 117 |
+
return outputs
|
| 118 |
+
|
| 119 |
+
# Apply the method we just defined to all the examples in all the splits of the dataset
|
| 120 |
+
# starting with the main process first:
|
| 121 |
+
with accelerator.main_process_first():
|
| 122 |
+
tokenized_datasets = datasets.map(
|
| 123 |
+
tokenize_function,
|
| 124 |
+
batched=True,
|
| 125 |
+
remove_columns=["sentence1", "sentence2", "label"],
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
def collate_fn(examples):
|
| 129 |
+
# On TPU it's best to pad everything to the same length or training will be very slow.
|
| 130 |
+
max_length = (
|
| 131 |
+
128
|
| 132 |
+
if accelerator.distributed_type == DistributedType.XLA
|
| 133 |
+
else max([len(e["input_ids"]) for e in examples])
|
| 134 |
+
)
|
| 135 |
+
# When using mixed precision we want round multiples of 8/16
|
| 136 |
+
if accelerator.mixed_precision == "fp8":
|
| 137 |
+
pad_to_multiple_of = 16
|
| 138 |
+
elif accelerator.mixed_precision != "no":
|
| 139 |
+
pad_to_multiple_of = 8
|
| 140 |
+
else:
|
| 141 |
+
pad_to_multiple_of = None
|
| 142 |
+
|
| 143 |
+
batch = tokenizer.pad(
|
| 144 |
+
examples,
|
| 145 |
+
padding="max_length",
|
| 146 |
+
max_length=max_length + 1,
|
| 147 |
+
pad_to_multiple_of=pad_to_multiple_of,
|
| 148 |
+
return_tensors="pt",
|
| 149 |
+
)
|
| 150 |
+
|
| 151 |
+
batch["labels"] = batch["input_ids"][:, 1:]
|
| 152 |
+
batch["input_ids"] = batch["input_ids"][:, :-1]
|
| 153 |
+
|
| 154 |
+
batch["labels"] = torch.where(batch["labels"] == tokenizer.pad_token_id, -100, batch["labels"])
|
| 155 |
+
|
| 156 |
+
return batch
|
| 157 |
+
|
| 158 |
+
# Instantiate dataloaders.
|
| 159 |
+
train_dataloader = DataLoader(tokenized_datasets["train"], shuffle=False, collate_fn=collate_fn, batch_size=2)
|
| 160 |
+
eval_dataloader = DataLoader(tokenized_datasets["validation"], shuffle=False, collate_fn=collate_fn, batch_size=1)
|
| 161 |
+
|
| 162 |
+
return train_dataloader, eval_dataloader
|
venv/lib/python3.11/site-packages/accelerate/tracking.py
ADDED
|
@@ -0,0 +1,1089 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
# Expectation:
|
| 16 |
+
# Provide a project dir name, then each type of logger gets stored in project/{`logging_dir`}
|
| 17 |
+
|
| 18 |
+
import json
|
| 19 |
+
import os
|
| 20 |
+
import time
|
| 21 |
+
from functools import wraps
|
| 22 |
+
from typing import Any, Optional, Union
|
| 23 |
+
|
| 24 |
+
import yaml
|
| 25 |
+
from packaging import version
|
| 26 |
+
|
| 27 |
+
from .logging import get_logger
|
| 28 |
+
from .state import PartialState
|
| 29 |
+
from .utils import (
|
| 30 |
+
LoggerType,
|
| 31 |
+
compare_versions,
|
| 32 |
+
is_aim_available,
|
| 33 |
+
is_clearml_available,
|
| 34 |
+
is_comet_ml_available,
|
| 35 |
+
is_dvclive_available,
|
| 36 |
+
is_mlflow_available,
|
| 37 |
+
is_tensorboard_available,
|
| 38 |
+
is_wandb_available,
|
| 39 |
+
listify,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
_available_trackers = []
|
| 44 |
+
|
| 45 |
+
if is_tensorboard_available():
|
| 46 |
+
_available_trackers.append(LoggerType.TENSORBOARD)
|
| 47 |
+
|
| 48 |
+
if is_wandb_available():
|
| 49 |
+
_available_trackers.append(LoggerType.WANDB)
|
| 50 |
+
|
| 51 |
+
if is_comet_ml_available():
|
| 52 |
+
_available_trackers.append(LoggerType.COMETML)
|
| 53 |
+
|
| 54 |
+
if is_aim_available():
|
| 55 |
+
_available_trackers.append(LoggerType.AIM)
|
| 56 |
+
|
| 57 |
+
if is_mlflow_available():
|
| 58 |
+
_available_trackers.append(LoggerType.MLFLOW)
|
| 59 |
+
|
| 60 |
+
if is_clearml_available():
|
| 61 |
+
_available_trackers.append(LoggerType.CLEARML)
|
| 62 |
+
|
| 63 |
+
if is_dvclive_available():
|
| 64 |
+
_available_trackers.append(LoggerType.DVCLIVE)
|
| 65 |
+
|
| 66 |
+
logger = get_logger(__name__)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def on_main_process(function):
|
| 70 |
+
"""
|
| 71 |
+
Decorator to selectively run the decorated function on the main process only based on the `main_process_only`
|
| 72 |
+
attribute in a class.
|
| 73 |
+
|
| 74 |
+
Checks at function execution rather than initialization time, not triggering the initialization of the
|
| 75 |
+
`PartialState`.
|
| 76 |
+
"""
|
| 77 |
+
|
| 78 |
+
@wraps(function)
|
| 79 |
+
def execute_on_main_process(self, *args, **kwargs):
|
| 80 |
+
if getattr(self, "main_process_only", False):
|
| 81 |
+
return PartialState().on_main_process(function)(self, *args, **kwargs)
|
| 82 |
+
else:
|
| 83 |
+
return function(self, *args, **kwargs)
|
| 84 |
+
|
| 85 |
+
return execute_on_main_process
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def get_available_trackers():
|
| 89 |
+
"Returns a list of all supported available trackers in the system"
|
| 90 |
+
return _available_trackers
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class GeneralTracker:
|
| 94 |
+
"""
|
| 95 |
+
A base Tracker class to be used for all logging integration implementations.
|
| 96 |
+
|
| 97 |
+
Each function should take in `**kwargs` that will automatically be passed in from a base dictionary provided to
|
| 98 |
+
[`Accelerator`].
|
| 99 |
+
|
| 100 |
+
Should implement `name`, `requires_logging_directory`, and `tracker` properties such that:
|
| 101 |
+
|
| 102 |
+
`name` (`str`): String representation of the tracker class name, such as "TensorBoard" `requires_logging_directory`
|
| 103 |
+
(`bool`): Whether the logger requires a directory to store their logs. `tracker` (`object`): Should return internal
|
| 104 |
+
tracking mechanism used by a tracker class (such as the `run` for wandb)
|
| 105 |
+
|
| 106 |
+
Implementations can also include a `main_process_only` (`bool`) attribute to toggle if relevent logging, init, and
|
| 107 |
+
other functions should occur on the main process or across all processes (by default will use `True`)
|
| 108 |
+
"""
|
| 109 |
+
|
| 110 |
+
main_process_only = True
|
| 111 |
+
|
| 112 |
+
def __init__(self, _blank=False):
|
| 113 |
+
if not _blank:
|
| 114 |
+
err = ""
|
| 115 |
+
if not hasattr(self, "name"):
|
| 116 |
+
err += "`name`"
|
| 117 |
+
if not hasattr(self, "requires_logging_directory"):
|
| 118 |
+
if len(err) > 0:
|
| 119 |
+
err += ", "
|
| 120 |
+
err += "`requires_logging_directory`"
|
| 121 |
+
|
| 122 |
+
# as tracker is a @property that relies on post-init
|
| 123 |
+
if "tracker" not in dir(self):
|
| 124 |
+
if len(err) > 0:
|
| 125 |
+
err += ", "
|
| 126 |
+
err += "`tracker`"
|
| 127 |
+
if len(err) > 0:
|
| 128 |
+
raise NotImplementedError(
|
| 129 |
+
f"The implementation for this tracker class is missing the following "
|
| 130 |
+
f"required attributes. Please define them in the class definition: "
|
| 131 |
+
f"{err}"
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
def store_init_configuration(self, values: dict):
|
| 135 |
+
"""
|
| 136 |
+
Logs `values` as hyperparameters for the run. Implementations should use the experiment configuration
|
| 137 |
+
functionality of a tracking API.
|
| 138 |
+
|
| 139 |
+
Args:
|
| 140 |
+
values (Dictionary `str` to `bool`, `str`, `float` or `int`):
|
| 141 |
+
Values to be stored as initial hyperparameters as key-value pairs. The values need to have type `bool`,
|
| 142 |
+
`str`, `float`, `int`, or `None`.
|
| 143 |
+
"""
|
| 144 |
+
pass
|
| 145 |
+
|
| 146 |
+
def log(self, values: dict, step: Optional[int], **kwargs):
|
| 147 |
+
"""
|
| 148 |
+
Logs `values` to the current run. Base `log` implementations of a tracking API should go in here, along with
|
| 149 |
+
special behavior for the `step parameter.
|
| 150 |
+
|
| 151 |
+
Args:
|
| 152 |
+
values (Dictionary `str` to `str`, `float`, or `int`):
|
| 153 |
+
Values to be logged as key-value pairs. The values need to have type `str`, `float`, or `int`.
|
| 154 |
+
step (`int`, *optional*):
|
| 155 |
+
The run step. If included, the log will be affiliated with this step.
|
| 156 |
+
"""
|
| 157 |
+
pass
|
| 158 |
+
|
| 159 |
+
def finish(self):
|
| 160 |
+
"""
|
| 161 |
+
Should run any finalizing functions within the tracking API. If the API should not have one, just don't
|
| 162 |
+
overwrite that method.
|
| 163 |
+
"""
|
| 164 |
+
pass
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class TensorBoardTracker(GeneralTracker):
|
| 168 |
+
"""
|
| 169 |
+
A `Tracker` class that supports `tensorboard`. Should be initialized at the start of your script.
|
| 170 |
+
|
| 171 |
+
Args:
|
| 172 |
+
run_name (`str`):
|
| 173 |
+
The name of the experiment run
|
| 174 |
+
logging_dir (`str`, `os.PathLike`):
|
| 175 |
+
Location for TensorBoard logs to be stored.
|
| 176 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 177 |
+
Additional key word arguments passed along to the `tensorboard.SummaryWriter.__init__` method.
|
| 178 |
+
"""
|
| 179 |
+
|
| 180 |
+
name = "tensorboard"
|
| 181 |
+
requires_logging_directory = True
|
| 182 |
+
|
| 183 |
+
@on_main_process
|
| 184 |
+
def __init__(self, run_name: str, logging_dir: Union[str, os.PathLike], **kwargs):
|
| 185 |
+
try:
|
| 186 |
+
from torch.utils import tensorboard
|
| 187 |
+
except ModuleNotFoundError:
|
| 188 |
+
import tensorboardX as tensorboard
|
| 189 |
+
super().__init__()
|
| 190 |
+
self.run_name = run_name
|
| 191 |
+
self.logging_dir = os.path.join(logging_dir, run_name)
|
| 192 |
+
self.writer = tensorboard.SummaryWriter(self.logging_dir, **kwargs)
|
| 193 |
+
logger.debug(f"Initialized TensorBoard project {self.run_name} logging to {self.logging_dir}")
|
| 194 |
+
logger.debug(
|
| 195 |
+
"Make sure to log any initial configurations with `self.store_init_configuration` before training!"
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
@property
|
| 199 |
+
def tracker(self):
|
| 200 |
+
return self.writer
|
| 201 |
+
|
| 202 |
+
@on_main_process
|
| 203 |
+
def store_init_configuration(self, values: dict):
|
| 204 |
+
"""
|
| 205 |
+
Logs `values` as hyperparameters for the run. Should be run at the beginning of your experiment. Stores the
|
| 206 |
+
hyperparameters in a yaml file for future use.
|
| 207 |
+
|
| 208 |
+
Args:
|
| 209 |
+
values (Dictionary `str` to `bool`, `str`, `float` or `int`):
|
| 210 |
+
Values to be stored as initial hyperparameters as key-value pairs. The values need to have type `bool`,
|
| 211 |
+
`str`, `float`, `int`, or `None`.
|
| 212 |
+
"""
|
| 213 |
+
self.writer.add_hparams(values, metric_dict={})
|
| 214 |
+
self.writer.flush()
|
| 215 |
+
project_run_name = time.time()
|
| 216 |
+
dir_name = os.path.join(self.logging_dir, str(project_run_name))
|
| 217 |
+
os.makedirs(dir_name, exist_ok=True)
|
| 218 |
+
with open(os.path.join(dir_name, "hparams.yml"), "w") as outfile:
|
| 219 |
+
try:
|
| 220 |
+
yaml.dump(values, outfile)
|
| 221 |
+
except yaml.representer.RepresenterError:
|
| 222 |
+
logger.error("Serialization to store hyperparameters failed")
|
| 223 |
+
raise
|
| 224 |
+
logger.debug("Stored initial configuration hyperparameters to TensorBoard and hparams yaml file")
|
| 225 |
+
|
| 226 |
+
@on_main_process
|
| 227 |
+
def log(self, values: dict, step: Optional[int] = None, **kwargs):
|
| 228 |
+
"""
|
| 229 |
+
Logs `values` to the current run.
|
| 230 |
+
|
| 231 |
+
Args:
|
| 232 |
+
values (Dictionary `str` to `str`, `float`, `int` or `dict` of `str` to `float`/`int`):
|
| 233 |
+
Values to be logged as key-value pairs. The values need to have type `str`, `float`, `int` or `dict` of
|
| 234 |
+
`str` to `float`/`int`.
|
| 235 |
+
step (`int`, *optional*):
|
| 236 |
+
The run step. If included, the log will be affiliated with this step.
|
| 237 |
+
kwargs:
|
| 238 |
+
Additional key word arguments passed along to either `SummaryWriter.add_scaler`,
|
| 239 |
+
`SummaryWriter.add_text`, or `SummaryWriter.add_scalers` method based on the contents of `values`.
|
| 240 |
+
"""
|
| 241 |
+
values = listify(values)
|
| 242 |
+
for k, v in values.items():
|
| 243 |
+
if isinstance(v, (int, float)):
|
| 244 |
+
self.writer.add_scalar(k, v, global_step=step, **kwargs)
|
| 245 |
+
elif isinstance(v, str):
|
| 246 |
+
self.writer.add_text(k, v, global_step=step, **kwargs)
|
| 247 |
+
elif isinstance(v, dict):
|
| 248 |
+
self.writer.add_scalars(k, v, global_step=step, **kwargs)
|
| 249 |
+
self.writer.flush()
|
| 250 |
+
logger.debug("Successfully logged to TensorBoard")
|
| 251 |
+
|
| 252 |
+
@on_main_process
|
| 253 |
+
def log_images(self, values: dict, step: Optional[int], **kwargs):
|
| 254 |
+
"""
|
| 255 |
+
Logs `images` to the current run.
|
| 256 |
+
|
| 257 |
+
Args:
|
| 258 |
+
values (Dictionary `str` to `List` of `np.ndarray` or `PIL.Image`):
|
| 259 |
+
Values to be logged as key-value pairs. The values need to have type `List` of `np.ndarray` or
|
| 260 |
+
step (`int`, *optional*):
|
| 261 |
+
The run step. If included, the log will be affiliated with this step.
|
| 262 |
+
kwargs:
|
| 263 |
+
Additional key word arguments passed along to the `SummaryWriter.add_image` method.
|
| 264 |
+
"""
|
| 265 |
+
for k, v in values.items():
|
| 266 |
+
self.writer.add_images(k, v, global_step=step, **kwargs)
|
| 267 |
+
logger.debug("Successfully logged images to TensorBoard")
|
| 268 |
+
|
| 269 |
+
@on_main_process
|
| 270 |
+
def finish(self):
|
| 271 |
+
"""
|
| 272 |
+
Closes `TensorBoard` writer
|
| 273 |
+
"""
|
| 274 |
+
self.writer.close()
|
| 275 |
+
logger.debug("TensorBoard writer closed")
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
class WandBTracker(GeneralTracker):
|
| 279 |
+
"""
|
| 280 |
+
A `Tracker` class that supports `wandb`. Should be initialized at the start of your script.
|
| 281 |
+
|
| 282 |
+
Args:
|
| 283 |
+
run_name (`str`):
|
| 284 |
+
The name of the experiment run.
|
| 285 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 286 |
+
Additional key word arguments passed along to the `wandb.init` method.
|
| 287 |
+
"""
|
| 288 |
+
|
| 289 |
+
name = "wandb"
|
| 290 |
+
requires_logging_directory = False
|
| 291 |
+
main_process_only = False
|
| 292 |
+
|
| 293 |
+
@on_main_process
|
| 294 |
+
def __init__(self, run_name: str, **kwargs):
|
| 295 |
+
super().__init__()
|
| 296 |
+
self.run_name = run_name
|
| 297 |
+
|
| 298 |
+
import wandb
|
| 299 |
+
|
| 300 |
+
self.run = wandb.init(project=self.run_name, **kwargs)
|
| 301 |
+
logger.debug(f"Initialized WandB project {self.run_name}")
|
| 302 |
+
logger.debug(
|
| 303 |
+
"Make sure to log any initial configurations with `self.store_init_configuration` before training!"
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
@property
|
| 307 |
+
def tracker(self):
|
| 308 |
+
return self.run
|
| 309 |
+
|
| 310 |
+
@on_main_process
|
| 311 |
+
def store_init_configuration(self, values: dict):
|
| 312 |
+
"""
|
| 313 |
+
Logs `values` as hyperparameters for the run. Should be run at the beginning of your experiment.
|
| 314 |
+
|
| 315 |
+
Args:
|
| 316 |
+
values (Dictionary `str` to `bool`, `str`, `float` or `int`):
|
| 317 |
+
Values to be stored as initial hyperparameters as key-value pairs. The values need to have type `bool`,
|
| 318 |
+
`str`, `float`, `int`, or `None`.
|
| 319 |
+
"""
|
| 320 |
+
import wandb
|
| 321 |
+
|
| 322 |
+
wandb.config.update(values, allow_val_change=True)
|
| 323 |
+
logger.debug("Stored initial configuration hyperparameters to WandB")
|
| 324 |
+
|
| 325 |
+
@on_main_process
|
| 326 |
+
def log(self, values: dict, step: Optional[int] = None, **kwargs):
|
| 327 |
+
"""
|
| 328 |
+
Logs `values` to the current run.
|
| 329 |
+
|
| 330 |
+
Args:
|
| 331 |
+
values (Dictionary `str` to `str`, `float`, `int` or `dict` of `str` to `float`/`int`):
|
| 332 |
+
Values to be logged as key-value pairs. The values need to have type `str`, `float`, `int` or `dict` of
|
| 333 |
+
`str` to `float`/`int`.
|
| 334 |
+
step (`int`, *optional*):
|
| 335 |
+
The run step. If included, the log will be affiliated with this step.
|
| 336 |
+
kwargs:
|
| 337 |
+
Additional key word arguments passed along to the `wandb.log` method.
|
| 338 |
+
"""
|
| 339 |
+
self.run.log(values, step=step, **kwargs)
|
| 340 |
+
logger.debug("Successfully logged to WandB")
|
| 341 |
+
|
| 342 |
+
@on_main_process
|
| 343 |
+
def log_images(self, values: dict, step: Optional[int] = None, **kwargs):
|
| 344 |
+
"""
|
| 345 |
+
Logs `images` to the current run.
|
| 346 |
+
|
| 347 |
+
Args:
|
| 348 |
+
values (Dictionary `str` to `List` of `np.ndarray` or `PIL.Image`):
|
| 349 |
+
Values to be logged as key-value pairs. The values need to have type `List` of `np.ndarray` or
|
| 350 |
+
step (`int`, *optional*):
|
| 351 |
+
The run step. If included, the log will be affiliated with this step.
|
| 352 |
+
kwargs:
|
| 353 |
+
Additional key word arguments passed along to the `wandb.log` method.
|
| 354 |
+
"""
|
| 355 |
+
import wandb
|
| 356 |
+
|
| 357 |
+
for k, v in values.items():
|
| 358 |
+
self.log({k: [wandb.Image(image) for image in v]}, step=step, **kwargs)
|
| 359 |
+
logger.debug("Successfully logged images to WandB")
|
| 360 |
+
|
| 361 |
+
@on_main_process
|
| 362 |
+
def log_table(
|
| 363 |
+
self,
|
| 364 |
+
table_name: str,
|
| 365 |
+
columns: list[str] = None,
|
| 366 |
+
data: list[list[Any]] = None,
|
| 367 |
+
dataframe: Any = None,
|
| 368 |
+
step: Optional[int] = None,
|
| 369 |
+
**kwargs,
|
| 370 |
+
):
|
| 371 |
+
"""
|
| 372 |
+
Log a Table containing any object type (text, image, audio, video, molecule, html, etc). Can be defined either
|
| 373 |
+
with `columns` and `data` or with `dataframe`.
|
| 374 |
+
|
| 375 |
+
Args:
|
| 376 |
+
table_name (`str`):
|
| 377 |
+
The name to give to the logged table on the wandb workspace
|
| 378 |
+
columns (list of `str`, *optional*):
|
| 379 |
+
The name of the columns on the table
|
| 380 |
+
data (List of List of Any data type, *optional*):
|
| 381 |
+
The data to be logged in the table
|
| 382 |
+
dataframe (Any data type, *optional*):
|
| 383 |
+
The data to be logged in the table
|
| 384 |
+
step (`int`, *optional*):
|
| 385 |
+
The run step. If included, the log will be affiliated with this step.
|
| 386 |
+
"""
|
| 387 |
+
import wandb
|
| 388 |
+
|
| 389 |
+
values = {table_name: wandb.Table(columns=columns, data=data, dataframe=dataframe)}
|
| 390 |
+
self.log(values, step=step, **kwargs)
|
| 391 |
+
|
| 392 |
+
@on_main_process
|
| 393 |
+
def finish(self):
|
| 394 |
+
"""
|
| 395 |
+
Closes `wandb` writer
|
| 396 |
+
"""
|
| 397 |
+
self.run.finish()
|
| 398 |
+
logger.debug("WandB run closed")
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
class CometMLTracker(GeneralTracker):
|
| 402 |
+
"""
|
| 403 |
+
A `Tracker` class that supports `comet_ml`. Should be initialized at the start of your script.
|
| 404 |
+
|
| 405 |
+
API keys must be stored in a Comet config file.
|
| 406 |
+
|
| 407 |
+
Note:
|
| 408 |
+
For `comet_ml` versions < 3.41.0, additional keyword arguments are passed to `comet_ml.Experiment` instead:
|
| 409 |
+
https://www.comet.com/docs/v2/api-and-sdk/python-sdk/reference/Experiment/#comet_ml.Experiment.__init__
|
| 410 |
+
|
| 411 |
+
Args:
|
| 412 |
+
run_name (`str`):
|
| 413 |
+
The name of the experiment run.
|
| 414 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 415 |
+
Additional key word arguments passed along to the `comet_ml.start` method:
|
| 416 |
+
https://www.comet.com/docs/v2/api-and-sdk/python-sdk/reference/start/
|
| 417 |
+
"""
|
| 418 |
+
|
| 419 |
+
name = "comet_ml"
|
| 420 |
+
requires_logging_directory = False
|
| 421 |
+
|
| 422 |
+
@on_main_process
|
| 423 |
+
def __init__(self, run_name: str, **kwargs):
|
| 424 |
+
super().__init__()
|
| 425 |
+
self.run_name = run_name
|
| 426 |
+
|
| 427 |
+
import comet_ml
|
| 428 |
+
|
| 429 |
+
comet_version = version.parse(comet_ml.__version__)
|
| 430 |
+
if compare_versions(comet_version, ">=", "3.41.0"):
|
| 431 |
+
self.writer = comet_ml.start(project_name=run_name, **kwargs)
|
| 432 |
+
else:
|
| 433 |
+
logger.info("Update `comet_ml` (>=3.41.0) for experiment reuse and offline support.")
|
| 434 |
+
self.writer = comet_ml.Experiment(project_name=run_name, **kwargs)
|
| 435 |
+
|
| 436 |
+
logger.debug(f"Initialized CometML project {self.run_name}")
|
| 437 |
+
logger.debug(
|
| 438 |
+
"Make sure to log any initial configurations with `self.store_init_configuration` before training!"
|
| 439 |
+
)
|
| 440 |
+
|
| 441 |
+
@property
|
| 442 |
+
def tracker(self):
|
| 443 |
+
return self.writer
|
| 444 |
+
|
| 445 |
+
@on_main_process
|
| 446 |
+
def store_init_configuration(self, values: dict):
|
| 447 |
+
"""
|
| 448 |
+
Logs `values` as hyperparameters for the run. Should be run at the beginning of your experiment.
|
| 449 |
+
|
| 450 |
+
Args:
|
| 451 |
+
values (Dictionary `str` to `bool`, `str`, `float` or `int`):
|
| 452 |
+
Values to be stored as initial hyperparameters as key-value pairs. The values need to have type `bool`,
|
| 453 |
+
`str`, `float`, `int`, or `None`.
|
| 454 |
+
"""
|
| 455 |
+
self.writer.log_parameters(values)
|
| 456 |
+
logger.debug("Stored initial configuration hyperparameters to Comet")
|
| 457 |
+
|
| 458 |
+
@on_main_process
|
| 459 |
+
def log(self, values: dict, step: Optional[int] = None, **kwargs):
|
| 460 |
+
"""
|
| 461 |
+
Logs `values` to the current run.
|
| 462 |
+
|
| 463 |
+
Args:
|
| 464 |
+
values (Dictionary `str` to `str`, `float`, `int` or `dict` of `str` to `float`/`int`):
|
| 465 |
+
Values to be logged as key-value pairs. The values need to have type `str`, `float`, `int` or `dict` of
|
| 466 |
+
`str` to `float`/`int`.
|
| 467 |
+
step (`int`, *optional*):
|
| 468 |
+
The run step. If included, the log will be affiliated with this step.
|
| 469 |
+
kwargs:
|
| 470 |
+
Additional key word arguments passed along to either `Experiment.log_metric`, `Experiment.log_other`,
|
| 471 |
+
or `Experiment.log_metrics` method based on the contents of `values`.
|
| 472 |
+
"""
|
| 473 |
+
if step is not None:
|
| 474 |
+
self.writer.set_step(step)
|
| 475 |
+
for k, v in values.items():
|
| 476 |
+
if isinstance(v, (int, float)):
|
| 477 |
+
self.writer.log_metric(k, v, step=step, **kwargs)
|
| 478 |
+
elif isinstance(v, str):
|
| 479 |
+
self.writer.log_other(k, v, **kwargs)
|
| 480 |
+
elif isinstance(v, dict):
|
| 481 |
+
self.writer.log_metrics(v, step=step, **kwargs)
|
| 482 |
+
logger.debug("Successfully logged to Comet")
|
| 483 |
+
|
| 484 |
+
@on_main_process
|
| 485 |
+
def finish(self):
|
| 486 |
+
"""
|
| 487 |
+
Flush `comet-ml` writer
|
| 488 |
+
"""
|
| 489 |
+
self.writer.end()
|
| 490 |
+
logger.debug("Comet run flushed")
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
class AimTracker(GeneralTracker):
|
| 494 |
+
"""
|
| 495 |
+
A `Tracker` class that supports `aim`. Should be initialized at the start of your script.
|
| 496 |
+
|
| 497 |
+
Args:
|
| 498 |
+
run_name (`str`):
|
| 499 |
+
The name of the experiment run.
|
| 500 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 501 |
+
Additional key word arguments passed along to the `Run.__init__` method.
|
| 502 |
+
"""
|
| 503 |
+
|
| 504 |
+
name = "aim"
|
| 505 |
+
requires_logging_directory = True
|
| 506 |
+
|
| 507 |
+
@on_main_process
|
| 508 |
+
def __init__(self, run_name: str, logging_dir: Optional[Union[str, os.PathLike]] = ".", **kwargs):
|
| 509 |
+
self.run_name = run_name
|
| 510 |
+
|
| 511 |
+
from aim import Run
|
| 512 |
+
|
| 513 |
+
self.writer = Run(repo=logging_dir, **kwargs)
|
| 514 |
+
self.writer.name = self.run_name
|
| 515 |
+
logger.debug(f"Initialized Aim project {self.run_name}")
|
| 516 |
+
logger.debug(
|
| 517 |
+
"Make sure to log any initial configurations with `self.store_init_configuration` before training!"
|
| 518 |
+
)
|
| 519 |
+
|
| 520 |
+
@property
|
| 521 |
+
def tracker(self):
|
| 522 |
+
return self.writer
|
| 523 |
+
|
| 524 |
+
@on_main_process
|
| 525 |
+
def store_init_configuration(self, values: dict):
|
| 526 |
+
"""
|
| 527 |
+
Logs `values` as hyperparameters for the run. Should be run at the beginning of your experiment.
|
| 528 |
+
|
| 529 |
+
Args:
|
| 530 |
+
values (`dict`):
|
| 531 |
+
Values to be stored as initial hyperparameters as key-value pairs.
|
| 532 |
+
"""
|
| 533 |
+
self.writer["hparams"] = values
|
| 534 |
+
|
| 535 |
+
@on_main_process
|
| 536 |
+
def log(self, values: dict, step: Optional[int], **kwargs):
|
| 537 |
+
"""
|
| 538 |
+
Logs `values` to the current run.
|
| 539 |
+
|
| 540 |
+
Args:
|
| 541 |
+
values (`dict`):
|
| 542 |
+
Values to be logged as key-value pairs.
|
| 543 |
+
step (`int`, *optional*):
|
| 544 |
+
The run step. If included, the log will be affiliated with this step.
|
| 545 |
+
kwargs:
|
| 546 |
+
Additional key word arguments passed along to the `Run.track` method.
|
| 547 |
+
"""
|
| 548 |
+
# Note: replace this with the dictionary support when merged
|
| 549 |
+
for key, value in values.items():
|
| 550 |
+
self.writer.track(value, name=key, step=step, **kwargs)
|
| 551 |
+
|
| 552 |
+
@on_main_process
|
| 553 |
+
def log_images(self, values: dict, step: Optional[int] = None, kwargs: Optional[dict[str, dict]] = None):
|
| 554 |
+
"""
|
| 555 |
+
Logs `images` to the current run.
|
| 556 |
+
|
| 557 |
+
Args:
|
| 558 |
+
values (`Dict[str, Union[np.ndarray, PIL.Image, Tuple[np.ndarray, str], Tuple[PIL.Image, str]]]`):
|
| 559 |
+
Values to be logged as key-value pairs. The values need to have type `np.ndarray` or PIL.Image. If a
|
| 560 |
+
tuple is provided, the first element should be the image and the second element should be the caption.
|
| 561 |
+
step (`int`, *optional*):
|
| 562 |
+
The run step. If included, the log will be affiliated with this step.
|
| 563 |
+
kwargs (`Dict[str, dict]`):
|
| 564 |
+
Additional key word arguments passed along to the `Run.Image` and `Run.track` method specified by the
|
| 565 |
+
keys `aim_image` and `track`, respectively.
|
| 566 |
+
"""
|
| 567 |
+
import aim
|
| 568 |
+
|
| 569 |
+
aim_image_kw = {}
|
| 570 |
+
track_kw = {}
|
| 571 |
+
|
| 572 |
+
if kwargs is not None:
|
| 573 |
+
aim_image_kw = kwargs.get("aim_image", {})
|
| 574 |
+
track_kw = kwargs.get("track", {})
|
| 575 |
+
|
| 576 |
+
for key, value in values.items():
|
| 577 |
+
if isinstance(value, tuple):
|
| 578 |
+
img, caption = value
|
| 579 |
+
else:
|
| 580 |
+
img, caption = value, ""
|
| 581 |
+
aim_image = aim.Image(img, caption=caption, **aim_image_kw)
|
| 582 |
+
self.writer.track(aim_image, name=key, step=step, **track_kw)
|
| 583 |
+
|
| 584 |
+
@on_main_process
|
| 585 |
+
def finish(self):
|
| 586 |
+
"""
|
| 587 |
+
Closes `aim` writer
|
| 588 |
+
"""
|
| 589 |
+
self.writer.close()
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
class MLflowTracker(GeneralTracker):
|
| 593 |
+
"""
|
| 594 |
+
A `Tracker` class that supports `mlflow`. Should be initialized at the start of your script.
|
| 595 |
+
|
| 596 |
+
Args:
|
| 597 |
+
experiment_name (`str`, *optional*):
|
| 598 |
+
Name of the experiment. Environment variable MLFLOW_EXPERIMENT_NAME has priority over this argument.
|
| 599 |
+
logging_dir (`str` or `os.PathLike`, defaults to `"."`):
|
| 600 |
+
Location for mlflow logs to be stored.
|
| 601 |
+
run_id (`str`, *optional*):
|
| 602 |
+
If specified, get the run with the specified UUID and log parameters and metrics under that run. The run’s
|
| 603 |
+
end time is unset and its status is set to running, but the run’s other attributes (source_version,
|
| 604 |
+
source_type, etc.) are not changed. Environment variable MLFLOW_RUN_ID has priority over this argument.
|
| 605 |
+
tags (`Dict[str, str]`, *optional*):
|
| 606 |
+
An optional `dict` of `str` keys and values, or a `str` dump from a `dict`, to set as tags on the run. If a
|
| 607 |
+
run is being resumed, these tags are set on the resumed run. If a new run is being created, these tags are
|
| 608 |
+
set on the new run. Environment variable MLFLOW_TAGS has priority over this argument.
|
| 609 |
+
nested_run (`bool`, *optional*, defaults to `False`):
|
| 610 |
+
Controls whether run is nested in parent run. True creates a nested run. Environment variable
|
| 611 |
+
MLFLOW_NESTED_RUN has priority over this argument.
|
| 612 |
+
run_name (`str`, *optional*):
|
| 613 |
+
Name of new run (stored as a mlflow.runName tag). Used only when `run_id` is unspecified.
|
| 614 |
+
description (`str`, *optional*):
|
| 615 |
+
An optional string that populates the description box of the run. If a run is being resumed, the
|
| 616 |
+
description is set on the resumed run. If a new run is being created, the description is set on the new
|
| 617 |
+
run.
|
| 618 |
+
"""
|
| 619 |
+
|
| 620 |
+
name = "mlflow"
|
| 621 |
+
requires_logging_directory = False
|
| 622 |
+
|
| 623 |
+
@on_main_process
|
| 624 |
+
def __init__(
|
| 625 |
+
self,
|
| 626 |
+
experiment_name: str = None,
|
| 627 |
+
logging_dir: Optional[Union[str, os.PathLike]] = None,
|
| 628 |
+
run_id: Optional[str] = None,
|
| 629 |
+
tags: Optional[Union[dict[str, Any], str]] = None,
|
| 630 |
+
nested_run: Optional[bool] = False,
|
| 631 |
+
run_name: Optional[str] = None,
|
| 632 |
+
description: Optional[str] = None,
|
| 633 |
+
):
|
| 634 |
+
experiment_name = os.environ.get("MLFLOW_EXPERIMENT_NAME", experiment_name)
|
| 635 |
+
run_id = os.environ.get("MLFLOW_RUN_ID", run_id)
|
| 636 |
+
tags = os.environ.get("MLFLOW_TAGS", tags)
|
| 637 |
+
if isinstance(tags, str):
|
| 638 |
+
tags = json.loads(tags)
|
| 639 |
+
|
| 640 |
+
nested_run = os.environ.get("MLFLOW_NESTED_RUN", nested_run)
|
| 641 |
+
|
| 642 |
+
import mlflow
|
| 643 |
+
|
| 644 |
+
exps = mlflow.search_experiments(filter_string=f"name = '{experiment_name}'")
|
| 645 |
+
if len(exps) > 0:
|
| 646 |
+
if len(exps) > 1:
|
| 647 |
+
logger.warning("Multiple experiments with the same name found. Using first one.")
|
| 648 |
+
experiment_id = exps[0].experiment_id
|
| 649 |
+
else:
|
| 650 |
+
experiment_id = mlflow.create_experiment(
|
| 651 |
+
name=experiment_name,
|
| 652 |
+
artifact_location=logging_dir,
|
| 653 |
+
tags=tags,
|
| 654 |
+
)
|
| 655 |
+
|
| 656 |
+
self.active_run = mlflow.start_run(
|
| 657 |
+
run_id=run_id,
|
| 658 |
+
experiment_id=experiment_id,
|
| 659 |
+
run_name=run_name,
|
| 660 |
+
nested=nested_run,
|
| 661 |
+
tags=tags,
|
| 662 |
+
description=description,
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
logger.debug(f"Initialized mlflow experiment {experiment_name}")
|
| 666 |
+
logger.debug(
|
| 667 |
+
"Make sure to log any initial configurations with `self.store_init_configuration` before training!"
|
| 668 |
+
)
|
| 669 |
+
|
| 670 |
+
@property
|
| 671 |
+
def tracker(self):
|
| 672 |
+
return self.active_run
|
| 673 |
+
|
| 674 |
+
@on_main_process
|
| 675 |
+
def store_init_configuration(self, values: dict):
|
| 676 |
+
"""
|
| 677 |
+
Logs `values` as hyperparameters for the run. Should be run at the beginning of your experiment.
|
| 678 |
+
|
| 679 |
+
Args:
|
| 680 |
+
values (`dict`):
|
| 681 |
+
Values to be stored as initial hyperparameters as key-value pairs.
|
| 682 |
+
"""
|
| 683 |
+
import mlflow
|
| 684 |
+
|
| 685 |
+
for name, value in list(values.items()):
|
| 686 |
+
# internally, all values are converted to str in MLflow
|
| 687 |
+
if len(str(value)) > mlflow.utils.validation.MAX_PARAM_VAL_LENGTH:
|
| 688 |
+
logger.warning_once(
|
| 689 |
+
f'Accelerate is attempting to log a value of "{value}" for key "{name}" as a parameter. MLflow\'s'
|
| 690 |
+
f" log_param() only accepts values no longer than {mlflow.utils.validation.MAX_PARAM_VAL_LENGTH} characters so we dropped this attribute."
|
| 691 |
+
)
|
| 692 |
+
del values[name]
|
| 693 |
+
|
| 694 |
+
values_list = list(values.items())
|
| 695 |
+
|
| 696 |
+
# MLflow cannot log more than 100 values in one go, so we have to split it
|
| 697 |
+
for i in range(0, len(values_list), mlflow.utils.validation.MAX_PARAMS_TAGS_PER_BATCH):
|
| 698 |
+
mlflow.log_params(dict(values_list[i : i + mlflow.utils.validation.MAX_PARAMS_TAGS_PER_BATCH]))
|
| 699 |
+
|
| 700 |
+
logger.debug("Stored initial configuration hyperparameters to MLflow")
|
| 701 |
+
|
| 702 |
+
@on_main_process
|
| 703 |
+
def log(self, values: dict, step: Optional[int]):
|
| 704 |
+
"""
|
| 705 |
+
Logs `values` to the current run.
|
| 706 |
+
|
| 707 |
+
Args:
|
| 708 |
+
values (`dict`):
|
| 709 |
+
Values to be logged as key-value pairs.
|
| 710 |
+
step (`int`, *optional*):
|
| 711 |
+
The run step. If included, the log will be affiliated with this step.
|
| 712 |
+
"""
|
| 713 |
+
metrics = {}
|
| 714 |
+
for k, v in values.items():
|
| 715 |
+
if isinstance(v, (int, float)):
|
| 716 |
+
metrics[k] = v
|
| 717 |
+
else:
|
| 718 |
+
logger.warning_once(
|
| 719 |
+
f'MLflowTracker is attempting to log a value of "{v}" of type {type(v)} for key "{k}" as a metric. '
|
| 720 |
+
"MLflow's log_metric() only accepts float and int types so we dropped this attribute."
|
| 721 |
+
)
|
| 722 |
+
import mlflow
|
| 723 |
+
|
| 724 |
+
mlflow.log_metrics(metrics, step=step)
|
| 725 |
+
logger.debug("Successfully logged to mlflow")
|
| 726 |
+
|
| 727 |
+
@on_main_process
|
| 728 |
+
def log_figure(self, figure: Any, artifact_file: str, **save_kwargs):
|
| 729 |
+
"""
|
| 730 |
+
Logs an figure to the current run.
|
| 731 |
+
|
| 732 |
+
Args:
|
| 733 |
+
figure (Any):
|
| 734 |
+
The figure to be logged.
|
| 735 |
+
artifact_file (`str`, *optional*):
|
| 736 |
+
The run-relative artifact file path in posixpath format to which the image is saved.
|
| 737 |
+
If not provided, the image is saved to a default location.
|
| 738 |
+
**kwargs:
|
| 739 |
+
Additional keyword arguments passed to the underlying mlflow.log_image function.
|
| 740 |
+
"""
|
| 741 |
+
import mlflow
|
| 742 |
+
|
| 743 |
+
mlflow.log_figure(figure=figure, artifact_file=artifact_file, **save_kwargs)
|
| 744 |
+
logger.debug("Successfully logged image to mlflow")
|
| 745 |
+
|
| 746 |
+
@on_main_process
|
| 747 |
+
def log_artifacts(self, local_dir: str, artifact_path: Optional[str] = None):
|
| 748 |
+
"""
|
| 749 |
+
Logs an artifacts (all content of a dir) to the current run.
|
| 750 |
+
|
| 751 |
+
local_dir (`str`):
|
| 752 |
+
Path to the directory to be logged as an artifact.
|
| 753 |
+
artifact_path (`str`, *optional*):
|
| 754 |
+
Directory within the run's artifact directory where the artifact will be logged. If omitted, the
|
| 755 |
+
artifact will be logged to the root of the run's artifact directory. The run step. If included, the
|
| 756 |
+
artifact will be affiliated with this step.
|
| 757 |
+
"""
|
| 758 |
+
import mlflow
|
| 759 |
+
|
| 760 |
+
mlflow.log_artifacts(local_dir=local_dir, artifact_path=artifact_path)
|
| 761 |
+
logger.debug("Successfully logged artofact to mlflow")
|
| 762 |
+
|
| 763 |
+
@on_main_process
|
| 764 |
+
def log_artifact(self, local_path: str, artifact_path: Optional[str] = None):
|
| 765 |
+
"""
|
| 766 |
+
Logs an artifact (file) to the current run.
|
| 767 |
+
|
| 768 |
+
local_path (`str`):
|
| 769 |
+
Path to the file to be logged as an artifact.
|
| 770 |
+
artifact_path (`str`, *optional*):
|
| 771 |
+
Directory within the run's artifact directory where the artifact will be logged. If omitted, the
|
| 772 |
+
artifact will be logged to the root of the run's artifact directory. The run step. If included, the
|
| 773 |
+
artifact will be affiliated with this step.
|
| 774 |
+
"""
|
| 775 |
+
import mlflow
|
| 776 |
+
|
| 777 |
+
mlflow.log_artifact(local_path=local_path, artifact_path=artifact_path)
|
| 778 |
+
logger.debug("Successfully logged artofact to mlflow")
|
| 779 |
+
|
| 780 |
+
@on_main_process
|
| 781 |
+
def finish(self):
|
| 782 |
+
"""
|
| 783 |
+
End the active MLflow run.
|
| 784 |
+
"""
|
| 785 |
+
import mlflow
|
| 786 |
+
|
| 787 |
+
mlflow.end_run()
|
| 788 |
+
|
| 789 |
+
|
| 790 |
+
class ClearMLTracker(GeneralTracker):
|
| 791 |
+
"""
|
| 792 |
+
A `Tracker` class that supports `clearml`. Should be initialized at the start of your script.
|
| 793 |
+
|
| 794 |
+
Args:
|
| 795 |
+
run_name (`str`, *optional*):
|
| 796 |
+
Name of the experiment. Environment variables `CLEARML_PROJECT` and `CLEARML_TASK` have priority over this
|
| 797 |
+
argument.
|
| 798 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 799 |
+
Kwargs passed along to the `Task.__init__` method.
|
| 800 |
+
"""
|
| 801 |
+
|
| 802 |
+
name = "clearml"
|
| 803 |
+
requires_logging_directory = False
|
| 804 |
+
|
| 805 |
+
@on_main_process
|
| 806 |
+
def __init__(self, run_name: str = None, **kwargs):
|
| 807 |
+
from clearml import Task
|
| 808 |
+
|
| 809 |
+
current_task = Task.current_task()
|
| 810 |
+
self._initialized_externally = False
|
| 811 |
+
if current_task:
|
| 812 |
+
self._initialized_externally = True
|
| 813 |
+
self.task = current_task
|
| 814 |
+
return
|
| 815 |
+
|
| 816 |
+
kwargs.setdefault("project_name", os.environ.get("CLEARML_PROJECT", run_name))
|
| 817 |
+
kwargs.setdefault("task_name", os.environ.get("CLEARML_TASK", run_name))
|
| 818 |
+
self.task = Task.init(**kwargs)
|
| 819 |
+
|
| 820 |
+
@property
|
| 821 |
+
def tracker(self):
|
| 822 |
+
return self.task
|
| 823 |
+
|
| 824 |
+
@on_main_process
|
| 825 |
+
def store_init_configuration(self, values: dict):
|
| 826 |
+
"""
|
| 827 |
+
Connect configuration dictionary to the Task object. Should be run at the beginning of your experiment.
|
| 828 |
+
|
| 829 |
+
Args:
|
| 830 |
+
values (`dict`):
|
| 831 |
+
Values to be stored as initial hyperparameters as key-value pairs.
|
| 832 |
+
"""
|
| 833 |
+
return self.task.connect_configuration(values)
|
| 834 |
+
|
| 835 |
+
@on_main_process
|
| 836 |
+
def log(self, values: dict[str, Union[int, float]], step: Optional[int] = None, **kwargs):
|
| 837 |
+
"""
|
| 838 |
+
Logs `values` dictionary to the current run. The dictionary keys must be strings. The dictionary values must be
|
| 839 |
+
ints or floats
|
| 840 |
+
|
| 841 |
+
Args:
|
| 842 |
+
values (`Dict[str, Union[int, float]]`):
|
| 843 |
+
Values to be logged as key-value pairs. If the key starts with 'eval_'/'test_'/'train_', the value will
|
| 844 |
+
be reported under the 'eval'/'test'/'train' series and the respective prefix will be removed.
|
| 845 |
+
Otherwise, the value will be reported under the 'train' series, and no prefix will be removed.
|
| 846 |
+
step (`int`, *optional*):
|
| 847 |
+
If specified, the values will be reported as scalars, with the iteration number equal to `step`.
|
| 848 |
+
Otherwise they will be reported as single values.
|
| 849 |
+
kwargs:
|
| 850 |
+
Additional key word arguments passed along to the `clearml.Logger.report_single_value` or
|
| 851 |
+
`clearml.Logger.report_scalar` methods.
|
| 852 |
+
"""
|
| 853 |
+
clearml_logger = self.task.get_logger()
|
| 854 |
+
for k, v in values.items():
|
| 855 |
+
if not isinstance(v, (int, float)):
|
| 856 |
+
logger.warning_once(
|
| 857 |
+
"Accelerator is attempting to log a value of "
|
| 858 |
+
f'"{v}" of type {type(v)} for key "{k}" as a scalar. '
|
| 859 |
+
"This invocation of ClearML logger's report_scalar() "
|
| 860 |
+
"is incorrect so we dropped this attribute."
|
| 861 |
+
)
|
| 862 |
+
continue
|
| 863 |
+
if step is None:
|
| 864 |
+
clearml_logger.report_single_value(name=k, value=v, **kwargs)
|
| 865 |
+
continue
|
| 866 |
+
title, series = ClearMLTracker._get_title_series(k)
|
| 867 |
+
clearml_logger.report_scalar(title=title, series=series, value=v, iteration=step, **kwargs)
|
| 868 |
+
|
| 869 |
+
@on_main_process
|
| 870 |
+
def log_images(self, values: dict, step: Optional[int] = None, **kwargs):
|
| 871 |
+
"""
|
| 872 |
+
Logs `images` to the current run.
|
| 873 |
+
|
| 874 |
+
Args:
|
| 875 |
+
values (`Dict[str, List[Union[np.ndarray, PIL.Image]]`):
|
| 876 |
+
Values to be logged as key-value pairs. The values need to have type `List` of `np.ndarray` or
|
| 877 |
+
step (`int`, *optional*):
|
| 878 |
+
The run step. If included, the log will be affiliated with this step.
|
| 879 |
+
kwargs:
|
| 880 |
+
Additional key word arguments passed along to the `clearml.Logger.report_image` method.
|
| 881 |
+
"""
|
| 882 |
+
clearml_logger = self.task.get_logger()
|
| 883 |
+
for k, v in values.items():
|
| 884 |
+
title, series = ClearMLTracker._get_title_series(k)
|
| 885 |
+
clearml_logger.report_image(title=title, series=series, iteration=step, image=v, **kwargs)
|
| 886 |
+
|
| 887 |
+
@on_main_process
|
| 888 |
+
def log_table(
|
| 889 |
+
self,
|
| 890 |
+
table_name: str,
|
| 891 |
+
columns: list[str] = None,
|
| 892 |
+
data: list[list[Any]] = None,
|
| 893 |
+
dataframe: Any = None,
|
| 894 |
+
step: Optional[int] = None,
|
| 895 |
+
**kwargs,
|
| 896 |
+
):
|
| 897 |
+
"""
|
| 898 |
+
Log a Table to the task. Can be defined eitherwith `columns` and `data` or with `dataframe`.
|
| 899 |
+
|
| 900 |
+
Args:
|
| 901 |
+
table_name (`str`):
|
| 902 |
+
The name of the table
|
| 903 |
+
columns (list of `str`, *optional*):
|
| 904 |
+
The name of the columns on the table
|
| 905 |
+
data (List of List of Any data type, *optional*):
|
| 906 |
+
The data to be logged in the table. If `columns` is not specified, then the first entry in data will be
|
| 907 |
+
the name of the columns of the table
|
| 908 |
+
dataframe (Any data type, *optional*):
|
| 909 |
+
The data to be logged in the table
|
| 910 |
+
step (`int`, *optional*):
|
| 911 |
+
The run step. If included, the log will be affiliated with this step.
|
| 912 |
+
kwargs:
|
| 913 |
+
Additional key word arguments passed along to the `clearml.Logger.report_table` method.
|
| 914 |
+
"""
|
| 915 |
+
to_report = dataframe
|
| 916 |
+
if dataframe is None:
|
| 917 |
+
if data is None:
|
| 918 |
+
raise ValueError(
|
| 919 |
+
"`ClearMLTracker.log_table` requires that `data` to be supplied if `dataframe` is `None`"
|
| 920 |
+
)
|
| 921 |
+
to_report = [columns] + data if columns else data
|
| 922 |
+
title, series = ClearMLTracker._get_title_series(table_name)
|
| 923 |
+
self.task.get_logger().report_table(title=title, series=series, table_plot=to_report, iteration=step, **kwargs)
|
| 924 |
+
|
| 925 |
+
@on_main_process
|
| 926 |
+
def finish(self):
|
| 927 |
+
"""
|
| 928 |
+
Close the ClearML task. If the task was initialized externally (e.g. by manually calling `Task.init`), this
|
| 929 |
+
function is a noop
|
| 930 |
+
"""
|
| 931 |
+
if self.task and not self._initialized_externally:
|
| 932 |
+
self.task.close()
|
| 933 |
+
|
| 934 |
+
@staticmethod
|
| 935 |
+
def _get_title_series(name):
|
| 936 |
+
for prefix in ["eval", "test", "train"]:
|
| 937 |
+
if name.startswith(prefix + "_"):
|
| 938 |
+
return name[len(prefix) + 1 :], prefix
|
| 939 |
+
return name, "train"
|
| 940 |
+
|
| 941 |
+
|
| 942 |
+
class DVCLiveTracker(GeneralTracker):
|
| 943 |
+
"""
|
| 944 |
+
A `Tracker` class that supports `dvclive`. Should be initialized at the start of your script.
|
| 945 |
+
|
| 946 |
+
Args:
|
| 947 |
+
run_name (`str`, *optional*):
|
| 948 |
+
Ignored for dvclive. See `kwargs` instead.
|
| 949 |
+
kwargs:
|
| 950 |
+
Additional key word arguments passed along to [`dvclive.Live()`](https://dvc.org/doc/dvclive/live).
|
| 951 |
+
|
| 952 |
+
Example:
|
| 953 |
+
|
| 954 |
+
```py
|
| 955 |
+
from accelerate import Accelerator
|
| 956 |
+
|
| 957 |
+
accelerator = Accelerator(log_with="dvclive")
|
| 958 |
+
accelerator.init_trackers(project_name="my_project", init_kwargs={"dvclive": {"dir": "my_directory"}})
|
| 959 |
+
```
|
| 960 |
+
"""
|
| 961 |
+
|
| 962 |
+
name = "dvclive"
|
| 963 |
+
requires_logging_directory = False
|
| 964 |
+
|
| 965 |
+
@on_main_process
|
| 966 |
+
def __init__(self, run_name: Optional[str] = None, live: Optional[Any] = None, **kwargs):
|
| 967 |
+
from dvclive import Live
|
| 968 |
+
|
| 969 |
+
super().__init__()
|
| 970 |
+
self.live = live if live is not None else Live(**kwargs)
|
| 971 |
+
|
| 972 |
+
@property
|
| 973 |
+
def tracker(self):
|
| 974 |
+
return self.live
|
| 975 |
+
|
| 976 |
+
@on_main_process
|
| 977 |
+
def store_init_configuration(self, values: dict):
|
| 978 |
+
"""
|
| 979 |
+
Logs `values` as hyperparameters for the run. Should be run at the beginning of your experiment. Stores the
|
| 980 |
+
hyperparameters in a yaml file for future use.
|
| 981 |
+
|
| 982 |
+
Args:
|
| 983 |
+
values (Dictionary `str` to `bool`, `str`, `float`, `int`, or a List or Dict of those types):
|
| 984 |
+
Values to be stored as initial hyperparameters as key-value pairs. The values need to have type `bool`,
|
| 985 |
+
`str`, `float`, or `int`.
|
| 986 |
+
"""
|
| 987 |
+
self.live.log_params(values)
|
| 988 |
+
|
| 989 |
+
@on_main_process
|
| 990 |
+
def log(self, values: dict, step: Optional[int] = None, **kwargs):
|
| 991 |
+
"""
|
| 992 |
+
Logs `values` to the current run.
|
| 993 |
+
|
| 994 |
+
Args:
|
| 995 |
+
values (Dictionary `str` to `str`, `float`, or `int`):
|
| 996 |
+
Values to be logged as key-value pairs. The values need to have type `str`, `float`, or `int`.
|
| 997 |
+
step (`int`, *optional*):
|
| 998 |
+
The run step. If included, the log will be affiliated with this step.
|
| 999 |
+
kwargs:
|
| 1000 |
+
Additional key word arguments passed along to `dvclive.Live.log_metric()`.
|
| 1001 |
+
"""
|
| 1002 |
+
from dvclive.plots import Metric
|
| 1003 |
+
|
| 1004 |
+
if step is not None:
|
| 1005 |
+
self.live.step = step
|
| 1006 |
+
for k, v in values.items():
|
| 1007 |
+
if Metric.could_log(v):
|
| 1008 |
+
self.live.log_metric(k, v, **kwargs)
|
| 1009 |
+
else:
|
| 1010 |
+
logger.warning_once(
|
| 1011 |
+
"Accelerator attempted to log a value of "
|
| 1012 |
+
f'"{v}" of type {type(v)} for key "{k}" as a scalar. '
|
| 1013 |
+
"This invocation of DVCLive's Live.log_metric() "
|
| 1014 |
+
"is incorrect so we dropped this attribute."
|
| 1015 |
+
)
|
| 1016 |
+
self.live.next_step()
|
| 1017 |
+
|
| 1018 |
+
@on_main_process
|
| 1019 |
+
def finish(self):
|
| 1020 |
+
"""
|
| 1021 |
+
Closes `dvclive.Live()`.
|
| 1022 |
+
"""
|
| 1023 |
+
self.live.end()
|
| 1024 |
+
|
| 1025 |
+
|
| 1026 |
+
LOGGER_TYPE_TO_CLASS = {
|
| 1027 |
+
"aim": AimTracker,
|
| 1028 |
+
"comet_ml": CometMLTracker,
|
| 1029 |
+
"mlflow": MLflowTracker,
|
| 1030 |
+
"tensorboard": TensorBoardTracker,
|
| 1031 |
+
"wandb": WandBTracker,
|
| 1032 |
+
"clearml": ClearMLTracker,
|
| 1033 |
+
"dvclive": DVCLiveTracker,
|
| 1034 |
+
}
|
| 1035 |
+
|
| 1036 |
+
|
| 1037 |
+
def filter_trackers(
|
| 1038 |
+
log_with: list[Union[str, LoggerType, GeneralTracker]],
|
| 1039 |
+
logging_dir: Union[str, os.PathLike] = None,
|
| 1040 |
+
):
|
| 1041 |
+
"""
|
| 1042 |
+
Takes in a list of potential tracker types and checks that:
|
| 1043 |
+
- The tracker wanted is available in that environment
|
| 1044 |
+
- Filters out repeats of tracker types
|
| 1045 |
+
- If `all` is in `log_with`, will return all trackers in the environment
|
| 1046 |
+
- If a tracker requires a `logging_dir`, ensures that `logging_dir` is not `None`
|
| 1047 |
+
|
| 1048 |
+
Args:
|
| 1049 |
+
log_with (list of `str`, [`~utils.LoggerType`] or [`~tracking.GeneralTracker`], *optional*):
|
| 1050 |
+
A list of loggers to be setup for experiment tracking. Should be one or several of:
|
| 1051 |
+
|
| 1052 |
+
- `"all"`
|
| 1053 |
+
- `"tensorboard"`
|
| 1054 |
+
- `"wandb"`
|
| 1055 |
+
- `"comet_ml"`
|
| 1056 |
+
- `"mlflow"`
|
| 1057 |
+
- `"dvclive"`
|
| 1058 |
+
If `"all"` is selected, will pick up all available trackers in the environment and initialize them. Can
|
| 1059 |
+
also accept implementations of `GeneralTracker` for custom trackers, and can be combined with `"all"`.
|
| 1060 |
+
logging_dir (`str`, `os.PathLike`, *optional*):
|
| 1061 |
+
A path to a directory for storing logs of locally-compatible loggers.
|
| 1062 |
+
"""
|
| 1063 |
+
loggers = []
|
| 1064 |
+
if log_with is not None:
|
| 1065 |
+
if not isinstance(log_with, (list, tuple)):
|
| 1066 |
+
log_with = [log_with]
|
| 1067 |
+
if "all" in log_with or LoggerType.ALL in log_with:
|
| 1068 |
+
loggers = [o for o in log_with if issubclass(type(o), GeneralTracker)] + get_available_trackers()
|
| 1069 |
+
else:
|
| 1070 |
+
for log_type in log_with:
|
| 1071 |
+
if log_type not in LoggerType and not issubclass(type(log_type), GeneralTracker):
|
| 1072 |
+
raise ValueError(f"Unsupported logging capability: {log_type}. Choose between {LoggerType.list()}")
|
| 1073 |
+
if issubclass(type(log_type), GeneralTracker):
|
| 1074 |
+
loggers.append(log_type)
|
| 1075 |
+
else:
|
| 1076 |
+
log_type = LoggerType(log_type)
|
| 1077 |
+
if log_type not in loggers:
|
| 1078 |
+
if log_type in get_available_trackers():
|
| 1079 |
+
tracker_init = LOGGER_TYPE_TO_CLASS[str(log_type)]
|
| 1080 |
+
if tracker_init.requires_logging_directory:
|
| 1081 |
+
if logging_dir is None:
|
| 1082 |
+
raise ValueError(
|
| 1083 |
+
f"Logging with `{log_type}` requires a `logging_dir` to be passed in."
|
| 1084 |
+
)
|
| 1085 |
+
loggers.append(log_type)
|
| 1086 |
+
else:
|
| 1087 |
+
logger.debug(f"Tried adding logger {log_type}, but package is unavailable in the system.")
|
| 1088 |
+
|
| 1089 |
+
return loggers
|
venv/lib/python3.11/site-packages/accelerate/utils/__init__.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
from .ao import convert_model_to_fp8_ao, filter_first_and_last_linear_layers, has_ao_layers
|
| 15 |
+
from .constants import (
|
| 16 |
+
MITA_PROFILING_AVAILABLE_PYTORCH_VERSION,
|
| 17 |
+
MODEL_NAME,
|
| 18 |
+
OPTIMIZER_NAME,
|
| 19 |
+
PROFILE_PATTERN_NAME,
|
| 20 |
+
RNG_STATE_NAME,
|
| 21 |
+
SAFE_MODEL_NAME,
|
| 22 |
+
SAFE_WEIGHTS_INDEX_NAME,
|
| 23 |
+
SAFE_WEIGHTS_NAME,
|
| 24 |
+
SAFE_WEIGHTS_PATTERN_NAME,
|
| 25 |
+
SAMPLER_NAME,
|
| 26 |
+
SCALER_NAME,
|
| 27 |
+
SCHEDULER_NAME,
|
| 28 |
+
TORCH_DISTRIBUTED_OPERATION_TYPES,
|
| 29 |
+
TORCH_LAUNCH_PARAMS,
|
| 30 |
+
WEIGHTS_INDEX_NAME,
|
| 31 |
+
WEIGHTS_NAME,
|
| 32 |
+
WEIGHTS_PATTERN_NAME,
|
| 33 |
+
XPU_PROFILING_AVAILABLE_PYTORCH_VERSION,
|
| 34 |
+
)
|
| 35 |
+
from .dataclasses import (
|
| 36 |
+
AORecipeKwargs,
|
| 37 |
+
AutocastKwargs,
|
| 38 |
+
BnbQuantizationConfig,
|
| 39 |
+
ComputeEnvironment,
|
| 40 |
+
CustomDtype,
|
| 41 |
+
DataLoaderConfiguration,
|
| 42 |
+
DDPCommunicationHookType,
|
| 43 |
+
DeepSpeedPlugin,
|
| 44 |
+
DistributedDataParallelKwargs,
|
| 45 |
+
DistributedType,
|
| 46 |
+
DynamoBackend,
|
| 47 |
+
FP8RecipeKwargs,
|
| 48 |
+
FullyShardedDataParallelPlugin,
|
| 49 |
+
GradientAccumulationPlugin,
|
| 50 |
+
GradScalerKwargs,
|
| 51 |
+
InitProcessGroupKwargs,
|
| 52 |
+
KwargsHandler,
|
| 53 |
+
LoggerType,
|
| 54 |
+
MegatronLMPlugin,
|
| 55 |
+
MSAMPRecipeKwargs,
|
| 56 |
+
PrecisionType,
|
| 57 |
+
ProfileKwargs,
|
| 58 |
+
ProjectConfiguration,
|
| 59 |
+
RNGType,
|
| 60 |
+
SageMakerDistributedType,
|
| 61 |
+
TensorInformation,
|
| 62 |
+
TERecipeKwargs,
|
| 63 |
+
TorchDynamoPlugin,
|
| 64 |
+
TorchTensorParallelPlugin,
|
| 65 |
+
add_model_config_to_megatron_parser,
|
| 66 |
+
)
|
| 67 |
+
from .environment import (
|
| 68 |
+
are_libraries_initialized,
|
| 69 |
+
check_cuda_fp8_capability,
|
| 70 |
+
check_cuda_p2p_ib_support,
|
| 71 |
+
clear_environment,
|
| 72 |
+
convert_dict_to_env_variables,
|
| 73 |
+
get_cpu_distributed_information,
|
| 74 |
+
get_gpu_info,
|
| 75 |
+
get_int_from_env,
|
| 76 |
+
parse_choice_from_env,
|
| 77 |
+
parse_flag_from_env,
|
| 78 |
+
patch_environment,
|
| 79 |
+
purge_accelerate_environment,
|
| 80 |
+
set_numa_affinity,
|
| 81 |
+
str_to_bool,
|
| 82 |
+
)
|
| 83 |
+
from .imports import (
|
| 84 |
+
deepspeed_required,
|
| 85 |
+
get_ccl_version,
|
| 86 |
+
is_4bit_bnb_available,
|
| 87 |
+
is_8bit_bnb_available,
|
| 88 |
+
is_aim_available,
|
| 89 |
+
is_bf16_available,
|
| 90 |
+
is_bitsandbytes_multi_backend_available,
|
| 91 |
+
is_bnb_available,
|
| 92 |
+
is_boto3_available,
|
| 93 |
+
is_ccl_available,
|
| 94 |
+
is_clearml_available,
|
| 95 |
+
is_comet_ml_available,
|
| 96 |
+
is_cuda_available,
|
| 97 |
+
is_datasets_available,
|
| 98 |
+
is_deepspeed_available,
|
| 99 |
+
is_dvclive_available,
|
| 100 |
+
is_fp8_available,
|
| 101 |
+
is_fp16_available,
|
| 102 |
+
is_habana_gaudi1,
|
| 103 |
+
is_hpu_available,
|
| 104 |
+
is_import_timer_available,
|
| 105 |
+
is_ipex_available,
|
| 106 |
+
is_lomo_available,
|
| 107 |
+
is_matplotlib_available,
|
| 108 |
+
is_megatron_lm_available,
|
| 109 |
+
is_mlflow_available,
|
| 110 |
+
is_mlu_available,
|
| 111 |
+
is_mps_available,
|
| 112 |
+
is_msamp_available,
|
| 113 |
+
is_musa_available,
|
| 114 |
+
is_npu_available,
|
| 115 |
+
is_pandas_available,
|
| 116 |
+
is_peft_available,
|
| 117 |
+
is_pippy_available,
|
| 118 |
+
is_pynvml_available,
|
| 119 |
+
is_pytest_available,
|
| 120 |
+
is_rich_available,
|
| 121 |
+
is_sagemaker_available,
|
| 122 |
+
is_schedulefree_available,
|
| 123 |
+
is_sdaa_available,
|
| 124 |
+
is_tensorboard_available,
|
| 125 |
+
is_timm_available,
|
| 126 |
+
is_torch_xla_available,
|
| 127 |
+
is_torchao_available,
|
| 128 |
+
is_torchdata_available,
|
| 129 |
+
is_torchdata_stateful_dataloader_available,
|
| 130 |
+
is_torchvision_available,
|
| 131 |
+
is_transformer_engine_available,
|
| 132 |
+
is_transformers_available,
|
| 133 |
+
is_triton_available,
|
| 134 |
+
is_wandb_available,
|
| 135 |
+
is_weights_only_available,
|
| 136 |
+
is_xccl_available,
|
| 137 |
+
is_xpu_available,
|
| 138 |
+
torchao_required,
|
| 139 |
+
)
|
| 140 |
+
from .modeling import (
|
| 141 |
+
align_module_device,
|
| 142 |
+
calculate_maximum_sizes,
|
| 143 |
+
check_device_map,
|
| 144 |
+
check_tied_parameters_in_config,
|
| 145 |
+
check_tied_parameters_on_same_device,
|
| 146 |
+
compute_module_sizes,
|
| 147 |
+
convert_file_size_to_int,
|
| 148 |
+
dtype_byte_size,
|
| 149 |
+
find_tied_parameters,
|
| 150 |
+
get_balanced_memory,
|
| 151 |
+
get_grad_scaler,
|
| 152 |
+
get_max_layer_size,
|
| 153 |
+
get_max_memory,
|
| 154 |
+
get_mixed_precision_context_manager,
|
| 155 |
+
has_offloaded_params,
|
| 156 |
+
id_tensor_storage,
|
| 157 |
+
infer_auto_device_map,
|
| 158 |
+
is_peft_model,
|
| 159 |
+
load_checkpoint_in_model,
|
| 160 |
+
load_offloaded_weights,
|
| 161 |
+
load_state_dict,
|
| 162 |
+
named_module_tensors,
|
| 163 |
+
retie_parameters,
|
| 164 |
+
set_module_tensor_to_device,
|
| 165 |
+
)
|
| 166 |
+
from .offload import (
|
| 167 |
+
OffloadedWeightsLoader,
|
| 168 |
+
PrefixedDataset,
|
| 169 |
+
extract_submodules_state_dict,
|
| 170 |
+
load_offloaded_weight,
|
| 171 |
+
offload_state_dict,
|
| 172 |
+
offload_weight,
|
| 173 |
+
save_offload_index,
|
| 174 |
+
)
|
| 175 |
+
from .operations import (
|
| 176 |
+
CannotPadNestedTensorWarning,
|
| 177 |
+
GatheredParameters,
|
| 178 |
+
broadcast,
|
| 179 |
+
broadcast_object_list,
|
| 180 |
+
concatenate,
|
| 181 |
+
convert_outputs_to_fp32,
|
| 182 |
+
convert_to_fp32,
|
| 183 |
+
copy_tensor_to_devices,
|
| 184 |
+
find_batch_size,
|
| 185 |
+
find_device,
|
| 186 |
+
gather,
|
| 187 |
+
gather_object,
|
| 188 |
+
get_data_structure,
|
| 189 |
+
honor_type,
|
| 190 |
+
ignorant_find_batch_size,
|
| 191 |
+
initialize_tensors,
|
| 192 |
+
is_namedtuple,
|
| 193 |
+
is_tensor_information,
|
| 194 |
+
is_torch_tensor,
|
| 195 |
+
listify,
|
| 196 |
+
pad_across_processes,
|
| 197 |
+
pad_input_tensors,
|
| 198 |
+
recursively_apply,
|
| 199 |
+
reduce,
|
| 200 |
+
send_to_device,
|
| 201 |
+
slice_tensors,
|
| 202 |
+
)
|
| 203 |
+
from .versions import compare_versions, is_torch_version
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
if is_deepspeed_available():
|
| 207 |
+
from .deepspeed import (
|
| 208 |
+
DeepSpeedEngineWrapper,
|
| 209 |
+
DeepSpeedOptimizerWrapper,
|
| 210 |
+
DeepSpeedSchedulerWrapper,
|
| 211 |
+
DummyOptim,
|
| 212 |
+
DummyScheduler,
|
| 213 |
+
HfDeepSpeedConfig,
|
| 214 |
+
get_active_deepspeed_plugin,
|
| 215 |
+
map_pytorch_optim_to_deepspeed,
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
from .bnb import has_4bit_bnb_layers, load_and_quantize_model
|
| 219 |
+
from .fsdp_utils import (
|
| 220 |
+
disable_fsdp_ram_efficient_loading,
|
| 221 |
+
enable_fsdp_ram_efficient_loading,
|
| 222 |
+
ensure_weights_retied,
|
| 223 |
+
fsdp2_load_full_state_dict,
|
| 224 |
+
fsdp2_prepare_model,
|
| 225 |
+
fsdp2_switch_optimizer_parameters,
|
| 226 |
+
get_fsdp2_grad_scaler,
|
| 227 |
+
load_fsdp_model,
|
| 228 |
+
load_fsdp_optimizer,
|
| 229 |
+
merge_fsdp_weights,
|
| 230 |
+
save_fsdp_model,
|
| 231 |
+
save_fsdp_optimizer,
|
| 232 |
+
)
|
| 233 |
+
from .launch import (
|
| 234 |
+
PrepareForLaunch,
|
| 235 |
+
_filter_args,
|
| 236 |
+
prepare_deepspeed_cmd_env,
|
| 237 |
+
prepare_multi_gpu_env,
|
| 238 |
+
prepare_sagemager_args_inputs,
|
| 239 |
+
prepare_simple_launcher_cmd_env,
|
| 240 |
+
prepare_tpu,
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
# For docs
|
| 244 |
+
from .megatron_lm import (
|
| 245 |
+
AbstractTrainStep,
|
| 246 |
+
BertTrainStep,
|
| 247 |
+
GPTTrainStep,
|
| 248 |
+
MegatronLMDummyDataLoader,
|
| 249 |
+
MegatronLMDummyScheduler,
|
| 250 |
+
T5TrainStep,
|
| 251 |
+
avg_losses_across_data_parallel_group,
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
if is_megatron_lm_available():
|
| 256 |
+
from .megatron_lm import (
|
| 257 |
+
MegatronEngine,
|
| 258 |
+
MegatronLMOptimizerWrapper,
|
| 259 |
+
MegatronLMSchedulerWrapper,
|
| 260 |
+
gather_across_data_parallel_groups,
|
| 261 |
+
)
|
| 262 |
+
from .megatron_lm import initialize as megatron_lm_initialize
|
| 263 |
+
from .megatron_lm import prepare_data_loader as megatron_lm_prepare_data_loader
|
| 264 |
+
from .megatron_lm import prepare_model_optimizer_scheduler as megatron_lm_prepare_model_optimizer_scheduler
|
| 265 |
+
from .megatron_lm import prepare_optimizer as megatron_lm_prepare_optimizer
|
| 266 |
+
from .megatron_lm import prepare_scheduler as megatron_lm_prepare_scheduler
|
| 267 |
+
from .memory import find_executable_batch_size, release_memory
|
| 268 |
+
from .other import (
|
| 269 |
+
check_os_kernel,
|
| 270 |
+
clean_state_dict_for_safetensors,
|
| 271 |
+
convert_bytes,
|
| 272 |
+
extract_model_from_parallel,
|
| 273 |
+
get_module_children_bottom_up,
|
| 274 |
+
get_pretty_name,
|
| 275 |
+
is_port_in_use,
|
| 276 |
+
load,
|
| 277 |
+
merge_dicts,
|
| 278 |
+
recursive_getattr,
|
| 279 |
+
save,
|
| 280 |
+
wait_for_everyone,
|
| 281 |
+
write_basic_config,
|
| 282 |
+
)
|
| 283 |
+
from .random import set_seed, synchronize_rng_state, synchronize_rng_states
|
| 284 |
+
from .torch_xla import install_xla
|
| 285 |
+
from .tqdm import tqdm
|
| 286 |
+
from .transformer_engine import (
|
| 287 |
+
apply_fp8_autowrap,
|
| 288 |
+
contextual_fp8_autocast,
|
| 289 |
+
convert_model,
|
| 290 |
+
has_transformer_engine_layers,
|
| 291 |
+
)
|
venv/lib/python3.11/site-packages/accelerate/utils/ao.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""
|
| 16 |
+
Needed utilities for torchao FP8 training.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
from functools import partial
|
| 20 |
+
from typing import Callable, Optional
|
| 21 |
+
|
| 22 |
+
import torch
|
| 23 |
+
|
| 24 |
+
from .imports import is_torchao_available, torchao_required
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
if is_torchao_available():
|
| 28 |
+
from torchao.float8.float8_linear import Float8LinearConfig
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def find_first_last_linear_layers(model: torch.nn.Module):
|
| 32 |
+
"""
|
| 33 |
+
Finds the first and last linear layer names in a model.
|
| 34 |
+
|
| 35 |
+
This is needed during FP8 to avoid issues with instability by keeping the first and last layers unquantized.
|
| 36 |
+
|
| 37 |
+
Ref: https://x.com/xariusrke/status/1826669142604141052
|
| 38 |
+
"""
|
| 39 |
+
first_linear, last_linear = None, None
|
| 40 |
+
for name, module in model.named_modules():
|
| 41 |
+
if isinstance(module, torch.nn.Linear):
|
| 42 |
+
if first_linear is None:
|
| 43 |
+
first_linear = name
|
| 44 |
+
last_linear = name
|
| 45 |
+
return first_linear, last_linear
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def filter_linear_layers(module, fqn: str, layers_to_filter: list[str]) -> bool:
|
| 49 |
+
"""
|
| 50 |
+
A function which will check if `module` is:
|
| 51 |
+
- a `torch.nn.Linear` layer
|
| 52 |
+
- has in_features and out_features divisible by 16
|
| 53 |
+
- is not part of `layers_to_filter`
|
| 54 |
+
|
| 55 |
+
Args:
|
| 56 |
+
module (`torch.nn.Module`):
|
| 57 |
+
The module to check.
|
| 58 |
+
fqn (`str`):
|
| 59 |
+
The fully qualified name of the layer.
|
| 60 |
+
layers_to_filter (`List[str]`):
|
| 61 |
+
The list of layers to filter.
|
| 62 |
+
"""
|
| 63 |
+
if isinstance(module, torch.nn.Linear):
|
| 64 |
+
if module.in_features % 16 != 0 or module.out_features % 16 != 0:
|
| 65 |
+
return False
|
| 66 |
+
if fqn in layers_to_filter:
|
| 67 |
+
return False
|
| 68 |
+
return True
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def filter_first_and_last_linear_layers(module, fqn: str) -> bool:
|
| 72 |
+
"""
|
| 73 |
+
A filter function which will filter out all linear layers except the first and last.
|
| 74 |
+
|
| 75 |
+
<Tip>
|
| 76 |
+
|
| 77 |
+
For stability reasons, we skip the first and last linear layers Otherwise can lead to the model not training or
|
| 78 |
+
converging properly
|
| 79 |
+
|
| 80 |
+
</Tip>
|
| 81 |
+
|
| 82 |
+
Args:
|
| 83 |
+
module (`torch.nn.Module`):
|
| 84 |
+
The module to check.
|
| 85 |
+
fqn (`str`):
|
| 86 |
+
The fully qualified name of the layer.
|
| 87 |
+
"""
|
| 88 |
+
first_linear, last_linear = find_first_last_linear_layers(module)
|
| 89 |
+
return filter_linear_layers(module, fqn, layers_to_filter=[first_linear, last_linear])
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
@torchao_required
|
| 93 |
+
def has_ao_layers(model: torch.nn.Module):
|
| 94 |
+
from torchao.float8.float8_linear import Float8Linear
|
| 95 |
+
|
| 96 |
+
for name, module in model.named_modules():
|
| 97 |
+
if isinstance(module, Float8Linear):
|
| 98 |
+
return True
|
| 99 |
+
return False
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
@torchao_required
|
| 103 |
+
def convert_model_to_fp8_ao(
|
| 104 |
+
model: torch.nn.Module,
|
| 105 |
+
config: Optional["Float8LinearConfig"] = None,
|
| 106 |
+
module_filter_func: Optional[Callable] = filter_first_and_last_linear_layers,
|
| 107 |
+
):
|
| 108 |
+
"""
|
| 109 |
+
Converts all `nn.Linear` layers in the model (except the first and last) to torchao's `Float8Linear` layer inplace.
|
| 110 |
+
|
| 111 |
+
Args:
|
| 112 |
+
model (`torch.nn.Module`):
|
| 113 |
+
The model to convert.
|
| 114 |
+
config (`torchao.float8.Float8LinearConfig`, *optional*):
|
| 115 |
+
The configuration for the FP8 training. Recommended to utilize
|
| 116 |
+
`torchao.float8.recipe_name_to_linear_config` to generate this. In general, the default config should be
|
| 117 |
+
sufficient (what is passed when set to `None`).
|
| 118 |
+
module_filter_func (`Callable`, *optional*, defaults to `filter_linear_layers`):
|
| 119 |
+
Optional function that must take in a module and layer name, and returns a boolean indicating whether the
|
| 120 |
+
module should be converted to FP8. Defaults to `filter_linear_layers`. See it for an example.
|
| 121 |
+
|
| 122 |
+
Example:
|
| 123 |
+
|
| 124 |
+
```python
|
| 125 |
+
from accelerate.utils.ao import convert_model_to_fp8_ao
|
| 126 |
+
|
| 127 |
+
model = MyModel()
|
| 128 |
+
model.to("cuda")
|
| 129 |
+
convert_to_float8_training(model)
|
| 130 |
+
|
| 131 |
+
model.train()
|
| 132 |
+
```
|
| 133 |
+
"""
|
| 134 |
+
from torchao.float8 import convert_to_float8_training
|
| 135 |
+
|
| 136 |
+
first_linear, last_linear = find_first_last_linear_layers(model)
|
| 137 |
+
if module_filter_func is None:
|
| 138 |
+
module_filter_func = partial(filter_linear_layers, layers_to_filter=[first_linear, last_linear])
|
| 139 |
+
convert_to_float8_training(model, module_filter_fn=module_filter_func, config=config)
|
venv/lib/python3.11/site-packages/accelerate/utils/bnb.py
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
import logging
|
| 17 |
+
import os
|
| 18 |
+
from copy import deepcopy
|
| 19 |
+
from typing import Optional, Union
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
import torch.nn as nn
|
| 23 |
+
|
| 24 |
+
from accelerate.utils.imports import (
|
| 25 |
+
is_4bit_bnb_available,
|
| 26 |
+
is_8bit_bnb_available,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
from ..big_modeling import dispatch_model, init_empty_weights
|
| 30 |
+
from .dataclasses import BnbQuantizationConfig
|
| 31 |
+
from .modeling import (
|
| 32 |
+
find_tied_parameters,
|
| 33 |
+
get_balanced_memory,
|
| 34 |
+
infer_auto_device_map,
|
| 35 |
+
load_checkpoint_in_model,
|
| 36 |
+
offload_weight,
|
| 37 |
+
set_module_tensor_to_device,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
logger = logging.getLogger(__name__)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def load_and_quantize_model(
|
| 45 |
+
model: torch.nn.Module,
|
| 46 |
+
bnb_quantization_config: BnbQuantizationConfig,
|
| 47 |
+
weights_location: Union[str, os.PathLike] = None,
|
| 48 |
+
device_map: Optional[dict[str, Union[int, str, torch.device]]] = None,
|
| 49 |
+
no_split_module_classes: Optional[list[str]] = None,
|
| 50 |
+
max_memory: Optional[dict[Union[int, str], Union[int, str]]] = None,
|
| 51 |
+
offload_folder: Optional[Union[str, os.PathLike]] = None,
|
| 52 |
+
offload_state_dict: bool = False,
|
| 53 |
+
):
|
| 54 |
+
"""
|
| 55 |
+
This function will quantize the input model with the associated config passed in `bnb_quantization_config`. If the
|
| 56 |
+
model is in the meta device, we will load and dispatch the weights according to the `device_map` passed. If the
|
| 57 |
+
model is already loaded, we will quantize the model and put the model on the GPU,
|
| 58 |
+
|
| 59 |
+
Args:
|
| 60 |
+
model (`torch.nn.Module`):
|
| 61 |
+
Input model. The model can be already loaded or on the meta device
|
| 62 |
+
bnb_quantization_config (`BnbQuantizationConfig`):
|
| 63 |
+
The bitsandbytes quantization parameters
|
| 64 |
+
weights_location (`str` or `os.PathLike`):
|
| 65 |
+
The folder weights_location to load. It can be:
|
| 66 |
+
- a path to a file containing a whole model state dict
|
| 67 |
+
- a path to a `.json` file containing the index to a sharded checkpoint
|
| 68 |
+
- a path to a folder containing a unique `.index.json` file and the shards of a checkpoint.
|
| 69 |
+
- a path to a folder containing a unique pytorch_model.bin file.
|
| 70 |
+
device_map (`Dict[str, Union[int, str, torch.device]]`, *optional*):
|
| 71 |
+
A map that specifies where each submodule should go. It doesn't need to be refined to each parameter/buffer
|
| 72 |
+
name, once a given module name is inside, every submodule of it will be sent to the same device.
|
| 73 |
+
no_split_module_classes (`List[str]`, *optional*):
|
| 74 |
+
A list of layer class names that should never be split across device (for instance any layer that has a
|
| 75 |
+
residual connection).
|
| 76 |
+
max_memory (`Dict`, *optional*):
|
| 77 |
+
A dictionary device identifier to maximum memory. Will default to the maximum memory available if unset.
|
| 78 |
+
offload_folder (`str` or `os.PathLike`, *optional*):
|
| 79 |
+
If the `device_map` contains any value `"disk"`, the folder where we will offload weights.
|
| 80 |
+
offload_state_dict (`bool`, *optional*, defaults to `False`):
|
| 81 |
+
If `True`, will temporarily offload the CPU state dict on the hard drive to avoid getting out of CPU RAM if
|
| 82 |
+
the weight of the CPU state dict + the biggest shard does not fit.
|
| 83 |
+
|
| 84 |
+
Returns:
|
| 85 |
+
`torch.nn.Module`: The quantized model
|
| 86 |
+
"""
|
| 87 |
+
|
| 88 |
+
load_in_4bit = bnb_quantization_config.load_in_4bit
|
| 89 |
+
load_in_8bit = bnb_quantization_config.load_in_8bit
|
| 90 |
+
|
| 91 |
+
if load_in_8bit and not is_8bit_bnb_available():
|
| 92 |
+
raise ImportError(
|
| 93 |
+
"You have a version of `bitsandbytes` that is not compatible with 8bit quantization,"
|
| 94 |
+
" make sure you have the latest version of `bitsandbytes` installed."
|
| 95 |
+
)
|
| 96 |
+
if load_in_4bit and not is_4bit_bnb_available():
|
| 97 |
+
raise ValueError(
|
| 98 |
+
"You have a version of `bitsandbytes` that is not compatible with 4bit quantization,"
|
| 99 |
+
"make sure you have the latest version of `bitsandbytes` installed."
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
modules_on_cpu = []
|
| 103 |
+
# custom device map
|
| 104 |
+
if isinstance(device_map, dict) and len(device_map.keys()) > 1:
|
| 105 |
+
modules_on_cpu = [key for key, value in device_map.items() if value in ["disk", "cpu"]]
|
| 106 |
+
|
| 107 |
+
# We keep some modules such as the lm_head in their original dtype for numerical stability reasons
|
| 108 |
+
if bnb_quantization_config.skip_modules is None:
|
| 109 |
+
bnb_quantization_config.skip_modules = get_keys_to_not_convert(model)
|
| 110 |
+
|
| 111 |
+
# add cpu modules to skip modules only for 4-bit modules
|
| 112 |
+
if load_in_4bit:
|
| 113 |
+
bnb_quantization_config.skip_modules.extend(modules_on_cpu)
|
| 114 |
+
modules_to_not_convert = bnb_quantization_config.skip_modules
|
| 115 |
+
|
| 116 |
+
# We add the modules we want to keep in full precision
|
| 117 |
+
if bnb_quantization_config.keep_in_fp32_modules is None:
|
| 118 |
+
bnb_quantization_config.keep_in_fp32_modules = []
|
| 119 |
+
keep_in_fp32_modules = bnb_quantization_config.keep_in_fp32_modules
|
| 120 |
+
modules_to_not_convert.extend(keep_in_fp32_modules)
|
| 121 |
+
|
| 122 |
+
# compatibility with peft
|
| 123 |
+
model.is_loaded_in_4bit = load_in_4bit
|
| 124 |
+
model.is_loaded_in_8bit = load_in_8bit
|
| 125 |
+
|
| 126 |
+
model_device = get_parameter_device(model)
|
| 127 |
+
if model_device.type != "meta":
|
| 128 |
+
# quantization of an already loaded model
|
| 129 |
+
logger.warning(
|
| 130 |
+
"It is not recommended to quantize a loaded model. "
|
| 131 |
+
"The model should be instantiated under the `init_empty_weights` context manager."
|
| 132 |
+
)
|
| 133 |
+
model = replace_with_bnb_layers(model, bnb_quantization_config, modules_to_not_convert=modules_to_not_convert)
|
| 134 |
+
# convert param to the right dtype
|
| 135 |
+
dtype = bnb_quantization_config.torch_dtype
|
| 136 |
+
for name, param in model.state_dict().items():
|
| 137 |
+
if any(module_to_keep_in_fp32 in name for module_to_keep_in_fp32 in keep_in_fp32_modules):
|
| 138 |
+
param.to(torch.float32)
|
| 139 |
+
if param.dtype != torch.float32:
|
| 140 |
+
name = name.replace(".weight", "").replace(".bias", "")
|
| 141 |
+
param = getattr(model, name, None)
|
| 142 |
+
if param is not None:
|
| 143 |
+
param.to(torch.float32)
|
| 144 |
+
elif torch.is_floating_point(param):
|
| 145 |
+
param.to(dtype)
|
| 146 |
+
if model_device.type == "cuda":
|
| 147 |
+
# move everything to cpu in the first place because we can't do quantization if the weights are already on cuda
|
| 148 |
+
model.cuda(torch.cuda.current_device())
|
| 149 |
+
torch.cuda.empty_cache()
|
| 150 |
+
elif torch.cuda.is_available():
|
| 151 |
+
model.to(torch.cuda.current_device())
|
| 152 |
+
elif torch.xpu.is_available():
|
| 153 |
+
model.to(torch.xpu.current_device())
|
| 154 |
+
else:
|
| 155 |
+
raise RuntimeError("No GPU found. A GPU is needed for quantization.")
|
| 156 |
+
logger.info(
|
| 157 |
+
f"The model device type is {model_device.type}. However, gpu is needed for quantization."
|
| 158 |
+
"We move the model to gpu."
|
| 159 |
+
)
|
| 160 |
+
return model
|
| 161 |
+
|
| 162 |
+
elif weights_location is None:
|
| 163 |
+
raise RuntimeError(
|
| 164 |
+
f"`weights_location` needs to be the folder path containing the weights of the model, but we found {weights_location} "
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
else:
|
| 168 |
+
with init_empty_weights():
|
| 169 |
+
model = replace_with_bnb_layers(
|
| 170 |
+
model, bnb_quantization_config, modules_to_not_convert=modules_to_not_convert
|
| 171 |
+
)
|
| 172 |
+
device_map = get_quantized_model_device_map(
|
| 173 |
+
model,
|
| 174 |
+
bnb_quantization_config,
|
| 175 |
+
device_map,
|
| 176 |
+
max_memory=max_memory,
|
| 177 |
+
no_split_module_classes=no_split_module_classes,
|
| 178 |
+
)
|
| 179 |
+
if offload_state_dict is None and device_map is not None and "disk" in device_map.values():
|
| 180 |
+
offload_state_dict = True
|
| 181 |
+
|
| 182 |
+
offload = any(x in list(device_map.values()) for x in ["cpu", "disk"])
|
| 183 |
+
|
| 184 |
+
load_checkpoint_in_model(
|
| 185 |
+
model,
|
| 186 |
+
weights_location,
|
| 187 |
+
device_map,
|
| 188 |
+
dtype=bnb_quantization_config.torch_dtype,
|
| 189 |
+
offload_folder=offload_folder,
|
| 190 |
+
offload_state_dict=offload_state_dict,
|
| 191 |
+
keep_in_fp32_modules=bnb_quantization_config.keep_in_fp32_modules,
|
| 192 |
+
offload_8bit_bnb=load_in_8bit and offload,
|
| 193 |
+
)
|
| 194 |
+
return dispatch_model(model, device_map=device_map, offload_dir=offload_folder)
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def get_quantized_model_device_map(
|
| 198 |
+
model, bnb_quantization_config, device_map=None, max_memory=None, no_split_module_classes=None
|
| 199 |
+
):
|
| 200 |
+
if device_map is None:
|
| 201 |
+
if torch.cuda.is_available():
|
| 202 |
+
device_map = {"": torch.cuda.current_device()}
|
| 203 |
+
elif torch.xpu.is_available():
|
| 204 |
+
device_map = {"": torch.xpu.current_device()}
|
| 205 |
+
else:
|
| 206 |
+
raise RuntimeError("No GPU found. A GPU is needed for quantization.")
|
| 207 |
+
logger.info("The device_map was not initialized.Setting device_map to `{'':torch.cuda.current_device()}`.")
|
| 208 |
+
|
| 209 |
+
if isinstance(device_map, str):
|
| 210 |
+
if device_map not in ["auto", "balanced", "balanced_low_0", "sequential"]:
|
| 211 |
+
raise ValueError(
|
| 212 |
+
"If passing a string for `device_map`, please choose 'auto', 'balanced', 'balanced_low_0' or "
|
| 213 |
+
"'sequential'."
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
special_dtypes = {}
|
| 217 |
+
special_dtypes.update(
|
| 218 |
+
{
|
| 219 |
+
name: bnb_quantization_config.torch_dtype
|
| 220 |
+
for name, _ in model.named_parameters()
|
| 221 |
+
if any(m in name for m in bnb_quantization_config.skip_modules)
|
| 222 |
+
}
|
| 223 |
+
)
|
| 224 |
+
special_dtypes.update(
|
| 225 |
+
{
|
| 226 |
+
name: torch.float32
|
| 227 |
+
for name, _ in model.named_parameters()
|
| 228 |
+
if any(m in name for m in bnb_quantization_config.keep_in_fp32_modules)
|
| 229 |
+
}
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
kwargs = {}
|
| 233 |
+
kwargs["special_dtypes"] = special_dtypes
|
| 234 |
+
kwargs["no_split_module_classes"] = no_split_module_classes
|
| 235 |
+
kwargs["dtype"] = bnb_quantization_config.target_dtype
|
| 236 |
+
|
| 237 |
+
# get max_memory for each device.
|
| 238 |
+
if device_map != "sequential":
|
| 239 |
+
max_memory = get_balanced_memory(
|
| 240 |
+
model,
|
| 241 |
+
low_zero=(device_map == "balanced_low_0"),
|
| 242 |
+
max_memory=max_memory,
|
| 243 |
+
**kwargs,
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
kwargs["max_memory"] = max_memory
|
| 247 |
+
device_map = infer_auto_device_map(model, **kwargs)
|
| 248 |
+
|
| 249 |
+
if isinstance(device_map, dict):
|
| 250 |
+
# check if don't have any quantized module on the cpu
|
| 251 |
+
modules_not_to_convert = bnb_quantization_config.skip_modules + bnb_quantization_config.keep_in_fp32_modules
|
| 252 |
+
|
| 253 |
+
device_map_without_some_modules = {
|
| 254 |
+
key: device_map[key] for key in device_map.keys() if key not in modules_not_to_convert
|
| 255 |
+
}
|
| 256 |
+
for device in ["cpu", "disk"]:
|
| 257 |
+
if device in device_map_without_some_modules.values():
|
| 258 |
+
if bnb_quantization_config.load_in_4bit:
|
| 259 |
+
raise ValueError(
|
| 260 |
+
"""
|
| 261 |
+
Some modules are dispatched on the CPU or the disk. Make sure you have enough GPU RAM to fit
|
| 262 |
+
the quantized model. If you want to dispatch the model on the CPU or the disk while keeping
|
| 263 |
+
these modules in `torch_dtype`, you need to pass a custom `device_map` to
|
| 264 |
+
`load_and_quantize_model`. Check
|
| 265 |
+
https://huggingface.co/docs/accelerate/main/en/usage_guides/quantization#offload-modules-to-cpu-and-disk
|
| 266 |
+
for more details.
|
| 267 |
+
"""
|
| 268 |
+
)
|
| 269 |
+
else:
|
| 270 |
+
logger.info(
|
| 271 |
+
"Some modules are are offloaded to the CPU or the disk. Note that these modules will be converted to 8-bit"
|
| 272 |
+
)
|
| 273 |
+
del device_map_without_some_modules
|
| 274 |
+
return device_map
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def replace_with_bnb_layers(model, bnb_quantization_config, modules_to_not_convert=None, current_key_name=None):
|
| 278 |
+
"""
|
| 279 |
+
A helper function to replace all `torch.nn.Linear` modules by `bnb.nn.Linear8bit` modules or by `bnb.nn.Linear4bit`
|
| 280 |
+
modules from the `bitsandbytes`library. The function will be run recursively and replace `torch.nn.Linear` modules.
|
| 281 |
+
|
| 282 |
+
Parameters:
|
| 283 |
+
model (`torch.nn.Module`):
|
| 284 |
+
Input model or `torch.nn.Module` as the function is run recursively.
|
| 285 |
+
modules_to_not_convert (`List[str]`):
|
| 286 |
+
Names of the modules to not quantize convert. In practice we keep the `lm_head` in full precision for
|
| 287 |
+
numerical stability reasons.
|
| 288 |
+
current_key_name (`List[str]`, *optional*):
|
| 289 |
+
An array to track the current key of the recursion. This is used to check whether the current key (part of
|
| 290 |
+
it) is not in the list of modules to not convert.
|
| 291 |
+
"""
|
| 292 |
+
|
| 293 |
+
if modules_to_not_convert is None:
|
| 294 |
+
modules_to_not_convert = []
|
| 295 |
+
|
| 296 |
+
model, has_been_replaced = _replace_with_bnb_layers(
|
| 297 |
+
model, bnb_quantization_config, modules_to_not_convert, current_key_name
|
| 298 |
+
)
|
| 299 |
+
if not has_been_replaced:
|
| 300 |
+
logger.warning(
|
| 301 |
+
"You are loading your model in 8bit or 4bit but no linear modules were found in your model."
|
| 302 |
+
" this can happen for some architectures such as gpt2 that uses Conv1D instead of Linear layers."
|
| 303 |
+
" Please double check your model architecture, or submit an issue on github if you think this is"
|
| 304 |
+
" a bug."
|
| 305 |
+
)
|
| 306 |
+
return model
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def _replace_with_bnb_layers(
|
| 310 |
+
model,
|
| 311 |
+
bnb_quantization_config,
|
| 312 |
+
modules_to_not_convert=None,
|
| 313 |
+
current_key_name=None,
|
| 314 |
+
):
|
| 315 |
+
"""
|
| 316 |
+
Private method that wraps the recursion for module replacement.
|
| 317 |
+
|
| 318 |
+
Returns the converted model and a boolean that indicates if the conversion has been successfull or not.
|
| 319 |
+
"""
|
| 320 |
+
# bitsandbytes will initialize CUDA on import, so it needs to be imported lazily
|
| 321 |
+
import bitsandbytes as bnb
|
| 322 |
+
|
| 323 |
+
has_been_replaced = False
|
| 324 |
+
for name, module in model.named_children():
|
| 325 |
+
if current_key_name is None:
|
| 326 |
+
current_key_name = []
|
| 327 |
+
current_key_name.append(name)
|
| 328 |
+
if isinstance(module, nn.Linear) and name not in modules_to_not_convert:
|
| 329 |
+
# Check if the current key is not in the `modules_to_not_convert`
|
| 330 |
+
current_key_name_str = ".".join(current_key_name)
|
| 331 |
+
proceed = True
|
| 332 |
+
for key in modules_to_not_convert:
|
| 333 |
+
if (
|
| 334 |
+
(key in current_key_name_str) and (key + "." in current_key_name_str)
|
| 335 |
+
) or key == current_key_name_str:
|
| 336 |
+
proceed = False
|
| 337 |
+
break
|
| 338 |
+
if proceed:
|
| 339 |
+
# Load bnb module with empty weight and replace ``nn.Linear` module
|
| 340 |
+
if bnb_quantization_config.load_in_8bit:
|
| 341 |
+
bnb_module = bnb.nn.Linear8bitLt(
|
| 342 |
+
module.in_features,
|
| 343 |
+
module.out_features,
|
| 344 |
+
module.bias is not None,
|
| 345 |
+
has_fp16_weights=False,
|
| 346 |
+
threshold=bnb_quantization_config.llm_int8_threshold,
|
| 347 |
+
)
|
| 348 |
+
elif bnb_quantization_config.load_in_4bit:
|
| 349 |
+
bnb_module = bnb.nn.Linear4bit(
|
| 350 |
+
module.in_features,
|
| 351 |
+
module.out_features,
|
| 352 |
+
module.bias is not None,
|
| 353 |
+
bnb_quantization_config.bnb_4bit_compute_dtype,
|
| 354 |
+
compress_statistics=bnb_quantization_config.bnb_4bit_use_double_quant,
|
| 355 |
+
quant_type=bnb_quantization_config.bnb_4bit_quant_type,
|
| 356 |
+
)
|
| 357 |
+
else:
|
| 358 |
+
raise ValueError("load_in_8bit and load_in_4bit can't be both False")
|
| 359 |
+
bnb_module.weight.data = module.weight.data
|
| 360 |
+
if module.bias is not None:
|
| 361 |
+
bnb_module.bias.data = module.bias.data
|
| 362 |
+
bnb_module.requires_grad_(False)
|
| 363 |
+
setattr(model, name, bnb_module)
|
| 364 |
+
has_been_replaced = True
|
| 365 |
+
if len(list(module.children())) > 0:
|
| 366 |
+
_, _has_been_replaced = _replace_with_bnb_layers(
|
| 367 |
+
module, bnb_quantization_config, modules_to_not_convert, current_key_name
|
| 368 |
+
)
|
| 369 |
+
has_been_replaced = has_been_replaced | _has_been_replaced
|
| 370 |
+
# Remove the last key for recursion
|
| 371 |
+
current_key_name.pop(-1)
|
| 372 |
+
return model, has_been_replaced
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
def get_keys_to_not_convert(model):
|
| 376 |
+
r"""
|
| 377 |
+
An utility function to get the key of the module to keep in full precision if any For example for CausalLM modules
|
| 378 |
+
we may want to keep the lm_head in full precision for numerical stability reasons. For other architectures, we want
|
| 379 |
+
to keep the tied weights of the model. The function will return a list of the keys of the modules to not convert in
|
| 380 |
+
int8.
|
| 381 |
+
|
| 382 |
+
Parameters:
|
| 383 |
+
model (`torch.nn.Module`):
|
| 384 |
+
Input model
|
| 385 |
+
"""
|
| 386 |
+
# Create a copy of the model
|
| 387 |
+
with init_empty_weights():
|
| 388 |
+
tied_model = deepcopy(model) # this has 0 cost since it is done inside `init_empty_weights` context manager`
|
| 389 |
+
|
| 390 |
+
tied_params = find_tied_parameters(tied_model)
|
| 391 |
+
# For compatibility with Accelerate < 0.18
|
| 392 |
+
if isinstance(tied_params, dict):
|
| 393 |
+
tied_keys = sum(list(tied_params.values()), []) + list(tied_params.keys())
|
| 394 |
+
else:
|
| 395 |
+
tied_keys = sum(tied_params, [])
|
| 396 |
+
has_tied_params = len(tied_keys) > 0
|
| 397 |
+
|
| 398 |
+
# Check if it is a base model
|
| 399 |
+
is_base_model = False
|
| 400 |
+
if hasattr(model, "base_model_prefix"):
|
| 401 |
+
is_base_model = not hasattr(model, model.base_model_prefix)
|
| 402 |
+
|
| 403 |
+
# Ignore this for base models (BertModel, GPT2Model, etc.)
|
| 404 |
+
if (not has_tied_params) and is_base_model:
|
| 405 |
+
return []
|
| 406 |
+
|
| 407 |
+
# otherwise they have an attached head
|
| 408 |
+
list_modules = list(model.named_children())
|
| 409 |
+
list_last_module = [list_modules[-1][0]]
|
| 410 |
+
|
| 411 |
+
# add last module together with tied weights
|
| 412 |
+
intersection = set(list_last_module) - set(tied_keys)
|
| 413 |
+
list_untouched = list(set(tied_keys)) + list(intersection)
|
| 414 |
+
|
| 415 |
+
# remove ".weight" from the keys
|
| 416 |
+
names_to_remove = [".weight", ".bias"]
|
| 417 |
+
filtered_module_names = []
|
| 418 |
+
for name in list_untouched:
|
| 419 |
+
for name_to_remove in names_to_remove:
|
| 420 |
+
if name_to_remove in name:
|
| 421 |
+
name = name.replace(name_to_remove, "")
|
| 422 |
+
filtered_module_names.append(name)
|
| 423 |
+
|
| 424 |
+
return filtered_module_names
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
def has_4bit_bnb_layers(model):
|
| 428 |
+
"""Check if we have `bnb.nn.Linear4bit` or `bnb.nn.Linear8bitLt` layers inside our model"""
|
| 429 |
+
# bitsandbytes will initialize CUDA on import, so it needs to be imported lazily
|
| 430 |
+
import bitsandbytes as bnb
|
| 431 |
+
|
| 432 |
+
for m in model.modules():
|
| 433 |
+
if isinstance(m, bnb.nn.Linear4bit):
|
| 434 |
+
return True
|
| 435 |
+
return False
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
def get_parameter_device(parameter: nn.Module):
|
| 439 |
+
return next(parameter.parameters()).device
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def quantize_and_offload_8bit(model, param, param_name, new_dtype, offload_folder, offload_index, fp16_statistics):
|
| 443 |
+
# if it is not quantized, we quantize and offload the quantized weights and the SCB stats
|
| 444 |
+
if fp16_statistics is None:
|
| 445 |
+
set_module_tensor_to_device(model, param_name, 0, dtype=new_dtype, value=param)
|
| 446 |
+
tensor_name = param_name
|
| 447 |
+
module = model
|
| 448 |
+
if "." in tensor_name:
|
| 449 |
+
splits = tensor_name.split(".")
|
| 450 |
+
for split in splits[:-1]:
|
| 451 |
+
new_module = getattr(module, split)
|
| 452 |
+
if new_module is None:
|
| 453 |
+
raise ValueError(f"{module} has no attribute {split}.")
|
| 454 |
+
module = new_module
|
| 455 |
+
tensor_name = splits[-1]
|
| 456 |
+
# offload weights
|
| 457 |
+
module._parameters[tensor_name].requires_grad = False
|
| 458 |
+
offload_weight(module._parameters[tensor_name], param_name, offload_folder, index=offload_index)
|
| 459 |
+
if hasattr(module._parameters[tensor_name], "SCB"):
|
| 460 |
+
offload_weight(
|
| 461 |
+
module._parameters[tensor_name].SCB,
|
| 462 |
+
param_name.replace("weight", "SCB"),
|
| 463 |
+
offload_folder,
|
| 464 |
+
index=offload_index,
|
| 465 |
+
)
|
| 466 |
+
else:
|
| 467 |
+
offload_weight(param, param_name, offload_folder, index=offload_index)
|
| 468 |
+
offload_weight(fp16_statistics, param_name.replace("weight", "SCB"), offload_folder, index=offload_index)
|
| 469 |
+
|
| 470 |
+
set_module_tensor_to_device(model, param_name, "meta", dtype=new_dtype, value=torch.empty(*param.size()))
|
venv/lib/python3.11/site-packages/accelerate/utils/constants.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import operator as op
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
SCALER_NAME = "scaler.pt"
|
| 19 |
+
MODEL_NAME = "pytorch_model"
|
| 20 |
+
SAFE_MODEL_NAME = "model"
|
| 21 |
+
RNG_STATE_NAME = "random_states"
|
| 22 |
+
OPTIMIZER_NAME = "optimizer"
|
| 23 |
+
SCHEDULER_NAME = "scheduler"
|
| 24 |
+
SAMPLER_NAME = "sampler"
|
| 25 |
+
PROFILE_PATTERN_NAME = "profile_{suffix}.json"
|
| 26 |
+
WEIGHTS_NAME = f"{MODEL_NAME}.bin"
|
| 27 |
+
WEIGHTS_PATTERN_NAME = "pytorch_model{suffix}.bin"
|
| 28 |
+
WEIGHTS_INDEX_NAME = f"{WEIGHTS_NAME}.index.json"
|
| 29 |
+
SAFE_WEIGHTS_NAME = f"{SAFE_MODEL_NAME}.safetensors"
|
| 30 |
+
SAFE_WEIGHTS_PATTERN_NAME = "model{suffix}.safetensors"
|
| 31 |
+
SAFE_WEIGHTS_INDEX_NAME = f"{SAFE_WEIGHTS_NAME}.index.json"
|
| 32 |
+
SAGEMAKER_PYTORCH_VERSION = "1.10.2"
|
| 33 |
+
SAGEMAKER_PYTHON_VERSION = "py38"
|
| 34 |
+
SAGEMAKER_TRANSFORMERS_VERSION = "4.17.0"
|
| 35 |
+
SAGEMAKER_PARALLEL_EC2_INSTANCES = ["ml.p3.16xlarge", "ml.p3dn.24xlarge", "ml.p4dn.24xlarge"]
|
| 36 |
+
FSDP_SHARDING_STRATEGY = ["FULL_SHARD", "SHARD_GRAD_OP", "NO_SHARD", "HYBRID_SHARD", "HYBRID_SHARD_ZERO2"]
|
| 37 |
+
FSDP_AUTO_WRAP_POLICY = ["TRANSFORMER_BASED_WRAP", "SIZE_BASED_WRAP", "NO_WRAP"]
|
| 38 |
+
FSDP_BACKWARD_PREFETCH = ["BACKWARD_PRE", "BACKWARD_POST", "NO_PREFETCH"]
|
| 39 |
+
FSDP_STATE_DICT_TYPE = ["FULL_STATE_DICT", "LOCAL_STATE_DICT", "SHARDED_STATE_DICT"]
|
| 40 |
+
FSDP2_STATE_DICT_TYPE = ["SHARDED_STATE_DICT"]
|
| 41 |
+
FSDP_PYTORCH_VERSION = (
|
| 42 |
+
"2.1.0.a0+32f93b1" # Technically should be 2.1.0, but MS-AMP uses this specific prerelease in their Docker image.
|
| 43 |
+
)
|
| 44 |
+
FSDP2_PYTORCH_VERSION = "2.5.1"
|
| 45 |
+
FSDP_MODEL_NAME = "pytorch_model_fsdp"
|
| 46 |
+
DEEPSPEED_MULTINODE_LAUNCHERS = ["pdsh", "standard", "openmpi", "mvapich", "mpich", "nossh", "slurm"]
|
| 47 |
+
TORCH_DYNAMO_MODES = ["default", "reduce-overhead", "max-autotune"]
|
| 48 |
+
ELASTIC_LOG_LINE_PREFIX_TEMPLATE_PYTORCH_VERSION = "2.2.0"
|
| 49 |
+
XPU_PROFILING_AVAILABLE_PYTORCH_VERSION = "2.4.0"
|
| 50 |
+
MITA_PROFILING_AVAILABLE_PYTORCH_VERSION = "2.1.0"
|
| 51 |
+
BETA_TP_AVAILABLE_PYTORCH_VERSION = "2.3.0"
|
| 52 |
+
BETA_TP_AVAILABLE_TRANSFORMERS_VERSION = "4.47.0"
|
| 53 |
+
|
| 54 |
+
STR_OPERATION_TO_FUNC = {">": op.gt, ">=": op.ge, "==": op.eq, "!=": op.ne, "<=": op.le, "<": op.lt}
|
| 55 |
+
|
| 56 |
+
# These are the args for `torch.distributed.launch` for pytorch < 1.9
|
| 57 |
+
TORCH_LAUNCH_PARAMS = [
|
| 58 |
+
"nnodes",
|
| 59 |
+
"nproc_per_node",
|
| 60 |
+
"rdzv_backend",
|
| 61 |
+
"rdzv_endpoint",
|
| 62 |
+
"rdzv_id",
|
| 63 |
+
"rdzv_conf",
|
| 64 |
+
"standalone",
|
| 65 |
+
"max_restarts",
|
| 66 |
+
"monitor_interval",
|
| 67 |
+
"start_method",
|
| 68 |
+
"role",
|
| 69 |
+
"module",
|
| 70 |
+
"m",
|
| 71 |
+
"no_python",
|
| 72 |
+
"run_path",
|
| 73 |
+
"log_dir",
|
| 74 |
+
"r",
|
| 75 |
+
"redirects",
|
| 76 |
+
"t",
|
| 77 |
+
"tee",
|
| 78 |
+
"node_rank",
|
| 79 |
+
"master_addr",
|
| 80 |
+
"master_port",
|
| 81 |
+
]
|
| 82 |
+
|
| 83 |
+
CUDA_DISTRIBUTED_TYPES = ["DEEPSPEED", "MULTI_GPU", "FSDP", "MEGATRON_LM", "TP"]
|
| 84 |
+
TORCH_DISTRIBUTED_OPERATION_TYPES = CUDA_DISTRIBUTED_TYPES + [
|
| 85 |
+
"MULTI_NPU",
|
| 86 |
+
"MULTI_MLU",
|
| 87 |
+
"MULTI_SDAA",
|
| 88 |
+
"MULTI_MUSA",
|
| 89 |
+
"MULTI_XPU",
|
| 90 |
+
"MULTI_CPU",
|
| 91 |
+
"MULTI_HPU",
|
| 92 |
+
]
|
venv/lib/python3.11/site-packages/accelerate/utils/dataclasses.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
venv/lib/python3.11/site-packages/accelerate/utils/deepspeed.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import base64
|
| 16 |
+
import json
|
| 17 |
+
import os
|
| 18 |
+
from copy import deepcopy
|
| 19 |
+
|
| 20 |
+
from torch import optim
|
| 21 |
+
|
| 22 |
+
from ..optimizer import AcceleratedOptimizer
|
| 23 |
+
from ..scheduler import AcceleratedScheduler
|
| 24 |
+
from .dataclasses import DistributedType
|
| 25 |
+
from .imports import is_bnb_available
|
| 26 |
+
from .versions import compare_versions
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def map_pytorch_optim_to_deepspeed(optimizer):
|
| 30 |
+
"""
|
| 31 |
+
Args:
|
| 32 |
+
optimizer: torch.optim.Optimizer
|
| 33 |
+
|
| 34 |
+
Returns the DeepSeedCPUOptimizer (deepspeed.ops) version of the optimizer.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
defaults = {k: v for k, v in optimizer.defaults.items() if k in ["lr", "weight_decay"]}
|
| 38 |
+
|
| 39 |
+
# Select the DeepSpeedCPUOptimizer based on the original optimizer class.
|
| 40 |
+
# DeepSpeedCPUAdam is the default
|
| 41 |
+
from deepspeed.ops.adam import DeepSpeedCPUAdam
|
| 42 |
+
|
| 43 |
+
optimizer_class = DeepSpeedCPUAdam
|
| 44 |
+
|
| 45 |
+
# For DeepSpeedCPUAdam (adamw_mode)
|
| 46 |
+
if compare_versions("deepspeed", ">=", "0.3.1"):
|
| 47 |
+
defaults["adamw_mode"] = False
|
| 48 |
+
is_adaw = isinstance(optimizer, optim.AdamW)
|
| 49 |
+
|
| 50 |
+
if is_bnb_available() and not is_adaw:
|
| 51 |
+
import bitsandbytes.optim as bnb_opt
|
| 52 |
+
|
| 53 |
+
if isinstance(optimizer, (bnb_opt.AdamW, bnb_opt.AdamW32bit)):
|
| 54 |
+
try:
|
| 55 |
+
is_adaw = optimizer.optim_bits == 32
|
| 56 |
+
except AttributeError:
|
| 57 |
+
is_adaw = optimizer.args.optim_bits == 32
|
| 58 |
+
else:
|
| 59 |
+
is_adaw = False
|
| 60 |
+
|
| 61 |
+
if is_adaw:
|
| 62 |
+
defaults["adamw_mode"] = True
|
| 63 |
+
|
| 64 |
+
# For DeepSpeedCPUAdagrad
|
| 65 |
+
if compare_versions("deepspeed", ">=", "0.5.5"):
|
| 66 |
+
# Check if the optimizer is PyTorch's Adagrad.
|
| 67 |
+
is_ada = isinstance(optimizer, optim.Adagrad)
|
| 68 |
+
# If not, and bitsandbytes is available,
|
| 69 |
+
# # check if the optimizer is the 32-bit bitsandbytes Adagrad.
|
| 70 |
+
if is_bnb_available() and not is_ada:
|
| 71 |
+
import bitsandbytes.optim as bnb_opt
|
| 72 |
+
|
| 73 |
+
if isinstance(optimizer, (bnb_opt.Adagrad, bnb_opt.Adagrad32bit)):
|
| 74 |
+
try:
|
| 75 |
+
is_ada = optimizer.optim_bits == 32
|
| 76 |
+
except AttributeError:
|
| 77 |
+
is_ada = optimizer.args.optim_bits == 32
|
| 78 |
+
if is_ada:
|
| 79 |
+
from deepspeed.ops.adagrad import DeepSpeedCPUAdagrad
|
| 80 |
+
|
| 81 |
+
optimizer_class = DeepSpeedCPUAdagrad
|
| 82 |
+
|
| 83 |
+
# For DeepSpeedCPULion
|
| 84 |
+
if is_bnb_available(min_version="0.38.0") and compare_versions("deepspeed", ">=", "0.11.0"):
|
| 85 |
+
from bitsandbytes.optim import Lion, Lion32bit
|
| 86 |
+
|
| 87 |
+
if isinstance(optimizer, (Lion, Lion32bit)):
|
| 88 |
+
try:
|
| 89 |
+
is_bnb_32bits = optimizer.optim_bits == 32
|
| 90 |
+
except AttributeError:
|
| 91 |
+
is_bnb_32bits = optimizer.args.optim_bits == 32
|
| 92 |
+
if is_bnb_32bits:
|
| 93 |
+
from deepspeed.ops.lion import DeepSpeedCPULion
|
| 94 |
+
|
| 95 |
+
optimizer_class = DeepSpeedCPULion
|
| 96 |
+
|
| 97 |
+
return optimizer_class(optimizer.param_groups, **defaults)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def get_active_deepspeed_plugin(state):
|
| 101 |
+
"""
|
| 102 |
+
Returns the currently active DeepSpeedPlugin.
|
| 103 |
+
|
| 104 |
+
Raises:
|
| 105 |
+
ValueError: If DeepSpeed was not enabled and this function is called.
|
| 106 |
+
"""
|
| 107 |
+
if state.distributed_type != DistributedType.DEEPSPEED:
|
| 108 |
+
raise ValueError(
|
| 109 |
+
"Couldn't retrieve the active `DeepSpeedPlugin` as none were enabled. "
|
| 110 |
+
"Please make sure that either `Accelerator` is configured for `deepspeed` "
|
| 111 |
+
"or make sure that the desired `DeepSpeedPlugin` has been enabled (`AcceleratorState().select_deepspeed_plugin(name)`) "
|
| 112 |
+
"before calling this function."
|
| 113 |
+
)
|
| 114 |
+
if not isinstance(state.deepspeed_plugins, dict):
|
| 115 |
+
return state.deepspeed_plugins
|
| 116 |
+
return next(plugin for plugin in state.deepspeed_plugins.values() if plugin.selected)
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class HfDeepSpeedConfig:
|
| 120 |
+
"""
|
| 121 |
+
This object contains a DeepSpeed configuration dictionary and can be quickly queried for things like zero stage.
|
| 122 |
+
|
| 123 |
+
A `weakref` of this object is stored in the module's globals to be able to access the config from areas where
|
| 124 |
+
things like the Trainer object is not available (e.g. `from_pretrained` and `_get_resized_embeddings`). Therefore
|
| 125 |
+
it's important that this object remains alive while the program is still running.
|
| 126 |
+
|
| 127 |
+
[`Trainer`] uses the `HfTrainerDeepSpeedConfig` subclass instead. That subclass has logic to sync the configuration
|
| 128 |
+
with values of [`TrainingArguments`] by replacing special placeholder values: `"auto"`. Without this special logic
|
| 129 |
+
the DeepSpeed configuration is not modified in any way.
|
| 130 |
+
|
| 131 |
+
Args:
|
| 132 |
+
config_file_or_dict (`Union[str, Dict]`): path to DeepSpeed config file or dict.
|
| 133 |
+
|
| 134 |
+
"""
|
| 135 |
+
|
| 136 |
+
def __init__(self, config_file_or_dict):
|
| 137 |
+
if isinstance(config_file_or_dict, dict):
|
| 138 |
+
# Don't modify user's data should they want to reuse it (e.g. in tests), because once we
|
| 139 |
+
# modified it, it will not be accepted here again, since `auto` values would have been overridden
|
| 140 |
+
config = deepcopy(config_file_or_dict)
|
| 141 |
+
elif os.path.exists(config_file_or_dict):
|
| 142 |
+
with open(config_file_or_dict, encoding="utf-8") as f:
|
| 143 |
+
config = json.load(f)
|
| 144 |
+
else:
|
| 145 |
+
try:
|
| 146 |
+
try:
|
| 147 |
+
# First try parsing as JSON directly
|
| 148 |
+
config = json.loads(config_file_or_dict)
|
| 149 |
+
except json.JSONDecodeError:
|
| 150 |
+
# If that fails, try base64 decoding
|
| 151 |
+
config_decoded = base64.urlsafe_b64decode(config_file_or_dict).decode("utf-8")
|
| 152 |
+
config = json.loads(config_decoded)
|
| 153 |
+
except (UnicodeDecodeError, AttributeError, ValueError):
|
| 154 |
+
raise ValueError(
|
| 155 |
+
f"Expected a string path to an existing deepspeed config, or a dictionary, or a base64 encoded string. Received: {config_file_or_dict}"
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
self.config = config
|
| 159 |
+
|
| 160 |
+
self.set_stage_and_offload()
|
| 161 |
+
|
| 162 |
+
def set_stage_and_offload(self):
|
| 163 |
+
# zero stage - this is done as early as possible, before model is created, to allow
|
| 164 |
+
# ``is_deepspeed_zero3_enabled`` query and getting to the early deepspeed config object
|
| 165 |
+
# during ``zero.Init()`` which needs to know the dtype, and some other hparams.
|
| 166 |
+
self._stage = self.get_value("zero_optimization.stage", -1)
|
| 167 |
+
|
| 168 |
+
# offload
|
| 169 |
+
self._offload = False
|
| 170 |
+
if self.is_zero2() or self.is_zero3():
|
| 171 |
+
offload_devices_valid = set(["cpu", "nvme"])
|
| 172 |
+
offload_devices = set(
|
| 173 |
+
[
|
| 174 |
+
self.get_value("zero_optimization.offload_optimizer.device"),
|
| 175 |
+
self.get_value("zero_optimization.offload_param.device"),
|
| 176 |
+
]
|
| 177 |
+
)
|
| 178 |
+
if len(offload_devices & offload_devices_valid) > 0:
|
| 179 |
+
self._offload = True
|
| 180 |
+
|
| 181 |
+
def find_config_node(self, ds_key_long):
|
| 182 |
+
config = self.config
|
| 183 |
+
|
| 184 |
+
# find the config node of interest if it exists
|
| 185 |
+
nodes = ds_key_long.split(".")
|
| 186 |
+
ds_key = nodes.pop()
|
| 187 |
+
for node in nodes:
|
| 188 |
+
config = config.get(node)
|
| 189 |
+
if config is None:
|
| 190 |
+
return None, ds_key
|
| 191 |
+
|
| 192 |
+
return config, ds_key
|
| 193 |
+
|
| 194 |
+
def get_value(self, ds_key_long, default=None):
|
| 195 |
+
"""
|
| 196 |
+
Returns the set value or `default` if no value is set
|
| 197 |
+
"""
|
| 198 |
+
config, ds_key = self.find_config_node(ds_key_long)
|
| 199 |
+
if config is None:
|
| 200 |
+
return default
|
| 201 |
+
return config.get(ds_key, default)
|
| 202 |
+
|
| 203 |
+
def del_config_sub_tree(self, ds_key_long, must_exist=False):
|
| 204 |
+
"""
|
| 205 |
+
Deletes a sub-section of the config file if it's found.
|
| 206 |
+
|
| 207 |
+
Unless `must_exist` is `True` the section doesn't have to exist.
|
| 208 |
+
"""
|
| 209 |
+
config = self.config
|
| 210 |
+
|
| 211 |
+
# find the config node of interest if it exists
|
| 212 |
+
nodes = ds_key_long.split(".")
|
| 213 |
+
for node in nodes:
|
| 214 |
+
parent_config = config
|
| 215 |
+
config = config.get(node)
|
| 216 |
+
if config is None:
|
| 217 |
+
if must_exist:
|
| 218 |
+
raise ValueError(f"Can't find {ds_key_long} entry in the config: {self.config}")
|
| 219 |
+
else:
|
| 220 |
+
return
|
| 221 |
+
|
| 222 |
+
# if found remove it
|
| 223 |
+
if parent_config is not None:
|
| 224 |
+
parent_config.pop(node)
|
| 225 |
+
|
| 226 |
+
def is_true(self, ds_key_long):
|
| 227 |
+
"""
|
| 228 |
+
Returns `True`/``False` only if the value is set, always `False` otherwise. So use this method to ask the very
|
| 229 |
+
specific question of whether the value is set to `True` (and it's not set to `False`` or isn't set).
|
| 230 |
+
|
| 231 |
+
"""
|
| 232 |
+
value = self.get_value(ds_key_long)
|
| 233 |
+
return False if value is None else bool(value)
|
| 234 |
+
|
| 235 |
+
def is_false(self, ds_key_long):
|
| 236 |
+
"""
|
| 237 |
+
Returns `True`/``False` only if the value is set, always `False` otherwise. So use this method to ask the very
|
| 238 |
+
specific question of whether the value is set to `False` (and it's not set to `True`` or isn't set).
|
| 239 |
+
"""
|
| 240 |
+
value = self.get_value(ds_key_long)
|
| 241 |
+
return False if value is None else not bool(value)
|
| 242 |
+
|
| 243 |
+
def is_zero2(self):
|
| 244 |
+
return self._stage == 2
|
| 245 |
+
|
| 246 |
+
def is_zero3(self):
|
| 247 |
+
return self._stage == 3
|
| 248 |
+
|
| 249 |
+
def is_offload(self):
|
| 250 |
+
return self._offload
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
class DeepSpeedEngineWrapper:
|
| 254 |
+
"""
|
| 255 |
+
Internal wrapper for deepspeed.runtime.engine.DeepSpeedEngine. This is used to follow conventional training loop.
|
| 256 |
+
|
| 257 |
+
Args:
|
| 258 |
+
engine (deepspeed.runtime.engine.DeepSpeedEngine): deepspeed engine to wrap
|
| 259 |
+
"""
|
| 260 |
+
|
| 261 |
+
def __init__(self, engine):
|
| 262 |
+
self.engine = engine
|
| 263 |
+
|
| 264 |
+
def backward(self, loss, **kwargs):
|
| 265 |
+
# runs backpropagation and handles mixed precision
|
| 266 |
+
self.engine.backward(loss, **kwargs)
|
| 267 |
+
|
| 268 |
+
# Deepspeed's `engine.step` performs the following operations:
|
| 269 |
+
# - gradient accumulation check
|
| 270 |
+
# - gradient clipping
|
| 271 |
+
# - optimizer step
|
| 272 |
+
# - zero grad
|
| 273 |
+
# - checking overflow
|
| 274 |
+
# - lr_scheduler step (only if engine.lr_scheduler is not None)
|
| 275 |
+
self.engine.step()
|
| 276 |
+
# and this plugin overrides the above calls with no-ops when Accelerate runs under
|
| 277 |
+
# Deepspeed, but allows normal functionality for non-Deepspeed cases thus enabling a simple
|
| 278 |
+
# training loop that works transparently under many training regimes.
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
class DeepSpeedOptimizerWrapper(AcceleratedOptimizer):
|
| 282 |
+
"""
|
| 283 |
+
Internal wrapper around a deepspeed optimizer.
|
| 284 |
+
|
| 285 |
+
Args:
|
| 286 |
+
optimizer (`torch.optim.optimizer.Optimizer`):
|
| 287 |
+
The optimizer to wrap.
|
| 288 |
+
"""
|
| 289 |
+
|
| 290 |
+
def __init__(self, optimizer):
|
| 291 |
+
super().__init__(optimizer, device_placement=False, scaler=None)
|
| 292 |
+
self.__has_overflow__ = hasattr(self.optimizer, "overflow")
|
| 293 |
+
|
| 294 |
+
def zero_grad(self, set_to_none=None):
|
| 295 |
+
pass # `accelerator.backward(loss)` is doing that automatically. Therefore, its implementation is not needed
|
| 296 |
+
|
| 297 |
+
def step(self):
|
| 298 |
+
pass # `accelerator.backward(loss)` is doing that automatically. Therefore, its implementation is not needed
|
| 299 |
+
|
| 300 |
+
@property
|
| 301 |
+
def step_was_skipped(self):
|
| 302 |
+
"""Whether or not the optimizer step was done, or skipped because of gradient overflow."""
|
| 303 |
+
if self.__has_overflow__:
|
| 304 |
+
return self.optimizer.overflow
|
| 305 |
+
return False
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
class DeepSpeedSchedulerWrapper(AcceleratedScheduler):
|
| 309 |
+
"""
|
| 310 |
+
Internal wrapper around a deepspeed scheduler.
|
| 311 |
+
|
| 312 |
+
Args:
|
| 313 |
+
scheduler (`torch.optim.lr_scheduler.LambdaLR`):
|
| 314 |
+
The scheduler to wrap.
|
| 315 |
+
optimizers (one or a list of `torch.optim.Optimizer`):
|
| 316 |
+
"""
|
| 317 |
+
|
| 318 |
+
def __init__(self, scheduler, optimizers):
|
| 319 |
+
super().__init__(scheduler, optimizers)
|
| 320 |
+
|
| 321 |
+
def step(self):
|
| 322 |
+
pass # `accelerator.backward(loss)` is doing that automatically. Therefore, its implementation is not needed
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
class DummyOptim:
|
| 326 |
+
"""
|
| 327 |
+
Dummy optimizer presents model parameters or param groups, this is primarily used to follow conventional training
|
| 328 |
+
loop when optimizer config is specified in the deepspeed config file.
|
| 329 |
+
|
| 330 |
+
Args:
|
| 331 |
+
lr (float):
|
| 332 |
+
Learning rate.
|
| 333 |
+
params (iterable): iterable of parameters to optimize or dicts defining
|
| 334 |
+
parameter groups
|
| 335 |
+
weight_decay (float):
|
| 336 |
+
Weight decay.
|
| 337 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 338 |
+
Other arguments.
|
| 339 |
+
"""
|
| 340 |
+
|
| 341 |
+
def __init__(self, params, lr=0.001, weight_decay=0, **kwargs):
|
| 342 |
+
self.params = params
|
| 343 |
+
self.lr = lr
|
| 344 |
+
self.weight_decay = weight_decay
|
| 345 |
+
self.kwargs = kwargs
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
class DummyScheduler:
|
| 349 |
+
"""
|
| 350 |
+
Dummy scheduler presents model parameters or param groups, this is primarily used to follow conventional training
|
| 351 |
+
loop when scheduler config is specified in the deepspeed config file.
|
| 352 |
+
|
| 353 |
+
Args:
|
| 354 |
+
optimizer (`torch.optim.optimizer.Optimizer`):
|
| 355 |
+
The optimizer to wrap.
|
| 356 |
+
total_num_steps (int, *optional*):
|
| 357 |
+
Total number of steps.
|
| 358 |
+
warmup_num_steps (int, *optional*):
|
| 359 |
+
Number of steps for warmup.
|
| 360 |
+
lr_scheduler_callable (callable, *optional*):
|
| 361 |
+
A callable function that creates an LR Scheduler. It accepts only one argument `optimizer`.
|
| 362 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 363 |
+
Other arguments.
|
| 364 |
+
"""
|
| 365 |
+
|
| 366 |
+
def __init__(self, optimizer, total_num_steps=None, warmup_num_steps=0, lr_scheduler_callable=None, **kwargs):
|
| 367 |
+
self.optimizer = optimizer
|
| 368 |
+
self.total_num_steps = total_num_steps
|
| 369 |
+
self.warmup_num_steps = warmup_num_steps
|
| 370 |
+
self.lr_scheduler_callable = lr_scheduler_callable
|
| 371 |
+
self.kwargs = kwargs
|
venv/lib/python3.11/site-packages/accelerate/utils/environment.py
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import logging
|
| 16 |
+
import math
|
| 17 |
+
import os
|
| 18 |
+
import platform
|
| 19 |
+
import subprocess
|
| 20 |
+
import sys
|
| 21 |
+
from contextlib import contextmanager
|
| 22 |
+
from dataclasses import dataclass, field
|
| 23 |
+
from functools import lru_cache, wraps
|
| 24 |
+
from shutil import which
|
| 25 |
+
from typing import Optional, Union
|
| 26 |
+
|
| 27 |
+
import torch
|
| 28 |
+
from packaging.version import parse
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
logger = logging.getLogger(__name__)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def convert_dict_to_env_variables(current_env: dict):
|
| 35 |
+
"""
|
| 36 |
+
Verifies that all keys and values in `current_env` do not contain illegal keys or values, and returns a list of
|
| 37 |
+
strings as the result.
|
| 38 |
+
|
| 39 |
+
Example:
|
| 40 |
+
```python
|
| 41 |
+
>>> from accelerate.utils.environment import verify_env
|
| 42 |
+
|
| 43 |
+
>>> env = {"ACCELERATE_DEBUG_MODE": "1", "BAD_ENV_NAME": "<mything", "OTHER_ENV": "2"}
|
| 44 |
+
>>> valid_env_items = verify_env(env)
|
| 45 |
+
>>> print(valid_env_items)
|
| 46 |
+
["ACCELERATE_DEBUG_MODE=1\n", "OTHER_ENV=2\n"]
|
| 47 |
+
```
|
| 48 |
+
"""
|
| 49 |
+
forbidden_chars = [";", "\n", "<", ">", " "]
|
| 50 |
+
valid_env_items = []
|
| 51 |
+
for key, value in current_env.items():
|
| 52 |
+
if all(char not in (key + value) for char in forbidden_chars) and len(key) >= 1 and len(value) >= 1:
|
| 53 |
+
valid_env_items.append(f"{key}={value}\n")
|
| 54 |
+
else:
|
| 55 |
+
logger.warning(f"WARNING: Skipping {key}={value} as it contains forbidden characters or missing values.")
|
| 56 |
+
return valid_env_items
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def str_to_bool(value, to_bool: bool = False) -> Union[int, bool]:
|
| 60 |
+
"""
|
| 61 |
+
Converts a string representation of truth to `True` (1) or `False` (0).
|
| 62 |
+
|
| 63 |
+
True values are `y`, `yes`, `t`, `true`, `on`, and `1`; False value are `n`, `no`, `f`, `false`, `off`, and `0`;
|
| 64 |
+
"""
|
| 65 |
+
value = value.lower()
|
| 66 |
+
if value in ("y", "yes", "t", "true", "on", "1"):
|
| 67 |
+
return 1 if not to_bool else True
|
| 68 |
+
elif value in ("n", "no", "f", "false", "off", "0"):
|
| 69 |
+
return 0 if not to_bool else False
|
| 70 |
+
else:
|
| 71 |
+
raise ValueError(f"invalid truth value {value}")
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def get_int_from_env(env_keys, default):
|
| 75 |
+
"""Returns the first positive env value found in the `env_keys` list or the default."""
|
| 76 |
+
for e in env_keys:
|
| 77 |
+
val = int(os.environ.get(e, -1))
|
| 78 |
+
if val >= 0:
|
| 79 |
+
return val
|
| 80 |
+
return default
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def parse_flag_from_env(key, default=False):
|
| 84 |
+
"""Returns truthy value for `key` from the env if available else the default."""
|
| 85 |
+
value = os.environ.get(key, str(default))
|
| 86 |
+
return str_to_bool(value) == 1 # As its name indicates `str_to_bool` actually returns an int...
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def parse_choice_from_env(key, default="no"):
|
| 90 |
+
value = os.environ.get(key, str(default))
|
| 91 |
+
return value
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def are_libraries_initialized(*library_names: str) -> list[str]:
|
| 95 |
+
"""
|
| 96 |
+
Checks if any of `library_names` are imported in the environment. Will return any names that are.
|
| 97 |
+
"""
|
| 98 |
+
return [lib_name for lib_name in library_names if lib_name in sys.modules.keys()]
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def _nvidia_smi():
|
| 102 |
+
"""
|
| 103 |
+
Returns the right nvidia-smi command based on the system.
|
| 104 |
+
"""
|
| 105 |
+
if platform.system() == "Windows":
|
| 106 |
+
# If platform is Windows and nvidia-smi can't be found in path
|
| 107 |
+
# try from systemd drive with default installation path
|
| 108 |
+
command = which("nvidia-smi")
|
| 109 |
+
if command is None:
|
| 110 |
+
command = f"{os.environ['systemdrive']}\\Program Files\\NVIDIA Corporation\\NVSMI\\nvidia-smi.exe"
|
| 111 |
+
else:
|
| 112 |
+
command = "nvidia-smi"
|
| 113 |
+
return command
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def get_gpu_info():
|
| 117 |
+
"""
|
| 118 |
+
Gets GPU count and names using `nvidia-smi` instead of torch to not initialize CUDA.
|
| 119 |
+
|
| 120 |
+
Largely based on the `gputil` library.
|
| 121 |
+
"""
|
| 122 |
+
# Returns as list of `n` GPUs and their names
|
| 123 |
+
output = subprocess.check_output(
|
| 124 |
+
[_nvidia_smi(), "--query-gpu=count,name", "--format=csv,noheader"], universal_newlines=True
|
| 125 |
+
)
|
| 126 |
+
output = output.strip()
|
| 127 |
+
gpus = output.split(os.linesep)
|
| 128 |
+
# Get names from output
|
| 129 |
+
gpu_count = len(gpus)
|
| 130 |
+
gpu_names = [gpu.split(",")[1].strip() for gpu in gpus]
|
| 131 |
+
return gpu_names, gpu_count
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def get_driver_version():
|
| 135 |
+
"""
|
| 136 |
+
Returns the driver version
|
| 137 |
+
|
| 138 |
+
In the case of multiple GPUs, will return the first.
|
| 139 |
+
"""
|
| 140 |
+
output = subprocess.check_output(
|
| 141 |
+
[_nvidia_smi(), "--query-gpu=driver_version", "--format=csv,noheader"], universal_newlines=True
|
| 142 |
+
)
|
| 143 |
+
output = output.strip()
|
| 144 |
+
return output.split(os.linesep)[0]
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def check_cuda_p2p_ib_support():
|
| 148 |
+
"""
|
| 149 |
+
Checks if the devices being used have issues with P2P and IB communications, namely any consumer GPU hardware after
|
| 150 |
+
the 3090.
|
| 151 |
+
|
| 152 |
+
Noteably uses `nvidia-smi` instead of torch to not initialize CUDA.
|
| 153 |
+
"""
|
| 154 |
+
try:
|
| 155 |
+
device_names, device_count = get_gpu_info()
|
| 156 |
+
# As new consumer GPUs get released, add them to `unsupported_devices``
|
| 157 |
+
unsupported_devices = {"RTX 40"}
|
| 158 |
+
if device_count > 1:
|
| 159 |
+
if any(
|
| 160 |
+
unsupported_device in device_name
|
| 161 |
+
for device_name in device_names
|
| 162 |
+
for unsupported_device in unsupported_devices
|
| 163 |
+
):
|
| 164 |
+
# Check if they have the right driver version
|
| 165 |
+
acceptable_driver_version = "550.40.07"
|
| 166 |
+
current_driver_version = get_driver_version()
|
| 167 |
+
if parse(current_driver_version) < parse(acceptable_driver_version):
|
| 168 |
+
return False
|
| 169 |
+
return True
|
| 170 |
+
except Exception:
|
| 171 |
+
pass
|
| 172 |
+
return True
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
@lru_cache
|
| 176 |
+
def check_cuda_fp8_capability():
|
| 177 |
+
"""
|
| 178 |
+
Checks if the current GPU available supports FP8.
|
| 179 |
+
|
| 180 |
+
Notably might initialize `torch.cuda` to check.
|
| 181 |
+
"""
|
| 182 |
+
|
| 183 |
+
try:
|
| 184 |
+
# try to get the compute capability from nvidia-smi
|
| 185 |
+
output = subprocess.check_output(
|
| 186 |
+
[_nvidia_smi(), "--query-gpu=compute_capability", "--format=csv,noheader"], universal_newlines=True
|
| 187 |
+
)
|
| 188 |
+
output = output.strip()
|
| 189 |
+
# we take the first GPU's compute capability
|
| 190 |
+
compute_capability = tuple(map(int, output.split(os.linesep)[0].split(".")))
|
| 191 |
+
except Exception:
|
| 192 |
+
compute_capability = torch.cuda.get_device_capability()
|
| 193 |
+
|
| 194 |
+
return compute_capability >= (8, 9)
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
@dataclass
|
| 198 |
+
class CPUInformation:
|
| 199 |
+
"""
|
| 200 |
+
Stores information about the CPU in a distributed environment. It contains the following attributes:
|
| 201 |
+
- rank: The rank of the current process.
|
| 202 |
+
- world_size: The total number of processes in the world.
|
| 203 |
+
- local_rank: The rank of the current process on the local node.
|
| 204 |
+
- local_world_size: The total number of processes on the local node.
|
| 205 |
+
"""
|
| 206 |
+
|
| 207 |
+
rank: int = field(default=0, metadata={"help": "The rank of the current process."})
|
| 208 |
+
world_size: int = field(default=1, metadata={"help": "The total number of processes in the world."})
|
| 209 |
+
local_rank: int = field(default=0, metadata={"help": "The rank of the current process on the local node."})
|
| 210 |
+
local_world_size: int = field(default=1, metadata={"help": "The total number of processes on the local node."})
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def get_cpu_distributed_information() -> CPUInformation:
|
| 214 |
+
"""
|
| 215 |
+
Returns various information about the environment in relation to CPU distributed training as a `CPUInformation`
|
| 216 |
+
dataclass.
|
| 217 |
+
"""
|
| 218 |
+
information = {}
|
| 219 |
+
information["rank"] = get_int_from_env(["RANK", "PMI_RANK", "OMPI_COMM_WORLD_RANK", "MV2_COMM_WORLD_RANK"], 0)
|
| 220 |
+
information["world_size"] = get_int_from_env(
|
| 221 |
+
["WORLD_SIZE", "PMI_SIZE", "OMPI_COMM_WORLD_SIZE", "MV2_COMM_WORLD_SIZE"], 1
|
| 222 |
+
)
|
| 223 |
+
information["local_rank"] = get_int_from_env(
|
| 224 |
+
["LOCAL_RANK", "MPI_LOCALRANKID", "OMPI_COMM_WORLD_LOCAL_RANK", "MV2_COMM_WORLD_LOCAL_RANK"], 0
|
| 225 |
+
)
|
| 226 |
+
information["local_world_size"] = get_int_from_env(
|
| 227 |
+
["LOCAL_WORLD_SIZE", "MPI_LOCALNRANKS", "OMPI_COMM_WORLD_LOCAL_SIZE", "MV2_COMM_WORLD_LOCAL_SIZE"],
|
| 228 |
+
1,
|
| 229 |
+
)
|
| 230 |
+
return CPUInformation(**information)
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def override_numa_affinity(local_process_index: int, verbose: Optional[bool] = None) -> None:
|
| 234 |
+
"""
|
| 235 |
+
Overrides whatever NUMA affinity is set for the current process. This is very taxing and requires recalculating the
|
| 236 |
+
affinity to set, ideally you should use `utils.environment.set_numa_affinity` instead.
|
| 237 |
+
|
| 238 |
+
Args:
|
| 239 |
+
local_process_index (int):
|
| 240 |
+
The index of the current process on the current server.
|
| 241 |
+
verbose (bool, *optional*):
|
| 242 |
+
Whether to log out the assignment of each CPU. If `ACCELERATE_DEBUG_MODE` is enabled, will default to True.
|
| 243 |
+
"""
|
| 244 |
+
if verbose is None:
|
| 245 |
+
verbose = parse_flag_from_env("ACCELERATE_DEBUG_MODE", False)
|
| 246 |
+
if torch.cuda.is_available():
|
| 247 |
+
from accelerate.utils import is_pynvml_available
|
| 248 |
+
|
| 249 |
+
if not is_pynvml_available():
|
| 250 |
+
raise ImportError(
|
| 251 |
+
"To set CPU affinity on CUDA GPUs the `pynvml` package must be available. (`pip install pynvml`)"
|
| 252 |
+
)
|
| 253 |
+
import pynvml as nvml
|
| 254 |
+
|
| 255 |
+
# The below code is based on https://github.com/NVIDIA/DeepLearningExamples/blob/master/TensorFlow2/LanguageModeling/BERT/gpu_affinity.py
|
| 256 |
+
nvml.nvmlInit()
|
| 257 |
+
num_elements = math.ceil(os.cpu_count() / 64)
|
| 258 |
+
handle = nvml.nvmlDeviceGetHandleByIndex(local_process_index)
|
| 259 |
+
affinity_string = ""
|
| 260 |
+
for j in nvml.nvmlDeviceGetCpuAffinity(handle, num_elements):
|
| 261 |
+
# assume nvml returns list of 64 bit ints
|
| 262 |
+
affinity_string = f"{j:064b}{affinity_string}"
|
| 263 |
+
affinity_list = [int(x) for x in affinity_string]
|
| 264 |
+
affinity_list.reverse() # so core 0 is the 0th element
|
| 265 |
+
affinity_to_set = [i for i, e in enumerate(affinity_list) if e != 0]
|
| 266 |
+
os.sched_setaffinity(0, affinity_to_set)
|
| 267 |
+
if verbose:
|
| 268 |
+
cpu_cores = os.sched_getaffinity(0)
|
| 269 |
+
logger.info(f"Assigning {len(cpu_cores)} cpu cores to process {local_process_index}: {cpu_cores}")
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
@lru_cache
|
| 273 |
+
def set_numa_affinity(local_process_index: int, verbose: Optional[bool] = None) -> None:
|
| 274 |
+
"""
|
| 275 |
+
Assigns the current process to a specific NUMA node. Ideally most efficient when having at least 2 cpus per node.
|
| 276 |
+
|
| 277 |
+
This result is cached between calls. If you want to override it, please use
|
| 278 |
+
`accelerate.utils.environment.override_numa_afifnity`.
|
| 279 |
+
|
| 280 |
+
Args:
|
| 281 |
+
local_process_index (int):
|
| 282 |
+
The index of the current process on the current server.
|
| 283 |
+
verbose (bool, *optional*):
|
| 284 |
+
Whether to print the new cpu cores assignment for each process. If `ACCELERATE_DEBUG_MODE` is enabled, will
|
| 285 |
+
default to True.
|
| 286 |
+
"""
|
| 287 |
+
override_numa_affinity(local_process_index=local_process_index, verbose=verbose)
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
@contextmanager
|
| 291 |
+
def clear_environment():
|
| 292 |
+
"""
|
| 293 |
+
A context manager that will temporarily clear environment variables.
|
| 294 |
+
|
| 295 |
+
When this context exits, the previous environment variables will be back.
|
| 296 |
+
|
| 297 |
+
Example:
|
| 298 |
+
|
| 299 |
+
```python
|
| 300 |
+
>>> import os
|
| 301 |
+
>>> from accelerate.utils import clear_environment
|
| 302 |
+
|
| 303 |
+
>>> os.environ["FOO"] = "bar"
|
| 304 |
+
>>> with clear_environment():
|
| 305 |
+
... print(os.environ)
|
| 306 |
+
... os.environ["FOO"] = "new_bar"
|
| 307 |
+
... print(os.environ["FOO"])
|
| 308 |
+
{}
|
| 309 |
+
new_bar
|
| 310 |
+
|
| 311 |
+
>>> print(os.environ["FOO"])
|
| 312 |
+
bar
|
| 313 |
+
```
|
| 314 |
+
"""
|
| 315 |
+
_old_os_environ = os.environ.copy()
|
| 316 |
+
os.environ.clear()
|
| 317 |
+
|
| 318 |
+
try:
|
| 319 |
+
yield
|
| 320 |
+
finally:
|
| 321 |
+
os.environ.clear() # clear any added keys,
|
| 322 |
+
os.environ.update(_old_os_environ) # then restore previous environment
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
@contextmanager
|
| 326 |
+
def patch_environment(**kwargs):
|
| 327 |
+
"""
|
| 328 |
+
A context manager that will add each keyword argument passed to `os.environ` and remove them when exiting.
|
| 329 |
+
|
| 330 |
+
Will convert the values in `kwargs` to strings and upper-case all the keys.
|
| 331 |
+
|
| 332 |
+
Example:
|
| 333 |
+
|
| 334 |
+
```python
|
| 335 |
+
>>> import os
|
| 336 |
+
>>> from accelerate.utils import patch_environment
|
| 337 |
+
|
| 338 |
+
>>> with patch_environment(FOO="bar"):
|
| 339 |
+
... print(os.environ["FOO"]) # prints "bar"
|
| 340 |
+
>>> print(os.environ["FOO"]) # raises KeyError
|
| 341 |
+
```
|
| 342 |
+
"""
|
| 343 |
+
existing_vars = {}
|
| 344 |
+
for key, value in kwargs.items():
|
| 345 |
+
key = key.upper()
|
| 346 |
+
if key in os.environ:
|
| 347 |
+
existing_vars[key] = os.environ[key]
|
| 348 |
+
os.environ[key] = str(value)
|
| 349 |
+
|
| 350 |
+
try:
|
| 351 |
+
yield
|
| 352 |
+
finally:
|
| 353 |
+
for key in kwargs:
|
| 354 |
+
key = key.upper()
|
| 355 |
+
if key in existing_vars:
|
| 356 |
+
# restore previous value
|
| 357 |
+
os.environ[key] = existing_vars[key]
|
| 358 |
+
else:
|
| 359 |
+
os.environ.pop(key, None)
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
def purge_accelerate_environment(func_or_cls):
|
| 363 |
+
"""Decorator to clean up accelerate environment variables set by the decorated class or function.
|
| 364 |
+
|
| 365 |
+
In some circumstances, calling certain classes or functions can result in accelerate env vars being set and not
|
| 366 |
+
being cleaned up afterwards. As an example, when calling:
|
| 367 |
+
|
| 368 |
+
TrainingArguments(fp16=True, ...)
|
| 369 |
+
|
| 370 |
+
The following env var will be set:
|
| 371 |
+
|
| 372 |
+
ACCELERATE_MIXED_PRECISION=fp16
|
| 373 |
+
|
| 374 |
+
This can affect subsequent code, since the env var takes precedence over TrainingArguments(fp16=False). This is
|
| 375 |
+
especially relevant for unit testing, where we want to avoid the individual tests to have side effects on one
|
| 376 |
+
another. Decorate the unit test function or whole class with this decorator to ensure that after each test, the env
|
| 377 |
+
vars are cleaned up. This works for both unittest.TestCase and normal classes (pytest); it also works when
|
| 378 |
+
decorating the parent class.
|
| 379 |
+
|
| 380 |
+
"""
|
| 381 |
+
prefix = "ACCELERATE_"
|
| 382 |
+
|
| 383 |
+
@contextmanager
|
| 384 |
+
def env_var_context():
|
| 385 |
+
# Store existing accelerate env vars
|
| 386 |
+
existing_vars = {k: v for k, v in os.environ.items() if k.startswith(prefix)}
|
| 387 |
+
try:
|
| 388 |
+
yield
|
| 389 |
+
finally:
|
| 390 |
+
# Restore original env vars or remove new ones
|
| 391 |
+
for key in [k for k in os.environ if k.startswith(prefix)]:
|
| 392 |
+
if key in existing_vars:
|
| 393 |
+
os.environ[key] = existing_vars[key]
|
| 394 |
+
else:
|
| 395 |
+
os.environ.pop(key, None)
|
| 396 |
+
|
| 397 |
+
def wrap_function(func):
|
| 398 |
+
@wraps(func)
|
| 399 |
+
def wrapper(*args, **kwargs):
|
| 400 |
+
with env_var_context():
|
| 401 |
+
return func(*args, **kwargs)
|
| 402 |
+
|
| 403 |
+
wrapper._accelerate_is_purged_environment_wrapped = True
|
| 404 |
+
return wrapper
|
| 405 |
+
|
| 406 |
+
if not isinstance(func_or_cls, type):
|
| 407 |
+
return wrap_function(func_or_cls)
|
| 408 |
+
|
| 409 |
+
# Handle classes by wrapping test methods
|
| 410 |
+
def wrap_test_methods(test_class_instance):
|
| 411 |
+
for name in dir(test_class_instance):
|
| 412 |
+
if name.startswith("test"):
|
| 413 |
+
method = getattr(test_class_instance, name)
|
| 414 |
+
if callable(method) and not hasattr(method, "_accelerate_is_purged_environment_wrapped"):
|
| 415 |
+
setattr(test_class_instance, name, wrap_function(method))
|
| 416 |
+
return test_class_instance
|
| 417 |
+
|
| 418 |
+
# Handle inheritance
|
| 419 |
+
wrap_test_methods(func_or_cls)
|
| 420 |
+
func_or_cls.__init_subclass__ = classmethod(lambda cls, **kw: wrap_test_methods(cls))
|
| 421 |
+
return func_or_cls
|
venv/lib/python3.11/site-packages/accelerate/utils/fsdp_utils.py
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
import functools
|
| 15 |
+
import os
|
| 16 |
+
import shutil
|
| 17 |
+
import warnings
|
| 18 |
+
from collections import defaultdict
|
| 19 |
+
from contextlib import nullcontext
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
from typing import Callable
|
| 22 |
+
|
| 23 |
+
import torch
|
| 24 |
+
|
| 25 |
+
from ..logging import get_logger
|
| 26 |
+
from .constants import FSDP_MODEL_NAME, OPTIMIZER_NAME, SAFE_WEIGHTS_NAME, WEIGHTS_NAME
|
| 27 |
+
from .dataclasses import get_module_class_from_name
|
| 28 |
+
from .modeling import is_peft_model
|
| 29 |
+
from .other import get_module_children_bottom_up, is_compiled_module, save
|
| 30 |
+
from .versions import is_torch_version
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
logger = get_logger(__name__)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def enable_fsdp_ram_efficient_loading():
|
| 37 |
+
"""
|
| 38 |
+
Enables RAM efficient loading of Hugging Face models for FSDP in the environment.
|
| 39 |
+
"""
|
| 40 |
+
# Sets values for `transformers.modeling_utils.is_fsdp_enabled`
|
| 41 |
+
if "ACCELERATE_USE_FSDP" not in os.environ:
|
| 42 |
+
os.environ["ACCELERATE_USE_FSDP"] = "True"
|
| 43 |
+
os.environ["FSDP_CPU_RAM_EFFICIENT_LOADING"] = "True"
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def disable_fsdp_ram_efficient_loading():
|
| 47 |
+
"""
|
| 48 |
+
Disables RAM efficient loading of Hugging Face models for FSDP in the environment.
|
| 49 |
+
"""
|
| 50 |
+
os.environ["FSDP_CPU_RAM_EFFICIENT_LOADING"] = "False"
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def _get_model_state_dict(model, adapter_only=False):
|
| 54 |
+
if adapter_only and is_peft_model(model):
|
| 55 |
+
from peft import get_peft_model_state_dict
|
| 56 |
+
|
| 57 |
+
return get_peft_model_state_dict(model, adapter_name=model.active_adapter)
|
| 58 |
+
else:
|
| 59 |
+
return model.state_dict()
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def _set_model_state_dict(model, state_dict, adapter_only=False):
|
| 63 |
+
if adapter_only and is_peft_model(model):
|
| 64 |
+
from peft import set_peft_model_state_dict
|
| 65 |
+
|
| 66 |
+
return set_peft_model_state_dict(model, state_dict, adapter_name=model.active_adapter)
|
| 67 |
+
else:
|
| 68 |
+
return model.load_state_dict(state_dict)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def save_fsdp_model(fsdp_plugin, accelerator, model, output_dir, model_index=0, adapter_only=False):
|
| 72 |
+
# Note: We import here to reduce import time from general modules, and isolate outside dependencies
|
| 73 |
+
import torch.distributed.checkpoint as dist_cp
|
| 74 |
+
from torch.distributed.checkpoint.default_planner import DefaultSavePlanner
|
| 75 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import FullyShardedDataParallel as FSDP
|
| 76 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import StateDictType
|
| 77 |
+
|
| 78 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 79 |
+
if fsdp_plugin.state_dict_type == StateDictType.FULL_STATE_DICT and fsdp_plugin.fsdp_version == 1:
|
| 80 |
+
# FSDP raises error when single GPU is used with `offload_to_cpu=True` for FULL_STATE_DICT
|
| 81 |
+
# so, only enable it when num_processes>1
|
| 82 |
+
is_multi_process = accelerator.num_processes > 1
|
| 83 |
+
fsdp_plugin.state_dict_config.offload_to_cpu = is_multi_process
|
| 84 |
+
fsdp_plugin.state_dict_config.rank0_only = is_multi_process
|
| 85 |
+
|
| 86 |
+
ctx = (
|
| 87 |
+
FSDP.state_dict_type(
|
| 88 |
+
model, fsdp_plugin.state_dict_type, fsdp_plugin.state_dict_config, fsdp_plugin.optim_state_dict_config
|
| 89 |
+
)
|
| 90 |
+
if fsdp_plugin.fsdp_version == 1
|
| 91 |
+
else nullcontext()
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
with ctx:
|
| 95 |
+
state_dict = _get_model_state_dict(model, adapter_only=adapter_only)
|
| 96 |
+
if fsdp_plugin.state_dict_type == StateDictType.FULL_STATE_DICT:
|
| 97 |
+
weights_name = f"{FSDP_MODEL_NAME}.bin" if model_index == 0 else f"{FSDP_MODEL_NAME}_{model_index}.bin"
|
| 98 |
+
output_model_file = os.path.join(output_dir, weights_name)
|
| 99 |
+
if accelerator.process_index == 0:
|
| 100 |
+
logger.info(f"Saving model to {output_model_file}")
|
| 101 |
+
torch.save(state_dict, output_model_file)
|
| 102 |
+
logger.info(f"Model saved to {output_model_file}")
|
| 103 |
+
# Invariant: `LOCAL_STATE_DICT` is never possible with `FSDP2`
|
| 104 |
+
elif fsdp_plugin.state_dict_type == StateDictType.LOCAL_STATE_DICT:
|
| 105 |
+
weights_name = (
|
| 106 |
+
f"{FSDP_MODEL_NAME}_rank{accelerator.process_index}.bin"
|
| 107 |
+
if model_index == 0
|
| 108 |
+
else f"{FSDP_MODEL_NAME}_{model_index}_rank{accelerator.process_index}.bin"
|
| 109 |
+
)
|
| 110 |
+
output_model_file = os.path.join(output_dir, weights_name)
|
| 111 |
+
logger.info(f"Saving model to {output_model_file}")
|
| 112 |
+
torch.save(state_dict, output_model_file)
|
| 113 |
+
logger.info(f"Model saved to {output_model_file}")
|
| 114 |
+
elif fsdp_plugin.state_dict_type == StateDictType.SHARDED_STATE_DICT:
|
| 115 |
+
ckpt_dir = os.path.join(output_dir, f"{FSDP_MODEL_NAME}_{model_index}")
|
| 116 |
+
os.makedirs(ckpt_dir, exist_ok=True)
|
| 117 |
+
logger.info(f"Saving model to {ckpt_dir}")
|
| 118 |
+
state_dict = {"model": state_dict}
|
| 119 |
+
|
| 120 |
+
dist_cp.save_state_dict(
|
| 121 |
+
state_dict=state_dict,
|
| 122 |
+
storage_writer=dist_cp.FileSystemWriter(ckpt_dir),
|
| 123 |
+
planner=DefaultSavePlanner(),
|
| 124 |
+
)
|
| 125 |
+
logger.info(f"Model saved to {ckpt_dir}")
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def load_fsdp_model(fsdp_plugin, accelerator, model, input_dir, model_index=0, adapter_only=False):
|
| 129 |
+
# Note: We import here to reduce import time from general modules, and isolate outside dependencies
|
| 130 |
+
import torch.distributed.checkpoint as dist_cp
|
| 131 |
+
from torch.distributed.checkpoint.default_planner import DefaultLoadPlanner
|
| 132 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import FullyShardedDataParallel as FSDP
|
| 133 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import StateDictType
|
| 134 |
+
|
| 135 |
+
accelerator.wait_for_everyone()
|
| 136 |
+
if fsdp_plugin.state_dict_type == StateDictType.FULL_STATE_DICT and fsdp_plugin.fsdp_version == 1:
|
| 137 |
+
# FSDP raises error when single GPU is used with `offload_to_cpu=True` for FULL_STATE_DICT
|
| 138 |
+
# so, only enable it when num_processes>1
|
| 139 |
+
is_multi_process = accelerator.num_processes > 1
|
| 140 |
+
fsdp_plugin.state_dict_config.offload_to_cpu = is_multi_process
|
| 141 |
+
fsdp_plugin.state_dict_config.rank0_only = is_multi_process
|
| 142 |
+
|
| 143 |
+
ctx = (
|
| 144 |
+
FSDP.state_dict_type(
|
| 145 |
+
model, fsdp_plugin.state_dict_type, fsdp_plugin.state_dict_config, fsdp_plugin.optim_state_dict_config
|
| 146 |
+
)
|
| 147 |
+
if fsdp_plugin.fsdp_version == 1
|
| 148 |
+
else nullcontext()
|
| 149 |
+
)
|
| 150 |
+
|
| 151 |
+
with ctx:
|
| 152 |
+
if fsdp_plugin.state_dict_type == StateDictType.FULL_STATE_DICT:
|
| 153 |
+
if type(model) is not FSDP and accelerator.process_index != 0:
|
| 154 |
+
if not fsdp_plugin.sync_module_states and fsdp_plugin.fsdp_version == 1:
|
| 155 |
+
raise ValueError(
|
| 156 |
+
"Set the `sync_module_states` flag to `True` so that model states are synced across processes when "
|
| 157 |
+
"initializing FSDP object"
|
| 158 |
+
)
|
| 159 |
+
return
|
| 160 |
+
weights_name = f"{FSDP_MODEL_NAME}.bin" if model_index == 0 else f"{FSDP_MODEL_NAME}_{model_index}.bin"
|
| 161 |
+
input_model_file = os.path.join(input_dir, weights_name)
|
| 162 |
+
logger.info(f"Loading model from {input_model_file}")
|
| 163 |
+
state_dict = torch.load(input_model_file)
|
| 164 |
+
logger.info(f"Model loaded from {input_model_file}")
|
| 165 |
+
elif fsdp_plugin.state_dict_type == StateDictType.LOCAL_STATE_DICT:
|
| 166 |
+
weights_name = (
|
| 167 |
+
f"{FSDP_MODEL_NAME}_rank{accelerator.process_index}.bin"
|
| 168 |
+
if model_index == 0
|
| 169 |
+
else f"{FSDP_MODEL_NAME}_{model_index}_rank{accelerator.process_index}.bin"
|
| 170 |
+
)
|
| 171 |
+
input_model_file = os.path.join(input_dir, weights_name)
|
| 172 |
+
logger.info(f"Loading model from {input_model_file}")
|
| 173 |
+
state_dict = torch.load(input_model_file)
|
| 174 |
+
logger.info(f"Model loaded from {input_model_file}")
|
| 175 |
+
elif fsdp_plugin.state_dict_type == StateDictType.SHARDED_STATE_DICT:
|
| 176 |
+
ckpt_dir = (
|
| 177 |
+
os.path.join(input_dir, f"{FSDP_MODEL_NAME}_{model_index}")
|
| 178 |
+
if f"{FSDP_MODEL_NAME}" not in input_dir
|
| 179 |
+
else input_dir
|
| 180 |
+
)
|
| 181 |
+
logger.info(f"Loading model from {ckpt_dir}")
|
| 182 |
+
state_dict = {"model": _get_model_state_dict(model, adapter_only=adapter_only)}
|
| 183 |
+
dist_cp.load_state_dict(
|
| 184 |
+
state_dict=state_dict,
|
| 185 |
+
storage_reader=dist_cp.FileSystemReader(ckpt_dir),
|
| 186 |
+
planner=DefaultLoadPlanner(),
|
| 187 |
+
)
|
| 188 |
+
state_dict = state_dict["model"]
|
| 189 |
+
logger.info(f"Model loaded from {ckpt_dir}")
|
| 190 |
+
|
| 191 |
+
if fsdp_plugin.fsdp_version == 1:
|
| 192 |
+
load_result = _set_model_state_dict(model, state_dict, adapter_only=adapter_only)
|
| 193 |
+
else:
|
| 194 |
+
from torch.distributed.checkpoint.state_dict import set_model_state_dict
|
| 195 |
+
|
| 196 |
+
load_result = set_model_state_dict(model, state_dict)
|
| 197 |
+
return load_result
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def save_fsdp_optimizer(fsdp_plugin, accelerator, optimizer, model, output_dir, optimizer_index=0):
|
| 201 |
+
# Note: We import here to reduce import time from general modules, and isolate outside dependencies
|
| 202 |
+
import torch.distributed.checkpoint as dist_cp
|
| 203 |
+
from torch.distributed.checkpoint.default_planner import DefaultSavePlanner
|
| 204 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import FullyShardedDataParallel as FSDP
|
| 205 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import StateDictType
|
| 206 |
+
|
| 207 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 208 |
+
|
| 209 |
+
ctx = (
|
| 210 |
+
FSDP.state_dict_type(
|
| 211 |
+
model, fsdp_plugin.state_dict_type, fsdp_plugin.state_dict_config, fsdp_plugin.optim_state_dict_config
|
| 212 |
+
)
|
| 213 |
+
if fsdp_plugin.fsdp_version == 1
|
| 214 |
+
else nullcontext()
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
+
with ctx:
|
| 218 |
+
if fsdp_plugin.fsdp_version == 1:
|
| 219 |
+
optim_state = FSDP.optim_state_dict(model, optimizer)
|
| 220 |
+
else:
|
| 221 |
+
optim_state = optimizer.state_dict()
|
| 222 |
+
|
| 223 |
+
if fsdp_plugin.state_dict_type == StateDictType.FULL_STATE_DICT:
|
| 224 |
+
if accelerator.process_index == 0:
|
| 225 |
+
optim_state_name = (
|
| 226 |
+
f"{OPTIMIZER_NAME}.bin" if optimizer_index == 0 else f"{OPTIMIZER_NAME}_{optimizer_index}.bin"
|
| 227 |
+
)
|
| 228 |
+
output_optimizer_file = os.path.join(output_dir, optim_state_name)
|
| 229 |
+
logger.info(f"Saving Optimizer state to {output_optimizer_file}")
|
| 230 |
+
torch.save(optim_state, output_optimizer_file)
|
| 231 |
+
logger.info(f"Optimizer state saved in {output_optimizer_file}")
|
| 232 |
+
else:
|
| 233 |
+
ckpt_dir = os.path.join(output_dir, f"{OPTIMIZER_NAME}_{optimizer_index}")
|
| 234 |
+
os.makedirs(ckpt_dir, exist_ok=True)
|
| 235 |
+
logger.info(f"Saving Optimizer state to {ckpt_dir}")
|
| 236 |
+
dist_cp.save_state_dict(
|
| 237 |
+
state_dict={"optimizer": optim_state},
|
| 238 |
+
storage_writer=dist_cp.FileSystemWriter(ckpt_dir),
|
| 239 |
+
planner=DefaultSavePlanner(),
|
| 240 |
+
)
|
| 241 |
+
logger.info(f"Optimizer state saved in {ckpt_dir}")
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
def load_fsdp_optimizer(fsdp_plugin, accelerator, optimizer, model, input_dir, optimizer_index=0, adapter_only=False):
|
| 245 |
+
# Note: We import here to reduce import time from general modules, and isolate outside dependencies
|
| 246 |
+
import torch.distributed.checkpoint as dist_cp
|
| 247 |
+
from torch.distributed.checkpoint.optimizer import load_sharded_optimizer_state_dict
|
| 248 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import FullyShardedDataParallel as FSDP
|
| 249 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import StateDictType
|
| 250 |
+
|
| 251 |
+
accelerator.wait_for_everyone()
|
| 252 |
+
ctx = (
|
| 253 |
+
FSDP.state_dict_type(
|
| 254 |
+
model, fsdp_plugin.state_dict_type, fsdp_plugin.state_dict_config, fsdp_plugin.optim_state_dict_config
|
| 255 |
+
)
|
| 256 |
+
if fsdp_plugin.fsdp_version == 1
|
| 257 |
+
else nullcontext()
|
| 258 |
+
)
|
| 259 |
+
with ctx:
|
| 260 |
+
if fsdp_plugin.state_dict_type == StateDictType.FULL_STATE_DICT:
|
| 261 |
+
optim_state = None
|
| 262 |
+
if accelerator.process_index == 0 or not fsdp_plugin.optim_state_dict_config.rank0_only:
|
| 263 |
+
optimizer_name = (
|
| 264 |
+
f"{OPTIMIZER_NAME}.bin" if optimizer_index == 0 else f"{OPTIMIZER_NAME}_{optimizer_index}.bin"
|
| 265 |
+
)
|
| 266 |
+
input_optimizer_file = os.path.join(input_dir, optimizer_name)
|
| 267 |
+
logger.info(f"Loading Optimizer state from {input_optimizer_file}")
|
| 268 |
+
optim_state = torch.load(input_optimizer_file)
|
| 269 |
+
logger.info(f"Optimizer state loaded from {input_optimizer_file}")
|
| 270 |
+
else:
|
| 271 |
+
ckpt_dir = (
|
| 272 |
+
os.path.join(input_dir, f"{OPTIMIZER_NAME}_{optimizer_index}")
|
| 273 |
+
if f"{OPTIMIZER_NAME}" not in input_dir
|
| 274 |
+
else input_dir
|
| 275 |
+
)
|
| 276 |
+
logger.info(f"Loading Optimizer from {ckpt_dir}")
|
| 277 |
+
if fsdp_plugin.fsdp_version == 1:
|
| 278 |
+
optim_state = load_sharded_optimizer_state_dict(
|
| 279 |
+
model_state_dict=_get_model_state_dict(model, adapter_only=adapter_only),
|
| 280 |
+
optimizer_key="optimizer",
|
| 281 |
+
storage_reader=dist_cp.FileSystemReader(ckpt_dir),
|
| 282 |
+
)
|
| 283 |
+
else:
|
| 284 |
+
optim_state = {"optimizer": optimizer.state_dict()}
|
| 285 |
+
dist_cp.load(
|
| 286 |
+
optim_state,
|
| 287 |
+
checkpoint_id=ckpt_dir,
|
| 288 |
+
storage_reader=dist_cp.FileSystemReader(ckpt_dir),
|
| 289 |
+
)
|
| 290 |
+
optim_state = optim_state["optimizer"]
|
| 291 |
+
logger.info(f"Optimizer loaded from {ckpt_dir}")
|
| 292 |
+
if fsdp_plugin.fsdp_version == 1:
|
| 293 |
+
flattened_osd = FSDP.optim_state_dict_to_load(model=model, optim=optimizer, optim_state_dict=optim_state)
|
| 294 |
+
optimizer.load_state_dict(flattened_osd)
|
| 295 |
+
else:
|
| 296 |
+
# we can't do `set_state_dict` here because it does a step of the optimizer which breaks with grad-scaler
|
| 297 |
+
# TODO(siro1): investigate
|
| 298 |
+
optimizer.load_state_dict(optim_state)
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def _distributed_checkpoint_to_merged_weights(checkpoint_dir: str, save_path: str, safe_serialization: bool = True):
|
| 302 |
+
"""
|
| 303 |
+
Passthrough to `torch.distributed.checkpoint.format_utils.dcp_to_torch_save`
|
| 304 |
+
|
| 305 |
+
Will save under `save_path` as either `model.safetensors` or `pytorch_model.bin`.
|
| 306 |
+
"""
|
| 307 |
+
# Note: We import here to reduce import time from general modules, and isolate outside dependencies
|
| 308 |
+
import torch.distributed.checkpoint as dist_cp
|
| 309 |
+
import torch.distributed.checkpoint.format_utils as dist_cp_format_utils
|
| 310 |
+
|
| 311 |
+
state_dict = {}
|
| 312 |
+
save_path = Path(save_path)
|
| 313 |
+
save_path.mkdir(exist_ok=True)
|
| 314 |
+
dist_cp_format_utils._load_state_dict(
|
| 315 |
+
state_dict,
|
| 316 |
+
storage_reader=dist_cp.FileSystemReader(checkpoint_dir),
|
| 317 |
+
planner=dist_cp_format_utils._EmptyStateDictLoadPlanner(),
|
| 318 |
+
no_dist=True,
|
| 319 |
+
)
|
| 320 |
+
save_path = save_path / SAFE_WEIGHTS_NAME if safe_serialization else save_path / WEIGHTS_NAME
|
| 321 |
+
|
| 322 |
+
# To handle if state is a dict like {model: {...}}
|
| 323 |
+
if len(state_dict.keys()) == 1:
|
| 324 |
+
state_dict = state_dict[list(state_dict)[0]]
|
| 325 |
+
save(state_dict, save_path, safe_serialization=safe_serialization)
|
| 326 |
+
return save_path
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def merge_fsdp_weights(
|
| 330 |
+
checkpoint_dir: str, output_path: str, safe_serialization: bool = True, remove_checkpoint_dir: bool = False
|
| 331 |
+
):
|
| 332 |
+
"""
|
| 333 |
+
Merge the weights from sharded FSDP model checkpoints into a single combined checkpoint. Should be used if
|
| 334 |
+
`SHARDED_STATE_DICT` was used for the model. Weights will be saved to `{output_path}/model.safetensors` if
|
| 335 |
+
`safe_serialization` else `pytorch_model.bin`.
|
| 336 |
+
|
| 337 |
+
Note: this is a CPU-bound process.
|
| 338 |
+
|
| 339 |
+
Args:
|
| 340 |
+
checkpoint_dir (`str`):
|
| 341 |
+
The directory containing the FSDP checkpoints (can be either the model or optimizer).
|
| 342 |
+
output_path (`str`):
|
| 343 |
+
The path to save the merged checkpoint.
|
| 344 |
+
safe_serialization (`bool`, *optional*, defaults to `True`):
|
| 345 |
+
Whether to save the merged weights with safetensors (recommended).
|
| 346 |
+
remove_checkpoint_dir (`bool`, *optional*, defaults to `False`):
|
| 347 |
+
Whether to remove the checkpoint directory after merging.
|
| 348 |
+
"""
|
| 349 |
+
checkpoint_dir = Path(checkpoint_dir)
|
| 350 |
+
from accelerate.state import PartialState
|
| 351 |
+
|
| 352 |
+
if not is_torch_version(">=", "2.3.0"):
|
| 353 |
+
raise ValueError("`merge_fsdp_weights` requires PyTorch >= 2.3.0`")
|
| 354 |
+
|
| 355 |
+
# Verify that the checkpoint directory exists
|
| 356 |
+
if not checkpoint_dir.exists():
|
| 357 |
+
model_path_exists = (checkpoint_dir / "pytorch_model_fsdp_0").exists()
|
| 358 |
+
optimizer_path_exists = (checkpoint_dir / "optimizer_0").exists()
|
| 359 |
+
err = f"Tried to load from {checkpoint_dir} but couldn't find a valid metadata file."
|
| 360 |
+
if model_path_exists and optimizer_path_exists:
|
| 361 |
+
err += " However, potential model and optimizer checkpoint directories exist."
|
| 362 |
+
err += f"Please pass in either {checkpoint_dir}/pytorch_model_fsdp_0 or {checkpoint_dir}/optimizer_0"
|
| 363 |
+
err += "instead."
|
| 364 |
+
elif model_path_exists:
|
| 365 |
+
err += " However, a potential model checkpoint directory exists."
|
| 366 |
+
err += f"Please try passing in {checkpoint_dir}/pytorch_model_fsdp_0 instead."
|
| 367 |
+
elif optimizer_path_exists:
|
| 368 |
+
err += " However, a potential optimizer checkpoint directory exists."
|
| 369 |
+
err += f"Please try passing in {checkpoint_dir}/optimizer_0 instead."
|
| 370 |
+
raise ValueError(err)
|
| 371 |
+
|
| 372 |
+
# To setup `save` to work
|
| 373 |
+
state = PartialState()
|
| 374 |
+
if state.is_main_process:
|
| 375 |
+
logger.info(f"Merging FSDP weights from {checkpoint_dir}")
|
| 376 |
+
save_path = _distributed_checkpoint_to_merged_weights(checkpoint_dir, output_path, safe_serialization)
|
| 377 |
+
logger.info(f"Successfully merged FSDP weights and saved to {save_path}")
|
| 378 |
+
if remove_checkpoint_dir:
|
| 379 |
+
logger.info(f"Removing old checkpoint directory {checkpoint_dir}")
|
| 380 |
+
shutil.rmtree(checkpoint_dir)
|
| 381 |
+
state.wait_for_everyone()
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def ensure_weights_retied(param_init_fn, model: torch.nn.Module, device: torch.cuda.device):
|
| 385 |
+
_tied_names = getattr(model, "_tied_weights_keys", None)
|
| 386 |
+
if not _tied_names:
|
| 387 |
+
# if no tied names just passthrough
|
| 388 |
+
return param_init_fn
|
| 389 |
+
|
| 390 |
+
# get map of parameter instances to params.
|
| 391 |
+
# - needed for replacement later
|
| 392 |
+
_tied_params = {}
|
| 393 |
+
for name in _tied_names:
|
| 394 |
+
name = name.split(".")
|
| 395 |
+
name, param_name = ".".join(name[:-1]), name[-1]
|
| 396 |
+
mod = model.get_submodule(name)
|
| 397 |
+
param = getattr(mod, param_name)
|
| 398 |
+
|
| 399 |
+
_tied_params[id(param)] = None # placeholder for the param first
|
| 400 |
+
|
| 401 |
+
# build param_init_fn for the case with tied params
|
| 402 |
+
def param_init_fn_tied_param(module: torch.nn.Module):
|
| 403 |
+
# track which params to tie
|
| 404 |
+
# - usually only 1, but for completeness consider > 1
|
| 405 |
+
params_to_tie = defaultdict(list)
|
| 406 |
+
for n, param in module.named_parameters(recurse=False):
|
| 407 |
+
if id(param) in _tied_params:
|
| 408 |
+
params_to_tie[id(param)].append(n)
|
| 409 |
+
|
| 410 |
+
# call the param init fn, which potentially re-allocates the
|
| 411 |
+
# parameters
|
| 412 |
+
module = param_init_fn(module)
|
| 413 |
+
|
| 414 |
+
# search the parameters again and tie them up again
|
| 415 |
+
for id_key, _param_names in params_to_tie.items():
|
| 416 |
+
for param_name in _param_names:
|
| 417 |
+
param = _tied_params[id_key]
|
| 418 |
+
if param is None:
|
| 419 |
+
# everything will be tied to the first time the
|
| 420 |
+
# param is observed
|
| 421 |
+
_tied_params[id_key] = getattr(module, param_name)
|
| 422 |
+
else:
|
| 423 |
+
setattr(module, param_name, param) # tie
|
| 424 |
+
|
| 425 |
+
return module
|
| 426 |
+
|
| 427 |
+
return param_init_fn_tied_param
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
def fsdp2_load_full_state_dict(accelerator, model: torch.nn.Module, full_sd: dict):
|
| 431 |
+
"""
|
| 432 |
+
Loads the full state dict (could be only on rank 0) into the sharded model. This is done by broadcasting the
|
| 433 |
+
parameters from rank 0 to all other ranks. This function modifies the model in-place.
|
| 434 |
+
|
| 435 |
+
Args:
|
| 436 |
+
accelerator (`Accelerator`): The accelerator instance
|
| 437 |
+
model (`torch.nn.Module`): The model to load the state dict into
|
| 438 |
+
full_sd (`dict`): The full state dict to load, can only be on rank 0
|
| 439 |
+
"""
|
| 440 |
+
import torch.distributed as dist
|
| 441 |
+
from torch.distributed.tensor import distribute_tensor
|
| 442 |
+
|
| 443 |
+
sharded_sd = model.state_dict()
|
| 444 |
+
if accelerator.is_main_process:
|
| 445 |
+
for (param_name, full_param), sharded_param in zip(full_sd.items(), sharded_sd.values()):
|
| 446 |
+
full_param = full_param.detach().cuda()
|
| 447 |
+
mesh = sharded_param.device_mesh
|
| 448 |
+
dist.broadcast(full_param, src=0, group=mesh.get_group())
|
| 449 |
+
sharded_tensor = distribute_tensor(full_param, mesh, sharded_param.placements)
|
| 450 |
+
sharded_sd[param_name] = sharded_tensor
|
| 451 |
+
else:
|
| 452 |
+
for param_name, sharded_param in sharded_sd.items():
|
| 453 |
+
full_tensor = torch.empty(sharded_param.size(), device="cuda", dtype=sharded_param.dtype)
|
| 454 |
+
mesh = sharded_param.device_mesh
|
| 455 |
+
dist.broadcast(full_tensor, src=0, group=mesh.get_group())
|
| 456 |
+
sharded_tensor = distribute_tensor(full_tensor, mesh, sharded_param.placements)
|
| 457 |
+
sharded_sd[param_name] = sharded_tensor
|
| 458 |
+
|
| 459 |
+
model.load_state_dict(sharded_sd)
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
def fsdp2_switch_optimizer_parameters(optimizer: torch.optim.Optimizer, mapping: dict):
|
| 463 |
+
"""
|
| 464 |
+
Switches the parameters of the optimizer to new ones (sharded parameters in usual case). This function modifies the
|
| 465 |
+
optimizer in-place.
|
| 466 |
+
|
| 467 |
+
Args:
|
| 468 |
+
optimizer (`torch.optim.Optimizer`): Optimizer instance which contains the original model parameters
|
| 469 |
+
mapping (`dict`): Mapping from the original parameter (specified by `data_ptr`) to the sharded parameter
|
| 470 |
+
|
| 471 |
+
Raises:
|
| 472 |
+
KeyError:
|
| 473 |
+
If a parameter in the optimizer couldn't be switched to its sharded version. This should never happen and
|
| 474 |
+
indicates a bug. If we kept the original params instead of raising, the training wouldn't be numerically
|
| 475 |
+
correct and weights wouldn't get updated.
|
| 476 |
+
"""
|
| 477 |
+
try:
|
| 478 |
+
for param_group in optimizer.param_groups:
|
| 479 |
+
param_group["params"] = [mapping[p.data_ptr] for p in param_group["params"]]
|
| 480 |
+
except KeyError:
|
| 481 |
+
# This shouldn't ever happen, but we want to fail here else training wouldn't be numerically correct
|
| 482 |
+
# This basically means that we're missing a mapping from the original parameter to the sharded parameter
|
| 483 |
+
raise KeyError(
|
| 484 |
+
"A parameter in the optimizer couldn't be switched to its sharded version. This breaks the training. Please raise an issue on GitHub."
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
def fsdp2_prepare_model(accelerator, model: torch.nn.Module) -> torch.nn.Module:
|
| 489 |
+
"""Prepares the model for FSDP2 in-place. Also returns the model to avoid misuse of the original model.
|
| 490 |
+
|
| 491 |
+
Args:
|
| 492 |
+
accelerator (`Accelerator`): The accelerator instance
|
| 493 |
+
model (`torch.nn.Module`): The model to prepare
|
| 494 |
+
|
| 495 |
+
Returns:
|
| 496 |
+
`torch.nn.Module`: Prepared model
|
| 497 |
+
"""
|
| 498 |
+
from torch.distributed.fsdp import FSDPModule, MixedPrecisionPolicy, fully_shard
|
| 499 |
+
|
| 500 |
+
is_type_fsdp = isinstance(model, FSDPModule) or (
|
| 501 |
+
is_compiled_module(model) and isinstance(model._orig_mod, FSDPModule)
|
| 502 |
+
)
|
| 503 |
+
if is_type_fsdp:
|
| 504 |
+
return model
|
| 505 |
+
|
| 506 |
+
fsdp2_plugin = accelerator.state.fsdp_plugin
|
| 507 |
+
|
| 508 |
+
original_sd = model.state_dict()
|
| 509 |
+
|
| 510 |
+
from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy, transformer_auto_wrap_policy
|
| 511 |
+
|
| 512 |
+
# We need the `auto_wrap_policy` original type to create a custom poilicy function for sharding
|
| 513 |
+
# This is because `fully_shard` doesn't support old auto wrap policies, rather we have to imitate the behaviour
|
| 514 |
+
auto_wrap_policy_type = None
|
| 515 |
+
if fsdp2_plugin.auto_wrap_policy is transformer_auto_wrap_policy:
|
| 516 |
+
auto_wrap_policy_type = "transformer"
|
| 517 |
+
elif fsdp2_plugin.auto_wrap_policy is size_based_auto_wrap_policy:
|
| 518 |
+
auto_wrap_policy_type = "size"
|
| 519 |
+
|
| 520 |
+
# We set `auto_wrap_policy` to `functools.partial` to avoid creating it again
|
| 521 |
+
# This is because of `apply_activation_checkpointing` which will can reuse this function
|
| 522 |
+
fsdp2_plugin.set_auto_wrap_policy(model)
|
| 523 |
+
|
| 524 |
+
if fsdp2_plugin.activation_checkpointing:
|
| 525 |
+
from torch.distributed.algorithms._checkpoint.checkpoint_wrapper import (
|
| 526 |
+
CheckpointImpl,
|
| 527 |
+
apply_activation_checkpointing,
|
| 528 |
+
checkpoint_wrapper,
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
# Apply activation checkpointing before applying `fully_shard`
|
| 532 |
+
apply_activation_checkpointing(
|
| 533 |
+
model,
|
| 534 |
+
checkpoint_wrapper_fn=functools.partial(
|
| 535 |
+
checkpoint_wrapper,
|
| 536 |
+
checkpoint_impl=CheckpointImpl.NO_REENTRANT,
|
| 537 |
+
),
|
| 538 |
+
auto_wrap_policy=fsdp2_plugin.auto_wrap_policy,
|
| 539 |
+
)
|
| 540 |
+
|
| 541 |
+
fsdp2_kwargs = {
|
| 542 |
+
"reshard_after_forward": fsdp2_plugin.reshard_after_forward,
|
| 543 |
+
"offload_policy": fsdp2_plugin.cpu_offload,
|
| 544 |
+
# `fully_shard` doesn't accept `None` in case of `MixedPrecisionPolicy`
|
| 545 |
+
"mp_policy": fsdp2_plugin.mixed_precision_policy or MixedPrecisionPolicy(),
|
| 546 |
+
}
|
| 547 |
+
|
| 548 |
+
auto_wrap_policy = fsdp2_prepare_auto_wrap_policy(fsdp2_plugin, auto_wrap_policy_type, model)
|
| 549 |
+
if auto_wrap_policy is not None:
|
| 550 |
+
# We skip the model itself, as that one is always wrapped
|
| 551 |
+
for module in get_module_children_bottom_up(model)[:-1]:
|
| 552 |
+
if auto_wrap_policy(module):
|
| 553 |
+
fully_shard(module, **fsdp2_kwargs)
|
| 554 |
+
|
| 555 |
+
fully_shard(model, **fsdp2_kwargs)
|
| 556 |
+
|
| 557 |
+
if fsdp2_plugin.cpu_ram_efficient_loading:
|
| 558 |
+
# If `cpu_ram_efficient_loading` is enabled, only rank 0 loads the weights
|
| 559 |
+
# Other ranks have an empty model on `meta` device, so we need to distribute the weights properly
|
| 560 |
+
fsdp2_load_full_state_dict(accelerator, model, original_sd)
|
| 561 |
+
|
| 562 |
+
if accelerator.mixed_precision != "no" and model.dtype != torch.float32:
|
| 563 |
+
# We upcast the model according to `deepspeed`'s implementation
|
| 564 |
+
# More info about this can be found in `accelerator.py:prepare_model`s FSDP1 section
|
| 565 |
+
model = model.to(torch.float32)
|
| 566 |
+
if accelerator.is_main_process:
|
| 567 |
+
# TODO(siro1): Add a warning for each parameter that was upcasted
|
| 568 |
+
warnings.warn(
|
| 569 |
+
"FSDP upcast of low precision parameters to fp32 (since mixed_precision != 'no') may affect the precision of model checkpoints."
|
| 570 |
+
)
|
| 571 |
+
return model
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
def fsdp2_prepare_auto_wrap_policy(
|
| 575 |
+
fsdp2_plugin, auto_wrap_policy_type: str, model: torch.nn.Module
|
| 576 |
+
) -> Callable[[torch.nn.Module], bool]:
|
| 577 |
+
"""Prepares the auto wrap policy based on its type, done to mimic the behaviour of FSDP1 auto wrap policy.
|
| 578 |
+
|
| 579 |
+
Args:
|
| 580 |
+
fsdp2_plugin (`FullyShardedDataParallelPlugin`):
|
| 581 |
+
Instance of `FullyShardedDataParallelPlugin` containing the configuration options
|
| 582 |
+
auto_wrap_policy_type (`str`):
|
| 583 |
+
Either `transformer` or `size`
|
| 584 |
+
model (`torch.nn.Module`):
|
| 585 |
+
The model to wrap
|
| 586 |
+
|
| 587 |
+
Returns:
|
| 588 |
+
`Callable[[torch.nn.Module], bool]`:
|
| 589 |
+
The auto wrap policy function to be applied to the model
|
| 590 |
+
"""
|
| 591 |
+
if auto_wrap_policy_type == "transformer":
|
| 592 |
+
no_split_modules = model._no_split_modules
|
| 593 |
+
if no_split_modules is None:
|
| 594 |
+
no_split_modules = []
|
| 595 |
+
transformer_cls_names_to_wrap = list(no_split_modules)
|
| 596 |
+
if fsdp2_plugin.transformer_cls_names_to_wrap is not None:
|
| 597 |
+
transformer_cls_names_to_wrap = fsdp2_plugin.transformer_cls_names_to_wrap
|
| 598 |
+
transformer_cls_to_wrap = set()
|
| 599 |
+
|
| 600 |
+
for layer_class in transformer_cls_names_to_wrap:
|
| 601 |
+
transformer_cls = get_module_class_from_name(model, layer_class)
|
| 602 |
+
if transformer_cls is None:
|
| 603 |
+
raise ValueError(f"Could not find the transformer layer class {layer_class} in the model.")
|
| 604 |
+
transformer_cls_to_wrap.add(transformer_cls)
|
| 605 |
+
|
| 606 |
+
def policy(module: torch.nn.Module) -> bool:
|
| 607 |
+
if fsdp2_plugin.transformer_cls_names_to_wrap is None:
|
| 608 |
+
return False
|
| 609 |
+
return isinstance(module, tuple(transformer_cls_to_wrap))
|
| 610 |
+
|
| 611 |
+
elif auto_wrap_policy_type == "size":
|
| 612 |
+
|
| 613 |
+
def policy(module: torch.nn.Module) -> bool:
|
| 614 |
+
return module.numel() > fsdp2_plugin.min_num_params
|
| 615 |
+
else:
|
| 616 |
+
return None
|
| 617 |
+
|
| 618 |
+
return policy
|
| 619 |
+
|
| 620 |
+
|
| 621 |
+
def get_fsdp2_grad_scaler(**kwargs):
|
| 622 |
+
"""
|
| 623 |
+
Returns a `GradScaler` for FSDP2, as the current implementation of `get_grad_scaler` doesn't accept other args. We
|
| 624 |
+
need this as current `get_grad_scaler` accepts only `distributed_type` as arg, which doesn't differentiate between
|
| 625 |
+
FSDP1 and FSDP2
|
| 626 |
+
"""
|
| 627 |
+
from torch.amp.grad_scaler import GradScaler
|
| 628 |
+
|
| 629 |
+
return GradScaler(**kwargs)
|
venv/lib/python3.11/site-packages/accelerate/utils/imports.py
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import importlib
|
| 16 |
+
import importlib.metadata
|
| 17 |
+
import os
|
| 18 |
+
import warnings
|
| 19 |
+
from functools import lru_cache, wraps
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
from packaging import version
|
| 23 |
+
from packaging.version import parse
|
| 24 |
+
|
| 25 |
+
from .environment import parse_flag_from_env, patch_environment, str_to_bool
|
| 26 |
+
from .versions import compare_versions, is_torch_version
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# Try to run Torch native job in an environment with TorchXLA installed by setting this value to 0.
|
| 30 |
+
USE_TORCH_XLA = parse_flag_from_env("USE_TORCH_XLA", default=True)
|
| 31 |
+
|
| 32 |
+
_torch_xla_available = False
|
| 33 |
+
if USE_TORCH_XLA:
|
| 34 |
+
try:
|
| 35 |
+
import torch_xla.core.xla_model as xm # noqa: F401
|
| 36 |
+
import torch_xla.runtime
|
| 37 |
+
|
| 38 |
+
_torch_xla_available = True
|
| 39 |
+
except ImportError:
|
| 40 |
+
pass
|
| 41 |
+
|
| 42 |
+
# Keep it for is_tpu_available. It will be removed along with is_tpu_available.
|
| 43 |
+
_tpu_available = _torch_xla_available
|
| 44 |
+
|
| 45 |
+
# Cache this result has it's a C FFI call which can be pretty time-consuming
|
| 46 |
+
_torch_distributed_available = torch.distributed.is_available()
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _is_package_available(pkg_name, metadata_name=None):
|
| 50 |
+
# Check we're not importing a "pkg_name" directory somewhere but the actual library by trying to grab the version
|
| 51 |
+
package_exists = importlib.util.find_spec(pkg_name) is not None
|
| 52 |
+
if package_exists:
|
| 53 |
+
try:
|
| 54 |
+
# Some libraries have different names in the metadata
|
| 55 |
+
_ = importlib.metadata.metadata(pkg_name if metadata_name is None else metadata_name)
|
| 56 |
+
return True
|
| 57 |
+
except importlib.metadata.PackageNotFoundError:
|
| 58 |
+
return False
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def is_torch_distributed_available() -> bool:
|
| 62 |
+
return _torch_distributed_available
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def is_xccl_available():
|
| 66 |
+
# Currently IPEX uses custom "ccl" distributed backend. Return False for "xccl"
|
| 67 |
+
# here to avoid collisions.
|
| 68 |
+
if is_ipex_available():
|
| 69 |
+
return False
|
| 70 |
+
# TODO: switch to is_torch_version() once torch 2.7 will be released
|
| 71 |
+
if version.parse(torch.__version__).release >= version.parse("2.7").release:
|
| 72 |
+
return torch.distributed.distributed_c10d.is_xccl_available()
|
| 73 |
+
return False
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def is_ccl_available():
|
| 77 |
+
try:
|
| 78 |
+
pass
|
| 79 |
+
except ImportError:
|
| 80 |
+
print(
|
| 81 |
+
"Intel(R) oneCCL Bindings for PyTorch* is required to run DDP on Intel(R) GPUs, but it is not"
|
| 82 |
+
" detected. If you see \"ValueError: Invalid backend: 'ccl'\" error, please install Intel(R) oneCCL"
|
| 83 |
+
" Bindings for PyTorch*."
|
| 84 |
+
)
|
| 85 |
+
return (
|
| 86 |
+
importlib.util.find_spec("torch_ccl") is not None
|
| 87 |
+
or importlib.util.find_spec("oneccl_bindings_for_pytorch") is not None
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def get_ccl_version():
|
| 92 |
+
return importlib.metadata.version("oneccl_bind_pt")
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def is_import_timer_available():
|
| 96 |
+
return _is_package_available("import_timer")
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def is_pynvml_available():
|
| 100 |
+
return _is_package_available("pynvml") or _is_package_available("pynvml", "nvidia-ml-py")
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def is_pytest_available():
|
| 104 |
+
return _is_package_available("pytest")
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def is_msamp_available():
|
| 108 |
+
return _is_package_available("msamp", "ms-amp")
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def is_schedulefree_available():
|
| 112 |
+
return _is_package_available("schedulefree")
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def is_transformer_engine_available():
|
| 116 |
+
if is_hpu_available():
|
| 117 |
+
return _is_package_available("intel_transformer_engine", "intel-transformer-engine")
|
| 118 |
+
else:
|
| 119 |
+
return _is_package_available("transformer_engine", "transformer-engine")
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def is_lomo_available():
|
| 123 |
+
return _is_package_available("lomo_optim")
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def is_cuda_available():
|
| 127 |
+
"""
|
| 128 |
+
Checks if `cuda` is available via an `nvml-based` check which won't trigger the drivers and leave cuda
|
| 129 |
+
uninitialized.
|
| 130 |
+
"""
|
| 131 |
+
with patch_environment(PYTORCH_NVML_BASED_CUDA_CHECK="1"):
|
| 132 |
+
available = torch.cuda.is_available()
|
| 133 |
+
|
| 134 |
+
return available
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
@lru_cache
|
| 138 |
+
def is_torch_xla_available(check_is_tpu=False, check_is_gpu=False):
|
| 139 |
+
"""
|
| 140 |
+
Check if `torch_xla` is available. To train a native pytorch job in an environment with torch xla installed, set
|
| 141 |
+
the USE_TORCH_XLA to false.
|
| 142 |
+
"""
|
| 143 |
+
assert not (check_is_tpu and check_is_gpu), "The check_is_tpu and check_is_gpu cannot both be true."
|
| 144 |
+
|
| 145 |
+
if not _torch_xla_available:
|
| 146 |
+
return False
|
| 147 |
+
elif check_is_gpu:
|
| 148 |
+
return torch_xla.runtime.device_type() in ["GPU", "CUDA"]
|
| 149 |
+
elif check_is_tpu:
|
| 150 |
+
return torch_xla.runtime.device_type() == "TPU"
|
| 151 |
+
|
| 152 |
+
return True
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def is_torchao_available():
|
| 156 |
+
package_exists = _is_package_available("torchao")
|
| 157 |
+
if package_exists:
|
| 158 |
+
torchao_version = version.parse(importlib.metadata.version("torchao"))
|
| 159 |
+
return compare_versions(torchao_version, ">=", "0.6.1")
|
| 160 |
+
return False
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def is_deepspeed_available():
|
| 164 |
+
return _is_package_available("deepspeed")
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def is_pippy_available():
|
| 168 |
+
return is_torch_version(">=", "2.4.0")
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def is_bf16_available(ignore_tpu=False):
|
| 172 |
+
"Checks if bf16 is supported, optionally ignoring the TPU"
|
| 173 |
+
if is_torch_xla_available(check_is_tpu=True):
|
| 174 |
+
return not ignore_tpu
|
| 175 |
+
if is_cuda_available():
|
| 176 |
+
return torch.cuda.is_bf16_supported()
|
| 177 |
+
if is_mlu_available():
|
| 178 |
+
return torch.mlu.is_bf16_supported()
|
| 179 |
+
if is_mps_available():
|
| 180 |
+
return False
|
| 181 |
+
return True
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def is_fp16_available():
|
| 185 |
+
"Checks if fp16 is supported"
|
| 186 |
+
if is_habana_gaudi1():
|
| 187 |
+
return False
|
| 188 |
+
|
| 189 |
+
return True
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def is_fp8_available():
|
| 193 |
+
"Checks if fp8 is supported"
|
| 194 |
+
return is_msamp_available() or is_transformer_engine_available() or is_torchao_available()
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def is_4bit_bnb_available():
|
| 198 |
+
package_exists = _is_package_available("bitsandbytes")
|
| 199 |
+
if package_exists:
|
| 200 |
+
bnb_version = version.parse(importlib.metadata.version("bitsandbytes"))
|
| 201 |
+
return compare_versions(bnb_version, ">=", "0.39.0")
|
| 202 |
+
return False
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
def is_8bit_bnb_available():
|
| 206 |
+
package_exists = _is_package_available("bitsandbytes")
|
| 207 |
+
if package_exists:
|
| 208 |
+
bnb_version = version.parse(importlib.metadata.version("bitsandbytes"))
|
| 209 |
+
return compare_versions(bnb_version, ">=", "0.37.2")
|
| 210 |
+
return False
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def is_bnb_available(min_version=None):
|
| 214 |
+
package_exists = _is_package_available("bitsandbytes")
|
| 215 |
+
if package_exists and min_version is not None:
|
| 216 |
+
bnb_version = version.parse(importlib.metadata.version("bitsandbytes"))
|
| 217 |
+
return compare_versions(bnb_version, ">=", min_version)
|
| 218 |
+
else:
|
| 219 |
+
return package_exists
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def is_bitsandbytes_multi_backend_available():
|
| 223 |
+
if not is_bnb_available():
|
| 224 |
+
return False
|
| 225 |
+
import bitsandbytes as bnb
|
| 226 |
+
|
| 227 |
+
return "multi_backend" in getattr(bnb, "features", set())
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def is_torchvision_available():
|
| 231 |
+
return _is_package_available("torchvision")
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
def is_megatron_lm_available():
|
| 235 |
+
if str_to_bool(os.environ.get("ACCELERATE_USE_MEGATRON_LM", "False")) == 1:
|
| 236 |
+
if importlib.util.find_spec("megatron") is not None:
|
| 237 |
+
try:
|
| 238 |
+
megatron_version = parse(importlib.metadata.version("megatron-core"))
|
| 239 |
+
if compare_versions(megatron_version, ">=", "0.8.0"):
|
| 240 |
+
return importlib.util.find_spec(".training", "megatron")
|
| 241 |
+
except Exception as e:
|
| 242 |
+
warnings.warn(f"Parse Megatron version failed. Exception:{e}")
|
| 243 |
+
return False
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
def is_transformers_available():
|
| 247 |
+
return _is_package_available("transformers")
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def is_datasets_available():
|
| 251 |
+
return _is_package_available("datasets")
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
def is_peft_available():
|
| 255 |
+
return _is_package_available("peft")
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def is_timm_available():
|
| 259 |
+
return _is_package_available("timm")
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
def is_triton_available():
|
| 263 |
+
if is_xpu_available():
|
| 264 |
+
return _is_package_available("triton", "pytorch-triton-xpu")
|
| 265 |
+
return _is_package_available("triton")
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def is_aim_available():
|
| 269 |
+
package_exists = _is_package_available("aim")
|
| 270 |
+
if package_exists:
|
| 271 |
+
aim_version = version.parse(importlib.metadata.version("aim"))
|
| 272 |
+
return compare_versions(aim_version, "<", "4.0.0")
|
| 273 |
+
return False
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
def is_tensorboard_available():
|
| 277 |
+
return _is_package_available("tensorboard") or _is_package_available("tensorboardX")
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
def is_wandb_available():
|
| 281 |
+
return _is_package_available("wandb")
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def is_comet_ml_available():
|
| 285 |
+
return _is_package_available("comet_ml")
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def is_boto3_available():
|
| 289 |
+
return _is_package_available("boto3")
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
def is_rich_available():
|
| 293 |
+
if _is_package_available("rich"):
|
| 294 |
+
return parse_flag_from_env("ACCELERATE_ENABLE_RICH", False)
|
| 295 |
+
return False
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
def is_sagemaker_available():
|
| 299 |
+
return _is_package_available("sagemaker")
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def is_tqdm_available():
|
| 303 |
+
return _is_package_available("tqdm")
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
def is_clearml_available():
|
| 307 |
+
return _is_package_available("clearml")
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
def is_pandas_available():
|
| 311 |
+
return _is_package_available("pandas")
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def is_matplotlib_available():
|
| 315 |
+
return _is_package_available("matplotlib")
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
def is_mlflow_available():
|
| 319 |
+
if _is_package_available("mlflow"):
|
| 320 |
+
return True
|
| 321 |
+
|
| 322 |
+
if importlib.util.find_spec("mlflow") is not None:
|
| 323 |
+
try:
|
| 324 |
+
_ = importlib.metadata.metadata("mlflow-skinny")
|
| 325 |
+
return True
|
| 326 |
+
except importlib.metadata.PackageNotFoundError:
|
| 327 |
+
return False
|
| 328 |
+
return False
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
def is_mps_available(min_version="1.12"):
|
| 332 |
+
"Checks if MPS device is available. The minimum version required is 1.12."
|
| 333 |
+
# With torch 1.12, you can use torch.backends.mps
|
| 334 |
+
# With torch 2.0.0, you can use torch.mps
|
| 335 |
+
return is_torch_version(">=", min_version) and torch.backends.mps.is_available() and torch.backends.mps.is_built()
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
def is_ipex_available():
|
| 339 |
+
"Checks if ipex is installed."
|
| 340 |
+
|
| 341 |
+
def get_major_and_minor_from_version(full_version):
|
| 342 |
+
return str(version.parse(full_version).major) + "." + str(version.parse(full_version).minor)
|
| 343 |
+
|
| 344 |
+
_torch_version = importlib.metadata.version("torch")
|
| 345 |
+
if importlib.util.find_spec("intel_extension_for_pytorch") is None:
|
| 346 |
+
return False
|
| 347 |
+
_ipex_version = "N/A"
|
| 348 |
+
try:
|
| 349 |
+
_ipex_version = importlib.metadata.version("intel_extension_for_pytorch")
|
| 350 |
+
except importlib.metadata.PackageNotFoundError:
|
| 351 |
+
return False
|
| 352 |
+
torch_major_and_minor = get_major_and_minor_from_version(_torch_version)
|
| 353 |
+
ipex_major_and_minor = get_major_and_minor_from_version(_ipex_version)
|
| 354 |
+
if torch_major_and_minor != ipex_major_and_minor:
|
| 355 |
+
warnings.warn(
|
| 356 |
+
f"Intel Extension for PyTorch {ipex_major_and_minor} needs to work with PyTorch {ipex_major_and_minor}.*,"
|
| 357 |
+
f" but PyTorch {_torch_version} is found. Please switch to the matching version and run again."
|
| 358 |
+
)
|
| 359 |
+
return False
|
| 360 |
+
return True
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
@lru_cache
|
| 364 |
+
def is_mlu_available(check_device=False):
|
| 365 |
+
"""
|
| 366 |
+
Checks if `mlu` is available via an `cndev-based` check which won't trigger the drivers and leave mlu
|
| 367 |
+
uninitialized.
|
| 368 |
+
"""
|
| 369 |
+
if importlib.util.find_spec("torch_mlu") is None:
|
| 370 |
+
return False
|
| 371 |
+
|
| 372 |
+
import torch_mlu # noqa: F401
|
| 373 |
+
|
| 374 |
+
with patch_environment(PYTORCH_CNDEV_BASED_MLU_CHECK="1"):
|
| 375 |
+
available = torch.mlu.is_available()
|
| 376 |
+
|
| 377 |
+
return available
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
@lru_cache
|
| 381 |
+
def is_musa_available(check_device=False):
|
| 382 |
+
"Checks if `torch_musa` is installed and potentially if a MUSA is in the environment"
|
| 383 |
+
if importlib.util.find_spec("torch_musa") is None:
|
| 384 |
+
return False
|
| 385 |
+
|
| 386 |
+
import torch_musa # noqa: F401
|
| 387 |
+
|
| 388 |
+
if check_device:
|
| 389 |
+
try:
|
| 390 |
+
# Will raise a RuntimeError if no MUSA is found
|
| 391 |
+
_ = torch.musa.device_count()
|
| 392 |
+
return torch.musa.is_available()
|
| 393 |
+
except RuntimeError:
|
| 394 |
+
return False
|
| 395 |
+
return hasattr(torch, "musa") and torch.musa.is_available()
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
@lru_cache
|
| 399 |
+
def is_npu_available(check_device=False):
|
| 400 |
+
"Checks if `torch_npu` is installed and potentially if a NPU is in the environment"
|
| 401 |
+
if importlib.util.find_spec("torch_npu") is None:
|
| 402 |
+
return False
|
| 403 |
+
|
| 404 |
+
import torch_npu # noqa: F401
|
| 405 |
+
|
| 406 |
+
if check_device:
|
| 407 |
+
try:
|
| 408 |
+
# Will raise a RuntimeError if no NPU is found
|
| 409 |
+
_ = torch.npu.device_count()
|
| 410 |
+
return torch.npu.is_available()
|
| 411 |
+
except RuntimeError:
|
| 412 |
+
return False
|
| 413 |
+
return hasattr(torch, "npu") and torch.npu.is_available()
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
@lru_cache
|
| 417 |
+
def is_sdaa_available(check_device=False):
|
| 418 |
+
"Checks if `torch_sdaa` is installed and potentially if a SDAA is in the environment"
|
| 419 |
+
if importlib.util.find_spec("torch_sdaa") is None:
|
| 420 |
+
return False
|
| 421 |
+
|
| 422 |
+
import torch_sdaa # noqa: F401
|
| 423 |
+
|
| 424 |
+
if check_device:
|
| 425 |
+
try:
|
| 426 |
+
# Will raise a RuntimeError if no NPU is found
|
| 427 |
+
_ = torch.sdaa.device_count()
|
| 428 |
+
return torch.sdaa.is_available()
|
| 429 |
+
except RuntimeError:
|
| 430 |
+
return False
|
| 431 |
+
return hasattr(torch, "sdaa") and torch.sdaa.is_available()
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
@lru_cache
|
| 435 |
+
def is_hpu_available(init_hccl=False):
|
| 436 |
+
"Checks if `torch.hpu` is installed and potentially if a HPU is in the environment"
|
| 437 |
+
if (
|
| 438 |
+
importlib.util.find_spec("habana_frameworks") is None
|
| 439 |
+
or importlib.util.find_spec("habana_frameworks.torch") is None
|
| 440 |
+
):
|
| 441 |
+
return False
|
| 442 |
+
|
| 443 |
+
import habana_frameworks.torch # noqa: F401
|
| 444 |
+
|
| 445 |
+
if init_hccl:
|
| 446 |
+
import habana_frameworks.torch.distributed.hccl as hccl # noqa: F401
|
| 447 |
+
|
| 448 |
+
return hasattr(torch, "hpu") and torch.hpu.is_available()
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
def is_habana_gaudi1():
|
| 452 |
+
if is_hpu_available():
|
| 453 |
+
import habana_frameworks.torch.utils.experimental as htexp # noqa: F401
|
| 454 |
+
|
| 455 |
+
if htexp._get_device_type() == htexp.synDeviceType.synDeviceGaudi:
|
| 456 |
+
return True
|
| 457 |
+
|
| 458 |
+
return False
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
@lru_cache
|
| 462 |
+
def is_xpu_available(check_device=False):
|
| 463 |
+
"""
|
| 464 |
+
Checks if XPU acceleration is available either via `intel_extension_for_pytorch` or via stock PyTorch (>=2.4) and
|
| 465 |
+
potentially if a XPU is in the environment
|
| 466 |
+
"""
|
| 467 |
+
|
| 468 |
+
if is_ipex_available():
|
| 469 |
+
import intel_extension_for_pytorch # noqa: F401
|
| 470 |
+
else:
|
| 471 |
+
if is_torch_version("<=", "2.3"):
|
| 472 |
+
return False
|
| 473 |
+
|
| 474 |
+
if check_device:
|
| 475 |
+
try:
|
| 476 |
+
# Will raise a RuntimeError if no XPU is found
|
| 477 |
+
_ = torch.xpu.device_count()
|
| 478 |
+
return torch.xpu.is_available()
|
| 479 |
+
except RuntimeError:
|
| 480 |
+
return False
|
| 481 |
+
return hasattr(torch, "xpu") and torch.xpu.is_available()
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
def is_dvclive_available():
|
| 485 |
+
return _is_package_available("dvclive")
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
def is_torchdata_available():
|
| 489 |
+
return _is_package_available("torchdata")
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
# TODO: Remove this function once stateful_dataloader is a stable feature in torchdata.
|
| 493 |
+
def is_torchdata_stateful_dataloader_available():
|
| 494 |
+
package_exists = _is_package_available("torchdata")
|
| 495 |
+
if package_exists:
|
| 496 |
+
torchdata_version = version.parse(importlib.metadata.version("torchdata"))
|
| 497 |
+
return compare_versions(torchdata_version, ">=", "0.8.0")
|
| 498 |
+
return False
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
def torchao_required(func):
|
| 502 |
+
"""
|
| 503 |
+
A decorator that ensures the decorated function is only called when torchao is available.
|
| 504 |
+
"""
|
| 505 |
+
|
| 506 |
+
@wraps(func)
|
| 507 |
+
def wrapper(*args, **kwargs):
|
| 508 |
+
if not is_torchao_available():
|
| 509 |
+
raise ImportError(
|
| 510 |
+
"`torchao` is not available, please install it before calling this function via `pip install torchao`."
|
| 511 |
+
)
|
| 512 |
+
return func(*args, **kwargs)
|
| 513 |
+
|
| 514 |
+
return wrapper
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
# TODO: Rework this into `utils.deepspeed` and migrate the "core" chunks into `accelerate.deepspeed`
|
| 518 |
+
def deepspeed_required(func):
|
| 519 |
+
"""
|
| 520 |
+
A decorator that ensures the decorated function is only called when deepspeed is enabled.
|
| 521 |
+
"""
|
| 522 |
+
|
| 523 |
+
@wraps(func)
|
| 524 |
+
def wrapper(*args, **kwargs):
|
| 525 |
+
from accelerate.state import AcceleratorState
|
| 526 |
+
from accelerate.utils.dataclasses import DistributedType
|
| 527 |
+
|
| 528 |
+
if AcceleratorState._shared_state != {} and AcceleratorState().distributed_type != DistributedType.DEEPSPEED:
|
| 529 |
+
raise ValueError(
|
| 530 |
+
"DeepSpeed is not enabled, please make sure that an `Accelerator` is configured for `deepspeed` "
|
| 531 |
+
"before calling this function."
|
| 532 |
+
)
|
| 533 |
+
return func(*args, **kwargs)
|
| 534 |
+
|
| 535 |
+
return wrapper
|
| 536 |
+
|
| 537 |
+
|
| 538 |
+
def is_weights_only_available():
|
| 539 |
+
# Weights only with allowlist was added in 2.4.0
|
| 540 |
+
# ref: https://github.com/pytorch/pytorch/pull/124331
|
| 541 |
+
return is_torch_version(">=", "2.4.0")
|
| 542 |
+
|
| 543 |
+
|
| 544 |
+
def is_numpy_available(min_version="1.25.0"):
|
| 545 |
+
numpy_version = parse(importlib.metadata.version("numpy"))
|
| 546 |
+
return compare_versions(numpy_version, ">=", min_version)
|
venv/lib/python3.11/site-packages/accelerate/utils/launch.py
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import argparse
|
| 16 |
+
import os
|
| 17 |
+
import subprocess
|
| 18 |
+
import sys
|
| 19 |
+
from ast import literal_eval
|
| 20 |
+
from shutil import which
|
| 21 |
+
from typing import Any
|
| 22 |
+
|
| 23 |
+
import torch
|
| 24 |
+
|
| 25 |
+
from ..commands.config.config_args import SageMakerConfig
|
| 26 |
+
from ..utils import (
|
| 27 |
+
DynamoBackend,
|
| 28 |
+
PrecisionType,
|
| 29 |
+
is_fp8_available,
|
| 30 |
+
is_hpu_available,
|
| 31 |
+
is_ipex_available,
|
| 32 |
+
is_mlu_available,
|
| 33 |
+
is_musa_available,
|
| 34 |
+
is_npu_available,
|
| 35 |
+
is_sdaa_available,
|
| 36 |
+
is_torch_xla_available,
|
| 37 |
+
is_xpu_available,
|
| 38 |
+
)
|
| 39 |
+
from ..utils.constants import DEEPSPEED_MULTINODE_LAUNCHERS
|
| 40 |
+
from ..utils.other import is_port_in_use, merge_dicts
|
| 41 |
+
from ..utils.versions import compare_versions
|
| 42 |
+
from .dataclasses import DistributedType, SageMakerDistributedType
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _filter_args(args, parser, default_args=[]):
|
| 46 |
+
"""
|
| 47 |
+
Filters out all `accelerate` specific args
|
| 48 |
+
"""
|
| 49 |
+
new_args, _ = parser.parse_known_args(default_args)
|
| 50 |
+
for key, value in vars(args).items():
|
| 51 |
+
if key in vars(new_args).keys():
|
| 52 |
+
setattr(new_args, key, value)
|
| 53 |
+
return new_args
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def _get_mpirun_args():
|
| 57 |
+
"""
|
| 58 |
+
Determines the executable and argument names for mpirun, based on the type of install. The supported MPI programs
|
| 59 |
+
are: OpenMPI, Intel MPI, or MVAPICH.
|
| 60 |
+
|
| 61 |
+
Returns: Program name and arg names for hostfile, num processes, and processes per node
|
| 62 |
+
"""
|
| 63 |
+
# Find the MPI program name
|
| 64 |
+
mpi_apps = [x for x in ["mpirun", "mpiexec"] if which(x)]
|
| 65 |
+
|
| 66 |
+
if len(mpi_apps) == 0:
|
| 67 |
+
raise OSError("mpirun or mpiexec were not found. Ensure that Intel MPI, Open MPI, or MVAPICH are installed.")
|
| 68 |
+
|
| 69 |
+
# Call the app with the --version flag to determine which MPI app is installed
|
| 70 |
+
mpi_app = mpi_apps[0]
|
| 71 |
+
mpirun_version = subprocess.check_output([mpi_app, "--version"])
|
| 72 |
+
|
| 73 |
+
if b"Open MPI" in mpirun_version:
|
| 74 |
+
return mpi_app, "--hostfile", "-n", "--npernode", "--bind-to"
|
| 75 |
+
else:
|
| 76 |
+
# Intel MPI and MVAPICH both use the same arg names
|
| 77 |
+
return mpi_app, "-f", "-n", "-ppn", ""
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def setup_fp8_env(args: argparse.Namespace, current_env: dict[str, str]):
|
| 81 |
+
"""
|
| 82 |
+
Setup the FP8 environment variables.
|
| 83 |
+
"""
|
| 84 |
+
prefix = "ACCELERATE_"
|
| 85 |
+
for arg in vars(args):
|
| 86 |
+
if arg.startswith("fp8_"):
|
| 87 |
+
value = getattr(args, arg)
|
| 88 |
+
if value is not None:
|
| 89 |
+
if arg == "fp8_override_linear_precision":
|
| 90 |
+
current_env[prefix + "FP8_OVERRIDE_FPROP"] = value[0]
|
| 91 |
+
current_env[prefix + "FP8_OVERRIDE_DGRAD"] = value[1]
|
| 92 |
+
current_env[prefix + "FP8_OVERRIDE_WGRAD"] = value[2]
|
| 93 |
+
else:
|
| 94 |
+
current_env[f"{prefix}{arg.upper()}"] = str(getattr(args, arg))
|
| 95 |
+
return current_env
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def prepare_simple_launcher_cmd_env(args: argparse.Namespace) -> tuple[list[str], dict[str, str]]:
|
| 99 |
+
"""
|
| 100 |
+
Prepares and returns the command list and an environment with the correct simple launcher environment variables.
|
| 101 |
+
"""
|
| 102 |
+
cmd = []
|
| 103 |
+
if args.no_python and args.module:
|
| 104 |
+
raise ValueError("--module and --no_python cannot be used together")
|
| 105 |
+
|
| 106 |
+
if args.mpirun_hostfile is not None:
|
| 107 |
+
mpi_app_name, hostfile_arg, num_proc_arg, proc_per_node_arg, bind_to_arg = _get_mpirun_args()
|
| 108 |
+
mpirun_ccl = getattr(args, "mpirun_ccl", None)
|
| 109 |
+
bind_to = getattr(args, "bind-to", "socket")
|
| 110 |
+
num_machines = args.num_machines
|
| 111 |
+
num_processes = getattr(args, "num_processes", None)
|
| 112 |
+
nproc_per_node = str(num_processes // num_machines) if num_processes and num_machines else "1"
|
| 113 |
+
cmd += [
|
| 114 |
+
mpi_app_name,
|
| 115 |
+
hostfile_arg,
|
| 116 |
+
args.mpirun_hostfile,
|
| 117 |
+
proc_per_node_arg,
|
| 118 |
+
nproc_per_node,
|
| 119 |
+
]
|
| 120 |
+
if num_processes:
|
| 121 |
+
cmd += [num_proc_arg, str(num_processes)]
|
| 122 |
+
if bind_to_arg:
|
| 123 |
+
cmd += [bind_to_arg, bind_to]
|
| 124 |
+
if not args.no_python:
|
| 125 |
+
cmd.append(sys.executable)
|
| 126 |
+
if args.module:
|
| 127 |
+
cmd.append("-m")
|
| 128 |
+
cmd.append(args.training_script)
|
| 129 |
+
cmd.extend(args.training_script_args)
|
| 130 |
+
|
| 131 |
+
current_env = os.environ.copy()
|
| 132 |
+
current_env["ACCELERATE_USE_CPU"] = str(args.cpu or args.use_cpu)
|
| 133 |
+
if args.debug:
|
| 134 |
+
current_env["ACCELERATE_DEBUG_MODE"] = "true"
|
| 135 |
+
if args.gpu_ids != "all" and args.gpu_ids is not None:
|
| 136 |
+
if is_xpu_available():
|
| 137 |
+
current_env["ZE_AFFINITY_MASK"] = args.gpu_ids
|
| 138 |
+
elif is_mlu_available():
|
| 139 |
+
current_env["MLU_VISIBLE_DEVICES"] = args.gpu_ids
|
| 140 |
+
elif is_sdaa_available():
|
| 141 |
+
current_env["SDAA_VISIBLE_DEVICES"] = args.gpu_ids
|
| 142 |
+
elif is_musa_available():
|
| 143 |
+
current_env["MUSA_VISIBLE_DEVICES"] = args.gpu_ids
|
| 144 |
+
elif is_npu_available():
|
| 145 |
+
current_env["ASCEND_RT_VISIBLE_DEVICES"] = args.gpu_ids
|
| 146 |
+
elif is_hpu_available():
|
| 147 |
+
current_env["HABANA_VISIBLE_MODULES"] = args.gpu_ids
|
| 148 |
+
else:
|
| 149 |
+
current_env["CUDA_VISIBLE_DEVICES"] = args.gpu_ids
|
| 150 |
+
if args.num_machines > 1:
|
| 151 |
+
current_env["MASTER_ADDR"] = args.main_process_ip
|
| 152 |
+
current_env["MASTER_PORT"] = str(args.main_process_port)
|
| 153 |
+
|
| 154 |
+
if args.mpirun_hostfile is not None:
|
| 155 |
+
current_env["CCL_WORKER_COUNT"] = str(mpirun_ccl)
|
| 156 |
+
elif args.num_processes > 1:
|
| 157 |
+
current_env["MASTER_ADDR"] = args.main_process_ip if args.main_process_ip is not None else "127.0.0.1"
|
| 158 |
+
current_env["MASTER_PORT"] = str(args.main_process_port) if args.main_process_port is not None else "29500"
|
| 159 |
+
|
| 160 |
+
try:
|
| 161 |
+
mixed_precision = PrecisionType(args.mixed_precision.lower())
|
| 162 |
+
except ValueError:
|
| 163 |
+
raise ValueError(
|
| 164 |
+
f"Unknown mixed_precision mode: {args.mixed_precision.lower()}. Choose between {PrecisionType.list()}."
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
current_env["ACCELERATE_MIXED_PRECISION"] = str(mixed_precision)
|
| 168 |
+
if args.mixed_precision.lower() == "fp8":
|
| 169 |
+
if not is_fp8_available():
|
| 170 |
+
raise RuntimeError(
|
| 171 |
+
"FP8 is not available on this machine. Please ensure that either Transformer Engine or MSAMP is installed."
|
| 172 |
+
)
|
| 173 |
+
current_env = setup_fp8_env(args, current_env)
|
| 174 |
+
|
| 175 |
+
try:
|
| 176 |
+
dynamo_backend = DynamoBackend(args.dynamo_backend.upper())
|
| 177 |
+
except ValueError:
|
| 178 |
+
raise ValueError(
|
| 179 |
+
f"Unknown dynamo backend: {args.dynamo_backend.upper()}. Choose between {DynamoBackend.list()}."
|
| 180 |
+
)
|
| 181 |
+
current_env["ACCELERATE_DYNAMO_BACKEND"] = dynamo_backend.value
|
| 182 |
+
current_env["ACCELERATE_DYNAMO_MODE"] = args.dynamo_mode
|
| 183 |
+
current_env["ACCELERATE_DYNAMO_USE_FULLGRAPH"] = str(args.dynamo_use_fullgraph)
|
| 184 |
+
current_env["ACCELERATE_DYNAMO_USE_DYNAMIC"] = str(args.dynamo_use_dynamic)
|
| 185 |
+
|
| 186 |
+
current_env["OMP_NUM_THREADS"] = str(args.num_cpu_threads_per_process)
|
| 187 |
+
if is_ipex_available():
|
| 188 |
+
current_env["ACCELERATE_USE_IPEX"] = str(args.ipex).lower()
|
| 189 |
+
if args.enable_cpu_affinity:
|
| 190 |
+
current_env["ACCELERATE_CPU_AFFINITY"] = "1"
|
| 191 |
+
return cmd, current_env
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def prepare_multi_gpu_env(args: argparse.Namespace) -> dict[str, str]:
|
| 195 |
+
"""
|
| 196 |
+
Prepares and returns an environment with the correct multi-GPU environment variables.
|
| 197 |
+
"""
|
| 198 |
+
num_processes = args.num_processes
|
| 199 |
+
num_machines = args.num_machines
|
| 200 |
+
main_process_ip = args.main_process_ip
|
| 201 |
+
main_process_port = args.main_process_port
|
| 202 |
+
if num_machines > 1:
|
| 203 |
+
args.nproc_per_node = str(num_processes // num_machines)
|
| 204 |
+
args.nnodes = str(num_machines)
|
| 205 |
+
args.node_rank = int(args.machine_rank)
|
| 206 |
+
if getattr(args, "same_network", False):
|
| 207 |
+
args.master_addr = str(main_process_ip)
|
| 208 |
+
args.master_port = str(main_process_port)
|
| 209 |
+
else:
|
| 210 |
+
args.rdzv_endpoint = f"{main_process_ip}:{main_process_port}"
|
| 211 |
+
else:
|
| 212 |
+
args.nproc_per_node = str(num_processes)
|
| 213 |
+
if main_process_port is not None:
|
| 214 |
+
args.master_port = str(main_process_port)
|
| 215 |
+
|
| 216 |
+
if main_process_port is None:
|
| 217 |
+
main_process_port = 29500
|
| 218 |
+
|
| 219 |
+
# only need to check port availability in main process, in case we have to start multiple launchers on the same machine
|
| 220 |
+
# for some reasons like splitting log files.
|
| 221 |
+
need_port_check = num_machines <= 1 or int(args.machine_rank) == 0
|
| 222 |
+
if need_port_check and is_port_in_use(main_process_port):
|
| 223 |
+
raise ConnectionError(
|
| 224 |
+
f"Tried to launch distributed communication on port `{main_process_port}`, but another process is utilizing it. "
|
| 225 |
+
"Please specify a different port (such as using the `--main_process_port` flag or specifying a different `main_process_port` in your config file)"
|
| 226 |
+
" and rerun your script. To automatically use the next open port (on a single node), you can set this to `0`."
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
if args.module and args.no_python:
|
| 230 |
+
raise ValueError("--module and --no_python cannot be used together")
|
| 231 |
+
elif args.module:
|
| 232 |
+
args.module = True
|
| 233 |
+
elif args.no_python:
|
| 234 |
+
args.no_python = True
|
| 235 |
+
|
| 236 |
+
current_env = os.environ.copy()
|
| 237 |
+
if args.debug:
|
| 238 |
+
current_env["ACCELERATE_DEBUG_MODE"] = "true"
|
| 239 |
+
gpu_ids = getattr(args, "gpu_ids", "all")
|
| 240 |
+
if gpu_ids != "all" and args.gpu_ids is not None:
|
| 241 |
+
if is_xpu_available():
|
| 242 |
+
current_env["ZE_AFFINITY_MASK"] = gpu_ids
|
| 243 |
+
elif is_mlu_available():
|
| 244 |
+
current_env["MLU_VISIBLE_DEVICES"] = gpu_ids
|
| 245 |
+
elif is_sdaa_available():
|
| 246 |
+
current_env["SDAA_VISIBLE_DEVICES"] = gpu_ids
|
| 247 |
+
elif is_musa_available():
|
| 248 |
+
current_env["MUSA_VISIBLE_DEVICES"] = gpu_ids
|
| 249 |
+
elif is_npu_available():
|
| 250 |
+
current_env["ASCEND_RT_VISIBLE_DEVICES"] = gpu_ids
|
| 251 |
+
elif is_hpu_available():
|
| 252 |
+
current_env["HABANA_VISIBLE_MODULES"] = gpu_ids
|
| 253 |
+
else:
|
| 254 |
+
current_env["CUDA_VISIBLE_DEVICES"] = gpu_ids
|
| 255 |
+
mixed_precision = args.mixed_precision.lower()
|
| 256 |
+
try:
|
| 257 |
+
mixed_precision = PrecisionType(mixed_precision)
|
| 258 |
+
except ValueError:
|
| 259 |
+
raise ValueError(f"Unknown mixed_precision mode: {mixed_precision}. Choose between {PrecisionType.list()}.")
|
| 260 |
+
|
| 261 |
+
current_env["ACCELERATE_MIXED_PRECISION"] = str(mixed_precision)
|
| 262 |
+
if args.mixed_precision.lower() == "fp8":
|
| 263 |
+
if not is_fp8_available():
|
| 264 |
+
raise RuntimeError(
|
| 265 |
+
"FP8 is not available on this machine. Please ensure that either Transformer Engine or MSAMP is installed."
|
| 266 |
+
)
|
| 267 |
+
current_env = setup_fp8_env(args, current_env)
|
| 268 |
+
|
| 269 |
+
try:
|
| 270 |
+
dynamo_backend = DynamoBackend(args.dynamo_backend.upper())
|
| 271 |
+
except ValueError:
|
| 272 |
+
raise ValueError(
|
| 273 |
+
f"Unknown dynamo backend: {args.dynamo_backend.upper()}. Choose between {DynamoBackend.list()}."
|
| 274 |
+
)
|
| 275 |
+
current_env["ACCELERATE_DYNAMO_BACKEND"] = dynamo_backend.value
|
| 276 |
+
current_env["ACCELERATE_DYNAMO_MODE"] = args.dynamo_mode
|
| 277 |
+
current_env["ACCELERATE_DYNAMO_USE_FULLGRAPH"] = str(args.dynamo_use_fullgraph)
|
| 278 |
+
current_env["ACCELERATE_DYNAMO_USE_DYNAMIC"] = str(args.dynamo_use_dynamic)
|
| 279 |
+
|
| 280 |
+
if args.use_fsdp:
|
| 281 |
+
current_env["ACCELERATE_USE_FSDP"] = "true"
|
| 282 |
+
if args.fsdp_cpu_ram_efficient_loading and not args.fsdp_sync_module_states:
|
| 283 |
+
raise ValueError("When using `--fsdp_cpu_ram_efficient_loading` set `--fsdp_sync_module_states` to `True`")
|
| 284 |
+
|
| 285 |
+
current_env["FSDP_VERSION"] = str(args.fsdp_version) if hasattr(args, "fsdp_version") else "1"
|
| 286 |
+
|
| 287 |
+
# For backwards compatibility, we support this in launched scripts,
|
| 288 |
+
# however, we do not ask users for this in `accelerate config` CLI
|
| 289 |
+
current_env["FSDP_SHARDING_STRATEGY"] = str(args.fsdp_sharding_strategy)
|
| 290 |
+
|
| 291 |
+
current_env["FSDP_RESHARD_AFTER_FORWARD"] = str(args.fsdp_reshard_after_forward).lower()
|
| 292 |
+
current_env["FSDP_OFFLOAD_PARAMS"] = str(args.fsdp_offload_params).lower()
|
| 293 |
+
current_env["FSDP_MIN_NUM_PARAMS"] = str(args.fsdp_min_num_params)
|
| 294 |
+
if args.fsdp_auto_wrap_policy is not None:
|
| 295 |
+
current_env["FSDP_AUTO_WRAP_POLICY"] = str(args.fsdp_auto_wrap_policy)
|
| 296 |
+
if args.fsdp_transformer_layer_cls_to_wrap is not None:
|
| 297 |
+
current_env["FSDP_TRANSFORMER_CLS_TO_WRAP"] = str(args.fsdp_transformer_layer_cls_to_wrap)
|
| 298 |
+
if args.fsdp_backward_prefetch is not None:
|
| 299 |
+
current_env["FSDP_BACKWARD_PREFETCH"] = str(args.fsdp_backward_prefetch)
|
| 300 |
+
if args.fsdp_state_dict_type is not None:
|
| 301 |
+
current_env["FSDP_STATE_DICT_TYPE"] = str(args.fsdp_state_dict_type)
|
| 302 |
+
current_env["FSDP_FORWARD_PREFETCH"] = str(args.fsdp_forward_prefetch).lower()
|
| 303 |
+
current_env["FSDP_USE_ORIG_PARAMS"] = str(args.fsdp_use_orig_params).lower()
|
| 304 |
+
current_env["FSDP_CPU_RAM_EFFICIENT_LOADING"] = str(args.fsdp_cpu_ram_efficient_loading).lower()
|
| 305 |
+
current_env["FSDP_SYNC_MODULE_STATES"] = str(args.fsdp_sync_module_states).lower()
|
| 306 |
+
current_env["FSDP_ACTIVATION_CHECKPOINTING"] = str(args.fsdp_activation_checkpointing).lower()
|
| 307 |
+
|
| 308 |
+
if args.use_tp:
|
| 309 |
+
current_env["ACCELERATE_USE_TP"] = "true"
|
| 310 |
+
current_env["TP_SIZE"] = str(args.tp_size)
|
| 311 |
+
|
| 312 |
+
if args.use_megatron_lm:
|
| 313 |
+
prefix = "MEGATRON_LM_"
|
| 314 |
+
current_env["ACCELERATE_USE_MEGATRON_LM"] = "true"
|
| 315 |
+
current_env[prefix + "TP_DEGREE"] = str(args.megatron_lm_tp_degree)
|
| 316 |
+
current_env[prefix + "PP_DEGREE"] = str(args.megatron_lm_pp_degree)
|
| 317 |
+
current_env[prefix + "GRADIENT_CLIPPING"] = str(args.megatron_lm_gradient_clipping)
|
| 318 |
+
if args.megatron_lm_num_micro_batches is not None:
|
| 319 |
+
current_env[prefix + "NUM_MICRO_BATCHES"] = str(args.megatron_lm_num_micro_batches)
|
| 320 |
+
if args.megatron_lm_sequence_parallelism is not None:
|
| 321 |
+
current_env[prefix + "SEQUENCE_PARALLELISM"] = str(args.megatron_lm_sequence_parallelism)
|
| 322 |
+
if args.megatron_lm_recompute_activations is not None:
|
| 323 |
+
current_env[prefix + "RECOMPUTE_ACTIVATIONS"] = str(args.megatron_lm_recompute_activations)
|
| 324 |
+
if args.megatron_lm_use_distributed_optimizer is not None:
|
| 325 |
+
current_env[prefix + "USE_DISTRIBUTED_OPTIMIZER"] = str(args.megatron_lm_use_distributed_optimizer)
|
| 326 |
+
|
| 327 |
+
current_env["OMP_NUM_THREADS"] = str(args.num_cpu_threads_per_process)
|
| 328 |
+
if args.enable_cpu_affinity:
|
| 329 |
+
current_env["ACCELERATE_CPU_AFFINITY"] = "1"
|
| 330 |
+
return current_env
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
def prepare_deepspeed_cmd_env(args: argparse.Namespace) -> tuple[list[str], dict[str, str]]:
|
| 334 |
+
"""
|
| 335 |
+
Prepares and returns the command list and an environment with the correct DeepSpeed environment variables.
|
| 336 |
+
"""
|
| 337 |
+
num_processes = args.num_processes
|
| 338 |
+
num_machines = args.num_machines
|
| 339 |
+
main_process_ip = args.main_process_ip
|
| 340 |
+
main_process_port = args.main_process_port
|
| 341 |
+
cmd = None
|
| 342 |
+
|
| 343 |
+
# make sure launcher is not None
|
| 344 |
+
if args.deepspeed_multinode_launcher is None:
|
| 345 |
+
# set to default pdsh
|
| 346 |
+
args.deepspeed_multinode_launcher = DEEPSPEED_MULTINODE_LAUNCHERS[0]
|
| 347 |
+
|
| 348 |
+
if num_machines > 1 and args.deepspeed_multinode_launcher != DEEPSPEED_MULTINODE_LAUNCHERS[1]:
|
| 349 |
+
cmd = ["deepspeed"]
|
| 350 |
+
cmd.extend(["--hostfile", str(args.deepspeed_hostfile)])
|
| 351 |
+
if args.deepspeed_multinode_launcher == "nossh":
|
| 352 |
+
if compare_versions("deepspeed", "<", "0.14.5"):
|
| 353 |
+
raise ValueError("nossh launcher requires DeepSpeed >= 0.14.5")
|
| 354 |
+
cmd.extend(["--node_rank", str(args.machine_rank), "--no_ssh"])
|
| 355 |
+
else:
|
| 356 |
+
cmd.extend(["--no_local_rank", "--launcher", str(args.deepspeed_multinode_launcher)])
|
| 357 |
+
if args.deepspeed_exclusion_filter is not None:
|
| 358 |
+
cmd.extend(
|
| 359 |
+
[
|
| 360 |
+
"--exclude",
|
| 361 |
+
str(args.deepspeed_exclusion_filter),
|
| 362 |
+
]
|
| 363 |
+
)
|
| 364 |
+
elif args.deepspeed_inclusion_filter is not None:
|
| 365 |
+
cmd.extend(
|
| 366 |
+
[
|
| 367 |
+
"--include",
|
| 368 |
+
str(args.deepspeed_inclusion_filter),
|
| 369 |
+
]
|
| 370 |
+
)
|
| 371 |
+
else:
|
| 372 |
+
cmd.extend(["--num_gpus", str(args.num_processes // args.num_machines)])
|
| 373 |
+
if main_process_ip:
|
| 374 |
+
cmd.extend(["--master_addr", str(main_process_ip)])
|
| 375 |
+
cmd.extend(["--master_port", str(main_process_port)])
|
| 376 |
+
if args.module and args.no_python:
|
| 377 |
+
raise ValueError("--module and --no_python cannot be used together")
|
| 378 |
+
elif args.module:
|
| 379 |
+
cmd.append("--module")
|
| 380 |
+
elif args.no_python:
|
| 381 |
+
cmd.append("--no_python")
|
| 382 |
+
cmd.append(args.training_script)
|
| 383 |
+
cmd.extend(args.training_script_args)
|
| 384 |
+
elif num_machines > 1 and args.deepspeed_multinode_launcher == DEEPSPEED_MULTINODE_LAUNCHERS[1]:
|
| 385 |
+
args.nproc_per_node = str(num_processes // num_machines)
|
| 386 |
+
args.nnodes = str(num_machines)
|
| 387 |
+
args.node_rank = int(args.machine_rank)
|
| 388 |
+
if getattr(args, "same_network", False):
|
| 389 |
+
args.master_addr = str(main_process_ip)
|
| 390 |
+
args.master_port = str(main_process_port)
|
| 391 |
+
else:
|
| 392 |
+
args.rdzv_endpoint = f"{main_process_ip}:{main_process_port}"
|
| 393 |
+
else:
|
| 394 |
+
args.nproc_per_node = str(num_processes)
|
| 395 |
+
if main_process_port is not None:
|
| 396 |
+
args.master_port = str(main_process_port)
|
| 397 |
+
|
| 398 |
+
if main_process_port is None:
|
| 399 |
+
main_process_port = 29500
|
| 400 |
+
|
| 401 |
+
# only need to check port availability in main process, in case we have to start multiple launchers on the same machine
|
| 402 |
+
# for some reasons like splitting log files.
|
| 403 |
+
need_port_check = num_machines <= 1 or int(args.machine_rank) == 0
|
| 404 |
+
if need_port_check and is_port_in_use(main_process_port):
|
| 405 |
+
raise ConnectionError(
|
| 406 |
+
f"Tried to launch distributed communication on port `{main_process_port}`, but another process is utilizing it. "
|
| 407 |
+
"Please specify a different port (such as using the `--main_process_port` flag or specifying a different `main_process_port` in your config file)"
|
| 408 |
+
" and rerun your script. To automatically use the next open port (on a single node), you can set this to `0`."
|
| 409 |
+
)
|
| 410 |
+
|
| 411 |
+
if args.module and args.no_python:
|
| 412 |
+
raise ValueError("--module and --no_python cannot be used together")
|
| 413 |
+
elif args.module:
|
| 414 |
+
args.module = True
|
| 415 |
+
elif args.no_python:
|
| 416 |
+
args.no_python = True
|
| 417 |
+
|
| 418 |
+
current_env = os.environ.copy()
|
| 419 |
+
if args.debug:
|
| 420 |
+
current_env["ACCELERATE_DEBUG_MODE"] = "true"
|
| 421 |
+
gpu_ids = getattr(args, "gpu_ids", "all")
|
| 422 |
+
if gpu_ids != "all" and args.gpu_ids is not None:
|
| 423 |
+
if is_xpu_available():
|
| 424 |
+
current_env["ZE_AFFINITY_MASK"] = gpu_ids
|
| 425 |
+
elif is_mlu_available():
|
| 426 |
+
current_env["MLU_VISIBLE_DEVICES"] = gpu_ids
|
| 427 |
+
elif is_sdaa_available():
|
| 428 |
+
current_env["SDAA_VISIBLE_DEVICES"] = gpu_ids
|
| 429 |
+
elif is_musa_available():
|
| 430 |
+
current_env["MUSA_VISIBLE_DEVICES"] = gpu_ids
|
| 431 |
+
elif is_npu_available():
|
| 432 |
+
current_env["ASCEND_RT_VISIBLE_DEVICES"] = gpu_ids
|
| 433 |
+
elif is_hpu_available():
|
| 434 |
+
current_env["HABANA_VISIBLE_MODULES"] = gpu_ids
|
| 435 |
+
else:
|
| 436 |
+
current_env["CUDA_VISIBLE_DEVICES"] = gpu_ids
|
| 437 |
+
try:
|
| 438 |
+
mixed_precision = PrecisionType(args.mixed_precision.lower())
|
| 439 |
+
except ValueError:
|
| 440 |
+
raise ValueError(
|
| 441 |
+
f"Unknown mixed_precision mode: {args.mixed_precision.lower()}. Choose between {PrecisionType.list()}."
|
| 442 |
+
)
|
| 443 |
+
|
| 444 |
+
current_env["PYTHONPATH"] = env_var_path_add("PYTHONPATH", os.path.abspath("."))
|
| 445 |
+
current_env["ACCELERATE_MIXED_PRECISION"] = str(mixed_precision)
|
| 446 |
+
if args.mixed_precision.lower() == "fp8":
|
| 447 |
+
if not is_fp8_available():
|
| 448 |
+
raise RuntimeError(
|
| 449 |
+
"FP8 is not available on this machine. Please ensure that either Transformer Engine or MSAMP is installed."
|
| 450 |
+
)
|
| 451 |
+
current_env = setup_fp8_env(args, current_env)
|
| 452 |
+
current_env["ACCELERATE_CONFIG_DS_FIELDS"] = str(args.deepspeed_fields_from_accelerate_config).lower()
|
| 453 |
+
current_env["ACCELERATE_USE_DEEPSPEED"] = "true"
|
| 454 |
+
if args.zero_stage is not None:
|
| 455 |
+
current_env["ACCELERATE_DEEPSPEED_ZERO_STAGE"] = str(args.zero_stage)
|
| 456 |
+
if args.gradient_accumulation_steps is not None:
|
| 457 |
+
current_env["ACCELERATE_GRADIENT_ACCUMULATION_STEPS"] = str(args.gradient_accumulation_steps)
|
| 458 |
+
if args.gradient_clipping is not None:
|
| 459 |
+
current_env["ACCELERATE_GRADIENT_CLIPPING"] = str(args.gradient_clipping).lower()
|
| 460 |
+
if args.offload_optimizer_device is not None:
|
| 461 |
+
current_env["ACCELERATE_DEEPSPEED_OFFLOAD_OPTIMIZER_DEVICE"] = str(args.offload_optimizer_device).lower()
|
| 462 |
+
if args.offload_param_device is not None:
|
| 463 |
+
current_env["ACCELERATE_DEEPSPEED_OFFLOAD_PARAM_DEVICE"] = str(args.offload_param_device).lower()
|
| 464 |
+
if args.zero3_init_flag is not None:
|
| 465 |
+
current_env["ACCELERATE_DEEPSPEED_ZERO3_INIT"] = str(args.zero3_init_flag).lower()
|
| 466 |
+
if args.zero3_save_16bit_model is not None:
|
| 467 |
+
current_env["ACCELERATE_DEEPSPEED_ZERO3_SAVE_16BIT_MODEL"] = str(args.zero3_save_16bit_model).lower()
|
| 468 |
+
if args.deepspeed_config_file is not None:
|
| 469 |
+
current_env["ACCELERATE_DEEPSPEED_CONFIG_FILE"] = str(args.deepspeed_config_file)
|
| 470 |
+
if args.enable_cpu_affinity:
|
| 471 |
+
current_env["ACCELERATE_CPU_AFFINITY"] = "1"
|
| 472 |
+
if args.deepspeed_moe_layer_cls_names is not None:
|
| 473 |
+
current_env["ACCELERATE_DEEPSPEED_MOE_LAYER_CLS_NAMES"] = str(args.deepspeed_moe_layer_cls_names)
|
| 474 |
+
return cmd, current_env
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
def prepare_tpu(
|
| 478 |
+
args: argparse.Namespace, current_env: dict[str, str], pod: bool = False
|
| 479 |
+
) -> tuple[argparse.Namespace, dict[str, str]]:
|
| 480 |
+
"""
|
| 481 |
+
Prepares and returns an environment with the correct TPU environment variables.
|
| 482 |
+
"""
|
| 483 |
+
if args.mixed_precision == "bf16" and is_torch_xla_available(check_is_tpu=True):
|
| 484 |
+
if args.downcast_bf16:
|
| 485 |
+
current_env["XLA_DOWNCAST_BF16"] = "1"
|
| 486 |
+
else:
|
| 487 |
+
current_env["XLA_USE_BF16"] = "1"
|
| 488 |
+
if args.debug:
|
| 489 |
+
current_env["ACCELERATE_DEBUG_MODE"] = "true"
|
| 490 |
+
if pod:
|
| 491 |
+
# Take explicit args and set them up for XLA
|
| 492 |
+
args.vm = args.tpu_vm
|
| 493 |
+
args.tpu = args.tpu_name
|
| 494 |
+
return args, current_env
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
def _convert_nargs_to_dict(nargs: list[str]) -> dict[str, str]:
|
| 498 |
+
if len(nargs) < 0:
|
| 499 |
+
return {}
|
| 500 |
+
# helper function to infer type for argsparser
|
| 501 |
+
|
| 502 |
+
def _infer_type(s):
|
| 503 |
+
try:
|
| 504 |
+
s = float(s)
|
| 505 |
+
|
| 506 |
+
if s // 1 == s:
|
| 507 |
+
return int(s)
|
| 508 |
+
return s
|
| 509 |
+
except ValueError:
|
| 510 |
+
return s
|
| 511 |
+
|
| 512 |
+
parser = argparse.ArgumentParser()
|
| 513 |
+
_, unknown = parser.parse_known_args(nargs)
|
| 514 |
+
for index, argument in enumerate(unknown):
|
| 515 |
+
if argument.startswith(("-", "--")):
|
| 516 |
+
action = None
|
| 517 |
+
if index + 1 < len(unknown): # checks if next index would be in list
|
| 518 |
+
if unknown[index + 1].startswith(("-", "--")): # checks if next element is an key
|
| 519 |
+
# raise an error if element is store_true or store_false
|
| 520 |
+
raise ValueError(
|
| 521 |
+
"SageMaker doesn’t support argparse actions for `store_true` or `store_false`. Please define explicit types"
|
| 522 |
+
)
|
| 523 |
+
else: # raise an error if last element is store_true or store_false
|
| 524 |
+
raise ValueError(
|
| 525 |
+
"SageMaker doesn’t support argparse actions for `store_true` or `store_false`. Please define explicit types"
|
| 526 |
+
)
|
| 527 |
+
# adds argument to parser based on action_store true
|
| 528 |
+
if action is None:
|
| 529 |
+
parser.add_argument(argument, type=_infer_type)
|
| 530 |
+
else:
|
| 531 |
+
parser.add_argument(argument, action=action)
|
| 532 |
+
|
| 533 |
+
return {
|
| 534 |
+
key: (literal_eval(value) if value in ("True", "False") else value)
|
| 535 |
+
for key, value in parser.parse_args(nargs).__dict__.items()
|
| 536 |
+
}
|
| 537 |
+
|
| 538 |
+
|
| 539 |
+
def prepare_sagemager_args_inputs(
|
| 540 |
+
sagemaker_config: SageMakerConfig, args: argparse.Namespace
|
| 541 |
+
) -> tuple[argparse.Namespace, dict[str, Any]]:
|
| 542 |
+
# configure environment
|
| 543 |
+
print("Configuring Amazon SageMaker environment")
|
| 544 |
+
os.environ["AWS_DEFAULT_REGION"] = sagemaker_config.region
|
| 545 |
+
|
| 546 |
+
# configure credentials
|
| 547 |
+
if sagemaker_config.profile is not None:
|
| 548 |
+
os.environ["AWS_PROFILE"] = sagemaker_config.profile
|
| 549 |
+
elif args.aws_access_key_id is not None and args.aws_secret_access_key is not None:
|
| 550 |
+
os.environ["AWS_ACCESS_KEY_ID"] = args.aws_access_key_id
|
| 551 |
+
os.environ["AWS_SECRET_ACCESS_KEY"] = args.aws_secret_access_key
|
| 552 |
+
else:
|
| 553 |
+
raise OSError("You need to provide an aws_access_key_id and aws_secret_access_key when not using aws_profile")
|
| 554 |
+
|
| 555 |
+
# extract needed arguments
|
| 556 |
+
source_dir = os.path.dirname(args.training_script)
|
| 557 |
+
if not source_dir: # checks if string is empty
|
| 558 |
+
source_dir = "."
|
| 559 |
+
entry_point = os.path.basename(args.training_script)
|
| 560 |
+
if not entry_point.endswith(".py"):
|
| 561 |
+
raise ValueError(f'Your training script should be a python script and not "{entry_point}"')
|
| 562 |
+
|
| 563 |
+
print("Converting Arguments to Hyperparameters")
|
| 564 |
+
hyperparameters = _convert_nargs_to_dict(args.training_script_args)
|
| 565 |
+
|
| 566 |
+
try:
|
| 567 |
+
mixed_precision = PrecisionType(args.mixed_precision.lower())
|
| 568 |
+
except ValueError:
|
| 569 |
+
raise ValueError(
|
| 570 |
+
f"Unknown mixed_precision mode: {args.mixed_precision.lower()}. Choose between {PrecisionType.list()}."
|
| 571 |
+
)
|
| 572 |
+
|
| 573 |
+
try:
|
| 574 |
+
dynamo_backend = DynamoBackend(args.dynamo_backend.upper())
|
| 575 |
+
except ValueError:
|
| 576 |
+
raise ValueError(
|
| 577 |
+
f"Unknown dynamo backend: {args.dynamo_backend.upper()}. Choose between {DynamoBackend.list()}."
|
| 578 |
+
)
|
| 579 |
+
|
| 580 |
+
# Environment variables to be set for use during training job
|
| 581 |
+
environment = {
|
| 582 |
+
"ACCELERATE_USE_SAGEMAKER": "true",
|
| 583 |
+
"ACCELERATE_MIXED_PRECISION": str(mixed_precision),
|
| 584 |
+
"ACCELERATE_DYNAMO_BACKEND": dynamo_backend.value,
|
| 585 |
+
"ACCELERATE_DYNAMO_MODE": args.dynamo_mode,
|
| 586 |
+
"ACCELERATE_DYNAMO_USE_FULLGRAPH": str(args.dynamo_use_fullgraph),
|
| 587 |
+
"ACCELERATE_DYNAMO_USE_DYNAMIC": str(args.dynamo_use_dynamic),
|
| 588 |
+
"ACCELERATE_SAGEMAKER_DISTRIBUTED_TYPE": sagemaker_config.distributed_type.value,
|
| 589 |
+
}
|
| 590 |
+
if args.mixed_precision.lower() == "fp8":
|
| 591 |
+
if not is_fp8_available():
|
| 592 |
+
raise RuntimeError(
|
| 593 |
+
"FP8 is not available on this machine. Please ensure that either Transformer Engine or MSAMP is installed."
|
| 594 |
+
)
|
| 595 |
+
environment = setup_fp8_env(args, environment)
|
| 596 |
+
# configure distribution set up
|
| 597 |
+
distribution = None
|
| 598 |
+
if sagemaker_config.distributed_type == SageMakerDistributedType.DATA_PARALLEL:
|
| 599 |
+
distribution = {"smdistributed": {"dataparallel": {"enabled": True}}}
|
| 600 |
+
|
| 601 |
+
# configure sagemaker inputs
|
| 602 |
+
sagemaker_inputs = None
|
| 603 |
+
if sagemaker_config.sagemaker_inputs_file is not None:
|
| 604 |
+
print(f"Loading SageMaker Inputs from {sagemaker_config.sagemaker_inputs_file} file")
|
| 605 |
+
sagemaker_inputs = {}
|
| 606 |
+
with open(sagemaker_config.sagemaker_inputs_file) as file:
|
| 607 |
+
for i, line in enumerate(file):
|
| 608 |
+
if i == 0:
|
| 609 |
+
continue
|
| 610 |
+
l = line.split("\t")
|
| 611 |
+
sagemaker_inputs[l[0]] = l[1].strip()
|
| 612 |
+
print(f"Loaded SageMaker Inputs: {sagemaker_inputs}")
|
| 613 |
+
|
| 614 |
+
# configure sagemaker metrics
|
| 615 |
+
sagemaker_metrics = None
|
| 616 |
+
if sagemaker_config.sagemaker_metrics_file is not None:
|
| 617 |
+
print(f"Loading SageMaker Metrics from {sagemaker_config.sagemaker_metrics_file} file")
|
| 618 |
+
sagemaker_metrics = []
|
| 619 |
+
with open(sagemaker_config.sagemaker_metrics_file) as file:
|
| 620 |
+
for i, line in enumerate(file):
|
| 621 |
+
if i == 0:
|
| 622 |
+
continue
|
| 623 |
+
l = line.split("\t")
|
| 624 |
+
metric_dict = {
|
| 625 |
+
"Name": l[0],
|
| 626 |
+
"Regex": l[1].strip(),
|
| 627 |
+
}
|
| 628 |
+
sagemaker_metrics.append(metric_dict)
|
| 629 |
+
print(f"Loaded SageMaker Metrics: {sagemaker_metrics}")
|
| 630 |
+
|
| 631 |
+
# configure session
|
| 632 |
+
print("Creating Estimator")
|
| 633 |
+
args = {
|
| 634 |
+
"image_uri": sagemaker_config.image_uri,
|
| 635 |
+
"entry_point": entry_point,
|
| 636 |
+
"source_dir": source_dir,
|
| 637 |
+
"role": sagemaker_config.iam_role_name,
|
| 638 |
+
"transformers_version": sagemaker_config.transformers_version,
|
| 639 |
+
"pytorch_version": sagemaker_config.pytorch_version,
|
| 640 |
+
"py_version": sagemaker_config.py_version,
|
| 641 |
+
"base_job_name": sagemaker_config.base_job_name,
|
| 642 |
+
"instance_count": sagemaker_config.num_machines,
|
| 643 |
+
"instance_type": sagemaker_config.ec2_instance_type,
|
| 644 |
+
"debugger_hook_config": False,
|
| 645 |
+
"distribution": distribution,
|
| 646 |
+
"hyperparameters": hyperparameters,
|
| 647 |
+
"environment": environment,
|
| 648 |
+
"metric_definitions": sagemaker_metrics,
|
| 649 |
+
}
|
| 650 |
+
|
| 651 |
+
if sagemaker_config.additional_args is not None:
|
| 652 |
+
args = merge_dicts(sagemaker_config.additional_args, args)
|
| 653 |
+
return args, sagemaker_inputs
|
| 654 |
+
|
| 655 |
+
|
| 656 |
+
def env_var_path_add(env_var_name, path_to_add):
|
| 657 |
+
"""
|
| 658 |
+
Extends a path-based environment variable's value with a new path and returns the updated value. It's up to the
|
| 659 |
+
caller to set it in os.environ.
|
| 660 |
+
"""
|
| 661 |
+
paths = [p for p in os.environ.get(env_var_name, "").split(":") if len(p) > 0]
|
| 662 |
+
paths.append(str(path_to_add))
|
| 663 |
+
return ":".join(paths)
|
| 664 |
+
|
| 665 |
+
|
| 666 |
+
class PrepareForLaunch:
|
| 667 |
+
"""
|
| 668 |
+
Prepare a function that will launched in a distributed setup.
|
| 669 |
+
|
| 670 |
+
Args:
|
| 671 |
+
launcher (`Callable`):
|
| 672 |
+
The function to launch.
|
| 673 |
+
distributed_type ([`~state.DistributedType`]):
|
| 674 |
+
The distributed type to prepare for.
|
| 675 |
+
debug (`bool`, *optional*, defaults to `False`):
|
| 676 |
+
Whether or not this is a debug launch.
|
| 677 |
+
"""
|
| 678 |
+
|
| 679 |
+
def __init__(self, launcher, distributed_type="NO", debug=False):
|
| 680 |
+
self.launcher = launcher
|
| 681 |
+
self.distributed_type = DistributedType(distributed_type)
|
| 682 |
+
self.debug = debug
|
| 683 |
+
|
| 684 |
+
def __call__(self, index, *args):
|
| 685 |
+
if self.debug:
|
| 686 |
+
world_size = int(os.environ.get("WORLD_SIZE"))
|
| 687 |
+
rdv_file = os.environ.get("ACCELERATE_DEBUG_RDV_FILE")
|
| 688 |
+
torch.distributed.init_process_group(
|
| 689 |
+
"gloo",
|
| 690 |
+
rank=index,
|
| 691 |
+
store=torch.distributed.FileStore(rdv_file, world_size),
|
| 692 |
+
world_size=world_size,
|
| 693 |
+
)
|
| 694 |
+
elif self.distributed_type in (
|
| 695 |
+
DistributedType.MULTI_GPU,
|
| 696 |
+
DistributedType.MULTI_MLU,
|
| 697 |
+
DistributedType.MULTI_MUSA,
|
| 698 |
+
DistributedType.MULTI_NPU,
|
| 699 |
+
DistributedType.MULTI_XPU,
|
| 700 |
+
DistributedType.MULTI_CPU,
|
| 701 |
+
):
|
| 702 |
+
# Prepare the environment for torch.distributed
|
| 703 |
+
os.environ["LOCAL_RANK"] = str(index)
|
| 704 |
+
nproc = int(os.environ.get("NPROC", 1))
|
| 705 |
+
node_rank = int(os.environ.get("NODE_RANK", 0))
|
| 706 |
+
os.environ["RANK"] = str(nproc * node_rank + index)
|
| 707 |
+
|
| 708 |
+
os.environ["FORK_LAUNCHED"] = str(1)
|
| 709 |
+
self.launcher(*args)
|
venv/lib/python3.11/site-packages/accelerate/utils/megatron_lm.py
ADDED
|
@@ -0,0 +1,1424 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import argparse
|
| 16 |
+
import math
|
| 17 |
+
import os
|
| 18 |
+
from abc import ABC
|
| 19 |
+
from functools import partial
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
import torch.nn.functional as F
|
| 23 |
+
from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
|
| 24 |
+
from torch.nn.parallel.distributed import DistributedDataParallel as torchDDP
|
| 25 |
+
|
| 26 |
+
from ..optimizer import AcceleratedOptimizer
|
| 27 |
+
from ..scheduler import AcceleratedScheduler
|
| 28 |
+
from .imports import is_megatron_lm_available
|
| 29 |
+
from .operations import recursively_apply, send_to_device
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
if is_megatron_lm_available():
|
| 33 |
+
from megatron.core import mpu, tensor_parallel
|
| 34 |
+
from megatron.core.distributed import DistributedDataParallel as LocalDDP
|
| 35 |
+
from megatron.core.distributed import finalize_model_grads
|
| 36 |
+
from megatron.core.enums import ModelType
|
| 37 |
+
from megatron.core.num_microbatches_calculator import get_num_microbatches
|
| 38 |
+
from megatron.core.optimizer import get_megatron_optimizer
|
| 39 |
+
from megatron.core.parallel_state import get_tensor_model_parallel_group, get_tensor_model_parallel_src_rank
|
| 40 |
+
from megatron.core.pipeline_parallel import get_forward_backward_func
|
| 41 |
+
from megatron.core.utils import get_model_config
|
| 42 |
+
from megatron.inference.text_generation.communication import broadcast_int_list, broadcast_tensor
|
| 43 |
+
from megatron.inference.text_generation.generation import (
|
| 44 |
+
beam_search_and_return_on_first_stage,
|
| 45 |
+
generate_tokens_probs_and_return_on_first_stage,
|
| 46 |
+
)
|
| 47 |
+
from megatron.legacy.data.dataset_utils import build_train_valid_test_datasets
|
| 48 |
+
from megatron.legacy.model import BertModel, Float16Module, GPTModel, T5Model
|
| 49 |
+
from megatron.legacy.model.classification import Classification
|
| 50 |
+
from megatron.training import (
|
| 51 |
+
get_args,
|
| 52 |
+
get_tensorboard_writer,
|
| 53 |
+
get_tokenizer,
|
| 54 |
+
print_rank_last,
|
| 55 |
+
)
|
| 56 |
+
from megatron.training.arguments import (
|
| 57 |
+
_add_data_args,
|
| 58 |
+
_add_validation_args,
|
| 59 |
+
core_transformer_config_from_args,
|
| 60 |
+
parse_args,
|
| 61 |
+
validate_args,
|
| 62 |
+
)
|
| 63 |
+
from megatron.training.checkpointing import load_args_from_checkpoint, load_checkpoint, save_checkpoint
|
| 64 |
+
from megatron.training.global_vars import set_global_variables
|
| 65 |
+
from megatron.training.initialize import (
|
| 66 |
+
_compile_dependencies,
|
| 67 |
+
_init_autoresume,
|
| 68 |
+
_initialize_distributed,
|
| 69 |
+
_set_random_seed,
|
| 70 |
+
set_jit_fusion_options,
|
| 71 |
+
write_args_to_tensorboard,
|
| 72 |
+
)
|
| 73 |
+
from megatron.training.tokenizer.tokenizer import _vocab_size_with_padding
|
| 74 |
+
from megatron.training.training import (
|
| 75 |
+
build_train_valid_test_data_iterators,
|
| 76 |
+
get_optimizer_param_scheduler,
|
| 77 |
+
num_floating_point_operations,
|
| 78 |
+
setup_model_and_optimizer,
|
| 79 |
+
train_step,
|
| 80 |
+
training_log,
|
| 81 |
+
)
|
| 82 |
+
from megatron.training.utils import (
|
| 83 |
+
average_losses_across_data_parallel_group,
|
| 84 |
+
calc_params_l2_norm,
|
| 85 |
+
get_ltor_masks_and_position_ids,
|
| 86 |
+
unwrap_model,
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
# model utilities
|
| 91 |
+
def model_provider_func(pre_process=True, post_process=True, add_encoder=True, add_decoder=True):
|
| 92 |
+
"""Build the model."""
|
| 93 |
+
args = get_args()
|
| 94 |
+
mode = "pre-training" if args.pretraining_flag else "fine-tuning"
|
| 95 |
+
if args.rank == 0:
|
| 96 |
+
print(f"Building {args.model_type_name} model in the {mode} mode.")
|
| 97 |
+
print(
|
| 98 |
+
"The Megatron LM model weights are initialized at random in `accelerator.prepare`. "
|
| 99 |
+
"Please use `accelerator.load_checkpoint` to load a pre-trained checkpoint matching the distributed setup."
|
| 100 |
+
)
|
| 101 |
+
config = core_transformer_config_from_args(args)
|
| 102 |
+
if args.model_type_name == "bert":
|
| 103 |
+
if args.pretraining_flag:
|
| 104 |
+
num_tokentypes = 2 if args.bert_binary_head else 0
|
| 105 |
+
model = BertModel(
|
| 106 |
+
config=config,
|
| 107 |
+
num_tokentypes=num_tokentypes,
|
| 108 |
+
add_binary_head=args.bert_binary_head,
|
| 109 |
+
parallel_output=True,
|
| 110 |
+
pre_process=pre_process,
|
| 111 |
+
post_process=post_process,
|
| 112 |
+
)
|
| 113 |
+
else:
|
| 114 |
+
model = Classification(
|
| 115 |
+
config=config,
|
| 116 |
+
num_classes=args.num_labels,
|
| 117 |
+
num_tokentypes=2,
|
| 118 |
+
pre_process=pre_process,
|
| 119 |
+
post_process=post_process,
|
| 120 |
+
)
|
| 121 |
+
elif args.model_type_name == "gpt":
|
| 122 |
+
model = GPTModel(
|
| 123 |
+
config=config,
|
| 124 |
+
num_tokentypes=0,
|
| 125 |
+
parallel_output=True,
|
| 126 |
+
pre_process=pre_process,
|
| 127 |
+
post_process=post_process,
|
| 128 |
+
)
|
| 129 |
+
elif args.model_type_name == "t5":
|
| 130 |
+
model = T5Model(
|
| 131 |
+
config=config,
|
| 132 |
+
num_tokentypes=0,
|
| 133 |
+
parallel_output=True,
|
| 134 |
+
pre_process=pre_process,
|
| 135 |
+
post_process=post_process,
|
| 136 |
+
add_encoder=add_encoder,
|
| 137 |
+
add_decoder=add_decoder,
|
| 138 |
+
)
|
| 139 |
+
else:
|
| 140 |
+
raise ValueError(f"Unsupported model type: {args.model_type_name}")
|
| 141 |
+
return model
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def prepare_model_optimizer_scheduler(accelerator):
|
| 145 |
+
accelerator.print("Preparing model optimizer scheduler")
|
| 146 |
+
args = get_args()
|
| 147 |
+
if accelerator.state.megatron_lm_plugin.custom_prepare_model_function is not None:
|
| 148 |
+
if accelerator.state.megatron_lm_plugin.custom_model_provider_function is None:
|
| 149 |
+
raise ValueError(
|
| 150 |
+
"You must provide a `custom_model_provider_function` when using a `custom_prepare_model_function`."
|
| 151 |
+
)
|
| 152 |
+
custom_model_provider_func = accelerator.state.megatron_lm_plugin.custom_model_provider_function
|
| 153 |
+
model = accelerator.state.megatron_lm_plugin.custom_prepare_model_function(custom_model_provider_func)
|
| 154 |
+
optimizer = prepare_optimizer(accelerator, model)
|
| 155 |
+
scheduler = prepare_scheduler(accelerator, optimizer, scheduler=None)
|
| 156 |
+
else:
|
| 157 |
+
model_type = ModelType.encoder_or_decoder
|
| 158 |
+
if args.model_type_name == "t5":
|
| 159 |
+
model_type = ModelType.encoder_and_decoder
|
| 160 |
+
model_provider_func_ = model_provider_func
|
| 161 |
+
if accelerator.state.megatron_lm_plugin.custom_model_provider_function is not None:
|
| 162 |
+
model_provider_func_ = accelerator.state.megatron_lm_plugin.custom_model_provider_function
|
| 163 |
+
(model, optimizer, scheduler) = setup_model_and_optimizer(
|
| 164 |
+
model_provider_func_,
|
| 165 |
+
model_type,
|
| 166 |
+
no_wd_decay_cond=args.no_wd_decay_cond,
|
| 167 |
+
scale_lr_cond=args.scale_lr_cond,
|
| 168 |
+
lr_mult=args.lr_mult,
|
| 169 |
+
)
|
| 170 |
+
args.model_len = len(model)
|
| 171 |
+
return model, optimizer, scheduler
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
# dataloader utilities
|
| 175 |
+
class MegatronLMDummyDataLoader:
|
| 176 |
+
"""
|
| 177 |
+
Dummy dataloader presents model parameters or param groups, this is primarily used to follow conventional training
|
| 178 |
+
|
| 179 |
+
Args:
|
| 180 |
+
**dataset_kwargs: Megatron data arguments.
|
| 181 |
+
"""
|
| 182 |
+
|
| 183 |
+
def __init__(self, **dataset_kwargs):
|
| 184 |
+
parser = argparse.ArgumentParser()
|
| 185 |
+
parser = _add_data_args(parser)
|
| 186 |
+
parser = _add_validation_args(parser)
|
| 187 |
+
data_args = parser.parse_known_args()
|
| 188 |
+
self.dataset_args = vars(data_args[0])
|
| 189 |
+
self.dataset_args.update(dataset_kwargs)
|
| 190 |
+
self.dataset_args["megatron_dataset_flag"] = True
|
| 191 |
+
|
| 192 |
+
def set_megatron_data_args(self):
|
| 193 |
+
args = get_args()
|
| 194 |
+
for key, value in self.dataset_args.items():
|
| 195 |
+
old_value = getattr(args, key, "")
|
| 196 |
+
if old_value != value:
|
| 197 |
+
print(
|
| 198 |
+
f"WARNING: MegatronLMDummyDataLoader overriding arguments for {key}:{old_value} with {key}:{value}"
|
| 199 |
+
)
|
| 200 |
+
setattr(args, key, value)
|
| 201 |
+
|
| 202 |
+
def get_train_valid_test_datasets_provider(self, accelerator):
|
| 203 |
+
def train_valid_test_datasets_provider(train_val_test_num_samples):
|
| 204 |
+
"""Build train, valid, and test datasets."""
|
| 205 |
+
args = get_args()
|
| 206 |
+
dataset_args = {
|
| 207 |
+
"data_prefix": args.data_path if isinstance(args.data_path, (list, tuple)) else [args.data_path],
|
| 208 |
+
"splits_string": args.split,
|
| 209 |
+
"train_valid_test_num_samples": train_val_test_num_samples,
|
| 210 |
+
"seed": args.seed,
|
| 211 |
+
}
|
| 212 |
+
if args.model_type_name == "bert":
|
| 213 |
+
dataset_args.update(
|
| 214 |
+
{
|
| 215 |
+
"max_seq_length": args.seq_length,
|
| 216 |
+
"binary_head": args.bert_binary_head,
|
| 217 |
+
}
|
| 218 |
+
)
|
| 219 |
+
elif args.model_type_name == "gpt":
|
| 220 |
+
dataset_args.update(
|
| 221 |
+
{
|
| 222 |
+
"max_seq_length": args.seq_length,
|
| 223 |
+
}
|
| 224 |
+
)
|
| 225 |
+
elif args.model_type_name == "t5":
|
| 226 |
+
dataset_args.update(
|
| 227 |
+
{
|
| 228 |
+
"max_seq_length": args.encoder_seq_length,
|
| 229 |
+
"max_seq_length_dec": args.decoder_seq_length,
|
| 230 |
+
"dataset_type": "t5",
|
| 231 |
+
}
|
| 232 |
+
)
|
| 233 |
+
else:
|
| 234 |
+
raise ValueError(f"Unsupported model type: {args.model_type_name}")
|
| 235 |
+
train_ds, valid_ds, test_ds = build_train_valid_test_datasets(**dataset_args)
|
| 236 |
+
return train_ds, valid_ds, test_ds
|
| 237 |
+
|
| 238 |
+
if accelerator.state.megatron_lm_plugin.custom_megatron_datasets_provider_function is not None:
|
| 239 |
+
return accelerator.state.megatron_lm_plugin.custom_megatron_datasets_provider_function
|
| 240 |
+
try:
|
| 241 |
+
args = get_args()
|
| 242 |
+
# Use '--no-use-pep517 -e' to pip install nvidia's megatron from source
|
| 243 |
+
if args.model_type_name == "bert":
|
| 244 |
+
from pretrain_bert import train_valid_test_datasets_provider
|
| 245 |
+
|
| 246 |
+
train_valid_test_datasets_provider.is_distributed = True
|
| 247 |
+
return train_valid_test_datasets_provider
|
| 248 |
+
elif args.model_type_name == "gpt":
|
| 249 |
+
from pretrain_gpt import train_valid_test_datasets_provider
|
| 250 |
+
|
| 251 |
+
train_valid_test_datasets_provider.is_distributed = True
|
| 252 |
+
return train_valid_test_datasets_provider
|
| 253 |
+
elif args.model_type_name == "t5":
|
| 254 |
+
from pretrain_t5 import train_valid_test_datasets_provider
|
| 255 |
+
|
| 256 |
+
train_valid_test_datasets_provider.is_distributed = True
|
| 257 |
+
return train_valid_test_datasets_provider
|
| 258 |
+
except ImportError:
|
| 259 |
+
pass
|
| 260 |
+
return train_valid_test_datasets_provider
|
| 261 |
+
|
| 262 |
+
def build_train_valid_test_data_iterators(self, accelerator):
|
| 263 |
+
args = get_args()
|
| 264 |
+
|
| 265 |
+
train_valid_test_dataset_provider = self.get_train_valid_test_datasets_provider(accelerator)
|
| 266 |
+
if args.virtual_pipeline_model_parallel_size is not None:
|
| 267 |
+
train_data_iterator = []
|
| 268 |
+
valid_data_iterator = []
|
| 269 |
+
test_data_iterator = []
|
| 270 |
+
for i in range(getattr(args, "model_len", 0)):
|
| 271 |
+
mpu.set_virtual_pipeline_model_parallel_rank(i)
|
| 272 |
+
iterators = build_train_valid_test_data_iterators(train_valid_test_dataset_provider)
|
| 273 |
+
train_data_iterator.append(iterators[0])
|
| 274 |
+
valid_data_iterator.append(iterators[1])
|
| 275 |
+
test_data_iterator.append(iterators[2])
|
| 276 |
+
else:
|
| 277 |
+
train_data_iterator, valid_data_iterator, test_data_iterator = build_train_valid_test_data_iterators(
|
| 278 |
+
train_valid_test_dataset_provider
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
return train_data_iterator, valid_data_iterator, test_data_iterator
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def _handle_megatron_data_iterator(accelerator, data_iterator):
|
| 285 |
+
class DummyMegatronDataloader:
|
| 286 |
+
def __iter__(self):
|
| 287 |
+
return self
|
| 288 |
+
|
| 289 |
+
def __next__(self):
|
| 290 |
+
return {}
|
| 291 |
+
|
| 292 |
+
is_data_iterator_empty = data_iterator is None
|
| 293 |
+
is_src_data_iterator_empty = torch.tensor(is_data_iterator_empty, dtype=torch.bool, device=accelerator.device)
|
| 294 |
+
torch.distributed.broadcast(
|
| 295 |
+
is_src_data_iterator_empty, get_tensor_model_parallel_src_rank(), group=get_tensor_model_parallel_group()
|
| 296 |
+
)
|
| 297 |
+
if not is_src_data_iterator_empty and is_data_iterator_empty:
|
| 298 |
+
return DummyMegatronDataloader()
|
| 299 |
+
return data_iterator
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def prepare_data_loader(accelerator, dataloader):
|
| 303 |
+
accelerator.print("Preparing dataloader")
|
| 304 |
+
args = get_args()
|
| 305 |
+
if not args.megatron_dataset_flag:
|
| 306 |
+
from ..data_loader import _PYTORCH_DATALOADER_KWARGS, prepare_data_loader
|
| 307 |
+
|
| 308 |
+
micro_batch_size = args.micro_batch_size * args.num_micro_batches
|
| 309 |
+
kwargs = {k: getattr(dataloader, k, _PYTORCH_DATALOADER_KWARGS[k]) for k in _PYTORCH_DATALOADER_KWARGS}
|
| 310 |
+
if kwargs["batch_size"] is None:
|
| 311 |
+
if isinstance(kwargs["sampler"], torch.utils.data.BatchSampler):
|
| 312 |
+
kwargs["sampler"].batch_size = micro_batch_size
|
| 313 |
+
else:
|
| 314 |
+
del kwargs["sampler"]
|
| 315 |
+
del kwargs["shuffle"]
|
| 316 |
+
del kwargs["batch_size"]
|
| 317 |
+
kwargs["batch_sampler"].batch_size = micro_batch_size
|
| 318 |
+
else:
|
| 319 |
+
del kwargs["batch_sampler"]
|
| 320 |
+
kwargs["batch_size"] = micro_batch_size
|
| 321 |
+
|
| 322 |
+
dataloader = torch.utils.data.DataLoader(dataloader.dataset, **kwargs)
|
| 323 |
+
# split_batches:
|
| 324 |
+
# Megatron only needs to fetch different data between different dp groups,
|
| 325 |
+
# and does not need to split the data within the dp group.
|
| 326 |
+
return prepare_data_loader(
|
| 327 |
+
dataloader,
|
| 328 |
+
accelerator.device,
|
| 329 |
+
num_processes=mpu.get_data_parallel_world_size(),
|
| 330 |
+
process_index=mpu.get_data_parallel_rank(),
|
| 331 |
+
split_batches=False,
|
| 332 |
+
put_on_device=True,
|
| 333 |
+
rng_types=accelerator.rng_types.copy(),
|
| 334 |
+
dispatch_batches=accelerator.dispatch_batches,
|
| 335 |
+
)
|
| 336 |
+
else:
|
| 337 |
+
if args.consumed_samples is not None:
|
| 338 |
+
(
|
| 339 |
+
args.consumed_train_samples,
|
| 340 |
+
args.consumed_valid_samples,
|
| 341 |
+
args.consumed_test_samples,
|
| 342 |
+
) = args.consumed_samples
|
| 343 |
+
else:
|
| 344 |
+
args.consumed_train_samples, args.consumed_valid_samples, args.consumed_test_samples = 0, 0, 0
|
| 345 |
+
args.micro_batch_size = args.micro_batch_size * args.num_micro_batches
|
| 346 |
+
# In order to be compatible with data in transform format,
|
| 347 |
+
# it needs to increase the size of mbs first,
|
| 348 |
+
# and then split the large batch data into some mbs.
|
| 349 |
+
(
|
| 350 |
+
train_data_iterator,
|
| 351 |
+
valid_data_iterator,
|
| 352 |
+
test_data_iterator,
|
| 353 |
+
) = dataloader.build_train_valid_test_data_iterators(accelerator)
|
| 354 |
+
args.micro_batch_size = args.micro_batch_size // args.num_micro_batches
|
| 355 |
+
|
| 356 |
+
train_data_iterator = _handle_megatron_data_iterator(
|
| 357 |
+
accelerator=accelerator, data_iterator=train_data_iterator
|
| 358 |
+
)
|
| 359 |
+
valid_data_iterator = _handle_megatron_data_iterator(
|
| 360 |
+
accelerator=accelerator, data_iterator=valid_data_iterator
|
| 361 |
+
)
|
| 362 |
+
test_data_iterator = _handle_megatron_data_iterator(accelerator=accelerator, data_iterator=test_data_iterator)
|
| 363 |
+
|
| 364 |
+
return train_data_iterator, valid_data_iterator, test_data_iterator
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
# optimizer utilities
|
| 368 |
+
class MegatronLMOptimizerWrapper(AcceleratedOptimizer):
|
| 369 |
+
def __init__(self, optimizer):
|
| 370 |
+
super().__init__(optimizer, device_placement=False, scaler=None)
|
| 371 |
+
|
| 372 |
+
def zero_grad(self, set_to_none=None):
|
| 373 |
+
pass # `model(**batch)` is doing that automatically. Therefore, it's implementation is not needed
|
| 374 |
+
|
| 375 |
+
def step(self):
|
| 376 |
+
pass # `model(**batch)` is doing that automatically. Therefore, it's implementation is not needed
|
| 377 |
+
|
| 378 |
+
@property
|
| 379 |
+
def step_was_skipped(self):
|
| 380 |
+
"""Whether or not the optimizer step was done, or skipped because of gradient overflow."""
|
| 381 |
+
return self.optimizer.skipped_iter
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def prepare_optimizer(accelerator, model):
|
| 385 |
+
accelerator.print("Preparing optimizer")
|
| 386 |
+
args = get_args()
|
| 387 |
+
return get_megatron_optimizer(model, args.no_wd_decay_cond, args.scale_lr_cond, args.lr_mult)
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
# scheduler utilities
|
| 391 |
+
class MegatronLMDummyScheduler:
|
| 392 |
+
"""
|
| 393 |
+
Dummy scheduler presents model parameters or param groups, this is primarily used to follow conventional training
|
| 394 |
+
loop when scheduler config is specified in the deepspeed config file.
|
| 395 |
+
|
| 396 |
+
Args:
|
| 397 |
+
optimizer (`torch.optim.optimizer.Optimizer`):
|
| 398 |
+
The optimizer to wrap.
|
| 399 |
+
total_num_steps (int):
|
| 400 |
+
Total number of steps.
|
| 401 |
+
warmup_num_steps (int):
|
| 402 |
+
Number of steps for warmup.
|
| 403 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 404 |
+
Other arguments.
|
| 405 |
+
"""
|
| 406 |
+
|
| 407 |
+
def __init__(self, optimizer, total_num_steps=None, warmup_num_steps=0, **kwargs):
|
| 408 |
+
self.optimizer = optimizer
|
| 409 |
+
self.total_num_steps = total_num_steps
|
| 410 |
+
self.warmup_num_steps = warmup_num_steps
|
| 411 |
+
self.kwargs = kwargs
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
class MegatronLMSchedulerWrapper(AcceleratedScheduler):
|
| 415 |
+
def __init__(self, scheduler, optimizers):
|
| 416 |
+
super().__init__(scheduler, optimizers)
|
| 417 |
+
|
| 418 |
+
def step(self, *args, **kwargs):
|
| 419 |
+
return # `model(**batch)` is doing that automatically. Therefore, it's implementation is not needed
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
def prepare_scheduler(accelerator, optimizer, scheduler):
|
| 423 |
+
accelerator.print("Preparing scheduler")
|
| 424 |
+
scheduler = get_optimizer_param_scheduler(optimizer)
|
| 425 |
+
return scheduler
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
class AbstractTrainStep(ABC):
|
| 429 |
+
"""Abstract class for batching, forward pass and loss handler."""
|
| 430 |
+
|
| 431 |
+
def __init__(self, name):
|
| 432 |
+
super().__init__()
|
| 433 |
+
self.name = name
|
| 434 |
+
|
| 435 |
+
def get_batch_func(self, accelerator, megatron_dataset_flag):
|
| 436 |
+
pass
|
| 437 |
+
|
| 438 |
+
def get_forward_step_func(self):
|
| 439 |
+
pass
|
| 440 |
+
|
| 441 |
+
def get_loss_func(self, accelerator):
|
| 442 |
+
pass
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
class BertTrainStep(AbstractTrainStep):
|
| 446 |
+
"""
|
| 447 |
+
Bert train step class.
|
| 448 |
+
|
| 449 |
+
Args:
|
| 450 |
+
args (`argparse.Namespace`): Megatron-LM arguments.
|
| 451 |
+
"""
|
| 452 |
+
|
| 453 |
+
def __init__(self, accelerator, args):
|
| 454 |
+
super().__init__("BertTrainStep")
|
| 455 |
+
self.get_batch = self.get_batch_func(accelerator, args.megatron_dataset_flag)
|
| 456 |
+
self.loss_func = self.get_loss_func(accelerator, args.pretraining_flag, args.num_labels)
|
| 457 |
+
self.forward_step = self.get_forward_step_func(args.pretraining_flag, args.bert_binary_head)
|
| 458 |
+
if not args.model_return_dict:
|
| 459 |
+
self.model_output_class = None
|
| 460 |
+
else:
|
| 461 |
+
from transformers.modeling_outputs import SequenceClassifierOutput
|
| 462 |
+
|
| 463 |
+
self.model_output_class = SequenceClassifierOutput
|
| 464 |
+
|
| 465 |
+
def get_batch_func(self, accelerator, megatron_dataset_flag):
|
| 466 |
+
def get_batch_megatron(data_iterator):
|
| 467 |
+
"""Build the batch."""
|
| 468 |
+
|
| 469 |
+
# Items and their type.
|
| 470 |
+
keys = ["text", "types", "labels", "is_random", "loss_mask", "padding_mask"]
|
| 471 |
+
datatype = torch.int64
|
| 472 |
+
|
| 473 |
+
# Broadcast data.
|
| 474 |
+
if data_iterator is not None:
|
| 475 |
+
data = next(data_iterator)
|
| 476 |
+
else:
|
| 477 |
+
data = None
|
| 478 |
+
data_b = tensor_parallel.broadcast_data(keys, data, datatype)
|
| 479 |
+
|
| 480 |
+
# Unpack.
|
| 481 |
+
tokens = data_b["text"].long()
|
| 482 |
+
types = data_b["types"].long()
|
| 483 |
+
sentence_order = data_b["is_random"].long()
|
| 484 |
+
loss_mask = data_b["loss_mask"].float()
|
| 485 |
+
lm_labels = data_b["labels"].long()
|
| 486 |
+
padding_mask = data_b["padding_mask"].long()
|
| 487 |
+
|
| 488 |
+
return tokens, types, sentence_order, loss_mask, lm_labels, padding_mask
|
| 489 |
+
|
| 490 |
+
def get_batch_transformer(data_iterator):
|
| 491 |
+
"""Build the batch."""
|
| 492 |
+
data = next(data_iterator)
|
| 493 |
+
data = send_to_device(data, torch.cuda.current_device())
|
| 494 |
+
|
| 495 |
+
# Unpack.
|
| 496 |
+
tokens = data["input_ids"].long()
|
| 497 |
+
padding_mask = data["attention_mask"].long()
|
| 498 |
+
if "token_type_ids" in data:
|
| 499 |
+
types = data["token_type_ids"].long()
|
| 500 |
+
else:
|
| 501 |
+
types = None
|
| 502 |
+
if "labels" in data:
|
| 503 |
+
lm_labels = data["labels"].long()
|
| 504 |
+
loss_mask = (data["labels"] != -100).to(torch.float)
|
| 505 |
+
else:
|
| 506 |
+
lm_labels = None
|
| 507 |
+
loss_mask = None
|
| 508 |
+
if "next_sentence_label" in data:
|
| 509 |
+
sentence_order = data["next_sentence_label"].long()
|
| 510 |
+
else:
|
| 511 |
+
sentence_order = None
|
| 512 |
+
|
| 513 |
+
return tokens, types, sentence_order, loss_mask, lm_labels, padding_mask
|
| 514 |
+
|
| 515 |
+
if accelerator.state.megatron_lm_plugin.custom_get_batch_function is not None:
|
| 516 |
+
return accelerator.state.megatron_lm_plugin.custom_get_batch_function
|
| 517 |
+
if megatron_dataset_flag:
|
| 518 |
+
try:
|
| 519 |
+
# Use '--no-use-pep517 -e' to pip install nvidia's megatron from source
|
| 520 |
+
from pretrain_bert import get_batch
|
| 521 |
+
|
| 522 |
+
return get_batch
|
| 523 |
+
except ImportError:
|
| 524 |
+
pass
|
| 525 |
+
return get_batch_megatron
|
| 526 |
+
else:
|
| 527 |
+
return get_batch_transformer
|
| 528 |
+
|
| 529 |
+
def get_loss_func(self, accelerator, pretraining_flag, num_labels):
|
| 530 |
+
def loss_func_pretrain(loss_mask, sentence_order, output_tensor):
|
| 531 |
+
lm_loss_, sop_logits = output_tensor
|
| 532 |
+
|
| 533 |
+
lm_loss_ = lm_loss_.float()
|
| 534 |
+
loss_mask = loss_mask.float()
|
| 535 |
+
lm_loss = torch.sum(lm_loss_.view(-1) * loss_mask.reshape(-1)) / loss_mask.sum()
|
| 536 |
+
|
| 537 |
+
if sop_logits is not None:
|
| 538 |
+
sop_loss = F.cross_entropy(sop_logits.view(-1, 2).float(), sentence_order.view(-1), ignore_index=-1)
|
| 539 |
+
sop_loss = sop_loss.float()
|
| 540 |
+
loss = lm_loss + sop_loss
|
| 541 |
+
averaged_losses = average_losses_across_data_parallel_group([lm_loss, sop_loss])
|
| 542 |
+
return loss, {"lm loss": averaged_losses[0], "sop loss": averaged_losses[1]}
|
| 543 |
+
|
| 544 |
+
else:
|
| 545 |
+
loss = lm_loss
|
| 546 |
+
averaged_losses = average_losses_across_data_parallel_group([lm_loss])
|
| 547 |
+
return loss, {"lm loss": averaged_losses[0]}
|
| 548 |
+
|
| 549 |
+
def loss_func_finetune(labels, logits):
|
| 550 |
+
if num_labels == 1:
|
| 551 |
+
# We are doing regression
|
| 552 |
+
loss_fct = MSELoss()
|
| 553 |
+
loss = loss_fct(logits.view(-1), labels.view(-1))
|
| 554 |
+
elif self.num_labels > 1 and (labels.dtype in (torch.long, torch.int)):
|
| 555 |
+
loss_fct = CrossEntropyLoss()
|
| 556 |
+
loss = loss_fct(logits.view(-1, num_labels), labels.view(-1))
|
| 557 |
+
else:
|
| 558 |
+
loss_fct = BCEWithLogitsLoss()
|
| 559 |
+
loss = loss_fct(logits, labels)
|
| 560 |
+
averaged_losses = average_losses_across_data_parallel_group([loss])
|
| 561 |
+
return loss, {"loss": averaged_losses[0]}
|
| 562 |
+
|
| 563 |
+
if accelerator.state.megatron_lm_plugin.custom_loss_function is not None:
|
| 564 |
+
return accelerator.state.megatron_lm_plugin.custom_loss_function
|
| 565 |
+
if pretraining_flag:
|
| 566 |
+
return loss_func_pretrain
|
| 567 |
+
else:
|
| 568 |
+
return loss_func_finetune
|
| 569 |
+
|
| 570 |
+
def get_forward_step_func(self, pretraining_flag, bert_binary_head):
|
| 571 |
+
def forward_step(data_iterator, model):
|
| 572 |
+
"""Forward step."""
|
| 573 |
+
tokens, types, sentence_order, loss_mask, labels, padding_mask = self.get_batch(data_iterator)
|
| 574 |
+
if not bert_binary_head:
|
| 575 |
+
types = None
|
| 576 |
+
# Forward pass through the model.
|
| 577 |
+
if pretraining_flag:
|
| 578 |
+
output_tensor = model(tokens, padding_mask, tokentype_ids=types, lm_labels=labels)
|
| 579 |
+
return output_tensor, partial(self.loss_func, loss_mask, sentence_order)
|
| 580 |
+
else:
|
| 581 |
+
logits = model(tokens, padding_mask, tokentype_ids=types)
|
| 582 |
+
return logits, partial(self.loss_func, labels)
|
| 583 |
+
|
| 584 |
+
return forward_step
|
| 585 |
+
|
| 586 |
+
|
| 587 |
+
class GPTTrainStep(AbstractTrainStep):
|
| 588 |
+
"""
|
| 589 |
+
GPT train step class.
|
| 590 |
+
|
| 591 |
+
Args:
|
| 592 |
+
args (`argparse.Namespace`): Megatron-LM arguments.
|
| 593 |
+
"""
|
| 594 |
+
|
| 595 |
+
def __init__(self, accelerator, args):
|
| 596 |
+
super().__init__("GPTTrainStep")
|
| 597 |
+
self.get_batch = self.get_batch_func(accelerator, args.megatron_dataset_flag)
|
| 598 |
+
self.loss_func = self.get_loss_func(accelerator)
|
| 599 |
+
self.forward_step = self.get_forward_step_func()
|
| 600 |
+
self.eod_token = args.padded_vocab_size - 1
|
| 601 |
+
if args.vocab_file is not None:
|
| 602 |
+
tokenizer = get_tokenizer()
|
| 603 |
+
self.eod_token = tokenizer.eod
|
| 604 |
+
self.reset_position_ids = args.reset_position_ids
|
| 605 |
+
self.reset_attention_mask = args.reset_attention_mask
|
| 606 |
+
self.eod_mask_loss = args.eod_mask_loss
|
| 607 |
+
if not args.model_return_dict:
|
| 608 |
+
self.model_output_class = None
|
| 609 |
+
else:
|
| 610 |
+
from transformers.modeling_outputs import CausalLMOutputWithCrossAttentions
|
| 611 |
+
|
| 612 |
+
self.model_output_class = CausalLMOutputWithCrossAttentions
|
| 613 |
+
|
| 614 |
+
def get_batch_func(self, accelerator, megatron_dataset_flag):
|
| 615 |
+
def get_batch_megatron(data_iterator):
|
| 616 |
+
"""Generate a batch"""
|
| 617 |
+
# Items and their type.
|
| 618 |
+
keys = ["text"]
|
| 619 |
+
datatype = torch.int64
|
| 620 |
+
|
| 621 |
+
# Broadcast data.
|
| 622 |
+
if data_iterator is not None:
|
| 623 |
+
data = next(data_iterator)
|
| 624 |
+
else:
|
| 625 |
+
data = None
|
| 626 |
+
data_b = tensor_parallel.broadcast_data(keys, data, datatype)
|
| 627 |
+
|
| 628 |
+
# Unpack.
|
| 629 |
+
tokens_ = data_b["text"].long()
|
| 630 |
+
labels = tokens_[:, 1:].contiguous()
|
| 631 |
+
tokens = tokens_[:, :-1].contiguous()
|
| 632 |
+
|
| 633 |
+
# Get the masks and postition ids.
|
| 634 |
+
attention_mask, loss_mask, position_ids = get_ltor_masks_and_position_ids(
|
| 635 |
+
tokens, self.eod_token, self.reset_position_ids, self.reset_attention_mask, self.eod_mask_loss
|
| 636 |
+
)
|
| 637 |
+
|
| 638 |
+
return tokens, labels, loss_mask, attention_mask, position_ids
|
| 639 |
+
|
| 640 |
+
def get_batch_transformer(data_iterator):
|
| 641 |
+
data = next(data_iterator)
|
| 642 |
+
data = {"input_ids": data["input_ids"]}
|
| 643 |
+
data = send_to_device(data, torch.cuda.current_device())
|
| 644 |
+
|
| 645 |
+
tokens_ = data["input_ids"].long()
|
| 646 |
+
padding = torch.zeros((tokens_.shape[0], 1), dtype=tokens_.dtype, device=tokens_.device) + self.eod_token
|
| 647 |
+
tokens_ = torch.concat([tokens_, padding], dim=1)
|
| 648 |
+
labels = tokens_[:, 1:].contiguous()
|
| 649 |
+
tokens = tokens_[:, :-1].contiguous()
|
| 650 |
+
# Get the masks and postition ids.
|
| 651 |
+
attention_mask, loss_mask, position_ids = get_ltor_masks_and_position_ids(
|
| 652 |
+
tokens, self.eod_token, self.reset_position_ids, self.reset_attention_mask, True
|
| 653 |
+
)
|
| 654 |
+
return tokens, labels, loss_mask, attention_mask, position_ids
|
| 655 |
+
|
| 656 |
+
if accelerator.state.megatron_lm_plugin.custom_get_batch_function is not None:
|
| 657 |
+
return accelerator.state.megatron_lm_plugin.custom_get_batch_function
|
| 658 |
+
if megatron_dataset_flag:
|
| 659 |
+
try:
|
| 660 |
+
# Use '--no-use-pep517 -e' to pip install nvidia's megatron from source
|
| 661 |
+
from pretrain_gpt import get_batch
|
| 662 |
+
|
| 663 |
+
return get_batch
|
| 664 |
+
except ImportError:
|
| 665 |
+
pass
|
| 666 |
+
return get_batch_megatron
|
| 667 |
+
else:
|
| 668 |
+
return get_batch_transformer
|
| 669 |
+
|
| 670 |
+
def get_loss_func(self, accelerator):
|
| 671 |
+
args = get_args()
|
| 672 |
+
|
| 673 |
+
def loss_func(loss_mask, output_tensor):
|
| 674 |
+
if args.return_logits:
|
| 675 |
+
losses, logits = output_tensor
|
| 676 |
+
else:
|
| 677 |
+
losses = output_tensor
|
| 678 |
+
losses = losses.float()
|
| 679 |
+
loss_mask = loss_mask.view(-1).float()
|
| 680 |
+
if args.context_parallel_size > 1:
|
| 681 |
+
loss = torch.cat([torch.sum(losses.view(-1) * loss_mask).view(1), loss_mask.sum().view(1)])
|
| 682 |
+
torch.distributed.all_reduce(loss, group=mpu.get_context_parallel_group())
|
| 683 |
+
loss = loss[0] / loss[1]
|
| 684 |
+
else:
|
| 685 |
+
loss = torch.sum(losses.view(-1) * loss_mask) / loss_mask.sum()
|
| 686 |
+
|
| 687 |
+
# Check individual rank losses are not NaN prior to DP all-reduce.
|
| 688 |
+
if args.check_for_nan_in_loss_and_grad:
|
| 689 |
+
global_rank = torch.distributed.get_rank()
|
| 690 |
+
assert not loss.isnan(), (
|
| 691 |
+
f"Rank {global_rank}: found NaN in local forward loss calculation. "
|
| 692 |
+
f"Device: {torch.cuda.current_device()}, node: {os.uname()[1]}"
|
| 693 |
+
)
|
| 694 |
+
|
| 695 |
+
# Reduce loss for logging.
|
| 696 |
+
averaged_loss = average_losses_across_data_parallel_group([loss])
|
| 697 |
+
|
| 698 |
+
output_dict = {"lm loss": averaged_loss[0]}
|
| 699 |
+
if args.return_logits:
|
| 700 |
+
output_dict.update({"logits": logits})
|
| 701 |
+
return loss, output_dict
|
| 702 |
+
|
| 703 |
+
if accelerator.state.megatron_lm_plugin.custom_loss_function is not None:
|
| 704 |
+
return accelerator.state.megatron_lm_plugin.custom_loss_function
|
| 705 |
+
return loss_func
|
| 706 |
+
|
| 707 |
+
def get_forward_step_func(self):
|
| 708 |
+
def forward_step(data_iterator, model):
|
| 709 |
+
"""Forward step."""
|
| 710 |
+
# Get the batch.
|
| 711 |
+
tokens, labels, loss_mask, attention_mask, position_ids = self.get_batch(data_iterator)
|
| 712 |
+
output_tensor = model(tokens, position_ids, attention_mask, labels=labels)
|
| 713 |
+
|
| 714 |
+
return output_tensor, partial(self.loss_func, loss_mask)
|
| 715 |
+
|
| 716 |
+
return forward_step
|
| 717 |
+
|
| 718 |
+
|
| 719 |
+
class T5TrainStep(AbstractTrainStep):
|
| 720 |
+
"""
|
| 721 |
+
T5 train step class.
|
| 722 |
+
|
| 723 |
+
Args:
|
| 724 |
+
args (`argparse.Namespace`): Megatron-LM arguments.
|
| 725 |
+
"""
|
| 726 |
+
|
| 727 |
+
def __init__(self, accelerator, args):
|
| 728 |
+
super().__init__("T5TrainStep")
|
| 729 |
+
self.get_batch = self.get_batch_func(accelerator, args.megatron_dataset_flag)
|
| 730 |
+
self.loss_func = self.get_loss_func(accelerator)
|
| 731 |
+
self.forward_step = self.get_forward_step_func()
|
| 732 |
+
if not args.model_return_dict:
|
| 733 |
+
self.model_output_class = None
|
| 734 |
+
else:
|
| 735 |
+
from transformers.modeling_outputs import Seq2SeqLMOutput
|
| 736 |
+
|
| 737 |
+
self.model_output_class = Seq2SeqLMOutput
|
| 738 |
+
|
| 739 |
+
@staticmethod
|
| 740 |
+
def attn_mask_postprocess(attention_mask):
|
| 741 |
+
# We create a 3D attention mask from a 2D tensor mask.
|
| 742 |
+
# [b, 1, s]
|
| 743 |
+
attention_mask_b1s = attention_mask.unsqueeze(1)
|
| 744 |
+
# [b, s, 1]
|
| 745 |
+
attention_mask_bs1 = attention_mask.unsqueeze(2)
|
| 746 |
+
# [b, s, s]
|
| 747 |
+
attention_mask_bss = attention_mask_b1s * attention_mask_bs1
|
| 748 |
+
# Convert attention mask to binary:
|
| 749 |
+
extended_attention_mask = attention_mask_bss < 0.5
|
| 750 |
+
return extended_attention_mask
|
| 751 |
+
|
| 752 |
+
@staticmethod
|
| 753 |
+
def get_decoder_mask(seq_length, device):
|
| 754 |
+
attention_mask = torch.tril(torch.ones((1, seq_length, seq_length), device=device))
|
| 755 |
+
attention_mask = attention_mask < 0.5
|
| 756 |
+
return attention_mask
|
| 757 |
+
|
| 758 |
+
@staticmethod
|
| 759 |
+
def get_enc_dec_mask(attention_mask, dec_seq_length, device):
|
| 760 |
+
batch_size, _ = attention_mask.shape
|
| 761 |
+
# We create a 3D attention mask from a 2D tensor mask.
|
| 762 |
+
# [b, 1, s]
|
| 763 |
+
attention_mask_b1s = attention_mask.unsqueeze(1)
|
| 764 |
+
# [b, s, 1]
|
| 765 |
+
attention_mask_bs1 = torch.ones((batch_size, dec_seq_length, 1), device=device)
|
| 766 |
+
attention_mask_bss = attention_mask_bs1 * attention_mask_b1s
|
| 767 |
+
extended_attention_mask = attention_mask_bss < 0.5
|
| 768 |
+
return extended_attention_mask
|
| 769 |
+
|
| 770 |
+
def get_batch_func(self, accelerator, megatron_dataset_flag):
|
| 771 |
+
def get_batch_megatron(data_iterator):
|
| 772 |
+
"""Build the batch."""
|
| 773 |
+
|
| 774 |
+
keys = ["text_enc", "text_dec", "labels", "loss_mask", "enc_mask", "dec_mask", "enc_dec_mask"]
|
| 775 |
+
datatype = torch.int64
|
| 776 |
+
|
| 777 |
+
# Broadcast data.
|
| 778 |
+
if data_iterator is not None:
|
| 779 |
+
data = next(data_iterator)
|
| 780 |
+
else:
|
| 781 |
+
data = None
|
| 782 |
+
data_b = tensor_parallel.broadcast_data(keys, data, datatype)
|
| 783 |
+
|
| 784 |
+
# Unpack.
|
| 785 |
+
tokens_enc = data_b["text_enc"].long()
|
| 786 |
+
tokens_dec = data_b["text_dec"].long()
|
| 787 |
+
labels = data_b["labels"].long()
|
| 788 |
+
loss_mask = data_b["loss_mask"].float()
|
| 789 |
+
|
| 790 |
+
enc_mask = data_b["enc_mask"] < 0.5
|
| 791 |
+
dec_mask = data_b["dec_mask"] < 0.5
|
| 792 |
+
enc_dec_mask = data_b["enc_dec_mask"] < 0.5
|
| 793 |
+
|
| 794 |
+
return tokens_enc, tokens_dec, loss_mask, labels, enc_mask, dec_mask, enc_dec_mask
|
| 795 |
+
|
| 796 |
+
def get_batch_transformer(data_iterator):
|
| 797 |
+
"""Build the batch."""
|
| 798 |
+
data = next(data_iterator)
|
| 799 |
+
data = send_to_device(data, torch.cuda.current_device())
|
| 800 |
+
|
| 801 |
+
tokens_enc = data["input_ids"].long()
|
| 802 |
+
labels = data["labels"].long()
|
| 803 |
+
loss_mask = (labels != -100).to(torch.float)
|
| 804 |
+
if "decoder_input_ids" in data:
|
| 805 |
+
tokens_dec = data["decoder_input_ids"].long()
|
| 806 |
+
else:
|
| 807 |
+
tokens_dec = labels.new_zeros(labels.shape, device=labels.device, dtype=torch.long)
|
| 808 |
+
tokens_dec[..., 1:] = labels[..., :-1].clone()
|
| 809 |
+
tokens_dec[..., 0] = 0
|
| 810 |
+
tokens_dec.masked_fill_(tokens_dec == -100, 0)
|
| 811 |
+
enc_mask = T5TrainStep.attn_mask_postprocess(data["attention_mask"].long())
|
| 812 |
+
dec_mask = T5TrainStep.get_decoder_mask(tokens_dec.shape[1], tokens_dec.device)
|
| 813 |
+
enc_dec_mask = T5TrainStep.get_enc_dec_mask(
|
| 814 |
+
data["attention_mask"].long(), tokens_dec.shape[1], tokens_dec.device
|
| 815 |
+
)
|
| 816 |
+
|
| 817 |
+
return tokens_enc, tokens_dec, loss_mask, labels, enc_mask, dec_mask, enc_dec_mask
|
| 818 |
+
|
| 819 |
+
if accelerator.state.megatron_lm_plugin.custom_get_batch_function is not None:
|
| 820 |
+
return accelerator.state.megatron_lm_plugin.custom_get_batch_function
|
| 821 |
+
if megatron_dataset_flag:
|
| 822 |
+
try:
|
| 823 |
+
# Use '--no-use-pep517 -e' to pip install nvidia's megatron from source
|
| 824 |
+
from pretrain_t5 import get_batch
|
| 825 |
+
|
| 826 |
+
return get_batch
|
| 827 |
+
except ImportError:
|
| 828 |
+
pass
|
| 829 |
+
return get_batch_megatron
|
| 830 |
+
else:
|
| 831 |
+
return get_batch_transformer
|
| 832 |
+
|
| 833 |
+
def get_loss_func(self, accelerator):
|
| 834 |
+
def loss_func(loss_mask, output_tensor):
|
| 835 |
+
lm_loss_ = output_tensor.float()
|
| 836 |
+
lm_loss = torch.sum(lm_loss_.view(-1) * loss_mask.reshape(-1)) / loss_mask.sum()
|
| 837 |
+
|
| 838 |
+
loss = lm_loss
|
| 839 |
+
averaged_losses = average_losses_across_data_parallel_group([lm_loss])
|
| 840 |
+
|
| 841 |
+
return loss, {"lm loss": averaged_losses[0]}
|
| 842 |
+
|
| 843 |
+
if accelerator.state.megatron_lm_plugin.custom_loss_function is not None:
|
| 844 |
+
return accelerator.state.megatron_lm_plugin.custom_loss_function
|
| 845 |
+
return loss_func
|
| 846 |
+
|
| 847 |
+
def get_forward_step_func(self):
|
| 848 |
+
def forward_step(data_iterator, model):
|
| 849 |
+
"""Forward step."""
|
| 850 |
+
# Get the batch.
|
| 851 |
+
tokens_enc, tokens_dec, loss_mask, lm_labels, enc_mask, dec_mask, enc_dec_mask = self.get_batch(
|
| 852 |
+
data_iterator
|
| 853 |
+
)
|
| 854 |
+
# Forward model lm_labels
|
| 855 |
+
output_tensor = model(
|
| 856 |
+
tokens_enc, tokens_dec, enc_mask, dec_mask, enc_dec_mask, tokentype_ids=None, lm_labels=lm_labels
|
| 857 |
+
)
|
| 858 |
+
|
| 859 |
+
return output_tensor, partial(self.loss_func, loss_mask)
|
| 860 |
+
|
| 861 |
+
return forward_step
|
| 862 |
+
|
| 863 |
+
|
| 864 |
+
def finish_mpu_init():
|
| 865 |
+
# torch.distributed initialization
|
| 866 |
+
args = get_args()
|
| 867 |
+
# Pytorch distributed.
|
| 868 |
+
_initialize_distributed()
|
| 869 |
+
|
| 870 |
+
# Random seeds for reproducibility.
|
| 871 |
+
if args.rank == 0:
|
| 872 |
+
print(f"> setting random seeds to {args.seed} ...")
|
| 873 |
+
_set_random_seed(args.seed, args.data_parallel_random_init)
|
| 874 |
+
|
| 875 |
+
|
| 876 |
+
# intialize megatron setup
|
| 877 |
+
def initialize(accelerator, extra_args_provider=None, args_defaults={}):
|
| 878 |
+
accelerator.print("Initializing Megatron-LM")
|
| 879 |
+
assert torch.cuda.is_available(), "Megatron requires CUDA."
|
| 880 |
+
|
| 881 |
+
# Parse arguments
|
| 882 |
+
args = parse_args(extra_args_provider, ignore_unknown_args=True)
|
| 883 |
+
|
| 884 |
+
# Set defaults
|
| 885 |
+
for key, value in args_defaults.items():
|
| 886 |
+
if getattr(args, key, None) is not None:
|
| 887 |
+
if args.rank == 0:
|
| 888 |
+
print(
|
| 889 |
+
f"WARNING: overriding default arguments for {key}:{getattr(args, key)} with {key}:{value}",
|
| 890 |
+
flush=True,
|
| 891 |
+
)
|
| 892 |
+
setattr(args, key, value)
|
| 893 |
+
|
| 894 |
+
if args.use_checkpoint_args or args_defaults.get("use_checkpoint_args", False):
|
| 895 |
+
assert args.load is not None, "--use-checkpoints-args requires --load argument"
|
| 896 |
+
load_args_from_checkpoint(args)
|
| 897 |
+
|
| 898 |
+
validate_args(args)
|
| 899 |
+
|
| 900 |
+
# set global args, build tokenizer, and set adlr-autoresume,
|
| 901 |
+
# tensorboard-writer, and timers.
|
| 902 |
+
set_global_variables(args)
|
| 903 |
+
|
| 904 |
+
# Megatron's MPU is the master. Complete initialization right away.
|
| 905 |
+
finish_mpu_init()
|
| 906 |
+
|
| 907 |
+
# Autoresume.
|
| 908 |
+
_init_autoresume()
|
| 909 |
+
|
| 910 |
+
# Compile dependencies.
|
| 911 |
+
_compile_dependencies()
|
| 912 |
+
|
| 913 |
+
# Set pytorch JIT layer fusion options and warmup JIT functions.
|
| 914 |
+
set_jit_fusion_options()
|
| 915 |
+
args = get_args()
|
| 916 |
+
if getattr(args, "padded_vocab_size", None) is None:
|
| 917 |
+
args.padded_vocab_size = _vocab_size_with_padding(args.orig_vocab_size, args)
|
| 918 |
+
if args.model_type_name == "bert" and args.pretraining_flag and args.num_labels == 2:
|
| 919 |
+
args.bert_binary_head = True
|
| 920 |
+
else:
|
| 921 |
+
args.bert_binary_head = False
|
| 922 |
+
args.iteration = 0
|
| 923 |
+
|
| 924 |
+
|
| 925 |
+
class MegatronEngine(torch.nn.Module):
|
| 926 |
+
"""
|
| 927 |
+
Megatron-LM model wrapper
|
| 928 |
+
|
| 929 |
+
Args:
|
| 930 |
+
accelerator (:class:`~accelerate.Accelerator`): The accelerator object to use.
|
| 931 |
+
model: Megatron-LM model
|
| 932 |
+
optimizer: Megatron-LM optimizer
|
| 933 |
+
lr_scheduler: Megatron-LM lr scheduler
|
| 934 |
+
"""
|
| 935 |
+
|
| 936 |
+
def __init__(self, accelerator, model, optimizer, scheduler):
|
| 937 |
+
super().__init__()
|
| 938 |
+
self.module = model
|
| 939 |
+
self.base_model = model[0]
|
| 940 |
+
self.optimizer = optimizer
|
| 941 |
+
self.scheduler = scheduler
|
| 942 |
+
args = get_args()
|
| 943 |
+
if accelerator.state.megatron_lm_plugin.custom_train_step_class is not None:
|
| 944 |
+
self.train_step_handler = accelerator.state.megatron_lm_plugin.custom_train_step_class(
|
| 945 |
+
args, **accelerator.state.megatron_lm_plugin.custom_train_step_kwargs
|
| 946 |
+
)
|
| 947 |
+
elif args.model_type_name == "bert":
|
| 948 |
+
self.train_step_handler = BertTrainStep(accelerator, args)
|
| 949 |
+
elif args.model_type_name == "gpt":
|
| 950 |
+
self.train_step_handler = GPTTrainStep(accelerator, args)
|
| 951 |
+
elif args.model_type_name == "t5":
|
| 952 |
+
self.train_step_handler = T5TrainStep(accelerator, args)
|
| 953 |
+
else:
|
| 954 |
+
raise ValueError(f"Unsupported model type: {args.model_type_name}")
|
| 955 |
+
self.optimizer.skipped_iter = False
|
| 956 |
+
|
| 957 |
+
# Tracking loss.
|
| 958 |
+
self.total_loss_dict = {}
|
| 959 |
+
self.eval_total_loss_dict = {}
|
| 960 |
+
self.iteration = 0
|
| 961 |
+
self.report_memory_flag = True
|
| 962 |
+
self.num_floating_point_operations_so_far = 0
|
| 963 |
+
self.module_config = None
|
| 964 |
+
if args.tensorboard_dir is not None:
|
| 965 |
+
write_args_to_tensorboard()
|
| 966 |
+
|
| 967 |
+
def get_module_config(self):
|
| 968 |
+
args = get_args()
|
| 969 |
+
config = get_model_config(self.module[0])
|
| 970 |
+
# Setup some training config params
|
| 971 |
+
config.grad_scale_func = self.optimizer.scale_loss
|
| 972 |
+
if isinstance(self.module[0], LocalDDP) and args.overlap_grad_reduce:
|
| 973 |
+
assert config.no_sync_func is None, (
|
| 974 |
+
"When overlap_grad_reduce is True, config.no_sync_func must be None; "
|
| 975 |
+
"a custom no_sync_func is not supported when overlapping grad-reduce"
|
| 976 |
+
)
|
| 977 |
+
config.no_sync_func = [model_chunk.no_sync for model_chunk in self.module]
|
| 978 |
+
if len(self.module) == 1:
|
| 979 |
+
config.no_sync_func = config.no_sync_func[0]
|
| 980 |
+
if args.delay_grad_reduce:
|
| 981 |
+
config.grad_sync_func = [model_chunk.start_grad_sync for model_chunk in self.module]
|
| 982 |
+
if len(self.module) == 1:
|
| 983 |
+
config.grad_sync_func = config.grad_sync_func[0]
|
| 984 |
+
if args.overlap_param_gather and args.delay_param_gather:
|
| 985 |
+
config.param_sync_func = [
|
| 986 |
+
lambda x: self.optimizer.finish_param_sync(model_index, x) for model_index in range(len(self.module))
|
| 987 |
+
]
|
| 988 |
+
if len(self.module) == 1:
|
| 989 |
+
config.param_sync_func = config.param_sync_func[0]
|
| 990 |
+
config.finalize_model_grads_func = finalize_model_grads
|
| 991 |
+
return config
|
| 992 |
+
|
| 993 |
+
def train(self):
|
| 994 |
+
for model_module in self.module:
|
| 995 |
+
model_module.train()
|
| 996 |
+
|
| 997 |
+
if self.module_config is None:
|
| 998 |
+
self.module_config = self.get_module_config()
|
| 999 |
+
|
| 1000 |
+
self.log_eval_results()
|
| 1001 |
+
|
| 1002 |
+
def eval(self):
|
| 1003 |
+
for model_module in self.module:
|
| 1004 |
+
model_module.eval()
|
| 1005 |
+
|
| 1006 |
+
if self.module_config is None:
|
| 1007 |
+
self.module_config = self.get_module_config()
|
| 1008 |
+
|
| 1009 |
+
def get_batch_data_iterator(self, batch_data):
|
| 1010 |
+
args = get_args()
|
| 1011 |
+
data_chunks = []
|
| 1012 |
+
if len(batch_data) > 0:
|
| 1013 |
+
if args.num_micro_batches > 1:
|
| 1014 |
+
for i in range(0, args.num_micro_batches):
|
| 1015 |
+
data_chunks.append(
|
| 1016 |
+
{
|
| 1017 |
+
k: v[i * args.micro_batch_size : (i + 1) * args.micro_batch_size]
|
| 1018 |
+
for k, v in batch_data.items()
|
| 1019 |
+
}
|
| 1020 |
+
)
|
| 1021 |
+
else:
|
| 1022 |
+
data_chunks = [batch_data]
|
| 1023 |
+
|
| 1024 |
+
if len(self.module) > 1:
|
| 1025 |
+
batch_data_iterator = (
|
| 1026 |
+
[iter(data_chunks) for _ in range(len(self.module))]
|
| 1027 |
+
if len(batch_data) > 0
|
| 1028 |
+
else [None] * len(self.module)
|
| 1029 |
+
)
|
| 1030 |
+
else:
|
| 1031 |
+
batch_data_iterator = iter(data_chunks) if len(batch_data) > 0 else None
|
| 1032 |
+
return batch_data_iterator
|
| 1033 |
+
|
| 1034 |
+
def train_step(self, **batch_data):
|
| 1035 |
+
"""
|
| 1036 |
+
Training step for Megatron-LM
|
| 1037 |
+
|
| 1038 |
+
Args:
|
| 1039 |
+
batch_data (:obj:`dict`): The batch data to train on.
|
| 1040 |
+
"""
|
| 1041 |
+
|
| 1042 |
+
batch_data_iterator = self.get_batch_data_iterator(batch_data)
|
| 1043 |
+
|
| 1044 |
+
loss_reduced, skipped_iter, grad_norm, num_zeros_in_grad = train_step(
|
| 1045 |
+
forward_step_func=self.train_step_handler.forward_step,
|
| 1046 |
+
data_iterator=batch_data_iterator,
|
| 1047 |
+
model=self.module,
|
| 1048 |
+
optimizer=self.optimizer,
|
| 1049 |
+
opt_param_scheduler=self.scheduler,
|
| 1050 |
+
config=self.module_config,
|
| 1051 |
+
)
|
| 1052 |
+
|
| 1053 |
+
self.optimizer.skipped_iter = skipped_iter == 1
|
| 1054 |
+
|
| 1055 |
+
return loss_reduced, skipped_iter, grad_norm, num_zeros_in_grad
|
| 1056 |
+
|
| 1057 |
+
def eval_step(self, **batch_data):
|
| 1058 |
+
"""
|
| 1059 |
+
Evaluation step for Megatron-LM
|
| 1060 |
+
|
| 1061 |
+
Args:
|
| 1062 |
+
batch_data (:obj:`dict`): The batch data to evaluate on.
|
| 1063 |
+
"""
|
| 1064 |
+
|
| 1065 |
+
args = get_args()
|
| 1066 |
+
batch_data_iterator = self.get_batch_data_iterator(batch_data)
|
| 1067 |
+
forward_backward_func = get_forward_backward_func()
|
| 1068 |
+
loss_dicts = forward_backward_func(
|
| 1069 |
+
forward_step_func=self.train_step_handler.forward_step,
|
| 1070 |
+
data_iterator=batch_data_iterator,
|
| 1071 |
+
model=self.module,
|
| 1072 |
+
num_microbatches=get_num_microbatches(),
|
| 1073 |
+
seq_length=args.seq_length,
|
| 1074 |
+
micro_batch_size=args.micro_batch_size,
|
| 1075 |
+
forward_only=True,
|
| 1076 |
+
)
|
| 1077 |
+
# Empty unused memory
|
| 1078 |
+
if args.empty_unused_memory_level >= 1:
|
| 1079 |
+
torch.cuda.empty_cache()
|
| 1080 |
+
|
| 1081 |
+
args.consumed_valid_samples += (
|
| 1082 |
+
mpu.get_data_parallel_world_size() * args.micro_batch_size * get_num_microbatches()
|
| 1083 |
+
)
|
| 1084 |
+
|
| 1085 |
+
if mpu.is_pipeline_last_stage(ignore_virtual=True):
|
| 1086 |
+
# Average loss across microbatches.
|
| 1087 |
+
loss_reduced = {}
|
| 1088 |
+
for key in loss_dicts[0]:
|
| 1089 |
+
losses_reduced_for_key = [x[key] for x in loss_dicts]
|
| 1090 |
+
if len(losses_reduced_for_key[0].shape) == 0:
|
| 1091 |
+
loss_reduced[key] = sum(losses_reduced_for_key) / len(losses_reduced_for_key)
|
| 1092 |
+
else:
|
| 1093 |
+
loss_reduced[key] = torch.concat(losses_reduced_for_key)
|
| 1094 |
+
return loss_reduced
|
| 1095 |
+
return {}
|
| 1096 |
+
|
| 1097 |
+
def forward(self, **batch_data):
|
| 1098 |
+
# During training, we use train_step()
|
| 1099 |
+
# model(**batch_data) performs following operations by delegating it to `self.train_step`:
|
| 1100 |
+
# 1. Prepare **batch_data for Tendor, Pipeline and Model Parallelism
|
| 1101 |
+
# 2. Set grad to zero.
|
| 1102 |
+
# 3. forward pass and backward pass using Pipeline Parallelism
|
| 1103 |
+
# 4. Empty unused memory.
|
| 1104 |
+
# 5. Reduce gradients.
|
| 1105 |
+
# 6. Update parameters.
|
| 1106 |
+
# 7. Gather params when using Distributed Optimizer (Data Parallelism).
|
| 1107 |
+
# 8. Update learning rate if scheduler is specified.
|
| 1108 |
+
# 9. Empty unused memory.
|
| 1109 |
+
# 10. Average loss across microbatches and across DP ranks.
|
| 1110 |
+
#
|
| 1111 |
+
# During evaluation, we use eval_step()
|
| 1112 |
+
args = get_args()
|
| 1113 |
+
if self.module[0].training:
|
| 1114 |
+
loss_dict, skipped_iter, grad_norm, num_zeros_in_grad = self.train_step(**batch_data)
|
| 1115 |
+
self.iteration += 1
|
| 1116 |
+
batch_size = mpu.get_data_parallel_world_size() * args.micro_batch_size * get_num_microbatches()
|
| 1117 |
+
args.consumed_train_samples += batch_size
|
| 1118 |
+
self.num_floating_point_operations_so_far += num_floating_point_operations(args, batch_size)
|
| 1119 |
+
if args.tensorboard_dir is not None:
|
| 1120 |
+
# Logging.
|
| 1121 |
+
loss_scale = self.optimizer.get_loss_scale().item()
|
| 1122 |
+
params_norm = None
|
| 1123 |
+
if args.log_params_norm:
|
| 1124 |
+
params_norm = calc_params_l2_norm(self.model)
|
| 1125 |
+
self.report_memory_flag = training_log(
|
| 1126 |
+
loss_dict,
|
| 1127 |
+
self.total_loss_dict,
|
| 1128 |
+
self.optimizer.param_groups[0]["lr"],
|
| 1129 |
+
self.iteration,
|
| 1130 |
+
loss_scale,
|
| 1131 |
+
self.report_memory_flag,
|
| 1132 |
+
skipped_iter,
|
| 1133 |
+
grad_norm,
|
| 1134 |
+
params_norm,
|
| 1135 |
+
num_zeros_in_grad,
|
| 1136 |
+
)
|
| 1137 |
+
else:
|
| 1138 |
+
loss_dict = self.eval_step(**batch_data)
|
| 1139 |
+
if args.tensorboard_dir is not None:
|
| 1140 |
+
for key in loss_dict:
|
| 1141 |
+
self.eval_total_loss_dict[key] = (
|
| 1142 |
+
self.eval_total_loss_dict.get(key, torch.cuda.FloatTensor([0.0])) + loss_dict[key]
|
| 1143 |
+
)
|
| 1144 |
+
self.eval_total_loss_dict[key + "_num_iters"] = self.eval_total_loss_dict.get(
|
| 1145 |
+
key + "_num_iters", torch.cuda.FloatTensor([0.0])
|
| 1146 |
+
) + torch.cuda.FloatTensor([1.0])
|
| 1147 |
+
|
| 1148 |
+
loss = torch.tensor(0.0, device=torch.cuda.current_device())
|
| 1149 |
+
for key in loss_dict:
|
| 1150 |
+
if len(loss_dict[key].shape) == 0:
|
| 1151 |
+
loss += loss_dict[key]
|
| 1152 |
+
|
| 1153 |
+
logits = None
|
| 1154 |
+
if "logits" in loss_dict:
|
| 1155 |
+
logits = loss_dict["logits"]
|
| 1156 |
+
if self.train_step_handler.model_output_class is not None:
|
| 1157 |
+
return self.train_step_handler.model_output_class(loss=loss, logits=logits)
|
| 1158 |
+
return loss
|
| 1159 |
+
|
| 1160 |
+
def log_eval_results(self):
|
| 1161 |
+
args = get_args()
|
| 1162 |
+
if args.tensorboard_dir is None or self.iteration == 0:
|
| 1163 |
+
return
|
| 1164 |
+
args = get_args()
|
| 1165 |
+
writer = get_tensorboard_writer()
|
| 1166 |
+
string = f"validation loss at iteration {self.iteration} | "
|
| 1167 |
+
for key in self.eval_total_loss_dict:
|
| 1168 |
+
if key.endswith("_num_iters"):
|
| 1169 |
+
continue
|
| 1170 |
+
value = self.eval_total_loss_dict[key] / self.eval_total_loss_dict[key + "_num_iters"]
|
| 1171 |
+
string += f"{key} value: {value} | "
|
| 1172 |
+
ppl = math.exp(min(20, value.item()))
|
| 1173 |
+
if args.pretraining_flag:
|
| 1174 |
+
string += f"{key} PPL: {ppl} | "
|
| 1175 |
+
if writer:
|
| 1176 |
+
writer.add_scalar(f"{key} validation", value.item(), self.iteration)
|
| 1177 |
+
if args.pretraining_flag:
|
| 1178 |
+
writer.add_scalar(f"{key} validation ppl", ppl, self.iteration)
|
| 1179 |
+
|
| 1180 |
+
length = len(string) + 1
|
| 1181 |
+
print_rank_last("-" * length)
|
| 1182 |
+
print_rank_last(string)
|
| 1183 |
+
print_rank_last("-" * length)
|
| 1184 |
+
self.eval_total_loss_dict = {}
|
| 1185 |
+
|
| 1186 |
+
def save_checkpoint(self, output_dir):
|
| 1187 |
+
self.log_eval_results()
|
| 1188 |
+
args = get_args()
|
| 1189 |
+
args.save = output_dir
|
| 1190 |
+
torch.distributed.barrier()
|
| 1191 |
+
save_checkpoint(
|
| 1192 |
+
self.iteration,
|
| 1193 |
+
self.module,
|
| 1194 |
+
self.optimizer,
|
| 1195 |
+
self.scheduler,
|
| 1196 |
+
num_floating_point_operations_so_far=self.num_floating_point_operations_so_far,
|
| 1197 |
+
)
|
| 1198 |
+
torch.distributed.barrier()
|
| 1199 |
+
|
| 1200 |
+
def load_checkpoint(self, input_dir):
|
| 1201 |
+
args = get_args()
|
| 1202 |
+
args.load = input_dir
|
| 1203 |
+
args.consumed_train_samples = 0
|
| 1204 |
+
args.consumed_valid_samples = 0
|
| 1205 |
+
torch.distributed.barrier()
|
| 1206 |
+
iteration, num_floating_point_operations_so_far = load_checkpoint(self.module, self.optimizer, self.scheduler)
|
| 1207 |
+
torch.distributed.barrier()
|
| 1208 |
+
self.iteration = iteration
|
| 1209 |
+
self.num_floating_point_operations_so_far = num_floating_point_operations_so_far
|
| 1210 |
+
if args.fp16 and self.iteration == 0:
|
| 1211 |
+
self.optimizer.reload_model_params()
|
| 1212 |
+
|
| 1213 |
+
def megatron_generate(
|
| 1214 |
+
self,
|
| 1215 |
+
inputs,
|
| 1216 |
+
attention_mask=None,
|
| 1217 |
+
max_length=None,
|
| 1218 |
+
max_new_tokens=None,
|
| 1219 |
+
num_beams=None,
|
| 1220 |
+
temperature=None,
|
| 1221 |
+
top_k=None,
|
| 1222 |
+
top_p=None,
|
| 1223 |
+
length_penalty=None,
|
| 1224 |
+
**kwargs,
|
| 1225 |
+
):
|
| 1226 |
+
"""
|
| 1227 |
+
Generate method for GPT2 model. This method is used for inference. Supports both greedy and beam search along
|
| 1228 |
+
with sampling. Refer the Megatron-LM repo for more details
|
| 1229 |
+
|
| 1230 |
+
Args:
|
| 1231 |
+
inputs (torch.Tensor): input ids
|
| 1232 |
+
attention_mask (torch.Tensor, optional): attention mask. Defaults to None.
|
| 1233 |
+
max_length (int, optional): max length of the generated sequence. Defaults to None.
|
| 1234 |
+
Either this or max_new_tokens should be provided.
|
| 1235 |
+
max_new_tokens (int, optional): max number of tokens to be generated. Defaults to None.
|
| 1236 |
+
Either this or max_length should be provided.
|
| 1237 |
+
num_beams (int, optional): number of beams to use for beam search. Defaults to None.
|
| 1238 |
+
temperature (float, optional): temperature for sampling. Defaults to 1.0.
|
| 1239 |
+
top_k (int, optional): top k tokens to consider for sampling. Defaults to 0.0.
|
| 1240 |
+
top_p (float, optional): tokens in top p probability are considered for sampling. Defaults to 0.0.
|
| 1241 |
+
length_penalty (float, optional): length penalty for beam search. Defaults to None.
|
| 1242 |
+
kwargs: additional key-value arguments
|
| 1243 |
+
"""
|
| 1244 |
+
|
| 1245 |
+
# checking if required arguments are passed
|
| 1246 |
+
args = get_args()
|
| 1247 |
+
if args.model_type_name != "gpt":
|
| 1248 |
+
raise NotImplementedError("Generate method is not implemented for this model")
|
| 1249 |
+
|
| 1250 |
+
if args.data_parallel_size > 1:
|
| 1251 |
+
raise ValueError("Generate method requires data parallelism to be 1")
|
| 1252 |
+
|
| 1253 |
+
if args.sequence_parallel:
|
| 1254 |
+
raise ValueError("Generate method requires sequence parallelism to be False")
|
| 1255 |
+
|
| 1256 |
+
if args.recompute_granularity is not None:
|
| 1257 |
+
raise ValueError("Checkpoint activations cannot be set for inference")
|
| 1258 |
+
|
| 1259 |
+
if args.vocab_file is None:
|
| 1260 |
+
raise ValueError("Vocab file is required for inference")
|
| 1261 |
+
|
| 1262 |
+
# Prepare inputs
|
| 1263 |
+
if max_length is None and max_new_tokens is None:
|
| 1264 |
+
raise ValueError("`max_length` or `max_new_tokens` are required for inference")
|
| 1265 |
+
|
| 1266 |
+
if temperature is None:
|
| 1267 |
+
temperature = 1.0
|
| 1268 |
+
elif not (0.0 < temperature <= 100.0):
|
| 1269 |
+
raise ValueError("temperature must be a positive number less than or equal to 100.0")
|
| 1270 |
+
|
| 1271 |
+
if top_k is None:
|
| 1272 |
+
top_k = 0
|
| 1273 |
+
elif not (0 <= top_k <= 1000):
|
| 1274 |
+
raise ValueError("top_k must be a positive number less than or equal to 1000")
|
| 1275 |
+
|
| 1276 |
+
if top_p is None:
|
| 1277 |
+
top_p = 0.0
|
| 1278 |
+
elif top_p > 0.0 and top_k > 0.0:
|
| 1279 |
+
raise ValueError("top_p and top_k sampling cannot be set together")
|
| 1280 |
+
else:
|
| 1281 |
+
if not (0.0 <= top_p <= 1.0):
|
| 1282 |
+
raise ValueError("top_p must be less than or equal to 1.0")
|
| 1283 |
+
|
| 1284 |
+
top_p_decay = kwargs.get("top_p_decay", 0.0)
|
| 1285 |
+
if not (0.0 <= top_p_decay <= 1.0):
|
| 1286 |
+
raise ValueError("top_p_decay must be less than or equal to 1.0")
|
| 1287 |
+
|
| 1288 |
+
top_p_bound = kwargs.get("top_p_bound", 0.0)
|
| 1289 |
+
if not (0.0 <= top_p_bound <= 1.0):
|
| 1290 |
+
raise ValueError("top_p_bound must be less than or equal to 1.0")
|
| 1291 |
+
|
| 1292 |
+
add_BOS = kwargs.get("add_BOS", False)
|
| 1293 |
+
if not (isinstance(add_BOS, bool)):
|
| 1294 |
+
raise ValueError("add_BOS must be a boolean")
|
| 1295 |
+
|
| 1296 |
+
beam_width = num_beams
|
| 1297 |
+
if beam_width is not None:
|
| 1298 |
+
if not isinstance(beam_width, int):
|
| 1299 |
+
raise ValueError("beam_width must be an integer")
|
| 1300 |
+
if beam_width < 1:
|
| 1301 |
+
raise ValueError("beam_width must be greater than 0")
|
| 1302 |
+
if inputs.shape[0] > 1:
|
| 1303 |
+
return "When doing beam_search, batch size must be 1"
|
| 1304 |
+
|
| 1305 |
+
tokenizer = get_tokenizer()
|
| 1306 |
+
|
| 1307 |
+
stop_token = kwargs.get("stop_token", tokenizer.eod)
|
| 1308 |
+
if stop_token is not None:
|
| 1309 |
+
if not isinstance(stop_token, int):
|
| 1310 |
+
raise ValueError("stop_token must be an integer")
|
| 1311 |
+
|
| 1312 |
+
if length_penalty is None:
|
| 1313 |
+
length_penalty = 1.0
|
| 1314 |
+
|
| 1315 |
+
sizes_list = None
|
| 1316 |
+
prompts_tokens_tensor = None
|
| 1317 |
+
prompts_length_tensor = None
|
| 1318 |
+
if torch.distributed.get_rank() == 0:
|
| 1319 |
+
# Get the prompts length.
|
| 1320 |
+
if attention_mask is None:
|
| 1321 |
+
prompts_length_tensor = torch.cuda.LongTensor([inputs.shape[1]] * inputs.shape[0])
|
| 1322 |
+
else:
|
| 1323 |
+
prompts_length_tensor = attention_mask.sum(axis=-1).cuda()
|
| 1324 |
+
|
| 1325 |
+
if max_new_tokens is None:
|
| 1326 |
+
max_new_tokens = max_length - inputs.shape[1]
|
| 1327 |
+
if max_new_tokens <= 0:
|
| 1328 |
+
raise ValueError("max_new_tokens must be greater than 0")
|
| 1329 |
+
|
| 1330 |
+
if add_BOS:
|
| 1331 |
+
max_length = max_new_tokens + inputs.shape[1] + 1
|
| 1332 |
+
# making sure that `max_length` is a multiple of 4 to leverage fused kernels
|
| 1333 |
+
max_length = 4 * math.ceil(max_length / 4)
|
| 1334 |
+
max_new_tokens = max_length - (inputs.shape[1] + 1)
|
| 1335 |
+
padding = torch.cuda.LongTensor([[tokenizer.eod] * max_new_tokens] * inputs.shape[0])
|
| 1336 |
+
prompts_tokens_tensor = torch.concat(
|
| 1337 |
+
[torch.unsqueeze(padding[:, 0], axis=-1), inputs.cuda(), padding], axis=-1
|
| 1338 |
+
)
|
| 1339 |
+
else:
|
| 1340 |
+
# making sure that `max_length` is a multiple of 4 to leverage fused kernels
|
| 1341 |
+
max_length = max_new_tokens + inputs.shape[1]
|
| 1342 |
+
max_length = 4 * math.ceil(max_length / 4)
|
| 1343 |
+
max_new_tokens = max_length - inputs.shape[1]
|
| 1344 |
+
padding = torch.cuda.LongTensor([[tokenizer.eod] * max_new_tokens] * inputs.shape[0])
|
| 1345 |
+
prompts_tokens_tensor = torch.concat([inputs.cuda(), padding], axis=-1)
|
| 1346 |
+
|
| 1347 |
+
# We need the sizes of these tensors for the boradcast
|
| 1348 |
+
sizes_list = [
|
| 1349 |
+
prompts_tokens_tensor.size(0), # Batch size
|
| 1350 |
+
prompts_tokens_tensor.size(1),
|
| 1351 |
+
] # Sequence lenght
|
| 1352 |
+
|
| 1353 |
+
# First, broadcast the sizes.
|
| 1354 |
+
sizes_tensor = broadcast_int_list(2, int_list=sizes_list, rank=0)
|
| 1355 |
+
|
| 1356 |
+
# Now that we have the sizes, we can boradcast the tokens
|
| 1357 |
+
# and length tensors.
|
| 1358 |
+
sizes = sizes_tensor.tolist()
|
| 1359 |
+
context_tokens_tensor = broadcast_tensor(sizes, torch.int64, tensor=prompts_tokens_tensor, rank=0)
|
| 1360 |
+
context_length_tensor = broadcast_tensor(sizes[0], torch.int64, tensor=prompts_length_tensor, rank=0)
|
| 1361 |
+
|
| 1362 |
+
# Run the inference
|
| 1363 |
+
random_seed = kwargs.get("random_seed", 0)
|
| 1364 |
+
torch.random.manual_seed(random_seed)
|
| 1365 |
+
unwrapped_model = unwrap_model(self.base_model, (torchDDP, LocalDDP, Float16Module))
|
| 1366 |
+
if beam_width is not None:
|
| 1367 |
+
tokens, _ = beam_search_and_return_on_first_stage(
|
| 1368 |
+
unwrapped_model,
|
| 1369 |
+
context_tokens_tensor,
|
| 1370 |
+
context_length_tensor,
|
| 1371 |
+
beam_width,
|
| 1372 |
+
stop_token=stop_token,
|
| 1373 |
+
num_return_gen=1,
|
| 1374 |
+
length_penalty=length_penalty,
|
| 1375 |
+
)
|
| 1376 |
+
else:
|
| 1377 |
+
tokens, _, _ = generate_tokens_probs_and_return_on_first_stage(
|
| 1378 |
+
unwrapped_model,
|
| 1379 |
+
context_tokens_tensor,
|
| 1380 |
+
context_length_tensor,
|
| 1381 |
+
return_output_log_probs=False,
|
| 1382 |
+
top_k=top_k,
|
| 1383 |
+
top_p=top_p,
|
| 1384 |
+
top_p_decay=top_p_decay,
|
| 1385 |
+
top_p_bound=top_p_bound,
|
| 1386 |
+
temperature=temperature,
|
| 1387 |
+
use_eod_token_for_early_termination=True,
|
| 1388 |
+
)
|
| 1389 |
+
return tokens
|
| 1390 |
+
|
| 1391 |
+
|
| 1392 |
+
# other utilities
|
| 1393 |
+
def avg_losses_across_data_parallel_group(losses):
|
| 1394 |
+
"""
|
| 1395 |
+
Average losses across data parallel group.
|
| 1396 |
+
|
| 1397 |
+
Args:
|
| 1398 |
+
losses (List[Tensor]): List of losses to average across data parallel group.
|
| 1399 |
+
"""
|
| 1400 |
+
|
| 1401 |
+
return average_losses_across_data_parallel_group(losses)
|
| 1402 |
+
|
| 1403 |
+
|
| 1404 |
+
def gather_across_data_parallel_groups(tensor):
|
| 1405 |
+
"""
|
| 1406 |
+
Recursively gather tensor in a nested list/tuple/dictionary of tensors from data parallel ranks.
|
| 1407 |
+
|
| 1408 |
+
Args:
|
| 1409 |
+
tensor (nested list/tuple/dictionary of `torch.Tensor`):
|
| 1410 |
+
The data to gather across data parallel ranks.
|
| 1411 |
+
|
| 1412 |
+
"""
|
| 1413 |
+
|
| 1414 |
+
def _gpu_gather_one(tensor):
|
| 1415 |
+
if tensor.ndim == 0:
|
| 1416 |
+
tensor = tensor.clone()[None]
|
| 1417 |
+
output_tensors = [
|
| 1418 |
+
torch.empty_like(tensor)
|
| 1419 |
+
for _ in range(torch.distributed.get_world_size(group=mpu.get_data_parallel_group()))
|
| 1420 |
+
]
|
| 1421 |
+
torch.distributed.all_gather(output_tensors, tensor, group=mpu.get_data_parallel_group())
|
| 1422 |
+
return torch.cat(output_tensors, dim=0)
|
| 1423 |
+
|
| 1424 |
+
return recursively_apply(_gpu_gather_one, tensor, error_on_other_type=True)
|
venv/lib/python3.11/site-packages/accelerate/utils/memory.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""
|
| 16 |
+
A collection of utilities for ensuring that training can always occur. Heavily influenced by the
|
| 17 |
+
[toma](https://github.com/BlackHC/toma) library.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
import functools
|
| 21 |
+
import gc
|
| 22 |
+
import importlib
|
| 23 |
+
import inspect
|
| 24 |
+
import warnings
|
| 25 |
+
|
| 26 |
+
import torch
|
| 27 |
+
from packaging import version
|
| 28 |
+
|
| 29 |
+
from .imports import (
|
| 30 |
+
is_cuda_available,
|
| 31 |
+
is_hpu_available,
|
| 32 |
+
is_ipex_available,
|
| 33 |
+
is_mlu_available,
|
| 34 |
+
is_mps_available,
|
| 35 |
+
is_musa_available,
|
| 36 |
+
is_npu_available,
|
| 37 |
+
is_sdaa_available,
|
| 38 |
+
is_xpu_available,
|
| 39 |
+
)
|
| 40 |
+
from .versions import compare_versions
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def clear_device_cache(garbage_collection=False):
|
| 44 |
+
"""
|
| 45 |
+
Clears the device cache by calling `torch.{backend}.empty_cache`. Can also run `gc.collect()`, but do note that
|
| 46 |
+
this is a *considerable* slowdown and should be used sparingly.
|
| 47 |
+
"""
|
| 48 |
+
if garbage_collection:
|
| 49 |
+
gc.collect()
|
| 50 |
+
|
| 51 |
+
if is_xpu_available():
|
| 52 |
+
torch.xpu.empty_cache()
|
| 53 |
+
elif is_mlu_available():
|
| 54 |
+
torch.mlu.empty_cache()
|
| 55 |
+
elif is_sdaa_available():
|
| 56 |
+
torch.sdaa.empty_cache()
|
| 57 |
+
elif is_musa_available():
|
| 58 |
+
torch.musa.empty_cache()
|
| 59 |
+
elif is_npu_available():
|
| 60 |
+
torch.npu.empty_cache()
|
| 61 |
+
elif is_mps_available(min_version="2.0"):
|
| 62 |
+
torch.mps.empty_cache()
|
| 63 |
+
elif is_cuda_available():
|
| 64 |
+
torch.cuda.empty_cache()
|
| 65 |
+
elif is_hpu_available():
|
| 66 |
+
# torch.hpu.empty_cache() # not available on hpu as it reserves all device memory for the current process
|
| 67 |
+
pass
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def release_memory(*objects):
|
| 71 |
+
"""
|
| 72 |
+
Releases memory from `objects` by setting them to `None` and calls `gc.collect()` and `torch.cuda.empty_cache()`.
|
| 73 |
+
Returned objects should be reassigned to the same variables.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
objects (`Iterable`):
|
| 77 |
+
An iterable of objects
|
| 78 |
+
Returns:
|
| 79 |
+
A list of `None` objects to replace `objects`
|
| 80 |
+
|
| 81 |
+
Example:
|
| 82 |
+
|
| 83 |
+
```python
|
| 84 |
+
>>> import torch
|
| 85 |
+
>>> from accelerate.utils import release_memory
|
| 86 |
+
|
| 87 |
+
>>> a = torch.ones(1000, 1000).cuda()
|
| 88 |
+
>>> b = torch.ones(1000, 1000).cuda()
|
| 89 |
+
>>> a, b = release_memory(a, b)
|
| 90 |
+
```
|
| 91 |
+
"""
|
| 92 |
+
if not isinstance(objects, list):
|
| 93 |
+
objects = list(objects)
|
| 94 |
+
for i in range(len(objects)):
|
| 95 |
+
objects[i] = None
|
| 96 |
+
clear_device_cache(garbage_collection=True)
|
| 97 |
+
return objects
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def should_reduce_batch_size(exception: Exception) -> bool:
|
| 101 |
+
"""
|
| 102 |
+
Checks if `exception` relates to CUDA out-of-memory, XPU out-of-memory, CUDNN not supported, or CPU out-of-memory
|
| 103 |
+
|
| 104 |
+
Args:
|
| 105 |
+
exception (`Exception`):
|
| 106 |
+
An exception
|
| 107 |
+
"""
|
| 108 |
+
_statements = [
|
| 109 |
+
" out of memory.", # OOM for CUDA, HIP, XPU
|
| 110 |
+
"cuDNN error: CUDNN_STATUS_NOT_SUPPORTED.", # CUDNN SNAFU
|
| 111 |
+
"DefaultCPUAllocator: can't allocate memory", # CPU OOM
|
| 112 |
+
"FATAL ERROR :: MODULE:PT_DEVMEM Allocation failed", # HPU OOM
|
| 113 |
+
]
|
| 114 |
+
if isinstance(exception, RuntimeError) and len(exception.args) == 1:
|
| 115 |
+
return any(err in exception.args[0] for err in _statements)
|
| 116 |
+
return False
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def find_executable_batch_size(function: callable = None, starting_batch_size: int = 128):
|
| 120 |
+
"""
|
| 121 |
+
A basic decorator that will try to execute `function`. If it fails from exceptions related to out-of-memory or
|
| 122 |
+
CUDNN, the batch size is cut in half and passed to `function`
|
| 123 |
+
|
| 124 |
+
`function` must take in a `batch_size` parameter as its first argument.
|
| 125 |
+
|
| 126 |
+
Args:
|
| 127 |
+
function (`callable`, *optional*):
|
| 128 |
+
A function to wrap
|
| 129 |
+
starting_batch_size (`int`, *optional*):
|
| 130 |
+
The batch size to try and fit into memory
|
| 131 |
+
|
| 132 |
+
Example:
|
| 133 |
+
|
| 134 |
+
```python
|
| 135 |
+
>>> from accelerate.utils import find_executable_batch_size
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
>>> @find_executable_batch_size(starting_batch_size=128)
|
| 139 |
+
... def train(batch_size, model, optimizer):
|
| 140 |
+
... ...
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
>>> train(model, optimizer)
|
| 144 |
+
```
|
| 145 |
+
"""
|
| 146 |
+
if function is None:
|
| 147 |
+
return functools.partial(find_executable_batch_size, starting_batch_size=starting_batch_size)
|
| 148 |
+
|
| 149 |
+
batch_size = starting_batch_size
|
| 150 |
+
|
| 151 |
+
def decorator(*args, **kwargs):
|
| 152 |
+
nonlocal batch_size
|
| 153 |
+
clear_device_cache(garbage_collection=True)
|
| 154 |
+
params = list(inspect.signature(function).parameters.keys())
|
| 155 |
+
# Guard against user error
|
| 156 |
+
if len(params) < (len(args) + 1):
|
| 157 |
+
arg_str = ", ".join([f"{arg}={value}" for arg, value in zip(params[1:], args[1:])])
|
| 158 |
+
raise TypeError(
|
| 159 |
+
f"Batch size was passed into `{function.__name__}` as the first argument when called."
|
| 160 |
+
f"Remove this as the decorator already does so: `{function.__name__}({arg_str})`"
|
| 161 |
+
)
|
| 162 |
+
while True:
|
| 163 |
+
if batch_size == 0:
|
| 164 |
+
raise RuntimeError("No executable batch size found, reached zero.")
|
| 165 |
+
try:
|
| 166 |
+
return function(batch_size, *args, **kwargs)
|
| 167 |
+
except Exception as e:
|
| 168 |
+
if should_reduce_batch_size(e):
|
| 169 |
+
clear_device_cache(garbage_collection=True)
|
| 170 |
+
batch_size //= 2
|
| 171 |
+
else:
|
| 172 |
+
raise
|
| 173 |
+
|
| 174 |
+
return decorator
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def get_xpu_available_memory(device_index: int):
|
| 178 |
+
if is_ipex_available():
|
| 179 |
+
ipex_version = version.parse(importlib.metadata.version("intel_extension_for_pytorch"))
|
| 180 |
+
if compare_versions(ipex_version, ">=", "2.5"):
|
| 181 |
+
from intel_extension_for_pytorch.xpu import mem_get_info
|
| 182 |
+
|
| 183 |
+
return mem_get_info(device_index)[0]
|
| 184 |
+
elif version.parse(torch.__version__).release >= version.parse("2.6").release:
|
| 185 |
+
# torch.xpu.mem_get_info API is available starting from PyTorch 2.6
|
| 186 |
+
# It further requires PyTorch built with the SYCL runtime which supports API
|
| 187 |
+
# to query available device memory. If not available, exception will be
|
| 188 |
+
# raised. Version of SYCL runtime used to build PyTorch is being reported
|
| 189 |
+
# with print(torch.version.xpu) and corresponds to the version of Intel DPC++
|
| 190 |
+
# SYCL compiler. First version to support required feature is 20250001.
|
| 191 |
+
try:
|
| 192 |
+
return torch.xpu.mem_get_info(device_index)[0]
|
| 193 |
+
except Exception:
|
| 194 |
+
pass
|
| 195 |
+
|
| 196 |
+
warnings.warn(
|
| 197 |
+
"The XPU `mem_get_info` API is available in IPEX version >=2.5 or PyTorch >=2.6. The current returned available memory is incorrect. Please consider upgrading your IPEX or PyTorch version."
|
| 198 |
+
)
|
| 199 |
+
return torch.xpu.max_memory_allocated(device_index)
|
venv/lib/python3.11/site-packages/accelerate/utils/modeling.py
ADDED
|
@@ -0,0 +1,2140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import contextlib
|
| 16 |
+
import gc
|
| 17 |
+
import importlib
|
| 18 |
+
import inspect
|
| 19 |
+
import json
|
| 20 |
+
import logging
|
| 21 |
+
import os
|
| 22 |
+
import re
|
| 23 |
+
import shutil
|
| 24 |
+
import tempfile
|
| 25 |
+
import warnings
|
| 26 |
+
from collections import OrderedDict, defaultdict
|
| 27 |
+
from typing import Optional, Union
|
| 28 |
+
|
| 29 |
+
import torch
|
| 30 |
+
import torch.nn as nn
|
| 31 |
+
|
| 32 |
+
from ..state import AcceleratorState
|
| 33 |
+
from .constants import SAFE_WEIGHTS_NAME, WEIGHTS_NAME
|
| 34 |
+
from .dataclasses import AutocastKwargs, CustomDtype, DistributedType
|
| 35 |
+
from .imports import (
|
| 36 |
+
is_hpu_available,
|
| 37 |
+
is_mlu_available,
|
| 38 |
+
is_mps_available,
|
| 39 |
+
is_musa_available,
|
| 40 |
+
is_npu_available,
|
| 41 |
+
is_peft_available,
|
| 42 |
+
is_sdaa_available,
|
| 43 |
+
is_torch_xla_available,
|
| 44 |
+
is_xpu_available,
|
| 45 |
+
)
|
| 46 |
+
from .memory import clear_device_cache, get_xpu_available_memory
|
| 47 |
+
from .offload import load_offloaded_weight, offload_weight, save_offload_index
|
| 48 |
+
from .tqdm import is_tqdm_available, tqdm
|
| 49 |
+
from .versions import compare_versions, is_torch_version
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
if is_npu_available(check_device=False):
|
| 53 |
+
import torch_npu # noqa: F401
|
| 54 |
+
|
| 55 |
+
if is_mlu_available(check_device=False):
|
| 56 |
+
import torch_mlu # noqa: F401
|
| 57 |
+
|
| 58 |
+
if is_sdaa_available(check_device=False):
|
| 59 |
+
import torch_sdaa # noqa: F401
|
| 60 |
+
|
| 61 |
+
if is_musa_available(check_device=False):
|
| 62 |
+
import torch_musa # noqa: F401
|
| 63 |
+
|
| 64 |
+
from safetensors import safe_open
|
| 65 |
+
from safetensors.torch import load_file as safe_load_file
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
WEIGHTS_INDEX_NAME = "pytorch_model.bin.index.json"
|
| 69 |
+
|
| 70 |
+
logger = logging.getLogger(__name__)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def is_peft_model(model):
|
| 74 |
+
from .other import extract_model_from_parallel
|
| 75 |
+
|
| 76 |
+
if is_peft_available():
|
| 77 |
+
from peft import PeftModel
|
| 78 |
+
|
| 79 |
+
return is_peft_available() and isinstance(extract_model_from_parallel(model), PeftModel)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def check_device_same(first_device, second_device):
|
| 83 |
+
"""
|
| 84 |
+
Utility method to check if two `torch` devices are similar. When dealing with CUDA devices, torch throws `False`
|
| 85 |
+
for `torch.device("cuda") == torch.device("cuda:0")` whereas they should be the same
|
| 86 |
+
|
| 87 |
+
Args:
|
| 88 |
+
first_device (`torch.device`):
|
| 89 |
+
First device to check
|
| 90 |
+
second_device (`torch.device`):
|
| 91 |
+
Second device to check
|
| 92 |
+
"""
|
| 93 |
+
if first_device.type != second_device.type:
|
| 94 |
+
return False
|
| 95 |
+
|
| 96 |
+
if first_device.type != "cpu" and first_device.index is None:
|
| 97 |
+
# In case the first_device is a cuda device and have
|
| 98 |
+
# the index attribute set to `None`, default it to `0`
|
| 99 |
+
first_device = torch.device(first_device.type, index=0)
|
| 100 |
+
|
| 101 |
+
if second_device.type != "cpu" and second_device.index is None:
|
| 102 |
+
# In case the second_device is a cuda device and have
|
| 103 |
+
# the index attribute set to `None`, default it to `0`
|
| 104 |
+
second_device = torch.device(second_device.type, index=0)
|
| 105 |
+
|
| 106 |
+
return first_device == second_device
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def convert_file_size_to_int(size: Union[int, str]):
|
| 110 |
+
"""
|
| 111 |
+
Converts a size expressed as a string with digits an unit (like `"5MB"`) to an integer (in bytes).
|
| 112 |
+
|
| 113 |
+
Args:
|
| 114 |
+
size (`int` or `str`): The size to convert. Will be directly returned if an `int`.
|
| 115 |
+
|
| 116 |
+
Example:
|
| 117 |
+
|
| 118 |
+
```py
|
| 119 |
+
>>> convert_file_size_to_int("1MiB")
|
| 120 |
+
1048576
|
| 121 |
+
```
|
| 122 |
+
"""
|
| 123 |
+
mem_size = -1
|
| 124 |
+
err_msg = (
|
| 125 |
+
f"`size` {size} is not in a valid format. Use an integer for bytes, or a string with an unit (like '5.0GB')."
|
| 126 |
+
)
|
| 127 |
+
try:
|
| 128 |
+
if isinstance(size, int):
|
| 129 |
+
mem_size = size
|
| 130 |
+
elif size.upper().endswith("GIB"):
|
| 131 |
+
mem_size = int(float(size[:-3]) * (2**30))
|
| 132 |
+
elif size.upper().endswith("MIB"):
|
| 133 |
+
mem_size = int(float(size[:-3]) * (2**20))
|
| 134 |
+
elif size.upper().endswith("KIB"):
|
| 135 |
+
mem_size = int(float(size[:-3]) * (2**10))
|
| 136 |
+
elif size.upper().endswith("GB"):
|
| 137 |
+
int_size = int(float(size[:-2]) * (10**9))
|
| 138 |
+
mem_size = int_size // 8 if size.endswith("b") else int_size
|
| 139 |
+
elif size.upper().endswith("MB"):
|
| 140 |
+
int_size = int(float(size[:-2]) * (10**6))
|
| 141 |
+
mem_size = int_size // 8 if size.endswith("b") else int_size
|
| 142 |
+
elif size.upper().endswith("KB"):
|
| 143 |
+
int_size = int(float(size[:-2]) * (10**3))
|
| 144 |
+
mem_size = int_size // 8 if size.endswith("b") else int_size
|
| 145 |
+
except ValueError:
|
| 146 |
+
raise ValueError(err_msg)
|
| 147 |
+
|
| 148 |
+
if mem_size < 0:
|
| 149 |
+
raise ValueError(err_msg)
|
| 150 |
+
return mem_size
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def dtype_byte_size(dtype: torch.dtype):
|
| 154 |
+
"""
|
| 155 |
+
Returns the size (in bytes) occupied by one parameter of type `dtype`.
|
| 156 |
+
|
| 157 |
+
Example:
|
| 158 |
+
|
| 159 |
+
```py
|
| 160 |
+
>>> dtype_byte_size(torch.float32)
|
| 161 |
+
4
|
| 162 |
+
```
|
| 163 |
+
"""
|
| 164 |
+
if dtype == torch.bool:
|
| 165 |
+
return 1 / 8
|
| 166 |
+
elif dtype == CustomDtype.INT2:
|
| 167 |
+
return 1 / 4
|
| 168 |
+
elif dtype == CustomDtype.INT4:
|
| 169 |
+
return 1 / 2
|
| 170 |
+
elif dtype == CustomDtype.FP8:
|
| 171 |
+
return 1
|
| 172 |
+
elif is_torch_version(">=", "2.1.0") and dtype == torch.float8_e4m3fn:
|
| 173 |
+
return 1
|
| 174 |
+
bit_search = re.search(r"[^\d](\d+)$", str(dtype))
|
| 175 |
+
if bit_search is None:
|
| 176 |
+
raise ValueError(f"`dtype` is not a valid dtype: {dtype}.")
|
| 177 |
+
bit_size = int(bit_search.groups()[0])
|
| 178 |
+
return bit_size // 8
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def id_tensor_storage(tensor: torch.Tensor) -> tuple[torch.device, int, int]:
|
| 182 |
+
"""
|
| 183 |
+
Unique identifier to a tensor storage. Multiple different tensors can share the same underlying storage. For
|
| 184 |
+
example, "meta" tensors all share the same storage, and thus their identifier will all be equal. This identifier is
|
| 185 |
+
guaranteed to be unique and constant for this tensor's storage during its lifetime. Two tensor storages with
|
| 186 |
+
non-overlapping lifetimes may have the same id.
|
| 187 |
+
"""
|
| 188 |
+
_SIZE = {
|
| 189 |
+
torch.int64: 8,
|
| 190 |
+
torch.float32: 4,
|
| 191 |
+
torch.int32: 4,
|
| 192 |
+
torch.bfloat16: 2,
|
| 193 |
+
torch.float16: 2,
|
| 194 |
+
torch.int16: 2,
|
| 195 |
+
torch.uint8: 1,
|
| 196 |
+
torch.int8: 1,
|
| 197 |
+
torch.bool: 1,
|
| 198 |
+
torch.float64: 8,
|
| 199 |
+
}
|
| 200 |
+
try:
|
| 201 |
+
storage_ptr = tensor.untyped_storage().data_ptr()
|
| 202 |
+
storage_size = tensor.untyped_storage().nbytes()
|
| 203 |
+
except Exception:
|
| 204 |
+
try:
|
| 205 |
+
# Fallback for torch==1.10
|
| 206 |
+
storage_ptr = tensor.storage().data_ptr()
|
| 207 |
+
storage_size = tensor.storage().size() * _SIZE[tensor.dtype]
|
| 208 |
+
except NotImplementedError:
|
| 209 |
+
# Fallback for meta storage
|
| 210 |
+
storage_ptr = 0
|
| 211 |
+
# On torch >=2.0 this is the tensor size
|
| 212 |
+
storage_size = tensor.nelement() * _SIZE[tensor.dtype]
|
| 213 |
+
|
| 214 |
+
return tensor.device, storage_ptr, storage_size
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
def set_module_tensor_to_device(
|
| 218 |
+
module: nn.Module,
|
| 219 |
+
tensor_name: str,
|
| 220 |
+
device: Union[int, str, torch.device],
|
| 221 |
+
value: Optional[torch.Tensor] = None,
|
| 222 |
+
dtype: Optional[Union[str, torch.dtype]] = None,
|
| 223 |
+
fp16_statistics: Optional[torch.HalfTensor] = None,
|
| 224 |
+
tied_params_map: Optional[dict[int, dict[torch.device, torch.Tensor]]] = None,
|
| 225 |
+
):
|
| 226 |
+
"""
|
| 227 |
+
A helper function to set a given tensor (parameter of buffer) of a module on a specific device (note that doing
|
| 228 |
+
`param.to(device)` creates a new tensor not linked to the parameter, which is why we need this function).
|
| 229 |
+
|
| 230 |
+
Args:
|
| 231 |
+
module (`torch.nn.Module`):
|
| 232 |
+
The module in which the tensor we want to move lives.
|
| 233 |
+
tensor_name (`str`):
|
| 234 |
+
The full name of the parameter/buffer.
|
| 235 |
+
device (`int`, `str` or `torch.device`):
|
| 236 |
+
The device on which to set the tensor.
|
| 237 |
+
value (`torch.Tensor`, *optional*):
|
| 238 |
+
The value of the tensor (useful when going from the meta device to any other device).
|
| 239 |
+
dtype (`torch.dtype`, *optional*):
|
| 240 |
+
If passed along the value of the parameter will be cast to this `dtype`. Otherwise, `value` will be cast to
|
| 241 |
+
the dtype of the existing parameter in the model.
|
| 242 |
+
fp16_statistics (`torch.HalfTensor`, *optional*):
|
| 243 |
+
The list of fp16 statistics to set on the module, used for 8 bit model serialization.
|
| 244 |
+
tied_params_map (Dict[int, Dict[torch.device, torch.Tensor]], *optional*, defaults to `None`):
|
| 245 |
+
A map of current data pointers to dictionaries of devices to already dispatched tied weights. For a given
|
| 246 |
+
execution device, this parameter is useful to reuse the first available pointer of a shared weight on the
|
| 247 |
+
device for all others, instead of duplicating memory.
|
| 248 |
+
"""
|
| 249 |
+
# Recurse if needed
|
| 250 |
+
if "." in tensor_name:
|
| 251 |
+
splits = tensor_name.split(".")
|
| 252 |
+
for split in splits[:-1]:
|
| 253 |
+
new_module = getattr(module, split)
|
| 254 |
+
if new_module is None:
|
| 255 |
+
raise ValueError(f"{module} has no attribute {split}.")
|
| 256 |
+
module = new_module
|
| 257 |
+
tensor_name = splits[-1]
|
| 258 |
+
|
| 259 |
+
if tensor_name not in module._parameters and tensor_name not in module._buffers:
|
| 260 |
+
raise ValueError(f"{module} does not have a parameter or a buffer named {tensor_name}.")
|
| 261 |
+
is_buffer = tensor_name in module._buffers
|
| 262 |
+
old_value = getattr(module, tensor_name)
|
| 263 |
+
|
| 264 |
+
# Treat the case where old_value (or a custom `value`, typically offloaded to RAM/disk) belongs to a tied group, and one of the weight
|
| 265 |
+
# in the tied group has already been dispatched to the device, by avoiding reallocating memory on the device and just copying the pointer.
|
| 266 |
+
if (
|
| 267 |
+
value is not None
|
| 268 |
+
and tied_params_map is not None
|
| 269 |
+
and value.data_ptr() in tied_params_map
|
| 270 |
+
and device in tied_params_map[value.data_ptr()]
|
| 271 |
+
):
|
| 272 |
+
module._parameters[tensor_name] = tied_params_map[value.data_ptr()][device]
|
| 273 |
+
return
|
| 274 |
+
elif (
|
| 275 |
+
tied_params_map is not None
|
| 276 |
+
and old_value.data_ptr() in tied_params_map
|
| 277 |
+
and device in tied_params_map[old_value.data_ptr()]
|
| 278 |
+
):
|
| 279 |
+
module._parameters[tensor_name] = tied_params_map[old_value.data_ptr()][device]
|
| 280 |
+
return
|
| 281 |
+
|
| 282 |
+
if old_value.device == torch.device("meta") and device not in ["meta", torch.device("meta")] and value is None:
|
| 283 |
+
raise ValueError(f"{tensor_name} is on the meta device, we need a `value` to put in on {device}.")
|
| 284 |
+
|
| 285 |
+
param = module._parameters[tensor_name] if tensor_name in module._parameters else None
|
| 286 |
+
param_cls = type(param)
|
| 287 |
+
|
| 288 |
+
if value is not None:
|
| 289 |
+
# We can expect mismatches when using bnb 4bit since Params4bit will reshape and pack the weights.
|
| 290 |
+
# In other cases, we want to make sure we're not loading checkpoints that do not match the config.
|
| 291 |
+
if old_value.shape != value.shape and param_cls.__name__ != "Params4bit":
|
| 292 |
+
raise ValueError(
|
| 293 |
+
f'Trying to set a tensor of shape {value.shape} in "{tensor_name}" (which has shape {old_value.shape}), this looks incorrect.'
|
| 294 |
+
)
|
| 295 |
+
|
| 296 |
+
if dtype is None:
|
| 297 |
+
# For compatibility with PyTorch load_state_dict which converts state dict dtype to existing dtype in model
|
| 298 |
+
value = value.to(old_value.dtype)
|
| 299 |
+
elif not str(value.dtype).startswith(("torch.uint", "torch.int", "torch.bool")):
|
| 300 |
+
value = value.to(dtype)
|
| 301 |
+
|
| 302 |
+
device_quantization = None
|
| 303 |
+
with torch.no_grad():
|
| 304 |
+
# leave it on cpu first before moving them to cuda
|
| 305 |
+
# # fix the case where the device is meta, we don't want to put it on cpu because there is no data =0
|
| 306 |
+
if (
|
| 307 |
+
param is not None
|
| 308 |
+
and param.device.type != "cuda"
|
| 309 |
+
and torch.device(device).type == "cuda"
|
| 310 |
+
and param_cls.__name__ in ["Int8Params", "FP4Params", "Params4bit"]
|
| 311 |
+
):
|
| 312 |
+
device_quantization = device
|
| 313 |
+
device = "cpu"
|
| 314 |
+
# `torch.Tensor.to(<int num>)` is not supported by `torch_npu` (see this [issue](https://github.com/Ascend/pytorch/issues/16)).
|
| 315 |
+
if isinstance(device, int):
|
| 316 |
+
if is_npu_available():
|
| 317 |
+
device = f"npu:{device}"
|
| 318 |
+
elif is_mlu_available():
|
| 319 |
+
device = f"mlu:{device}"
|
| 320 |
+
elif is_sdaa_available():
|
| 321 |
+
device = f"sdaa:{device}"
|
| 322 |
+
elif is_musa_available():
|
| 323 |
+
device = f"musa:{device}"
|
| 324 |
+
elif is_hpu_available():
|
| 325 |
+
device = "hpu"
|
| 326 |
+
if "xpu" in str(device) and not is_xpu_available():
|
| 327 |
+
raise ValueError(f'{device} is not available, you should use device="cpu" instead')
|
| 328 |
+
if value is None:
|
| 329 |
+
new_value = old_value.to(device)
|
| 330 |
+
if dtype is not None and device in ["meta", torch.device("meta")]:
|
| 331 |
+
if not str(old_value.dtype).startswith(("torch.uint", "torch.int", "torch.bool")):
|
| 332 |
+
new_value = new_value.to(dtype)
|
| 333 |
+
|
| 334 |
+
if not is_buffer:
|
| 335 |
+
module._parameters[tensor_name] = param_cls(new_value, requires_grad=old_value.requires_grad)
|
| 336 |
+
elif isinstance(value, torch.Tensor):
|
| 337 |
+
new_value = value.to(device)
|
| 338 |
+
else:
|
| 339 |
+
new_value = torch.tensor(value, device=device)
|
| 340 |
+
if device_quantization is not None:
|
| 341 |
+
device = device_quantization
|
| 342 |
+
if is_buffer:
|
| 343 |
+
module._buffers[tensor_name] = new_value
|
| 344 |
+
elif value is not None or not check_device_same(torch.device(device), module._parameters[tensor_name].device):
|
| 345 |
+
param_cls = type(module._parameters[tensor_name])
|
| 346 |
+
kwargs = module._parameters[tensor_name].__dict__
|
| 347 |
+
if param_cls.__name__ in ["Int8Params", "FP4Params", "Params4bit"]:
|
| 348 |
+
if param_cls.__name__ == "Int8Params" and new_value.dtype == torch.float32:
|
| 349 |
+
# downcast to fp16 if any - needed for 8bit serialization
|
| 350 |
+
new_value = new_value.to(torch.float16)
|
| 351 |
+
# quantize module that are going to stay on the cpu so that we offload quantized weights
|
| 352 |
+
if device == "cpu" and param_cls.__name__ == "Int8Params":
|
| 353 |
+
new_value = param_cls(new_value, requires_grad=old_value.requires_grad, **kwargs).to(0).to("cpu")
|
| 354 |
+
new_value.CB = new_value.CB.to("cpu")
|
| 355 |
+
new_value.SCB = new_value.SCB.to("cpu")
|
| 356 |
+
else:
|
| 357 |
+
new_value = param_cls(new_value, requires_grad=old_value.requires_grad, **kwargs).to(device)
|
| 358 |
+
elif param_cls.__name__ in ["QTensor", "QBitsTensor"]:
|
| 359 |
+
new_value = torch.nn.Parameter(new_value, requires_grad=old_value.requires_grad).to(device)
|
| 360 |
+
elif param_cls.__name__ in ["AffineQuantizedTensor"]:
|
| 361 |
+
if importlib.util.find_spec("torchao") is not None and compare_versions("torchao", ">=", "0.7.0"):
|
| 362 |
+
# TorchAO v0.7.0 made layout_tensor an internal private variable and exposed tensor_impl
|
| 363 |
+
args = (new_value.tensor_impl,)
|
| 364 |
+
else:
|
| 365 |
+
args = (new_value.layout_tensor,)
|
| 366 |
+
args += (
|
| 367 |
+
new_value.block_size,
|
| 368 |
+
new_value.shape,
|
| 369 |
+
new_value.quant_min,
|
| 370 |
+
new_value.quant_max,
|
| 371 |
+
new_value.zero_point_domain,
|
| 372 |
+
)
|
| 373 |
+
new_value = torch.nn.Parameter(param_cls(*args), requires_grad=old_value.requires_grad).to(device)
|
| 374 |
+
else:
|
| 375 |
+
new_value = param_cls(new_value, requires_grad=old_value.requires_grad).to(device)
|
| 376 |
+
|
| 377 |
+
module._parameters[tensor_name] = new_value
|
| 378 |
+
if fp16_statistics is not None:
|
| 379 |
+
module._parameters[tensor_name].SCB = fp16_statistics.to(device)
|
| 380 |
+
del fp16_statistics
|
| 381 |
+
# as we put the weight to meta, it doesn't have SCB attr anymore. make sure that it is not a meta weight
|
| 382 |
+
if (
|
| 383 |
+
module.__class__.__name__ == "Linear8bitLt"
|
| 384 |
+
and getattr(module.weight, "SCB", None) is None
|
| 385 |
+
and str(module.weight.device) != "meta"
|
| 386 |
+
):
|
| 387 |
+
# quantize only if necessary
|
| 388 |
+
device_index = torch.device(device).index if torch.device(device).type == "cuda" else None
|
| 389 |
+
if not getattr(module.weight, "SCB", None) and device_index is not None:
|
| 390 |
+
if module.bias is not None and module.bias.device.type != "meta":
|
| 391 |
+
# if a bias exists, we need to wait until the bias is set on the correct device
|
| 392 |
+
module = module.cuda(device_index)
|
| 393 |
+
elif module.bias is None:
|
| 394 |
+
# if no bias exists, we can quantize right away
|
| 395 |
+
module = module.cuda(device_index)
|
| 396 |
+
elif (
|
| 397 |
+
module.__class__.__name__ == "Linear4bit"
|
| 398 |
+
and getattr(module.weight, "quant_state", None) is None
|
| 399 |
+
and str(module.weight.device) != "meta"
|
| 400 |
+
):
|
| 401 |
+
# quantize only if necessary
|
| 402 |
+
device_index = torch.device(device).index if torch.device(device).type == "cuda" else None
|
| 403 |
+
if not getattr(module.weight, "quant_state", None) and device_index is not None:
|
| 404 |
+
module.weight = module.weight.cuda(device_index)
|
| 405 |
+
# clean pre and post foward hook
|
| 406 |
+
if device != "cpu":
|
| 407 |
+
clear_device_cache()
|
| 408 |
+
|
| 409 |
+
# When handling tied weights, we update tied_params_map to keep track of the tied weights that have already been allocated on the device in
|
| 410 |
+
# order to avoid duplicating memory, see above.
|
| 411 |
+
if (
|
| 412 |
+
tied_params_map is not None
|
| 413 |
+
and old_value.data_ptr() in tied_params_map
|
| 414 |
+
and device not in tied_params_map[old_value.data_ptr()]
|
| 415 |
+
):
|
| 416 |
+
tied_params_map[old_value.data_ptr()][device] = new_value
|
| 417 |
+
elif (
|
| 418 |
+
value is not None
|
| 419 |
+
and tied_params_map is not None
|
| 420 |
+
and value.data_ptr() in tied_params_map
|
| 421 |
+
and device not in tied_params_map[value.data_ptr()]
|
| 422 |
+
):
|
| 423 |
+
tied_params_map[value.data_ptr()][device] = new_value
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
def named_module_tensors(
|
| 427 |
+
module: nn.Module, include_buffers: bool = True, recurse: bool = False, remove_non_persistent: bool = False
|
| 428 |
+
):
|
| 429 |
+
"""
|
| 430 |
+
A helper function that gathers all the tensors (parameters + buffers) of a given module. If `include_buffers=True`
|
| 431 |
+
it's the same as doing `module.named_parameters(recurse=recurse) + module.named_buffers(recurse=recurse)`.
|
| 432 |
+
|
| 433 |
+
Args:
|
| 434 |
+
module (`torch.nn.Module`):
|
| 435 |
+
The module we want the tensors on.
|
| 436 |
+
include_buffer (`bool`, *optional*, defaults to `True`):
|
| 437 |
+
Whether or not to include the buffers in the result.
|
| 438 |
+
recurse (`bool`, *optional`, defaults to `False`):
|
| 439 |
+
Whether or not to go look in every submodule or just return the direct parameters and buffers.
|
| 440 |
+
remove_non_persistent (`bool`, *optional*, defaults to `False`):
|
| 441 |
+
Whether or not to remove the non persistent buffer from the buffers. Useful only when include_buffers =
|
| 442 |
+
True
|
| 443 |
+
"""
|
| 444 |
+
yield from module.named_parameters(recurse=recurse)
|
| 445 |
+
|
| 446 |
+
if include_buffers:
|
| 447 |
+
non_persistent_buffers = set()
|
| 448 |
+
if remove_non_persistent:
|
| 449 |
+
non_persistent_buffers = get_non_persistent_buffers(module, recurse=recurse)
|
| 450 |
+
for named_buffer in module.named_buffers(recurse=recurse):
|
| 451 |
+
name, _ = named_buffer
|
| 452 |
+
if name not in non_persistent_buffers:
|
| 453 |
+
yield named_buffer
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
def get_non_persistent_buffers(module: nn.Module, recurse: bool = False):
|
| 457 |
+
"""
|
| 458 |
+
Gather all non persistent buffers of a given modules into a set
|
| 459 |
+
|
| 460 |
+
Args:
|
| 461 |
+
module (`nn.Module`):
|
| 462 |
+
The module we want the non persistent buffers on.
|
| 463 |
+
recurse (`bool`, *optional*, defaults to `False`):
|
| 464 |
+
Whether or not to go look in every submodule or just return the direct non persistent buffers.
|
| 465 |
+
"""
|
| 466 |
+
|
| 467 |
+
non_persistent_buffers_set = module._non_persistent_buffers_set
|
| 468 |
+
if recurse:
|
| 469 |
+
for _, m in module.named_modules():
|
| 470 |
+
non_persistent_buffers_set |= m._non_persistent_buffers_set
|
| 471 |
+
|
| 472 |
+
return non_persistent_buffers_set
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
class FindTiedParametersResult(list):
|
| 476 |
+
"""
|
| 477 |
+
This is a subclass of a list to handle backward compatibility for Transformers. Do not rely on the fact this is not
|
| 478 |
+
a list or on the `values` method as in the future this will be removed.
|
| 479 |
+
"""
|
| 480 |
+
|
| 481 |
+
def __init__(self, *args, **kwargs):
|
| 482 |
+
super().__init__(*args, **kwargs)
|
| 483 |
+
|
| 484 |
+
def values(self):
|
| 485 |
+
warnings.warn(
|
| 486 |
+
"The 'values' method of FindTiedParametersResult is deprecated and will be removed in Accelerate v1.3.0. ",
|
| 487 |
+
FutureWarning,
|
| 488 |
+
)
|
| 489 |
+
return sum([x[1:] for x in self], [])
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
def check_tied_parameters_in_config(model: nn.Module):
|
| 493 |
+
"""
|
| 494 |
+
Check if there is any indication in the given model that some weights should be tied.
|
| 495 |
+
|
| 496 |
+
Args:
|
| 497 |
+
model (`torch.nn.Module`): The model to inspect
|
| 498 |
+
|
| 499 |
+
Returns:
|
| 500 |
+
bool: True if the model needs to have tied weights
|
| 501 |
+
"""
|
| 502 |
+
|
| 503 |
+
# based on model.tie_weights() method
|
| 504 |
+
has_tied_word_embedding = False
|
| 505 |
+
has_tied_encoder_decoder = False
|
| 506 |
+
has_tied_module = False
|
| 507 |
+
|
| 508 |
+
if "PreTrainedModel" in [c.__name__ for c in inspect.getmro(model.__class__)]:
|
| 509 |
+
has_tied_word_embedding = (
|
| 510 |
+
hasattr(model, "config")
|
| 511 |
+
and getattr(model.config, "tie_word_embeddings", False)
|
| 512 |
+
and model.get_output_embeddings()
|
| 513 |
+
)
|
| 514 |
+
has_tied_encoder_decoder = (
|
| 515 |
+
hasattr(model, "config")
|
| 516 |
+
and getattr(model.config, "is_encoder_decoder", False)
|
| 517 |
+
and getattr(model.config, "tie_encoder_decoder", False)
|
| 518 |
+
)
|
| 519 |
+
has_tied_module = any(hasattr(module, "_tie_weights") for module in model.modules())
|
| 520 |
+
|
| 521 |
+
return any([has_tied_word_embedding, has_tied_encoder_decoder, has_tied_module])
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
def _get_param_device(param, device_map):
|
| 525 |
+
if param in device_map:
|
| 526 |
+
return device_map[param]
|
| 527 |
+
parent_param = ".".join(param.split(".")[:-1])
|
| 528 |
+
if parent_param == param:
|
| 529 |
+
raise ValueError(f"The `device_map` does not contain the module {param}.")
|
| 530 |
+
else:
|
| 531 |
+
return _get_param_device(parent_param, device_map)
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
def check_tied_parameters_on_same_device(tied_params, device_map):
|
| 535 |
+
"""
|
| 536 |
+
Check if tied parameters are on the same device
|
| 537 |
+
|
| 538 |
+
Args:
|
| 539 |
+
tied_params (`List[List[str]]`):
|
| 540 |
+
A list of lists of parameter names being all tied together.
|
| 541 |
+
|
| 542 |
+
device_map (`Dict[str, Union[int, str, torch.device]]`):
|
| 543 |
+
A map that specifies where each submodule should go.
|
| 544 |
+
|
| 545 |
+
"""
|
| 546 |
+
for tie_param in tied_params:
|
| 547 |
+
tie_param_devices = {}
|
| 548 |
+
for param in tie_param:
|
| 549 |
+
tie_param_devices[param] = _get_param_device(param, device_map)
|
| 550 |
+
if len(set(tie_param_devices.values())) > 1:
|
| 551 |
+
logger.warn(
|
| 552 |
+
f"Tied parameters are on different devices: {tie_param_devices}. "
|
| 553 |
+
"Please modify your custom device map or set `device_map='auto'`. "
|
| 554 |
+
)
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
def find_tied_parameters(model: torch.nn.Module, **kwargs):
|
| 558 |
+
"""
|
| 559 |
+
Find the tied parameters in a given model.
|
| 560 |
+
|
| 561 |
+
<Tip warning={true}>
|
| 562 |
+
|
| 563 |
+
The signature accepts keyword arguments, but they are for the recursive part of this function and you should ignore
|
| 564 |
+
them.
|
| 565 |
+
|
| 566 |
+
</Tip>
|
| 567 |
+
|
| 568 |
+
Args:
|
| 569 |
+
model (`torch.nn.Module`): The model to inspect.
|
| 570 |
+
|
| 571 |
+
Returns:
|
| 572 |
+
List[List[str]]: A list of lists of parameter names being all tied together.
|
| 573 |
+
|
| 574 |
+
Example:
|
| 575 |
+
|
| 576 |
+
```py
|
| 577 |
+
>>> from collections import OrderedDict
|
| 578 |
+
>>> import torch.nn as nn
|
| 579 |
+
|
| 580 |
+
>>> model = nn.Sequential(OrderedDict([("linear1", nn.Linear(4, 4)), ("linear2", nn.Linear(4, 4))]))
|
| 581 |
+
>>> model.linear2.weight = model.linear1.weight
|
| 582 |
+
>>> find_tied_parameters(model)
|
| 583 |
+
[['linear1.weight', 'linear2.weight']]
|
| 584 |
+
```
|
| 585 |
+
"""
|
| 586 |
+
|
| 587 |
+
# get ALL model parameters and their names
|
| 588 |
+
all_named_parameters = {name: param for name, param in model.named_parameters(remove_duplicate=False)}
|
| 589 |
+
|
| 590 |
+
# get ONLY unique named parameters,
|
| 591 |
+
# if parameter is tied and have multiple names, it will be included only once
|
| 592 |
+
no_duplicate_named_parameters = {name: param for name, param in model.named_parameters(remove_duplicate=True)}
|
| 593 |
+
|
| 594 |
+
# the difference of the two sets will give us the tied parameters
|
| 595 |
+
tied_param_names = set(all_named_parameters.keys()) - set(no_duplicate_named_parameters.keys())
|
| 596 |
+
|
| 597 |
+
# 'tied_param_names' contains the names of parameters that are tied in the model, but we do not know
|
| 598 |
+
# which names refer to the same parameter. To identify this, we need to group them together.
|
| 599 |
+
tied_param_groups = {}
|
| 600 |
+
for tied_param_name in tied_param_names:
|
| 601 |
+
tied_param = all_named_parameters[tied_param_name]
|
| 602 |
+
for param_name, param in no_duplicate_named_parameters.items():
|
| 603 |
+
# compare if parameters are the same, if so, group their names together
|
| 604 |
+
if param is tied_param:
|
| 605 |
+
if param_name not in tied_param_groups:
|
| 606 |
+
tied_param_groups[param_name] = []
|
| 607 |
+
tied_param_groups[param_name].append(tied_param_name)
|
| 608 |
+
|
| 609 |
+
return FindTiedParametersResult([sorted([weight] + list(set(tied))) for weight, tied in tied_param_groups.items()])
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
def retie_parameters(model, tied_params):
|
| 613 |
+
"""
|
| 614 |
+
Reties tied parameters in a given model if the link was broken (for instance when adding hooks).
|
| 615 |
+
|
| 616 |
+
Args:
|
| 617 |
+
model (`torch.nn.Module`):
|
| 618 |
+
The model in which to retie parameters.
|
| 619 |
+
tied_params (`List[List[str]]`):
|
| 620 |
+
A mapping parameter name to tied parameter name as obtained by `find_tied_parameters`.
|
| 621 |
+
"""
|
| 622 |
+
for tied_group in tied_params:
|
| 623 |
+
param_to_tie = None
|
| 624 |
+
# two loops : the first one to set param_to_tie , the second one to change the values of tied_group
|
| 625 |
+
for param_name in tied_group:
|
| 626 |
+
module = model
|
| 627 |
+
splits = param_name.split(".")
|
| 628 |
+
for split in splits[:-1]:
|
| 629 |
+
module = getattr(module, split)
|
| 630 |
+
param = getattr(module, splits[-1])
|
| 631 |
+
if param_to_tie is None and param.device != torch.device("meta"):
|
| 632 |
+
param_to_tie = param
|
| 633 |
+
break
|
| 634 |
+
if param_to_tie is not None:
|
| 635 |
+
for param_name in tied_group:
|
| 636 |
+
module = model
|
| 637 |
+
splits = param_name.split(".")
|
| 638 |
+
for split in splits[:-1]:
|
| 639 |
+
module = getattr(module, split)
|
| 640 |
+
setattr(module, splits[-1], param_to_tie)
|
| 641 |
+
|
| 642 |
+
|
| 643 |
+
def _get_proper_dtype(dtype: Union[str, torch.device]) -> torch.dtype:
|
| 644 |
+
"""
|
| 645 |
+
Just does torch.dtype(dtype) if necessary.
|
| 646 |
+
"""
|
| 647 |
+
if isinstance(dtype, str):
|
| 648 |
+
# We accept "torch.float16" or just "float16"
|
| 649 |
+
dtype = dtype.replace("torch.", "")
|
| 650 |
+
dtype = getattr(torch, dtype)
|
| 651 |
+
return dtype
|
| 652 |
+
|
| 653 |
+
|
| 654 |
+
def compute_module_sizes(
|
| 655 |
+
model: nn.Module,
|
| 656 |
+
dtype: Optional[Union[str, torch.device]] = None,
|
| 657 |
+
special_dtypes: Optional[dict[str, Union[str, torch.device]]] = None,
|
| 658 |
+
buffers_only: bool = False,
|
| 659 |
+
):
|
| 660 |
+
"""
|
| 661 |
+
Compute the size of each submodule of a given model.
|
| 662 |
+
"""
|
| 663 |
+
if dtype is not None:
|
| 664 |
+
dtype = _get_proper_dtype(dtype)
|
| 665 |
+
dtype_size = dtype_byte_size(dtype)
|
| 666 |
+
if special_dtypes is not None:
|
| 667 |
+
special_dtypes = {key: _get_proper_dtype(dtyp) for key, dtyp in special_dtypes.items()}
|
| 668 |
+
special_dtypes_size = {key: dtype_byte_size(dtyp) for key, dtyp in special_dtypes.items()}
|
| 669 |
+
module_sizes = defaultdict(int)
|
| 670 |
+
|
| 671 |
+
module_list = []
|
| 672 |
+
|
| 673 |
+
if not buffers_only:
|
| 674 |
+
module_list = named_module_tensors(model, recurse=True)
|
| 675 |
+
else:
|
| 676 |
+
module_list = model.named_buffers(recurse=True)
|
| 677 |
+
|
| 678 |
+
for name, tensor in module_list:
|
| 679 |
+
if special_dtypes is not None and name in special_dtypes:
|
| 680 |
+
size = tensor.numel() * special_dtypes_size[name]
|
| 681 |
+
elif dtype is None:
|
| 682 |
+
size = tensor.numel() * dtype_byte_size(tensor.dtype)
|
| 683 |
+
elif str(tensor.dtype).startswith(("torch.uint", "torch.int", "torch.bool")):
|
| 684 |
+
# According to the code in set_module_tensor_to_device, these types won't be converted
|
| 685 |
+
# so use their original size here
|
| 686 |
+
size = tensor.numel() * dtype_byte_size(tensor.dtype)
|
| 687 |
+
else:
|
| 688 |
+
size = tensor.numel() * min(dtype_size, dtype_byte_size(tensor.dtype))
|
| 689 |
+
name_parts = name.split(".")
|
| 690 |
+
for idx in range(len(name_parts) + 1):
|
| 691 |
+
module_sizes[".".join(name_parts[:idx])] += size
|
| 692 |
+
|
| 693 |
+
return module_sizes
|
| 694 |
+
|
| 695 |
+
|
| 696 |
+
def compute_module_total_buffer_size(
|
| 697 |
+
model: nn.Module,
|
| 698 |
+
dtype: Optional[Union[str, torch.device]] = None,
|
| 699 |
+
special_dtypes: Optional[dict[str, Union[str, torch.device]]] = None,
|
| 700 |
+
):
|
| 701 |
+
"""
|
| 702 |
+
Compute the total size of buffers in each submodule of a given model.
|
| 703 |
+
"""
|
| 704 |
+
module_sizes = compute_module_sizes(model, dtype=dtype, special_dtypes=special_dtypes, buffers_only=True)
|
| 705 |
+
return module_sizes.get("", 0)
|
| 706 |
+
|
| 707 |
+
|
| 708 |
+
def get_max_layer_size(
|
| 709 |
+
modules: list[tuple[str, torch.nn.Module]], module_sizes: dict[str, int], no_split_module_classes: list[str]
|
| 710 |
+
):
|
| 711 |
+
"""
|
| 712 |
+
Utility function that will scan a list of named modules and return the maximum size used by one full layer. The
|
| 713 |
+
definition of a layer being:
|
| 714 |
+
- a module with no direct children (just parameters and buffers)
|
| 715 |
+
- a module whose class name is in the list `no_split_module_classes`
|
| 716 |
+
|
| 717 |
+
Args:
|
| 718 |
+
modules (`List[Tuple[str, torch.nn.Module]]`):
|
| 719 |
+
The list of named modules where we want to determine the maximum layer size.
|
| 720 |
+
module_sizes (`Dict[str, int]`):
|
| 721 |
+
A dictionary mapping each layer name to its size (as generated by `compute_module_sizes`).
|
| 722 |
+
no_split_module_classes (`List[str]`):
|
| 723 |
+
A list of class names for layers we don't want to be split.
|
| 724 |
+
|
| 725 |
+
Returns:
|
| 726 |
+
`Tuple[int, List[str]]`: The maximum size of a layer with the list of layer names realizing that maximum size.
|
| 727 |
+
"""
|
| 728 |
+
max_size = 0
|
| 729 |
+
layer_names = []
|
| 730 |
+
modules_to_treat = modules.copy()
|
| 731 |
+
while len(modules_to_treat) > 0:
|
| 732 |
+
module_name, module = modules_to_treat.pop(0)
|
| 733 |
+
modules_children = list(module.named_children()) if isinstance(module, torch.nn.Module) else []
|
| 734 |
+
if len(modules_children) == 0 or module.__class__.__name__ in no_split_module_classes:
|
| 735 |
+
# No splitting this one so we compare to the max_size
|
| 736 |
+
size = module_sizes[module_name]
|
| 737 |
+
if size > max_size:
|
| 738 |
+
max_size = size
|
| 739 |
+
layer_names = [module_name]
|
| 740 |
+
elif size == max_size:
|
| 741 |
+
layer_names.append(module_name)
|
| 742 |
+
else:
|
| 743 |
+
modules_to_treat = [(f"{module_name}.{n}", v) for n, v in modules_children] + modules_to_treat
|
| 744 |
+
return max_size, layer_names
|
| 745 |
+
|
| 746 |
+
|
| 747 |
+
def get_max_memory(max_memory: Optional[dict[Union[int, str], Union[int, str]]] = None):
|
| 748 |
+
"""
|
| 749 |
+
Get the maximum memory available if nothing is passed, converts string to int otherwise.
|
| 750 |
+
"""
|
| 751 |
+
import psutil
|
| 752 |
+
|
| 753 |
+
if max_memory is None:
|
| 754 |
+
max_memory = {}
|
| 755 |
+
# Make sure CUDA is initialized on each GPU to have the right memory info.
|
| 756 |
+
if is_npu_available():
|
| 757 |
+
for i in range(torch.npu.device_count()):
|
| 758 |
+
try:
|
| 759 |
+
_ = torch.tensor(0, device=torch.device("npu", i))
|
| 760 |
+
max_memory[i] = torch.npu.mem_get_info(i)[0]
|
| 761 |
+
except Exception:
|
| 762 |
+
logger.info(f"Device {i} seems unavailable, Proceeding to check subsequent devices.")
|
| 763 |
+
continue
|
| 764 |
+
elif is_mlu_available():
|
| 765 |
+
for i in range(torch.mlu.device_count()):
|
| 766 |
+
try:
|
| 767 |
+
_ = torch.tensor(0, device=torch.device("mlu", i))
|
| 768 |
+
max_memory[i] = torch.mlu.mem_get_info(i)[0]
|
| 769 |
+
except Exception:
|
| 770 |
+
logger.info(f"Device {i} seems unavailable, Proceeding to check subsequent devices.")
|
| 771 |
+
continue
|
| 772 |
+
elif is_sdaa_available():
|
| 773 |
+
for i in range(torch.sdaa.device_count()):
|
| 774 |
+
try:
|
| 775 |
+
_ = torch.tensor(0, device=torch.device("sdaa", i))
|
| 776 |
+
max_memory[i] = torch.sdaa.mem_get_info(i)[0]
|
| 777 |
+
except Exception:
|
| 778 |
+
logger.info(f"Device {i} seems unavailable, Proceeding to check subsequent devices.")
|
| 779 |
+
continue
|
| 780 |
+
elif is_musa_available():
|
| 781 |
+
for i in range(torch.musa.device_count()):
|
| 782 |
+
try:
|
| 783 |
+
_ = torch.tensor(0, device=torch.device("musa", i))
|
| 784 |
+
max_memory[i] = torch.musa.mem_get_info(i)[0]
|
| 785 |
+
except Exception:
|
| 786 |
+
logger.info(f"Device {i} seems unavailable, Proceeding to check subsequent devices.")
|
| 787 |
+
continue
|
| 788 |
+
elif is_xpu_available():
|
| 789 |
+
for i in range(torch.xpu.device_count()):
|
| 790 |
+
try:
|
| 791 |
+
_ = torch.tensor(0, device=torch.device("xpu", i))
|
| 792 |
+
max_memory[i] = get_xpu_available_memory(i)
|
| 793 |
+
except Exception:
|
| 794 |
+
logger.info(f"Device {i} seems unavailable, Proceeding to check subsequent devices.")
|
| 795 |
+
continue
|
| 796 |
+
elif is_hpu_available():
|
| 797 |
+
for i in range(torch.hpu.device_count()):
|
| 798 |
+
try:
|
| 799 |
+
_ = torch.tensor(0, device=torch.device("hpu", i))
|
| 800 |
+
max_memory[i] = torch.hpu.mem_get_info(i)[0]
|
| 801 |
+
except Exception:
|
| 802 |
+
logger.info(f"Device {i} seems unavailable, Proceeding to check subsequent devices.")
|
| 803 |
+
continue
|
| 804 |
+
else:
|
| 805 |
+
for i in range(torch.cuda.device_count()):
|
| 806 |
+
try:
|
| 807 |
+
_ = torch.tensor([0], device=i)
|
| 808 |
+
max_memory[i] = torch.cuda.mem_get_info(i)[0]
|
| 809 |
+
except Exception:
|
| 810 |
+
logger.info(f"Device {i} seems unavailable, Proceeding to check subsequent devices.")
|
| 811 |
+
continue
|
| 812 |
+
# allocate everything in the mps device as the RAM is shared
|
| 813 |
+
if is_mps_available():
|
| 814 |
+
max_memory["mps"] = psutil.virtual_memory().available
|
| 815 |
+
else:
|
| 816 |
+
max_memory["cpu"] = psutil.virtual_memory().available
|
| 817 |
+
return max_memory
|
| 818 |
+
|
| 819 |
+
for key in max_memory:
|
| 820 |
+
if isinstance(max_memory[key], str):
|
| 821 |
+
max_memory[key] = convert_file_size_to_int(max_memory[key])
|
| 822 |
+
|
| 823 |
+
# Need to sort the device by type to make sure that we allocate the gpu first.
|
| 824 |
+
# As gpu/npu/xpu are represented by int, we need to sort them first.
|
| 825 |
+
gpu_devices = [k for k in max_memory.keys() if isinstance(k, int)]
|
| 826 |
+
gpu_devices.sort()
|
| 827 |
+
# check if gpu/npu/xpu devices are available and if not, throw a warning
|
| 828 |
+
if is_npu_available():
|
| 829 |
+
num_devices = torch.npu.device_count()
|
| 830 |
+
elif is_mlu_available():
|
| 831 |
+
num_devices = torch.mlu.device_count()
|
| 832 |
+
elif is_sdaa_available():
|
| 833 |
+
num_devices = torch.sdaa.device_count()
|
| 834 |
+
elif is_musa_available():
|
| 835 |
+
num_devices = torch.musa.device_count()
|
| 836 |
+
elif is_xpu_available():
|
| 837 |
+
num_devices = torch.xpu.device_count()
|
| 838 |
+
elif is_hpu_available():
|
| 839 |
+
num_devices = torch.hpu.device_count()
|
| 840 |
+
else:
|
| 841 |
+
num_devices = torch.cuda.device_count()
|
| 842 |
+
for device in gpu_devices:
|
| 843 |
+
if device >= num_devices or device < 0:
|
| 844 |
+
logger.warning(f"Device {device} is not available, available devices are {list(range(num_devices))}")
|
| 845 |
+
# Add the other devices in the preset order if they are available
|
| 846 |
+
all_devices = gpu_devices + [k for k in ["mps", "cpu", "disk"] if k in max_memory.keys()]
|
| 847 |
+
# Raise an error if a device is not recognized
|
| 848 |
+
for k in max_memory.keys():
|
| 849 |
+
if k not in all_devices:
|
| 850 |
+
raise ValueError(
|
| 851 |
+
f"Device {k} is not recognized, available devices are integers(for GPU/XPU), 'mps', 'cpu' and 'disk'"
|
| 852 |
+
)
|
| 853 |
+
max_memory = {k: max_memory[k] for k in all_devices}
|
| 854 |
+
|
| 855 |
+
return max_memory
|
| 856 |
+
|
| 857 |
+
|
| 858 |
+
def clean_device_map(device_map: dict[str, Union[int, str, torch.device]], module_name: str = ""):
|
| 859 |
+
"""
|
| 860 |
+
Cleans a device_map by grouping all submodules that go on the same device together.
|
| 861 |
+
"""
|
| 862 |
+
# Get the value of the current module and if there is only one split across several keys, regroup it.
|
| 863 |
+
prefix = "" if module_name == "" else f"{module_name}."
|
| 864 |
+
values = [v for k, v in device_map.items() if k.startswith(prefix)]
|
| 865 |
+
if len(set(values)) == 1 and len(values) > 1:
|
| 866 |
+
for k in [k for k in device_map if k.startswith(prefix)]:
|
| 867 |
+
del device_map[k]
|
| 868 |
+
device_map[module_name] = values[0]
|
| 869 |
+
|
| 870 |
+
# Recurse over the children
|
| 871 |
+
children_modules = [k for k in device_map.keys() if k.startswith(prefix) and len(k) > len(module_name)]
|
| 872 |
+
idx = len(module_name.split(".")) + 1 if len(module_name) > 0 else 1
|
| 873 |
+
children_modules = set(".".join(k.split(".")[:idx]) for k in children_modules)
|
| 874 |
+
for child in children_modules:
|
| 875 |
+
clean_device_map(device_map, module_name=child)
|
| 876 |
+
|
| 877 |
+
return device_map
|
| 878 |
+
|
| 879 |
+
|
| 880 |
+
def load_offloaded_weights(model, index, offload_folder):
|
| 881 |
+
"""
|
| 882 |
+
Loads the weights from the offload folder into the model.
|
| 883 |
+
|
| 884 |
+
Args:
|
| 885 |
+
model (`torch.nn.Module`):
|
| 886 |
+
The model to load the weights into.
|
| 887 |
+
index (`dict`):
|
| 888 |
+
A dictionary containing the parameter name and its metadata for each parameter that was offloaded from the
|
| 889 |
+
model.
|
| 890 |
+
offload_folder (`str`):
|
| 891 |
+
The folder where the offloaded weights are stored.
|
| 892 |
+
"""
|
| 893 |
+
if index is None or len(index) == 0:
|
| 894 |
+
# Nothing to do
|
| 895 |
+
return
|
| 896 |
+
for param_name, metadata in index.items():
|
| 897 |
+
if "SCB" in param_name:
|
| 898 |
+
continue
|
| 899 |
+
fp16_statistics = None
|
| 900 |
+
if "weight" in param_name and param_name.replace("weight", "SCB") in index.keys():
|
| 901 |
+
weight_name = param_name.replace("weight", "SCB")
|
| 902 |
+
fp16_statistics = load_offloaded_weight(
|
| 903 |
+
os.path.join(offload_folder, f"{weight_name}.dat"), index[weight_name]
|
| 904 |
+
)
|
| 905 |
+
tensor_file = os.path.join(offload_folder, f"{param_name}.dat")
|
| 906 |
+
weight = load_offloaded_weight(tensor_file, metadata)
|
| 907 |
+
set_module_tensor_to_device(model, param_name, "cpu", value=weight, fp16_statistics=fp16_statistics)
|
| 908 |
+
|
| 909 |
+
|
| 910 |
+
def get_module_leaves(module_sizes):
|
| 911 |
+
module_children = {}
|
| 912 |
+
for module in module_sizes:
|
| 913 |
+
if module == "" or "." not in module:
|
| 914 |
+
continue
|
| 915 |
+
parent = module.rsplit(".", 1)[0]
|
| 916 |
+
module_children[parent] = module_children.get(parent, 0) + 1
|
| 917 |
+
leaves = [module for module in module_sizes if module_children.get(module, 0) == 0 and module != ""]
|
| 918 |
+
return leaves
|
| 919 |
+
|
| 920 |
+
|
| 921 |
+
def get_balanced_memory(
|
| 922 |
+
model: nn.Module,
|
| 923 |
+
max_memory: Optional[dict[Union[int, str], Union[int, str]]] = None,
|
| 924 |
+
no_split_module_classes: Optional[list[str]] = None,
|
| 925 |
+
dtype: Optional[Union[str, torch.dtype]] = None,
|
| 926 |
+
special_dtypes: Optional[dict[str, Union[str, torch.device]]] = None,
|
| 927 |
+
low_zero: bool = False,
|
| 928 |
+
):
|
| 929 |
+
"""
|
| 930 |
+
Compute a `max_memory` dictionary for [`infer_auto_device_map`] that will balance the use of each available GPU.
|
| 931 |
+
|
| 932 |
+
<Tip>
|
| 933 |
+
|
| 934 |
+
All computation is done analyzing sizes and dtypes of the model parameters. As a result, the model can be on the
|
| 935 |
+
meta device (as it would if initialized within the `init_empty_weights` context manager).
|
| 936 |
+
|
| 937 |
+
</Tip>
|
| 938 |
+
|
| 939 |
+
Args:
|
| 940 |
+
model (`torch.nn.Module`):
|
| 941 |
+
The model to analyze.
|
| 942 |
+
max_memory (`Dict`, *optional*):
|
| 943 |
+
A dictionary device identifier to maximum memory. Will default to the maximum memory available if unset.
|
| 944 |
+
Example: `max_memory={0: "1GB"}`.
|
| 945 |
+
no_split_module_classes (`List[str]`, *optional*):
|
| 946 |
+
A list of layer class names that should never be split across device (for instance any layer that has a
|
| 947 |
+
residual connection).
|
| 948 |
+
dtype (`str` or `torch.dtype`, *optional*):
|
| 949 |
+
If provided, the weights will be converted to that type when loaded.
|
| 950 |
+
special_dtypes (`Dict[str, Union[str, torch.device]]`, *optional*):
|
| 951 |
+
If provided, special dtypes to consider for some specific weights (will override dtype used as default for
|
| 952 |
+
all weights).
|
| 953 |
+
low_zero (`bool`, *optional*):
|
| 954 |
+
Minimizes the number of weights on GPU 0, which is convenient when it's used for other operations (like the
|
| 955 |
+
Transformers generate function).
|
| 956 |
+
"""
|
| 957 |
+
# Get default / clean up max_memory
|
| 958 |
+
user_not_set_max_memory = max_memory is None
|
| 959 |
+
max_memory = get_max_memory(max_memory)
|
| 960 |
+
|
| 961 |
+
if is_npu_available():
|
| 962 |
+
expected_device_type = "npu"
|
| 963 |
+
elif is_mlu_available():
|
| 964 |
+
expected_device_type = "mlu"
|
| 965 |
+
elif is_sdaa_available():
|
| 966 |
+
expected_device_type = "sdaa"
|
| 967 |
+
elif is_musa_available():
|
| 968 |
+
expected_device_type = "musa"
|
| 969 |
+
elif is_xpu_available():
|
| 970 |
+
expected_device_type = "xpu"
|
| 971 |
+
elif is_hpu_available():
|
| 972 |
+
expected_device_type = "hpu"
|
| 973 |
+
elif is_mps_available():
|
| 974 |
+
expected_device_type = "mps"
|
| 975 |
+
else:
|
| 976 |
+
expected_device_type = "cuda"
|
| 977 |
+
num_devices = len([d for d in max_memory if torch.device(d).type == expected_device_type and max_memory[d] > 0])
|
| 978 |
+
|
| 979 |
+
if num_devices == 0:
|
| 980 |
+
return max_memory
|
| 981 |
+
|
| 982 |
+
if num_devices == 1:
|
| 983 |
+
# We cannot do low_zero on just one GPU, but we will still reserve some memory for the buffer
|
| 984 |
+
low_zero = False
|
| 985 |
+
# If user just asked us to handle memory usage, we should avoid OOM
|
| 986 |
+
if user_not_set_max_memory:
|
| 987 |
+
for key in max_memory.keys():
|
| 988 |
+
if isinstance(key, int):
|
| 989 |
+
max_memory[key] *= 0.9 # 90% is a good compromise
|
| 990 |
+
logger.info(
|
| 991 |
+
f"We will use 90% of the memory on device {key} for storing the model, and 10% for the buffer to avoid OOM. "
|
| 992 |
+
"You can set `max_memory` in to a higher value to use more memory (at your own risk)."
|
| 993 |
+
)
|
| 994 |
+
break # only one device
|
| 995 |
+
|
| 996 |
+
module_sizes = compute_module_sizes(model, dtype=dtype, special_dtypes=special_dtypes)
|
| 997 |
+
per_gpu = module_sizes[""] // (num_devices - 1 if low_zero else num_devices)
|
| 998 |
+
|
| 999 |
+
# We can't just set the memory to model_size // num_devices as it will end being too small: each GPU will get
|
| 1000 |
+
# slightly less layers and some layers will end up offload at the end. So this function computes a buffer size to
|
| 1001 |
+
# add which is the biggest of:
|
| 1002 |
+
# - the size of no split block (if applicable)
|
| 1003 |
+
# - the mean of the layer sizes
|
| 1004 |
+
if no_split_module_classes is None:
|
| 1005 |
+
no_split_module_classes = []
|
| 1006 |
+
elif not isinstance(no_split_module_classes, (list, tuple)):
|
| 1007 |
+
no_split_module_classes = [no_split_module_classes]
|
| 1008 |
+
|
| 1009 |
+
# Identify the size of the no_split_block modules
|
| 1010 |
+
if len(no_split_module_classes) > 0:
|
| 1011 |
+
no_split_children = {}
|
| 1012 |
+
for name, size in module_sizes.items():
|
| 1013 |
+
if name == "":
|
| 1014 |
+
continue
|
| 1015 |
+
submodule = model
|
| 1016 |
+
for submodule_name in name.split("."):
|
| 1017 |
+
submodule = getattr(submodule, submodule_name)
|
| 1018 |
+
class_name = submodule.__class__.__name__
|
| 1019 |
+
if class_name in no_split_module_classes and class_name not in no_split_children:
|
| 1020 |
+
no_split_children[class_name] = size
|
| 1021 |
+
|
| 1022 |
+
if set(no_split_children.keys()) == set(no_split_module_classes):
|
| 1023 |
+
break
|
| 1024 |
+
buffer = max(no_split_children.values()) if len(no_split_children) > 0 else 0
|
| 1025 |
+
else:
|
| 1026 |
+
buffer = 0
|
| 1027 |
+
|
| 1028 |
+
# Compute mean of final modules. In the first dict of module sizes, leaves are the parameters
|
| 1029 |
+
leaves = get_module_leaves(module_sizes)
|
| 1030 |
+
module_sizes = {n: v for n, v in module_sizes.items() if n not in leaves}
|
| 1031 |
+
# Once removed, leaves are the final modules.
|
| 1032 |
+
leaves = get_module_leaves(module_sizes)
|
| 1033 |
+
mean_leaves = int(sum([module_sizes[n] for n in leaves]) / max(len(leaves), 1))
|
| 1034 |
+
buffer = int(1.25 * max(buffer, mean_leaves))
|
| 1035 |
+
per_gpu += buffer
|
| 1036 |
+
|
| 1037 |
+
# Sorted list of GPUs id (we may have some gpu ids not included in the our max_memory list - let's ignore them)
|
| 1038 |
+
gpus_idx_list = list(
|
| 1039 |
+
sorted(
|
| 1040 |
+
device_id for device_id, device_mem in max_memory.items() if isinstance(device_id, int) and device_mem > 0
|
| 1041 |
+
)
|
| 1042 |
+
)
|
| 1043 |
+
# The last device is left with max_memory just in case the buffer is not enough.
|
| 1044 |
+
for idx in gpus_idx_list[:-1]:
|
| 1045 |
+
max_memory[idx] = min(max_memory[0] if low_zero and idx == 0 else per_gpu, max_memory[idx])
|
| 1046 |
+
|
| 1047 |
+
if low_zero:
|
| 1048 |
+
min_zero = max(0, module_sizes[""] - sum([max_memory[i] for i in range(1, num_devices)]))
|
| 1049 |
+
max_memory[0] = min(min_zero, max_memory[0])
|
| 1050 |
+
|
| 1051 |
+
return max_memory
|
| 1052 |
+
|
| 1053 |
+
|
| 1054 |
+
def calculate_maximum_sizes(model: torch.nn.Module):
|
| 1055 |
+
"Computes the total size of the model and its largest layer"
|
| 1056 |
+
sizes = compute_module_sizes(model)
|
| 1057 |
+
# `transformers` models store this information for us
|
| 1058 |
+
no_split_modules = getattr(model, "_no_split_modules", None)
|
| 1059 |
+
if no_split_modules is None:
|
| 1060 |
+
no_split_modules = []
|
| 1061 |
+
|
| 1062 |
+
modules_to_treat = (
|
| 1063 |
+
list(model.named_parameters(recurse=False))
|
| 1064 |
+
+ list(model.named_children())
|
| 1065 |
+
+ list(model.named_buffers(recurse=False))
|
| 1066 |
+
)
|
| 1067 |
+
largest_layer = get_max_layer_size(modules_to_treat, sizes, no_split_modules)
|
| 1068 |
+
total_size = sizes[""]
|
| 1069 |
+
return total_size, largest_layer
|
| 1070 |
+
|
| 1071 |
+
|
| 1072 |
+
def _init_infer_auto_device_map(
|
| 1073 |
+
model: nn.Module,
|
| 1074 |
+
max_memory: Optional[dict[Union[int, str], Union[int, str]]] = None,
|
| 1075 |
+
no_split_module_classes: Optional[list[str]] = None,
|
| 1076 |
+
dtype: Optional[Union[str, torch.dtype]] = None,
|
| 1077 |
+
special_dtypes: Optional[dict[str, Union[str, torch.device]]] = None,
|
| 1078 |
+
) -> tuple[
|
| 1079 |
+
list[Union[int, str]],
|
| 1080 |
+
dict[Union[int, str], Union[int, str]],
|
| 1081 |
+
list[Union[int, str]],
|
| 1082 |
+
list[int],
|
| 1083 |
+
dict[str, int],
|
| 1084 |
+
list[list[str]],
|
| 1085 |
+
list[str],
|
| 1086 |
+
list[tuple[str, nn.Module]],
|
| 1087 |
+
]:
|
| 1088 |
+
"""
|
| 1089 |
+
Initialize variables required for computing the device map for model allocation.
|
| 1090 |
+
"""
|
| 1091 |
+
max_memory = get_max_memory(max_memory)
|
| 1092 |
+
if no_split_module_classes is None:
|
| 1093 |
+
no_split_module_classes = []
|
| 1094 |
+
elif not isinstance(no_split_module_classes, (list, tuple)):
|
| 1095 |
+
no_split_module_classes = [no_split_module_classes]
|
| 1096 |
+
|
| 1097 |
+
devices = list(max_memory.keys())
|
| 1098 |
+
if "disk" not in devices:
|
| 1099 |
+
devices.append("disk")
|
| 1100 |
+
gpus = [device for device in devices if device not in ["cpu", "disk"]]
|
| 1101 |
+
|
| 1102 |
+
# Devices that need to keep space for a potential offloaded layer.
|
| 1103 |
+
if "mps" in gpus:
|
| 1104 |
+
main_devices = ["mps"]
|
| 1105 |
+
elif len(gpus) > 0:
|
| 1106 |
+
main_devices = [gpus[0], "cpu"]
|
| 1107 |
+
else:
|
| 1108 |
+
main_devices = ["cpu"]
|
| 1109 |
+
|
| 1110 |
+
module_sizes = compute_module_sizes(model, dtype=dtype, special_dtypes=special_dtypes)
|
| 1111 |
+
tied_parameters = find_tied_parameters(model)
|
| 1112 |
+
|
| 1113 |
+
if check_tied_parameters_in_config(model) and len(tied_parameters) == 0:
|
| 1114 |
+
logger.warn(
|
| 1115 |
+
"The model weights are not tied. Please use the `tie_weights` method before using the `infer_auto_device` function."
|
| 1116 |
+
)
|
| 1117 |
+
|
| 1118 |
+
# Direct submodules and parameters
|
| 1119 |
+
modules_to_treat = (
|
| 1120 |
+
list(model.named_parameters(recurse=False))
|
| 1121 |
+
+ list(model.named_children())
|
| 1122 |
+
+ list(model.named_buffers(recurse=False))
|
| 1123 |
+
)
|
| 1124 |
+
|
| 1125 |
+
return (
|
| 1126 |
+
devices,
|
| 1127 |
+
max_memory,
|
| 1128 |
+
main_devices,
|
| 1129 |
+
gpus,
|
| 1130 |
+
module_sizes,
|
| 1131 |
+
tied_parameters,
|
| 1132 |
+
no_split_module_classes,
|
| 1133 |
+
modules_to_treat,
|
| 1134 |
+
)
|
| 1135 |
+
|
| 1136 |
+
|
| 1137 |
+
def get_module_size_with_ties(
|
| 1138 |
+
tied_params,
|
| 1139 |
+
module_size,
|
| 1140 |
+
module_sizes,
|
| 1141 |
+
modules_to_treat,
|
| 1142 |
+
) -> tuple[int, list[str], list[nn.Module]]:
|
| 1143 |
+
"""
|
| 1144 |
+
Calculate the total size of a module, including its tied parameters.
|
| 1145 |
+
|
| 1146 |
+
Args:
|
| 1147 |
+
tied_params (`List[str]`): The list of tied parameters.
|
| 1148 |
+
module_size (`int`): The size of the module without tied parameters.
|
| 1149 |
+
module_sizes (`Dict[str, int]`): A dictionary mapping each layer name to its size.
|
| 1150 |
+
modules_to_treat (`List[Tuple[str, nn.Module]]`): The list of named modules to treat.
|
| 1151 |
+
|
| 1152 |
+
Returns:
|
| 1153 |
+
`Tuple[int, List[str], List[nn.Module]]`: The total size of the module, the names of the tied modules, and the
|
| 1154 |
+
tied modules.
|
| 1155 |
+
"""
|
| 1156 |
+
if len(tied_params) < 1:
|
| 1157 |
+
return module_size, [], []
|
| 1158 |
+
tied_module_names = []
|
| 1159 |
+
tied_modules = []
|
| 1160 |
+
|
| 1161 |
+
for tied_param in tied_params:
|
| 1162 |
+
tied_module_index = [i for i, (n, _) in enumerate(modules_to_treat) if tied_param.startswith(n + ".")][0]
|
| 1163 |
+
tied_module_names.append(modules_to_treat[tied_module_index][0])
|
| 1164 |
+
tied_modules.append(modules_to_treat[tied_module_index][1])
|
| 1165 |
+
|
| 1166 |
+
module_size_with_ties = module_size
|
| 1167 |
+
for tied_param, tied_module_name in zip(tied_params, tied_module_names):
|
| 1168 |
+
module_size_with_ties += module_sizes[tied_module_name] - module_sizes[tied_param]
|
| 1169 |
+
|
| 1170 |
+
return module_size_with_ties, tied_module_names, tied_modules
|
| 1171 |
+
|
| 1172 |
+
|
| 1173 |
+
def fallback_allocate(
|
| 1174 |
+
modules: list[tuple[str, nn.Module]],
|
| 1175 |
+
module_sizes: dict[str, int],
|
| 1176 |
+
size_limit: Union[int, str],
|
| 1177 |
+
no_split_module_classes: Optional[list[str]] = None,
|
| 1178 |
+
tied_parameters: Optional[list[list[str]]] = None,
|
| 1179 |
+
) -> tuple[Optional[str], Optional[nn.Module], list[tuple[str, nn.Module]]]:
|
| 1180 |
+
"""
|
| 1181 |
+
Find a module that fits in the size limit using BFS and return it with its name and the remaining modules.
|
| 1182 |
+
|
| 1183 |
+
Args:
|
| 1184 |
+
modules (`List[Tuple[str, nn.Module]]`):
|
| 1185 |
+
The list of named modules to search in.
|
| 1186 |
+
module_sizes (`Dict[str, int]`):
|
| 1187 |
+
A dictionary mapping each layer name to its size (as generated by `compute_module_sizes`).
|
| 1188 |
+
size_limit (`Union[int, str]`):
|
| 1189 |
+
The maximum size a module can have.
|
| 1190 |
+
no_split_module_classes (`Optional[List[str]]`, *optional*):
|
| 1191 |
+
A list of class names for layers we don't want to be split.
|
| 1192 |
+
tied_parameters (`Optional[List[List[str]]`, *optional*):
|
| 1193 |
+
A list of lists of parameter names being all tied together.
|
| 1194 |
+
|
| 1195 |
+
Returns:
|
| 1196 |
+
`Tuple[Optional[str], Optional[nn.Module], List[Tuple[str, nn.Module]]]`: A tuple containing:
|
| 1197 |
+
- The name of the module that fits within the size limit.
|
| 1198 |
+
- The module itself.
|
| 1199 |
+
- The list of remaining modules after the found module is removed.
|
| 1200 |
+
"""
|
| 1201 |
+
try:
|
| 1202 |
+
size_limit = convert_file_size_to_int(size_limit)
|
| 1203 |
+
except ValueError:
|
| 1204 |
+
return None, None, modules
|
| 1205 |
+
|
| 1206 |
+
if no_split_module_classes is None:
|
| 1207 |
+
no_split_module_classes = []
|
| 1208 |
+
|
| 1209 |
+
if tied_parameters is None:
|
| 1210 |
+
tied_parameters = []
|
| 1211 |
+
|
| 1212 |
+
modules_to_search = modules.copy()
|
| 1213 |
+
module_found = False
|
| 1214 |
+
|
| 1215 |
+
while modules_to_search:
|
| 1216 |
+
name, module = modules_to_search.pop(0)
|
| 1217 |
+
|
| 1218 |
+
tied_param_groups = [
|
| 1219 |
+
tied_group
|
| 1220 |
+
for tied_group in tied_parameters
|
| 1221 |
+
if any(name + "." in k + "." for k in tied_group) and not all(name + "." in k + "." for k in tied_group)
|
| 1222 |
+
]
|
| 1223 |
+
|
| 1224 |
+
tied_params = sum(
|
| 1225 |
+
[[p for p in tied_group if name + "." not in p + "."] for tied_group in tied_param_groups], []
|
| 1226 |
+
)
|
| 1227 |
+
|
| 1228 |
+
module_size_with_ties, _, _ = get_module_size_with_ties(
|
| 1229 |
+
tied_params, module_sizes[name], module_sizes, modules_to_search
|
| 1230 |
+
)
|
| 1231 |
+
|
| 1232 |
+
# If the module fits in the size limit, we found it.
|
| 1233 |
+
if module_size_with_ties <= size_limit:
|
| 1234 |
+
module_found = True
|
| 1235 |
+
break
|
| 1236 |
+
|
| 1237 |
+
# The module is too big, we need to split it if possible.
|
| 1238 |
+
modules_children = (
|
| 1239 |
+
[]
|
| 1240 |
+
if isinstance(module, nn.Parameter) or isinstance(module, torch.Tensor)
|
| 1241 |
+
else list(module.named_children())
|
| 1242 |
+
)
|
| 1243 |
+
|
| 1244 |
+
# Split fails, move to the next module
|
| 1245 |
+
if len(modules_children) == 0 or module.__class__.__name__ in no_split_module_classes:
|
| 1246 |
+
continue
|
| 1247 |
+
|
| 1248 |
+
# split is possible, add the children to the list of modules to search
|
| 1249 |
+
modules_children = list(module.named_parameters(recurse=False)) + modules_children
|
| 1250 |
+
modules_to_search = [(f"{name}.{n}", v) for n, v in modules_children] + modules_to_search
|
| 1251 |
+
|
| 1252 |
+
if not module_found:
|
| 1253 |
+
return None, None, modules
|
| 1254 |
+
|
| 1255 |
+
# Prepare the module list for removal of the found module
|
| 1256 |
+
current_names = [n for n, _ in modules]
|
| 1257 |
+
dot_idx = [i for i, c in enumerate(name) if c == "."]
|
| 1258 |
+
|
| 1259 |
+
for dot_index in dot_idx:
|
| 1260 |
+
parent_name = name[:dot_index]
|
| 1261 |
+
if parent_name in current_names:
|
| 1262 |
+
parent_module_idx = current_names.index(parent_name)
|
| 1263 |
+
_, parent_module = modules[parent_module_idx]
|
| 1264 |
+
module_children = list(parent_module.named_parameters(recurse=False)) + list(
|
| 1265 |
+
parent_module.named_children()
|
| 1266 |
+
)
|
| 1267 |
+
modules = (
|
| 1268 |
+
modules[:parent_module_idx]
|
| 1269 |
+
+ [(f"{parent_name}.{n}", v) for n, v in module_children]
|
| 1270 |
+
+ modules[parent_module_idx + 1 :]
|
| 1271 |
+
)
|
| 1272 |
+
current_names = [n for n, _ in modules]
|
| 1273 |
+
|
| 1274 |
+
# Now the target module should be directly in the list
|
| 1275 |
+
target_idx = current_names.index(name)
|
| 1276 |
+
name, module = modules.pop(target_idx)
|
| 1277 |
+
|
| 1278 |
+
return name, module, modules
|
| 1279 |
+
|
| 1280 |
+
|
| 1281 |
+
def infer_auto_device_map(
|
| 1282 |
+
model: nn.Module,
|
| 1283 |
+
max_memory: Optional[dict[Union[int, str], Union[int, str]]] = None,
|
| 1284 |
+
no_split_module_classes: Optional[list[str]] = None,
|
| 1285 |
+
dtype: Optional[Union[str, torch.dtype]] = None,
|
| 1286 |
+
special_dtypes: Optional[dict[str, Union[str, torch.dtype]]] = None,
|
| 1287 |
+
verbose: bool = False,
|
| 1288 |
+
clean_result: bool = True,
|
| 1289 |
+
offload_buffers: bool = False,
|
| 1290 |
+
fallback_allocation: bool = False,
|
| 1291 |
+
):
|
| 1292 |
+
"""
|
| 1293 |
+
Compute a device map for a given model giving priority to GPUs, then offload on CPU and finally offload to disk,
|
| 1294 |
+
such that:
|
| 1295 |
+
- we don't exceed the memory available of any of the GPU.
|
| 1296 |
+
- if offload to the CPU is needed, there is always room left on GPU 0 to put back the layer offloaded on CPU that
|
| 1297 |
+
has the largest size.
|
| 1298 |
+
- if offload to the CPU is needed,we don't exceed the RAM available on the CPU.
|
| 1299 |
+
- if offload to the disk is needed, there is always room left on the CPU to put back the layer offloaded on disk
|
| 1300 |
+
that has the largest size.
|
| 1301 |
+
|
| 1302 |
+
<Tip>
|
| 1303 |
+
|
| 1304 |
+
All computation is done analyzing sizes and dtypes of the model parameters. As a result, the model can be on the
|
| 1305 |
+
meta device (as it would if initialized within the `init_empty_weights` context manager).
|
| 1306 |
+
|
| 1307 |
+
</Tip>
|
| 1308 |
+
|
| 1309 |
+
Args:
|
| 1310 |
+
model (`torch.nn.Module`):
|
| 1311 |
+
The model to analyze.
|
| 1312 |
+
max_memory (`Dict`, *optional*):
|
| 1313 |
+
A dictionary device identifier to maximum memory. Will default to the maximum memory available if unset.
|
| 1314 |
+
Example: `max_memory={0: "1GB"}`.
|
| 1315 |
+
no_split_module_classes (`List[str]`, *optional*):
|
| 1316 |
+
A list of layer class names that should never be split across device (for instance any layer that has a
|
| 1317 |
+
residual connection).
|
| 1318 |
+
dtype (`str` or `torch.dtype`, *optional*):
|
| 1319 |
+
If provided, the weights will be converted to that type when loaded.
|
| 1320 |
+
special_dtypes (`Dict[str, Union[str, torch.device]]`, *optional*):
|
| 1321 |
+
If provided, special dtypes to consider for some specific weights (will override dtype used as default for
|
| 1322 |
+
all weights).
|
| 1323 |
+
verbose (`bool`, *optional*, defaults to `False`):
|
| 1324 |
+
Whether or not to provide debugging statements as the function builds the device_map.
|
| 1325 |
+
clean_result (`bool`, *optional*, defaults to `True`):
|
| 1326 |
+
Clean the resulting device_map by grouping all submodules that go on the same device together.
|
| 1327 |
+
offload_buffers (`bool`, *optional*, defaults to `False`):
|
| 1328 |
+
In the layers that are offloaded on the CPU or the hard drive, whether or not to offload the buffers as
|
| 1329 |
+
well as the parameters.
|
| 1330 |
+
fallback_allocation (`bool`, *optional*, defaults to `False`):
|
| 1331 |
+
When regular allocation fails, try to allocate a module that fits in the size limit using BFS.
|
| 1332 |
+
"""
|
| 1333 |
+
|
| 1334 |
+
# Initialize the variables
|
| 1335 |
+
(
|
| 1336 |
+
devices,
|
| 1337 |
+
max_memory,
|
| 1338 |
+
main_devices,
|
| 1339 |
+
gpus,
|
| 1340 |
+
module_sizes,
|
| 1341 |
+
tied_parameters,
|
| 1342 |
+
no_split_module_classes,
|
| 1343 |
+
modules_to_treat,
|
| 1344 |
+
) = _init_infer_auto_device_map(model, max_memory, no_split_module_classes, dtype, special_dtypes)
|
| 1345 |
+
|
| 1346 |
+
device_map = OrderedDict()
|
| 1347 |
+
current_device = 0
|
| 1348 |
+
device_memory_used = {device: 0 for device in devices}
|
| 1349 |
+
device_buffer_sizes = {}
|
| 1350 |
+
device_minimum_assignment_memory = {}
|
| 1351 |
+
|
| 1352 |
+
# Initialize maximum largest layer, to know which space to keep in memory
|
| 1353 |
+
max_layer_size, max_layer_names = get_max_layer_size(modules_to_treat, module_sizes, no_split_module_classes)
|
| 1354 |
+
|
| 1355 |
+
# Ready ? This is going to be a bit messy.
|
| 1356 |
+
while len(modules_to_treat) > 0:
|
| 1357 |
+
name, module = modules_to_treat.pop(0)
|
| 1358 |
+
if verbose:
|
| 1359 |
+
print(f"\nTreating module {name}.")
|
| 1360 |
+
# Max size in the remaining layers may have changed since we took one, so we maybe update it.
|
| 1361 |
+
max_layer_names = [n for n in max_layer_names if n != name and not n.startswith(name + ".")]
|
| 1362 |
+
if len(max_layer_names) == 0:
|
| 1363 |
+
max_layer_size, max_layer_names = get_max_layer_size(
|
| 1364 |
+
[(n, m) for n, m in modules_to_treat if isinstance(m, torch.nn.Module)],
|
| 1365 |
+
module_sizes,
|
| 1366 |
+
no_split_module_classes,
|
| 1367 |
+
)
|
| 1368 |
+
# Assess size needed
|
| 1369 |
+
module_size = module_sizes[name]
|
| 1370 |
+
|
| 1371 |
+
# We keep relevant tied parameters only: one of the tied parameters in the group is inside the current module
|
| 1372 |
+
# and the other is not.
|
| 1373 |
+
# Note: If we are currently processing the name `compute.weight`, an other parameter named
|
| 1374 |
+
# e.g. `compute.weight_submodule.parameter`
|
| 1375 |
+
# needs to be considered outside the current module, hence the check with additional dots.
|
| 1376 |
+
tied_param_groups = [
|
| 1377 |
+
tied_group
|
| 1378 |
+
for tied_group in tied_parameters
|
| 1379 |
+
if any(name + "." in k + "." for k in tied_group) and not all(name + "." in k + "." for k in tied_group)
|
| 1380 |
+
]
|
| 1381 |
+
|
| 1382 |
+
if verbose and len(tied_param_groups) > 0:
|
| 1383 |
+
print(f" Found the relevant tied param groups {tied_param_groups}")
|
| 1384 |
+
|
| 1385 |
+
# Then we keep track of all the parameters that are tied to the current module, but not in the current module
|
| 1386 |
+
tied_params = sum(
|
| 1387 |
+
[[p for p in tied_group if name + "." not in p + "."] for tied_group in tied_param_groups], []
|
| 1388 |
+
)
|
| 1389 |
+
|
| 1390 |
+
if verbose and len(tied_params) > 0:
|
| 1391 |
+
print(f" So those parameters need to be taken into account {tied_params}")
|
| 1392 |
+
|
| 1393 |
+
device = devices[current_device]
|
| 1394 |
+
current_max_size = max_memory[device] if device != "disk" else None
|
| 1395 |
+
current_memory_reserved = 0
|
| 1396 |
+
# Reduce max size available by the largest layer.
|
| 1397 |
+
if devices[current_device] in main_devices:
|
| 1398 |
+
current_max_size = current_max_size - max_layer_size
|
| 1399 |
+
current_memory_reserved = max_layer_size
|
| 1400 |
+
|
| 1401 |
+
module_size_with_ties, tied_module_names, tied_modules = get_module_size_with_ties(
|
| 1402 |
+
tied_params, module_size, module_sizes, modules_to_treat
|
| 1403 |
+
)
|
| 1404 |
+
|
| 1405 |
+
# The module and its tied modules fit on the current device.
|
| 1406 |
+
if current_max_size is None or device_memory_used[device] + module_size_with_ties <= current_max_size:
|
| 1407 |
+
if verbose:
|
| 1408 |
+
output = f"Putting {name}"
|
| 1409 |
+
|
| 1410 |
+
if tied_module_names:
|
| 1411 |
+
output += f" and {tied_module_names}"
|
| 1412 |
+
else:
|
| 1413 |
+
output += f" (size={module_size})"
|
| 1414 |
+
|
| 1415 |
+
if current_max_size is not None:
|
| 1416 |
+
output += f" (available={current_max_size - device_memory_used[device]})"
|
| 1417 |
+
|
| 1418 |
+
output += f" on {device}."
|
| 1419 |
+
print(output)
|
| 1420 |
+
|
| 1421 |
+
device_memory_used[device] += module_size_with_ties
|
| 1422 |
+
|
| 1423 |
+
# Assign the primary module to the device.
|
| 1424 |
+
device_map[name] = device
|
| 1425 |
+
|
| 1426 |
+
# Assign tied modules if any.
|
| 1427 |
+
for tied_module_name in tied_module_names:
|
| 1428 |
+
if tied_module_name in [m[0] for m in modules_to_treat]:
|
| 1429 |
+
# Find the index of the tied module in the list
|
| 1430 |
+
tied_module_index = next(i for i, (n, _) in enumerate(modules_to_treat) if n == tied_module_name)
|
| 1431 |
+
# Remove the tied module from the list to prevent reprocessing
|
| 1432 |
+
modules_to_treat.pop(tied_module_index)
|
| 1433 |
+
|
| 1434 |
+
# Assign the tied module to the device
|
| 1435 |
+
device_map[tied_module_name] = device
|
| 1436 |
+
|
| 1437 |
+
# Buffer Handling
|
| 1438 |
+
if not offload_buffers and isinstance(module, nn.Module):
|
| 1439 |
+
# Compute the total buffer size for the module
|
| 1440 |
+
current_buffer_size = compute_module_total_buffer_size(
|
| 1441 |
+
module, dtype=dtype, special_dtypes=special_dtypes
|
| 1442 |
+
)
|
| 1443 |
+
# Update the buffer size on the device
|
| 1444 |
+
device_buffer_sizes[device] = device_buffer_sizes.get(device, 0) + current_buffer_size
|
| 1445 |
+
|
| 1446 |
+
continue
|
| 1447 |
+
|
| 1448 |
+
# The current module itself fits, so we try to split the tied modules.
|
| 1449 |
+
if len(tied_params) > 0 and device_memory_used[device] + module_size <= current_max_size:
|
| 1450 |
+
# can we split one of the tied modules to make it smaller or do we need to go on the next device?
|
| 1451 |
+
if verbose:
|
| 1452 |
+
print(
|
| 1453 |
+
f"Not enough space on {devices[current_device]} to put {name} and {tied_module_names} (space "
|
| 1454 |
+
f"available {current_max_size - device_memory_used[device]}, needed size {module_size_with_ties})."
|
| 1455 |
+
)
|
| 1456 |
+
split_happened = False
|
| 1457 |
+
for tied_module_name, tied_module in zip(tied_module_names, tied_modules):
|
| 1458 |
+
tied_module_children = list(tied_module.named_children())
|
| 1459 |
+
if len(tied_module_children) == 0 or tied_module.__class__.__name__ in no_split_module_classes:
|
| 1460 |
+
# can't break this one.
|
| 1461 |
+
continue
|
| 1462 |
+
|
| 1463 |
+
if verbose:
|
| 1464 |
+
print(f"Splitting {tied_module_name}.")
|
| 1465 |
+
tied_module_children = list(tied_module.named_parameters(recurse=False)) + tied_module_children
|
| 1466 |
+
tied_module_children = [(f"{tied_module_name}.{n}", v) for n, v in tied_module_children]
|
| 1467 |
+
tied_module_index = [i for i, (n, _) in enumerate(modules_to_treat) if n == tied_module_name][0]
|
| 1468 |
+
|
| 1469 |
+
modules_to_treat = (
|
| 1470 |
+
[(name, module)]
|
| 1471 |
+
+ modules_to_treat[:tied_module_index]
|
| 1472 |
+
+ tied_module_children
|
| 1473 |
+
+ modules_to_treat[tied_module_index + 1 :]
|
| 1474 |
+
)
|
| 1475 |
+
# Update the max layer size.
|
| 1476 |
+
max_layer_size, max_layer_names = get_max_layer_size(
|
| 1477 |
+
[(n, m) for n, m in modules_to_treat if isinstance(m, torch.nn.Module)],
|
| 1478 |
+
module_sizes,
|
| 1479 |
+
no_split_module_classes,
|
| 1480 |
+
)
|
| 1481 |
+
split_happened = True
|
| 1482 |
+
break
|
| 1483 |
+
|
| 1484 |
+
if split_happened:
|
| 1485 |
+
continue
|
| 1486 |
+
|
| 1487 |
+
# If the tied module is not split, we go to the next device
|
| 1488 |
+
if verbose:
|
| 1489 |
+
print("None of the tied module can be split, going to the next device.")
|
| 1490 |
+
|
| 1491 |
+
# The current module itself doesn't fit, so we have to split it or go to the next device.
|
| 1492 |
+
if device_memory_used[device] + module_size >= current_max_size:
|
| 1493 |
+
# Split or not split?
|
| 1494 |
+
modules_children = (
|
| 1495 |
+
[]
|
| 1496 |
+
if isinstance(module, nn.Parameter) or isinstance(module, torch.Tensor)
|
| 1497 |
+
else list(module.named_children())
|
| 1498 |
+
)
|
| 1499 |
+
if verbose:
|
| 1500 |
+
print(
|
| 1501 |
+
f"Not enough space on {devices[current_device]} to put {name} (space available "
|
| 1502 |
+
f"{current_max_size - device_memory_used[device]}, module size {module_size})."
|
| 1503 |
+
)
|
| 1504 |
+
if len(modules_children) == 0 or module.__class__.__name__ in no_split_module_classes:
|
| 1505 |
+
# -> no split, we go to the next device
|
| 1506 |
+
if verbose:
|
| 1507 |
+
print("This module cannot be split, going to the next device.")
|
| 1508 |
+
|
| 1509 |
+
else:
|
| 1510 |
+
# -> split, we replace the module studied by its children + parameters
|
| 1511 |
+
if verbose:
|
| 1512 |
+
print(f"Splitting {name}.")
|
| 1513 |
+
modules_children = list(module.named_parameters(recurse=False)) + modules_children
|
| 1514 |
+
modules_to_treat = [(f"{name}.{n}", v) for n, v in modules_children] + modules_to_treat
|
| 1515 |
+
# Update the max layer size.
|
| 1516 |
+
max_layer_size, max_layer_names = get_max_layer_size(
|
| 1517 |
+
[(n, m) for n, m in modules_to_treat if isinstance(m, torch.nn.Module)],
|
| 1518 |
+
module_sizes,
|
| 1519 |
+
no_split_module_classes,
|
| 1520 |
+
)
|
| 1521 |
+
continue
|
| 1522 |
+
|
| 1523 |
+
# If no module is assigned to the current device, we attempt to allocate a fallback module
|
| 1524 |
+
# if fallback_allocation is enabled.
|
| 1525 |
+
if device_memory_used[device] == 0 and fallback_allocation and device != "disk":
|
| 1526 |
+
# We try to allocate a module that fits in the size limit using BFS.
|
| 1527 |
+
# Recompute the current max size as we need to consider the current module as well.
|
| 1528 |
+
current_max_size = max_memory[device] - max(max_layer_size, module_size_with_ties)
|
| 1529 |
+
|
| 1530 |
+
fallback_module_name, fallback_module, remaining_modules = fallback_allocate(
|
| 1531 |
+
modules_to_treat,
|
| 1532 |
+
module_sizes,
|
| 1533 |
+
current_max_size - device_memory_used[device],
|
| 1534 |
+
no_split_module_classes,
|
| 1535 |
+
tied_parameters,
|
| 1536 |
+
)
|
| 1537 |
+
# use the next iteration to put the fallback module on the next device to avoid code duplication
|
| 1538 |
+
if fallback_module is not None:
|
| 1539 |
+
modules_to_treat = [(fallback_module_name, fallback_module)] + [(name, module)] + remaining_modules
|
| 1540 |
+
continue
|
| 1541 |
+
|
| 1542 |
+
if device_memory_used[device] == 0:
|
| 1543 |
+
device_minimum_assignment_memory[device] = module_size_with_ties + current_memory_reserved
|
| 1544 |
+
|
| 1545 |
+
# Neither the current module nor any tied modules can be split, so we move to the next device.
|
| 1546 |
+
device_memory_used[device] = device_memory_used[device] + current_memory_reserved
|
| 1547 |
+
current_device += 1
|
| 1548 |
+
modules_to_treat = [(name, module)] + modules_to_treat
|
| 1549 |
+
|
| 1550 |
+
device_memory_used = {device: mem for device, mem in device_memory_used.items() if mem > 0}
|
| 1551 |
+
|
| 1552 |
+
if clean_result:
|
| 1553 |
+
device_map = clean_device_map(device_map)
|
| 1554 |
+
|
| 1555 |
+
non_gpu_buffer_size = device_buffer_sizes.get("cpu", 0) + device_buffer_sizes.get("disk", 0)
|
| 1556 |
+
if non_gpu_buffer_size > 0 and not offload_buffers:
|
| 1557 |
+
is_buffer_fit_any_gpu = False
|
| 1558 |
+
for gpu_device, gpu_max_memory in max_memory.items():
|
| 1559 |
+
if gpu_device == "cpu" or gpu_device == "disk":
|
| 1560 |
+
continue
|
| 1561 |
+
|
| 1562 |
+
if not is_buffer_fit_any_gpu:
|
| 1563 |
+
gpu_memory_used = device_memory_used.get(gpu_device, 0)
|
| 1564 |
+
|
| 1565 |
+
if gpu_max_memory >= non_gpu_buffer_size + gpu_memory_used:
|
| 1566 |
+
is_buffer_fit_any_gpu = True
|
| 1567 |
+
|
| 1568 |
+
if len(gpus) > 0 and not is_buffer_fit_any_gpu:
|
| 1569 |
+
warnings.warn(
|
| 1570 |
+
f"Current model requires {non_gpu_buffer_size} bytes of buffer for offloaded layers, which seems does "
|
| 1571 |
+
f"not fit any GPU's remaining memory. If you are experiencing a OOM later, please consider using "
|
| 1572 |
+
f"offload_buffers=True."
|
| 1573 |
+
)
|
| 1574 |
+
|
| 1575 |
+
if device_minimum_assignment_memory:
|
| 1576 |
+
devices_info = "\n".join(
|
| 1577 |
+
f" - {device}: {mem} bytes required" for device, mem in device_minimum_assignment_memory.items()
|
| 1578 |
+
)
|
| 1579 |
+
logger.info(
|
| 1580 |
+
f"Based on the current allocation process, no modules could be assigned to the following devices due to "
|
| 1581 |
+
f"insufficient memory:\n"
|
| 1582 |
+
f"{devices_info}\n"
|
| 1583 |
+
f"These minimum requirements are specific to this allocation attempt and may vary. Consider increasing "
|
| 1584 |
+
f"the available memory for these devices to at least the specified minimum, or adjusting the model config."
|
| 1585 |
+
)
|
| 1586 |
+
return device_map
|
| 1587 |
+
|
| 1588 |
+
|
| 1589 |
+
def check_device_map(model: nn.Module, device_map: dict[str, Union[int, str, torch.device]]):
|
| 1590 |
+
"""
|
| 1591 |
+
Checks a device map covers everything in a given model.
|
| 1592 |
+
|
| 1593 |
+
Args:
|
| 1594 |
+
model (`torch.nn.Module`): The model to check the device map against.
|
| 1595 |
+
device_map (`Dict[str, Union[int, str, torch.device]]`): The device map to check.
|
| 1596 |
+
"""
|
| 1597 |
+
all_model_tensors = [name for name, _ in model.state_dict().items()]
|
| 1598 |
+
for module_name in device_map.keys():
|
| 1599 |
+
if module_name == "":
|
| 1600 |
+
all_model_tensors.clear()
|
| 1601 |
+
break
|
| 1602 |
+
else:
|
| 1603 |
+
all_model_tensors = [
|
| 1604 |
+
name
|
| 1605 |
+
for name in all_model_tensors
|
| 1606 |
+
if not name == module_name and not name.startswith(module_name + ".")
|
| 1607 |
+
]
|
| 1608 |
+
if len(all_model_tensors) > 0:
|
| 1609 |
+
non_covered_params = ", ".join(all_model_tensors)
|
| 1610 |
+
raise ValueError(
|
| 1611 |
+
f"The device_map provided does not give any device for the following parameters: {non_covered_params}"
|
| 1612 |
+
)
|
| 1613 |
+
|
| 1614 |
+
|
| 1615 |
+
def load_state_dict(checkpoint_file, device_map=None):
|
| 1616 |
+
"""
|
| 1617 |
+
Load a checkpoint from a given file. If the checkpoint is in the safetensors format and a device map is passed, the
|
| 1618 |
+
weights can be fast-loaded directly on the GPU.
|
| 1619 |
+
|
| 1620 |
+
Args:
|
| 1621 |
+
checkpoint_file (`str`): The path to the checkpoint to load.
|
| 1622 |
+
device_map (`Dict[str, Union[int, str, torch.device]]`, *optional*):
|
| 1623 |
+
A map that specifies where each submodule should go. It doesn't need to be refined to each parameter/buffer
|
| 1624 |
+
name, once a given module name is inside, every submodule of it will be sent to the same device.
|
| 1625 |
+
"""
|
| 1626 |
+
if checkpoint_file.endswith(".safetensors"):
|
| 1627 |
+
with safe_open(checkpoint_file, framework="pt") as f:
|
| 1628 |
+
metadata = f.metadata()
|
| 1629 |
+
weight_names = f.keys()
|
| 1630 |
+
|
| 1631 |
+
if metadata is None:
|
| 1632 |
+
logger.warn(
|
| 1633 |
+
f"The safetensors archive passed at {checkpoint_file} does not contain metadata. "
|
| 1634 |
+
"Make sure to save your model with the `save_pretrained` method. Defaulting to 'pt' metadata."
|
| 1635 |
+
)
|
| 1636 |
+
metadata = {"format": "pt"}
|
| 1637 |
+
|
| 1638 |
+
if metadata.get("format") not in ["pt", "tf", "flax"]:
|
| 1639 |
+
raise OSError(
|
| 1640 |
+
f"The safetensors archive passed at {checkpoint_file} does not contain the valid metadata. Make sure "
|
| 1641 |
+
"you save your model with the `save_pretrained` method."
|
| 1642 |
+
)
|
| 1643 |
+
elif metadata["format"] != "pt":
|
| 1644 |
+
raise ValueError(f"The checkpoint passed was saved with {metadata['format']}, we need a the pt format.")
|
| 1645 |
+
if device_map is None:
|
| 1646 |
+
return safe_load_file(checkpoint_file)
|
| 1647 |
+
else:
|
| 1648 |
+
# if we only have one device we can load everything directly
|
| 1649 |
+
if len(set(device_map.values())) == 1:
|
| 1650 |
+
device = list(device_map.values())[0]
|
| 1651 |
+
target_device = device
|
| 1652 |
+
if isinstance(device, int):
|
| 1653 |
+
if is_npu_available():
|
| 1654 |
+
target_device = f"npu:{device}"
|
| 1655 |
+
elif is_hpu_available():
|
| 1656 |
+
target_device = "hpu"
|
| 1657 |
+
|
| 1658 |
+
return safe_load_file(checkpoint_file, device=target_device)
|
| 1659 |
+
|
| 1660 |
+
devices = list(set(device_map.values()) - {"disk"})
|
| 1661 |
+
# cpu device should always exist as fallback option
|
| 1662 |
+
if "cpu" not in devices:
|
| 1663 |
+
devices.append("cpu")
|
| 1664 |
+
|
| 1665 |
+
# For each device, get the weights that go there
|
| 1666 |
+
device_weights = {device: [] for device in devices}
|
| 1667 |
+
for module_name, device in device_map.items():
|
| 1668 |
+
if device in devices:
|
| 1669 |
+
device_weights[device].extend(
|
| 1670 |
+
[k for k in weight_names if k == module_name or k.startswith(module_name + ".")]
|
| 1671 |
+
)
|
| 1672 |
+
|
| 1673 |
+
# all weights that haven't defined a device should be loaded on CPU
|
| 1674 |
+
device_weights["cpu"].extend([k for k in weight_names if k not in sum(device_weights.values(), [])])
|
| 1675 |
+
tensors = {}
|
| 1676 |
+
if is_tqdm_available():
|
| 1677 |
+
progress_bar = tqdm(
|
| 1678 |
+
main_process_only=False,
|
| 1679 |
+
total=sum([len(device_weights[device]) for device in devices]),
|
| 1680 |
+
unit="w",
|
| 1681 |
+
smoothing=0,
|
| 1682 |
+
leave=False,
|
| 1683 |
+
)
|
| 1684 |
+
else:
|
| 1685 |
+
progress_bar = None
|
| 1686 |
+
for device in devices:
|
| 1687 |
+
target_device = device
|
| 1688 |
+
if isinstance(device, int):
|
| 1689 |
+
if is_npu_available():
|
| 1690 |
+
target_device = f"npu:{device}"
|
| 1691 |
+
elif is_hpu_available():
|
| 1692 |
+
target_device = "hpu"
|
| 1693 |
+
|
| 1694 |
+
with safe_open(checkpoint_file, framework="pt", device=target_device) as f:
|
| 1695 |
+
for key in device_weights[device]:
|
| 1696 |
+
if progress_bar is not None:
|
| 1697 |
+
progress_bar.set_postfix(dev=device, refresh=False)
|
| 1698 |
+
progress_bar.set_description(key)
|
| 1699 |
+
tensors[key] = f.get_tensor(key)
|
| 1700 |
+
if progress_bar is not None:
|
| 1701 |
+
progress_bar.update()
|
| 1702 |
+
if progress_bar is not None:
|
| 1703 |
+
progress_bar.close()
|
| 1704 |
+
|
| 1705 |
+
return tensors
|
| 1706 |
+
else:
|
| 1707 |
+
return torch.load(checkpoint_file, map_location=torch.device("cpu"))
|
| 1708 |
+
|
| 1709 |
+
|
| 1710 |
+
def get_state_dict_offloaded_model(model: nn.Module):
|
| 1711 |
+
"""
|
| 1712 |
+
Returns the state dictionary for an offloaded model via iterative onloading
|
| 1713 |
+
|
| 1714 |
+
Args:
|
| 1715 |
+
model (`torch.nn.Module`):
|
| 1716 |
+
The offloaded model we want to save
|
| 1717 |
+
"""
|
| 1718 |
+
|
| 1719 |
+
state_dict = {}
|
| 1720 |
+
placeholders = set()
|
| 1721 |
+
for name, module in model.named_modules():
|
| 1722 |
+
if name == "":
|
| 1723 |
+
continue
|
| 1724 |
+
|
| 1725 |
+
try:
|
| 1726 |
+
with align_module_device(module, "cpu"):
|
| 1727 |
+
module_state_dict = module.state_dict()
|
| 1728 |
+
except MemoryError:
|
| 1729 |
+
raise MemoryError("Offloaded module must fit in CPU memory to call save_model!") from None
|
| 1730 |
+
|
| 1731 |
+
for key in module_state_dict:
|
| 1732 |
+
# ignore placeholder parameters that are still on the meta device
|
| 1733 |
+
if module_state_dict[key].device == torch.device("meta"):
|
| 1734 |
+
placeholders.add(name + f".{key}")
|
| 1735 |
+
continue
|
| 1736 |
+
params = module_state_dict[key]
|
| 1737 |
+
state_dict[name + f".{key}"] = params.to("cpu") # move buffers to cpu
|
| 1738 |
+
for key in placeholders.copy():
|
| 1739 |
+
if key in state_dict:
|
| 1740 |
+
placeholders.remove(key)
|
| 1741 |
+
if placeholders:
|
| 1742 |
+
logger.warning(f"The following tensors were not saved because they were still on meta device: {placeholders}")
|
| 1743 |
+
|
| 1744 |
+
return state_dict
|
| 1745 |
+
|
| 1746 |
+
|
| 1747 |
+
def get_state_dict_from_offload(
|
| 1748 |
+
module: nn.Module,
|
| 1749 |
+
module_name: str,
|
| 1750 |
+
state_dict: dict[str, Union[str, torch.tensor]],
|
| 1751 |
+
device_to_put_offload: Union[int, str, torch.device] = "cpu",
|
| 1752 |
+
):
|
| 1753 |
+
"""
|
| 1754 |
+
Retrieve the state dictionary (with parameters) from an offloaded module and load into a specified device (defaults
|
| 1755 |
+
to cpu).
|
| 1756 |
+
|
| 1757 |
+
Args:
|
| 1758 |
+
module: (`torch.nn.Module`):
|
| 1759 |
+
The module we want to retrieve a state dictionary from
|
| 1760 |
+
module_name: (`str`):
|
| 1761 |
+
The name of the module of interest
|
| 1762 |
+
state_dict (`Dict[str, Union[int, str, torch.device]]`):
|
| 1763 |
+
Dictionary of {module names: parameters}
|
| 1764 |
+
device_to_put_offload (`Union[int, str, torch.device]`):
|
| 1765 |
+
Device to load offloaded parameters into, defaults to the cpu.
|
| 1766 |
+
"""
|
| 1767 |
+
|
| 1768 |
+
root = module_name[: module_name.rfind(".")] # module name without .weight or .bias
|
| 1769 |
+
|
| 1770 |
+
# do not move parameters if the module is not offloaded
|
| 1771 |
+
if not has_offloaded_params(module):
|
| 1772 |
+
device_to_put_offload = None
|
| 1773 |
+
|
| 1774 |
+
# assign the device to which the offloaded parameters will be sent
|
| 1775 |
+
with align_module_device(module, device_to_put_offload):
|
| 1776 |
+
for m_key, params in module.state_dict().items():
|
| 1777 |
+
if (root + f".{m_key}") in state_dict:
|
| 1778 |
+
state_dict[root + f".{m_key}"] = params
|
| 1779 |
+
|
| 1780 |
+
return state_dict
|
| 1781 |
+
|
| 1782 |
+
|
| 1783 |
+
def load_checkpoint_in_model(
|
| 1784 |
+
model: nn.Module,
|
| 1785 |
+
checkpoint: Union[str, os.PathLike],
|
| 1786 |
+
device_map: Optional[dict[str, Union[int, str, torch.device]]] = None,
|
| 1787 |
+
offload_folder: Optional[Union[str, os.PathLike]] = None,
|
| 1788 |
+
dtype: Optional[Union[str, torch.dtype]] = None,
|
| 1789 |
+
offload_state_dict: bool = False,
|
| 1790 |
+
offload_buffers: bool = False,
|
| 1791 |
+
keep_in_fp32_modules: list[str] = None,
|
| 1792 |
+
offload_8bit_bnb: bool = False,
|
| 1793 |
+
strict: bool = False,
|
| 1794 |
+
):
|
| 1795 |
+
"""
|
| 1796 |
+
Loads a (potentially sharded) checkpoint inside a model, potentially sending weights to a given device as they are
|
| 1797 |
+
loaded.
|
| 1798 |
+
|
| 1799 |
+
<Tip warning={true}>
|
| 1800 |
+
|
| 1801 |
+
Once loaded across devices, you still need to call [`dispatch_model`] on your model to make it able to run. To
|
| 1802 |
+
group the checkpoint loading and dispatch in one single call, use [`load_checkpoint_and_dispatch`].
|
| 1803 |
+
|
| 1804 |
+
</Tip>
|
| 1805 |
+
|
| 1806 |
+
Args:
|
| 1807 |
+
model (`torch.nn.Module`):
|
| 1808 |
+
The model in which we want to load a checkpoint.
|
| 1809 |
+
checkpoint (`str` or `os.PathLike`):
|
| 1810 |
+
The folder checkpoint to load. It can be:
|
| 1811 |
+
- a path to a file containing a whole model state dict
|
| 1812 |
+
- a path to a `.json` file containing the index to a sharded checkpoint
|
| 1813 |
+
- a path to a folder containing a unique `.index.json` file and the shards of a checkpoint.
|
| 1814 |
+
- a path to a folder containing a unique pytorch_model.bin or a model.safetensors file.
|
| 1815 |
+
device_map (`Dict[str, Union[int, str, torch.device]]`, *optional*):
|
| 1816 |
+
A map that specifies where each submodule should go. It doesn't need to be refined to each parameter/buffer
|
| 1817 |
+
name, once a given module name is inside, every submodule of it will be sent to the same device.
|
| 1818 |
+
offload_folder (`str` or `os.PathLike`, *optional*):
|
| 1819 |
+
If the `device_map` contains any value `"disk"`, the folder where we will offload weights.
|
| 1820 |
+
dtype (`str` or `torch.dtype`, *optional*):
|
| 1821 |
+
If provided, the weights will be converted to that type when loaded.
|
| 1822 |
+
offload_state_dict (`bool`, *optional*, defaults to `False`):
|
| 1823 |
+
If `True`, will temporarily offload the CPU state dict on the hard drive to avoid getting out of CPU RAM if
|
| 1824 |
+
the weight of the CPU state dict + the biggest shard does not fit.
|
| 1825 |
+
offload_buffers (`bool`, *optional*, defaults to `False`):
|
| 1826 |
+
Whether or not to include the buffers in the weights offloaded to disk.
|
| 1827 |
+
keep_in_fp32_modules(`List[str]`, *optional*):
|
| 1828 |
+
A list of the modules that we keep in `torch.float32` dtype.
|
| 1829 |
+
offload_8bit_bnb (`bool`, *optional*):
|
| 1830 |
+
Whether or not to enable offload of 8-bit modules on cpu/disk.
|
| 1831 |
+
strict (`bool`, *optional*, defaults to `False`):
|
| 1832 |
+
Whether to strictly enforce that the keys in the checkpoint state_dict match the keys of the model's
|
| 1833 |
+
state_dict.
|
| 1834 |
+
|
| 1835 |
+
"""
|
| 1836 |
+
if offload_8bit_bnb:
|
| 1837 |
+
from .bnb import quantize_and_offload_8bit
|
| 1838 |
+
|
| 1839 |
+
tied_params = find_tied_parameters(model)
|
| 1840 |
+
|
| 1841 |
+
if check_tied_parameters_in_config(model) and len(tied_params) == 0:
|
| 1842 |
+
logger.warn(
|
| 1843 |
+
"The model weights are not tied. Please use the `tie_weights` method before using the `infer_auto_device` function."
|
| 1844 |
+
)
|
| 1845 |
+
if device_map is not None:
|
| 1846 |
+
check_tied_parameters_on_same_device(tied_params, device_map)
|
| 1847 |
+
|
| 1848 |
+
if offload_folder is None and device_map is not None and "disk" in device_map.values():
|
| 1849 |
+
raise ValueError(
|
| 1850 |
+
"At least one of the model submodule will be offloaded to disk, please pass along an `offload_folder`."
|
| 1851 |
+
)
|
| 1852 |
+
elif offload_folder is not None and device_map is not None and "disk" in device_map.values():
|
| 1853 |
+
os.makedirs(offload_folder, exist_ok=True)
|
| 1854 |
+
|
| 1855 |
+
if isinstance(dtype, str):
|
| 1856 |
+
# We accept "torch.float16" or just "float16"
|
| 1857 |
+
dtype = dtype.replace("torch.", "")
|
| 1858 |
+
dtype = getattr(torch, dtype)
|
| 1859 |
+
|
| 1860 |
+
checkpoint_files = None
|
| 1861 |
+
index_filename = None
|
| 1862 |
+
if os.path.isfile(checkpoint):
|
| 1863 |
+
if str(checkpoint).endswith(".json"):
|
| 1864 |
+
index_filename = checkpoint
|
| 1865 |
+
else:
|
| 1866 |
+
checkpoint_files = [checkpoint]
|
| 1867 |
+
elif os.path.isdir(checkpoint):
|
| 1868 |
+
# check if the whole state dict is present
|
| 1869 |
+
potential_state_bin = [f for f in os.listdir(checkpoint) if f == WEIGHTS_NAME]
|
| 1870 |
+
potential_state_safetensor = [f for f in os.listdir(checkpoint) if f == SAFE_WEIGHTS_NAME]
|
| 1871 |
+
if len(potential_state_bin) == 1:
|
| 1872 |
+
checkpoint_files = [os.path.join(checkpoint, potential_state_bin[0])]
|
| 1873 |
+
elif len(potential_state_safetensor) == 1:
|
| 1874 |
+
checkpoint_files = [os.path.join(checkpoint, potential_state_safetensor[0])]
|
| 1875 |
+
else:
|
| 1876 |
+
# otherwise check for sharded checkpoints
|
| 1877 |
+
potential_index = [f for f in os.listdir(checkpoint) if f.endswith(".index.json")]
|
| 1878 |
+
if len(potential_index) == 0:
|
| 1879 |
+
raise ValueError(
|
| 1880 |
+
f"{checkpoint} is not a folder containing a `.index.json` file or a {WEIGHTS_NAME} or a {SAFE_WEIGHTS_NAME} file"
|
| 1881 |
+
)
|
| 1882 |
+
elif len(potential_index) == 1:
|
| 1883 |
+
index_filename = os.path.join(checkpoint, potential_index[0])
|
| 1884 |
+
else:
|
| 1885 |
+
raise ValueError(
|
| 1886 |
+
f"{checkpoint} containing more than one `.index.json` file, delete the irrelevant ones."
|
| 1887 |
+
)
|
| 1888 |
+
else:
|
| 1889 |
+
raise ValueError(
|
| 1890 |
+
"`checkpoint` should be the path to a file containing a whole state dict, or the index of a sharded "
|
| 1891 |
+
f"checkpoint, or a folder containing a sharded checkpoint or the whole state dict, but got {checkpoint}."
|
| 1892 |
+
)
|
| 1893 |
+
|
| 1894 |
+
if index_filename is not None:
|
| 1895 |
+
checkpoint_folder = os.path.split(index_filename)[0]
|
| 1896 |
+
with open(index_filename) as f:
|
| 1897 |
+
index = json.loads(f.read())
|
| 1898 |
+
|
| 1899 |
+
if "weight_map" in index:
|
| 1900 |
+
index = index["weight_map"]
|
| 1901 |
+
checkpoint_files = sorted(list(set(index.values())))
|
| 1902 |
+
checkpoint_files = [os.path.join(checkpoint_folder, f) for f in checkpoint_files]
|
| 1903 |
+
|
| 1904 |
+
# Logic for missing/unexepected keys goes here.
|
| 1905 |
+
|
| 1906 |
+
offload_index = {}
|
| 1907 |
+
if offload_state_dict:
|
| 1908 |
+
state_dict_folder = tempfile.mkdtemp()
|
| 1909 |
+
state_dict_index = {}
|
| 1910 |
+
|
| 1911 |
+
unexpected_keys = set()
|
| 1912 |
+
model_keys = set(model.state_dict().keys())
|
| 1913 |
+
buffer_names = [name for name, _ in model.named_buffers()]
|
| 1914 |
+
for checkpoint_file in checkpoint_files:
|
| 1915 |
+
loaded_checkpoint = load_state_dict(checkpoint_file, device_map=device_map)
|
| 1916 |
+
if device_map is None:
|
| 1917 |
+
model.load_state_dict(loaded_checkpoint, strict=strict)
|
| 1918 |
+
unexpected_keys.update(set(loaded_checkpoint.keys()) - model_keys)
|
| 1919 |
+
else:
|
| 1920 |
+
for param_name, param in loaded_checkpoint.items():
|
| 1921 |
+
# skip SCB parameter (for 8-bit serialization)
|
| 1922 |
+
if "SCB" in param_name:
|
| 1923 |
+
continue
|
| 1924 |
+
|
| 1925 |
+
if param_name not in model_keys:
|
| 1926 |
+
unexpected_keys.add(param_name)
|
| 1927 |
+
if not strict:
|
| 1928 |
+
continue # Skip loading this parameter.
|
| 1929 |
+
|
| 1930 |
+
module_name = param_name
|
| 1931 |
+
|
| 1932 |
+
while len(module_name) > 0 and module_name not in device_map:
|
| 1933 |
+
module_name = ".".join(module_name.split(".")[:-1])
|
| 1934 |
+
if module_name == "" and "" not in device_map:
|
| 1935 |
+
# TODO: group all errors and raise at the end.
|
| 1936 |
+
raise ValueError(f"{param_name} doesn't have any device set.")
|
| 1937 |
+
param_device = device_map[module_name]
|
| 1938 |
+
new_dtype = dtype
|
| 1939 |
+
if dtype is not None and torch.is_floating_point(param):
|
| 1940 |
+
if keep_in_fp32_modules is not None and dtype == torch.float16:
|
| 1941 |
+
proceed = False
|
| 1942 |
+
for key in keep_in_fp32_modules:
|
| 1943 |
+
if ((key in param_name) and (key + "." in param_name)) or key == param_name:
|
| 1944 |
+
proceed = True
|
| 1945 |
+
break
|
| 1946 |
+
if proceed:
|
| 1947 |
+
new_dtype = torch.float32
|
| 1948 |
+
|
| 1949 |
+
if "weight" in param_name and param_name.replace("weight", "SCB") in loaded_checkpoint.keys():
|
| 1950 |
+
if param.dtype == torch.int8:
|
| 1951 |
+
fp16_statistics = loaded_checkpoint[param_name.replace("weight", "SCB")]
|
| 1952 |
+
else:
|
| 1953 |
+
fp16_statistics = None
|
| 1954 |
+
|
| 1955 |
+
if param_device == "disk":
|
| 1956 |
+
if offload_buffers or param_name not in buffer_names:
|
| 1957 |
+
if new_dtype is None:
|
| 1958 |
+
new_dtype = param.dtype
|
| 1959 |
+
if offload_8bit_bnb:
|
| 1960 |
+
quantize_and_offload_8bit(
|
| 1961 |
+
model, param, param_name, new_dtype, offload_folder, offload_index, fp16_statistics
|
| 1962 |
+
)
|
| 1963 |
+
continue
|
| 1964 |
+
else:
|
| 1965 |
+
set_module_tensor_to_device(model, param_name, "meta", dtype=new_dtype)
|
| 1966 |
+
offload_weight(param, param_name, offload_folder, index=offload_index)
|
| 1967 |
+
elif param_device == "cpu" and offload_state_dict:
|
| 1968 |
+
if new_dtype is None:
|
| 1969 |
+
new_dtype = param.dtype
|
| 1970 |
+
if offload_8bit_bnb:
|
| 1971 |
+
quantize_and_offload_8bit(
|
| 1972 |
+
model, param, param_name, new_dtype, state_dict_folder, state_dict_index, fp16_statistics
|
| 1973 |
+
)
|
| 1974 |
+
else:
|
| 1975 |
+
set_module_tensor_to_device(model, param_name, "meta", dtype=new_dtype)
|
| 1976 |
+
offload_weight(param, param_name, state_dict_folder, index=state_dict_index)
|
| 1977 |
+
else:
|
| 1978 |
+
set_module_tensor_to_device(
|
| 1979 |
+
model,
|
| 1980 |
+
param_name,
|
| 1981 |
+
param_device,
|
| 1982 |
+
value=param,
|
| 1983 |
+
dtype=new_dtype,
|
| 1984 |
+
fp16_statistics=fp16_statistics,
|
| 1985 |
+
)
|
| 1986 |
+
|
| 1987 |
+
# Force Python to clean up.
|
| 1988 |
+
del loaded_checkpoint
|
| 1989 |
+
gc.collect()
|
| 1990 |
+
|
| 1991 |
+
if not strict and len(unexpected_keys) > 0:
|
| 1992 |
+
logger.warning(
|
| 1993 |
+
f"Some weights of the model checkpoint at {checkpoint} were not used when"
|
| 1994 |
+
f" initializing {model.__class__.__name__}: {unexpected_keys}. This may or may not be an issue - make sure that the checkpoint does not have unnecessary parameters, or that the model definition correctly corresponds to the checkpoint."
|
| 1995 |
+
)
|
| 1996 |
+
|
| 1997 |
+
save_offload_index(offload_index, offload_folder)
|
| 1998 |
+
|
| 1999 |
+
# Load back offloaded state dict on CPU
|
| 2000 |
+
if offload_state_dict:
|
| 2001 |
+
load_offloaded_weights(model, state_dict_index, state_dict_folder)
|
| 2002 |
+
shutil.rmtree(state_dict_folder)
|
| 2003 |
+
|
| 2004 |
+
retie_parameters(model, tied_params)
|
| 2005 |
+
|
| 2006 |
+
|
| 2007 |
+
def get_mixed_precision_context_manager(native_amp: bool = False, autocast_kwargs: AutocastKwargs = None):
|
| 2008 |
+
"""
|
| 2009 |
+
Return a context manager for autocasting mixed precision
|
| 2010 |
+
|
| 2011 |
+
Args:
|
| 2012 |
+
native_amp (`bool`, *optional*, defaults to False):
|
| 2013 |
+
Whether mixed precision is actually enabled.
|
| 2014 |
+
cache_enabled (`bool`, *optional*, defaults to True):
|
| 2015 |
+
Whether the weight cache inside autocast should be enabled.
|
| 2016 |
+
"""
|
| 2017 |
+
state = AcceleratorState()
|
| 2018 |
+
if autocast_kwargs is None:
|
| 2019 |
+
autocast_kwargs = {}
|
| 2020 |
+
else:
|
| 2021 |
+
autocast_kwargs = autocast_kwargs.to_kwargs()
|
| 2022 |
+
if native_amp:
|
| 2023 |
+
device_type = (
|
| 2024 |
+
"cuda"
|
| 2025 |
+
if (state.distributed_type == DistributedType.XLA and is_torch_xla_available(check_is_gpu=True))
|
| 2026 |
+
else state.device.type
|
| 2027 |
+
)
|
| 2028 |
+
if state.mixed_precision == "fp16":
|
| 2029 |
+
return torch.autocast(device_type=device_type, dtype=torch.float16, **autocast_kwargs)
|
| 2030 |
+
elif state.mixed_precision in ["bf16", "fp8"] and state.distributed_type in [
|
| 2031 |
+
DistributedType.NO,
|
| 2032 |
+
DistributedType.MULTI_CPU,
|
| 2033 |
+
DistributedType.MULTI_GPU,
|
| 2034 |
+
DistributedType.MULTI_MLU,
|
| 2035 |
+
DistributedType.MULTI_SDAA,
|
| 2036 |
+
DistributedType.MULTI_MUSA,
|
| 2037 |
+
DistributedType.MULTI_NPU,
|
| 2038 |
+
DistributedType.MULTI_XPU,
|
| 2039 |
+
DistributedType.MULTI_HPU,
|
| 2040 |
+
DistributedType.FSDP,
|
| 2041 |
+
DistributedType.XLA,
|
| 2042 |
+
]:
|
| 2043 |
+
return torch.autocast(device_type=device_type, dtype=torch.bfloat16, **autocast_kwargs)
|
| 2044 |
+
else:
|
| 2045 |
+
return torch.autocast(device_type=device_type, **autocast_kwargs)
|
| 2046 |
+
else:
|
| 2047 |
+
return contextlib.nullcontext()
|
| 2048 |
+
|
| 2049 |
+
|
| 2050 |
+
def get_grad_scaler(distributed_type: DistributedType = None, **kwargs):
|
| 2051 |
+
"""
|
| 2052 |
+
A generic helper which will initialize the correct `GradScaler` implementation based on the environment and return
|
| 2053 |
+
it.
|
| 2054 |
+
|
| 2055 |
+
Args:
|
| 2056 |
+
distributed_type (`DistributedType`, *optional*, defaults to None):
|
| 2057 |
+
The type of distributed environment.
|
| 2058 |
+
kwargs:
|
| 2059 |
+
Additional arguments for the utilized `GradScaler` constructor.
|
| 2060 |
+
"""
|
| 2061 |
+
if distributed_type == DistributedType.FSDP:
|
| 2062 |
+
from torch.distributed.fsdp.sharded_grad_scaler import ShardedGradScaler
|
| 2063 |
+
|
| 2064 |
+
return ShardedGradScaler(**kwargs)
|
| 2065 |
+
if is_torch_xla_available(check_is_gpu=True):
|
| 2066 |
+
import torch_xla.amp as xamp
|
| 2067 |
+
|
| 2068 |
+
return xamp.GradScaler(**kwargs)
|
| 2069 |
+
elif is_mlu_available():
|
| 2070 |
+
return torch.mlu.amp.GradScaler(**kwargs)
|
| 2071 |
+
elif is_sdaa_available():
|
| 2072 |
+
return torch.sdaa.amp.GradScaler(**kwargs)
|
| 2073 |
+
elif is_musa_available():
|
| 2074 |
+
return torch.musa.amp.GradScaler(**kwargs)
|
| 2075 |
+
elif is_npu_available():
|
| 2076 |
+
return torch.npu.amp.GradScaler(**kwargs)
|
| 2077 |
+
elif is_hpu_available():
|
| 2078 |
+
return torch.amp.GradScaler("hpu", **kwargs)
|
| 2079 |
+
elif is_xpu_available():
|
| 2080 |
+
return torch.amp.GradScaler("xpu", **kwargs)
|
| 2081 |
+
else:
|
| 2082 |
+
if is_torch_version(">=", "2.3"):
|
| 2083 |
+
return torch.amp.GradScaler("cuda", **kwargs)
|
| 2084 |
+
else:
|
| 2085 |
+
return torch.cuda.amp.GradScaler(**kwargs)
|
| 2086 |
+
|
| 2087 |
+
|
| 2088 |
+
def has_offloaded_params(module: torch.nn.Module) -> bool:
|
| 2089 |
+
"""
|
| 2090 |
+
Checks if a module has offloaded parameters by checking if the given module has a AlignDevicesHook attached with
|
| 2091 |
+
offloading enabled
|
| 2092 |
+
|
| 2093 |
+
Args:
|
| 2094 |
+
module (`torch.nn.Module`): The module to check for an offload hook.
|
| 2095 |
+
|
| 2096 |
+
Returns:
|
| 2097 |
+
bool: `True` if the module has an offload hook and offloading is enabled, `False` otherwise.
|
| 2098 |
+
"""
|
| 2099 |
+
from ..hooks import AlignDevicesHook # avoid circular import
|
| 2100 |
+
|
| 2101 |
+
return hasattr(module, "_hf_hook") and isinstance(module._hf_hook, AlignDevicesHook) and module._hf_hook.offload
|
| 2102 |
+
|
| 2103 |
+
|
| 2104 |
+
@contextlib.contextmanager
|
| 2105 |
+
def align_module_device(module: torch.nn.Module, execution_device: Optional[torch.device] = None):
|
| 2106 |
+
"""
|
| 2107 |
+
Context manager that moves a module's parameters to the specified execution device.
|
| 2108 |
+
|
| 2109 |
+
Args:
|
| 2110 |
+
module (`torch.nn.Module`):
|
| 2111 |
+
Module with parameters to align.
|
| 2112 |
+
execution_device (`torch.device`, *optional*):
|
| 2113 |
+
If provided, overrides the module's execution device within the context. Otherwise, use hook execution
|
| 2114 |
+
device or pass
|
| 2115 |
+
"""
|
| 2116 |
+
if has_offloaded_params(module):
|
| 2117 |
+
if execution_device is not None:
|
| 2118 |
+
original_device = module._hf_hook.execution_device
|
| 2119 |
+
module._hf_hook.execution_device = execution_device
|
| 2120 |
+
|
| 2121 |
+
try:
|
| 2122 |
+
module._hf_hook.pre_forward(module)
|
| 2123 |
+
yield
|
| 2124 |
+
finally:
|
| 2125 |
+
module._hf_hook.post_forward(module, None)
|
| 2126 |
+
if execution_device is not None:
|
| 2127 |
+
module._hf_hook.execution_device = original_device
|
| 2128 |
+
|
| 2129 |
+
elif execution_device is not None:
|
| 2130 |
+
devices = {name: param.device for name, param in module.named_parameters(recurse=False)}
|
| 2131 |
+
try:
|
| 2132 |
+
for name in devices:
|
| 2133 |
+
set_module_tensor_to_device(module, name, execution_device)
|
| 2134 |
+
yield
|
| 2135 |
+
finally:
|
| 2136 |
+
for name, device in devices.items():
|
| 2137 |
+
set_module_tensor_to_device(module, name, device)
|
| 2138 |
+
|
| 2139 |
+
else:
|
| 2140 |
+
yield
|
venv/lib/python3.11/site-packages/accelerate/utils/offload.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import json
|
| 16 |
+
import os
|
| 17 |
+
from collections.abc import Mapping
|
| 18 |
+
from typing import Optional, Union
|
| 19 |
+
|
| 20 |
+
import numpy as np
|
| 21 |
+
import torch
|
| 22 |
+
from safetensors import safe_open
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def offload_weight(weight, weight_name, offload_folder, index=None):
|
| 26 |
+
dtype = None
|
| 27 |
+
# Check the string instead of the dtype to be compatible with versions of PyTorch that don't have bfloat16.
|
| 28 |
+
if str(weight.dtype) == "torch.bfloat16":
|
| 29 |
+
# Need to reinterpret the underlined data as int16 since NumPy does not handle bfloat16s.
|
| 30 |
+
weight = weight.view(torch.int16)
|
| 31 |
+
dtype = "bfloat16"
|
| 32 |
+
array = weight.cpu().numpy()
|
| 33 |
+
tensor_file = os.path.join(offload_folder, f"{weight_name}.dat")
|
| 34 |
+
if index is not None:
|
| 35 |
+
if dtype is None:
|
| 36 |
+
dtype = str(array.dtype)
|
| 37 |
+
index[weight_name] = {"dtype": dtype, "shape": list(array.shape)}
|
| 38 |
+
if array.ndim == 0:
|
| 39 |
+
array = array[None]
|
| 40 |
+
file_array = np.memmap(tensor_file, dtype=array.dtype, mode="w+", shape=array.shape)
|
| 41 |
+
file_array[:] = array[:]
|
| 42 |
+
file_array.flush()
|
| 43 |
+
return index
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def load_offloaded_weight(weight_file, weight_info):
|
| 47 |
+
shape = tuple(weight_info["shape"])
|
| 48 |
+
if shape == ():
|
| 49 |
+
# NumPy memory-mapped arrays can't have 0 dims so it was saved as 1d tensor
|
| 50 |
+
shape = (1,)
|
| 51 |
+
|
| 52 |
+
dtype = weight_info["dtype"]
|
| 53 |
+
if dtype == "bfloat16":
|
| 54 |
+
# NumPy does not support bfloat16 so this was saved as a int16
|
| 55 |
+
dtype = "int16"
|
| 56 |
+
|
| 57 |
+
weight = np.memmap(weight_file, dtype=dtype, shape=shape, mode="r")
|
| 58 |
+
|
| 59 |
+
if len(weight_info["shape"]) == 0:
|
| 60 |
+
weight = weight[0]
|
| 61 |
+
weight = torch.tensor(weight)
|
| 62 |
+
if weight_info["dtype"] == "bfloat16":
|
| 63 |
+
weight = weight.view(torch.bfloat16)
|
| 64 |
+
|
| 65 |
+
return weight
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def save_offload_index(index, offload_folder):
|
| 69 |
+
if index is None or len(index) == 0:
|
| 70 |
+
# Nothing to save
|
| 71 |
+
return
|
| 72 |
+
|
| 73 |
+
offload_index_file = os.path.join(offload_folder, "index.json")
|
| 74 |
+
if os.path.isfile(offload_index_file):
|
| 75 |
+
with open(offload_index_file, encoding="utf-8") as f:
|
| 76 |
+
current_index = json.load(f)
|
| 77 |
+
else:
|
| 78 |
+
current_index = {}
|
| 79 |
+
current_index.update(index)
|
| 80 |
+
|
| 81 |
+
with open(offload_index_file, "w", encoding="utf-8") as f:
|
| 82 |
+
json.dump(current_index, f, indent=2)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def offload_state_dict(save_dir: Union[str, os.PathLike], state_dict: dict[str, torch.Tensor]):
|
| 86 |
+
"""
|
| 87 |
+
Offload a state dict in a given folder.
|
| 88 |
+
|
| 89 |
+
Args:
|
| 90 |
+
save_dir (`str` or `os.PathLike`):
|
| 91 |
+
The directory in which to offload the state dict.
|
| 92 |
+
state_dict (`Dict[str, torch.Tensor]`):
|
| 93 |
+
The dictionary of tensors to offload.
|
| 94 |
+
"""
|
| 95 |
+
os.makedirs(save_dir, exist_ok=True)
|
| 96 |
+
index = {}
|
| 97 |
+
for name, parameter in state_dict.items():
|
| 98 |
+
index = offload_weight(parameter, name, save_dir, index=index)
|
| 99 |
+
|
| 100 |
+
# Update index
|
| 101 |
+
save_offload_index(index, save_dir)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class PrefixedDataset(Mapping):
|
| 105 |
+
"""
|
| 106 |
+
Will access keys in a given dataset by adding a prefix.
|
| 107 |
+
|
| 108 |
+
Args:
|
| 109 |
+
dataset (`Mapping`): Any map with string keys.
|
| 110 |
+
prefix (`str`): A prefix to add when trying to access any element in the underlying dataset.
|
| 111 |
+
"""
|
| 112 |
+
|
| 113 |
+
def __init__(self, dataset: Mapping, prefix: str):
|
| 114 |
+
self.dataset = dataset
|
| 115 |
+
self.prefix = prefix
|
| 116 |
+
|
| 117 |
+
def __getitem__(self, key):
|
| 118 |
+
return self.dataset[f"{self.prefix}{key}"]
|
| 119 |
+
|
| 120 |
+
def __iter__(self):
|
| 121 |
+
return iter([key for key in self.dataset if key.startswith(self.prefix)])
|
| 122 |
+
|
| 123 |
+
def __len__(self):
|
| 124 |
+
return len(self.dataset)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class OffloadedWeightsLoader(Mapping):
|
| 128 |
+
"""
|
| 129 |
+
A collection that loads weights stored in a given state dict or memory-mapped on disk.
|
| 130 |
+
|
| 131 |
+
Args:
|
| 132 |
+
state_dict (`Dict[str, torch.Tensor]`, *optional*):
|
| 133 |
+
A dictionary parameter name to tensor.
|
| 134 |
+
save_folder (`str` or `os.PathLike`, *optional*):
|
| 135 |
+
The directory in which the weights are stored (by `offload_state_dict` for instance).
|
| 136 |
+
index (`Dict`, *optional*):
|
| 137 |
+
A dictionary from weight name to their information (`dtype`/ `shape` or safetensors filename). Will default
|
| 138 |
+
to the index saved in `save_folder`.
|
| 139 |
+
"""
|
| 140 |
+
|
| 141 |
+
def __init__(
|
| 142 |
+
self,
|
| 143 |
+
state_dict: dict[str, torch.Tensor] = None,
|
| 144 |
+
save_folder: Optional[Union[str, os.PathLike]] = None,
|
| 145 |
+
index: Mapping = None,
|
| 146 |
+
device=None,
|
| 147 |
+
):
|
| 148 |
+
if state_dict is None and save_folder is None and index is None:
|
| 149 |
+
raise ValueError("Need either a `state_dict`, a `save_folder` or an `index` containing offloaded weights.")
|
| 150 |
+
|
| 151 |
+
self.state_dict = {} if state_dict is None else state_dict
|
| 152 |
+
self.save_folder = save_folder
|
| 153 |
+
if index is None and save_folder is not None:
|
| 154 |
+
with open(os.path.join(save_folder, "index.json")) as f:
|
| 155 |
+
index = json.load(f)
|
| 156 |
+
self.index = {} if index is None else index
|
| 157 |
+
self.all_keys = list(self.state_dict.keys())
|
| 158 |
+
self.all_keys.extend([key for key in self.index if key not in self.all_keys])
|
| 159 |
+
self.device = device
|
| 160 |
+
|
| 161 |
+
def __getitem__(self, key: str):
|
| 162 |
+
# State dict gets priority
|
| 163 |
+
if key in self.state_dict:
|
| 164 |
+
return self.state_dict[key]
|
| 165 |
+
weight_info = self.index[key]
|
| 166 |
+
if weight_info.get("safetensors_file") is not None:
|
| 167 |
+
device = "cpu" if self.device is None else self.device
|
| 168 |
+
tensor = None
|
| 169 |
+
try:
|
| 170 |
+
with safe_open(weight_info["safetensors_file"], framework="pt", device=device) as f:
|
| 171 |
+
tensor = f.get_tensor(weight_info.get("weight_name", key))
|
| 172 |
+
except TypeError:
|
| 173 |
+
# if failed to get_tensor on the device, such as bf16 on mps, try to load it on CPU first
|
| 174 |
+
with safe_open(weight_info["safetensors_file"], framework="pt", device="cpu") as f:
|
| 175 |
+
tensor = f.get_tensor(weight_info.get("weight_name", key))
|
| 176 |
+
|
| 177 |
+
if "dtype" in weight_info:
|
| 178 |
+
tensor = tensor.to(getattr(torch, weight_info["dtype"]))
|
| 179 |
+
|
| 180 |
+
if tensor.device != torch.device(device):
|
| 181 |
+
tensor = tensor.to(device)
|
| 182 |
+
return tensor
|
| 183 |
+
|
| 184 |
+
weight_file = os.path.join(self.save_folder, f"{key}.dat")
|
| 185 |
+
return load_offloaded_weight(weight_file, weight_info)
|
| 186 |
+
|
| 187 |
+
def __iter__(self):
|
| 188 |
+
return iter(self.all_keys)
|
| 189 |
+
|
| 190 |
+
def __len__(self):
|
| 191 |
+
return len(self.all_keys)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def extract_submodules_state_dict(state_dict: dict[str, torch.Tensor], submodule_names: list[str]):
|
| 195 |
+
"""
|
| 196 |
+
Extract the sub state-dict corresponding to a list of given submodules.
|
| 197 |
+
|
| 198 |
+
Args:
|
| 199 |
+
state_dict (`Dict[str, torch.Tensor]`): The state dict to extract from.
|
| 200 |
+
submodule_names (`List[str]`): The list of submodule names we want to extract.
|
| 201 |
+
"""
|
| 202 |
+
result = {}
|
| 203 |
+
for module_name in submodule_names:
|
| 204 |
+
# We want to catch module_name parameter (module_name.xxx) or potentially module_name, but not any of the
|
| 205 |
+
# submodules that could being like module_name (transformers.h.1 and transformers.h.10 for instance)
|
| 206 |
+
result.update(
|
| 207 |
+
{
|
| 208 |
+
key: param
|
| 209 |
+
for key, param in state_dict.items()
|
| 210 |
+
if key == module_name or key.startswith(module_name + ".")
|
| 211 |
+
}
|
| 212 |
+
)
|
| 213 |
+
return result
|
venv/lib/python3.11/site-packages/accelerate/utils/operations.py
ADDED
|
@@ -0,0 +1,862 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""
|
| 15 |
+
A set of basic tensor ops compatible with tpu, gpu, and multigpu
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
import pickle
|
| 19 |
+
import warnings
|
| 20 |
+
from collections.abc import Mapping
|
| 21 |
+
from contextlib import contextmanager, nullcontext
|
| 22 |
+
from functools import update_wrapper, wraps
|
| 23 |
+
from typing import Any
|
| 24 |
+
|
| 25 |
+
import torch
|
| 26 |
+
|
| 27 |
+
from ..state import AcceleratorState, PartialState
|
| 28 |
+
from .constants import TORCH_DISTRIBUTED_OPERATION_TYPES
|
| 29 |
+
from .dataclasses import DistributedType, TensorInformation
|
| 30 |
+
from .imports import (
|
| 31 |
+
is_npu_available,
|
| 32 |
+
is_torch_distributed_available,
|
| 33 |
+
is_torch_xla_available,
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
if is_torch_xla_available():
|
| 38 |
+
import torch_xla.core.xla_model as xm
|
| 39 |
+
|
| 40 |
+
if is_torch_distributed_available():
|
| 41 |
+
from torch.distributed import ReduceOp
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def is_torch_tensor(tensor):
|
| 45 |
+
return isinstance(tensor, torch.Tensor)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def is_torch_xpu_tensor(tensor):
|
| 49 |
+
return isinstance(
|
| 50 |
+
tensor,
|
| 51 |
+
torch.xpu.FloatTensor,
|
| 52 |
+
torch.xpu.ByteTensor,
|
| 53 |
+
torch.xpu.IntTensor,
|
| 54 |
+
torch.xpu.LongTensor,
|
| 55 |
+
torch.xpu.HalfTensor,
|
| 56 |
+
torch.xpu.DoubleTensor,
|
| 57 |
+
torch.xpu.BFloat16Tensor,
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def is_tensor_information(tensor_info):
|
| 62 |
+
return isinstance(tensor_info, TensorInformation)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def is_namedtuple(data):
|
| 66 |
+
"""
|
| 67 |
+
Checks if `data` is a `namedtuple` or not. Can have false positives, but only if a user is trying to mimic a
|
| 68 |
+
`namedtuple` perfectly.
|
| 69 |
+
"""
|
| 70 |
+
return isinstance(data, tuple) and hasattr(data, "_asdict") and hasattr(data, "_fields")
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def honor_type(obj, generator):
|
| 74 |
+
"""
|
| 75 |
+
Cast a generator to the same type as obj (list, tuple, or namedtuple)
|
| 76 |
+
"""
|
| 77 |
+
# Some objects may not be able to instantiate from a generator directly
|
| 78 |
+
if is_namedtuple(obj):
|
| 79 |
+
return type(obj)(*list(generator))
|
| 80 |
+
else:
|
| 81 |
+
return type(obj)(generator)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def recursively_apply(func, data, *args, test_type=is_torch_tensor, error_on_other_type=False, **kwargs):
|
| 85 |
+
"""
|
| 86 |
+
Recursively apply a function on a data structure that is a nested list/tuple/dictionary of a given base type.
|
| 87 |
+
|
| 88 |
+
Args:
|
| 89 |
+
func (`callable`):
|
| 90 |
+
The function to recursively apply.
|
| 91 |
+
data (nested list/tuple/dictionary of `main_type`):
|
| 92 |
+
The data on which to apply `func`
|
| 93 |
+
*args:
|
| 94 |
+
Positional arguments that will be passed to `func` when applied on the unpacked data.
|
| 95 |
+
main_type (`type`, *optional*, defaults to `torch.Tensor`):
|
| 96 |
+
The base type of the objects to which apply `func`.
|
| 97 |
+
error_on_other_type (`bool`, *optional*, defaults to `False`):
|
| 98 |
+
Whether to return an error or not if after unpacking `data`, we get on an object that is not of type
|
| 99 |
+
`main_type`. If `False`, the function will leave objects of types different than `main_type` unchanged.
|
| 100 |
+
**kwargs (additional keyword arguments, *optional*):
|
| 101 |
+
Keyword arguments that will be passed to `func` when applied on the unpacked data.
|
| 102 |
+
|
| 103 |
+
Returns:
|
| 104 |
+
The same data structure as `data` with `func` applied to every object of type `main_type`.
|
| 105 |
+
"""
|
| 106 |
+
if isinstance(data, (tuple, list)):
|
| 107 |
+
return honor_type(
|
| 108 |
+
data,
|
| 109 |
+
(
|
| 110 |
+
recursively_apply(
|
| 111 |
+
func, o, *args, test_type=test_type, error_on_other_type=error_on_other_type, **kwargs
|
| 112 |
+
)
|
| 113 |
+
for o in data
|
| 114 |
+
),
|
| 115 |
+
)
|
| 116 |
+
elif isinstance(data, Mapping):
|
| 117 |
+
return type(data)(
|
| 118 |
+
{
|
| 119 |
+
k: recursively_apply(
|
| 120 |
+
func, v, *args, test_type=test_type, error_on_other_type=error_on_other_type, **kwargs
|
| 121 |
+
)
|
| 122 |
+
for k, v in data.items()
|
| 123 |
+
}
|
| 124 |
+
)
|
| 125 |
+
elif test_type(data):
|
| 126 |
+
return func(data, *args, **kwargs)
|
| 127 |
+
elif error_on_other_type:
|
| 128 |
+
raise TypeError(
|
| 129 |
+
f"Unsupported types ({type(data)}) passed to `{func.__name__}`. Only nested list/tuple/dicts of "
|
| 130 |
+
f"objects that are valid for `{test_type.__name__}` should be passed."
|
| 131 |
+
)
|
| 132 |
+
return data
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def send_to_device(tensor, device, non_blocking=False, skip_keys=None):
|
| 136 |
+
"""
|
| 137 |
+
Recursively sends the elements in a nested list/tuple/dictionary of tensors to a given device.
|
| 138 |
+
|
| 139 |
+
Args:
|
| 140 |
+
tensor (nested list/tuple/dictionary of `torch.Tensor`):
|
| 141 |
+
The data to send to a given device.
|
| 142 |
+
device (`torch.device`):
|
| 143 |
+
The device to send the data to.
|
| 144 |
+
|
| 145 |
+
Returns:
|
| 146 |
+
The same data structure as `tensor` with all tensors sent to the proper device.
|
| 147 |
+
"""
|
| 148 |
+
if is_torch_tensor(tensor) or hasattr(tensor, "to"):
|
| 149 |
+
# `torch.Tensor.to("npu")` could not find context when called for the first time (see this [issue](https://gitee.com/ascend/pytorch/issues/I8KECW?from=project-issue)).
|
| 150 |
+
if device == "npu":
|
| 151 |
+
device = "npu:0"
|
| 152 |
+
try:
|
| 153 |
+
return tensor.to(device, non_blocking=non_blocking)
|
| 154 |
+
except TypeError: # .to() doesn't accept non_blocking as kwarg
|
| 155 |
+
return tensor.to(device)
|
| 156 |
+
except AssertionError as error:
|
| 157 |
+
# `torch.Tensor.to(<int num>)` is not supported by `torch_npu` (see this [issue](https://github.com/Ascend/pytorch/issues/16)).
|
| 158 |
+
# This call is inside the try-block since is_npu_available is not supported by torch.compile.
|
| 159 |
+
if is_npu_available():
|
| 160 |
+
if isinstance(device, int):
|
| 161 |
+
device = f"npu:{device}"
|
| 162 |
+
else:
|
| 163 |
+
raise error
|
| 164 |
+
try:
|
| 165 |
+
return tensor.to(device, non_blocking=non_blocking)
|
| 166 |
+
except TypeError: # .to() doesn't accept non_blocking as kwarg
|
| 167 |
+
return tensor.to(device)
|
| 168 |
+
elif isinstance(tensor, (tuple, list)):
|
| 169 |
+
return honor_type(
|
| 170 |
+
tensor, (send_to_device(t, device, non_blocking=non_blocking, skip_keys=skip_keys) for t in tensor)
|
| 171 |
+
)
|
| 172 |
+
elif isinstance(tensor, Mapping):
|
| 173 |
+
if isinstance(skip_keys, str):
|
| 174 |
+
skip_keys = [skip_keys]
|
| 175 |
+
elif skip_keys is None:
|
| 176 |
+
skip_keys = []
|
| 177 |
+
return type(tensor)(
|
| 178 |
+
{
|
| 179 |
+
k: t if k in skip_keys else send_to_device(t, device, non_blocking=non_blocking, skip_keys=skip_keys)
|
| 180 |
+
for k, t in tensor.items()
|
| 181 |
+
}
|
| 182 |
+
)
|
| 183 |
+
else:
|
| 184 |
+
return tensor
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def get_data_structure(data):
|
| 188 |
+
"""
|
| 189 |
+
Recursively gathers the information needed to rebuild a nested list/tuple/dictionary of tensors.
|
| 190 |
+
|
| 191 |
+
Args:
|
| 192 |
+
data (nested list/tuple/dictionary of `torch.Tensor`):
|
| 193 |
+
The data to send to analyze.
|
| 194 |
+
|
| 195 |
+
Returns:
|
| 196 |
+
The same data structure as `data` with [`~utils.TensorInformation`] instead of tensors.
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
def _get_data_structure(tensor):
|
| 200 |
+
return TensorInformation(shape=tensor.shape, dtype=tensor.dtype)
|
| 201 |
+
|
| 202 |
+
return recursively_apply(_get_data_structure, data)
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
def get_shape(data):
|
| 206 |
+
"""
|
| 207 |
+
Recursively gathers the shape of a nested list/tuple/dictionary of tensors as a list.
|
| 208 |
+
|
| 209 |
+
Args:
|
| 210 |
+
data (nested list/tuple/dictionary of `torch.Tensor`):
|
| 211 |
+
The data to send to analyze.
|
| 212 |
+
|
| 213 |
+
Returns:
|
| 214 |
+
The same data structure as `data` with lists of tensor shapes instead of tensors.
|
| 215 |
+
"""
|
| 216 |
+
|
| 217 |
+
def _get_shape(tensor):
|
| 218 |
+
return list(tensor.shape)
|
| 219 |
+
|
| 220 |
+
return recursively_apply(_get_shape, data)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def initialize_tensors(data_structure):
|
| 224 |
+
"""
|
| 225 |
+
Recursively initializes tensors from a nested list/tuple/dictionary of [`~utils.TensorInformation`].
|
| 226 |
+
|
| 227 |
+
Returns:
|
| 228 |
+
The same data structure as `data` with tensors instead of [`~utils.TensorInformation`].
|
| 229 |
+
"""
|
| 230 |
+
|
| 231 |
+
def _initialize_tensor(tensor_info):
|
| 232 |
+
return torch.empty(*tensor_info.shape, dtype=tensor_info.dtype)
|
| 233 |
+
|
| 234 |
+
return recursively_apply(_initialize_tensor, data_structure, test_type=is_tensor_information)
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def find_batch_size(data):
|
| 238 |
+
"""
|
| 239 |
+
Recursively finds the batch size in a nested list/tuple/dictionary of lists of tensors.
|
| 240 |
+
|
| 241 |
+
Args:
|
| 242 |
+
data (nested list/tuple/dictionary of `torch.Tensor`): The data from which to find the batch size.
|
| 243 |
+
|
| 244 |
+
Returns:
|
| 245 |
+
`int`: The batch size.
|
| 246 |
+
"""
|
| 247 |
+
if isinstance(data, (tuple, list, Mapping)) and (len(data) == 0):
|
| 248 |
+
raise ValueError(f"Cannot find the batch size from empty {type(data)}.")
|
| 249 |
+
|
| 250 |
+
if isinstance(data, (tuple, list)):
|
| 251 |
+
return find_batch_size(data[0])
|
| 252 |
+
elif isinstance(data, Mapping):
|
| 253 |
+
for k in data.keys():
|
| 254 |
+
return find_batch_size(data[k])
|
| 255 |
+
elif not isinstance(data, torch.Tensor):
|
| 256 |
+
raise TypeError(f"Can only find the batch size of tensors but got {type(data)}.")
|
| 257 |
+
return data.shape[0]
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
def ignorant_find_batch_size(data):
|
| 261 |
+
"""
|
| 262 |
+
Same as [`utils.operations.find_batch_size`] except will ignore if `ValueError` and `TypeErrors` are raised
|
| 263 |
+
|
| 264 |
+
Args:
|
| 265 |
+
data (nested list/tuple/dictionary of `torch.Tensor`): The data from which to find the batch size.
|
| 266 |
+
|
| 267 |
+
Returns:
|
| 268 |
+
`int`: The batch size.
|
| 269 |
+
"""
|
| 270 |
+
try:
|
| 271 |
+
return find_batch_size(data)
|
| 272 |
+
except (ValueError, TypeError):
|
| 273 |
+
pass
|
| 274 |
+
return None
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def listify(data):
|
| 278 |
+
"""
|
| 279 |
+
Recursively finds tensors in a nested list/tuple/dictionary and converts them to a list of numbers.
|
| 280 |
+
|
| 281 |
+
Args:
|
| 282 |
+
data (nested list/tuple/dictionary of `torch.Tensor`): The data from which to convert to regular numbers.
|
| 283 |
+
|
| 284 |
+
Returns:
|
| 285 |
+
The same data structure as `data` with lists of numbers instead of `torch.Tensor`.
|
| 286 |
+
"""
|
| 287 |
+
|
| 288 |
+
def _convert_to_list(tensor):
|
| 289 |
+
tensor = tensor.detach().cpu()
|
| 290 |
+
if tensor.dtype == torch.bfloat16:
|
| 291 |
+
# As of Numpy 1.21.4, NumPy does not support bfloat16 (see
|
| 292 |
+
# https://github.com/numpy/numpy/blob/a47ecdea856986cd60eabbd53265c2ca5916ad5d/doc/source/user/basics.types.rst ).
|
| 293 |
+
# Until Numpy adds bfloat16, we must convert float32.
|
| 294 |
+
tensor = tensor.to(torch.float32)
|
| 295 |
+
return tensor.tolist()
|
| 296 |
+
|
| 297 |
+
return recursively_apply(_convert_to_list, data)
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def _tpu_gather(tensor):
|
| 301 |
+
def _tpu_gather_one(tensor):
|
| 302 |
+
if tensor.ndim == 0:
|
| 303 |
+
tensor = tensor.clone()[None]
|
| 304 |
+
|
| 305 |
+
# Can only gather contiguous tensors
|
| 306 |
+
if not tensor.is_contiguous():
|
| 307 |
+
tensor = tensor.contiguous()
|
| 308 |
+
return xm.all_gather(tensor)
|
| 309 |
+
|
| 310 |
+
res = recursively_apply(_tpu_gather_one, tensor, error_on_other_type=True)
|
| 311 |
+
xm.mark_step()
|
| 312 |
+
return res
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def _gpu_gather(tensor):
|
| 316 |
+
state = PartialState()
|
| 317 |
+
gather_op = torch.distributed.all_gather_into_tensor
|
| 318 |
+
|
| 319 |
+
def _gpu_gather_one(tensor):
|
| 320 |
+
if tensor.ndim == 0:
|
| 321 |
+
tensor = tensor.clone()[None]
|
| 322 |
+
|
| 323 |
+
# Can only gather contiguous tensors
|
| 324 |
+
if not tensor.is_contiguous():
|
| 325 |
+
tensor = tensor.contiguous()
|
| 326 |
+
|
| 327 |
+
if state.backend is not None and state.backend != "gloo":
|
| 328 |
+
# We use `empty` as `all_gather_into_tensor` slightly
|
| 329 |
+
# differs from `all_gather` for better efficiency,
|
| 330 |
+
# and we rely on the number of items in the tensor
|
| 331 |
+
# rather than its direct shape
|
| 332 |
+
output_tensors = torch.empty(
|
| 333 |
+
state.num_processes * tensor.numel(),
|
| 334 |
+
dtype=tensor.dtype,
|
| 335 |
+
device=state.device,
|
| 336 |
+
)
|
| 337 |
+
gather_op(output_tensors, tensor)
|
| 338 |
+
return output_tensors.view(-1, *tensor.size()[1:])
|
| 339 |
+
else:
|
| 340 |
+
# a backend of `None` is always CPU
|
| 341 |
+
# also gloo does not support `all_gather_into_tensor`,
|
| 342 |
+
# which will result in a larger memory overhead for the op
|
| 343 |
+
output_tensors = [torch.empty_like(tensor) for _ in range(state.num_processes)]
|
| 344 |
+
torch.distributed.all_gather(output_tensors, tensor)
|
| 345 |
+
return torch.cat(output_tensors, dim=0)
|
| 346 |
+
|
| 347 |
+
return recursively_apply(_gpu_gather_one, tensor, error_on_other_type=True)
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
class DistributedOperationException(Exception):
|
| 351 |
+
"""
|
| 352 |
+
An exception class for distributed operations. Raised if the operation cannot be performed due to the shape of the
|
| 353 |
+
tensors.
|
| 354 |
+
"""
|
| 355 |
+
|
| 356 |
+
pass
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def verify_operation(function):
|
| 360 |
+
"""
|
| 361 |
+
Verifies that `tensor` is the same shape across all processes. Only ran if `PartialState().debug` is `True`.
|
| 362 |
+
"""
|
| 363 |
+
|
| 364 |
+
@wraps(function)
|
| 365 |
+
def wrapper(*args, **kwargs):
|
| 366 |
+
if PartialState().distributed_type == DistributedType.NO or not PartialState().debug:
|
| 367 |
+
return function(*args, **kwargs)
|
| 368 |
+
operation = f"{function.__module__}.{function.__name__}"
|
| 369 |
+
if "tensor" in kwargs:
|
| 370 |
+
tensor = kwargs["tensor"]
|
| 371 |
+
else:
|
| 372 |
+
tensor = args[0]
|
| 373 |
+
if PartialState().device.type != find_device(tensor).type:
|
| 374 |
+
raise DistributedOperationException(
|
| 375 |
+
f"One or more of the tensors passed to {operation} were not on the {tensor.device.type} while the `Accelerator` is configured for {PartialState().device.type}. "
|
| 376 |
+
f"Please move it to the {PartialState().device.type} before calling {operation}."
|
| 377 |
+
)
|
| 378 |
+
shapes = get_shape(tensor)
|
| 379 |
+
output = gather_object([shapes])
|
| 380 |
+
if output[0] is not None:
|
| 381 |
+
are_same = output.count(output[0]) == len(output)
|
| 382 |
+
if not are_same:
|
| 383 |
+
process_shape_str = "\n - ".join([f"Process {i}: {shape}" for i, shape in enumerate(output)])
|
| 384 |
+
raise DistributedOperationException(
|
| 385 |
+
f"Cannot apply desired operation due to shape mismatches. "
|
| 386 |
+
"All shapes across devices must be valid."
|
| 387 |
+
f"\n\nOperation: `{operation}`\nInput shapes:\n - {process_shape_str}"
|
| 388 |
+
)
|
| 389 |
+
return function(*args, **kwargs)
|
| 390 |
+
|
| 391 |
+
return wrapper
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
def chained_operation(function):
|
| 395 |
+
"""
|
| 396 |
+
Checks that `verify_operation` failed and if so reports a more helpful error chaining the existing
|
| 397 |
+
`DistributedOperationException`.
|
| 398 |
+
"""
|
| 399 |
+
|
| 400 |
+
@wraps(function)
|
| 401 |
+
def wrapper(*args, **kwargs):
|
| 402 |
+
try:
|
| 403 |
+
return function(*args, **kwargs)
|
| 404 |
+
except DistributedOperationException as e:
|
| 405 |
+
operation = f"{function.__module__}.{function.__name__}"
|
| 406 |
+
raise DistributedOperationException(
|
| 407 |
+
f"Error found while calling `{operation}`. Please see the earlier error for more details."
|
| 408 |
+
) from e
|
| 409 |
+
|
| 410 |
+
return wrapper
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
@verify_operation
|
| 414 |
+
def gather(tensor):
|
| 415 |
+
"""
|
| 416 |
+
Recursively gather tensor in a nested list/tuple/dictionary of tensors from all devices.
|
| 417 |
+
|
| 418 |
+
Args:
|
| 419 |
+
tensor (nested list/tuple/dictionary of `torch.Tensor`):
|
| 420 |
+
The data to gather.
|
| 421 |
+
|
| 422 |
+
Returns:
|
| 423 |
+
The same data structure as `tensor` with all tensors sent to the proper device.
|
| 424 |
+
"""
|
| 425 |
+
if PartialState().distributed_type == DistributedType.XLA:
|
| 426 |
+
return _tpu_gather(tensor)
|
| 427 |
+
elif PartialState().distributed_type in TORCH_DISTRIBUTED_OPERATION_TYPES:
|
| 428 |
+
return _gpu_gather(tensor)
|
| 429 |
+
else:
|
| 430 |
+
return tensor
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
def _gpu_gather_object(object: Any):
|
| 434 |
+
output_objects = [None for _ in range(PartialState().num_processes)]
|
| 435 |
+
torch.distributed.all_gather_object(output_objects, object)
|
| 436 |
+
# all_gather_object returns a list of lists, so we need to flatten it
|
| 437 |
+
return [x for y in output_objects for x in y]
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
def gather_object(object: Any):
|
| 441 |
+
"""
|
| 442 |
+
Recursively gather object in a nested list/tuple/dictionary of objects from all devices.
|
| 443 |
+
|
| 444 |
+
Args:
|
| 445 |
+
object (nested list/tuple/dictionary of picklable object):
|
| 446 |
+
The data to gather.
|
| 447 |
+
|
| 448 |
+
Returns:
|
| 449 |
+
The same data structure as `object` with all the objects sent to every device.
|
| 450 |
+
"""
|
| 451 |
+
if PartialState().distributed_type == DistributedType.XLA:
|
| 452 |
+
raise NotImplementedError("gather objects in TPU is not supported")
|
| 453 |
+
elif PartialState().distributed_type in TORCH_DISTRIBUTED_OPERATION_TYPES:
|
| 454 |
+
return _gpu_gather_object(object)
|
| 455 |
+
else:
|
| 456 |
+
return object
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
def _gpu_broadcast(data, src=0):
|
| 460 |
+
def _gpu_broadcast_one(tensor, src=0):
|
| 461 |
+
torch.distributed.broadcast(tensor, src=src)
|
| 462 |
+
return tensor
|
| 463 |
+
|
| 464 |
+
return recursively_apply(_gpu_broadcast_one, data, error_on_other_type=True, src=src)
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
def _tpu_broadcast(tensor, src=0, name="broadcast tensor"):
|
| 468 |
+
if isinstance(tensor, (list, tuple)):
|
| 469 |
+
return honor_type(tensor, (_tpu_broadcast(t, name=f"{name}_{i}") for i, t in enumerate(tensor)))
|
| 470 |
+
elif isinstance(tensor, Mapping):
|
| 471 |
+
return type(tensor)({k: _tpu_broadcast(v, name=f"{name}_{k}") for k, v in tensor.items()})
|
| 472 |
+
return xm.mesh_reduce(name, tensor, lambda x: x[src])
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
TENSOR_TYPE_TO_INT = {
|
| 476 |
+
torch.float: 1,
|
| 477 |
+
torch.double: 2,
|
| 478 |
+
torch.half: 3,
|
| 479 |
+
torch.bfloat16: 4,
|
| 480 |
+
torch.uint8: 5,
|
| 481 |
+
torch.int8: 6,
|
| 482 |
+
torch.int16: 7,
|
| 483 |
+
torch.int32: 8,
|
| 484 |
+
torch.int64: 9,
|
| 485 |
+
torch.bool: 10,
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
TENSOR_INT_TO_DTYPE = {v: k for k, v in TENSOR_TYPE_TO_INT.items()}
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
def gather_tensor_shape(tensor):
|
| 492 |
+
"""
|
| 493 |
+
Grabs the shape of `tensor` only available on one process and returns a tensor of its shape
|
| 494 |
+
"""
|
| 495 |
+
# Allocate 80 bytes to store the shape
|
| 496 |
+
max_tensor_dimension = 2**20
|
| 497 |
+
state = PartialState()
|
| 498 |
+
base_tensor = torch.empty(max_tensor_dimension, dtype=torch.int, device=state.device)
|
| 499 |
+
|
| 500 |
+
# Since PyTorch can't just send a tensor to another GPU without
|
| 501 |
+
# knowing its size, we store the size of the tensor with data
|
| 502 |
+
# in an allocation
|
| 503 |
+
if tensor is not None:
|
| 504 |
+
shape = tensor.shape
|
| 505 |
+
tensor_dtype = TENSOR_TYPE_TO_INT[tensor.dtype]
|
| 506 |
+
base_tensor[: len(shape) + 1] = torch.tensor(list(shape) + [tensor_dtype], dtype=int)
|
| 507 |
+
# Perform a reduction to copy the size data onto all GPUs
|
| 508 |
+
base_tensor = reduce(base_tensor, reduction="sum")
|
| 509 |
+
base_tensor = base_tensor[base_tensor.nonzero()]
|
| 510 |
+
# The last non-zero data contains the coded dtype the source tensor is
|
| 511 |
+
dtype = int(base_tensor[-1:][0])
|
| 512 |
+
base_tensor = base_tensor[:-1]
|
| 513 |
+
return base_tensor, dtype
|
| 514 |
+
|
| 515 |
+
|
| 516 |
+
def copy_tensor_to_devices(tensor=None) -> torch.Tensor:
|
| 517 |
+
"""
|
| 518 |
+
Copys a tensor that only exists on a single device and broadcasts it to other devices. Differs from `broadcast` as
|
| 519 |
+
each worker doesn't need to know its shape when used (and tensor can be `None`)
|
| 520 |
+
|
| 521 |
+
Args:
|
| 522 |
+
tensor (`torch.tensor`):
|
| 523 |
+
The tensor that should be sent to all devices. Must only have it be defined on a single device, the rest
|
| 524 |
+
should be `None`.
|
| 525 |
+
"""
|
| 526 |
+
state = PartialState()
|
| 527 |
+
shape, dtype = gather_tensor_shape(tensor)
|
| 528 |
+
if tensor is None:
|
| 529 |
+
tensor = torch.zeros(shape, dtype=TENSOR_INT_TO_DTYPE[dtype]).to(state.device)
|
| 530 |
+
return reduce(tensor, reduction="sum")
|
| 531 |
+
|
| 532 |
+
|
| 533 |
+
@verify_operation
|
| 534 |
+
def broadcast(tensor, from_process: int = 0):
|
| 535 |
+
"""
|
| 536 |
+
Recursively broadcast tensor in a nested list/tuple/dictionary of tensors to all devices.
|
| 537 |
+
|
| 538 |
+
Args:
|
| 539 |
+
tensor (nested list/tuple/dictionary of `torch.Tensor`):
|
| 540 |
+
The data to gather.
|
| 541 |
+
from_process (`int`, *optional*, defaults to 0):
|
| 542 |
+
The process from which to send the data
|
| 543 |
+
|
| 544 |
+
Returns:
|
| 545 |
+
The same data structure as `tensor` with all tensors broadcasted to the proper device.
|
| 546 |
+
"""
|
| 547 |
+
if PartialState().distributed_type == DistributedType.XLA:
|
| 548 |
+
return _tpu_broadcast(tensor, src=from_process, name="accelerate.utils.broadcast")
|
| 549 |
+
elif PartialState().distributed_type in TORCH_DISTRIBUTED_OPERATION_TYPES:
|
| 550 |
+
return _gpu_broadcast(tensor, src=from_process)
|
| 551 |
+
else:
|
| 552 |
+
return tensor
|
| 553 |
+
|
| 554 |
+
|
| 555 |
+
def broadcast_object_list(object_list, from_process: int = 0):
|
| 556 |
+
"""
|
| 557 |
+
Broadcast a list of picklable objects form one process to the others.
|
| 558 |
+
|
| 559 |
+
Args:
|
| 560 |
+
object_list (list of picklable objects):
|
| 561 |
+
The list of objects to broadcast. This list will be modified inplace.
|
| 562 |
+
from_process (`int`, *optional*, defaults to 0):
|
| 563 |
+
The process from which to send the data.
|
| 564 |
+
|
| 565 |
+
Returns:
|
| 566 |
+
The same list containing the objects from process 0.
|
| 567 |
+
"""
|
| 568 |
+
if PartialState().distributed_type == DistributedType.XLA:
|
| 569 |
+
for i, obj in enumerate(object_list):
|
| 570 |
+
object_list[i] = xm.mesh_reduce("accelerate.utils.broadcast_object_list", obj, lambda x: x[from_process])
|
| 571 |
+
elif PartialState().distributed_type in TORCH_DISTRIBUTED_OPERATION_TYPES:
|
| 572 |
+
torch.distributed.broadcast_object_list(object_list, src=from_process)
|
| 573 |
+
return object_list
|
| 574 |
+
|
| 575 |
+
|
| 576 |
+
def slice_tensors(data, tensor_slice, process_index=None, num_processes=None):
|
| 577 |
+
"""
|
| 578 |
+
Recursively takes a slice in a nested list/tuple/dictionary of tensors.
|
| 579 |
+
|
| 580 |
+
Args:
|
| 581 |
+
data (nested list/tuple/dictionary of `torch.Tensor`):
|
| 582 |
+
The data to slice.
|
| 583 |
+
tensor_slice (`slice`):
|
| 584 |
+
The slice to take.
|
| 585 |
+
|
| 586 |
+
Returns:
|
| 587 |
+
The same data structure as `data` with all the tensors slices.
|
| 588 |
+
"""
|
| 589 |
+
|
| 590 |
+
def _slice_tensor(tensor, tensor_slice):
|
| 591 |
+
return tensor[tensor_slice]
|
| 592 |
+
|
| 593 |
+
return recursively_apply(_slice_tensor, data, tensor_slice)
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
def concatenate(data, dim=0):
|
| 597 |
+
"""
|
| 598 |
+
Recursively concatenate the tensors in a nested list/tuple/dictionary of lists of tensors with the same shape.
|
| 599 |
+
|
| 600 |
+
Args:
|
| 601 |
+
data (nested list/tuple/dictionary of lists of tensors `torch.Tensor`):
|
| 602 |
+
The data to concatenate.
|
| 603 |
+
dim (`int`, *optional*, defaults to 0):
|
| 604 |
+
The dimension on which to concatenate.
|
| 605 |
+
|
| 606 |
+
Returns:
|
| 607 |
+
The same data structure as `data` with all the tensors concatenated.
|
| 608 |
+
"""
|
| 609 |
+
if isinstance(data[0], (tuple, list)):
|
| 610 |
+
return honor_type(data[0], (concatenate([d[i] for d in data], dim=dim) for i in range(len(data[0]))))
|
| 611 |
+
elif isinstance(data[0], Mapping):
|
| 612 |
+
return type(data[0])({k: concatenate([d[k] for d in data], dim=dim) for k in data[0].keys()})
|
| 613 |
+
elif not isinstance(data[0], torch.Tensor):
|
| 614 |
+
raise TypeError(f"Can only concatenate tensors but got {type(data[0])}")
|
| 615 |
+
return torch.cat(data, dim=dim)
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
class CannotPadNestedTensorWarning(UserWarning):
|
| 619 |
+
pass
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
@chained_operation
|
| 623 |
+
def pad_across_processes(tensor, dim=0, pad_index=0, pad_first=False):
|
| 624 |
+
"""
|
| 625 |
+
Recursively pad the tensors in a nested list/tuple/dictionary of tensors from all devices to the same size so they
|
| 626 |
+
can safely be gathered.
|
| 627 |
+
|
| 628 |
+
Args:
|
| 629 |
+
tensor (nested list/tuple/dictionary of `torch.Tensor`):
|
| 630 |
+
The data to gather.
|
| 631 |
+
dim (`int`, *optional*, defaults to 0):
|
| 632 |
+
The dimension on which to pad.
|
| 633 |
+
pad_index (`int`, *optional*, defaults to 0):
|
| 634 |
+
The value with which to pad.
|
| 635 |
+
pad_first (`bool`, *optional*, defaults to `False`):
|
| 636 |
+
Whether to pad at the beginning or the end.
|
| 637 |
+
"""
|
| 638 |
+
|
| 639 |
+
def _pad_across_processes(tensor, dim=0, pad_index=0, pad_first=False):
|
| 640 |
+
if getattr(tensor, "is_nested", False):
|
| 641 |
+
warnings.warn(
|
| 642 |
+
"Cannot pad nested tensors without more information. Leaving unprocessed.",
|
| 643 |
+
CannotPadNestedTensorWarning,
|
| 644 |
+
)
|
| 645 |
+
return tensor
|
| 646 |
+
if dim >= len(tensor.shape) or dim < -len(tensor.shape):
|
| 647 |
+
return tensor
|
| 648 |
+
# Convert negative dimensions to non-negative
|
| 649 |
+
if dim < 0:
|
| 650 |
+
dim += len(tensor.shape)
|
| 651 |
+
|
| 652 |
+
# Gather all sizes
|
| 653 |
+
size = torch.tensor(tensor.shape, device=tensor.device)[None]
|
| 654 |
+
sizes = gather(size).cpu()
|
| 655 |
+
# Then pad to the maximum size
|
| 656 |
+
max_size = max(s[dim] for s in sizes)
|
| 657 |
+
if max_size == tensor.shape[dim]:
|
| 658 |
+
return tensor
|
| 659 |
+
|
| 660 |
+
old_size = tensor.shape
|
| 661 |
+
new_size = list(old_size)
|
| 662 |
+
new_size[dim] = max_size
|
| 663 |
+
new_tensor = tensor.new_zeros(tuple(new_size)) + pad_index
|
| 664 |
+
if pad_first:
|
| 665 |
+
indices = tuple(
|
| 666 |
+
slice(max_size - old_size[dim], max_size) if i == dim else slice(None) for i in range(len(new_size))
|
| 667 |
+
)
|
| 668 |
+
else:
|
| 669 |
+
indices = tuple(slice(0, old_size[dim]) if i == dim else slice(None) for i in range(len(new_size)))
|
| 670 |
+
new_tensor[indices] = tensor
|
| 671 |
+
return new_tensor
|
| 672 |
+
|
| 673 |
+
return recursively_apply(
|
| 674 |
+
_pad_across_processes, tensor, error_on_other_type=True, dim=dim, pad_index=pad_index, pad_first=pad_first
|
| 675 |
+
)
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
def pad_input_tensors(tensor, batch_size, num_processes, dim=0):
|
| 679 |
+
"""
|
| 680 |
+
Takes a `tensor` of arbitrary size and pads it so that it can work given `num_processes` needed dimensions.
|
| 681 |
+
|
| 682 |
+
New tensors are just the last input repeated.
|
| 683 |
+
|
| 684 |
+
E.g.:
|
| 685 |
+
Tensor: ([3,4,4]) Num processes: 4 Expected result shape: ([4,4,4])
|
| 686 |
+
|
| 687 |
+
"""
|
| 688 |
+
|
| 689 |
+
def _pad_input_tensors(tensor, batch_size, num_processes, dim=0):
|
| 690 |
+
remainder = batch_size // num_processes
|
| 691 |
+
last_inputs = batch_size - (remainder * num_processes)
|
| 692 |
+
if batch_size // num_processes == 0:
|
| 693 |
+
to_pad = num_processes - batch_size
|
| 694 |
+
else:
|
| 695 |
+
to_pad = num_processes - (batch_size // num_processes)
|
| 696 |
+
# In the rare case that `to_pad` is negative,
|
| 697 |
+
# we need to pad the last inputs - the found `to_pad`
|
| 698 |
+
if last_inputs > to_pad & to_pad < 1:
|
| 699 |
+
to_pad = last_inputs - to_pad
|
| 700 |
+
old_size = tensor.shape
|
| 701 |
+
new_size = list(old_size)
|
| 702 |
+
new_size[0] = batch_size + to_pad
|
| 703 |
+
new_tensor = tensor.new_zeros(tuple(new_size))
|
| 704 |
+
indices = tuple(slice(0, old_size[dim]) if i == dim else slice(None) for i in range(len(new_size)))
|
| 705 |
+
new_tensor[indices] = tensor
|
| 706 |
+
return new_tensor
|
| 707 |
+
|
| 708 |
+
return recursively_apply(
|
| 709 |
+
_pad_input_tensors,
|
| 710 |
+
tensor,
|
| 711 |
+
error_on_other_type=True,
|
| 712 |
+
batch_size=batch_size,
|
| 713 |
+
num_processes=num_processes,
|
| 714 |
+
dim=dim,
|
| 715 |
+
)
|
| 716 |
+
|
| 717 |
+
|
| 718 |
+
@verify_operation
|
| 719 |
+
def reduce(tensor, reduction="mean", scale=1.0):
|
| 720 |
+
"""
|
| 721 |
+
Recursively reduce the tensors in a nested list/tuple/dictionary of lists of tensors across all processes by the
|
| 722 |
+
mean of a given operation.
|
| 723 |
+
|
| 724 |
+
Args:
|
| 725 |
+
tensor (nested list/tuple/dictionary of `torch.Tensor`):
|
| 726 |
+
The data to reduce.
|
| 727 |
+
reduction (`str`, *optional*, defaults to `"mean"`):
|
| 728 |
+
A reduction method. Can be of "mean", "sum", or "none"
|
| 729 |
+
scale (`float`, *optional*):
|
| 730 |
+
A default scaling value to be applied after the reduce, only valied on XLA.
|
| 731 |
+
|
| 732 |
+
Returns:
|
| 733 |
+
The same data structure as `data` with all the tensors reduced.
|
| 734 |
+
"""
|
| 735 |
+
|
| 736 |
+
def _reduce_across_processes(tensor, reduction="mean", scale=1.0):
|
| 737 |
+
state = PartialState()
|
| 738 |
+
cloned_tensor = tensor.clone()
|
| 739 |
+
if state.distributed_type == DistributedType.NO:
|
| 740 |
+
return cloned_tensor
|
| 741 |
+
if state.distributed_type == DistributedType.XLA:
|
| 742 |
+
# Some processes may have different HLO graphs than other
|
| 743 |
+
# processes, for example in the breakpoint API
|
| 744 |
+
# accelerator.set_trigger(). Use mark_step to make HLOs
|
| 745 |
+
# the same on all processes.
|
| 746 |
+
xm.mark_step()
|
| 747 |
+
xm.all_reduce(xm.REDUCE_SUM, [cloned_tensor], scale)
|
| 748 |
+
xm.mark_step()
|
| 749 |
+
elif state.distributed_type.value in TORCH_DISTRIBUTED_OPERATION_TYPES:
|
| 750 |
+
torch.distributed.all_reduce(cloned_tensor, ReduceOp.SUM)
|
| 751 |
+
if reduction == "mean":
|
| 752 |
+
cloned_tensor /= state.num_processes
|
| 753 |
+
return cloned_tensor
|
| 754 |
+
|
| 755 |
+
return recursively_apply(
|
| 756 |
+
_reduce_across_processes, tensor, error_on_other_type=True, reduction=reduction, scale=scale
|
| 757 |
+
)
|
| 758 |
+
|
| 759 |
+
|
| 760 |
+
def convert_to_fp32(tensor):
|
| 761 |
+
"""
|
| 762 |
+
Recursively converts the elements nested list/tuple/dictionary of tensors in FP16/BF16 precision to FP32.
|
| 763 |
+
|
| 764 |
+
Args:
|
| 765 |
+
tensor (nested list/tuple/dictionary of `torch.Tensor`):
|
| 766 |
+
The data to convert from FP16/BF16 to FP32.
|
| 767 |
+
|
| 768 |
+
Returns:
|
| 769 |
+
The same data structure as `tensor` with all tensors that were in FP16/BF16 precision converted to FP32.
|
| 770 |
+
"""
|
| 771 |
+
|
| 772 |
+
def _convert_to_fp32(tensor):
|
| 773 |
+
return tensor.float()
|
| 774 |
+
|
| 775 |
+
def _is_fp16_bf16_tensor(tensor):
|
| 776 |
+
return (is_torch_tensor(tensor) or hasattr(tensor, "dtype")) and tensor.dtype in (
|
| 777 |
+
torch.float16,
|
| 778 |
+
torch.bfloat16,
|
| 779 |
+
)
|
| 780 |
+
|
| 781 |
+
return recursively_apply(_convert_to_fp32, tensor, test_type=_is_fp16_bf16_tensor)
|
| 782 |
+
|
| 783 |
+
|
| 784 |
+
class ConvertOutputsToFp32:
|
| 785 |
+
"""
|
| 786 |
+
Decorator to apply to a function outputing tensors (like a model forward pass) that ensures the outputs in FP16
|
| 787 |
+
precision will be convert back to FP32.
|
| 788 |
+
|
| 789 |
+
Args:
|
| 790 |
+
model_forward (`Callable`):
|
| 791 |
+
The function which outputs we want to treat.
|
| 792 |
+
|
| 793 |
+
Returns:
|
| 794 |
+
The same function as `model_forward` but with converted outputs.
|
| 795 |
+
"""
|
| 796 |
+
|
| 797 |
+
def __init__(self, model_forward):
|
| 798 |
+
self.model_forward = model_forward
|
| 799 |
+
update_wrapper(self, model_forward)
|
| 800 |
+
|
| 801 |
+
def __call__(self, *args, **kwargs):
|
| 802 |
+
return convert_to_fp32(self.model_forward(*args, **kwargs))
|
| 803 |
+
|
| 804 |
+
def __getstate__(self):
|
| 805 |
+
raise pickle.PicklingError(
|
| 806 |
+
"Cannot pickle a prepared model with automatic mixed precision, please unwrap the model with `Accelerator.unwrap_model(model)` before pickling it."
|
| 807 |
+
)
|
| 808 |
+
|
| 809 |
+
|
| 810 |
+
def convert_outputs_to_fp32(model_forward):
|
| 811 |
+
model_forward = ConvertOutputsToFp32(model_forward)
|
| 812 |
+
|
| 813 |
+
def forward(*args, **kwargs):
|
| 814 |
+
return model_forward(*args, **kwargs)
|
| 815 |
+
|
| 816 |
+
# To act like a decorator so that it can be popped when doing `extract_model_from_parallel`
|
| 817 |
+
forward.__wrapped__ = model_forward
|
| 818 |
+
|
| 819 |
+
return forward
|
| 820 |
+
|
| 821 |
+
|
| 822 |
+
def find_device(data):
|
| 823 |
+
"""
|
| 824 |
+
Finds the device on which a nested dict/list/tuple of tensors lies (assuming they are all on the same device).
|
| 825 |
+
|
| 826 |
+
Args:
|
| 827 |
+
(nested list/tuple/dictionary of `torch.Tensor`): The data we want to know the device of.
|
| 828 |
+
"""
|
| 829 |
+
if isinstance(data, Mapping):
|
| 830 |
+
for obj in data.values():
|
| 831 |
+
device = find_device(obj)
|
| 832 |
+
if device is not None:
|
| 833 |
+
return device
|
| 834 |
+
elif isinstance(data, (tuple, list)):
|
| 835 |
+
for obj in data:
|
| 836 |
+
device = find_device(obj)
|
| 837 |
+
if device is not None:
|
| 838 |
+
return device
|
| 839 |
+
elif isinstance(data, torch.Tensor):
|
| 840 |
+
return data.device
|
| 841 |
+
|
| 842 |
+
|
| 843 |
+
@contextmanager
|
| 844 |
+
def GatheredParameters(params, modifier_rank=None, fwd_module=None, enabled=True):
|
| 845 |
+
"""
|
| 846 |
+
Wrapper around `deepspeed.runtime.zero.GatheredParameters`, but if Zero-3 is not enabled, will be a no-op context
|
| 847 |
+
manager.
|
| 848 |
+
"""
|
| 849 |
+
# We need to use the `AcceleratorState` here since it has access to the deepspeed plugin
|
| 850 |
+
if AcceleratorState().distributed_type != DistributedType.DEEPSPEED or (
|
| 851 |
+
AcceleratorState().deepspeed_plugin is not None
|
| 852 |
+
and not AcceleratorState().deepspeed_plugin.is_zero3_init_enabled()
|
| 853 |
+
):
|
| 854 |
+
gather_param_context = nullcontext()
|
| 855 |
+
else:
|
| 856 |
+
import deepspeed
|
| 857 |
+
|
| 858 |
+
gather_param_context = deepspeed.zero.GatheredParameters(
|
| 859 |
+
params, modifier_rank=modifier_rank, fwd_module=fwd_module, enabled=enabled
|
| 860 |
+
)
|
| 861 |
+
with gather_param_context:
|
| 862 |
+
yield
|
venv/lib/python3.11/site-packages/accelerate/utils/other.py
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import collections
|
| 16 |
+
import platform
|
| 17 |
+
import re
|
| 18 |
+
import socket
|
| 19 |
+
from codecs import encode
|
| 20 |
+
from collections import OrderedDict
|
| 21 |
+
from functools import partial, reduce
|
| 22 |
+
from types import MethodType
|
| 23 |
+
|
| 24 |
+
import numpy as np
|
| 25 |
+
import torch
|
| 26 |
+
from packaging.version import Version
|
| 27 |
+
from safetensors.torch import save_file as safe_save_file
|
| 28 |
+
|
| 29 |
+
from ..commands.config.default import write_basic_config # noqa: F401
|
| 30 |
+
from ..logging import get_logger
|
| 31 |
+
from ..state import PartialState
|
| 32 |
+
from .constants import FSDP_PYTORCH_VERSION
|
| 33 |
+
from .dataclasses import DistributedType
|
| 34 |
+
from .imports import (
|
| 35 |
+
is_deepspeed_available,
|
| 36 |
+
is_numpy_available,
|
| 37 |
+
is_torch_distributed_available,
|
| 38 |
+
is_torch_xla_available,
|
| 39 |
+
is_weights_only_available,
|
| 40 |
+
)
|
| 41 |
+
from .modeling import id_tensor_storage
|
| 42 |
+
from .transformer_engine import convert_model
|
| 43 |
+
from .versions import is_torch_version
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
logger = get_logger(__name__)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
if is_torch_xla_available():
|
| 50 |
+
import torch_xla.core.xla_model as xm
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def is_compiled_module(module):
|
| 54 |
+
"""
|
| 55 |
+
Check whether the module was compiled with torch.compile()
|
| 56 |
+
"""
|
| 57 |
+
if not hasattr(torch, "_dynamo"):
|
| 58 |
+
return False
|
| 59 |
+
return isinstance(module, torch._dynamo.eval_frame.OptimizedModule)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def extract_model_from_parallel(
|
| 63 |
+
model, keep_fp32_wrapper: bool = True, keep_torch_compile: bool = True, recursive: bool = False
|
| 64 |
+
):
|
| 65 |
+
"""
|
| 66 |
+
Extract a model from its distributed containers.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
model (`torch.nn.Module`):
|
| 70 |
+
The model to extract.
|
| 71 |
+
keep_fp32_wrapper (`bool`, *optional*):
|
| 72 |
+
Whether to remove mixed precision hooks from the model.
|
| 73 |
+
keep_torch_compile (`bool`, *optional*):
|
| 74 |
+
Whether to unwrap compiled model.
|
| 75 |
+
recursive (`bool`, *optional*, defaults to `False`):
|
| 76 |
+
Whether to recursively extract all cases of `module.module` from `model` as well as unwrap child sublayers
|
| 77 |
+
recursively, not just the top-level distributed containers.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
`torch.nn.Module`: The extracted model.
|
| 81 |
+
"""
|
| 82 |
+
options = (torch.nn.parallel.DistributedDataParallel, torch.nn.DataParallel)
|
| 83 |
+
|
| 84 |
+
is_compiled = is_compiled_module(model)
|
| 85 |
+
if is_compiled:
|
| 86 |
+
compiled_model = model
|
| 87 |
+
model = model._orig_mod
|
| 88 |
+
|
| 89 |
+
if is_deepspeed_available():
|
| 90 |
+
from deepspeed import DeepSpeedEngine
|
| 91 |
+
|
| 92 |
+
options += (DeepSpeedEngine,)
|
| 93 |
+
|
| 94 |
+
if is_torch_version(">=", FSDP_PYTORCH_VERSION) and is_torch_distributed_available():
|
| 95 |
+
from torch.distributed.fsdp.fully_sharded_data_parallel import FullyShardedDataParallel as FSDP
|
| 96 |
+
|
| 97 |
+
options += (FSDP,)
|
| 98 |
+
|
| 99 |
+
while isinstance(model, options):
|
| 100 |
+
model = model.module
|
| 101 |
+
|
| 102 |
+
if recursive:
|
| 103 |
+
# This is needed in cases such as using FSDPv2 on XLA
|
| 104 |
+
def _recursive_unwrap(module):
|
| 105 |
+
# Wrapped modules are standardly wrapped as `module`, similar to the cases earlier
|
| 106 |
+
# with DDP, DataParallel, DeepSpeed, and FSDP
|
| 107 |
+
if hasattr(module, "module"):
|
| 108 |
+
unwrapped_module = _recursive_unwrap(module.module)
|
| 109 |
+
else:
|
| 110 |
+
unwrapped_module = module
|
| 111 |
+
# Next unwrap child sublayers recursively
|
| 112 |
+
for name, child in unwrapped_module.named_children():
|
| 113 |
+
setattr(unwrapped_module, name, _recursive_unwrap(child))
|
| 114 |
+
return unwrapped_module
|
| 115 |
+
|
| 116 |
+
# Start with top-level
|
| 117 |
+
model = _recursive_unwrap(model)
|
| 118 |
+
|
| 119 |
+
if not keep_fp32_wrapper:
|
| 120 |
+
forward = model.forward
|
| 121 |
+
original_forward = model.__dict__.pop("_original_forward", None)
|
| 122 |
+
if original_forward is not None:
|
| 123 |
+
while hasattr(forward, "__wrapped__"):
|
| 124 |
+
forward = forward.__wrapped__
|
| 125 |
+
if forward == original_forward:
|
| 126 |
+
break
|
| 127 |
+
model.forward = MethodType(forward, model)
|
| 128 |
+
if getattr(model, "_converted_to_transformer_engine", False):
|
| 129 |
+
convert_model(model, to_transformer_engine=False)
|
| 130 |
+
|
| 131 |
+
if keep_torch_compile and is_compiled:
|
| 132 |
+
compiled_model._orig_mod = model
|
| 133 |
+
model = compiled_model
|
| 134 |
+
|
| 135 |
+
return model
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def wait_for_everyone():
|
| 139 |
+
"""
|
| 140 |
+
Introduces a blocking point in the script, making sure all processes have reached this point before continuing.
|
| 141 |
+
|
| 142 |
+
<Tip warning={true}>
|
| 143 |
+
|
| 144 |
+
Make sure all processes will reach this instruction otherwise one of your processes will hang forever.
|
| 145 |
+
|
| 146 |
+
</Tip>
|
| 147 |
+
"""
|
| 148 |
+
PartialState().wait_for_everyone()
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def clean_state_dict_for_safetensors(state_dict: dict):
|
| 152 |
+
"""
|
| 153 |
+
Cleans the state dictionary from a model and removes tensor aliasing if present.
|
| 154 |
+
|
| 155 |
+
Args:
|
| 156 |
+
state_dict (`dict`):
|
| 157 |
+
The state dictionary from a model
|
| 158 |
+
"""
|
| 159 |
+
ptrs = collections.defaultdict(list)
|
| 160 |
+
# When bnb serialization is used, weights in state dict can be strings
|
| 161 |
+
for name, tensor in state_dict.items():
|
| 162 |
+
if not isinstance(tensor, str):
|
| 163 |
+
ptrs[id_tensor_storage(tensor)].append(name)
|
| 164 |
+
|
| 165 |
+
# These are all pointers of tensors with shared memory
|
| 166 |
+
shared_ptrs = {ptr: names for ptr, names in ptrs.items() if len(names) > 1}
|
| 167 |
+
warn_names = set()
|
| 168 |
+
for names in shared_ptrs.values():
|
| 169 |
+
# When not all duplicates have been cleaned, we still remove those keys but put a clear warning.
|
| 170 |
+
# If the link between tensors was done at runtime then `from_pretrained` will not get
|
| 171 |
+
# the key back leading to random tensor. A proper warning will be shown
|
| 172 |
+
# during reload (if applicable), but since the file is not necessarily compatible with
|
| 173 |
+
# the config, better show a proper warning.
|
| 174 |
+
found_names = [name for name in names if name in state_dict]
|
| 175 |
+
warn_names.update(found_names[1:])
|
| 176 |
+
for name in found_names[1:]:
|
| 177 |
+
del state_dict[name]
|
| 178 |
+
if len(warn_names) > 0:
|
| 179 |
+
logger.warning(
|
| 180 |
+
f"Removed shared tensor {warn_names} while saving. This should be OK, but check by verifying that you don't receive any warning while reloading",
|
| 181 |
+
)
|
| 182 |
+
state_dict = {k: v.contiguous() if isinstance(v, torch.Tensor) else v for k, v in state_dict.items()}
|
| 183 |
+
return state_dict
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def save(obj, f, save_on_each_node: bool = False, safe_serialization: bool = False):
|
| 187 |
+
"""
|
| 188 |
+
Save the data to disk. Use in place of `torch.save()`.
|
| 189 |
+
|
| 190 |
+
Args:
|
| 191 |
+
obj:
|
| 192 |
+
The data to save
|
| 193 |
+
f:
|
| 194 |
+
The file (or file-like object) to use to save the data
|
| 195 |
+
save_on_each_node (`bool`, *optional*, defaults to `False`):
|
| 196 |
+
Whether to only save on the global main process
|
| 197 |
+
safe_serialization (`bool`, *optional*, defaults to `False`):
|
| 198 |
+
Whether to save `obj` using `safetensors` or the traditional PyTorch way (that uses `pickle`).
|
| 199 |
+
"""
|
| 200 |
+
# When TorchXLA is enabled, it's necessary to transfer all data to the CPU before saving.
|
| 201 |
+
# Another issue arises with `id_tensor_storage`, which treats all XLA tensors as identical.
|
| 202 |
+
# If tensors remain on XLA, calling `clean_state_dict_for_safetensors` will result in only
|
| 203 |
+
# one XLA tensor remaining.
|
| 204 |
+
if PartialState().distributed_type == DistributedType.XLA:
|
| 205 |
+
obj = xm._maybe_convert_to_cpu(obj)
|
| 206 |
+
# Check if it's a model and remove duplicates
|
| 207 |
+
if safe_serialization:
|
| 208 |
+
save_func = partial(safe_save_file, metadata={"format": "pt"})
|
| 209 |
+
if isinstance(obj, OrderedDict):
|
| 210 |
+
obj = clean_state_dict_for_safetensors(obj)
|
| 211 |
+
else:
|
| 212 |
+
save_func = torch.save
|
| 213 |
+
|
| 214 |
+
if PartialState().is_main_process and not save_on_each_node:
|
| 215 |
+
save_func(obj, f)
|
| 216 |
+
elif PartialState().is_local_main_process and save_on_each_node:
|
| 217 |
+
save_func(obj, f)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
# The following are considered "safe" globals to reconstruct various types of objects when using `weights_only=True`
|
| 221 |
+
# These should be added and then removed after loading in the file
|
| 222 |
+
np_core = np._core if is_numpy_available("2.0.0") else np.core
|
| 223 |
+
TORCH_SAFE_GLOBALS = [
|
| 224 |
+
# numpy arrays are just numbers, not objects, so we can reconstruct them safely
|
| 225 |
+
np_core.multiarray._reconstruct,
|
| 226 |
+
np.ndarray,
|
| 227 |
+
# The following are needed for the RNG states
|
| 228 |
+
encode,
|
| 229 |
+
np.dtype,
|
| 230 |
+
]
|
| 231 |
+
|
| 232 |
+
if is_numpy_available("1.25.0"):
|
| 233 |
+
TORCH_SAFE_GLOBALS.append(np.dtypes.UInt32DType)
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
def load(f, map_location=None, **kwargs):
|
| 237 |
+
"""
|
| 238 |
+
Compatible drop-in replacement of `torch.load()` which allows for `weights_only` to be used if `torch` version is
|
| 239 |
+
2.4.0 or higher. Otherwise will ignore the kwarg.
|
| 240 |
+
|
| 241 |
+
Will also add (and then remove) an exception for numpy arrays
|
| 242 |
+
|
| 243 |
+
Args:
|
| 244 |
+
f:
|
| 245 |
+
The file (or file-like object) to use to load the data
|
| 246 |
+
map_location:
|
| 247 |
+
a function, `torch.device`, string or a dict specifying how to remap storage locations
|
| 248 |
+
**kwargs:
|
| 249 |
+
Additional keyword arguments to pass to `torch.load()`.
|
| 250 |
+
"""
|
| 251 |
+
try:
|
| 252 |
+
if is_weights_only_available():
|
| 253 |
+
old_safe_globals = torch.serialization.get_safe_globals()
|
| 254 |
+
if "weights_only" not in kwargs:
|
| 255 |
+
kwargs["weights_only"] = True
|
| 256 |
+
torch.serialization.add_safe_globals(TORCH_SAFE_GLOBALS)
|
| 257 |
+
else:
|
| 258 |
+
kwargs.pop("weights_only", None)
|
| 259 |
+
loaded_obj = torch.load(f, map_location=map_location, **kwargs)
|
| 260 |
+
finally:
|
| 261 |
+
if is_weights_only_available():
|
| 262 |
+
torch.serialization.clear_safe_globals()
|
| 263 |
+
if old_safe_globals:
|
| 264 |
+
torch.serialization.add_safe_globals(old_safe_globals)
|
| 265 |
+
return loaded_obj
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def get_pretty_name(obj):
|
| 269 |
+
"""
|
| 270 |
+
Gets a pretty name from `obj`.
|
| 271 |
+
"""
|
| 272 |
+
if not hasattr(obj, "__qualname__") and not hasattr(obj, "__name__"):
|
| 273 |
+
obj = getattr(obj, "__class__", obj)
|
| 274 |
+
if hasattr(obj, "__qualname__"):
|
| 275 |
+
return obj.__qualname__
|
| 276 |
+
if hasattr(obj, "__name__"):
|
| 277 |
+
return obj.__name__
|
| 278 |
+
return str(obj)
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def merge_dicts(source, destination):
|
| 282 |
+
"""
|
| 283 |
+
Recursively merges two dictionaries.
|
| 284 |
+
|
| 285 |
+
Args:
|
| 286 |
+
source (`dict`): The dictionary to merge into `destination`.
|
| 287 |
+
destination (`dict`): The dictionary to merge `source` into.
|
| 288 |
+
"""
|
| 289 |
+
for key, value in source.items():
|
| 290 |
+
if isinstance(value, dict):
|
| 291 |
+
node = destination.setdefault(key, {})
|
| 292 |
+
merge_dicts(value, node)
|
| 293 |
+
else:
|
| 294 |
+
destination[key] = value
|
| 295 |
+
|
| 296 |
+
return destination
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def is_port_in_use(port: int = None) -> bool:
|
| 300 |
+
"""
|
| 301 |
+
Checks if a port is in use on `localhost`. Useful for checking if multiple `accelerate launch` commands have been
|
| 302 |
+
run and need to see if the port is already in use.
|
| 303 |
+
"""
|
| 304 |
+
if port is None:
|
| 305 |
+
port = 29500
|
| 306 |
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
| 307 |
+
return s.connect_ex(("localhost", port)) == 0
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
def convert_bytes(size):
|
| 311 |
+
"Converts `size` from bytes to the largest possible unit"
|
| 312 |
+
for x in ["bytes", "KB", "MB", "GB", "TB"]:
|
| 313 |
+
if size < 1024.0:
|
| 314 |
+
return f"{round(size, 2)} {x}"
|
| 315 |
+
size /= 1024.0
|
| 316 |
+
|
| 317 |
+
return f"{round(size, 2)} PB"
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
def check_os_kernel():
|
| 321 |
+
"""Warns if the kernel version is below the recommended minimum on Linux."""
|
| 322 |
+
# see issue #1929
|
| 323 |
+
info = platform.uname()
|
| 324 |
+
system = info.system
|
| 325 |
+
if system != "Linux":
|
| 326 |
+
return
|
| 327 |
+
|
| 328 |
+
_, version, *_ = re.split(r"(\d+\.\d+\.\d+)", info.release)
|
| 329 |
+
min_version = "5.5.0"
|
| 330 |
+
if Version(version) < Version(min_version):
|
| 331 |
+
msg = (
|
| 332 |
+
f"Detected kernel version {version}, which is below the recommended minimum of {min_version}; this can "
|
| 333 |
+
"cause the process to hang. It is recommended to upgrade the kernel to the minimum version or higher."
|
| 334 |
+
)
|
| 335 |
+
logger.warning(msg, main_process_only=True)
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
def recursive_getattr(obj, attr: str):
|
| 339 |
+
"""
|
| 340 |
+
Recursive `getattr`.
|
| 341 |
+
|
| 342 |
+
Args:
|
| 343 |
+
obj:
|
| 344 |
+
A class instance holding the attribute.
|
| 345 |
+
attr (`str`):
|
| 346 |
+
The attribute that is to be retrieved, e.g. 'attribute1.attribute2'.
|
| 347 |
+
"""
|
| 348 |
+
|
| 349 |
+
def _getattr(obj, attr):
|
| 350 |
+
return getattr(obj, attr)
|
| 351 |
+
|
| 352 |
+
return reduce(_getattr, [obj] + attr.split("."))
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def get_module_children_bottom_up(model: torch.nn.Module) -> list[torch.nn.Module]:
|
| 356 |
+
"""Traverse the model in bottom-up order and return the children modules in that order.
|
| 357 |
+
|
| 358 |
+
Args:
|
| 359 |
+
model (`torch.nn.Module`): the model to get the children of
|
| 360 |
+
|
| 361 |
+
Returns:
|
| 362 |
+
`list[torch.nn.Module]`: a list of children modules of `model` in bottom-up order. The last element is the
|
| 363 |
+
`model` itself.
|
| 364 |
+
"""
|
| 365 |
+
stack = [model]
|
| 366 |
+
ordered_modules = []
|
| 367 |
+
while stack:
|
| 368 |
+
current_module = stack.pop()
|
| 369 |
+
for _, attr in current_module.named_children():
|
| 370 |
+
if isinstance(attr, torch.nn.Module):
|
| 371 |
+
stack.append(attr)
|
| 372 |
+
ordered_modules.append(current_module)
|
| 373 |
+
return ordered_modules[::-1]
|
venv/lib/python3.11/site-packages/accelerate/utils/random.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import random
|
| 16 |
+
from typing import Optional, Union
|
| 17 |
+
|
| 18 |
+
import numpy as np
|
| 19 |
+
import torch
|
| 20 |
+
|
| 21 |
+
from ..state import AcceleratorState
|
| 22 |
+
from .constants import CUDA_DISTRIBUTED_TYPES
|
| 23 |
+
from .dataclasses import DistributedType, RNGType
|
| 24 |
+
from .imports import (
|
| 25 |
+
is_hpu_available,
|
| 26 |
+
is_mlu_available,
|
| 27 |
+
is_musa_available,
|
| 28 |
+
is_npu_available,
|
| 29 |
+
is_sdaa_available,
|
| 30 |
+
is_torch_xla_available,
|
| 31 |
+
is_xpu_available,
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
if is_torch_xla_available():
|
| 36 |
+
import torch_xla.core.xla_model as xm
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def set_seed(seed: int, device_specific: bool = False, deterministic: bool = False):
|
| 40 |
+
"""
|
| 41 |
+
Helper function for reproducible behavior to set the seed in `random`, `numpy`, `torch`.
|
| 42 |
+
|
| 43 |
+
Args:
|
| 44 |
+
seed (`int`):
|
| 45 |
+
The seed to set.
|
| 46 |
+
device_specific (`bool`, *optional*, defaults to `False`):
|
| 47 |
+
Whether to differ the seed on each device slightly with `self.process_index`.
|
| 48 |
+
deterministic (`bool`, *optional*, defaults to `False`):
|
| 49 |
+
Whether to use deterministic algorithms where available. Can slow down training.
|
| 50 |
+
"""
|
| 51 |
+
if device_specific:
|
| 52 |
+
seed += AcceleratorState().process_index
|
| 53 |
+
random.seed(seed)
|
| 54 |
+
np.random.seed(seed)
|
| 55 |
+
torch.manual_seed(seed)
|
| 56 |
+
if is_xpu_available():
|
| 57 |
+
torch.xpu.manual_seed_all(seed)
|
| 58 |
+
elif is_npu_available():
|
| 59 |
+
torch.npu.manual_seed_all(seed)
|
| 60 |
+
elif is_mlu_available():
|
| 61 |
+
torch.mlu.manual_seed_all(seed)
|
| 62 |
+
elif is_sdaa_available():
|
| 63 |
+
torch.sdaa.manual_seed_all(seed)
|
| 64 |
+
elif is_musa_available():
|
| 65 |
+
torch.musa.manual_seed_all(seed)
|
| 66 |
+
elif is_hpu_available():
|
| 67 |
+
torch.hpu.manual_seed_all(seed)
|
| 68 |
+
else:
|
| 69 |
+
torch.cuda.manual_seed_all(seed)
|
| 70 |
+
# ^^ safe to call this function even if cuda is not available
|
| 71 |
+
if is_torch_xla_available():
|
| 72 |
+
xm.set_rng_state(seed)
|
| 73 |
+
|
| 74 |
+
if deterministic:
|
| 75 |
+
torch.use_deterministic_algorithms(True)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def synchronize_rng_state(rng_type: Optional[RNGType] = None, generator: Optional[torch.Generator] = None):
|
| 79 |
+
# Get the proper rng state
|
| 80 |
+
if rng_type == RNGType.TORCH:
|
| 81 |
+
rng_state = torch.get_rng_state()
|
| 82 |
+
elif rng_type == RNGType.CUDA:
|
| 83 |
+
rng_state = torch.cuda.get_rng_state()
|
| 84 |
+
elif rng_type == RNGType.XLA:
|
| 85 |
+
assert is_torch_xla_available(), "Can't synchronize XLA seeds as torch_xla is unavailable."
|
| 86 |
+
rng_state = torch.tensor(xm.get_rng_state())
|
| 87 |
+
elif rng_type == RNGType.NPU:
|
| 88 |
+
assert is_npu_available(), "Can't synchronize NPU seeds on an environment without NPUs."
|
| 89 |
+
rng_state = torch.npu.get_rng_state()
|
| 90 |
+
elif rng_type == RNGType.MLU:
|
| 91 |
+
assert is_mlu_available(), "Can't synchronize MLU seeds on an environment without MLUs."
|
| 92 |
+
rng_state = torch.mlu.get_rng_state()
|
| 93 |
+
elif rng_type == RNGType.SDAA:
|
| 94 |
+
assert is_sdaa_available(), "Can't synchronize SDAA seeds on an environment without SDAAs."
|
| 95 |
+
rng_state = torch.sdaa.get_rng_state()
|
| 96 |
+
elif rng_type == RNGType.MUSA:
|
| 97 |
+
assert is_musa_available(), "Can't synchronize MUSA seeds on an environment without MUSAs."
|
| 98 |
+
rng_state = torch.musa.get_rng_state()
|
| 99 |
+
elif rng_type == RNGType.XPU:
|
| 100 |
+
assert is_xpu_available(), "Can't synchronize XPU seeds on an environment without XPUs."
|
| 101 |
+
rng_state = torch.xpu.get_rng_state()
|
| 102 |
+
elif rng_type == RNGType.HPU:
|
| 103 |
+
assert is_hpu_available(), "Can't synchronize HPU seeds on an environment without HPUs."
|
| 104 |
+
rng_state = torch.hpu.get_rng_state()
|
| 105 |
+
elif rng_type == RNGType.GENERATOR:
|
| 106 |
+
assert generator is not None, "Need a generator to synchronize its seed."
|
| 107 |
+
rng_state = generator.get_state()
|
| 108 |
+
|
| 109 |
+
# Broadcast the rng state from device 0 to other devices
|
| 110 |
+
state = AcceleratorState()
|
| 111 |
+
if state.distributed_type == DistributedType.XLA:
|
| 112 |
+
rng_state = rng_state.to(xm.xla_device())
|
| 113 |
+
xm.collective_broadcast([rng_state])
|
| 114 |
+
xm.mark_step()
|
| 115 |
+
rng_state = rng_state.cpu()
|
| 116 |
+
elif (
|
| 117 |
+
state.distributed_type in CUDA_DISTRIBUTED_TYPES
|
| 118 |
+
or state.distributed_type == DistributedType.MULTI_MLU
|
| 119 |
+
or state.distributed_type == DistributedType.MULTI_SDAA
|
| 120 |
+
or state.distributed_type == DistributedType.MULTI_MUSA
|
| 121 |
+
or state.distributed_type == DistributedType.MULTI_NPU
|
| 122 |
+
or state.distributed_type == DistributedType.MULTI_XPU
|
| 123 |
+
or state.distributed_type == DistributedType.MULTI_HPU
|
| 124 |
+
):
|
| 125 |
+
rng_state = rng_state.to(state.device)
|
| 126 |
+
torch.distributed.broadcast(rng_state, 0)
|
| 127 |
+
rng_state = rng_state.cpu()
|
| 128 |
+
elif state.distributed_type == DistributedType.MULTI_CPU:
|
| 129 |
+
torch.distributed.broadcast(rng_state, 0)
|
| 130 |
+
|
| 131 |
+
# Set the broadcast rng state
|
| 132 |
+
if rng_type == RNGType.TORCH:
|
| 133 |
+
torch.set_rng_state(rng_state)
|
| 134 |
+
elif rng_type == RNGType.CUDA:
|
| 135 |
+
torch.cuda.set_rng_state(rng_state)
|
| 136 |
+
elif rng_type == RNGType.NPU:
|
| 137 |
+
torch.npu.set_rng_state(rng_state)
|
| 138 |
+
elif rng_type == RNGType.MLU:
|
| 139 |
+
torch.mlu.set_rng_state(rng_state)
|
| 140 |
+
elif rng_type == RNGType.SDAA:
|
| 141 |
+
torch.sdaa.set_rng_state(rng_state)
|
| 142 |
+
elif rng_type == RNGType.MUSA:
|
| 143 |
+
torch.musa.set_rng_state(rng_state)
|
| 144 |
+
elif rng_type == RNGType.XPU:
|
| 145 |
+
torch.xpu.set_rng_state(rng_state)
|
| 146 |
+
elif rng_state == RNGType.HPU:
|
| 147 |
+
torch.hpu.set_rng_state(rng_state)
|
| 148 |
+
elif rng_type == RNGType.XLA:
|
| 149 |
+
xm.set_rng_state(rng_state.item())
|
| 150 |
+
elif rng_type == RNGType.GENERATOR:
|
| 151 |
+
generator.set_state(rng_state)
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def synchronize_rng_states(rng_types: list[Union[str, RNGType]], generator: Optional[torch.Generator] = None):
|
| 155 |
+
for rng_type in rng_types:
|
| 156 |
+
synchronize_rng_state(RNGType(rng_type), generator=generator)
|
venv/lib/python3.11/site-packages/accelerate/utils/rich.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from .imports import is_rich_available
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
if is_rich_available():
|
| 19 |
+
from rich.traceback import install
|
| 20 |
+
|
| 21 |
+
install(show_locals=False)
|
| 22 |
+
|
| 23 |
+
else:
|
| 24 |
+
raise ModuleNotFoundError("To use the rich extension, install rich with `pip install rich`")
|
venv/lib/python3.11/site-packages/accelerate/utils/torch_xla.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import importlib.metadata
|
| 16 |
+
import subprocess
|
| 17 |
+
import sys
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def install_xla(upgrade: bool = False):
|
| 21 |
+
"""
|
| 22 |
+
Helper function to install appropriate xla wheels based on the `torch` version in Google Colaboratory.
|
| 23 |
+
|
| 24 |
+
Args:
|
| 25 |
+
upgrade (`bool`, *optional*, defaults to `False`):
|
| 26 |
+
Whether to upgrade `torch` and install the latest `torch_xla` wheels.
|
| 27 |
+
|
| 28 |
+
Example:
|
| 29 |
+
|
| 30 |
+
```python
|
| 31 |
+
>>> from accelerate.utils import install_xla
|
| 32 |
+
|
| 33 |
+
>>> install_xla(upgrade=True)
|
| 34 |
+
```
|
| 35 |
+
"""
|
| 36 |
+
in_colab = False
|
| 37 |
+
if "IPython" in sys.modules:
|
| 38 |
+
in_colab = "google.colab" in str(sys.modules["IPython"].get_ipython())
|
| 39 |
+
|
| 40 |
+
if in_colab:
|
| 41 |
+
if upgrade:
|
| 42 |
+
torch_install_cmd = ["pip", "install", "-U", "torch"]
|
| 43 |
+
subprocess.run(torch_install_cmd, check=True)
|
| 44 |
+
# get the current version of torch
|
| 45 |
+
torch_version = importlib.metadata.version("torch")
|
| 46 |
+
torch_version_trunc = torch_version[: torch_version.rindex(".")]
|
| 47 |
+
xla_wheel = f"https://storage.googleapis.com/tpu-pytorch/wheels/colab/torch_xla-{torch_version_trunc}-cp37-cp37m-linux_x86_64.whl"
|
| 48 |
+
xla_install_cmd = ["pip", "install", xla_wheel]
|
| 49 |
+
subprocess.run(xla_install_cmd, check=True)
|
| 50 |
+
else:
|
| 51 |
+
raise RuntimeError("`install_xla` utility works only on google colab.")
|
venv/lib/python3.11/site-packages/accelerate/utils/tqdm.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
from .imports import is_tqdm_available
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
if is_tqdm_available():
|
| 20 |
+
from tqdm.auto import tqdm as _tqdm
|
| 21 |
+
|
| 22 |
+
from ..state import PartialState
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def tqdm(*args, main_process_only: bool = True, **kwargs):
|
| 26 |
+
"""
|
| 27 |
+
Wrapper around `tqdm.tqdm` that optionally displays only on the main process.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
main_process_only (`bool`, *optional*):
|
| 31 |
+
Whether to display the progress bar only on the main process
|
| 32 |
+
"""
|
| 33 |
+
if not is_tqdm_available():
|
| 34 |
+
raise ImportError("Accelerate's `tqdm` module requires `tqdm` to be installed. Please run `pip install tqdm`.")
|
| 35 |
+
if len(args) > 0 and isinstance(args[0], bool):
|
| 36 |
+
raise ValueError(
|
| 37 |
+
"Passing `True` or `False` as the first argument to Accelerate's `tqdm` wrapper is unsupported. "
|
| 38 |
+
"Please use the `main_process_only` keyword argument instead."
|
| 39 |
+
)
|
| 40 |
+
disable = kwargs.pop("disable", False)
|
| 41 |
+
if main_process_only and not disable:
|
| 42 |
+
disable = PartialState().local_process_index != 0
|
| 43 |
+
return _tqdm(*args, **kwargs, disable=disable)
|
venv/lib/python3.11/site-packages/accelerate/utils/transformer_engine.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from types import MethodType
|
| 16 |
+
|
| 17 |
+
import torch.nn as nn
|
| 18 |
+
|
| 19 |
+
from .imports import is_fp8_available, is_hpu_available
|
| 20 |
+
from .operations import GatheredParameters
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
# Do not import `transformer_engine` at package level to avoid potential issues
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def convert_model(model, to_transformer_engine=True, _convert_linear=True, _convert_ln=True):
|
| 27 |
+
"""
|
| 28 |
+
Recursively converts the linear and layernorm layers of a model to their `transformers_engine` counterpart.
|
| 29 |
+
"""
|
| 30 |
+
if not is_fp8_available():
|
| 31 |
+
raise ImportError("Using `convert_model` requires transformer_engine to be installed.")
|
| 32 |
+
|
| 33 |
+
if is_hpu_available():
|
| 34 |
+
import intel_transformer_engine as te
|
| 35 |
+
else:
|
| 36 |
+
import transformer_engine.pytorch as te
|
| 37 |
+
|
| 38 |
+
for name, module in model.named_children():
|
| 39 |
+
if isinstance(module, nn.Linear) and to_transformer_engine and _convert_linear:
|
| 40 |
+
has_bias = module.bias is not None
|
| 41 |
+
params_to_gather = [module.weight]
|
| 42 |
+
if has_bias:
|
| 43 |
+
params_to_gather.append(module.bias)
|
| 44 |
+
|
| 45 |
+
with GatheredParameters(params_to_gather, modifier_rank=0):
|
| 46 |
+
if any(p % 16 != 0 for p in module.weight.shape):
|
| 47 |
+
return
|
| 48 |
+
te_module = te.Linear(
|
| 49 |
+
module.in_features, module.out_features, bias=has_bias, params_dtype=module.weight.dtype
|
| 50 |
+
)
|
| 51 |
+
te_module.weight.copy_(module.weight)
|
| 52 |
+
if has_bias:
|
| 53 |
+
te_module.bias.copy_(module.bias)
|
| 54 |
+
|
| 55 |
+
setattr(model, name, te_module)
|
| 56 |
+
# Note: @xrsrke (Phuc) found that te.LayerNorm doesn't have any real memory savings or speedups over nn.LayerNorm
|
| 57 |
+
elif isinstance(module, nn.LayerNorm) and to_transformer_engine and _convert_ln:
|
| 58 |
+
with GatheredParameters([module.weight, module.bias], modifier_rank=0):
|
| 59 |
+
te_module = te.LayerNorm(module.normalized_shape[0], eps=module.eps, params_dtype=module.weight.dtype)
|
| 60 |
+
te_module.weight.copy_(module.weight)
|
| 61 |
+
te_module.bias.copy_(module.bias)
|
| 62 |
+
|
| 63 |
+
setattr(model, name, te_module)
|
| 64 |
+
elif isinstance(module, te.Linear) and not to_transformer_engine and _convert_linear:
|
| 65 |
+
has_bias = module.bias is not None
|
| 66 |
+
new_module = nn.Linear(
|
| 67 |
+
module.in_features, module.out_features, bias=has_bias, params_dtype=module.weight.dtype
|
| 68 |
+
)
|
| 69 |
+
new_module.weight.copy_(module.weight)
|
| 70 |
+
if has_bias:
|
| 71 |
+
new_module.bias.copy_(module.bias)
|
| 72 |
+
|
| 73 |
+
setattr(model, name, new_module)
|
| 74 |
+
elif isinstance(module, te.LayerNorm) and not to_transformer_engine and _convert_ln:
|
| 75 |
+
new_module = nn.LayerNorm(module.normalized_shape[0], eps=module.eps, params_dtype=module.weight.dtype)
|
| 76 |
+
new_module.weight.copy_(module.weight)
|
| 77 |
+
new_module.bias.copy_(module.bias)
|
| 78 |
+
|
| 79 |
+
setattr(model, name, new_module)
|
| 80 |
+
else:
|
| 81 |
+
convert_model(
|
| 82 |
+
module,
|
| 83 |
+
to_transformer_engine=to_transformer_engine,
|
| 84 |
+
_convert_linear=_convert_linear,
|
| 85 |
+
_convert_ln=_convert_ln,
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def has_transformer_engine_layers(model):
|
| 90 |
+
"""
|
| 91 |
+
Returns whether a given model has some `transformer_engine` layer or not.
|
| 92 |
+
"""
|
| 93 |
+
if not is_fp8_available():
|
| 94 |
+
raise ImportError("Using `has_transformer_engine_layers` requires transformer_engine to be installed.")
|
| 95 |
+
|
| 96 |
+
if is_hpu_available():
|
| 97 |
+
import intel_transformer_engine as te
|
| 98 |
+
|
| 99 |
+
module_cls_to_check = te.Linear
|
| 100 |
+
else:
|
| 101 |
+
import transformer_engine.pytorch as te
|
| 102 |
+
|
| 103 |
+
module_cls_to_check = (te.LayerNorm, te.Linear, te.TransformerLayer)
|
| 104 |
+
|
| 105 |
+
for m in model.modules():
|
| 106 |
+
if isinstance(m, module_cls_to_check):
|
| 107 |
+
return True
|
| 108 |
+
|
| 109 |
+
return False
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def contextual_fp8_autocast(model_forward, fp8_recipe, use_during_eval=False):
|
| 113 |
+
"""
|
| 114 |
+
Wrapper for a model's forward method to apply FP8 autocast. Is context aware, meaning that by default it will
|
| 115 |
+
disable FP8 autocast during eval mode, which is generally better for more accurate metrics.
|
| 116 |
+
"""
|
| 117 |
+
if not is_fp8_available():
|
| 118 |
+
raise ImportError("Using `contextual_fp8_autocast` requires transformer_engine to be installed.")
|
| 119 |
+
|
| 120 |
+
if is_hpu_available():
|
| 121 |
+
from intel_transformer_engine import fp8_autocast
|
| 122 |
+
else:
|
| 123 |
+
from transformer_engine.pytorch import fp8_autocast
|
| 124 |
+
|
| 125 |
+
def forward(self, *args, **kwargs):
|
| 126 |
+
enabled = use_during_eval or self.training
|
| 127 |
+
with fp8_autocast(enabled=enabled, fp8_recipe=fp8_recipe):
|
| 128 |
+
return model_forward(*args, **kwargs)
|
| 129 |
+
|
| 130 |
+
# To act like a decorator so that it can be popped when doing `extract_model_from_parallel`
|
| 131 |
+
forward.__wrapped__ = model_forward
|
| 132 |
+
|
| 133 |
+
return forward
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def apply_fp8_autowrap(model, fp8_recipe_handler):
|
| 137 |
+
"""
|
| 138 |
+
Applies FP8 context manager to the model's forward method
|
| 139 |
+
"""
|
| 140 |
+
if not is_fp8_available():
|
| 141 |
+
raise ImportError("Using `apply_fp8_autowrap` requires transformer_engine to be installed.")
|
| 142 |
+
|
| 143 |
+
if is_hpu_available():
|
| 144 |
+
import intel_transformer_engine.recipe as te_recipe
|
| 145 |
+
else:
|
| 146 |
+
import transformer_engine.common.recipe as te_recipe
|
| 147 |
+
|
| 148 |
+
kwargs = fp8_recipe_handler.to_kwargs() if fp8_recipe_handler is not None else {}
|
| 149 |
+
if "fp8_format" in kwargs:
|
| 150 |
+
kwargs["fp8_format"] = getattr(te_recipe.Format, kwargs["fp8_format"])
|
| 151 |
+
use_during_eval = kwargs.pop("use_autocast_during_eval", False)
|
| 152 |
+
fp8_recipe = te_recipe.DelayedScaling(**kwargs)
|
| 153 |
+
new_forward = contextual_fp8_autocast(model.forward, fp8_recipe, use_during_eval)
|
| 154 |
+
|
| 155 |
+
if hasattr(model.forward, "__func__"):
|
| 156 |
+
model.forward = MethodType(new_forward, model)
|
| 157 |
+
else:
|
| 158 |
+
model.forward = new_forward
|
| 159 |
+
|
| 160 |
+
return model
|
venv/lib/python3.11/site-packages/accelerate/utils/versions.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import importlib.metadata
|
| 16 |
+
from typing import Union
|
| 17 |
+
|
| 18 |
+
from packaging.version import Version, parse
|
| 19 |
+
|
| 20 |
+
from .constants import STR_OPERATION_TO_FUNC
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
torch_version = parse(importlib.metadata.version("torch"))
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def compare_versions(library_or_version: Union[str, Version], operation: str, requirement_version: str):
|
| 27 |
+
"""
|
| 28 |
+
Compares a library version to some requirement using a given operation.
|
| 29 |
+
|
| 30 |
+
Args:
|
| 31 |
+
library_or_version (`str` or `packaging.version.Version`):
|
| 32 |
+
A library name or a version to check.
|
| 33 |
+
operation (`str`):
|
| 34 |
+
A string representation of an operator, such as `">"` or `"<="`.
|
| 35 |
+
requirement_version (`str`):
|
| 36 |
+
The version to compare the library version against
|
| 37 |
+
"""
|
| 38 |
+
if operation not in STR_OPERATION_TO_FUNC.keys():
|
| 39 |
+
raise ValueError(f"`operation` must be one of {list(STR_OPERATION_TO_FUNC.keys())}, received {operation}")
|
| 40 |
+
operation = STR_OPERATION_TO_FUNC[operation]
|
| 41 |
+
if isinstance(library_or_version, str):
|
| 42 |
+
library_or_version = parse(importlib.metadata.version(library_or_version))
|
| 43 |
+
return operation(library_or_version, parse(requirement_version))
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def is_torch_version(operation: str, version: str):
|
| 47 |
+
"""
|
| 48 |
+
Compares the current PyTorch version to a given reference with an operation.
|
| 49 |
+
|
| 50 |
+
Args:
|
| 51 |
+
operation (`str`):
|
| 52 |
+
A string representation of an operator, such as `">"` or `"<="`
|
| 53 |
+
version (`str`):
|
| 54 |
+
A string version of PyTorch
|
| 55 |
+
"""
|
| 56 |
+
return compare_versions(torch_version, operation, version)
|
venv/lib/python3.11/site-packages/ada92cb5d92a588d1b93__mypyc.cpython-311-x86_64-linux-gnu.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bff8964f346c6579f65b70f06199dab85af861fd00f30f31b3ff23ee67d36c5c
|
| 3 |
+
size 445952
|
venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/METADATA
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: annotated-doc
|
| 3 |
+
Version: 0.0.4
|
| 4 |
+
Summary: Document parameters, class attributes, return types, and variables inline, with Annotated.
|
| 5 |
+
Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiangolo@gmail.com>
|
| 6 |
+
License-Expression: MIT
|
| 7 |
+
License-File: LICENSE
|
| 8 |
+
Classifier: Intended Audience :: Information Technology
|
| 9 |
+
Classifier: Intended Audience :: System Administrators
|
| 10 |
+
Classifier: Operating System :: OS Independent
|
| 11 |
+
Classifier: Programming Language :: Python :: 3
|
| 12 |
+
Classifier: Programming Language :: Python
|
| 13 |
+
Classifier: Topic :: Internet
|
| 14 |
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
| 15 |
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
| 16 |
+
Classifier: Topic :: Software Development :: Libraries
|
| 17 |
+
Classifier: Topic :: Software Development
|
| 18 |
+
Classifier: Typing :: Typed
|
| 19 |
+
Classifier: Development Status :: 4 - Beta
|
| 20 |
+
Classifier: Intended Audience :: Developers
|
| 21 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 23 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 24 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 25 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 26 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 27 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 28 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 29 |
+
Project-URL: Homepage, https://github.com/fastapi/annotated-doc
|
| 30 |
+
Project-URL: Documentation, https://github.com/fastapi/annotated-doc
|
| 31 |
+
Project-URL: Repository, https://github.com/fastapi/annotated-doc
|
| 32 |
+
Project-URL: Issues, https://github.com/fastapi/annotated-doc/issues
|
| 33 |
+
Project-URL: Changelog, https://github.com/fastapi/annotated-doc/release-notes.md
|
| 34 |
+
Requires-Python: >=3.8
|
| 35 |
+
Description-Content-Type: text/markdown
|
| 36 |
+
|
| 37 |
+
# Annotated Doc
|
| 38 |
+
|
| 39 |
+
Document parameters, class attributes, return types, and variables inline, with `Annotated`.
|
| 40 |
+
|
| 41 |
+
<a href="https://github.com/fastapi/annotated-doc/actions?query=workflow%3ATest+event%3Apush+branch%3Amain" target="_blank">
|
| 42 |
+
<img src="https://github.com/fastapi/annotated-doc/actions/workflows/test.yml/badge.svg?event=push&branch=main" alt="Test">
|
| 43 |
+
</a>
|
| 44 |
+
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/annotated-doc" target="_blank">
|
| 45 |
+
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/annotated-doc.svg" alt="Coverage">
|
| 46 |
+
</a>
|
| 47 |
+
<a href="https://pypi.org/project/annotated-doc" target="_blank">
|
| 48 |
+
<img src="https://img.shields.io/pypi/v/annotated-doc?color=%2334D058&label=pypi%20package" alt="Package version">
|
| 49 |
+
</a>
|
| 50 |
+
<a href="https://pypi.org/project/annotated-doc" target="_blank">
|
| 51 |
+
<img src="https://img.shields.io/pypi/pyversions/annotated-doc.svg?color=%2334D058" alt="Supported Python versions">
|
| 52 |
+
</a>
|
| 53 |
+
|
| 54 |
+
## Installation
|
| 55 |
+
|
| 56 |
+
```bash
|
| 57 |
+
pip install annotated-doc
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
Or with `uv`:
|
| 61 |
+
|
| 62 |
+
```Python
|
| 63 |
+
uv add annotated-doc
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
## Usage
|
| 67 |
+
|
| 68 |
+
Import `Doc` and pass a single literal string with the documentation for the specific parameter, class attribute, return type, or variable.
|
| 69 |
+
|
| 70 |
+
For example, to document a parameter `name` in a function `hi` you could do:
|
| 71 |
+
|
| 72 |
+
```Python
|
| 73 |
+
from typing import Annotated
|
| 74 |
+
|
| 75 |
+
from annotated_doc import Doc
|
| 76 |
+
|
| 77 |
+
def hi(name: Annotated[str, Doc("Who to say hi to")]) -> None:
|
| 78 |
+
print(f"Hi, {name}!")
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
You can also use it to document class attributes:
|
| 82 |
+
|
| 83 |
+
```Python
|
| 84 |
+
from typing import Annotated
|
| 85 |
+
|
| 86 |
+
from annotated_doc import Doc
|
| 87 |
+
|
| 88 |
+
class User:
|
| 89 |
+
name: Annotated[str, Doc("The user's name")]
|
| 90 |
+
age: Annotated[int, Doc("The user's age")]
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
The same way, you could document return types and variables, or anything that could have a type annotation with `Annotated`.
|
| 94 |
+
|
| 95 |
+
## Who Uses This
|
| 96 |
+
|
| 97 |
+
`annotated-doc` was made for:
|
| 98 |
+
|
| 99 |
+
* [FastAPI](https://fastapi.tiangolo.com/)
|
| 100 |
+
* [Typer](https://typer.tiangolo.com/)
|
| 101 |
+
* [SQLModel](https://sqlmodel.tiangolo.com/)
|
| 102 |
+
* [Asyncer](https://asyncer.tiangolo.com/)
|
| 103 |
+
|
| 104 |
+
`annotated-doc` is supported by [griffe-typingdoc](https://github.com/mkdocstrings/griffe-typingdoc), which powers reference documentation like the one in the [FastAPI Reference](https://fastapi.tiangolo.com/reference/).
|
| 105 |
+
|
| 106 |
+
## Reasons not to use `annotated-doc`
|
| 107 |
+
|
| 108 |
+
You are already comfortable with one of the existing docstring formats, like:
|
| 109 |
+
|
| 110 |
+
* Sphinx
|
| 111 |
+
* numpydoc
|
| 112 |
+
* Google
|
| 113 |
+
* Keras
|
| 114 |
+
|
| 115 |
+
Your team is already comfortable using them.
|
| 116 |
+
|
| 117 |
+
You prefer having the documentation about parameters all together in a docstring, separated from the code defining them.
|
| 118 |
+
|
| 119 |
+
You care about a specific set of users, using one specific editor, and that editor already has support for the specific docstring format you use.
|
| 120 |
+
|
| 121 |
+
## Reasons to use `annotated-doc`
|
| 122 |
+
|
| 123 |
+
* No micro-syntax to learn for newcomers, it’s **just Python** syntax.
|
| 124 |
+
* **Editing** would be already fully supported by default by any editor (current or future) supporting Python syntax, including syntax errors, syntax highlighting, etc.
|
| 125 |
+
* **Rendering** would be relatively straightforward to implement by static tools (tools that don't need runtime execution), as the information can be extracted from the AST they normally already create.
|
| 126 |
+
* **Deduplication of information**: the name of a parameter would be defined in a single place, not duplicated inside of a docstring.
|
| 127 |
+
* **Elimination** of the possibility of having **inconsistencies** when removing a parameter or class variable and **forgetting to remove** its documentation.
|
| 128 |
+
* **Minimization** of the probability of adding a new parameter or class variable and **forgetting to add its documentation**.
|
| 129 |
+
* **Elimination** of the possibility of having **inconsistencies** between the **name** of a parameter in the **signature** and the name in the docstring when it is renamed.
|
| 130 |
+
* **Access** to the documentation string for each symbol at **runtime**, including existing (older) Python versions.
|
| 131 |
+
* A more formalized way to document other symbols, like type aliases, that could use Annotated.
|
| 132 |
+
* **Support** for apps using FastAPI, Typer and others.
|
| 133 |
+
* **AI Accessibility**: AI tools will have an easier way understanding each parameter as the distance from documentation to parameter is much closer.
|
| 134 |
+
|
| 135 |
+
## History
|
| 136 |
+
|
| 137 |
+
I ([@tiangolo](https://github.com/tiangolo)) originally wanted for this to be part of the Python standard library (in [PEP 727](https://peps.python.org/pep-0727/)), but the proposal was withdrawn as there was a fair amount of negative feedback and opposition.
|
| 138 |
+
|
| 139 |
+
The conclusion was that this was better done as an external effort, in a third-party library.
|
| 140 |
+
|
| 141 |
+
So, here it is, with a simpler approach, as a third-party library, in a way that can be used by others, starting with FastAPI and friends.
|
| 142 |
+
|
| 143 |
+
## License
|
| 144 |
+
|
| 145 |
+
This project is licensed under the terms of the MIT license.
|
venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/RECORD
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
annotated_doc-0.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 2 |
+
annotated_doc-0.0.4.dist-info/METADATA,sha256=Irm5KJua33dY2qKKAjJ-OhKaVBVIfwFGej_dSe3Z1TU,6566
|
| 3 |
+
annotated_doc-0.0.4.dist-info/RECORD,,
|
| 4 |
+
annotated_doc-0.0.4.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
|
| 5 |
+
annotated_doc-0.0.4.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
| 6 |
+
annotated_doc-0.0.4.dist-info/licenses/LICENSE,sha256=__Fwd5pqy_ZavbQFwIfxzuF4ZpHkqWpANFF-SlBKDN8,1086
|
| 7 |
+
annotated_doc/__init__.py,sha256=VuyxxUe80kfEyWnOrCx_Bk8hybo3aKo6RYBlkBBYW8k,52
|
| 8 |
+
annotated_doc/__pycache__/__init__.cpython-311.pyc,,
|
| 9 |
+
annotated_doc/__pycache__/main.cpython-311.pyc,,
|
| 10 |
+
annotated_doc/main.py,sha256=5Zfvxv80SwwLqpRW73AZyZyiM4bWma9QWRbp_cgD20s,1075
|
| 11 |
+
annotated_doc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: pdm-backend (2.4.5)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
|
| 3 |
+
[gui_scripts]
|
| 4 |
+
|
venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The MIT License (MIT)
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2025 Sebastián Ramírez
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in
|
| 13 |
+
all copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 21 |
+
THE SOFTWARE.
|
venv/lib/python3.11/site-packages/annotated_doc/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .main import Doc as Doc
|
| 2 |
+
|
| 3 |
+
__version__ = "0.0.4"
|
venv/lib/python3.11/site-packages/annotated_doc/main.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class Doc:
|
| 2 |
+
"""Define the documentation of a type annotation using `Annotated`, to be
|
| 3 |
+
used in class attributes, function and method parameters, return values,
|
| 4 |
+
and variables.
|
| 5 |
+
|
| 6 |
+
The value should be a positional-only string literal to allow static tools
|
| 7 |
+
like editors and documentation generators to use it.
|
| 8 |
+
|
| 9 |
+
This complements docstrings.
|
| 10 |
+
|
| 11 |
+
The string value passed is available in the attribute `documentation`.
|
| 12 |
+
|
| 13 |
+
Example:
|
| 14 |
+
|
| 15 |
+
```Python
|
| 16 |
+
from typing import Annotated
|
| 17 |
+
from annotated_doc import Doc
|
| 18 |
+
|
| 19 |
+
def hi(name: Annotated[str, Doc("Who to say hi to")]) -> None:
|
| 20 |
+
print(f"Hi, {name}!")
|
| 21 |
+
```
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
def __init__(self, documentation: str, /) -> None:
|
| 25 |
+
self.documentation = documentation
|
| 26 |
+
|
| 27 |
+
def __repr__(self) -> str:
|
| 28 |
+
return f"Doc({self.documentation!r})"
|
| 29 |
+
|
| 30 |
+
def __hash__(self) -> int:
|
| 31 |
+
return hash(self.documentation)
|
| 32 |
+
|
| 33 |
+
def __eq__(self, other: object) -> bool:
|
| 34 |
+
if not isinstance(other, Doc):
|
| 35 |
+
return NotImplemented
|
| 36 |
+
return self.documentation == other.documentation
|