nanaj commited on
Commit
ea35292
·
verified ·
1 Parent(s): aa69dc8

Upload folder using huggingface_hub (part 4)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +92 -0
  2. venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/INSTALLER +1 -0
  3. venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/LICENSE +201 -0
  4. venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/METADATA +380 -0
  5. venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/RECORD +95 -0
  6. venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/REQUESTED +0 -0
  7. venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/WHEEL +5 -0
  8. venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/entry_points.txt +6 -0
  9. venv/lib/python3.11/site-packages/accelerate-1.6.0.dist-info/top_level.txt +1 -0
  10. venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_ddp_comm_hook.py +85 -0
  11. venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_distributed_data_loop.py +410 -0
  12. venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_merge_weights.py +162 -0
  13. venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_notebook.py +118 -0
  14. venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_ops.py +181 -0
  15. venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_script.py +901 -0
  16. venv/lib/python3.11/site-packages/accelerate/test_utils/scripts/test_sync.py +410 -0
  17. venv/lib/python3.11/site-packages/accelerate/test_utils/testing.py +814 -0
  18. venv/lib/python3.11/site-packages/accelerate/test_utils/training.py +162 -0
  19. venv/lib/python3.11/site-packages/accelerate/tracking.py +1089 -0
  20. venv/lib/python3.11/site-packages/accelerate/utils/__init__.py +291 -0
  21. venv/lib/python3.11/site-packages/accelerate/utils/ao.py +139 -0
  22. venv/lib/python3.11/site-packages/accelerate/utils/bnb.py +470 -0
  23. venv/lib/python3.11/site-packages/accelerate/utils/constants.py +92 -0
  24. venv/lib/python3.11/site-packages/accelerate/utils/dataclasses.py +0 -0
  25. venv/lib/python3.11/site-packages/accelerate/utils/deepspeed.py +371 -0
  26. venv/lib/python3.11/site-packages/accelerate/utils/environment.py +421 -0
  27. venv/lib/python3.11/site-packages/accelerate/utils/fsdp_utils.py +629 -0
  28. venv/lib/python3.11/site-packages/accelerate/utils/imports.py +546 -0
  29. venv/lib/python3.11/site-packages/accelerate/utils/launch.py +709 -0
  30. venv/lib/python3.11/site-packages/accelerate/utils/megatron_lm.py +1424 -0
  31. venv/lib/python3.11/site-packages/accelerate/utils/memory.py +199 -0
  32. venv/lib/python3.11/site-packages/accelerate/utils/modeling.py +2140 -0
  33. venv/lib/python3.11/site-packages/accelerate/utils/offload.py +213 -0
  34. venv/lib/python3.11/site-packages/accelerate/utils/operations.py +862 -0
  35. venv/lib/python3.11/site-packages/accelerate/utils/other.py +373 -0
  36. venv/lib/python3.11/site-packages/accelerate/utils/random.py +156 -0
  37. venv/lib/python3.11/site-packages/accelerate/utils/rich.py +24 -0
  38. venv/lib/python3.11/site-packages/accelerate/utils/torch_xla.py +51 -0
  39. venv/lib/python3.11/site-packages/accelerate/utils/tqdm.py +43 -0
  40. venv/lib/python3.11/site-packages/accelerate/utils/transformer_engine.py +160 -0
  41. venv/lib/python3.11/site-packages/accelerate/utils/versions.py +56 -0
  42. venv/lib/python3.11/site-packages/ada92cb5d92a588d1b93__mypyc.cpython-311-x86_64-linux-gnu.so +3 -0
  43. venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/INSTALLER +1 -0
  44. venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/METADATA +145 -0
  45. venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/RECORD +11 -0
  46. venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/WHEEL +4 -0
  47. venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/entry_points.txt +4 -0
  48. venv/lib/python3.11/site-packages/annotated_doc-0.0.4.dist-info/licenses/LICENSE +21 -0
  49. venv/lib/python3.11/site-packages/annotated_doc/__init__.py +3 -0
  50. 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). [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](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