Je bent in een ML Engineer interview bij Google. Interviewer: We moeten een LLM trainen op 1.000 GPU's. Hoe zou je ervoor zorgen dat alle GPU's delen wat ze leren? Jij: Gebruik een centrale parameter server om de gewichten te aggregeren en opnieuw te distribueren. Interview voorbij. Hier is wat je gemist hebt:
Een belangrijke runtime-bottleneck in multi-GPU-training vindt plaats tijdens de GPU-synchronisatie. Bijvoorbeeld, in multi-GPU-training via data-parallelisme: - Hetzelfde model wordt verdeeld over verschillende GPU's. - Elke GPU verwerkt een andere subset van de gehele dataset. Controleer dit 👇
Dit leidt tot verschillende gradients op verschillende apparaten. Dus, voordat we de modelparameters op elk GPU-apparaat bijwerken, moeten we de gradients naar alle andere apparaten communiceren om ze te synchroniseren. Laten we de volgende 2 veelvoorkomende strategieën begrijpen!
Algoritme 1) All-reduce Een voor de hand liggende manier is om de gradients van één apparaat naar alle andere apparaten te sturen om ze te synchroniseren. Maar dit maakt gebruik van hoge bandbreedte. Als elke GPU "N" elementen heeft en er zijn "G" GPU's, resulteert dit in een totale overdracht van G*(G-1)*N elementen 👇
We kunnen dit optimaliseren door alle elementen naar één GPU over te dragen, de uiteindelijke waarde te berekenen en deze terug te sturen naar alle andere GPU's. Dit is een aanzienlijke verbetering. Maar nu moet één enkele GPU de gradiënten ontvangen, berekenen en terugcommuniceren, dus dit schaalt niet.
Algoritme 2) Ring-reduce Fase #1) Share-reduce Eerst worden de gradients verdeeld in G segmenten op elke GPU (G = totaal aantal GPU's). Controleer dit 👇
In een iteratie stuurt elke GPU een segment naar de volgende GPU: - GPU1 stuurt a₁ naar GPU2, waar het wordt opgeteld bij b₁ - GPU2 stuurt b₂ naar GPU3, waar het wordt opgeteld bij c₂ - GPU3 stuurt c₃ naar GPU4, waar het wordt opgeteld bij d₃ - GPU4 stuurt d₄ naar GPU1, waar het wordt opgeteld bij a₄ Controleer dit 👇
Dit proces wordt opnieuw uitgevoerd: - GPU1 stuurt (d₄+a₄) naar GPU2, waar het wordt opgeteld bij b₄. - GPU2 stuurt (a₁+b₁) naar GPU3, waar het wordt opgeteld bij c₁. - GPU3 stuurt (b₂+c₂) naar GPU4, waar het wordt opgeteld bij d₂. - GPU4 stuurt (c₃+d₃) naar GPU1, waar het wordt opgeteld bij a₃. Controleer dit 👇
In de laatste iteratie worden de volgende segmenten overgedragen naar de volgende GPU. Dit leidt tot een staat waarin elke GPU één volledig segment heeft, en we kunnen deze complete segmenten naar alle andere GPU's overdragen. Controleer dit 👇
Fase #2) Alleen delen Nu elke GPU één volledig segment heeft, kunnen we deze complete segmenten naar alle andere GPU's overdragen. Het proces wordt op een vergelijkbare manier uitgevoerd als wat we hierboven hebben besproken, dus we zullen niet in detail treden. Iteratie 1 is hieronder weergegeven👇
Vervolgens voeren we iteraties 2 en 3 uit. Deze worden op een vergelijkbare manier uitgevoerd als wat we in Fase 1 hebben geleerd. Controleer dit 👇
En daar ga je! Modelgewichten zijn gesynchroniseerd over de GPU's. Hoewel het totale aantal overgedragen elementen nog steeds hetzelfde is als we hadden in de "single-GPU-master" aanpak, is deze ringaanpak veel schaalbaarder omdat het niet de volledige belasting op één GPU legt. Bekijk dit 👇
Standaard gebruiken deep learning-modellen slechts één GPU voor training, zelfs als er meerdere GPU's beschikbaar zijn. Een ideale manier om modellen te trainen is om de trainingsbelasting over meerdere GPU's te verdelen. De afbeelding toont vier strategieën voor multi-GPU training👇
364,81K