Former-commit-id: 7a3dccf2c738eaf535e62996211c8bc5f3de5683 Update README.md Former-commit-id: aee63a3a8422e029f1550e47825ca4beb916874b Update README.md Former-commit-id: fb46c80c1a2663c09849472e93b2da6cd068a98c Update README.md Former-commit-id: 5fd1e6afe1277ccffdfeaf507477d944a6a35f03 Update README.md Former-commit-id: 1cd7d8b5df34ab9f61d9346b2647b643f78a8f5d Update README.md Former-commit-id: e27cc5d49a19e9580ba1a29d948051b918aede74 Update README.md Former-commit-id: 0ff9314bddef1dfbbf429726ed4240b823b221b3 Update README.md Former-commit-id: f295f4623448dd4ffa9be747a5cc6733cf48c663 Create Contribution_Guidelines.md Former-commit-id: 897dbd63dc6f53095e1dda3f10a86dd40e4bd263 Update README.md Former-commit-id: c652acd1fc5a003b4d6eefcfb83dfb310f5b5e81 Rename data.md to Data.md Former-commit-id: 27484f7a2d8634bd1cd9781a2f10afa7f304f5cb Update README.md Former-commit-id: 351be764d8648ac60cf170c3fe4e6ed9c544c90e reformat code Former-commit-id: ce23b280fbb419c08347ebcbf1ed4075bdf9af60 Update requirements.txt As in previous step,the three packages have been installed, these packages should be deleted here because it can not be installed by " pip install -r requirements.txt" command and will abort the command Former-commit-id: 0655fa28cf8bb761f1a3d2def62f44197b257b2c Update README.md Former-commit-id: 1dd9c945be2a170d87d568bb00e1ec30e7be592b Update README.md Former-commit-id: 6ecaba8e03d7c7f99c068a57c923f11c5456a8c9 Update Contribution_Guidelines.md Former-commit-id: dc87e459ca5a8a090800331111118acb65214f65 Update README.md Former-commit-id: be7ecc3d56f7289052fc66d535038e7111f7ac84 Update README.md Former-commit-id: 164e76cf496b1157eebea299ed58ca6c1799d70d Update README.md Former-commit-id: 3e3d674134498512b1ef1b0dd72d6e19afd6ef65 [feat]: frame_interpolation Former-commit-id: 711fc4bba80d5f051f712e3460e2c0685117cf68 Update README.md Former-commit-id: 1436d8feea7f82d3d747c632a68c47b3ea74258f fix_readme_requirements Former-commit-id: eb5a5c69d12cc412c12a4fea7f00baafd02701ca Update README.md Former-commit-id: c5a79c7e993d9e6a77c6c70efa1c0ac2bb3a063b add latte Former-commit-id: 01ba10e5d65c9d2d957472fbe7958e1b07a971e9 Update README.md Former-commit-id: 437bc5c68b5c7a85865ad97d544c4818be45cb52 first commit Former-commit-id: ec3481bc35a8120d519082dc76585428f0896470 [refactor] reformat videogpt, support training videogpt on accelerate Former-commit-id: 75385ed47fe60008b61b1bcf89f61ea696d095bc [refactor] adapt to old training code Former-commit-id: 358d362ecbc100b21ea02895cb0426cf5b386a72 Delete LICENSE.txt Former-commit-id: b10d8b2c6620e3a8412527bd03ea0adc26c981a6 Create LICENSE Former-commit-id: 369fe085e91e55b9be6165da4a1eb7f9c0cf5a7f Update README.md Former-commit-id: f1aaeca19bc9e4304961c8c903f9bf03cb42fd9f Update README.md Former-commit-id: 0f2bdd2bcd819d528f2a41a96ba93f5c3c04dc8c Update README.md Former-commit-id: 10ee64c122f28efea39fc1a4542d278683a58ce1 option to use rebased linear attention Former-commit-id: f1b39b34df5fbdef14ef3f6a0e6d0972a8570445 support for ring attention Former-commit-id: 0533c73b99217e88da121bd0f68e0949bc88016e add vae and fix bug in dit Former-commit-id: 2dd4254c22ce671d5cd5e7d22a2386b7660fbdc4 Update README.md Former-commit-id: 898d35f15ce0fd124cd48078f0a8af2764bf55d1 support latte training Former-commit-id: d471a9414842f422d1200c2fd3f3ddfcf9e97fd6 Update README.md Former-commit-id: 9fd88eb8ac5122b0999ccb0071f195aaeb0979a0 Update README.md Former-commit-id: 398cfcd7b19cf1ba827eb49fc11223f72fa93a85 Update README.md Former-commit-id: 673659495b7503ba402fbcb6c46204499761b4cf [fix] fix a bug when using ucf101_stride4x4x4 Former-commit-id: 9911403c688321db731450d9383bc02923a4be2d [refactor] remove test.ipynb Former-commit-id: e73c3a9e0d41e51a0fd755e57739424213a76312 [fix] fix a bug when using ucf101_stride4x4x4 Former-commit-id: f541e723d65ea44ae4560c68908123c7e6781bbc [refactor] rename Former-commit-id: ec317605d24223527b8c2d4b4d2ee876f86abbaf update add RGT Former-commit-id: a491111e48cec588095381eb70ffca94600ca4f7 add sample script Former-commit-id: 9ce847651bb6dba28c51ce5807e222ae2902a78a [refactor] configuration default values Former-commit-id: d7ca7685d6a8c6d015082dda241bc98ab156770d [refactor] added more methods in abstract classes Former-commit-id: a535e3a06eb766bebe7baa3d9df64b04d04d40e6 Update README.md Former-commit-id: 59a595a1e31b156500a944f244afec40988e872d [fix]: disable gradient computation to save GPU memory when reconstructing a video Former-commit-id: b7bf763e7f8800959ed82bb89f41684db216dd8e support accelerate training Former-commit-id: b32671c9991b9a9db66cb148822ccb3e8be7dc11 Update train.sh Former-commit-id: 6c4957d715eaa489691be21dd0abdb9309660e0d Update train.sh Former-commit-id: 66aa3709f80d89a30d0a57c5e033ce1ae46f998f Update README.md Former-commit-id: 604a29d1de43e0fcb90ada4dd97f5f080e37caea Update README.md Former-commit-id: d097e40998a3396f74c2bfbcf26d6ace8080d0b6 Update README.md Former-commit-id: 1a7178a61547c33a744d032302fb76ee0a3c6671 denorm fun Former-commit-id: 05707a07b14295f5e1932d0c10bb84a20a0cd328 Update README.md Former-commit-id: b3f6bb7f252505efb0ffd8195003c79206fac313 refine readme. Former-commit-id: 391d9511b72aa9c09c7ad8992ae5df8c89c88c63 complement emoji. Former-commit-id: 50679cc482d8ecf4e3083c0f5a791f195a58a855 Update README.md Former-commit-id: 29f24a819fe2d4593c48afe6ca990659784bd776 [feat]: add sit model Former-commit-id: 2eda854d6c25254b71d0105a3ed092953a9d6f68 fix: remove replicated diffusion module in sit Former-commit-id: 997cb049185421d303b14d59ab6c0603f1d1a66f fix: rename sit model filename Former-commit-id: f3b2cd7dcf644f680616eee0571db90478fede23 feat: Incorporating SiT sample Former-commit-id: 91e2931e412f8c96ef748cb2322ee956309fb6aa feat: update sample Former-commit-id: e7dc492b9b4764db28aa31cc1eede914052b4ca6 attn_mask with bf16 Former-commit-id: d9667a8c15a5344e3c777197fd6badec15dcddbd Update README.md Former-commit-id: a1cc0b26a3b156d7708e8df0cfe42aad33b1c946 Update README.md Former-commit-id: 4179ed075e68d48094655c38dbde784836fd6e0f Update README.md Former-commit-id: 36e69275598b0cff38fdc5006926e422cda069b3 [fix]: disable gradient computation to save GPU memory when reconstructing a video Former-commit-id: 06da17790b53cf593a0c264dda17c76f68d2a541 Update README.md Former-commit-id: 8e9658ed993380abc43b518fcbac21a02d8ba9ba Update README.md Former-commit-id: 8b553a4477c64b5452f8424ca6987c0fb8c111ce Update README.md Former-commit-id: 0898e4b917f5e245c4394118511bcb7639973817 Update README.md Former-commit-id: 4a893ff6177d59219b6755c4a89764ef54fc7f62 Update README.md Former-commit-id: 89f9fc8eed3ca8cc2434ec227acbb845bfbcff82 [refactor] rename train_videogpt.sh Former-commit-id: e2ea2caacaf5be046f48cf50a53ccafa7a3cc351 [docs] Modify README.md and add VQVAE documentation. Former-commit-id: a5bfe70f8a8998db969d9c4938fda123aa153bc0 fixed dit forward Former-commit-id: 4cd537fab12231f7471107762b02b36217ca42d8 videogpt inference Former-commit-id: c5d617450a8271ad3b8a5e8f76ee2d77a822e31b Update README.md Former-commit-id: 4d83f1feab7d6872aca1c6b41be95950ec3cf465 Update README.md Former-commit-id: da5507e570a27becab754ba24a0671d669b7e08e Update README.md Former-commit-id: 4a04e71636dabccbef446623674f3a514395b7f9 Update README.md Former-commit-id: 2a286e5aab48aa68e7d3f04df23ec1cdfe71c6de Update README.md Former-commit-id: f4c4b3b4b1ce3c1d0b56ba5a109cd6cc57da900d Update README.md Former-commit-id: 7af6b82b29c265e53e9c48477083b4cff0e0a3d3 Update README.md Former-commit-id: 7bc544af55c0a987d0df426848cb06cb330fd52b Update README.md Former-commit-id: 2a68a1aa399a53f58246df4f9f64dfdcdd130bcf [docs] Modify README.md to fix translation error in name "CloseGPT" Former-commit-id: dd36043df4b0408e592a82f9046f6d654a5375c3 [docs] Modify README.md to fix other grammatical errors Former-commit-id: f0de27fcc38bb4dab0eeafac225a2924080a3218 Update README.md Former-commit-id: a881ff5198e1125032418f4f02fd95d2179ffb3a Update README.md Former-commit-id: 8a191abc1903ad5998fe83149c9c9ef5c8226b9e Update README.md Former-commit-id: 3b4eb14dcd8862b8251b72f30cba523a5dc1def8 Update README.md Former-commit-id: e458366e47d4b13737a71bbf866e20ec10961ade init pos embed Former-commit-id: 28f8076136ec214c66039ead7547c9b25cfe7bfc Update README.md Former-commit-id: a1d1f07c83eb0c03d93b023b5c9162a6a3c26cd9 Update README.md Former-commit-id: deddba1f03bf697fb1252a0ce4fdb5df5bcae8de Update README.md Former-commit-id: 076e6e70e717888f24265d624c6a7a8ffa2f5d1b Update README.md Former-commit-id: 7cd398721e556db6199f1d06c7a0d1de2a9720dd [updata] updata video super resolution updata video super resolution Former-commit-id: 82397545859791d112d6f0569e33fe23437563df train script Former-commit-id: 3fd1ccf099c2deed20af50bf9556e2fc3b7a4382 Delete opensora/models/super_resolution/placeholder Former-commit-id: 8dd5a6a3c85dc28989ff95e6ad41be7fdb0f0811 Update Data.md Former-commit-id: 9531e33666f842125b331cb266eb26a3a03a4009 Update Data.md Former-commit-id: 86be6b2bf5a9be1030bb3af661a2ec85e710a5c3 commit Former-commit-id: 81359cf67248b6c594ff79f121a8a38558c1cc70 Update README.md Former-commit-id: c4d7aafa3ceb6e4d48bf85b402ae2b2ce9394e53 Update README.md Former-commit-id: bca7070fac79d5ae5c37830cd55ca1e81a289197 Update README.md Former-commit-id: b37da2202e730a4930a194a80a703b591a535adf Update __init__.py Former-commit-id: 03a45aaf60d294b7a68d8fc44132d3d671e4472b Update README.md Former-commit-id: 97fe914aaa2b9bd7c2ff5d4239381631c47e8356 Update README.md Former-commit-id: c6b1ea8f1ef01096aa8c3e5272bc9fb255b5532b Delete scripts/train_vqvae.sh Former-commit-id: 952a4ce3a3b0da159d599ff3a0a052265f48c2dd update script Former-commit-id: 43f539b68af150c45040d79918a84de1dce47c33 feature dataset Former-commit-id: 9683caae8648735552270b871de22fc540ba2b55 update train.py Former-commit-id: 844173711342f2dcc021294e87971ca39854bfe4 del use_fp16 Former-commit-id: 5424c0112e04b56d1df30ac76cbc104e848406a3 update sample.py Former-commit-id: 10d8621051d1510270c6a55abe686eac86297170 fix extract feature Former-commit-id: d4dce5ec1aaa058e452a41f15cd35fe1713f0b78 Update README.md Former-commit-id: 70f6ebf421f0830a51dcc2838c2c3783cfb67376 [Updata] updata the video super resolution updata the run.py and add README.md Former-commit-id: 1e6d6ace7a0963149e228813801210b022aceacf add CI for docker autobuild Former-commit-id: c4e2d5ff1dce8662a42f1ed6a7786855d40173f7 [refactor]: add docker development support Former-commit-id: fded3baff7915c0cfc9cec3765c271259359831c update docker scripts and configs Former-commit-id: 4567e6368f1bf413d8f6319a97a852b8289ca770 Update README.md Former-commit-id: 99b0066895af072a8887737f2b603ec7fc893ccd extract script Former-commit-id: 4139f2f68ae09695302ce49afdb7318df83cabfd [docs]:update the eval_code for calculate the FVD, clip_score, ssim, lpips, psnr Former-commit-id: 1ee226c338126fbdc5178020b12271b980a7ce0c [docs]:update the eval_code for calculate the FVD, clip_score, ssim, lpips, psnr Former-commit-id: b504623d7ec2ac0b508530d21ffc88978f8a4f71 [docs]: update the evaluation Former-commit-id: 02e3789b74daa036fbbff6d13dfc7e98372260ed Update latte.py revise the xformers inputs Former-commit-id: 60db66c912192c3fbafe55ebe16157514b884e5b Fix the memory error bug when loading metadata pickle file of vqvae dataset. Former-commit-id: 97ab243bd12915cea7aa2deecfac4b99417f78a3 Update README.md Former-commit-id: 8f7b41118a3f491d1d5b5789c53790c46b023191 Update pyproject.toml Former-commit-id: 36da5d0e950625e629aebe954f89f92827397d0d Update README.md Former-commit-id: 826cc5e9162888bed82413088c6be662c278176b Update README.md Former-commit-id: 88e9f21cfc8ec775dbc9a65f740043e91e26bd91 Update README.md Former-commit-id: 38e0e290e7a5909706c2cce2e4dd047647134fb8 [docs]:update the eval code Former-commit-id: 937f2fc87d34ab9d4724cb95a6457b54a6bf4ebc [docs]:update the eval code Former-commit-id: a8c9e6cb9e39a9e929dab34077280a8d80d85573 flash-attn train and sample Former-commit-id: e336565bd5ff2aaf4d0b4ad5f42732e89cd216b3 [refactor] add static type checking Former-commit-id: 12c4240dc28888c61f1f79243627e7cdb445c840 Update README.md Former-commit-id: 513883df1ad853c9a5e7457c364227261df17a83 Fix einops bug: module 'keras.backend' has no attribute 'is_tensor' Former-commit-id: 849f8ed815295bac78c3c10d319e6731aa26ca02 [feat] Add 2D RoPE Former-commit-id: ccae80d339e34fb23706827565b5095ae2c2f320 Update README.md Former-commit-id: 6630c22baa2340a8b55badd61472daab7177bc63 xformers and flashattn Former-commit-id: 002cad95253f7da36344237ce8c8410ffb517711 safe dit Former-commit-id: f407cab7dc0e53257972d430e873f4fd77bb039d [fix] 2d RoPE init Former-commit-id: ff5931bae6efa94675b9ef6bc25f1b76217d128e fixed attn_mask in flash-attn Former-commit-id: 5e2504f429a342b75bc7a9004cee7b3f75582ee3 typo Former-commit-id: 9e87366e906ed19726c8905afb27b5df4d4e1512 Decouple mixed_precision and gradient_accumulation to command args. Former-commit-id: 903d473d1432eb492a87efe184d03439f643b92d Fix mixed_precision bugs of latte. Former-commit-id: d70a551972e04d95444d46cdbe2bfb16ad099fcb Refactor latte. Former-commit-id: 304534269f105578097bc16ab497636ad59fd9b2 Refactor latte. Former-commit-id: 7408fd0ee16247afc3d4ba43b8c8e7dfb1091a97 Support deepspeed zero2 and zero2_offload for latte training. Former-commit-id: c87e95dd357e6fa6d6fb2dcfee781bc49c039e13 Add some comments. Former-commit-id: d5b5bd7743edde6a63dc74457bded0f8c56be1c2 Fix typos. Former-commit-id: c10330d373466f3c1ab455bfb391569b9175bc3a deepspeed Former-commit-id: 6179a4b0e7d06caed4201a27679abc64615febe6 Update README.md Former-commit-id: 582070d4f97c4a4398dcb9a6699f4ab6e980c812 Add base config. Former-commit-id: d37c551a0421d6b73dee26060b517c46d8217e4f train 1080p video Former-commit-id: 74625142ef892f9e7fa459b2f98f2f7ae5b36d6a Update README.md Former-commit-id: 87636c1dc2275c4da77c6368a0f4425bea15dc12 Update README.md Former-commit-id: 059d9e863958cfd679dbb8d365de6cd933e284fc Update pyproject.toml Former-commit-id: 5c379b6e4515ad75b89cdebbc5cd490c9a821d78 Update README.md Former-commit-id: 7a62dec060c91b02ca556451266e6f3ef28e03e7 clean script Former-commit-id: 02c61f63a8539a31516b92f5bb7c2d2e428ca7d7 Update README.md Former-commit-id: 6d41cfe906f97dbed1cea8c9ed06a1f6dc84308e Update README.md Former-commit-id: 64d147863368aeef479ad25f2e5463fdcb76de46 Update README.md Former-commit-id: 2808edf11005e04aa730971a786d1a4e3bca88b5 Update README.md Former-commit-id: 1f94f2721f35670aeae8dbfb7c0a5abd06cd2aee [feat]: Caption Refiner Caption Refiner for Video Caption Former-commit-id: 97c16bccb9979acf19fb91ae8d7b84ac4a3b4d97 [BUG]: Shorten the refined caption Shorten the refined caption using GPT-3.5 summary Former-commit-id: bb159f0d5fa928258475b2058983397ceb91af9d Fix resolution typos. Former-commit-id: 3ad55d28eaf13f9c8fd23912d2691965737527e9 clean dit Former-commit-id: 117ec4196a6f2cd83da223b993b14e28f7a693f8 Update README.md Former-commit-id: abb8ed57a29b1f66adec66d854429f36f9199540 Update README.md Former-commit-id: c2c64665d4f281e264d87c20cc1cfd38e7e5f168 [fix]: correct attention_mode to attention-mode Former-commit-id: 9957e2eebb2166b02600c6052fb54908f5d12bad Update README.md Former-commit-id: 266f9f97ea8f327fdefb2ca959c23d08b7084137 Update README.md Former-commit-id: dd00e04220ff5232c5c4503a095e409471476a51 Update README.md Former-commit-id: 5717aab628fceba311e284d1bdddeacc4c43c7c5 Create train_256.sh Former-commit-id: bf62151617feb49663f8dfe0c78aa01f41e62092 Update README.md Former-commit-id: c5eea8d3492872255a6a34d422230509d1c3531c [feat]: use accelerate on multi-node Former-commit-id: 0b12fc31f1d2a63f9da38821174ab63d9b3a2d72 Update README.md Former-commit-id: 0d7dd6233811ec3c8741affb436d8004d8c676b8 Update sample.py Former-commit-id: 6e09fc266a626f465109abc199d9fb452657929e Update interpolation.py Former-commit-id: e10e3416433e5f396aa13e7d8beaf451ff47a383 Add files via upload Former-commit-id: b707b1968fa59d85891040604e7d54bef61b4549 Update and rename Frame Interpolation.md to readme.md Former-commit-id: 87bdf1ebecb002c086497b3c47acb3f3cb2aaf34 Update readme.md Former-commit-id: e287f94838007f2eda434a46bcd250099b627097 Support deepspeed for videogpt training. Former-commit-id: 1f3dfe038845d91b81b0ecc9b6f3cabb06eed5d5 Fix config typos. Former-commit-id: 455973b57bda19379d41e59aa418421fd269281e Add hidden_size argument for videogpt config. Former-commit-id: 1be9f22767fe9c80ad90cb17184751b97aff9012 Add deepspeed script for videogpt training. Former-commit-id: 3bda2a05cb789fe785b2355a613385b3eb7abbbc use fp16 temporarily. Former-commit-id: 93381968e10aae7bbec23ec1ef40e7cb311fddf0 update videogpt. Former-commit-id: 2e211563c684a8b6ef62dfc0c4f1897224dfb58d Fix deepspeed zero loss bug. Former-commit-id: e24af415bc5d6fd38d72a24d6312f4e65d3627dd Can't fix zero loss bug. Former-commit-id: 1f32f9d889743b7961b34b823b276bfd03974191 use imageio for write video which has better compatibility. Former-commit-id: 005119dc9f78ea2a611d22a53772fd59d97d7139 Update training arguments of videogpt training. Former-commit-id: c9660af891c0dae1f51a1075ef5b2df012f65e06 Update README.md Former-commit-id: 8ec7524f4f171e650d7a25cc88ed29f7b5017454 Update train_256.sh Former-commit-id: a2dfc9a8c70845446e731c88acd222e9b2db9122 Update README.md Former-commit-id: 7e74356a3279b02d062bab9c9f361f61eb756179 Update README.md Former-commit-id: cf1c56c0058288427c1dfd651c8c0e9a981c5653 refactor dit for supporting deepspeed. Former-commit-id: e1909745cb05ea2f28f3a391c194f7656593a236 Fix typos. Former-commit-id: 6add5ee7ef39a973e91b7a7c1b3a4b0980130b88 Update README.md Former-commit-id: 2d5b6815f95e94a98847b8a0376473f01bb15d14 [bug] fix quantization loss Former-commit-id: 111fcf55f969ffe4c9be284a317ae5712570a527 del t2v Former-commit-id: 910398ca297be8a188d59c59c121857666774af1 Update sample.py Former-commit-id: eb906691abec030d7aa2078b63fc9c7d678662ed Update sample.sh Former-commit-id: b149a300f1aff53d9ab6e66a91cc2bcd37900347 Update README.md Former-commit-id: 7b96fd89eaf3743a4a9fbc995e6a2a791c734f2e del t2v Former-commit-id: 5daa2952997a29e74e2b5d235001d6d9dabf9ff6 [fix]: fix sample.sh Former-commit-id: 09916dfcfb0b80a4217016919e0394a2cb665cfd update scripts Former-commit-id: 8b2a794095f2deb9b1e8090486cb8242bf81031d Update pyproject.toml Former-commit-id: 7fb921440b608e47781d2f541b2a8d893a48193e Update README.md Former-commit-id: 34e529fd02712d4466ab2bc1a63f9bd69976b6c3 Update README.md Former-commit-id: d97edd0e714a237a54f191dcebb6fd39f73185d6 resume and compress kv Former-commit-id: 0af7d58f88c1b65b4089da6f3e5c70456614da7c refactor and fix resume bug Former-commit-id: 1b1136671b9a7e0b15b70168e2c44dbff04a22e2 Update train.py Former-commit-id: 783faf5cd86c148a713ea986ceed8527b108f2b8 Update train_t2v.py Former-commit-id: d25b39ffac4474abd40b551fe0e51c421cb7bf5e Update README.md Former-commit-id: 6729039f059a23bf71faa944e2498657ef2c7442 [docs]: fix VQVAE script path Former-commit-id: d5f5c681b8c50622a84256881d193d5803ff3c28 Update pyproject.toml Former-commit-id: 5637656a79faba1b397b56918c42022cc209e731 [feat] add casual vqvae Former-commit-id: 89087970b267908c8667984aba42b1ade2505467 Update __init__.py Former-commit-id: 7c8af2909c4549e96bc0893d09fcc53d71fb6cc7 Create clip.py Former-commit-id: e3397c567efcfeef6f58dfef2eb4251bedbbf33a Update clip.py Former-commit-id: 1ee53d152630b6acc69369be33287abfb143224d train with image Former-commit-id: 209b9a8ad2f19a50e46f9b7ec68dd781898e491a support attention_mode Former-commit-id: c72f354fbfd948e8cd4b0cc63c157bc4688c7665 Update README.md Former-commit-id: 9065d12ee867c76452ce370fb71cc83bdc01470b Update README.md Former-commit-id: 95b28706d257d7769049abe30a7121b60e39a53f t2v attention_mode Former-commit-id: 4fd8bd87e0d0a98d5d0a3201d5db9a5ba055b894 Update README.md Former-commit-id: 859c0f60dac1562b8a6bbb72b793a4709dbc3597 Update Data.md Former-commit-id: d1c72e3209769d0204efb8e7d705bdf4f22b82ea train t2v feature Former-commit-id: 7f16b162cae0e3533c8ba9aa03186a846d9e02bf scripts Former-commit-id: 047046f684c2831e68e0cf256cb46fc3790cda33 add causalvae Former-commit-id: ac936276fd3213ee1815dfe096cd64cecb601428 Create causalvideovae.md Former-commit-id: 98414c2ad7dd39f740440b2d9059140f93492e83 Rename causalvideovae.md to CausalVideoVAE.md Former-commit-id: 0a78e4193fcaec4662b7c6465a9d80643b354cd6 Update README.md Former-commit-id: 8d70a890fc4191bfc98c3aca6dd3c8a70534fb3d Update README.md Former-commit-id: a41674f5ef71ced59b374226dddfa09c15a99f25 Update CausalVideoVAE.md Former-commit-id: 1cd665f699632e9dc9357379225c085d9c1278d5 Update README.md Former-commit-id: 90d6d298eaee6255098b342b31444e7a347fc50e Update README.md Former-commit-id: f294a0a28f288ae592e7de004320d3eb5e48e945 Update README.md Former-commit-id: 02b0422fd745b4b7ec091cc6ee81d1d237245fe6 [feat] eval Former-commit-id: d2c3cf5ef8d18feb7b0544d68d9796656e90ba8d fix training bug Former-commit-id: 09a6283ddd9ab11479f30324de788bd7415702ae update model Former-commit-id: e4e24650376d9dce974b290752efb416d5061983 update vae Former-commit-id: 24298df5d374d5aaa5696f6200d4e23ae887715f fix videovae trainer Former-commit-id: 79e1feb412b6eeead3306f7f0d55a01f9b2a543e Update CausalVideoVAE.md Former-commit-id: fcde4d0ff783448c2006d6f4751a0e4f9cc23d0f Update README.md Former-commit-id: 2d9ae56309717a91e0690bd5c354fe3cbe5d5ed6 Update __init__.py Former-commit-id: 2bdc29d86ebff3d6f29c38ded57c7b0dd1e6bbdb update sample Former-commit-id: 116726b9d565c689496123d4b6e62b98400536a6 tile conv Former-commit-id: 5133615bcba221c4b86ae4e8b09b82ee5d34c66d fix reshape bugs in AttnBlock3D Former-commit-id: 299622e168a6cbee14ef2056c47cae722c20e005 tile only2d Former-commit-id: 7f31ad03fdbaac97aea51a98bd29dc7327b700c9 released v1.0.0 Former-commit-id: 8df5897a4ffc341ced45faf617687fb8ebf1286c sample pipeline Former-commit-id: 8964b16c0dd65445a20050c9a0e82d909b55eaff add tile training Former-commit-id: 216079dc925beefe1ec722a4e87e46a4e1ca56be update train script Former-commit-id: ac16f77d19488cd46130587369cd7a5ea378052b refactor vae to hf Former-commit-id: a938d3f7f962709f7ecff5e9d49c9bf1ce40d7fa release Former-commit-id: 52fe2ac6610661d6a13a08390c84a8237f37986a v1.0.0 Former-commit-id: 93f44f3b425c090d9bc92687a9c50a26b4f0d7c8 clean Former-commit-id: 56ed87088e13cdd93929e2ceedbfae693e2f013a update gradio demo Former-commit-id: 80c2cc0bcc2a7532bc2440df16c1f811b0b30516 Update and rename CausalVideoVAE.md to Report-v1.0.0.md Former-commit-id: 3b848f8de33cf4f0ed802428e9ad31d2fa26de63 Update README.md Former-commit-id: e6fb5d864ac8af8214cdc89c71b42f296dda14e7 Update README.md Former-commit-id: a61200bea2eea4fd22bd8095f192fef4c22c49b4 Update README.md Former-commit-id: a2ec90a19eacb099cb9f36c1b5dd6e24be307fae Add files via upload Former-commit-id: 1ae8512b7846e9faafdb38ca532898d7f1bdcce2 Add files via upload Former-commit-id: 292cbcfa4e4f384fe6958badc27d3067173119f9 Update Report-v1.0.0.md Former-commit-id: 92d906b340f5970a77377586d7295f46b5de99ec Update README.md Former-commit-id: 037d7d3e084d7ff426347db49c4b210a2ee3927d Update README.md Former-commit-id: 7d4118566b77e09de4a3615612a5b368c76c293e Update README.md Former-commit-id: 3210474a46845ae3a6c0bde7fb6c7f9845866628 Add files via upload Former-commit-id: a232ec2d98b245a9b502a2c49fc4f468a86fbd64 Update README.md Former-commit-id: d5ab411b2e2fd5d6abeda500895e3339167572ab Update README.md Former-commit-id: 30f61958e221c3d41c4aa5917f4511e8c0482cf9 Update README.md Former-commit-id: f8cb7f38751d7a6ef586c293fb769f5d60ad5c59 Update Report-v1.0.0.md Former-commit-id: bd9aa7991212d98a6224b855a3bfb2c12feb5950 Update Report-v1.0.0.md Former-commit-id: aea1f4a82a525d3e6e1d1a1bb7631326b294cdcc Update README.md Former-commit-id: f709d75e7a7d8a08ec60132aa906f6b880af8e58 Add files via upload Former-commit-id: 9a207435d9efac0b19771df5d2288160af1bdd6e Update README.md Former-commit-id: 9bae31c73ea902581e1ee94cfd9012efdf78d349 Update README.md Former-commit-id: 9f926abb08f156807091dd044c133788649a6bac Update Report-v1.0.0.md Former-commit-id: 7cec40420601f7da57449586508e50119bd02a61 Update README.md Former-commit-id: ce2bbf82045041495ed448b46d5f4bdba84aa740 Fix some compatibility issues Former-commit-id: 942d8598b28516369780c3519607929869b427b7 Fix bugs in inference Former-commit-id: c251a86ac7c91d6f2bb12ae645628e9cf4b83d3d fix bugs in inference Former-commit-id: e17c7a7b7704920cb86894419bf573d0fb0c06fa Update README.md Former-commit-id: c1492211ced3cdbec93ef28678a1467245e122e9 Update README.md Former-commit-id: c4a976e9b93536299b2e350454c780fec54fbe95 Update README.md Former-commit-id: bbf3cef43a129a25a80ce134a8895796e24353c6 Update README.md Former-commit-id: 5731e810e2efb7ca7eda1aa8954eb7fd260f4250 Update README.md Former-commit-id: 1b54444249fa91b29334b8fc364fb1978c25fb2d Update README.md Former-commit-id: 9c0f82ae9953b56f2540e9c7ada021e4e1bc6165 Update Report-v1.0.0.md Former-commit-id: 38b779022418cc41cdf9fc508e461f2f6299fd60 Update Report-v1.0.0.md Former-commit-id: 6ebe2c62c0904142026d3796027e95ce43afe16a Update README.md Former-commit-id: 2873d0eaedfad392c627a714448118949d7b7e13 Update Report-v1.0.0.md Former-commit-id: 400f708a080f0799e02697bb8e66d8430ffb1f56 Update Report-v1.0.0.md Former-commit-id: 37f181f34f64ca1141a1345b7c1f2af283be1cad Update README.md Former-commit-id: dc923fe2413b3be31d8d88e7dd83a931fcbf9458 Update README.md Former-commit-id: 828be4267f0795969293e4a321e4e0bfa8e1f1e6 clean Former-commit-id: a7f2c1c8b2243587cedd6e152e64298842287095 Update pyproject.toml Former-commit-id: bb037420b75303b58486ba79da9180b2bf63cf88 Update Data.md Former-commit-id: d9a5c8fbd36a34b7baf720bc27d69604ae5c9d57 add causalvae doc Former-commit-id: 4e6d9e038c8edb81f8402834bd01eeeb823924f9 Update Train_And_Eval_CausalVideoVAE.md Former-commit-id: 589657b089930379bb75f3d49063ebb72671efc6 Update Train_And_Eval_CausalVideoVAE.md Former-commit-id: 156739c35b5dda594138b2e3b1fd6ec99556af23 Update README.md Former-commit-id: 567ade18047a9cc81648997c5d9287fa8900614d 更新 README.md Former-commit-id: 42607786f3f3d0eba9336f774b0a2ae076cce793 Update README.md Former-commit-id: 88e7f2062c494c199caff468f5f14f3165f763de Create Report-v1.0.0-cn.md Former-commit-id: d484754e080bd65f6b35619b840040581fa6b69c Update README.md Former-commit-id: e386ec112337271bb9f55ea6ec872eaba94aff50 Update README.md Former-commit-id: c437a18dbec7ac6e02287565a84d75f96664ee99 Update README.md Former-commit-id: 0cc27a5b065b61f37d44dc2c90ba2782075eefb2 Update Report-v1.0.0-cn.md Former-commit-id: d5cf6aed7c1673562a6d2022c3fd7f7b16006cfd Update Data.md Former-commit-id: 15defc1a3846335fd68e18fa9b8ffbe38e3e7100 Update Report-v1.0.0.md Former-commit-id: 3c8eb75dc2045154e8654f860b1fabd7e597094b Update Report-v1.0.0-cn.md Former-commit-id: 0edac24fcaa447a534755bf3aac1f2027a491c06 Update Report-v1.0.0-cn.md Former-commit-id: 59edce70edbaed957ba40f1d95b4a5050ece12bf Update Report-v1.0.0-cn.md Former-commit-id: 83124efeaa5a8cd528a15760bdf88acc403142bd Add files via upload Former-commit-id: 943dd9a063baaa05d7f0d0bd52cbde35cf2b3d98 [formerly 5542359738243a8e948a8f08ed4e8dedf6b4b818] Former-commit-id: fbee07f6fef2ac86bc15ff66f2e2a1a68b64ed01 Update README.md Former-commit-id: 6fd60c73b6ae69d6ad9dc14c7371bd9bff086b44 [formerly 2ed5d5b7bdcd88044da26ca94237c50af7ab47dd] Former-commit-id: 443c333353963bc1ec64a4946afbe5e17f9b5a87 Update README.md Former-commit-id: 54bad83bf054a2ff574199931121c686b4e28008 [formerly 06bb190a39a9b98c8de5e385fc304a86cc8c9fb2] Former-commit-id: 23e8a235d6828d9ab7c02ef59d40fe13a46fa4b1 Update README.md Former-commit-id: c48eba0bb15d594007809d4cc605dc46ba029cec [formerly 7deb8a0decf7a9757af4475e3029d415d78d185f] Former-commit-id: 7f43bb2f1d9308bd08154171d78e9b36e00a837f Update README.md Former-commit-id: 68eaf2e6b00266cf0858b33f9b4d09681efbd2bc [formerly d8dcad250ed8357964ab8ee570b9973b45e29fcb] Former-commit-id: 75f51b3d84203be3672f6b07bb3c70ba0ae5556c Update README.md Former-commit-id: be00a17729ac889493f63fd1d649054d7980ca42 [formerly 9b4d8dcfc8e6e2abe4ca727e01561d27d1ba3968] Former-commit-id: f1b6f5013b99d0d288af0f05c46cb8300d79a8aa Update train_videoae_65x256x256.sh Former-commit-id: b2f5cfe72452dfb369aba166e4a101dfcf5758c9 [formerly 557b78ab38a61d7520e91044137f818e50ec24cb] Former-commit-id: 0e46c9f261e7f5fd4d08e3d7735c17c7975b0e72 Update train_videoae_65x512x512.sh Former-commit-id: 8e40db106efa2fd879cca42bd9ce2d9b79d777f8 [formerly 3e11781b045a838a5aa98ea75e274198f2b26a44] Former-commit-id: f3b0804a7cf063e7ff04ee57dccee0320c1f32a5 fix using pretrained bug Former-commit-id: 2dfbb0683b2f3bd1a1d619e5e4fabb5bd4c92d79 [formerly 27f352e822f732cb351d64ac5726c5df3b62c1bb] Former-commit-id: 722d60ae5ef28499c47292e0a4807a87b9658b01 Update train_t2v_feature.py Former-commit-id: 4a4f7ea2366f1dff5669d6d4834238ebd790e6e2 [formerly ca7d3158fb01f8e7228f833cea8e6741be127c2a] Former-commit-id: 389f0e7dc66826026046b8e12d7a9f8692d32e42 [docs] update docs and train.sh Former-commit-id: 1d9358e84d12863681b396c7621ae7244caa4dc2 [formerly b801491889b3abad1211225d7f07af3ae0389895] Former-commit-id: 9e6a59ae2f246b50ace0a033cbd6548660241204 [fix] fix bug in #185 Former-commit-id: d74efbd0195380ca383598c06d3d8185dd9cc9bc [formerly 0f2687266bc2271e11b2f06edfe514f466e57b0f] Former-commit-id: cb6bb234befc49b3870c5690653469a0714890c5 [docs] add evaluation detail Former-commit-id: c15a717cc61159e5f6adafb718237ab578ac81e7 [formerly 9e92772776fa4dd18772c7fec939a42ad6dfbc0a] Former-commit-id: 98c2a38af5064f27888d1b102c9fded87d2420d5 [docs] update train.sh Former-commit-id: eeb00ac046692bc97a064ec1b47c994436798ab1 [formerly 6d0fd85e1b13a7824b8181ba9ccebec2a3c76d40] Former-commit-id: 1e8e669ad9967aeeeb7622bc04a0759f348cb26f Delete assets/we_want_you.jpg Update README.md fix typo Fix typos line 24: `github -> GitHub` line 54: `a -> an` line 85: `re-organizes -> re-organize, modulizes -> modulize` line 87: `opened -> open` (modified according to the changelogs of other dates) line 94: `a -> an` [fix]: Fix variable naming errors Update README.md [docs] update inference example. [feat] add time chunk inference [docs] fix typo [refactor] fix hardcode Update LICENSE Update Report-v1.0.0.md Update pyproject.toml add 2drope and dynamic training refactor dynamic training read image from folder img training img training support newvae vae temporal tiling abs and rope compress kv and rope pi fix rope with compress update dataset mask loss multi-data fix dataset train vis update fix mask 5.15 prepare release prepare scripts fix demo 5.27 update prompt Update README.md Update README.md Create Report-v1.1.0.md Update README.md Update README.md Update README.md Update README.md Update README.md Update README.md Update README.md Update README.md Update README.md Update README.md Update README.md Update LICENSE Update Report-v1.1.0.md solve typos Update pyproject.toml Update README.md Update dataset_utils.py Update modeling_latte.py Update train_t2v.py Update train_t2v.py Update t2v_datasets.py fix wrong code Update LICENSE fix train bug fix vis [docs] update CausalVideoVAE docs Update README.md fix the bug Update gradio_web_server.py Update README.md Update gradio_utils.py Update sample_video_513.sh Rename sample_video_513.sh to sample_video_221.sh Update README.md Update Report-v1.1.0.md release v1.2.0 Update README.md Update Report-v1.2.0.md Update README.md Update pyproject.toml Update Report-v1.2.0.md Update Report-v1.2.0.md Update Report-v1.2.0.md Update t2v_datasets.py Update Report-v1.2.0.md Update Report-v1.2.0.md Update README.md Update Report-v1.2.0.md Update Report-v1.2.0.md Update README.md fix sample Delete opensora/train/train_t2v_diffusers_lora.py lora bug Update README.md Update Report-v1.2.0.md Update train_t2v_diffusers.py release Open-Sora Plan v1.2.0 i2v (#389) * gitignore * inpaint * Create condition_image_path.txt * Rename condition_image_path.txt to condition_images_path.txt * Update sample_inpaint.py * Update sample_inpaint.sh * inpaint * fix bug * Update README.md * inpainting * release v1.2.0 i2v * release v1.2.0 i2v * Update Report-v1.2.0.md * Update pipeline_inpaint_sp.py * Update sample_inpaint_ddp.py * Update sample_inpaint_sp.py * Update sample_inpaint.py * Grammar Error Correction * Grammar Error Correction --------- Co-authored-by: LinB203 <2267330597@qq.com> Update Report-v1.2.0.md add 29x480p link Update README.md Update train_inpaint.sh do not set seed [fix] fix the path typo for google/mt5-xxl in gradio_web_server.py (#405) * [fix] fix the path typo for google/mt5-xxl in gradio_web_server.py * [fix] fix the issue: the cache of pretrained mt5-xxl weights is inconsistent. --------- Co-authored-by: guangyi <guangyi.liu@mbz-h100-029.core42.ai>
3569 lines
162 KiB
Python
3569 lines
162 KiB
Python
# Copyright (c) Microsoft Corporation.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# DeepSpeed Team
|
|
|
|
import os
|
|
import re
|
|
import stat
|
|
import torch
|
|
import hashlib
|
|
from collections import defaultdict, OrderedDict, deque
|
|
from shutil import copyfile
|
|
import gc
|
|
|
|
from torch.nn.modules import Module
|
|
from torch.nn.parameter import Parameter
|
|
from torch.optim import Optimizer
|
|
from torch.optim.lr_scheduler import _LRScheduler
|
|
from torch._utils import _flatten_dense_tensors, _unflatten_dense_tensors
|
|
|
|
from typing import Callable, Dict, Union, Iterable
|
|
|
|
import deepspeed
|
|
|
|
from deepspeed import comm as dist
|
|
from deepspeed.runtime.utils import see_memory_usage, DummyOptim
|
|
from deepspeed.runtime.zero.offload_config import OffloadDeviceEnum
|
|
from deepspeed.runtime.zero.stage_1_and_2 import DeepSpeedZeroOptimizer
|
|
from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus
|
|
from deepspeed.runtime.zero.utils import is_zero_supported_optimizer, ZeRORuntimeException
|
|
from deepspeed.runtime.zero.parameter_offload import DeepSpeedZeRoOffload
|
|
from deepspeed.runtime.zero.config import ZERO_OPTIMIZATION
|
|
|
|
from deepspeed.runtime.fp16.fused_optimizer import FP16_Optimizer
|
|
from deepspeed.runtime.fp16.unfused_optimizer import FP16_UnfusedOptimizer
|
|
from deepspeed.runtime.bf16_optimizer import BF16_Optimizer
|
|
|
|
from deepspeed.runtime.config import DEEPSPEED_OPTIMIZERS, \
|
|
ADAGRAD_OPTIMIZER, ADAM_OPTIMIZER, ADAMW_OPTIMIZER, LAMB_OPTIMIZER, ONEBIT_ADAM_OPTIMIZER, ONEBIT_LAMB_OPTIMIZER, \
|
|
TORCH_ADAM_PARAM, ADAM_W_MODE, ADAM_W_MODE_DEFAULT, ZERO_ONE_ADAM_OPTIMIZER, MUADAM_OPTIMIZER, MUADAMW_OPTIMIZER, \
|
|
MUSGD_OPTIMIZER, LION_OPTIMIZER
|
|
|
|
from deepspeed.runtime.dataloader import DeepSpeedDataLoader
|
|
from deepspeed.runtime.constants import \
|
|
ROUTE_TRAIN, ROUTE_PREDICT, ROUTE_EVAL, \
|
|
PLD_THETA, PLD_GAMMA, BFLOAT16, FP16, AMP, GRADIENT_ACCUMULATION_STEPS, \
|
|
DATA_PARALLEL_GROUP, GLOBAL_RANK
|
|
from deepspeed.runtime.zero.config import ZeroStageEnum
|
|
from deepspeed.compression import compression_scheduler
|
|
from deepspeed.compression.constants import \
|
|
WEIGHT_QUANTIZE_IN_FORWARD_ENABLED, \
|
|
WEIGHT_QUANTIZATION, SHARED_PARAMETERS, \
|
|
WEIGHT_QUANTIZE_ENABLED, \
|
|
WEIGHT_QUANTIZE_GROUPS, \
|
|
WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE, \
|
|
WEIGHT_QUANTIZE_CHANGE_RATIO, \
|
|
WEIGHT_QUANTIZE_TYPE, \
|
|
WEIGHT_QUANTIZE_ROUNDING, \
|
|
WEIGHT_QUANTIZE_VERBOSE, \
|
|
WEIGHT_QUANTIZE_KERNEL
|
|
from deepspeed.checkpoint.constants import OPTIMIZER_STATE_DICT, FROZEN_PARAM_FRAGMENTS
|
|
from deepspeed.runtime.sparse_tensor import SparseTensor
|
|
|
|
from deepspeed.runtime import lr_schedules
|
|
from deepspeed.utils import groups
|
|
from deepspeed.utils import logger, log_dist, instrument_w_nvtx
|
|
from deepspeed.utils.timer import NoopTimer, ThroughputTimer, SynchronizedWallClockTimer, \
|
|
FORWARD_MICRO_TIMER, BACKWARD_MICRO_TIMER, BACKWARD_INNER_MICRO_TIMER, BACKWARD_REDUCE_MICRO_TIMER, \
|
|
STEP_MICRO_TIMER, \
|
|
FORWARD_GLOBAL_TIMER, BACKWARD_GLOBAL_TIMER, BACKWARD_INNER_GLOBAL_TIMER, BACKWARD_REDUCE_GLOBAL_TIMER, \
|
|
STEP_GLOBAL_TIMER
|
|
from deepspeed.utils.debug import debug_extract_module_and_param_names
|
|
from deepspeed.monitor.monitor import MonitorMaster
|
|
from deepspeed.runtime.progressive_layer_drop import ProgressiveLayerDrop
|
|
from deepspeed.runtime.utils import clip_grad_norm_
|
|
from deepspeed.runtime.eigenvalue import Eigenvalue
|
|
from deepspeed.runtime.data_pipeline.constants import DATA_SAMPLING, \
|
|
DATA_ROUTING, DATA_SAMPLING_ENABLED, CURRICULUM_LEARNING, \
|
|
CURRICULUM_LEARNING_ENABLED, DATA_SAMPLING_NUM_WORKERS, RANDOM_LTD, \
|
|
RANDOM_LTD_ENABLED, RANDOM_LTD_LAYER_ID, RANDOM_LTD_LAYER_NUM, \
|
|
RANDOM_LTD_LAYER_TOKEN_LR_SCHEDULE, RANDOM_LTD_LAYER_TOKEN_LR_ENABLED, \
|
|
RANDOM_LTD_GLOBAL_BATCH_SIZE, RANDOM_LTD_MICRO_BATCH_SIZE, DATA_EFFICIENCY
|
|
from deepspeed.runtime.data_pipeline.curriculum_scheduler import CurriculumScheduler
|
|
from deepspeed.runtime.data_pipeline.data_routing.scheduler import RandomLTDScheduler
|
|
from deepspeed.runtime.data_pipeline.data_routing.helper import remove_random_ltd_state_dict
|
|
from deepspeed.runtime.data_pipeline.data_routing.basic_layer import RandomLayerTokenDrop
|
|
|
|
from deepspeed.runtime.checkpoint_engine.torch_checkpoint_engine import TorchCheckpointEngine
|
|
from deepspeed.utils.zero_to_fp32 import get_fp32_state_dict_from_zero_checkpoint
|
|
|
|
from deepspeed.runtime.pipe.module import PipelineModule
|
|
from deepspeed.runtime.utils import get_ma_status
|
|
from deepspeed.ops.adam import FusedAdam
|
|
from deepspeed.moe.sharded_moe import TopKGate, MOELayer
|
|
from deepspeed.moe.layer import MoE
|
|
from deepspeed.moe.utils import is_moe_param
|
|
from deepspeed.git_version_info import version
|
|
|
|
from deepspeed.profiling.flops_profiler.profiler import FlopsProfiler
|
|
from deepspeed.utils.logging import print_json_dist, print_configuration
|
|
|
|
from deepspeed.accelerator import get_accelerator
|
|
|
|
from deepspeed.runtime.config import DtypeEnum
|
|
|
|
from opensora.adaptor.zp_manager import zp_manager
|
|
|
|
MEMORY_OPT_ALLREDUCE_SIZE = 500000000
|
|
|
|
DeepSpeedOptimizerCallable = \
|
|
Callable[[Union[Iterable[Parameter], Dict[str, Iterable]]], Optimizer]
|
|
DeepSpeedSchedulerCallable = Callable[[Optimizer], _LRScheduler]
|
|
|
|
try:
|
|
import apex
|
|
from apex import amp
|
|
APEX_INSTALLED = True
|
|
except ImportError:
|
|
# Fail silently so we don't spam logs unnecessarily if user isn't using amp
|
|
APEX_INSTALLED = False
|
|
|
|
|
|
def split_half_float_double_sparse(tensors):
|
|
device_type = get_accelerator().device_name()
|
|
supported_types = [
|
|
"torch.{}.HalfTensor".format(device_type), "torch.{}.FloatTensor".format(device_type),
|
|
"torch.{}.DoubleTensor".format(device_type), "torch.{}.BFloat16Tensor".format(device_type),
|
|
SparseTensor.type()
|
|
]
|
|
|
|
for t in tensors:
|
|
assert t.type() in supported_types, f"attempting to reduce an unsupported grad type: {t.type()}"
|
|
|
|
buckets = []
|
|
for i, dtype in enumerate(supported_types):
|
|
bucket = [t for t in tensors if t.type() == dtype]
|
|
if bucket:
|
|
buckets.append((dtype, bucket))
|
|
return buckets
|
|
|
|
|
|
class EngineTimers(object):
|
|
r"""Wallclock timers for DeepSpeedEngine"""
|
|
|
|
def __init__(self, enable_micro_timers, enable_global_timers):
|
|
self.forward_timers = []
|
|
self.backward_timers = []
|
|
self.backward_inner_timers = []
|
|
self.backward_reduce_timers = []
|
|
self.step_timers = []
|
|
self.global_timers = []
|
|
self.micro_timers = []
|
|
|
|
if enable_micro_timers:
|
|
self.forward_timers += [FORWARD_MICRO_TIMER]
|
|
self.backward_timers += [BACKWARD_MICRO_TIMER]
|
|
self.backward_inner_timers += [BACKWARD_INNER_MICRO_TIMER]
|
|
self.backward_reduce_timers += [BACKWARD_REDUCE_MICRO_TIMER]
|
|
self.step_timers += [STEP_MICRO_TIMER]
|
|
self.micro_timers += [
|
|
FORWARD_MICRO_TIMER, BACKWARD_MICRO_TIMER, BACKWARD_INNER_MICRO_TIMER, BACKWARD_REDUCE_MICRO_TIMER,
|
|
STEP_MICRO_TIMER
|
|
]
|
|
|
|
if enable_global_timers:
|
|
self.forward_timers += [FORWARD_GLOBAL_TIMER]
|
|
self.backward_timers += [BACKWARD_GLOBAL_TIMER]
|
|
self.backward_inner_timers += [BACKWARD_INNER_GLOBAL_TIMER]
|
|
self.backward_reduce_timers += [BACKWARD_REDUCE_GLOBAL_TIMER]
|
|
self.step_timers += [STEP_GLOBAL_TIMER]
|
|
self.global_timers += [
|
|
FORWARD_GLOBAL_TIMER, BACKWARD_GLOBAL_TIMER, BACKWARD_INNER_GLOBAL_TIMER, BACKWARD_REDUCE_GLOBAL_TIMER,
|
|
STEP_GLOBAL_TIMER
|
|
]
|
|
|
|
|
|
class DeepSpeedEngine(Module):
|
|
r"""DeepSpeed engine for training."""
|
|
|
|
def __init__(
|
|
self,
|
|
args,
|
|
model,
|
|
optimizer=None,
|
|
model_parameters=None,
|
|
training_data=None,
|
|
lr_scheduler=None,
|
|
mpu=None,
|
|
dist_init_required=None,
|
|
collate_fn=None,
|
|
config=None,
|
|
config_class=None,
|
|
dont_change_device=False,
|
|
):
|
|
super(DeepSpeedEngine, self).__init__()
|
|
self.dont_change_device = dont_change_device
|
|
self.client_optimizer = optimizer
|
|
self.client_lr_scheduler = lr_scheduler
|
|
self.training_data = training_data
|
|
self.collate_fn = collate_fn
|
|
self.mpu = mpu
|
|
self.all_to_all_group = None
|
|
self.data_parallel_group = None
|
|
self.global_steps = 0
|
|
self.global_samples = 0
|
|
self.micro_steps = 0
|
|
self.skipped_steps = 0
|
|
self.gradient_average = True
|
|
self.warn_unscaled_loss = True
|
|
self.config = config
|
|
self._config = config_class
|
|
self.loaded_checkpoint_mp_world_size = None
|
|
self.loaded_checkpoint_dp_world_size = None
|
|
self.loaded_checkpoint_zp_world_size = None
|
|
self.enable_backward_allreduce = True
|
|
self.progressive_layer_drop = None
|
|
self.eigenvalue = None
|
|
self.block_eigenvalue = None
|
|
self.gas_boundary_ctr = 0
|
|
self.dist_backend = get_accelerator().communication_backend_name()
|
|
self.has_moe_layers = False
|
|
self.num_experts = []
|
|
self.gate_modules = []
|
|
self.moe_layers = []
|
|
self._step_applied = False
|
|
self._global_grad_norm = None
|
|
self.use_ds_comm = False # False --> Use torch.dist, True --> Use ds.comm backend.
|
|
|
|
self.checkpoint_engine = None
|
|
|
|
self._is_gradient_accumulation_boundary = None
|
|
self.scale_wrt_gas = None
|
|
self.losses = 0.0
|
|
|
|
# for debug purposes - can then debug print: debug_get_module_name(module)
|
|
debug_extract_module_and_param_names(model)
|
|
|
|
self._do_args_sanity_check(args)
|
|
self._configure_with_arguments(args, mpu)
|
|
self._do_sanity_check()
|
|
see_memory_usage(f"DeepSpeed Engine: After args sanity test", force=self.memory_breakdown())
|
|
if mpu is not None:
|
|
if self.elasticity_enabled():
|
|
if not self.is_elastic_model_parallel_supported():
|
|
assert not self.elasticity_enabled(), ("Elasticity is not currently supported"
|
|
" with model parallelism.")
|
|
|
|
self._set_distributed_vars(args)
|
|
|
|
dist.configure(self._config)
|
|
|
|
self.monitor = MonitorMaster(self._config.monitor_config)
|
|
|
|
see_memory_usage(
|
|
f"DeepSpeed Engine: Before configure distributed model",
|
|
force=self.memory_breakdown(),
|
|
)
|
|
|
|
self.pipeline_parallelism = isinstance(model, PipelineModule)
|
|
|
|
# Configure distributed model
|
|
self._configure_distributed_model(model)
|
|
|
|
# needed for zero_to_fp32 weights reconstruction to remap nameless data to state_dict
|
|
self.param_names = {param: name for name, param in model.named_parameters()}
|
|
|
|
self._get_model_parameters()
|
|
|
|
see_memory_usage(f"DeepSpeed Engine: After configure distributed model")
|
|
|
|
# Configure wall clock timers
|
|
self.timers = SynchronizedWallClockTimer()
|
|
# Throughput timer
|
|
self.tput_timer = ThroughputTimer(
|
|
batch_size=self.train_batch_size(),
|
|
steps_per_output=self.steps_per_print(),
|
|
monitor_memory=False,
|
|
)
|
|
|
|
log_dist(f"DeepSpeed Flops Profiler Enabled: {self.flops_profiler_enabled()}", ranks=[0])
|
|
|
|
if self.flops_profiler_enabled():
|
|
self.flops_profiler = FlopsProfiler(self.module, self, self.flops_profiler_recompute_fwd_factor())
|
|
|
|
if training_data:
|
|
self.training_dataloader = self.deepspeed_io(training_data)
|
|
else:
|
|
self.training_dataloader = None
|
|
|
|
# Configure optimizer and scheduler
|
|
self.optimizer = None
|
|
self.basic_optimizer = None
|
|
self.lr_scheduler = None
|
|
has_optimizer = False
|
|
|
|
if optimizer or self.optimizer_name():
|
|
has_optimizer = True
|
|
# If no parameters given by init default to module parameters
|
|
if model_parameters is None:
|
|
model_parameters = self.module.parameters()
|
|
|
|
# Convert model parameters from generator to list
|
|
if not isinstance(model_parameters, list):
|
|
model_parameters = list(model_parameters)
|
|
|
|
if has_optimizer:
|
|
self._configure_optimizer(optimizer, model_parameters)
|
|
self._configure_lr_scheduler(lr_scheduler)
|
|
self._report_progress(0)
|
|
elif self.zero_optimization():
|
|
# no optim selected but zero is enabled
|
|
self.optimizer = self._configure_zero_optimizer(optimizer=None)
|
|
elif self.bfloat16_enabled():
|
|
self.optimizer = self._configure_bf16_optimizer(optimizer=None)
|
|
|
|
# Hook optimizer for snip_momentum pruning
|
|
if hasattr(model, 'pruners'):
|
|
from deepspeed.compression.helper import rewrite_optimizer_step
|
|
self.optimizer.pruners = model.pruners
|
|
rewrite_optimizer_step(self.optimizer)
|
|
|
|
# Bookkeeping for sparse support
|
|
self.sparse_tensor_module_names = set()
|
|
# if self.sparse_gradients_enabled():
|
|
for name, module in self.module.named_modules():
|
|
if isinstance(module, (torch.nn.Embedding, torch.nn.EmbeddingBag)) and self.sparse_gradients_enabled():
|
|
self.sparse_tensor_module_names.add(name + ".weight")
|
|
logger.info("Will convert {} to sparse tensor during training".format(name))
|
|
|
|
self.save_non_zero_checkpoint = False
|
|
self.save_zero_checkpoint = False
|
|
if not isinstance(self.optimizer, DeepSpeedZeRoOffload):
|
|
self._configure_checkpointing(dist_init_required)
|
|
|
|
if self.eigenvalue_enabled():
|
|
self.eigenvalue = self._configure_eigenvalue()
|
|
|
|
if self.pld_enabled():
|
|
self.progressive_layer_drop = self._configure_progressive_layer_drop()
|
|
|
|
if self.curriculum_enabled_legacy():
|
|
self.curriculum_scheduler_legacy = self._configure_curriculum_scheduler_legacy()
|
|
|
|
if self.random_ltd_enabled():
|
|
random_ltd_config = self.random_ltd_config()
|
|
random_ltd_config[RANDOM_LTD_GLOBAL_BATCH_SIZE] = self.train_batch_size()
|
|
random_ltd_config[RANDOM_LTD_MICRO_BATCH_SIZE] = self.train_micro_batch_size_per_gpu()
|
|
self.random_ltd_scheduler = self._configure_random_ltd_scheduler(random_ltd_config)
|
|
|
|
# Engine timers
|
|
|
|
self.engine_timers = EngineTimers(enable_micro_timers=self.wall_clock_breakdown(),
|
|
enable_global_timers=self.wall_clock_breakdown()
|
|
or self.flops_profiler_enabled())
|
|
|
|
if self.global_rank == 0:
|
|
self._config.print("DeepSpeedEngine configuration")
|
|
if self.dump_state():
|
|
print_configuration(self, "DeepSpeedEngine")
|
|
|
|
# Use torch (un)flatten ops
|
|
self.flatten = _flatten_dense_tensors
|
|
self.unflatten = _unflatten_dense_tensors
|
|
|
|
def destroy(self):
|
|
if self.optimizer is not None and hasattr(self.optimizer, 'destroy'):
|
|
self.optimizer.destroy()
|
|
|
|
def _get_model_parameters(self):
|
|
if self.autotuning_profile_model_info():
|
|
self.autotuning_model_info = {}
|
|
num_params = 0
|
|
trainable_num_params = 0
|
|
|
|
for p in self.module.parameters():
|
|
# since user code might call deepspeed.zero.Init() before deepspeed.initialize(), need to check the attribute to check if the parameter is partitioned in zero 3 already or not
|
|
n = 0
|
|
if hasattr(p, "ds_tensor"): # if the parameter is partitioned in zero 3
|
|
n += p.ds_numel
|
|
else: # if the parameter is not partitioned in zero 3 yet
|
|
n += p.numel()
|
|
num_params += n
|
|
if p.requires_grad:
|
|
trainable_num_params += n
|
|
if self.global_rank == 0:
|
|
self.autotuning_model_info["num_params"] = num_params * self.mp_world_size
|
|
self.autotuning_model_info["trainable_num_params"] = trainable_num_params * self.mp_world_size
|
|
|
|
logger.info(f"model parameter = {num_params}")
|
|
|
|
def get_batch_info(self):
|
|
"""Get all training batch related settings.
|
|
Returns:
|
|
train_batch_size (int): The effective training batch size. This is the amount of data
|
|
samples that leads to one step of model update.
|
|
train_micro_batch_size_per_gpu (int): Batch size to be processed by one GPU in one
|
|
step (without gradient accumulation).
|
|
gradient_accumulation_steps (int): Number of training steps to accumulate gradients
|
|
before averaging and applying them.
|
|
"""
|
|
return (
|
|
self.train_batch_size,
|
|
self.train_micro_batch_size_per_gpu,
|
|
self.gradient_accumulation_steps,
|
|
)
|
|
|
|
def set_train_batch_size(self, train_batch_size):
|
|
"""Adjust the global batch size by increasing or decreasing the number of
|
|
micro-batches (i.e., gradient accumulation steps). The size of each micro-batch
|
|
(i.e., ``train_micro_batch_size_per_gpu``) is not changed.
|
|
Args:
|
|
train_batch_size (int): The new global batch size for training.
|
|
Raises:
|
|
ValueError: if ``train_batch_size`` is not divisible by the
|
|
configured micro-batch size and data parallelism.
|
|
"""
|
|
if train_batch_size % (self.train_micro_batch_size_per_gpu() * self.dp_world_size) != 0:
|
|
#print(f'{train_batch_size=} {self.train_micro_batch_size_per_gpu()=} {self.dp_world_size=}')
|
|
raise ValueError(f'Train batch size must be divisible by micro-batch data parallelism')
|
|
new_gas = train_batch_size // (self.train_micro_batch_size_per_gpu() * self.dp_world_size)
|
|
# overwrite config
|
|
self._config.train_batch_size = train_batch_size
|
|
self._config.gradient_accumulation_steps = new_gas
|
|
|
|
def set_train_micro_batch_size(self, micro_batch_size):
|
|
"""Adjust the micro batch size(i.e., the micro batch size in every data parallel group),
|
|
while keep the gradient accumulation steps the same.
|
|
Args:
|
|
micro_batch_size (int): The new micro batch size for training.
|
|
"""
|
|
# overwrite config
|
|
new_global_batch_size = micro_batch_size * self._config.gradient_accumulation_steps * self.dp_world_size
|
|
self._config.train_batch_size = new_global_batch_size
|
|
self._config.train_micro_batch_size_per_gpu = micro_batch_size
|
|
|
|
def set_data_post_process_func(self, post_process_func):
|
|
if self.training_dataloader is not None:
|
|
self.training_dataloader.post_process_func = post_process_func
|
|
|
|
def set_custom_curriculum_learning_schedule(self, schedule_func_dict):
|
|
if self.training_dataloader is not None and self.curriculum_learning_enabled():
|
|
self.training_dataloader.data_sampler.set_custom_curriculum_learning_schedule(schedule_func_dict)
|
|
|
|
def get_global_grad_norm(self) -> float:
|
|
"""Return the 2-norm of all gradients. If there is model parallelism,
|
|
the norm will be global.
|
|
The computed norm will be cached and reused until the next step() pass.
|
|
.. note::
|
|
In the presence of model parallelism, this is a collective call
|
|
and acts as a barrier among ``mpu.get_model_parallel_group()``.
|
|
Returns:
|
|
float: norm
|
|
"""
|
|
return self._global_grad_norm
|
|
|
|
def __getattr__(self, name):
|
|
"""
|
|
Pass through attributes defined in the model if they are not overridden by ds-engine.
|
|
"""
|
|
|
|
_module = {}
|
|
if "module" in self.__dict__:
|
|
_module = self.__dict__['module']
|
|
if name in dir(self):
|
|
return getattr(self, name)
|
|
elif name in dir(_module):
|
|
return getattr(_module, name)
|
|
else:
|
|
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
|
|
def checkpoint_tag_validation_enabled(self):
|
|
return self._config.checkpoint_tag_validation_enabled
|
|
|
|
def checkpoint_tag_validation_fail(self):
|
|
return self._config.checkpoint_tag_validation_fail
|
|
|
|
def elasticity_enabled(self):
|
|
return self._config.elasticity_enabled
|
|
|
|
def is_elastic_model_parallel_supported(self):
|
|
if self.elasticity_enabled():
|
|
# Add code for finding number of GPUs per node automatically
|
|
if self._config.num_gpus_per_node % self._config.elastic_model_parallel_size == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def pld_enabled(self):
|
|
return self._config.pld_enabled
|
|
|
|
def pld_params(self):
|
|
return self._config.pld_params
|
|
|
|
def pld_theta(self):
|
|
return self.pld_params()[PLD_THETA]
|
|
|
|
def pld_gamma(self):
|
|
return self.pld_params()[PLD_GAMMA]
|
|
|
|
def eigenvalue_enabled(self):
|
|
return self._config.eigenvalue_enabled
|
|
|
|
def eigenvalue_verbose(self):
|
|
return self._config.eigenvalue_verbose
|
|
|
|
def eigenvalue_max_iter(self):
|
|
return self._config.eigenvalue_max_iter
|
|
|
|
def eigenvalue_tol(self):
|
|
return self._config.eigenvalue_tol
|
|
|
|
def eigenvalue_stability(self):
|
|
return self._config.eigenvalue_stability
|
|
|
|
def eigenvalue_gas_boundary_resolution(self):
|
|
return self._config.eigenvalue_gas_boundary_resolution
|
|
|
|
def eigenvalue_layer_name(self):
|
|
return self._config.eigenvalue_layer_name
|
|
|
|
def eigenvalue_layer_num(self):
|
|
return self._config.eigenvalue_layer_num
|
|
|
|
def curriculum_enabled_legacy(self):
|
|
return self._config.curriculum_enabled_legacy
|
|
|
|
def curriculum_params_legacy(self):
|
|
return self._config.curriculum_params_legacy
|
|
|
|
def data_efficiency_enabled(self):
|
|
return self._config.data_efficiency_enabled
|
|
|
|
def data_efficiency_config(self):
|
|
return self._config.data_efficiency_config
|
|
|
|
def data_sampling_enabled(self):
|
|
return self._config.data_efficiency_config[DATA_SAMPLING][DATA_SAMPLING_ENABLED]
|
|
|
|
def data_sampling_config(self):
|
|
return self._config.data_efficiency_config[DATA_SAMPLING]
|
|
|
|
def curriculum_learning_enabled(self):
|
|
return self._config.data_efficiency_config[DATA_SAMPLING][CURRICULUM_LEARNING][CURRICULUM_LEARNING_ENABLED]
|
|
|
|
def curriculum_learning_config(self):
|
|
return self._config.data_efficiency_config[DATA_SAMPLING][CURRICULUM_LEARNING]
|
|
|
|
def random_ltd_enabled(self):
|
|
return self._config.data_efficiency_config[DATA_ROUTING][RANDOM_LTD][RANDOM_LTD_ENABLED]
|
|
|
|
def random_ltd_config(self):
|
|
return self._config.data_efficiency_config[DATA_ROUTING][RANDOM_LTD]
|
|
|
|
def random_ltd_initialize(self):
|
|
assert self.random_ltd_enabled()
|
|
random_ltd_config = self.random_ltd_config()
|
|
random_ltd_queue = deque([x for x in sorted(random_ltd_config[RANDOM_LTD_LAYER_ID])])
|
|
count = 0
|
|
for name, layer in self.module.named_modules():
|
|
if isinstance(layer, RandomLayerTokenDrop):
|
|
if len(random_ltd_queue) != 0 and str(random_ltd_queue[0]) in name: ###[1,2,3]
|
|
layer.init_config(random_ltd_config, self.random_ltd_scheduler, count)
|
|
random_ltd_queue.popleft()
|
|
count += 1
|
|
|
|
if random_ltd_config[RANDOM_LTD_LAYER_NUM] != count:
|
|
raise ValueError(f'random_ltd_layer_num {random_ltd_config[RANDOM_LTD_LAYER_NUM]} must be \
|
|
equivalent to the len of random_ltd_layer_id {count}')
|
|
|
|
if random_ltd_config[RANDOM_LTD_LAYER_TOKEN_LR_SCHEDULE][RANDOM_LTD_LAYER_TOKEN_LR_ENABLED]:
|
|
assert self.client_lr_scheduler is None
|
|
raise ValueError(f'not yet support')
|
|
#self.lr_scheduler = lr_schedules.WarmupLayerTokenDecayLR(self.optimizer, self.random_ltd_scheduler)
|
|
|
|
def wall_clock_breakdown(self):
|
|
return self._config.wall_clock_breakdown
|
|
|
|
def flops_profiler_enabled(self):
|
|
return self._config.flops_profiler_config.enabled or self.autotuning_enabled()
|
|
|
|
def flops_profiler_recompute_fwd_factor(self):
|
|
return self._config.flops_profiler_config.recompute_fwd_factor
|
|
|
|
def flops_profiler_profile_step(self):
|
|
step = self._config.flops_profiler_config.profile_step
|
|
if self._config.autotuning_config.enabled:
|
|
step = self.autotuning_start_profile_step()
|
|
return step
|
|
|
|
def flops_profiler_module_depth(self):
|
|
return self._config.flops_profiler_config.module_depth
|
|
|
|
def flops_profiler_top_modules(self):
|
|
return self._config.flops_profiler_config.top_modules
|
|
|
|
def flops_profiler_detailed(self):
|
|
if self._config.autotuning_config.enabled:
|
|
return False
|
|
return self._config.flops_profiler_config.detailed
|
|
|
|
def flops_profiler_output_file(self):
|
|
return self._config.flops_profiler_config.output_file
|
|
|
|
def memory_breakdown(self):
|
|
return self._config.memory_breakdown
|
|
|
|
def autotuning_enabled(self):
|
|
return self._config.autotuning_config.enabled
|
|
|
|
def autotuning_start_profile_step(self):
|
|
return self._config.autotuning_config.start_profile_step
|
|
|
|
def autotuning_end_profile_step(self):
|
|
return self._config.autotuning_config.end_profile_step
|
|
|
|
def autotuning_metric_path(self):
|
|
path = self._config.autotuning_config.metric_path
|
|
if not path:
|
|
path = os.path.join(os.getcwd(), "autotuning_metric.json")
|
|
return path
|
|
|
|
def autotuning_model_info_path(self):
|
|
path = self._config.autotuning_config.model_info_path
|
|
if not path:
|
|
path = os.path.join(os.getcwd(), "autotuning_model_info.json")
|
|
return path
|
|
|
|
def autotuning_metric(self):
|
|
return self._config.autotuning_config.metric
|
|
|
|
def autotuning_profile_model_info(self):
|
|
return self.autotuning_enabled(
|
|
) and self._config.autotuning_config.model_info and self._config.autotuning_config.model_info.get(
|
|
"profile", False)
|
|
|
|
def sparse_gradients_enabled(self):
|
|
return self._config.sparse_gradients_enabled
|
|
|
|
def train_batch_size(self):
|
|
return self._config.train_batch_size
|
|
|
|
def train_micro_batch_size_per_gpu(self):
|
|
return self._config.train_micro_batch_size_per_gpu
|
|
|
|
def optimizer_name(self):
|
|
return (self.client_optimizer.__class__.__name__ if self.client_optimizer else self._config.optimizer_name)
|
|
|
|
def optimizer_params(self):
|
|
return self._config.optimizer_params
|
|
|
|
def optimizer_legacy_fusion(self):
|
|
return self._config.optimizer_legacy_fusion
|
|
|
|
def scheduler_name(self):
|
|
return self._config.scheduler_name
|
|
|
|
def scheduler_params(self):
|
|
return self._config.scheduler_params
|
|
|
|
def quantize_training(self):
|
|
return (
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS]
|
|
[WEIGHT_QUANTIZE_IN_FORWARD_ENABLED],
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_ENABLED],
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_GROUPS],
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS]
|
|
[WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE],
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_CHANGE_RATIO],
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_TYPE],
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_ROUNDING],
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_VERBOSE],
|
|
self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_KERNEL],
|
|
)
|
|
|
|
def zero_optimization(self):
|
|
return self._config.zero_enabled
|
|
|
|
def zero_allow_untested_optimizer(self):
|
|
return self._config.zero_allow_untested_optimizer
|
|
|
|
def zero_force_ds_cpu_optimizer(self):
|
|
return self._config.zero_force_ds_cpu_optimizer
|
|
|
|
def zero_reduce_scatter(self):
|
|
return self._config.zero_config.reduce_scatter
|
|
|
|
def zero_overlap_comm(self):
|
|
return self._config.zero_config.overlap_comm
|
|
|
|
def zero_offload_optimizer(self):
|
|
return self._config.zero_config.offload_optimizer
|
|
|
|
def zero_offload_param(self):
|
|
return self._config.zero_config.offload_param
|
|
|
|
def zero_use_cpu_optimizer(self):
|
|
if self._config.zero_config.offload_optimizer is not None:
|
|
return self._config.zero_config.offload_optimizer.device in [OffloadDeviceEnum.cpu, OffloadDeviceEnum.nvme]
|
|
return False
|
|
|
|
def zero_cpu_offload(self):
|
|
if self._config.zero_config.offload_optimizer is not None:
|
|
return self._config.zero_config.offload_optimizer.device == OffloadDeviceEnum.cpu
|
|
return False
|
|
|
|
def zero_partial_offload(self):
|
|
return getattr(self._config.zero_config.offload_optimizer, "ratio", 1.0)
|
|
|
|
def zero_sub_group_size(self):
|
|
return self._config.zero_config.sub_group_size
|
|
|
|
def zero_optimization_stage(self):
|
|
return self._config.zero_optimization_stage
|
|
|
|
def mics_shard_size(self):
|
|
return self._config.mics_shard_size
|
|
|
|
def zero_reduce_bucket_size(self):
|
|
return self._config.zero_config.reduce_bucket_size
|
|
|
|
def zero_multi_rank_bucket_allreduce(self):
|
|
return self._config.zero_config.use_multi_rank_bucket_allreduce
|
|
|
|
def zero_allgather_bucket_size(self):
|
|
return self._config.zero_config.allgather_bucket_size
|
|
|
|
def zero_optimization_partition_gradients(self):
|
|
return self.zero_optimization_stage() >= ZeroStageEnum.gradients
|
|
|
|
def zero_optimization_partition_weights(self):
|
|
return self.zero_optimization_stage() >= ZeroStageEnum.weights
|
|
|
|
def is_first_weights_partition_group(self):
|
|
ret = True if self.mics_shard_size() < 0 \
|
|
and self.zero_optimization_partition_weights() else False
|
|
if self.mics_shard_size() > 0 and self.global_rank < self.mics_shard_size():
|
|
ret = True
|
|
return ret
|
|
|
|
def zero_contiguous_gradients(self):
|
|
return self._config.zero_config.contiguous_gradients
|
|
|
|
def zero_load_from_fp32_weights(self):
|
|
return self._config.zero_config.load_from_fp32_weights
|
|
|
|
def zero_elastic_checkpoint(self):
|
|
return self._config.zero_config.elastic_checkpoint
|
|
|
|
def zero_max_live_parameters(self):
|
|
return self._config.zero_config.max_live_parameters
|
|
|
|
def zero_max_reuse_distance(self):
|
|
return self._config.zero_config.max_reuse_distance
|
|
|
|
def zero_prefetch_bucket_size(self):
|
|
return self._config.zero_config.prefetch_bucket_size
|
|
|
|
def zero_param_persistence_threshold(self):
|
|
return self._config.zero_config.param_persistence_threshold
|
|
|
|
def zero_model_persistence_threshold(self):
|
|
return self._config.zero_config.model_persistence_threshold
|
|
|
|
def zero_gather_16bit_weights_on_model_save(self):
|
|
return self._config.zero_config.gather_16bit_weights_on_model_save
|
|
|
|
def zero_grad_hooks(self):
|
|
return self._config.zero_config.grad_hooks
|
|
|
|
def zero_legacy_stage1(self):
|
|
return self._config.zero_config.legacy_stage1
|
|
|
|
def zero_ignore_unused_parameters(self):
|
|
return self._config.zero_config.ignore_unused_parameters
|
|
|
|
def graph_harvesting(self):
|
|
return self._config.graph_harvesting
|
|
|
|
def fp16_enabled(self):
|
|
return self._config.fp16_enabled
|
|
|
|
def bfloat16_enabled(self):
|
|
return self._config.bfloat16_enabled
|
|
|
|
def fp16_master_weights_and_gradients(self):
|
|
return self._config.fp16_master_weights_and_gradients
|
|
|
|
def amp_enabled(self):
|
|
return self._config.amp_enabled
|
|
|
|
def amp_params(self):
|
|
return self._config.amp_params
|
|
|
|
def fp16_auto_cast(self):
|
|
return self._config.fp16_auto_cast
|
|
|
|
def loss_scale(self):
|
|
return self._config.loss_scale
|
|
|
|
def gradient_accumulation_steps(self):
|
|
return self._config.gradient_accumulation_steps
|
|
|
|
def use_node_local_storage(self):
|
|
return self._config.use_node_local_storage
|
|
|
|
def load_universal_checkpoint(self):
|
|
return self._config.load_universal_checkpoint
|
|
|
|
@property
|
|
def communication_data_type(self):
|
|
res = self._config.communication_data_type
|
|
if res is not None:
|
|
return res
|
|
|
|
if self.fp16_enabled():
|
|
return torch.float16
|
|
|
|
if self.bfloat16_enabled():
|
|
return torch.bfloat16
|
|
|
|
return torch.float32
|
|
|
|
@communication_data_type.setter
|
|
def communication_data_type(self, value):
|
|
self._config.communication_data_type = value
|
|
|
|
def postscale_gradients(self):
|
|
return not self._config.prescale_gradients
|
|
|
|
def gradient_predivide_factor(self):
|
|
return self._config.gradient_predivide_factor
|
|
|
|
def steps_per_print(self):
|
|
return self._config.steps_per_print
|
|
|
|
def zero_allgather_partitions(self):
|
|
return self._config.zero_config.allgather_partitions
|
|
|
|
def zero_round_robin_gradients(self):
|
|
return self._config.zero_config.round_robin_gradients
|
|
|
|
def zero_hpz_partition_size(self):
|
|
return self._config.zero_config.zero_hpz_partition_size
|
|
|
|
def zero_quantized_weights(self):
|
|
return self._config.zero_config.zero_quantized_weights
|
|
|
|
def zero_quantized_nontrainable_weights(self):
|
|
return self._config.zero_config.zero_quantized_nontrainable_weights
|
|
|
|
def zero_quantized_gradients(self):
|
|
return self._config.zero_config.zero_quantized_gradients
|
|
|
|
def dump_state(self):
|
|
return self._config.dump_state
|
|
|
|
def gradient_clipping(self):
|
|
return self._config.gradient_clipping
|
|
|
|
def dynamic_loss_scale(self):
|
|
return self._config.loss_scale == 0
|
|
|
|
def initial_dynamic_scale(self):
|
|
return self._config.initial_dynamic_scale
|
|
|
|
def dynamic_loss_scale_args(self):
|
|
return self._config.dynamic_loss_scale_args
|
|
|
|
def swap_tensor_config(self):
|
|
return self._config.swap_tensor_config
|
|
|
|
def aio_config(self):
|
|
return self._config.aio_config
|
|
|
|
def get_data_types(self):
|
|
model_dtype = torch.float32
|
|
if self.fp16_enabled():
|
|
model_dtype = torch.float16
|
|
elif self.bfloat16_enabled():
|
|
model_dtype = torch.bfloat16
|
|
|
|
if self._config.grad_accum_dtype is None:
|
|
if model_dtype == torch.bfloat16 and not self.zero_optimization():
|
|
grad_accum_dtype = torch.float32
|
|
else:
|
|
grad_accum_dtype = model_dtype
|
|
else:
|
|
grad_accum_dtype = DtypeEnum(self._config.grad_accum_dtype).value
|
|
|
|
return (model_dtype, grad_accum_dtype)
|
|
|
|
def _optimizer_has_ckpt_event_prologue(self):
|
|
return self.optimizer is not None and hasattr(self.optimizer, 'checkpoint_event_prologue')
|
|
|
|
def _optimizer_has_ckpt_event_epilogue(self):
|
|
return self.optimizer is not None and hasattr(self.optimizer, 'checkpoint_event_epilogue')
|
|
|
|
def _configure_lr_scheduler(self, client_lr_scheduler):
|
|
# First check for scheduler in json configuration
|
|
lr_scheduler = self._scheduler_from_config(self.optimizer)
|
|
if lr_scheduler:
|
|
log_dist(f"DeepSpeed using configured LR scheduler = {self.scheduler_name()}", ranks=[0])
|
|
self.lr_scheduler = lr_scheduler
|
|
else:
|
|
if isinstance(client_lr_scheduler, Callable):
|
|
log_dist('DeepSpeed using client callable to create LR scheduler', ranks=[0])
|
|
self.lr_scheduler = client_lr_scheduler(self.basic_optimizer)
|
|
else:
|
|
log_dist('DeepSpeed using client LR scheduler', ranks=[0])
|
|
self.lr_scheduler = client_lr_scheduler
|
|
|
|
log_dist(f'DeepSpeed LR Scheduler = {self.lr_scheduler}', ranks=[0])
|
|
|
|
def _configure_checkpointing(self, dist_init_required):
|
|
self.checkpoint_engine = TorchCheckpointEngine()
|
|
|
|
if self._config is not None and self._config.nebula_config.enabled:
|
|
try:
|
|
from deepspeed.runtime.checkpoint_engine.nebula_checkpoint_engine import \
|
|
NebulaCheckpointEngine
|
|
self.checkpoint_engine = NebulaCheckpointEngine(config_params=self._config.nebula_config)
|
|
except ImportError as err:
|
|
logger.error(f"No torch_nebula was found! Will fall back to torch.save. Details: {err}")
|
|
self.checkpoint_engine = TorchCheckpointEngine()
|
|
|
|
dp_rank = groups._get_sequence_data_parallel_rank()
|
|
|
|
rank = self.local_rank if self.use_node_local_storage() else dp_rank
|
|
|
|
# only the first data parallel process needs to store the model checkpoint
|
|
# if you want to use node local storage this must be done by rank 0 on each
|
|
# node
|
|
self.save_non_zero_checkpoint = (rank == 0) or (self.zero_optimization_partition_weights()
|
|
and self.is_first_weights_partition_group())
|
|
|
|
if self.zero_optimization() or self.bfloat16_enabled():
|
|
param_rank = dist.get_rank(group=self.optimizer.zp_process_group)
|
|
|
|
# Only the first parameter parallel process needs to store the
|
|
# optimizer state checkpoints for zero
|
|
self.save_zero_checkpoint = param_rank == dp_rank
|
|
|
|
def _scheduler_from_config(self, optimizer):
|
|
scheduler_name = self.scheduler_name()
|
|
if scheduler_name is not None:
|
|
if hasattr(lr_schedules, scheduler_name):
|
|
scheduler = getattr(lr_schedules, scheduler_name)
|
|
else:
|
|
assert hasattr(torch.optim.lr_scheduler,
|
|
scheduler_name), f"DeepSpeed does not recognize LR scheduler {scheduler_name}"
|
|
|
|
scheduler = getattr(torch.optim.lr_scheduler, scheduler_name)
|
|
|
|
scheduler_params = self.scheduler_params()
|
|
instantiated_scheduler = scheduler(optimizer, **scheduler_params)
|
|
return instantiated_scheduler
|
|
else:
|
|
return None
|
|
|
|
def _set_distributed_vars(self, args):
|
|
device_rank = args.device_rank if args is not None and hasattr(args, 'device_rank') else self.local_rank
|
|
if device_rank >= 0:
|
|
get_accelerator().set_device(device_rank)
|
|
self.device = torch.device(get_accelerator().device_name(), device_rank)
|
|
self.world_size = dist.get_world_size()
|
|
self.global_rank = dist.get_rank()
|
|
else:
|
|
self.world_size = 1
|
|
self.global_rank = 0
|
|
self.device = torch.device(get_accelerator().device_name())
|
|
|
|
# Configure based on command line arguments
|
|
def _configure_with_arguments(self, args, mpu):
|
|
# After the distributed backend is initialized we are guaranteed the LOCAL_RANK
|
|
# environment variable is set. We must align args.local_rank to this value for
|
|
# backwards compatibility with scripts relying on [args|self].local_rank containing
|
|
# the correct local rank info. _do_args_sanity_check will ensure this is the case.
|
|
|
|
if "OMPI_COMM_WORLD_LOCAL_RANK" in os.environ:
|
|
ompi_local_rank = os.environ.get("OMPI_COMM_WORLD_LOCAL_RANK")
|
|
local_rank = os.environ.get('LOCAL_RANK', ompi_local_rank)
|
|
assert ompi_local_rank == local_rank, f"LOCAL_RANK ({local_rank}) != OMPI_COMM_WORLD_LOCAL_RANK ({ompi_local_rank}), " \
|
|
"not sure how to proceed as we're seeing conflicting local rank info."
|
|
os.environ['LOCAL_RANK'] = local_rank
|
|
|
|
self.local_rank = int(os.environ['LOCAL_RANK'])
|
|
if hasattr(args, 'local_rank'):
|
|
args.local_rank = self.local_rank
|
|
|
|
# Validate command line arguments
|
|
def _do_args_sanity_check(self, args):
|
|
assert "LOCAL_RANK" in os.environ or "OMPI_COMM_WORLD_LOCAL_RANK" in os.environ, "DeepSpeed requires the LOCAL_RANK environment " \
|
|
"variable, it is set by the deepspeed launcher, deepspeed.init_distributed, or the torch's launcher. If using a " \
|
|
"different launcher please ensure LOCAL_RANK is set prior to initializing deepspeed."
|
|
|
|
if hasattr(args, 'local_rank') and args.local_rank is not None:
|
|
assert isinstance(args.local_rank,
|
|
int), f"args.local_rank of {args.local_rank} is an unknown type {type(args.local_rank)}"
|
|
if args.local_rank >= 0:
|
|
env_local_rank = int(os.environ.get("LOCAL_RANK"))
|
|
assert (
|
|
env_local_rank == args.local_rank
|
|
), f"Mismatch in local rank setting, args.local_rank={args.local_rank} but env['LOCAL_RANK']={env_local_rank}."
|
|
|
|
def _is_supported_optimizer(self, optimizer_name):
|
|
return (optimizer_name in DEEPSPEED_OPTIMIZERS or getattr(torch.optim, optimizer_name, None) is not None)
|
|
|
|
def _supported_optims(self):
|
|
FairseqOptimizer = None
|
|
try:
|
|
from fairseq.optim.fairseq_optimizer import FairseqOptimizer
|
|
except ImportError:
|
|
pass
|
|
|
|
expected_optim_types = [Optimizer]
|
|
if FairseqOptimizer:
|
|
# fairseq optims are not torch.optim objects
|
|
expected_optim_types.append(FairseqOptimizer)
|
|
return expected_optim_types
|
|
|
|
# Validate configuration based on command line arguments
|
|
def _do_sanity_check(self):
|
|
expected_optim_types = self._supported_optims()
|
|
expected_optim_types += [type(None), Callable]
|
|
assert isinstance(self.client_optimizer, tuple(expected_optim_types)), \
|
|
f'Client Optimizer is of unexpected type {type(self.client_optimizer)}'
|
|
|
|
if not self.client_optimizer:
|
|
if self.optimizer_name() is not None:
|
|
assert self._is_supported_optimizer(
|
|
self.optimizer_name()), "{} is not a supported DeepSpeed Optimizer".format(self.optimizer_name())
|
|
|
|
if (self.optimizer_name() == LAMB_OPTIMIZER or self.optimizer_name() == ONEBIT_LAMB_OPTIMIZER):
|
|
assert (self.dynamic_loss_scale()), "DeepSpeed {} optimizer requires dynamic loss scaling".format(
|
|
self.optimizer_name())
|
|
|
|
# Detect invalid combinations of client optimizer and client scheduler
|
|
if isinstance(self.client_lr_scheduler, _LRScheduler):
|
|
assert isinstance(self.client_optimizer, Optimizer), \
|
|
f'Client Optimizer (type = {type(self.client_optimizer)} is not instantiated but Client LR Scheduler is instantiated'
|
|
|
|
def _broadcast_model(self):
|
|
|
|
def is_replicated(p):
|
|
if hasattr(p, "ds_status") and p.ds_status is not ZeroParamStatus.AVAILABLE:
|
|
return False
|
|
return True
|
|
|
|
for p in self.module.parameters():
|
|
# Broadcast the model for different parameters
|
|
if is_moe_param(p):
|
|
if torch.is_tensor(p) and is_replicated(p):
|
|
dist.broadcast(p,
|
|
groups._get_expert_broadcast_src_rank(p.group_name),
|
|
group=self.expert_data_parallel_group[p.group_name])
|
|
else:
|
|
if torch.is_tensor(p) and is_replicated(p):
|
|
dist.broadcast(p, groups._get_broadcast_src_rank(), group=self.seq_data_parallel_group)
|
|
|
|
@staticmethod
|
|
def __check_params(model: Module, dtype: torch.dtype) -> None:
|
|
return
|
|
if not all(param.dtype == dtype for param in model.parameters()) and dist.get_rank() == 0:
|
|
raise ValueError(f"{dtype} is enabled but the following parameters have dtype that is "
|
|
f"not {dtype}: "
|
|
f"{[(n, p.dtype) for n, p in model.named_parameters() if p.dtype != dtype]}")
|
|
|
|
def _set_client_model(self, model):
|
|
# register client model in _modules so that nn.module methods work correctly
|
|
modules = self.__dict__.get('_modules')
|
|
modules['module'] = model
|
|
# register module attribute in engine but avoid getattr
|
|
self.__dict__['module'] = model
|
|
|
|
def _configure_distributed_model(self, model):
|
|
self._set_client_model(model)
|
|
is_zero_init_model = self.zero_optimization_partition_weights() and any(
|
|
[hasattr(param, "ds_id") for param in self.module.parameters()])
|
|
|
|
if self.fp16_enabled():
|
|
if is_zero_init_model:
|
|
self.__check_params(self.module, torch.half)
|
|
self.module.half()
|
|
elif self.bfloat16_enabled():
|
|
if is_zero_init_model:
|
|
self.__check_params(self.module, torch.bfloat16)
|
|
self.module.bfloat16()
|
|
else:
|
|
self.__check_params(self.module, torch.float)
|
|
|
|
# zero.Init() handles device placement of model
|
|
if not (self.dont_change_device or is_zero_init_model):
|
|
self.module.to(self.device)
|
|
|
|
# MoE related initialization
|
|
for _, module in self.module.named_modules():
|
|
if isinstance(module, MoE):
|
|
self.has_moe_layers = True
|
|
self.num_experts.append(module.num_experts)
|
|
|
|
if self.has_moe_layers:
|
|
for _, module in self.module.named_modules():
|
|
if isinstance(module, TopKGate):
|
|
self.gate_modules.append(module)
|
|
if self.wall_clock_breakdown():
|
|
module.wall_clock_breakdown = True
|
|
if isinstance(module, MOELayer):
|
|
self.moe_layers.append(module)
|
|
if self.wall_clock_breakdown():
|
|
module.wall_clock_breakdown = True
|
|
|
|
# Pass the mpu from here to groups. For subsequent use, just query groups
|
|
if self.mpu is not None:
|
|
groups.mpu = self.mpu
|
|
|
|
# Set deepspeed parallelism spec. for the model including expert parallelism
|
|
for _, module in self.module.named_modules():
|
|
if hasattr(module, 'set_deepspeed_parallelism'):
|
|
module.set_deepspeed_parallelism(self._config.use_data_before_expert_parallel_)
|
|
|
|
# Query the groups module to get information about various parallel groups
|
|
self.local_all_to_all_group = None
|
|
if self.zero_quantized_gradients():
|
|
log_dist("Using quantized gradients", ranks=[0])
|
|
self.local_all_to_all_group = groups._get_local_all_to_all_group()
|
|
self.data_parallel_group = groups._get_data_parallel_group()
|
|
self.dp_world_size = groups._get_data_parallel_world_size()
|
|
self.zp_world_size = zp_manager.zp_size
|
|
self.seq_data_parallel_group = groups._get_sequence_data_parallel_group()
|
|
self.seq_dp_world_size = groups._get_sequence_data_parallel_world_size()
|
|
self.mp_world_size = groups._get_model_parallel_world_size()
|
|
self.expert_parallel_group = groups._get_expert_parallel_group_dict()
|
|
self.expert_data_parallel_group = groups._get_expert_data_parallel_group_dict()
|
|
self.sequence_parallel_size = groups._get_sequence_parallel_world_size()
|
|
if self.sequence_parallel_size > 1:
|
|
self.communication_data_type = self._config.seq_parallel_communication_data_type
|
|
|
|
if not (self.amp_enabled() or is_zero_init_model):
|
|
self._broadcast_model()
|
|
|
|
# check if parameters are duplicated in optimizer param_groups
|
|
def _check_for_duplicates(self, optimizer):
|
|
for name, param in self.module.named_parameters():
|
|
param_id = id(param)
|
|
|
|
def ids_list(group):
|
|
return [id(param) for param in group]
|
|
|
|
occurrence = sum([
|
|
ids_list(group['params']).count(param_id) if param_id in ids_list(group['params']) else 0
|
|
for group in optimizer.param_groups
|
|
])
|
|
assert occurrence <= 1, f"Parameter with name: {name} occurs multiple times in optimizer.param_groups. Make sure it only appears once to prevent undefined behavior."
|
|
|
|
def _do_optimizer_sanity_check(self, basic_optimizer):
|
|
model_dtype, grad_accum_dtype = self.get_data_types()
|
|
zero_enabled = self.zero_optimization()
|
|
amp_enabled = self.amp_enabled()
|
|
# config based assertions
|
|
assert (
|
|
not (amp_enabled and zero_enabled)
|
|
), "Amp and ZeRO are not currently compatible, please use (legacy) fp16 mode which performs similar to amp opt_mode=O2"
|
|
if zero_enabled:
|
|
if not is_zero_supported_optimizer(basic_optimizer):
|
|
assert (
|
|
self.zero_allow_untested_optimizer()
|
|
), 'You are using an untested ZeRO Optimizer. Please add <"zero_allow_untested_optimizer": true> in the configuration file to use it.'
|
|
|
|
if self.global_rank == 0:
|
|
logger.warning("**** You are using ZeRO with an untested optimizer, proceed with caution *****")
|
|
if model_dtype == torch.bfloat16 and grad_accum_dtype == torch.float32 and self.zero_optimization_stage(
|
|
) == 1 and not self.zero_cpu_offload():
|
|
return BFLOAT16
|
|
return ZERO_OPTIMIZATION
|
|
elif amp_enabled:
|
|
if model_dtype != grad_accum_dtype:
|
|
raise NotImplementedError(
|
|
"Model data type and gradient accumulation data type must be equal to use Amp")
|
|
if model_dtype == torch.bfloat16 or model_dtype == torch.float16:
|
|
raise NotImplementedError("Cannot enable both amp with (legacy) fp16 or bfloat16 mode")
|
|
try:
|
|
logger.info("Initializing Apex amp from: {}".format(amp.__path__))
|
|
except NameError:
|
|
# If apex/amp is available it will be imported above
|
|
raise RuntimeError("Unable to import apex/amp, please make sure it is installed")
|
|
return AMP
|
|
# data type checks
|
|
elif model_dtype == grad_accum_dtype:
|
|
if model_dtype == torch.bfloat16:
|
|
if self.pipeline_parallelism:
|
|
logger.warning(
|
|
"**** BF16 gradient accumulation is not safe numerically with large number of accumulation steps, proceed with caution *****"
|
|
)
|
|
return BFLOAT16
|
|
else:
|
|
raise NotImplementedError(
|
|
"Bfloat16 wrapper must use a gradient accumulation type of fp32, enable ZeRO to use Bfloat16 gradient accumulation"
|
|
)
|
|
if model_dtype == torch.float16:
|
|
return FP16
|
|
# else optimizer_wrapper = None
|
|
elif model_dtype == torch.bfloat16 and grad_accum_dtype == torch.float32:
|
|
return BFLOAT16
|
|
else:
|
|
raise NotImplementedError("unsupported mix of model dtype and gradient accumulation type")
|
|
|
|
return None
|
|
|
|
# Configure optimizer
|
|
def _configure_optimizer(self, client_optimizer, model_parameters):
|
|
if client_optimizer is None:
|
|
basic_optimizer = self._configure_basic_optimizer(model_parameters)
|
|
log_dist(f"Using DeepSpeed Optimizer param name {self.optimizer_name()} as basic optimizer", ranks=[0])
|
|
else:
|
|
if isinstance(client_optimizer, tuple(self._supported_optims())):
|
|
basic_optimizer = client_optimizer
|
|
log_dist('Using client Optimizer as basic optimizer', ranks=[0])
|
|
else:
|
|
basic_optimizer = client_optimizer(model_parameters)
|
|
log_dist('Using client callable to create basic optimizer', ranks=[0])
|
|
|
|
if self.zero_use_cpu_optimizer() and not isinstance(basic_optimizer, deepspeed.ops.adam.DeepSpeedCPUAdam):
|
|
if self.zero_force_ds_cpu_optimizer():
|
|
msg = f'You are using ZeRO-Offload with a client provided optimizer ({type(basic_optimizer)}) which in most cases will yield poor performance. Please either use deepspeed.ops.adam.DeepSpeedCPUAdam or set an optimizer in your ds-config (https://www.deepspeed.ai/docs/config-json/#optimizer-parameters). If you really want to use a custom optimizer w. ZeRO-Offload and understand the performance impacts you can also set <"zero_force_ds_cpu_optimizer": false> in your configuration file.'
|
|
raise ZeRORuntimeException(msg)
|
|
|
|
basic_optimizer.param_groups[:] = [pg for pg in basic_optimizer.param_groups if len(pg["params"]) != 0]
|
|
log_dist("Removing param_group that has no 'params' in the basic Optimizer", ranks=[0])
|
|
|
|
self._check_for_duplicates(basic_optimizer)
|
|
|
|
self.basic_optimizer = basic_optimizer
|
|
log_dist("DeepSpeed Basic Optimizer = {}".format(basic_optimizer.__class__.__name__), ranks=[0])
|
|
|
|
optimizer_wrapper = self._do_optimizer_sanity_check(basic_optimizer)
|
|
|
|
if optimizer_wrapper == ZERO_OPTIMIZATION:
|
|
self.optimizer = self._configure_zero_optimizer(basic_optimizer)
|
|
elif optimizer_wrapper == AMP:
|
|
amp_params = self.amp_params()
|
|
log_dist(f"Initializing AMP with these params: {amp_params}", ranks=[0])
|
|
model, self.optimizer = amp.initialize(self.module, basic_optimizer, **amp_params)
|
|
self._set_client_model(model)
|
|
self._broadcast_model()
|
|
# TODO: maybe need to broadcast experts differently?
|
|
elif optimizer_wrapper == FP16:
|
|
self.optimizer = self._configure_fp16_optimizer(basic_optimizer)
|
|
elif optimizer_wrapper == BFLOAT16:
|
|
self.optimizer = self._configure_bf16_optimizer(basic_optimizer)
|
|
else:
|
|
self.optimizer = basic_optimizer
|
|
|
|
log_dist("DeepSpeed Final Optimizer = {}".format(self.optimizer_name()), ranks=[0])
|
|
|
|
self.compression_scheduler = self._configure_compression_scheduler()
|
|
self.quantizer = self._configure_quantization()
|
|
|
|
def _configure_basic_optimizer(self, model_parameters):
|
|
optimizer_parameters = self.optimizer_params()
|
|
if optimizer_parameters is None:
|
|
optimizer_parameters = {}
|
|
# print(optimizer_parameters.keys())
|
|
if "max_grad_norm" in optimizer_parameters.keys():
|
|
raise ValueError(
|
|
"'max_grad_norm' is not supported as an optimizer parameter, please switch to using the deepspeed parameter 'gradient_clipping' see: https://www.deepspeed.ai/docs/config-json/#gradient-clipping for more details"
|
|
)
|
|
|
|
if self.optimizer_name() in [ADAM_OPTIMIZER, ADAMW_OPTIMIZER]:
|
|
torch_adam = optimizer_parameters.pop(TORCH_ADAM_PARAM, False)
|
|
adam_w_mode = optimizer_parameters.pop(ADAM_W_MODE, ADAM_W_MODE_DEFAULT)
|
|
|
|
# Optimizer name of Adam forces AdamW logic unless adam_w_mode is explicitly set
|
|
effective_adam_w_mode = self.optimizer_name() == ADAMW_OPTIMIZER or adam_w_mode
|
|
|
|
if torch_adam:
|
|
if not effective_adam_w_mode:
|
|
optimizer = torch.optim.Adam(model_parameters, **optimizer_parameters)
|
|
else:
|
|
optimizer = torch.optim.AdamW(model_parameters, **optimizer_parameters)
|
|
else:
|
|
if self.zero_use_cpu_optimizer():
|
|
from deepspeed.ops.adam import DeepSpeedCPUAdam
|
|
optimizer = DeepSpeedCPUAdam(model_parameters,
|
|
**optimizer_parameters,
|
|
adamw_mode=effective_adam_w_mode)
|
|
else:
|
|
from deepspeed.ops.adam import FusedAdam
|
|
|
|
optimizer = FusedAdam(
|
|
model_parameters,
|
|
**optimizer_parameters,
|
|
adam_w_mode=effective_adam_w_mode,
|
|
)
|
|
|
|
elif self.optimizer_name() == ADAGRAD_OPTIMIZER:
|
|
if self.zero_use_cpu_optimizer():
|
|
from deepspeed.ops.adagrad import DeepSpeedCPUAdagrad
|
|
optimizer = DeepSpeedCPUAdagrad(model_parameters, **optimizer_parameters)
|
|
else:
|
|
optimizer = torch.optim.Adagrad(model_parameters, **optimizer_parameters)
|
|
elif self.optimizer_name() == LAMB_OPTIMIZER:
|
|
from deepspeed.ops.lamb import FusedLamb
|
|
|
|
optimizer = FusedLamb(model_parameters, **optimizer_parameters)
|
|
elif self.optimizer_name() == ONEBIT_ADAM_OPTIMIZER:
|
|
assert not self.zero_optimization(), "1bit-Adam is not compatible with ZeRO"
|
|
from deepspeed.runtime.fp16.onebit.adam import OnebitAdam
|
|
|
|
optimizer = OnebitAdam(model_parameters, self, **optimizer_parameters)
|
|
if not self.fp16_enabled():
|
|
logger.warning(f"Currently the convergence of 1-bit Adam is only verified under FP16")
|
|
elif self.optimizer_name() == ZERO_ONE_ADAM_OPTIMIZER:
|
|
assert not self.zero_optimization(), "0/1 Adam is not compatible with ZeRO"
|
|
from deepspeed.runtime.fp16.onebit.zoadam import ZeroOneAdam
|
|
|
|
optimizer = ZeroOneAdam(model_parameters, self, **optimizer_parameters)
|
|
if not self.fp16_enabled():
|
|
logger.warning(f'Currently the convergence of 0/1 Adam is only verified under FP16')
|
|
elif self.optimizer_name() == ONEBIT_LAMB_OPTIMIZER:
|
|
assert not self.zero_optimization(), "1bit-Lamb is not compatible with ZeRO"
|
|
from deepspeed.runtime.fp16.onebit.lamb import OnebitLamb
|
|
|
|
optimizer = OnebitLamb(model_parameters, self, **optimizer_parameters)
|
|
if not self.fp16_enabled():
|
|
logger.warning(f"Currently the convergence of 1-bit Lamb is only verified under FP16")
|
|
elif self.optimizer_name() == LION_OPTIMIZER:
|
|
if self.zero_use_cpu_optimizer():
|
|
from deepspeed.ops.lion import DeepSpeedCPULion
|
|
optimizer = DeepSpeedCPULion(model_parameters, **optimizer_parameters)
|
|
else:
|
|
from deepspeed.ops.lion import FusedLion
|
|
optimizer = FusedLion(model_parameters, **optimizer_parameters)
|
|
elif self.optimizer_name() == MUADAM_OPTIMIZER:
|
|
try:
|
|
from mup import MuAdam
|
|
except ImportError:
|
|
logger.error(f"Install mup to use MuAdam optimizer")
|
|
optimizer = MuAdam(model_parameters, **optimizer_parameters)
|
|
elif self.optimizer_name() == MUADAMW_OPTIMIZER:
|
|
try:
|
|
from mup import MuAdamW
|
|
except ImportError:
|
|
logger.error(f"Install mup to use MuAdamW optimizer")
|
|
optimizer = MuAdamW(model_parameters, **optimizer_parameters)
|
|
elif self.optimizer_name() == MUSGD_OPTIMIZER:
|
|
try:
|
|
from mup import MuSGD
|
|
except ImportError:
|
|
logger.error(f"Install mup to use MuSGD optimizer")
|
|
optimizer = MuSGD(model_parameters, **optimizer_parameters)
|
|
else:
|
|
torch_optimizer = getattr(torch.optim, self.optimizer_name())
|
|
optimizer = torch_optimizer(model_parameters, **optimizer_parameters)
|
|
return optimizer
|
|
|
|
def _configure_compression_scheduler(self):
|
|
return compression_scheduler(self.module, self._config.compression_config)
|
|
|
|
def _configure_random_ltd_scheduler(self, configs):
|
|
return RandomLTDScheduler(configs)
|
|
|
|
def _configure_quantization(self):
|
|
(
|
|
quantize_weight_in_forward,
|
|
quantize_enabled,
|
|
q_groups,
|
|
q_mixed_fp16,
|
|
q_change_ratio,
|
|
q_type,
|
|
q_rounding,
|
|
q_verbose,
|
|
use_quantizer_kernel,
|
|
) = self.quantize_training()
|
|
if quantize_enabled and not quantize_weight_in_forward:
|
|
assert self.fp16_enabled(
|
|
), "MoQ (quantize in optimization step) weight quantization is only supported for FP16"
|
|
quantizer = None
|
|
if quantize_enabled and not quantize_weight_in_forward:
|
|
from deepspeed.runtime.quantize import Quantizer
|
|
|
|
quantizer = Quantizer(
|
|
q_groups,
|
|
q_mixed_fp16,
|
|
q_change_ratio,
|
|
q_type,
|
|
q_rounding,
|
|
q_verbose,
|
|
self.eigenvalue_enabled(),
|
|
use_quantizer_kernel,
|
|
self.eigenvalue_layer_num() if self.eigenvalue_enabled() else 0,
|
|
)
|
|
return quantizer
|
|
|
|
def _configure_fp16_optimizer(self, optimizer):
|
|
initial_dynamic_scale = self.initial_dynamic_scale()
|
|
dynamic_loss_args = self.dynamic_loss_scale_args()
|
|
clip_grad = self.gradient_clipping()
|
|
if APEX_INSTALLED:
|
|
fused_opts = (apex.optimizers.FusedAdam, FusedAdam)
|
|
else:
|
|
fused_opts = FusedAdam
|
|
if isinstance(optimizer, fused_opts) \
|
|
or self.optimizer_name() in [ONEBIT_ADAM_OPTIMIZER, ZERO_ONE_ADAM_OPTIMIZER]:
|
|
if self.dynamic_loss_scale():
|
|
log_dist(f'Creating fp16 optimizer with dynamic loss scale', ranks=[0])
|
|
timers = self.timers if self.wall_clock_breakdown() else NoopTimer()
|
|
optimizer = FP16_Optimizer(
|
|
optimizer,
|
|
deepspeed=self,
|
|
dynamic_loss_scale=True,
|
|
initial_dynamic_scale=initial_dynamic_scale,
|
|
dynamic_loss_args=dynamic_loss_args,
|
|
mpu=self.mpu,
|
|
clip_grad=clip_grad,
|
|
fused_adam_legacy=self.optimizer_legacy_fusion(),
|
|
timers=timers,
|
|
has_moe_layers=self.has_moe_layers,
|
|
)
|
|
else:
|
|
log_dist(f'Creating fp16 optimizer with static loss scale: {self.loss_scale()}', ranks=[0])
|
|
optimizer = FP16_Optimizer(
|
|
optimizer,
|
|
deepspeed=self,
|
|
static_loss_scale=self.loss_scale(),
|
|
mpu=self.mpu,
|
|
clip_grad=clip_grad,
|
|
fused_adam_legacy=self.optimizer_legacy_fusion(),
|
|
has_moe_layers=self.has_moe_layers,
|
|
)
|
|
else:
|
|
log_dist(f'Creating fp16 unfused optimizer with dynamic loss scale', ranks=[0])
|
|
optimizer = FP16_UnfusedOptimizer(
|
|
optimizer,
|
|
deepspeed=self,
|
|
static_loss_scale=self.loss_scale(),
|
|
dynamic_loss_scale=self.dynamic_loss_scale(),
|
|
dynamic_loss_args=dynamic_loss_args,
|
|
mpu=self.mpu,
|
|
clip_grad=clip_grad,
|
|
fused_lamb_legacy=self.optimizer_name() == LAMB_OPTIMIZER,
|
|
)
|
|
|
|
return optimizer
|
|
|
|
def _configure_bf16_optimizer(self, optimizer):
|
|
clip_grad = self.gradient_clipping()
|
|
|
|
if optimizer is None:
|
|
optimizer = DummyOptim(list(self.module.parameters()))
|
|
|
|
log_dist('Creating BF16 optimizer', ranks=[0])
|
|
|
|
timers = self.timers if self.wall_clock_breakdown() else NoopTimer()
|
|
optimizer = BF16_Optimizer(optimizer,
|
|
self.param_names,
|
|
mpu=self.mpu,
|
|
clip_grad=clip_grad,
|
|
allgather_bucket_size=self.zero_allgather_bucket_size(),
|
|
dp_process_group=self.seq_data_parallel_group,
|
|
timers=timers,
|
|
grad_acc_dtype=self.get_data_types()[1],
|
|
graph_harvesting=self.graph_harvesting())
|
|
|
|
return optimizer
|
|
|
|
def _configure_zero_optimizer(self, optimizer):
|
|
zero_stage = self.zero_optimization_stage()
|
|
|
|
mics_shard_size = self.mics_shard_size()
|
|
model_dtype, gradient_accumulation_dtype = self.get_data_types()
|
|
|
|
timers = self.timers if self.wall_clock_breakdown() else NoopTimer()
|
|
|
|
if optimizer is None:
|
|
optimizer = DummyOptim(list(self.module.parameters()))
|
|
|
|
if self.zero_legacy_stage1():
|
|
raise Exception(
|
|
"The deprecated version of ZeRO Stage 1 is not supported in deepspeed >= 0.5.9. Please downgrade to a version less than 0.5.9 if you need to use this deprecated version of ZeRO."
|
|
)
|
|
|
|
if zero_stage <= ZeroStageEnum.gradients:
|
|
overlap_comm = self.zero_overlap_comm()
|
|
contiguous_gradients = self.zero_contiguous_gradients()
|
|
round_robin_gradients = self.zero_round_robin_gradients()
|
|
assert not isinstance(optimizer, DummyOptim), "zero stage {} requires an optimizer".format(zero_stage)
|
|
|
|
log_dist(f'Creating {model_dtype} ZeRO stage {zero_stage} optimizer', ranks=[0])
|
|
# Overlap and contiguous grads are meaningless in stage 1 and are ignored
|
|
if zero_stage == ZeroStageEnum.optimizer_states:
|
|
overlap_comm = False
|
|
round_robin_gradients = False
|
|
# Non-MoE requires contiguous grads to be disabled w. stage 1
|
|
if not self.has_moe_layers:
|
|
contiguous_gradients = False
|
|
|
|
if isinstance(self.module, PipelineModule):
|
|
if overlap_comm:
|
|
logger.warning("Pipeline parallelism does not support overlapped communication, will be disabled.")
|
|
overlap_comm = False
|
|
optimizer = DeepSpeedZeroOptimizer(
|
|
optimizer,
|
|
self.param_names,
|
|
timers=timers,
|
|
static_loss_scale=self.loss_scale(),
|
|
dynamic_loss_scale=self.dynamic_loss_scale(),
|
|
dynamic_loss_args=self.dynamic_loss_scale_args(),
|
|
clip_grad=self.gradient_clipping(),
|
|
contiguous_gradients=contiguous_gradients,
|
|
reduce_bucket_size=self.zero_reduce_bucket_size(),
|
|
use_multi_rank_bucket_allreduce=self.zero_multi_rank_bucket_allreduce(),
|
|
allgather_bucket_size=self.zero_allgather_bucket_size(),
|
|
dp_process_group=self.seq_data_parallel_group,
|
|
expert_parallel_group=self.expert_parallel_group if self.has_moe_layers else None,
|
|
expert_data_parallel_group=self.expert_data_parallel_group if self.has_moe_layers else None,
|
|
reduce_scatter=self.zero_reduce_scatter(),
|
|
overlap_comm=overlap_comm,
|
|
offload_optimizer_config=self.zero_offload_optimizer(),
|
|
mpu=self.mpu,
|
|
postscale_gradients=self.postscale_gradients(),
|
|
gradient_predivide_factor=self.gradient_predivide_factor(),
|
|
gradient_accumulation_steps=self.gradient_accumulation_steps(),
|
|
ignore_unused_parameters=self.zero_ignore_unused_parameters(),
|
|
partition_grads=zero_stage == ZeroStageEnum.gradients,
|
|
round_robin_gradients=round_robin_gradients,
|
|
has_moe_layers=self.has_moe_layers,
|
|
fp16_master_weights_and_gradients=self.fp16_master_weights_and_gradients(),
|
|
gradient_accumulation_dtype=gradient_accumulation_dtype,
|
|
communication_data_type=self.communication_data_type,
|
|
elastic_checkpoint=self.zero_elastic_checkpoint())
|
|
|
|
elif zero_stage == ZeroStageEnum.weights:
|
|
assert not self.has_moe_layers, "MoE not supported with Stage 3"
|
|
if isinstance(optimizer, DummyOptim):
|
|
log_dist("Creating ZeRO Offload", ranks=[0])
|
|
zero_param_parallel_group = groups._get_zero_param_intra_parallel_group()
|
|
if self.zero_hpz_partition_size() > 1 and zero_param_parallel_group is None:
|
|
self._set_zero_group_parallelism()
|
|
zero_param_parallel_group = groups._get_zero_param_intra_parallel_group()
|
|
optimizer = DeepSpeedZeRoOffload(
|
|
self.module,
|
|
timers=timers,
|
|
ds_config=self.config,
|
|
overlap_comm=self.zero_overlap_comm(),
|
|
prefetch_bucket_size=self.zero_prefetch_bucket_size(),
|
|
max_reuse_distance=self.zero_max_reuse_distance(),
|
|
max_live_parameters=self.zero_max_live_parameters(),
|
|
param_persistence_threshold=self.zero_param_persistence_threshold(),
|
|
model_persistence_threshold=self.zero_model_persistence_threshold(),
|
|
offload_param_config=self.zero_offload_param(),
|
|
mpu=self.mpu,
|
|
zero_param_parallel_group=zero_param_parallel_group,
|
|
zero_quantized_weights=self.zero_quantized_weights(),
|
|
zero_quantized_nontrainable_weights=self.zero_quantized_nontrainable_weights(),
|
|
)
|
|
else:
|
|
log_dist(
|
|
f'Creating fp16 ZeRO stage {zero_stage} optimizer,'
|
|
f' MiCS is enabled {mics_shard_size>0},'
|
|
f' Hierarchical params gather {self._config.mics_hierarchial_params_gather}',
|
|
ranks=[0])
|
|
if mics_shard_size > 0:
|
|
return self._return_mics_optimizer(optimizer, timers)
|
|
|
|
log_dist(f'Creating {model_dtype} ZeRO stage {zero_stage} optimizer', ranks=[0])
|
|
from deepspeed.runtime.zero.stage3 import DeepSpeedZeroOptimizer_Stage3
|
|
optimizer = DeepSpeedZeroOptimizer_Stage3(
|
|
self.module,
|
|
optimizer,
|
|
timers=timers,
|
|
ds_config=self.config,
|
|
static_loss_scale=self.loss_scale(),
|
|
dynamic_loss_scale=self.dynamic_loss_scale(),
|
|
dynamic_loss_args=self.dynamic_loss_scale_args(),
|
|
clip_grad=self.gradient_clipping(),
|
|
contiguous_gradients=self.zero_contiguous_gradients(),
|
|
reduce_bucket_size=self.zero_reduce_bucket_size(),
|
|
prefetch_bucket_size=self.zero_prefetch_bucket_size(),
|
|
max_reuse_distance=self.zero_max_reuse_distance(),
|
|
max_live_parameters=self.zero_max_live_parameters(),
|
|
param_persistence_threshold=self.zero_param_persistence_threshold(),
|
|
model_persistence_threshold=self.zero_model_persistence_threshold(),
|
|
dp_process_group=self.seq_data_parallel_group,
|
|
all2all_process_group=self.local_all_to_all_group,
|
|
reduce_scatter=self.zero_reduce_scatter(),
|
|
overlap_comm=self.zero_overlap_comm(),
|
|
offload_optimizer_config=self.zero_offload_optimizer(),
|
|
offload_param_config=self.zero_offload_param(),
|
|
sub_group_size=self.zero_sub_group_size(),
|
|
offload_ratio=self.zero_partial_offload(),
|
|
mpu=self.mpu,
|
|
postscale_gradients=self.postscale_gradients(),
|
|
gradient_predivide_factor=self.gradient_predivide_factor(),
|
|
gradient_accumulation_steps=self.gradient_accumulation_steps(),
|
|
aio_config=self.aio_config(),
|
|
gradient_accumulation_dtype=gradient_accumulation_dtype,
|
|
communication_data_type=self.communication_data_type,
|
|
zero_hpz_partition_size=self.zero_hpz_partition_size(),
|
|
zero_quantized_weights=self.zero_quantized_weights(),
|
|
zero_quantized_nontrainable_weights=self.zero_quantized_nontrainable_weights(),
|
|
)
|
|
|
|
else:
|
|
raise NotImplementedError("ZeRO stage {} not implemented".format(zero_stage))
|
|
|
|
return optimizer
|
|
|
|
def _return_mics_optimizer(self, basic_optimizer, timers):
|
|
from deepspeed.runtime.zero.mics import MiCS_Optimizer
|
|
model_dtype, gradient_accumulation_dtype = self.get_data_types()
|
|
optimizer = MiCS_Optimizer(self.module,
|
|
basic_optimizer,
|
|
timers=timers,
|
|
ds_config=self.config,
|
|
static_loss_scale=self.loss_scale(),
|
|
dynamic_loss_scale=self.dynamic_loss_scale(),
|
|
dynamic_loss_args=self.dynamic_loss_scale_args(),
|
|
clip_grad=self.gradient_clipping(),
|
|
contiguous_gradients=self.zero_contiguous_gradients(),
|
|
reduce_bucket_size=self.zero_reduce_bucket_size(),
|
|
prefetch_bucket_size=self.zero_prefetch_bucket_size(),
|
|
max_reuse_distance=self.zero_max_reuse_distance(),
|
|
max_live_parameters=self.zero_max_live_parameters(),
|
|
param_persistence_threshold=self.zero_param_persistence_threshold(),
|
|
model_persistence_threshold=self.zero_model_persistence_threshold(),
|
|
dp_process_group=self.seq_data_parallel_group,
|
|
reduce_scatter=self.zero_reduce_scatter(),
|
|
overlap_comm=self.zero_overlap_comm(),
|
|
offload_optimizer_config=self.zero_offload_optimizer(),
|
|
offload_param_config=self.zero_offload_param(),
|
|
sub_group_size=self.zero_sub_group_size(),
|
|
mpu=self.mpu,
|
|
postscale_gradients=self.postscale_gradients(),
|
|
gradient_predivide_factor=self.gradient_predivide_factor(),
|
|
gradient_accumulation_steps=self.gradient_accumulation_steps(),
|
|
aio_config=self.aio_config(),
|
|
gradient_accumulation_dtype=gradient_accumulation_dtype,
|
|
communication_data_type=self.communication_data_type)
|
|
return optimizer
|
|
|
|
def _configure_eigenvalue(self):
|
|
eigenvalue = Eigenvalue(
|
|
verbose=self.eigenvalue_verbose(),
|
|
max_iter=self.eigenvalue_max_iter(),
|
|
tol=self.eigenvalue_tol(),
|
|
stability=self.eigenvalue_stability(),
|
|
gas_boundary_resolution=self.eigenvalue_gas_boundary_resolution(),
|
|
layer_name=self.eigenvalue_layer_name(),
|
|
layer_num=self.eigenvalue_layer_num(),
|
|
)
|
|
|
|
return eigenvalue
|
|
|
|
def _configure_progressive_layer_drop(self):
|
|
pld = ProgressiveLayerDrop(theta=self.pld_theta(), gamma=self.pld_gamma())
|
|
|
|
return pld
|
|
|
|
def _configure_curriculum_scheduler_legacy(self):
|
|
scheduler = CurriculumScheduler(self.curriculum_params_legacy())
|
|
return scheduler
|
|
|
|
@staticmethod
|
|
def is_map_style_dataset(obj):
|
|
return hasattr(obj, "__getitem__") and hasattr(obj, "__len__")
|
|
|
|
@staticmethod
|
|
def is_iterable_style_dataset(obj):
|
|
return isinstance(obj, torch.utils.data.IterableDataset) # hasattr(obj, "__iter__") should work as well
|
|
|
|
def dataloader_drop_last(self):
|
|
return self._config.dataloader_drop_last
|
|
|
|
def was_step_applied(self) -> bool:
|
|
"""Returns True if the latest ``step()`` produced in parameter updates.
|
|
Note that a ``False`` return is not an error condition. Steps are frequently
|
|
no-ops, such as between gradient accumulation boundaries or when overflows
|
|
occur.
|
|
Returns:
|
|
bool: Whether the latest ``step()`` modified model parameters.
|
|
"""
|
|
return self._step_applied
|
|
|
|
def deepspeed_io(self,
|
|
dataset,
|
|
batch_size=None,
|
|
route=ROUTE_TRAIN,
|
|
pin_memory=True,
|
|
data_sampler=None,
|
|
collate_fn=None,
|
|
num_local_io_workers=None):
|
|
if not (self.is_map_style_dataset(dataset) or self.is_iterable_style_dataset(dataset)):
|
|
raise ValueError("Training data must be a torch Dataset")
|
|
|
|
if batch_size is None:
|
|
batch_size = self.train_micro_batch_size_per_gpu()
|
|
|
|
if collate_fn is None:
|
|
collate_fn = self.collate_fn
|
|
|
|
# Currently we only use timer in train route
|
|
deepspeed_io_timer = None
|
|
if route == ROUTE_TRAIN:
|
|
deepspeed_io_timer = self.tput_timer
|
|
|
|
# If mpu is provided, forward world size and parallel rank to sampler.
|
|
data_parallel_world_size = self.dp_world_size
|
|
data_parallel_rank = self.global_rank
|
|
if self.mpu is not None:
|
|
data_parallel_world_size = self.mpu.get_data_parallel_world_size()
|
|
data_parallel_rank = self.mpu.get_data_parallel_rank()
|
|
|
|
if data_sampler is None and (route == ROUTE_PREDICT or route == ROUTE_EVAL):
|
|
data_sampler = torch.utils.data.DistributedSampler(
|
|
dataset,
|
|
num_replicas=data_parallel_world_size,
|
|
rank=data_parallel_rank,
|
|
shuffle=False,
|
|
)
|
|
|
|
deepspeed_dataloader_config = {}
|
|
if self.curriculum_learning_enabled():
|
|
deepspeed_dataloader_config = {
|
|
CURRICULUM_LEARNING: self.curriculum_learning_enabled(),
|
|
DATA_EFFICIENCY: self.data_efficiency_config(),
|
|
DATA_PARALLEL_GROUP: self.data_parallel_group,
|
|
GRADIENT_ACCUMULATION_STEPS: self.gradient_accumulation_steps(),
|
|
GLOBAL_RANK: self.global_rank,
|
|
DATA_SAMPLING_NUM_WORKERS: self.data_sampling_config()[DATA_SAMPLING_NUM_WORKERS]
|
|
}
|
|
|
|
return DeepSpeedDataLoader(dataset=dataset,
|
|
batch_size=batch_size,
|
|
pin_memory=pin_memory,
|
|
collate_fn=collate_fn,
|
|
local_rank=self.local_rank,
|
|
tput_timer=deepspeed_io_timer,
|
|
num_local_io_workers=num_local_io_workers,
|
|
data_sampler=data_sampler,
|
|
data_parallel_world_size=data_parallel_world_size,
|
|
data_parallel_rank=data_parallel_rank,
|
|
dataloader_drop_last=self.dataloader_drop_last(),
|
|
deepspeed_dataloader_config=deepspeed_dataloader_config)
|
|
|
|
def train(self, mode=True):
|
|
r""""""
|
|
|
|
self.warn_unscaled_loss = True
|
|
self.module.train(mode)
|
|
|
|
def eval(self):
|
|
r""""""
|
|
|
|
self.warn_unscaled_loss = True
|
|
self.module.train(False)
|
|
|
|
def _scale_loss_by_gas(self, prescaled_loss):
|
|
if isinstance(prescaled_loss, torch.Tensor):
|
|
scaled_loss = prescaled_loss / self.gradient_accumulation_steps()
|
|
elif isinstance(prescaled_loss, tuple) or isinstance(prescaled_loss, list):
|
|
scaled_loss = []
|
|
for l in prescaled_loss:
|
|
if isinstance(l, torch.Tensor):
|
|
scaled_loss.append(l / self.gradient_accumulation_steps())
|
|
else:
|
|
scaled_loss.append(l)
|
|
else:
|
|
scaled_loss = prescaled_loss
|
|
if self.warn_unscaled_loss:
|
|
logger.warning(f"DeepSpeed unable to scale loss because of type: {type(prescaled_loss)}")
|
|
self.warn_unscaled_loss = False
|
|
|
|
return scaled_loss
|
|
|
|
@instrument_w_nvtx
|
|
def forward(self, *inputs, **kwargs):
|
|
r"""Execute forward propagation
|
|
Arguments:
|
|
*inputs: Variable length input list
|
|
**kwargs: variable length keyword arguments
|
|
"""
|
|
|
|
if self.autotuning_profile_model_info():
|
|
ma = get_ma_status()
|
|
else:
|
|
see_memory_usage("Engine before forward", force=self.memory_breakdown())
|
|
|
|
flops_profiler_active = (self.flops_profiler_enabled()
|
|
and self.global_steps == self.flops_profiler_profile_step() and self.global_rank == 0)
|
|
|
|
# used to check quantization happens at step 0!
|
|
if self.global_steps == 0 and hasattr(self, "compression_scheduler"):
|
|
self.compression_scheduler.step(step_zero_check=True)
|
|
if self.quantizer:
|
|
tensor_to_quantize = self.optimizer.bit16_groups if self.zero_optimization_stage(
|
|
) == 2 else self.optimizer.fp16_groups
|
|
if self.compression_scheduler.weight_quantization_enabled:
|
|
self.quantizer.quantize(
|
|
tensor_to_quantize,
|
|
(self.optimizer.overflow if self.fp16_enabled() else False),
|
|
self.eigenvalue_enabled(),
|
|
None,
|
|
)
|
|
|
|
if flops_profiler_active:
|
|
self.flops_profiler.start_profile(ignore_list=None)
|
|
|
|
if self.module.training:
|
|
if self.progressive_layer_drop:
|
|
kwargs.update(self.progressive_layer_drop.get_state())
|
|
|
|
if self.__class__.__name__ != "PipelineEngine":
|
|
# TODO: The above if condition is a HACK since for PipelineEngine
|
|
# it's difficult to inject argument in forward pass.
|
|
if self.module.training and self.curriculum_enabled_legacy():
|
|
self.curriculum_scheduler_legacy.update_difficulty(self.global_steps + 1)
|
|
if self.curriculum_params_legacy()["curriculum_type"] == "seqlen":
|
|
kwargs.update({"curriculum_seqlen": self.curriculum_scheduler_legacy.get_current_difficulty()})
|
|
|
|
if self.module.training and self.random_ltd_enabled():
|
|
self.random_ltd_scheduler.update_seq(self.global_steps)
|
|
|
|
if self.zero_optimization_partition_weights():
|
|
# Enable automated discovery of external parameters by indicating that
|
|
# we are in a forward pass.
|
|
for module in self.module.modules():
|
|
module._parameters._in_forward = True
|
|
|
|
self._start_timers(self.engine_timers.forward_timers)
|
|
|
|
if self.training_dataloader is None:
|
|
self.tput_timer.start()
|
|
|
|
if self.fp16_auto_cast():
|
|
inputs = self._cast_inputs_half(inputs)
|
|
# print(f"RANK[{self.global_rank}] self.fp16_auto_cast() is {self.fp16_auto_cast()}")
|
|
|
|
loss = self.module(*inputs, **kwargs)
|
|
|
|
# print(f"RANK[{self.global_rank}]'s loss is {loss}")
|
|
|
|
if self.zero_optimization_partition_weights():
|
|
# Disable automated discovery of external parameters
|
|
for module in self.module.modules():
|
|
module._parameters._in_forward = False
|
|
|
|
self._stop_timers(self.engine_timers.forward_timers)
|
|
|
|
if flops_profiler_active:
|
|
self.flops_profiler.stop_profile()
|
|
|
|
if self.autotuning_profile_model_info():
|
|
activation_mem = get_ma_status() - ma
|
|
self.autotuning_model_info["activation_mem_per_gpu"] = activation_mem
|
|
print_json_dist(self.autotuning_model_info, [0], path=self.autotuning_model_info_path())
|
|
exit()
|
|
else:
|
|
see_memory_usage("Engine after forward", force=self.memory_breakdown())
|
|
return loss
|
|
|
|
def _cast_inputs_half(self, inputs):
|
|
if isinstance(inputs, (list, tuple)):
|
|
new_inputs = []
|
|
for v in inputs:
|
|
new_inputs.append(self._cast_inputs_half(v))
|
|
return inputs.__class__(new_inputs)
|
|
elif isinstance(inputs, dict):
|
|
new_inputs = {}
|
|
for k, v in inputs.items():
|
|
new_inputs[k] = self._cast_inputs_half(v)
|
|
return new_inputs
|
|
elif hasattr(inputs, 'half'):
|
|
return inputs.half()
|
|
else:
|
|
return inputs
|
|
|
|
def print_forward_breakdown(self, fwd_time):
|
|
gate_time = 0.0
|
|
moe_time = 0.0
|
|
falltoall = 0.0
|
|
salltoall = 0.0
|
|
|
|
for gate in self.gate_modules:
|
|
#logger.info(f"Individual TopK gate time: {gate.gate_time:.2f} ms")
|
|
gate_time += gate.gate_time
|
|
|
|
for l in self.moe_layers:
|
|
#logger.info(f"MoE layer; total: {l.time_moe:.2f} ms, first alltoall: {l.time_falltoall:.2f}, second alltoall: {l.time_salltoall:.2f}")
|
|
moe_time += l.time_moe
|
|
falltoall += l.time_falltoall
|
|
salltoall += l.time_salltoall
|
|
|
|
# TODO: Allreduce/average them across ranks for more accurate timing.
|
|
|
|
# if deepspeed.comm.get_rank() == 0:
|
|
log_dist(
|
|
f"time (ms) | fwd: {fwd_time:.2f} (fwd_moe: {moe_time:.2f}, 1st_a2a: {falltoall:.2f}, 2nd_a2a: {salltoall:.2f}, top_k: {gate_time:.2f})",
|
|
ranks=[0])
|
|
|
|
@instrument_w_nvtx
|
|
def allreduce_gradients(self, bucket_size=MEMORY_OPT_ALLREDUCE_SIZE):
|
|
assert not (self.bfloat16_enabled() and self.pipeline_parallelism), \
|
|
f'allreduce_gradients() is not valid when bfloat+pipeline_parallelism is enabled'
|
|
|
|
# Pass (PP) gas boundary flag to optimizer (required for zero)
|
|
self.optimizer.is_gradient_accumulation_boundary = self.is_gradient_accumulation_boundary()
|
|
# ZeRO stage >= 2 communicates during non gradient accumulation boundaries as well
|
|
if self.zero_optimization_partition_gradients():
|
|
self.optimizer.overlapping_partition_gradients_reduce_epilogue()
|
|
|
|
# Communicate only at gradient accumulation boundaries
|
|
elif self.is_gradient_accumulation_boundary():
|
|
if self.zero_optimization_stage() == ZeroStageEnum.optimizer_states and hasattr(
|
|
self.optimizer, 'reduce_gradients'):
|
|
self.optimizer.reduce_gradients(pipeline_parallel=self.pipeline_parallelism)
|
|
else:
|
|
self.buffered_allreduce_fallback(elements_per_buffer=bucket_size)
|
|
|
|
@instrument_w_nvtx
|
|
def backward(self, loss, allreduce_gradients=True, release_loss=False, retain_graph=False, scale_wrt_gas=True):
|
|
r"""Execute backward pass on the loss
|
|
Arguments:
|
|
loss: Torch tensor on which to execute backward propagation
|
|
allreduce_gradients: is deprecated, ignored, and will soon be removed'
|
|
retain_graph: bool, default: false
|
|
forward on user defined choice of retain_graph
|
|
"""
|
|
|
|
see_memory_usage("Engine before backward", force=self.memory_breakdown())
|
|
|
|
if self.scale_wrt_gas is not None:
|
|
scale_wrt_gas = self.scale_wrt_gas
|
|
|
|
if not allreduce_gradients:
|
|
logger.warning(f"Argument `allreduce_gradients` is deprecated, ignored, and will soon be removed")
|
|
|
|
# scale loss w.r.t. gradient accumulation if needed
|
|
if self.gradient_accumulation_steps() > 1 and scale_wrt_gas:
|
|
loss = self._scale_loss_by_gas(loss.float())
|
|
|
|
# Log training loss
|
|
self.losses += loss.mean().item()
|
|
if self.monitor.enabled:
|
|
if self.is_gradient_accumulation_boundary():
|
|
if self.global_rank == 0:
|
|
self.summary_events = [(
|
|
f"Train/Samples/train_loss",
|
|
self.losses,
|
|
self.global_samples,
|
|
)]
|
|
self.monitor.write_events(self.summary_events)
|
|
|
|
self._start_timers(self.engine_timers.backward_timers)
|
|
|
|
assert self.optimizer is not None and not isinstance(self.optimizer, DummyOptim), \
|
|
"must provide optimizer during init in order to use backward"
|
|
|
|
self._start_timers(self.engine_timers.backward_inner_timers)
|
|
|
|
if self.zero_optimization():
|
|
self.optimizer.is_gradient_accumulation_boundary = self.is_gradient_accumulation_boundary()
|
|
self.optimizer.backward(loss, retain_graph=retain_graph)
|
|
elif self.amp_enabled():
|
|
# AMP requires delaying unscale when inside gradient accumulation boundaries
|
|
# https://nvidia.github.io/apex/advanced.html#gradient-accumulation-across-iterations
|
|
delay_unscale = not self.is_gradient_accumulation_boundary()
|
|
with amp.scale_loss(loss, self.optimizer, delay_unscale=delay_unscale) as scaled_loss:
|
|
scaled_loss.backward(retain_graph=retain_graph)
|
|
elif self.fp16_enabled():
|
|
if self.eigenvalue_enabled():
|
|
self.optimizer.backward(loss, create_graph=True, retain_graph=True)
|
|
else:
|
|
self.optimizer.backward(loss, retain_graph=retain_graph)
|
|
elif self.bfloat16_enabled():
|
|
self.optimizer.backward(loss)
|
|
else:
|
|
if self.eigenvalue_enabled():
|
|
loss.backward(create_graph=True, retain_graph=True)
|
|
else:
|
|
loss.backward(retain_graph=retain_graph)
|
|
|
|
self._stop_timers(self.engine_timers.backward_inner_timers)
|
|
|
|
self._start_timers(self.engine_timers.backward_reduce_timers)
|
|
|
|
if allreduce_gradients and self.enable_backward_allreduce:
|
|
# Traditional code path that allreduces the module parameter grads
|
|
self.allreduce_gradients()
|
|
|
|
self._stop_timers(self.engine_timers.backward_reduce_timers)
|
|
|
|
self._stop_timers(self.engine_timers.backward_timers)
|
|
|
|
if release_loss:
|
|
# loss.data = None
|
|
pass
|
|
|
|
see_memory_usage("Engine after backward", force=self.memory_breakdown())
|
|
|
|
return loss
|
|
|
|
def is_gradient_accumulation_boundary(self):
|
|
"""
|
|
Query whether the current micro-batch is at the boundary of
|
|
gradient accumulation, and thus will trigger gradient reductions and
|
|
an optimizer step.
|
|
|
|
Returns:
|
|
bool: if the current step is a gradient accumulation boundary.
|
|
|
|
"""
|
|
if self._is_gradient_accumulation_boundary is None:
|
|
return (self.micro_steps + 1) % \
|
|
self.gradient_accumulation_steps() == 0
|
|
else:
|
|
return self._is_gradient_accumulation_boundary
|
|
|
|
def set_gradient_accumulation_boundary(self, is_boundary):
|
|
"""
|
|
Manually overrides the DeepSpeed engine's gradient accumulation boundary state, this is an optional
|
|
feature and should be used with care. The state should be set before to the intended
|
|
value before each forward/backward. The final forward/backward should have the
|
|
boundary state set to True. This style allows client code to only call engine.step() once after all
|
|
the gradient accumulation passes are complete. See example below:
|
|
.. code-block:: python
|
|
engine.set_gradient_accumulation_boundary(False)
|
|
for _ in range(gradient_accumulation_steps - 1):
|
|
micro_batch = next(data_loader)
|
|
loss = engine(micro_batch)
|
|
engine.backward(loss)
|
|
engine.set_gradient_accumulation_boundary(True)
|
|
micro_batch = next(data_loader)
|
|
loss = engine(micro_batch)
|
|
engine.backward(loss)
|
|
engine.step()
|
|
Arguments:
|
|
is_boundary (bool): are we at a gradient accumulation boundary or not?
|
|
"""
|
|
self._is_gradient_accumulation_boundary = is_boundary
|
|
self.optimizer.is_gradient_accumulation_boundary = is_boundary
|
|
|
|
def zero_grad(self):
|
|
"""
|
|
Zero parameter grads.
|
|
"""
|
|
for param_name, param in self.module.named_parameters():
|
|
param.grad = None
|
|
|
|
def clip_fp32_gradients(self):
|
|
clip_grad_norm_(parameters=self.module.parameters(), max_norm=self.gradient_clipping(), mpu=self.mpu)
|
|
|
|
def _take_model_step(self, lr_kwargs, block_eigenvalue={}):
|
|
if self.gradient_clipping() > 0.0:
|
|
if not (self.fp16_enabled() or self.bfloat16_enabled() or self.amp_enabled() or self.zero_optimization()):
|
|
self.clip_fp32_gradients()
|
|
elif self.amp_enabled():
|
|
# AMP's recommended way of doing clipping
|
|
# https://nvidia.github.io/apex/advanced.html#gradient-clipping
|
|
master_params = amp.master_params(self.optimizer)
|
|
clip_grad_norm_(parameters=master_params, max_norm=self.gradient_clipping(), mpu=self.mpu)
|
|
self.optimizer.step()
|
|
|
|
if hasattr(self.optimizer, '_global_grad_norm'):
|
|
self._global_grad_norm = self.optimizer._global_grad_norm
|
|
|
|
# Quantize the updated parameter if there is no overflow
|
|
if self.quantizer:
|
|
tensor_to_quantize = self.optimizer.bit16_groups if self.zero_optimization_stage(
|
|
) == 2 else self.optimizer.fp16_groups
|
|
if self.compression_scheduler.weight_quantization_enabled:
|
|
self.quantizer.quantize(
|
|
tensor_to_quantize,
|
|
(self.optimizer.overflow if self.fp16_enabled() else False),
|
|
self.eigenvalue_enabled(),
|
|
block_eigenvalue,
|
|
)
|
|
# zero grad in basic optimizer could be unreliable and may not exhibit
|
|
# the behavior that we want
|
|
if self.bfloat16_enabled():
|
|
# TODO: Temporary until bf16_optimizer and zero_optimizer are integrated
|
|
if self.zero_optimization() and hasattr(self.optimizer, "zero_grad"):
|
|
self.optimizer.zero_grad()
|
|
else:
|
|
pass
|
|
elif self.zero_optimization() or self.fp16_enabled() or self.amp_enabled():
|
|
self.optimizer.zero_grad()
|
|
else:
|
|
self.zero_grad()
|
|
|
|
report_progress = self.global_rank == 0 if self.global_rank else True
|
|
|
|
# Check overflow here since in DS fp16 optimizer, the overflow is updated in above step() function.
|
|
overflow = False
|
|
if hasattr(self.optimizer, "overflow"):
|
|
overflow = self.optimizer.overflow
|
|
self._step_applied = not overflow
|
|
|
|
if overflow:
|
|
self.skipped_steps += 1
|
|
else:
|
|
self.compression_scheduler.step()
|
|
if self.lr_scheduler is not None:
|
|
try:
|
|
self.lr_scheduler.step(**(lr_kwargs or {}))
|
|
except TypeError:
|
|
# XXX Hack to work with Megatron 2.0 and DeepSpeed pipelines.
|
|
# We don't currently have a way to specify lr_kwargs from
|
|
# pipe_engine.train_batch()
|
|
self.lr_scheduler.step(self.train_batch_size())
|
|
|
|
if report_progress and (self.global_steps + 1) % self.steps_per_print() == 0:
|
|
self._report_progress(self.global_steps + 1)
|
|
|
|
self.losses = 0.0
|
|
self.global_steps += 1
|
|
self.global_samples += self.train_batch_size()
|
|
|
|
def step(self, lr_kwargs=None):
|
|
r"""Execute the weight update step after forward and backward propagation
|
|
on effective_train_batch.
|
|
"""
|
|
see_memory_usage("Engine before step", force=self.memory_breakdown())
|
|
|
|
# Check early because self.global_steps is incremented at some point here.
|
|
# TODO: Delay self.global_steps increment until very end of this function.
|
|
flops_profiler_active = self.flops_profiler_enabled(
|
|
) and self.global_steps == self.flops_profiler_profile_step() and self.global_rank == 0
|
|
|
|
self._start_timers(self.engine_timers.step_timers)
|
|
|
|
assert self.optimizer is not None and not isinstance(self.optimizer, DummyOptim), \
|
|
"must provide optimizer during init in order to use step"
|
|
|
|
report_progress = False
|
|
|
|
self._step_applied = False # assume False, will flip to True
|
|
|
|
# Update the model when we reach gradient accumulation boundaries
|
|
if self.is_gradient_accumulation_boundary():
|
|
self.gas_boundary_ctr += 1
|
|
|
|
if (self.eigenvalue_enabled() and (self.gas_boundary_ctr % self.eigenvalue_gas_boundary_resolution() == 0)
|
|
and self.quantizer.any_precision_switch()):
|
|
log_dist(f"computing eigenvalue...", ranks=[0])
|
|
self.block_eigenvalue = self.eigenvalue.compute_eigenvalue(self.module, self.device,
|
|
self.optimizer.cur_scale)
|
|
|
|
if self.progressive_layer_drop:
|
|
self.progressive_layer_drop.update_state(self.global_steps)
|
|
|
|
if (self.eigenvalue_enabled() and not self.gas_boundary_ctr % self.eigenvalue_gas_boundary_resolution()
|
|
and self.quantizer.any_precision_switch()):
|
|
self._take_model_step(lr_kwargs, self.block_eigenvalue)
|
|
else:
|
|
self._take_model_step(lr_kwargs)
|
|
|
|
report_progress = self.global_rank == 0 if self.global_rank else True
|
|
|
|
self.tput_timer.stop(global_step=self.is_gradient_accumulation_boundary(), report_speed=report_progress)
|
|
|
|
self._stop_timers(self.engine_timers.step_timers)
|
|
|
|
# Log learning rate
|
|
if self.monitor.enabled:
|
|
if self.is_gradient_accumulation_boundary():
|
|
if self.global_rank == 0:
|
|
self.summary_events = [(f"Train/Samples/lr", self.get_lr()[0], self.global_samples)]
|
|
|
|
if self.fp16_enabled() and hasattr(self.optimizer, "cur_scale"):
|
|
self.summary_events.append((
|
|
f"Train/Samples/loss_scale",
|
|
self.optimizer.cur_scale,
|
|
self.global_samples,
|
|
))
|
|
|
|
if (self.eigenvalue_enabled()
|
|
and not self.gas_boundary_ctr % self.eigenvalue_gas_boundary_resolution()):
|
|
ev_values = self.block_eigenvalue.values()
|
|
for i in range(len(ev_values)):
|
|
self.summary_events.append((
|
|
f"Train/Eigenvalues/ModelBlockParam_{i}",
|
|
self.ev_values[i][0],
|
|
self.global_samples,
|
|
))
|
|
self.monitor.write_events(self.summary_events)
|
|
|
|
# Check flops profiling
|
|
if flops_profiler_active:
|
|
if self.autotuning_enabled():
|
|
self.flops = self.flops_profiler.get_total_flops() * 3
|
|
self.fwd_duration = self.flops_profiler.get_total_duration()
|
|
else:
|
|
self.flops_profiler.print_model_profile(
|
|
profile_step=self.global_steps,
|
|
module_depth=self.flops_profiler_module_depth(),
|
|
top_modules=self.flops_profiler_top_modules(),
|
|
detailed=self.flops_profiler_detailed(),
|
|
output_file=self.flops_profiler_output_file(),
|
|
)
|
|
self.flops_profiler.end_profile()
|
|
|
|
if self.autotuning_enabled() and self.global_steps == (self.autotuning_end_profile_step() + 1):
|
|
self._autotuning_exit()
|
|
|
|
if self.wall_clock_breakdown():
|
|
# Log micro timing and reset
|
|
self.timers.log(names=self.engine_timers.micro_timers, memory_breakdown=self.memory_breakdown())
|
|
|
|
if self.wall_clock_breakdown() or self.flops_profiler_enabled():
|
|
# Log global timing and reset
|
|
if self.is_gradient_accumulation_boundary():
|
|
if self.monitor.enabled:
|
|
self._write_monitor()
|
|
|
|
if self.has_moe_layers:
|
|
fwd_time = self.timers(FORWARD_GLOBAL_TIMER).elapsed(reset=False)
|
|
self.print_forward_breakdown(fwd_time=fwd_time)
|
|
|
|
self.timers.log(self.engine_timers.global_timers)
|
|
|
|
self.micro_steps += 1
|
|
see_memory_usage("Engine after step", force=self.memory_breakdown())
|
|
|
|
def _start_timers(self, timer_names):
|
|
for name in timer_names:
|
|
self.timers(name).start()
|
|
|
|
def _stop_timers(self, timer_names):
|
|
record = self.is_gradient_accumulation_boundary() and \
|
|
self.flops_profiler_enabled() and \
|
|
(self.global_steps >= self.flops_profiler_profile_step())
|
|
for name in timer_names:
|
|
self.timers(name).stop(record=record)
|
|
|
|
def _autotuning_exit(self):
|
|
if self.global_rank == 0:
|
|
msg = self.timers.get_mean([
|
|
FORWARD_GLOBAL_TIMER,
|
|
BACKWARD_GLOBAL_TIMER,
|
|
STEP_GLOBAL_TIMER,
|
|
], reset=False)
|
|
titer = 0.0
|
|
titer += msg[FORWARD_GLOBAL_TIMER] if FORWARD_GLOBAL_TIMER in msg else 0
|
|
titer += msg[BACKWARD_GLOBAL_TIMER] if BACKWARD_GLOBAL_TIMER in msg else 0
|
|
titer += msg[STEP_GLOBAL_TIMER] if STEP_GLOBAL_TIMER in msg else 0
|
|
titer *= self.gradient_accumulation_steps()
|
|
msg["latency"] = titer
|
|
msg["FLOPS_per_gpu"] = self.flops * 1_000_000 * self.gradient_accumulation_steps() / titer
|
|
msg["throughput"] = self.train_batch_size() * 1_000_000 / \
|
|
msg["latency"]
|
|
print_json_dist(msg, [0], path=self.autotuning_metric_path())
|
|
log_dist(
|
|
f"Wrote metrics to {self.autotuning_metric_path()}, {os.path.abspath(self.autotuning_metric_path())}",
|
|
ranks=[0])
|
|
import atexit
|
|
atexit.register(print, "Autotuning: done with running current ds config.")
|
|
exit()
|
|
|
|
def _write_monitor(self):
|
|
if self.global_rank == 0:
|
|
self.summary_events = [
|
|
(
|
|
f"Train/Samples/elapsed_time_ms_forward",
|
|
self.timers(FORWARD_GLOBAL_TIMER).elapsed(reset=False),
|
|
self.global_samples,
|
|
),
|
|
(
|
|
f"Train/Samples/elapsed_time_ms_backward",
|
|
self.timers(BACKWARD_GLOBAL_TIMER).elapsed(reset=False),
|
|
self.global_samples,
|
|
),
|
|
(
|
|
f"Train/Samples/elapsed_time_ms_backward_inner",
|
|
self.timers(BACKWARD_INNER_GLOBAL_TIMER).elapsed(reset=False),
|
|
self.global_samples,
|
|
),
|
|
(
|
|
f"Train/Samples/elapsed_time_ms_backward_allreduce",
|
|
self.timers(BACKWARD_REDUCE_GLOBAL_TIMER).elapsed(reset=False),
|
|
self.global_samples,
|
|
),
|
|
(
|
|
f"Train/Samples/elapsed_time_ms_step",
|
|
self.timers(STEP_GLOBAL_TIMER).elapsed(reset=False),
|
|
self.global_samples,
|
|
),
|
|
]
|
|
self.monitor.write_events(self.summary_events)
|
|
|
|
def _get_optimizer_param(self, param_name):
|
|
result = []
|
|
if not self.optimizer:
|
|
return result
|
|
for group in self.optimizer.param_groups:
|
|
if param_name in group:
|
|
result.append(group[param_name])
|
|
else:
|
|
result.append(0.0)
|
|
return result
|
|
|
|
def get_lr(self):
|
|
return self._get_optimizer_param("lr")
|
|
|
|
def get_type(self):
|
|
return self._get_optimizer_param("type")
|
|
|
|
def get_mom(self):
|
|
if self.optimizer_name() in ["SGD", "RMSprop"]:
|
|
return self._get_optimizer_param("momentum")
|
|
else:
|
|
return self._get_optimizer_param("betas")
|
|
|
|
def get_pld_theta(self):
|
|
if self.progressive_layer_drop:
|
|
return self.progressive_layer_drop.get_theta()
|
|
else:
|
|
return None
|
|
|
|
def _report_progress(self, step):
|
|
lr = self.get_lr()
|
|
mom = self.get_mom()
|
|
log_dist(f"step={step}, skipped={self.skipped_steps}, lr={lr}, mom={mom}", ranks=[0])
|
|
|
|
def allreduce_bucket(self, bucket, dp_group):
|
|
tensor = self.flatten(bucket)
|
|
|
|
tensor_to_allreduce = tensor
|
|
|
|
if self.communication_data_type != tensor.dtype:
|
|
tensor_to_allreduce = tensor.to(self.communication_data_type)
|
|
|
|
if self.postscale_gradients():
|
|
if self.gradient_predivide_factor() != 1.0:
|
|
tensor_to_allreduce.mul_(1.0 / self.gradient_predivide_factor())
|
|
|
|
dist.all_reduce(tensor_to_allreduce, group=dp_group)
|
|
if self.gradient_average:
|
|
if self.gradient_predivide_factor() != dist.get_world_size(group=dp_group):
|
|
tensor_to_allreduce.mul_(self.gradient_predivide_factor() / dist.get_world_size(group=dp_group))
|
|
else:
|
|
tensor_to_allreduce.mul_(1. / dist.get_world_size(group=dp_group))
|
|
dist.all_reduce(tensor_to_allreduce, group=dp_group)
|
|
|
|
if self.communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce:
|
|
tensor.copy_(tensor_to_allreduce)
|
|
|
|
return tensor
|
|
|
|
def allreduce_and_copy(self, small_bucket, dp_group):
|
|
allreduced = self.allreduce_bucket(small_bucket, dp_group)
|
|
for buf, synced in zip(small_bucket, self.unflatten(allreduced, small_bucket)):
|
|
buf.copy_(synced)
|
|
|
|
def allreduce_no_retain(self, bucket, dp_group, numel_per_bucket=500000000):
|
|
small_bucket = []
|
|
numel = 0
|
|
for tensor in bucket:
|
|
small_bucket.append(tensor)
|
|
numel = numel + tensor.numel()
|
|
if numel > numel_per_bucket:
|
|
self.allreduce_and_copy(small_bucket, dp_group)
|
|
small_bucket = []
|
|
numel = 0
|
|
if len(small_bucket) > 0:
|
|
self.allreduce_and_copy(small_bucket, dp_group)
|
|
|
|
def _get_gradients_for_reduction(self):
|
|
non_expert_grads = []
|
|
expert_grads = {}
|
|
if self.has_moe_layers:
|
|
for key in self.expert_data_parallel_group.keys():
|
|
expert_grads[key] = []
|
|
|
|
for param_name, param in self.module.named_parameters():
|
|
if not param.requires_grad:
|
|
continue
|
|
|
|
if param.grad is None:
|
|
# In cases where there is an imbalance of empty grads across
|
|
# ranks we must create empty grads, this will ensure that every
|
|
# rank is reducing the same size. In some cases it may make
|
|
# sense in the future to support the ability to average not
|
|
# w.r.t. world size but with a different value.
|
|
param.grad = torch.zeros(param.size(), dtype=param.dtype, device=param.device)
|
|
|
|
grad_data = param.grad.data
|
|
if param_name in self.sparse_tensor_module_names or grad_data.is_sparse:
|
|
# Call param.grad without data to avoid problem with setting of updated grads
|
|
grad_data = SparseTensor(param.grad)
|
|
|
|
if is_moe_param(param):
|
|
expert_grads[param.group_name].append(grad_data)
|
|
else:
|
|
non_expert_grads.append(grad_data)
|
|
|
|
return non_expert_grads, expert_grads
|
|
|
|
def _reduce_non_expert_gradients(self, grads, elements_per_buffer):
|
|
split_buckets = split_half_float_double_sparse(grads)
|
|
for _, bucket_tuple in enumerate(split_buckets):
|
|
bucket_type, bucket = bucket_tuple
|
|
|
|
if self.pipeline_parallelism:
|
|
dp_group = self.mpu.get_data_parallel_group()
|
|
else:
|
|
dp_group = groups._get_sequence_data_parallel_group()
|
|
|
|
if bucket_type == SparseTensor.type():
|
|
self.sparse_allreduce_no_retain(bucket, dp_group=dp_group)
|
|
else:
|
|
self.allreduce_no_retain(bucket, dp_group=dp_group, numel_per_bucket=elements_per_buffer)
|
|
|
|
def _reduce_expert_gradients(self, expert_grads, elements_per_buffer):
|
|
for ep_name, expert_grads_group in expert_grads.items():
|
|
expert_split_buckets = split_half_float_double_sparse(expert_grads_group)
|
|
for i, bucket_tuple in enumerate(expert_split_buckets):
|
|
bucket_type, bucket = bucket_tuple
|
|
if bucket_type == SparseTensor.type():
|
|
self.sparse_allreduce_no_retain(bucket, groups._get_expert_data_parallel_group(ep_name))
|
|
else:
|
|
# Separate between diff groups
|
|
self.allreduce_no_retain(bucket,
|
|
dp_group=groups._get_expert_data_parallel_group(ep_name),
|
|
numel_per_bucket=elements_per_buffer)
|
|
|
|
def buffered_allreduce_fallback(self, grads=None, elements_per_buffer=500000000):
|
|
if grads is None:
|
|
non_expert_grads, expert_grads = self._get_gradients_for_reduction()
|
|
else:
|
|
assert not self.has_moe_layers, "attempting to reduce grads in unsupported way w.r.t. MoE"
|
|
non_expert_grads = grads
|
|
|
|
self._reduce_non_expert_gradients(non_expert_grads, elements_per_buffer)
|
|
|
|
if self.has_moe_layers:
|
|
self._reduce_expert_gradients(expert_grads, elements_per_buffer)
|
|
|
|
def sparse_allreduce_no_retain(self, bucket, dp_group):
|
|
allreduced_sparses = self.sparse_allreduce_bucket(bucket, dp_group)
|
|
# Densify sparse tensor and copy back to original location
|
|
for tensor in allreduced_sparses:
|
|
if tensor.is_sparse:
|
|
tensor.orig_dense_tensor.data = tensor.to_coo_tensor()
|
|
else:
|
|
tensor.orig_dense_tensor.copy_(tensor.to_dense())
|
|
|
|
def sparse_allreduce_bucket(self, bucket, dp_group):
|
|
sparse_list = []
|
|
for sparse in bucket:
|
|
sparse_list.append(self.sparse_allreduce(sparse, dp_group))
|
|
return sparse_list
|
|
|
|
def sparse_allreduce(self, sparse, dp_group):
|
|
original_data_type = sparse.values.dtype
|
|
if self.communication_data_type != sparse.values.dtype:
|
|
if self.communication_data_type in (torch.float16, torch.bfloat16):
|
|
indices = sparse.indices.to(torch.int32)
|
|
else:
|
|
indices = sparse.indices
|
|
values = sparse.values.to(self.communication_data_type)
|
|
else:
|
|
indices = sparse.indices
|
|
values = sparse.values
|
|
|
|
if self.postscale_gradients():
|
|
if self.gradient_average:
|
|
values.mul_(self.gradient_predivide_factor() /
|
|
(dist.get_world_size(group=dp_group) / float(self.sequence_parallel_size)))
|
|
else:
|
|
values.mul_(1. / (dist.get_world_size(group=dp_group) / float(self.sequence_parallel_size)))
|
|
|
|
indices_device_list = self.sparse_all_gather(indices, dp_group)
|
|
values_device_list = self.sparse_all_gather(values, dp_group)
|
|
|
|
sparse.indices = torch.cat(indices_device_list).to(torch.long)
|
|
sparse.values = torch.cat(values_device_list).to(original_data_type)
|
|
return sparse
|
|
|
|
def sparse_all_gather(self, value, dp_group):
|
|
my_size = torch.LongTensor([value.size()[0]]).to(self.device)
|
|
all_sizes = self.all_gather_scalar(my_size, dp_group)
|
|
max_size = torch.cat(all_sizes).max()
|
|
fill_size = max_size - my_size
|
|
|
|
assert value.dim() in [1, 2]
|
|
if value.dim() == 1:
|
|
if fill_size > 0:
|
|
value = torch.cat([value, value.new_empty(fill_size)])
|
|
tensor_list = [value.new_empty(max_size) for _ in range(dist.get_world_size(group=dp_group))]
|
|
else:
|
|
if fill_size > 0:
|
|
value = torch.cat([value, value.new_empty(fill_size, value.size()[1])])
|
|
tensor_list = [
|
|
value.new_empty(max_size,
|
|
value.size()[1]) for _ in range(dist.get_world_size(group=dp_group))
|
|
]
|
|
|
|
dist.all_gather(tensor_list, value, group=dp_group)
|
|
tensors = []
|
|
for dev_idx, t in enumerate(tensor_list):
|
|
size = all_sizes[dev_idx][0]
|
|
tensors.append(t.index_select(0, torch.arange(size, dtype=torch.long, device=self.device)))
|
|
|
|
return tensors
|
|
|
|
def all_gather_scalar(self, value, dp_group):
|
|
tensor_list = [value.new_zeros(value.size()) for _ in range(dist.get_world_size(group=dp_group))]
|
|
dist.all_gather(tensor_list, value, group=dp_group)
|
|
return tensor_list
|
|
|
|
def module_state_dict(self, destination=None, prefix="", keep_vars=False, exclude_frozen_parameters=False):
|
|
sd = self.module.state_dict(destination, prefix, keep_vars)
|
|
|
|
# Remove frozen parameter weights from state_dict if specified
|
|
if exclude_frozen_parameters:
|
|
for n, p in self.module.named_parameters():
|
|
if not p.requires_grad and n in sd:
|
|
del sd[n]
|
|
|
|
if self.random_ltd_enabled():
|
|
sd = remove_random_ltd_state_dict(sd)
|
|
return sd
|
|
|
|
@staticmethod
|
|
def load_moe_state_dict(checkpoint_path,
|
|
tag,
|
|
state_dict,
|
|
old_moe_load,
|
|
model=None,
|
|
mpu=None,
|
|
num_experts=1,
|
|
checkpoint_engine=TorchCheckpointEngine()):
|
|
if old_moe_load:
|
|
expp_rank = groups._get_expert_data_parallel_rank(groups._get_max_expert_size_name())
|
|
|
|
num_local_experts = max(num_experts) // groups._get_expert_parallel_world_size(
|
|
groups._get_max_expert_size_name())
|
|
for local_expert_id in range(num_local_experts):
|
|
global_expert_id = expp_rank * num_local_experts + local_expert_id
|
|
expert_state_dict = checkpoint_engine.load(
|
|
DeepSpeedEngine._get_expert_ckpt_name(
|
|
checkpoint_path,
|
|
-1, # -1 means ignore layer_id
|
|
global_expert_id,
|
|
tag,
|
|
mpu),
|
|
map_location=torch.device('cpu'))
|
|
|
|
# Updating global -> local expert ids
|
|
moe_str_prefix = '.deepspeed_moe.experts.deepspeed_experts.'
|
|
for key in list(expert_state_dict.keys()):
|
|
local_key = key.replace(f'{moe_str_prefix}{global_expert_id}',
|
|
f'{moe_str_prefix}{local_expert_id}')
|
|
expert_state_dict[local_key] = expert_state_dict.pop(key)
|
|
state_dict.update(expert_state_dict)
|
|
|
|
else:
|
|
moe_layer_id = 0
|
|
for n_module, module in model.named_modules():
|
|
if isinstance(module, MoE): # and deepspeed.comm.get_rank() == 0:
|
|
group_name = module.expert_group_name
|
|
num_local_experts = module.num_local_experts
|
|
expp_rank = groups._get_expert_parallel_rank(group_name)
|
|
# loop all local_experts
|
|
for local_expert_id in range(num_local_experts):
|
|
global_expert_id = expp_rank * num_local_experts + local_expert_id
|
|
expert_state_dict = checkpoint_engine.load(DeepSpeedEngine._get_expert_ckpt_name(
|
|
checkpoint_path, moe_layer_id, global_expert_id, tag, mpu),
|
|
map_location=torch.device('cpu'))
|
|
# print(expert_state_dict.keys())
|
|
# Updating global -> local expert ids
|
|
moe_str_prefix = '.deepspeed_moe.experts.deepspeed_experts.'
|
|
for key in list(expert_state_dict.keys()):
|
|
local_key = key.replace(f'{moe_str_prefix}{global_expert_id}',
|
|
f'{moe_str_prefix}{local_expert_id}')
|
|
expert_state_dict[local_key] = expert_state_dict.pop(key)
|
|
state_dict.update(expert_state_dict)
|
|
moe_layer_id += 1
|
|
|
|
def load_module_state_dict(self, checkpoint, strict=True, custom_load_fn=None, fetch_z3_params=False):
|
|
if fetch_z3_params:
|
|
params_to_fetch = [
|
|
p for p in self.module.parameters()
|
|
if hasattr(p, 'ds_id') and p.ds_status == ZeroParamStatus.NOT_AVAILABLE
|
|
]
|
|
else:
|
|
params_to_fetch = []
|
|
|
|
with deepspeed.zero.GatheredParameters(params_to_fetch, modifier_rank=0):
|
|
module_state_dict = checkpoint['module']
|
|
if custom_load_fn:
|
|
custom_load_fn(src=module_state_dict, dst=self.module)
|
|
else:
|
|
self.module.load_state_dict(
|
|
module_state_dict, # TODO
|
|
strict=strict)
|
|
|
|
if checkpoint.get(FROZEN_PARAM_FRAGMENTS, None) is not None:
|
|
saved_frozen_params = checkpoint[FROZEN_PARAM_FRAGMENTS]
|
|
for param in self.module.parameters():
|
|
if param.requires_grad:
|
|
continue
|
|
if param not in self.param_names:
|
|
raise ValueError(f"failed to find frozen {param} in named params")
|
|
name = self.param_names[param]
|
|
if hasattr(param, 'ds_id'):
|
|
param.ds_tensor.data.copy_(saved_frozen_params[name].data)
|
|
else:
|
|
param.data.copy_(saved_frozen_params[name].data)
|
|
|
|
def _get_zero_ckpt_prefix(self, dp_rank, bf16_mode):
|
|
return f'{"bf16_" if bf16_mode else ""}zero_pp_rank_{dp_rank}'
|
|
|
|
def _get_rank_zero_ckpt_name(self, checkpoints_path, tag, mp_rank, dp_rank, bf16_mode):
|
|
file_prefix = self._get_zero_ckpt_prefix(dp_rank, bf16_mode=bf16_mode)
|
|
zero_ckpt_name = os.path.join(
|
|
checkpoints_path,
|
|
str(tag),
|
|
f"{file_prefix}_mp_rank_{mp_rank:02d}_optim_states.pt",
|
|
)
|
|
return zero_ckpt_name
|
|
|
|
def _get_zero_ckpt_name(self, checkpoints_path, tag):
|
|
mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank()
|
|
pp_rank = dist.get_rank(group=self.optimizer.zp_process_group)
|
|
bf16_mode = self.bfloat16_enabled()
|
|
return self._get_rank_zero_ckpt_name(checkpoints_path, tag, mp_rank, pp_rank, bf16_mode)
|
|
|
|
def _get_ckpt_name(self, checkpoints_path, tag, mp_placeholder=None):
|
|
if mp_placeholder is not None:
|
|
mp_rank_str = mp_placeholder
|
|
else:
|
|
mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank()
|
|
mp_rank_str = f"{mp_rank:02d}"
|
|
|
|
if self.zero_optimization_partition_weights():
|
|
filename = "zero_pp_rank_{}".format(dist.get_rank(group=self.optimizer.zp_process_group))
|
|
ckpt_name = os.path.join(
|
|
checkpoints_path,
|
|
str(tag),
|
|
f"{filename}_mp_rank_{mp_rank_str}_model_states.pt",
|
|
)
|
|
else:
|
|
ckpt_name = os.path.join(
|
|
checkpoints_path,
|
|
str(tag),
|
|
"mp_rank_" + mp_rank_str + "_model_states.pt",
|
|
)
|
|
return ckpt_name
|
|
|
|
def _get_optimizer_ckpt_name(self, checkpoints_path, tag, expp_rank):
|
|
mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank()
|
|
ckpt_name = os.path.join(checkpoints_path, str(tag),
|
|
f'expp_rank_{expp_rank}_mp_rank_{mp_rank:02d}_optim_states.pt')
|
|
return ckpt_name
|
|
|
|
@staticmethod
|
|
def _get_expert_ckpt_name(checkpoints_path, layer_id, expert_id, tag, mpu=None):
|
|
mp_rank = 0 if mpu is None else mpu.get_model_parallel_rank()
|
|
if layer_id <= -1:
|
|
# Used to support old checkpoint loading
|
|
ckpt_name = os.path.join(checkpoints_path, '' if tag is None else str(tag),
|
|
f'expert_{expert_id}_mp_rank_{mp_rank:02d}_model_states.pt')
|
|
else:
|
|
# Used to support new checkpoint loading
|
|
ckpt_name = os.path.join(checkpoints_path, '' if tag is None else str(tag),
|
|
f'layer_{layer_id}_expert_{expert_id}_mp_rank_{mp_rank:02d}_model_states.pt')
|
|
return ckpt_name
|
|
|
|
def _get_all_ckpt_names(self, checkpoints_path, tag):
|
|
# It is required that (checkpoints_path, tag) are consistent among all ranks.
|
|
ckpt_file_pattern = self._get_ckpt_name(checkpoints_path, tag, mp_placeholder="*")
|
|
import glob
|
|
|
|
ckpt_files = glob.glob(ckpt_file_pattern)
|
|
ckpt_files.sort()
|
|
return ckpt_files
|
|
|
|
def load_checkpoint(self,
|
|
load_dir,
|
|
tag=None,
|
|
load_module_strict=True,
|
|
load_optimizer_states=True,
|
|
load_lr_scheduler_states=True,
|
|
load_module_only=False,
|
|
custom_load_fn=None):
|
|
"""
|
|
Load training checkpoint
|
|
|
|
Arguments:
|
|
load_dir: Required. Directory to load the checkpoint from
|
|
tag: Checkpoint tag used as a unique identifier for checkpoint, if not provided will attempt to load tag in 'latest' file
|
|
load_module_strict: Optional. Boolean to strictly enforce that the keys in state_dict of module and checkpoint match.
|
|
load_optimizer_states: Optional. Boolean to load the training optimizer states from Checkpoint. Ex. ADAM's momentum and variance
|
|
load_lr_scheduler_states: Optional. Boolean to add the learning rate scheduler states from Checkpoint.
|
|
load_module_only: Optional. Boolean to load only the model weights from the checkpoint. Ex. warmstarting.
|
|
custom_load_fn: Optional. Custom model load function.
|
|
|
|
Returns:
|
|
A tuple of ``load_path`` and ``client_state``.
|
|
*``load_path``: Path of the loaded checkpoint. ``None`` if loading the checkpoint failed.
|
|
*``client_state``: State dictionary used for loading required training states in the client code.
|
|
|
|
Important: under ZeRO3, one cannot load checkpoint with ``engine.load_checkpoint()`` right
|
|
after ``engine.save_checkpoint()``. It is because ``engine.module`` is partitioned, and
|
|
``load_checkpoint()`` wants a pristine model. If insisting to do so, please reinitialize engine
|
|
before ``load_checkpoint()``.
|
|
|
|
"""
|
|
|
|
if tag is None:
|
|
latest_tag = "latest_universal" if self.load_universal_checkpoint() else "latest"
|
|
latest_path = os.path.join(load_dir, latest_tag)
|
|
if os.path.isfile(latest_path):
|
|
with open(latest_path, "r") as fd:
|
|
tag = fd.read().strip()
|
|
else:
|
|
if self.load_universal_checkpoint():
|
|
raise ValueError(f'Invalid for universal checkpoint: {latest_path} does not exist')
|
|
else:
|
|
logger.warning(
|
|
f"Unable to find latest file at {latest_path}, if trying to load latest "
|
|
"checkpoint please ensure this file exists or pass an explicit checkpoint tag when loading a checkpoint."
|
|
)
|
|
return None, None
|
|
|
|
if self._optimizer_has_ckpt_event_prologue():
|
|
# Prepare for checkpoint load by ensuring all parameters are partitioned
|
|
self.optimizer.checkpoint_event_prologue()
|
|
|
|
load_path, client_states = self._load_checkpoint(load_dir,
|
|
tag,
|
|
load_module_strict=load_module_strict,
|
|
load_optimizer_states=load_optimizer_states,
|
|
load_lr_scheduler_states=load_lr_scheduler_states,
|
|
load_module_only=load_module_only,
|
|
custom_load_fn=custom_load_fn)
|
|
|
|
load_zero_checkpoint = load_optimizer_states and load_path is not None and (self.zero_optimization()
|
|
or self.bfloat16_enabled())
|
|
if load_zero_checkpoint:
|
|
success = self._load_zero_checkpoint(load_dir, tag, load_optimizer_states=load_optimizer_states)
|
|
if not success:
|
|
self.optimizer._restore_from_bit16_weights()
|
|
|
|
if self._optimizer_has_ckpt_event_epilogue():
|
|
self.optimizer.checkpoint_event_epilogue()
|
|
|
|
if self.load_universal_checkpoint():
|
|
self.optimizer.update_lp_params()
|
|
if load_zero_checkpoint:
|
|
self.update_optimizer_step(step=client_states['iteration'] + 1)
|
|
|
|
return load_path, client_states
|
|
|
|
def _load_checkpoint(self,
|
|
load_dir,
|
|
tag,
|
|
load_module_strict=True,
|
|
load_optimizer_states=True,
|
|
load_lr_scheduler_states=True,
|
|
load_module_only=False,
|
|
custom_load_fn=None):
|
|
|
|
from deepspeed.runtime.state_dict_factory import SDLoaderFactory
|
|
|
|
ckpt_list = self._get_all_ckpt_names(load_dir, tag)
|
|
sd_loader = SDLoaderFactory.get_sd_loader(ckpt_list, checkpoint_engine=self.checkpoint_engine)
|
|
|
|
is_pipe_parallel = isinstance(self.module, PipelineModule)
|
|
|
|
mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank()
|
|
load_path, checkpoint, _ = sd_loader.load(self.mp_world_size, mp_rank, is_pipe_parallel=is_pipe_parallel)
|
|
|
|
if checkpoint is None:
|
|
return None, None
|
|
|
|
fetch_z3_params = False
|
|
if self.zero_optimization_partition_weights() and not load_optimizer_states:
|
|
checkpoint['module'] = get_fp32_state_dict_from_zero_checkpoint(load_dir)
|
|
fetch_z3_params = True
|
|
|
|
if is_pipe_parallel:
|
|
# Pipeline parallelism uses this to load its own checkpoint files.
|
|
self._curr_ckpt_path = os.path.join(load_dir, tag)
|
|
|
|
if self.has_moe_layers:
|
|
# print(checkpoint.keys())
|
|
old_moe_load = False
|
|
if not isinstance(checkpoint['num_experts'], list):
|
|
old_moe_load = True
|
|
DeepSpeedEngine.load_moe_state_dict(load_dir,
|
|
tag,
|
|
state_dict=checkpoint['module'],
|
|
old_moe_load=old_moe_load,
|
|
model=self.module,
|
|
mpu=self.mpu,
|
|
num_experts=self.num_experts,
|
|
checkpoint_engine=self.checkpoint_engine)
|
|
if not self.load_universal_checkpoint():
|
|
self.load_module_state_dict(checkpoint=checkpoint,
|
|
strict=load_module_strict,
|
|
custom_load_fn=custom_load_fn,
|
|
fetch_z3_params=fetch_z3_params)
|
|
|
|
self.loaded_checkpoint_dp_world_size = checkpoint['dp_world_size']
|
|
if 'zp_world_size' not in checkpoint:
|
|
checkpoint['zp_world_size'] = self.zp_world_size
|
|
self.loaded_checkpoint_zp_world_size = checkpoint['zp_world_size']
|
|
|
|
optim_checkpoint = None
|
|
if load_module_only:
|
|
deepspeed_states = ['module']
|
|
if self.optimizer is not None and self.fp16_enabled():
|
|
self.optimizer.refresh_fp32_params()
|
|
else:
|
|
has_zero_optimizer_state = self.zero_optimization() or self.bfloat16_enabled()
|
|
if load_optimizer_states and self.optimizer is not None and not has_zero_optimizer_state:
|
|
if self.has_moe_layers:
|
|
largest_group_name = groups._get_max_expert_size_name()
|
|
expp_rank = groups._get_expert_parallel_rank(largest_group_name)
|
|
optim_load_path = self._get_optimizer_ckpt_name(load_dir, tag, expp_rank)
|
|
optim_checkpoint = self.checkpoint_engine.load(optim_load_path, map_location=torch.device('cpu'))
|
|
else:
|
|
optim_checkpoint = checkpoint
|
|
|
|
if self.fp16_enabled() or self.bfloat16_enabled():
|
|
self.optimizer.load_state_dict(optim_checkpoint['optimizer'],
|
|
load_optimizer_states=load_optimizer_states)
|
|
else:
|
|
optim_checkpoint = checkpoint
|
|
|
|
self.optimizer.load_state_dict(optim_checkpoint['optimizer'])
|
|
|
|
if load_lr_scheduler_states and self.lr_scheduler is not None:
|
|
self.lr_scheduler.load_state_dict(checkpoint['lr_scheduler'])
|
|
|
|
if self.random_ltd_enabled() and self.random_ltd_scheduler is not None and 'random_ltd' in checkpoint:
|
|
self.random_ltd_scheduler.load_state_dict(checkpoint['random_ltd'])
|
|
|
|
if self.training_dataloader is not None and self.curriculum_learning_enabled(
|
|
) and 'data_sampler' in checkpoint:
|
|
self.training_dataloader.data_sampler.load_state_dict(checkpoint['data_sampler'])
|
|
|
|
def get_sparse_tensor_module_names(original_set, loaded_set, original_parameters, loaded_parameters):
|
|
result = set()
|
|
|
|
for name in original_set:
|
|
if name in loaded_parameters and name not in loaded_set:
|
|
continue # parameter existed in previous model and was not sparse
|
|
result.add(name)
|
|
|
|
for name in loaded_set:
|
|
if name in original_parameters:
|
|
result.add(name) # parameter exists in both configs and it was sparse
|
|
|
|
return result
|
|
|
|
if 'sparse_tensor_module_names' in checkpoint:
|
|
sparse_tensor_module_names = checkpoint['sparse_tensor_module_names']
|
|
elif 'csr_tensor_module_names' in checkpoint:
|
|
sparse_tensor_module_names = checkpoint['csr_tensor_module_names']
|
|
else:
|
|
sparse_tensor_module_names = None
|
|
if sparse_tensor_module_names is not None:
|
|
if load_module_strict:
|
|
self.sparse_tensor_module_names = sparse_tensor_module_names
|
|
else:
|
|
self.sparse_tensor_module_names = get_sparse_tensor_module_names(
|
|
self.sparse_tensor_module_names, sparse_tensor_module_names,
|
|
dict(self.module.named_parameters()), checkpoint["module"])
|
|
|
|
self.global_steps = checkpoint['global_steps']
|
|
self.global_samples = checkpoint.get('global_samples', self.global_steps * self.train_batch_size())
|
|
self.skipped_steps = checkpoint['skipped_steps']
|
|
self.loaded_checkpoint_mp_world_size = checkpoint['mp_world_size']
|
|
deepspeed_states = [
|
|
'module', 'sparse_tensor_module_names', 'skipped_steps', 'global_steps', 'zp_world_size',
|
|
'mp_world_size', 'data_sampler', 'random_ltd', 'dp_world_size',
|
|
]
|
|
client_state = {}
|
|
|
|
if load_lr_scheduler_states:
|
|
deepspeed_states.append('lr_scheduler')
|
|
if load_optimizer_states:
|
|
deepspeed_states.append('optimizer')
|
|
|
|
client_state = {key: value for key, value in checkpoint.items() if not key in deepspeed_states}
|
|
|
|
if optim_checkpoint is not None:
|
|
client_state['optimizer'] = optim_checkpoint['optimizer']
|
|
|
|
return load_path, client_state
|
|
|
|
def _load_zero_checkpoint(self, load_dir, tag, load_optimizer_states=True):
|
|
|
|
load_serial = None
|
|
# When use loading checkpoint serial, checkpoint loading start from local rank 0,
|
|
# all other local rank would be paused, waiting for its rank-1 peer ready and its notification.
|
|
if self._config.zero_config.pipeline_loading_checkpoint:
|
|
assert self.zero_optimization_stage(
|
|
) == ZeroStageEnum.weights, "Only stage3 support for pipeline checkpoint loading"
|
|
load_serial = torch.zeros(1).to(self.device)
|
|
if dist.get_local_rank() != 0:
|
|
dist.recv(tensor=load_serial, src=dist.get_rank() - 1)
|
|
if self.load_universal_checkpoint():
|
|
zero_sd_list = None
|
|
checkpoint_folder = f'{os.path.join(load_dir, tag)}'
|
|
else:
|
|
if load_optimizer_states and self.zp_world_size != self.loaded_checkpoint_zp_world_size:
|
|
raise ZeRORuntimeException("The checkpoint being loaded used a DP " \
|
|
f"world size of {self.loaded_checkpoint_zp_world_size} but the " \
|
|
f"current world size is {self.zp_world_size}. Automatic adjustment " \
|
|
"of ZeRO's optimizer state partitioning with a new world size is not " \
|
|
"currently supported.")
|
|
checkpoint_folder = None
|
|
zero_sd_list = self._get_all_zero_checkpoints(load_dir, tag)
|
|
if zero_sd_list is None:
|
|
return False
|
|
|
|
self.optimizer.load_state_dict(state_dict_list=zero_sd_list,
|
|
load_optimizer_states=load_optimizer_states,
|
|
load_from_fp32_weights=self.zero_load_from_fp32_weights(),
|
|
checkpoint_folder=checkpoint_folder,
|
|
load_serial=load_serial)
|
|
|
|
if self.load_universal_checkpoint():
|
|
logger.info(f'loaded universal zero checkpoints from {checkpoint_folder} for rank {self.global_rank}')
|
|
else:
|
|
logger.info(f"loading {len(zero_sd_list)} zero partition checkpoints for rank {self.global_rank}")
|
|
return True
|
|
|
|
def update_optimizer_step(self, step):
|
|
|
|
def set_step(d):
|
|
if isinstance(d['step'], torch.Tensor):
|
|
d['step'] = torch.tensor(step, dtype=d['step'].dtype, device=d['step'].device)
|
|
else:
|
|
d['step'] = step
|
|
|
|
optimizer = self.optimizer
|
|
base_optimizer = optimizer.optimizer
|
|
state = base_optimizer.state
|
|
for group in optimizer.param_groups:
|
|
if 'step' in group:
|
|
set_step(group)
|
|
for p in group['params']:
|
|
if p in state and len(state[p]) > 0 and 'step' in state[p]:
|
|
set_step(state[p])
|
|
|
|
def _get_mp_rank_zero_checkpoint_names(self, load_dir, tag, mp_rank, dp_world_size, bf16_mode):
|
|
zero_ckpt_names = []
|
|
for dp_rank in range(dp_world_size):
|
|
ckpt_name = self._get_rank_zero_ckpt_name(checkpoints_path=load_dir,
|
|
tag=tag,
|
|
mp_rank=mp_rank,
|
|
dp_rank=dp_rank,
|
|
bf16_mode=bf16_mode)
|
|
zero_ckpt_names.append(ckpt_name)
|
|
|
|
return zero_ckpt_names
|
|
|
|
def _get_all_zero_checkpoint_names(self, load_dir, tag, bf16_mode):
|
|
mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank()
|
|
zero_ckpt_names = self._get_mp_rank_zero_checkpoint_names(load_dir=load_dir,
|
|
tag=tag,
|
|
mp_rank=mp_rank,
|
|
dp_world_size=self.loaded_checkpoint_dp_world_size,
|
|
bf16_mode=bf16_mode)
|
|
for i, ckpt_name in enumerate(zero_ckpt_names):
|
|
if not os.path.exists(ckpt_name):
|
|
# transparently handle the old file pattern for optim_states
|
|
if "optim_states.pt" in ckpt_name:
|
|
ckpt_name_try = ckpt_name.replace("_optim_states.pt", "optim_states.pt")
|
|
if os.path.exists(ckpt_name_try):
|
|
zero_ckpt_names[i] = ckpt_name_try
|
|
continue
|
|
|
|
return zero_ckpt_names
|
|
|
|
def _get_all_zero_checkpoint_state_dicts(self, zero_ckpt_names):
|
|
zero_sd_list = []
|
|
for i, ckpt_name in enumerate(zero_ckpt_names):
|
|
_state = None
|
|
if ckpt_name is None:
|
|
_state = {OPTIMIZER_STATE_DICT: None}
|
|
# Fully load state for current rank
|
|
elif self.zero_elastic_checkpoint() or dist.get_rank(group=self.optimizer.zp_process_group) == i:
|
|
_state = self.checkpoint_engine.load(
|
|
ckpt_name,
|
|
map_location='cpu',
|
|
)
|
|
else:
|
|
_state = {OPTIMIZER_STATE_DICT: None}
|
|
zero_sd_list.append(_state)
|
|
|
|
zero_optimizer_sd = [sd[OPTIMIZER_STATE_DICT] for sd in zero_sd_list]
|
|
logger.info(f"successfully read {len(zero_optimizer_sd)} ZeRO state_dicts for rank {self.global_rank}")
|
|
return zero_optimizer_sd
|
|
|
|
def _get_all_zero_checkpoints(self, load_dir, tag):
|
|
for bf16_mode in [self.bfloat16_enabled(), not self.bfloat16_enabled()]:
|
|
zero_ckpt_names = self._get_all_zero_checkpoint_names(load_dir, tag, bf16_mode)
|
|
if zero_ckpt_names is not None:
|
|
# Warn if loading checkpoint of different bit16 type
|
|
if bf16_mode is not self.bfloat16_enabled():
|
|
checkpoint_bit16 = BFLOAT16 if bf16_mode else FP16
|
|
engine_bit16 = BFLOAT16 if self.bfloat16_enabled() else FP16
|
|
logger.warn(f'Loading {checkpoint_bit16} zero checkpoints into {engine_bit16} training engine')
|
|
return self._get_all_zero_checkpoint_state_dicts(zero_ckpt_names)
|
|
|
|
return None
|
|
|
|
def _checkpoint_tag_validation(self, tag):
|
|
if self.checkpoint_tag_validation_enabled():
|
|
s_hash = hashlib.sha1(tag.encode())
|
|
bhash = torch.ByteTensor([s_hash.digest()]).flatten().to(self.device)
|
|
max_bhash = bhash.clone()
|
|
min_bhash = bhash.clone()
|
|
dist.all_reduce(max_bhash, op=dist.ReduceOp.MAX)
|
|
dist.all_reduce(min_bhash, op=dist.ReduceOp.MIN)
|
|
valid = all(min_bhash == bhash) and all(max_bhash == bhash)
|
|
msg = (f"[rank={dist.get_rank()}] The checkpoint tag name '{tag}' is not consistent across "
|
|
"all ranks. Including rank unique information in checkpoint tag could cause issues when "
|
|
"restoring with different world sizes.")
|
|
if self.checkpoint_tag_validation_fail():
|
|
assert valid, msg
|
|
elif not valid:
|
|
logger.warning(msg)
|
|
|
|
def save_checkpoint(self, save_dir, tag=None, client_state={}, save_latest=True, exclude_frozen_parameters=False):
|
|
"""Save training checkpoint
|
|
|
|
Arguments:
|
|
save_dir: Required. Directory for saving the checkpoint
|
|
tag: Optional. Checkpoint tag used as a unique identifier for the checkpoint, global step is
|
|
used if not provided. Tag name must be the same across all ranks.
|
|
client_state: Optional. State dictionary used for saving required training states in the client code.
|
|
save_latest: Optional. Save a file 'latest' pointing to the latest saved checkpoint.
|
|
exclude_frozen_parameters: Optional. Exclude frozen parameters from checkpointed state.
|
|
Important: all processes must call this method and not just the process with rank 0. It is
|
|
because each process needs to save its master weights and scheduler+optimizer states. This
|
|
method will hang waiting to synchronize with other processes if it's called just for the
|
|
process with rank 0.
|
|
|
|
"""
|
|
if self._optimizer_has_ckpt_event_prologue():
|
|
# Custom preparation for checkpoint save, if applicable
|
|
self.optimizer.checkpoint_event_prologue()
|
|
|
|
rank = self.local_rank if self.use_node_local_storage() else self.global_rank
|
|
|
|
# This is to make sure the checkpoint names are created without collision
|
|
# There seems to be issue creating them in parallel
|
|
|
|
# Ensure save_dir directory exists
|
|
if rank == 0:
|
|
self.checkpoint_engine.makedirs(save_dir, exist_ok=True)
|
|
dist.barrier()
|
|
|
|
if tag is None:
|
|
tag = f"global_step{self.global_steps}"
|
|
|
|
# Ensure tag is a string
|
|
tag = str(tag)
|
|
self.checkpoint_engine.create(tag)
|
|
|
|
# Ensure checkpoint tag is consistent across ranks
|
|
self._checkpoint_tag_validation(tag)
|
|
|
|
if self.has_moe_layers:
|
|
self.save_non_zero_checkpoint = False
|
|
self._create_checkpoint_file(save_dir, tag, False)
|
|
self._save_moe_checkpoint(save_dir,
|
|
tag,
|
|
client_state=client_state,
|
|
exclude_frozen_parameters=exclude_frozen_parameters)
|
|
|
|
# We distribute the task of saving layer checkpoint files among
|
|
# data parallel instances, so all procs should call _save_checkpoint.
|
|
# All procs then call module_state_dict(), but only procs of data
|
|
# parallel rank 0 save the general model params.
|
|
if not self.has_moe_layers:
|
|
self._create_checkpoint_file(save_dir, tag, False)
|
|
self._save_checkpoint(save_dir,
|
|
tag,
|
|
client_state=client_state,
|
|
exclude_frozen_parameters=exclude_frozen_parameters)
|
|
|
|
if self.save_zero_checkpoint:
|
|
self._create_zero_checkpoint_files(save_dir, tag)
|
|
self._save_zero_checkpoint(save_dir, tag)
|
|
|
|
if self._optimizer_has_ckpt_event_epilogue():
|
|
self.optimizer.checkpoint_event_epilogue()
|
|
|
|
# Save latest checkpoint tag
|
|
self.checkpoint_engine.commit(tag)
|
|
if save_latest and rank == 0:
|
|
with open(os.path.join(save_dir, 'latest'), 'w') as fd:
|
|
fd.write(tag)
|
|
|
|
dist.barrier()
|
|
|
|
return True
|
|
|
|
def _get_non_moe_state_dict(self, full_state_dict):
|
|
"""
|
|
Get the state dict of the non-moe layers
|
|
"""
|
|
for key in list(full_state_dict.keys()):
|
|
if 'expert' in key and 'moe.gate.wg.weight' not in key:
|
|
full_state_dict.pop(key)
|
|
|
|
return full_state_dict
|
|
|
|
def _save_moe_checkpoint(self, save_dir, tag, client_state={}, exclude_frozen_parameters=False):
|
|
save_path = self._get_ckpt_name(save_dir, tag)
|
|
# A hack to save the checkpointing directory. Pipeline parallelism overrides
|
|
# module_state_dict() and uses this path to save the model. module_state_dict()
|
|
# then instead just returns None.
|
|
|
|
# Using layer_#_export_# to save the model's expert state_dict
|
|
moe_layer_id = 0
|
|
for n_module, module in self.module.named_modules():
|
|
if isinstance(module, MoE): # and deepspeed.comm.get_rank() == 0:
|
|
group_name = module.expert_group_name
|
|
num_local_experts = module.num_local_experts
|
|
expp_rank = groups._get_expert_parallel_rank(group_name)
|
|
exp_dp_rank = groups._get_expert_data_parallel_rank(group_name)
|
|
# print(expp_rank, exp_dp_rank)
|
|
if exp_dp_rank != 0:
|
|
moe_layer_id += 1
|
|
continue
|
|
|
|
# get all moe parameters
|
|
moe_state_dict = {}
|
|
for n, p in module.state_dict().items():
|
|
if 'expert' in n and 'moe.gate.wg.weight' not in n:
|
|
moe_state_dict[n_module + '.' + n] = p
|
|
moe_str_prefix = '.deepspeed_moe.experts.deepspeed_experts.'
|
|
# print(moe_state_dict.keys()) # until now, everything is fine. So the bug happens at next few lines
|
|
# Reorder the moe name rank, so that each checkpoint only has one expert
|
|
experts_state_dict = defaultdict(dict)
|
|
for key in list(moe_state_dict.keys()):
|
|
m = re.match(f".*{moe_str_prefix}([0-9]+).*", key)
|
|
|
|
local_expert_id = None
|
|
if not m:
|
|
logger.warn(f'No expert found in key {key}.')
|
|
else:
|
|
local_expert_id = m.group(1)
|
|
|
|
global_expert_id = expp_rank * \
|
|
num_local_experts + int(local_expert_id)
|
|
expert_key = key.replace(f'{moe_str_prefix}{local_expert_id}',
|
|
f'{moe_str_prefix}{global_expert_id}')
|
|
# truncating extra tensor (shared) storage
|
|
truncated = moe_state_dict.pop(key).clone().detach()
|
|
experts_state_dict[str(global_expert_id)][expert_key] = truncated
|
|
|
|
# let save the moe parameters
|
|
for global_expert_id, expert_state_dict in experts_state_dict.items():
|
|
# save the moe parameters
|
|
moe_save_path = self._get_expert_ckpt_name(save_dir, moe_layer_id, global_expert_id, tag, self.mpu)
|
|
if self.random_ltd_enabled():
|
|
expert_state_dict = remove_random_ltd_state_dict(expert_state_dict)
|
|
self.checkpoint_engine.save(expert_state_dict, moe_save_path)
|
|
moe_layer_id += 1
|
|
|
|
self._curr_ckpt_path = os.path.join(save_dir, tag)
|
|
|
|
largest_group_name = groups._get_max_expert_size_name()
|
|
expp_rank = groups._get_expert_parallel_rank(largest_group_name)
|
|
exp_dp_rank = groups._get_expert_data_parallel_rank(largest_group_name)
|
|
|
|
# In the case of E + D parallelism, only the
|
|
# first expert parallel group should save the expert weights
|
|
# since each expert parallel group is a copy of the model's experts
|
|
if exp_dp_rank != 0:
|
|
return
|
|
|
|
# Save optimizer states. They are different across each exp parallel rank.
|
|
optimizer_state = {
|
|
'optimizer': self.optimizer.state_dict() if self.optimizer and not self.zero_optimization() else None
|
|
}
|
|
# TODO: why use BufferedWriter not the path
|
|
file_path = self._get_optimizer_ckpt_name(save_dir, tag, expp_rank)
|
|
self.checkpoint_engine.save(optimizer_state, file_path)
|
|
|
|
# get non-moe parameters
|
|
model_state_dict = self._get_non_moe_state_dict(
|
|
self.module_state_dict(exclude_frozen_parameters=exclude_frozen_parameters))
|
|
|
|
if expp_rank == 0:
|
|
# TODO: update num experts info,.. in checkpoint
|
|
state = {
|
|
'module':
|
|
model_state_dict,
|
|
'lr_scheduler':
|
|
self.lr_scheduler.state_dict() if self.lr_scheduler is not None else None,
|
|
'data_sampler':
|
|
self.training_dataloader.data_sampler.state_dict() if
|
|
(self.training_dataloader is not None and self.curriculum_learning_enabled()) else None,
|
|
'random_ltd':
|
|
self.random_ltd_scheduler.state_dict() if self.random_ltd_enabled() else None,
|
|
'sparse_tensor_module_names':
|
|
self.sparse_tensor_module_names,
|
|
'skipped_steps':
|
|
self.skipped_steps,
|
|
'global_steps':
|
|
self.global_steps,
|
|
'global_samples':
|
|
self.global_samples,
|
|
'zp_world_size':
|
|
self.zp_world_size,
|
|
'dp_world_size':
|
|
self.dp_world_size,
|
|
'mp_world_size':
|
|
self.mp_world_size,
|
|
'num_experts':
|
|
self.num_experts
|
|
}
|
|
state.update(client_state)
|
|
logger.info(f'Saving model checkpoint: {save_path}')
|
|
self.checkpoint_engine.save(state, save_path)
|
|
|
|
def _create_checkpoint_file(self, save_dir, tag, zero_checkpoint):
|
|
name_function = (self._get_zero_ckpt_name if zero_checkpoint else self._get_ckpt_name)
|
|
try:
|
|
checkpoint_name = name_function(save_dir, tag)
|
|
path = os.path.dirname(checkpoint_name)
|
|
self.checkpoint_engine.makedirs(path, exist_ok=True)
|
|
except:
|
|
logger.error(f"Failed saving model checkpoint to {save_dir} with tag {tag}")
|
|
return False
|
|
|
|
return True
|
|
|
|
def _create_zero_checkpoint_files(self, save_dir, tag):
|
|
success = True
|
|
# zero checkpoint files are created sequentially
|
|
for rank in range(dist.get_world_size(self.optimizer.zp_process_group)):
|
|
if rank == self.global_rank:
|
|
success = self._create_checkpoint_file(save_dir, tag, True)
|
|
|
|
dist.barrier(group=self.optimizer.zp_process_group)
|
|
|
|
return success
|
|
|
|
def _save_checkpoint(self, save_dir, tag, client_state={}, exclude_frozen_parameters=False):
|
|
|
|
save_path = self._get_ckpt_name(save_dir, tag)
|
|
|
|
zero_optimizer_state = self.zero_optimization() or self.bfloat16_enabled()
|
|
|
|
save_frozen_param = self.zero_optimization_partition_gradients() and not exclude_frozen_parameters
|
|
|
|
# A hack to save the checkpointing directory. Pipeline parallelism overrides
|
|
# module_state_dict() and uses this path to save the model. module_state_dict()
|
|
# then instead just returns None. The module_state_dict() implementation in
|
|
# PipelineEngine expects the save path to be set in self._curr_ckpt_path.
|
|
self._curr_ckpt_path = os.path.join(save_dir, tag)
|
|
module = self.module_state_dict(exclude_frozen_parameters=exclude_frozen_parameters)
|
|
self._curr_ckpt_path = None
|
|
|
|
state = dict(module=module,
|
|
buffer_names=self._get_buffer_names(),
|
|
optimizer=self.optimizer.state_dict() if self.optimizer and not zero_optimizer_state else None,
|
|
param_shapes=self._get_zero_param_shapes() if self.optimizer and zero_optimizer_state else None,
|
|
frozen_param_shapes=self._get_zero_frozen_param_attributes(self._get_param_shape_func)
|
|
if save_frozen_param else None,
|
|
shared_params=self._get_shared_params() if self.optimizer and zero_optimizer_state else None,
|
|
frozen_param_fragments=self._get_zero_frozen_param_attributes(self._get_param_fragment_func)
|
|
if save_frozen_param else None,
|
|
lr_scheduler=self.lr_scheduler.state_dict() if self.lr_scheduler is not None else None,
|
|
data_sampler=self.training_dataloader.data_sampler.state_dict() if
|
|
(self.training_dataloader is not None and self.curriculum_learning_enabled()) else None,
|
|
random_ltd=self.random_ltd_scheduler.state_dict() if self.random_ltd_enabled() else None,
|
|
sparse_tensor_module_names=self.sparse_tensor_module_names,
|
|
skipped_steps=self.skipped_steps,
|
|
global_steps=self.global_steps,
|
|
global_samples=self.global_samples,
|
|
dp_world_size=self.seq_dp_world_size,
|
|
mp_world_size=self.mp_world_size,
|
|
ds_config=self.config,
|
|
ds_version=version)
|
|
state.update(client_state)
|
|
|
|
if self.save_non_zero_checkpoint:
|
|
log_dist(message=f'Saving model checkpoint: {save_path}', ranks=[0, 1])
|
|
self.checkpoint_engine.save(state, save_path)
|
|
|
|
def _get_buffer_names(self):
|
|
buffer_names = []
|
|
|
|
# we save buffer names so that we could extract later the real buffers from the saved
|
|
# state_dict["module"] in the non-zero checkpoint - the buffers are already there but they
|
|
# are intermixed with param placeholders
|
|
|
|
# have to traverse the tree to be able to skip non-persistent buffers
|
|
def get_layer_named_buffers(module, prefix=""):
|
|
for name, buf in module.named_buffers(recurse=False):
|
|
if buf is not None and name not in module._non_persistent_buffers_set:
|
|
buffer_names.append(prefix + name)
|
|
|
|
for name, child in module.named_children():
|
|
if child is not None:
|
|
get_layer_named_buffers(child, prefix + name + ".")
|
|
|
|
get_layer_named_buffers(self.module, prefix="")
|
|
|
|
return buffer_names
|
|
|
|
def _get_param_shape_func(self, param):
|
|
return param.ds_shape if hasattr(param, 'ds_id') else param.shape
|
|
|
|
def _get_param_fragment_func(self, param):
|
|
return param.ds_tensor.detach().cpu() if hasattr(param, 'ds_id') else param.detach().cpu()
|
|
|
|
def _get_zero_frozen_param_attributes(self, attr_func):
|
|
frozen_param_fragments = OrderedDict()
|
|
|
|
for param in self.module.parameters():
|
|
if param.requires_grad:
|
|
continue
|
|
if param not in self.param_names:
|
|
raise ValueError(f"failed to find frozen {param} in named params")
|
|
name = self.param_names[param]
|
|
frozen_param_fragments[name] = attr_func(param)
|
|
|
|
return frozen_param_fragments
|
|
|
|
def _get_zero_param_shapes(self):
|
|
"""Returns a dict of name to shape mapping, only for the flattened fp32 weights saved by the
|
|
optimizer. the names are exactly as in state_dict. The order is absolutely important, since
|
|
the saved data is just flattened data with no identifiers and requires reconstruction in the
|
|
same order it was saved.
|
|
We can't rely on self.module.named_parameters() to get the saved tensors, as some params
|
|
will be missing and others unsaved and then it'd be impossible to reconstruct state_dict
|
|
from the flattened weights.
|
|
optimizer.bit16_groups seems to be the easiest to use as it's in all zeroX versions.
|
|
"""
|
|
param_group_shapes = []
|
|
cnt = 0
|
|
numel = 0
|
|
|
|
# zero2 started using a round_robin_bit16_groups which is a shuffled version of bit16_groups -
|
|
# if we don't use it, we get parameters ordered incorrectly
|
|
if hasattr(self.optimizer, "round_robin_bit16_groups"):
|
|
bit16_groups = self.optimizer.round_robin_bit16_groups
|
|
elif self.bfloat16_enabled() and hasattr(self.optimizer, "bf16_groups"):
|
|
bit16_groups = self.optimizer.bf16_groups
|
|
else:
|
|
bit16_groups = self.optimizer.bit16_groups if self.zero_optimization_stage(
|
|
) == 2 else self.optimizer.fp16_groups
|
|
|
|
for bit16_group in bit16_groups:
|
|
param_shapes = OrderedDict()
|
|
for param in bit16_group:
|
|
cnt += 1
|
|
numel += param.ds_numel if hasattr(param, "ds_numel") else param.numel()
|
|
shape = param.ds_shape if hasattr(param, "ds_shape") else param.shape
|
|
if param not in self.param_names:
|
|
raise ValueError(f"failed to find optimizer param in named params")
|
|
name = self.param_names[param]
|
|
param_shapes[name] = shape
|
|
|
|
# uncomment to debug zero_to_fp32.py problems
|
|
# if self.global_rank == 0: print(f"saving param {name} {shape} (numel={shape.numel()})")
|
|
param_group_shapes.append(param_shapes)
|
|
# if self.global_rank == 0: print(f"Total saved {numel} numels in {cnt} params")
|
|
|
|
return param_group_shapes
|
|
|
|
def _get_shared_params(self):
|
|
"""
|
|
Returns a dict of shared params, which can later be used to reconstruct the original state dict,
|
|
e.g. in `zero_to_fp32`. Each dict entry is a pair of param names, where the key is the name
|
|
of the variable that isn't stored and the value is the actual param holding data.
|
|
"""
|
|
shared_index = {}
|
|
shared_params_by_full_name = {}
|
|
|
|
is_zero3_model = (self.zero_optimization_partition_weights()
|
|
and any(hasattr(param, "ds_id") for param in self.module.parameters()))
|
|
|
|
def get_layer_state_dict(module, prefix=""):
|
|
# handle params
|
|
for name, param in module.named_parameters(recurse=False):
|
|
if param is None or (is_zero3_model and not hasattr(param, "ds_id")):
|
|
continue
|
|
key = prefix + name
|
|
|
|
# When weights are manged by stage 3, we can't rely on param.data_ptr() as it will be reused
|
|
# as weights get gathered and reduced, but param.ds_id is unique across all zero weights
|
|
# (and shared params will have the same param.ds_id)
|
|
param_id = param.ds_id if is_zero3_model else param.data_ptr()
|
|
|
|
if param_id in shared_index:
|
|
# shared weights
|
|
#print(f"`{key}` is shared with `{shared_index[param_id]}`")
|
|
shared_params_by_full_name[key] = shared_index[param_id]
|
|
else:
|
|
shared_index[param_id] = key
|
|
|
|
for name, child in module.named_children():
|
|
if child is not None:
|
|
get_layer_state_dict(child, prefix + name + ".")
|
|
|
|
if dist.get_rank() == 0:
|
|
get_layer_state_dict(self.module, prefix="")
|
|
|
|
return shared_params_by_full_name
|
|
|
|
def _copy_recovery_script(self, save_path):
|
|
base_dir = os.path.dirname(os.path.dirname(__file__))
|
|
script = "zero_to_fp32.py"
|
|
src = os.path.join(base_dir, "utils", script)
|
|
dst = os.path.join(save_path, script)
|
|
#logger.info(f"creating recovery script {dst}")
|
|
copyfile(src, dst)
|
|
self._change_recovery_script_permissions(dst)
|
|
|
|
def _change_recovery_script_permissions(self, dst):
|
|
# make executable (safeguard for file shares - Azure as example)
|
|
try:
|
|
os.chmod(dst, os.stat(dst).st_mode | stat.S_IEXEC)
|
|
except (FileNotFoundError, PermissionError) as e:
|
|
#this message is used in unit test TestZeRONonDistributed
|
|
logger.info(
|
|
f'Warning: Could not change permissions for {dst} due to error: {e}. Continuing without changing permissions.'
|
|
)
|
|
|
|
def _save_zero_checkpoint(self, save_path, tag):
|
|
zero_checkpoint_name = self._get_zero_ckpt_name(save_path, tag)
|
|
zero_sd = dict(optimizer_state_dict=self.optimizer.state_dict(), ds_config=self.config, ds_version=version)
|
|
self.checkpoint_engine.save(zero_sd, zero_checkpoint_name)
|
|
|
|
if self.global_rank == 0:
|
|
self._copy_recovery_script(save_path)
|
|
ckpt_type = 'zero' if self.zero_optimization() else 'bf16_zero'
|
|
logger.info(f'{ckpt_type} checkpoint saved {zero_checkpoint_name}')
|
|
|
|
def _zero3_consolidated_16bit_state_dict(self):
|
|
"""
|
|
Get a full non-partitioned state_dict with fp16 weights on cpu.
|
|
Important: this function must be called on all ranks and not just rank 0.
|
|
This is similar to nn.Module.state_dict (modelled after _save_to_state_dict), but:
|
|
1. consolidates the weights from different partitions on gpu0
|
|
2. works on one layer at a time to require as little gpu0 memory as possible, by
|
|
moving the already consolidated weights to cpu
|
|
3. takes care to keep the shared params shared when gradually copying the params to cpu
|
|
Returns:
|
|
a consolidated fp16 ``state_dict`` on cpu on rank 0, ``None`` on other ranks
|
|
"""
|
|
if not self.zero_optimization_partition_weights():
|
|
raise ValueError("this function requires ZeRO-3 mode")
|
|
|
|
state_dict = OrderedDict() if dist.get_rank() == 0 else None
|
|
shared_params = {}
|
|
|
|
def get_layer_state_dict(module, prefix=""):
|
|
# gather one layer at a time to be memory-efficient
|
|
# must use modifier_rank=0 to release GPU memory after each layer gathered
|
|
#see_memory_usage("before GatheredParameters", force=True)
|
|
with deepspeed.zero.GatheredParameters(list(module.parameters(recurse=False)), modifier_rank=0):
|
|
if dist.get_rank() == 0:
|
|
# handle params
|
|
for name, param in module.named_parameters(recurse=False):
|
|
if param is None:
|
|
continue
|
|
key = prefix + name
|
|
# can't rely on param.data_ptr() as it will be reused as weights gets
|
|
# gathered and reduced, but param.ds_id is unique across all zero weights
|
|
# (and shared params will have the same param.ds_id)
|
|
if param.ds_id in shared_params:
|
|
# shared weights
|
|
#print(f"`{key}` is shared with `{shared_params[param.ds_id]}`")
|
|
state_dict[key] = state_dict[shared_params[param.ds_id]]
|
|
else:
|
|
state_dict[key] = param.detach().cpu()
|
|
shared_params[param.ds_id] = key
|
|
#print(f"param {param.ds_id} {param.shape} {key} ")
|
|
|
|
# now buffers - not sure if need to take care of potentially shared weights here
|
|
for name, buf in module.named_buffers(recurse=False):
|
|
if (buf is not None and name not in module._non_persistent_buffers_set):
|
|
state_dict[prefix + name] = buf.detach().cpu()
|
|
#see_memory_usage("after GatheredParameters", force=True)
|
|
|
|
for name, child in module.named_children():
|
|
if child is not None:
|
|
get_layer_state_dict(child, prefix + name + ".")
|
|
|
|
# Prepare for checkpoint save by ensuring all parameters are partitioned
|
|
if self._optimizer_has_ckpt_event_prologue():
|
|
self.optimizer.checkpoint_event_prologue()
|
|
|
|
see_memory_usage("before get_layer_state_dict", force=False)
|
|
get_layer_state_dict(self.module, prefix="")
|
|
see_memory_usage("after get_layer_state_dict", force=False)
|
|
|
|
if self._optimizer_has_ckpt_event_epilogue():
|
|
self.optimizer.checkpoint_event_epilogue()
|
|
|
|
return state_dict
|
|
|
|
def save_fp16_model(self, save_dir, save_filename="pytorch_model.bin"):
|
|
"""has been renamed to save_16bit_model, keeping this around for backwards
|
|
compatibility"""
|
|
return self.save_16bit_model(save_dir, save_filename)
|
|
|
|
def save_16bit_model(self, save_dir, save_filename="pytorch_model.bin"):
|
|
"""
|
|
Save 16bit model weights
|
|
|
|
This method saves the 16bit model weights at the desired destination.
|
|
|
|
Arguments:
|
|
save_dir: Required. Directory for saving the model
|
|
save_filename: Optional. Filename to save to. Defaults to ``pytorch_model.bin``
|
|
|
|
Returns:
|
|
``True`` when a model has been saved, ``False`` otherwise. It will not be saved if
|
|
stage3_gather_16bit_weights_on_model_save is ``False``.
|
|
|
|
Important: all processes must call this method and not just the process with rank 0. It is
|
|
because the processes need to work in sync to gather the weights. This method will hang
|
|
waiting to synchronize with other processes if it's called just for the process with rank 0.
|
|
|
|
"""
|
|
|
|
path = os.path.join(save_dir, save_filename)
|
|
|
|
if self.zero_optimization_partition_weights():
|
|
if self.zero_gather_16bit_weights_on_model_save():
|
|
# consolidation is expensive in time and memory and therefore isn't a default
|
|
state_dict = self._zero3_consolidated_16bit_state_dict()
|
|
else:
|
|
# the model will be bogus if not consolidated so don't confuse the user by saving it
|
|
logger.info(
|
|
f"Did not save the model {path} because `stage3_gather_16bit_weights_on_model_save` is False")
|
|
return False
|
|
else:
|
|
state_dict = self.module.state_dict()
|
|
|
|
tag = f"global_step{self.global_steps}"
|
|
tag = str(tag)
|
|
self.checkpoint_engine.create(tag)
|
|
|
|
if dist.get_rank() == 0:
|
|
self.checkpoint_engine.makedirs(save_dir, exist_ok=True)
|
|
logger.info(f"Saving model weights to {path}, tag: {tag}")
|
|
self.checkpoint_engine.save(state_dict, path)
|
|
|
|
self.checkpoint_engine.commit(tag)
|
|
|
|
return True
|
|
|
|
def empty_partition_cache(self):
|
|
"""
|
|
Release GPU memory consumed by offloaded model parameters.
|
|
"""
|
|
if hasattr(self.optimizer, 'empty_partition_cache'):
|
|
self.optimizer.empty_partition_cache()
|
|
gc.collect()
|
|
get_accelerator().empty_cache()
|