Sei in un colloquio per un ingegnere ML presso Google. Intervistatore: Dobbiamo addestrare un LLM su 1.000 GPU. Come ti assicureresti che tutte le GPU condividano ciò che apprendono? Tu: Usa un server di parametri centrale per aggregare e ridistribuire i pesi. Colloquio finito. Ecco cosa ti sei perso:
Un importante collo di bottiglia durante l'esecuzione nel training multi-GPU si verifica durante la sincronizzazione delle GPU. Ad esempio, nel training multi-GPU tramite parallelismo dei dati: - Lo stesso modello è distribuito a diverse GPU. - Ogni GPU elabora un diverso sottoinsieme dell'intero dataset. Controlla questo 👇
Questo porta a gradienti diversi su dispositivi diversi. Quindi, prima di aggiornare i parametri del modello su ciascun dispositivo GPU, dobbiamo comunicare i gradienti a tutti gli altri dispositivi per sincronizzarli. Comprendiamo ora 2 strategie comuni!
Algoritmo 1) All-reduce Un modo ovvio è inviare i gradienti da un dispositivo a tutti gli altri dispositivi per sincronizzarli. Ma questo utilizza un'ampia larghezza di banda. Se ogni GPU ha “N” elementi e ci sono “G” GPU, si ottiene un trasferimento totale di G*(G-1)*N elementi 👇
Possiamo ottimizzare questo trasferendo tutti gli elementi a una sola GPU, calcolando il valore finale e inviandolo indietro a tutte le altre GPU. Questo è un miglioramento significativo. Ma ora una singola GPU deve ricevere, calcolare e comunicare indietro i gradienti, quindi questo non scala.
Algoritmo 2) Riduzione a cerchio Fase #1) Riduzione di condivisione Innanzitutto, i gradienti vengono divisi in G segmenti su ogni GPU (G = numero totale di GPU). Controlla questo 👇
In un'iterazione, ogni GPU invia un segmento alla GPU successiva: - GPU1 invia a₁ a GPU2, dove viene aggiunto a b₁ - GPU2 invia b₂ a GPU3, dove viene aggiunto a c₂ - GPU3 invia c₃ a GPU4, dove viene aggiunto a d₃ - GPU4 invia d₄ a GPU1, dove viene aggiunto a a₄ Controlla questo 👇
Questo processo viene ripetuto: - GPU1 invia (d₄+a₄) a GPU2, dove viene aggiunto a b₄. - GPU2 invia (a₁+b₁) a GPU3, dove viene aggiunto a c₁. - GPU3 invia (b₂+c₂) a GPU4, dove viene aggiunto a d₂. - GPU4 invia (c₃+d₃) a GPU1, dove viene aggiunto a a₃. Controlla questo 👇
Nell'ultima iterazione, i seguenti segmenti vengono trasferiti alla GPU successiva. Questo porta a uno stato in cui ogni GPU ha un intero segmento, e possiamo trasferire questi segmenti completi a tutte le altre GPU. Controlla questo 👇
Fase #2) Solo condivisione Ora che ogni GPU ha un intero segmento, possiamo trasferire questi segmenti completi a tutte le altre GPU. Il processo viene eseguito in modo simile a quanto discusso sopra, quindi non entreremo nei dettagli. L'iterazione 1 è mostrata di seguito👇
Successivamente, eseguiamo le iterazioni 2 e 3. Queste vengono effettuate in modo simile a quanto abbiamo appreso nella Fase 1. Controlla questo 👇
Ecco fatto! I pesi del modello tra le GPU sono stati sincronizzati. Sebbene il numero totale di elementi trasferiti sia ancora lo stesso di quello che avevamo nell'approccio "single-GPU-master", questo approccio a cerchio è molto più scalabile poiché non carica l'intero lavoro su una sola GPU. Controlla questo 👇
Per impostazione predefinita, i modelli di deep learning utilizzano solo una singola GPU per l'addestramento, anche se sono disponibili più GPU. Un modo ideale per addestrare i modelli è distribuire il carico di lavoro dell'addestramento su più GPU. Il grafico mostra quattro strategie per l'addestramento multi-GPU👇
362,51K