Accelerate documentation

Jupyter Defterlerinden Dağıtık Eğitimi Başlatma

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v1.15.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Jupyter Defterlerinden Dağıtık Eğitimi Başlatma

Bu eğitim, dağıtık bir sistemde Jupyter Notebook üzerinden 🤗 Accelerate ile bir bilgisayar görme modelini nasıl ince ayar yapacağınızı öğretir. Ayrıca, ortamınızın doğru yapılandırıldığından, verilerinizin doğru hazırlandığından ve sonunda eğitimin nasıl başlatılacağından emin olmak için gereken birkaç gereksinimin nasıl ayarlanacağını da öğreneceksiniz.

Bu eğitim, bir Jupyter Notebook olarak da burada mevcuttur.

Ortamı Yapılandırma

Eğitim yapılabilecek herhangi bir işlem başlatılmadan önce, sistemde bir Accelerate yapılandırma dosyasının bulunması gerekir. Bu işlem genellikle bir terminalde aşağıdaki komutu çalıştırıp gelen isteklere yanıt vererek yapılabilir:

accelerate config

Ancak genel varsayılanlar uygunsa ve bir TPU üzerinde çalışmıyorsanız, Accelerate, utils.write_basic_config() aracılığıyla cihaz yapılandırmanızı bir yapılandırma dosyasına hızlıca yazmak için bir yardımcı işlev sunar.

Aşağıdaki kod, CUDA çalışma zamanı veya XPU çalışma zamanının bu işlemi gerçekleştirmek için çağrılması nedeniyle yapılandırma yazıldıktan sonra Jupyter'i yeniden başlatacaktır.

CUDA ve XPU, çoklu cihazlı bir sistemde birden fazla kez başlatılamaz. Defterde hata ayıklama yapmak ve CUDA/XPU çağrılarını içermek sorun değildir, ancak tam bir eğitim için nihai olarak kapsamlı bir temizlik ve yeniden başlatma yapılması gerekir.

import os
from accelerate.utils import write_basic_config

write_basic_config()  # Write a config file
os._exit(00)  # Restart the notebook

Veri Seti ve Modelin Hazırlanması

Bir sonraki adım olarak veri setinizi hazırlamalısınız. Daha önce belirtildiği gibi, hiçbir şeyin hiçbir GPU'ya yüklenmediğinden emin olmak için DataLoaders ve modeli hazırlarken büyük bir özen gösterilmelidir.

Bunu yapıyorsanız, bu belirli kodu bir işlevin içine alıp daha sonra gösterilecek olan notebook başlatıcı arayüzünden bu işlevi çağırmak önerilir.

Yönergeler burada verildiği gibi veri setinin indirildiğinden emin olun

import os, re, torch, PIL
import numpy as np

from torch.optim.lr_scheduler import OneCycleLR
from torch.utils.data import DataLoader, Dataset
from torchvision.transforms import Compose, RandomResizedCrop, Resize, ToTensor

from accelerate import Accelerator
from accelerate.utils import set_seed
from timm import create_model

Önce dosya adına göre sınıf adını çıkarmak için bir fonksiyon oluşturmanız gerekir:

import os

data_dir = "../../images"
fnames = os.listdir(data_dir)
fname = fnames[0]
print(fname)
beagle_32.jpg

Buradaki durumda etiket beagle. Düzenli ifadeler (regex) kullanarak etiketi dosya adından çıkarabilirsiniz:

import re


def extract_label(fname):
    stem = fname.split(os.path.sep)[-1]
    return re.search(r"^(.*)_\d+\.jpg$", stem).groups()[0]
extract_label(fname)

Ve dosyamız için doğru adın düzgün bir şekilde döndürüldüğünü görebilirsiniz:

"beagle"

Ardından, görüntüyü ve etiketi almakla ilgilenen bir Dataset sınıfı oluşturulmalıdır:

class PetsDataset(Dataset):
    def __init__(self, file_names, image_transform=None, label_to_id=None):
        self.file_names = file_names
        self.image_transform = image_transform
        self.label_to_id = label_to_id

    def __len__(self):
        return len(self.file_names)

    def __getitem__(self, idx):
        fname = self.file_names[idx]
        raw_image = PIL.Image.open(fname)
        image = raw_image.convert("RGB")
        if self.image_transform is not None:
            image = self.image_transform(image)
        label = extract_label(fname)
        if self.label_to_id is not None:
            label = self.label_to_id[label]
        return {"image": image, "label": label}

Şimdi veri setini oluşturalım. Eğitim fonksiyonunun dışında tüm dosya adlarını ve etiketleri bulup beyan edebilir ve başlatılan fonksiyon içinde bunlara referans olarak kullanabilirsiniz:

fnames = [os.path.join("../../images", fname) for fname in fnames if fname.endswith(".jpg")]

Şimdi tüm etiketleri toplayın:

all_labels = [extract_label(fname) for fname in fnames]
id_to_label = list(set(all_labels))
id_to_label.sort()
label_to_id = {lbl: i for i, lbl in enumerate(id_to_label)}

Ardından, oluşturduğunuz dataloader'ları size döndürecek bir get_dataloaders fonksiyonu oluşturmalısınız. Daha önce belirtildiği gibi, DataLoaders'ı oluştururken veri otomatik olarak GPU veya TPU cihazına gönderiliyorsa, bunların bu yöntemle oluşturulması gerekir.

def get_dataloaders(batch_size: int = 64):
    "Builds a set of dataloaders with a batch_size"
    random_perm = np.random.permutation(len(fnames))
    cut = int(0.8 * len(fnames))
    train_split = random_perm[:cut]
    eval_split = random_perm[cut:]

    # For training a simple RandomResizedCrop will be used
    train_tfm = Compose([RandomResizedCrop((224, 224), scale=(0.5, 1.0)), ToTensor()])
    train_dataset = PetsDataset([fnames[i] for i in train_split], image_transform=train_tfm, label_to_id=label_to_id)

    # For evaluation a deterministic Resize will be used
    eval_tfm = Compose([Resize((224, 224)), ToTensor()])
    eval_dataset = PetsDataset([fnames[i] for i in eval_split], image_transform=eval_tfm, label_to_id=label_to_id)

    # Instantiate dataloaders
    train_dataloader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size, num_workers=4)
    eval_dataloader = DataLoader(eval_dataset, shuffle=False, batch_size=batch_size * 2, num_workers=4)
    return train_dataloader, eval_dataloader

Son olarak, daha sonra kullanılacak zamanlayıcıyı içe aktarmalısınız:

from torch.optim.lr_scheduler import CosineAnnealingLR

Eğitim Fonksiyonunu Yazma

Şimdi eğitim döngüsünü oluşturabilirsiniz. notebook_launcher(), dağıtık sistemde çalıştırılacak bir fonksiyonu parametre olarak alarak çalışır.

İşte hayvan sınıflandırma sorunu için temel bir eğitim döngüsü:

Kod, her bölüm için açıklamalara izin verebilmek üzere parçalara ayrılmıştır. Kopyala-yapıştır edilebilecek tam sürüm, sonucunda yer alacaktır

def training_loop(mixed_precision="fp16", seed: int = 42, batch_size: int = 64):
    set_seed(seed)
    accelerator = Accelerator(mixed_precision=mixed_precision)

Öncelikle tohumu ayarlamalı ve eğitim döngüsünde mümkün olan en erken aşamada bir Accelerator nesnesi oluşturmalısınız.

TPU üzerinde eğitim yapıyorsanız, eğitim döngünüz modeli bir parametre olarak almalı ve model, eğitim döngüsü fonksiyonunun dışında örneklenmelidir. Bunun nedenini öğrenmek için TPU en iyi uygulamalarına göz atın

Sıradaki adım, veri yükleyicilerinizi oluşturmak ve modelinizi hazırlamaktır:

    train_dataloader, eval_dataloader = get_dataloaders(batch_size)
    model = create_model("resnet50d", pretrained=True, num_classes=len(label_to_id))

Tohumun yeni ağırlık başlatma işlemini de kontrol etmesi için modeli burada oluşturursunuz

Bu örnekte transfer öğrenimi (transfer learning) yaptığınız için, modelin kodlayıcısı (encoder) başlangıçta dondurulur ve modelin başlığı (head) yalnızca başlangıçta eğitilebilir:

    for param in model.parameters():
        param.requires_grad = False
    for param in model.get_classifier().parameters():
        param.requires_grad = True

Görsel partilerini normalleştirmek eğitimi biraz daha hızlı hale getirecektir:

    mean = torch.tensor(model.default_cfg["mean"])[None, :, None, None]
    std = torch.tensor(model.default_cfg["std"])[None, :, None, None]

Bu sabitlerin etkin cihazda kullanılabilir hale getirilmesi için, bunları Accelerator’ın cihazına ayarlamanız gerekir:

    mean = mean.to(accelerator.device)
    std = std.to(accelerator.device)

Ardından eğitimde kullanılan diğer PyTorch sınıflarını örnekleyin:

    optimizer = torch.optim.Adam(params=model.parameters(), lr=3e-2 / 25)
    lr_scheduler = OneCycleLR(optimizer=optimizer, max_lr=3e-2, epochs=5, steps_per_epoch=len(train_dataloader))

Her şeyi prepare() fonksiyonuna iletmeden önce.

Belirli bir sıralama ezberlemenize gerek yoktur, nesneleri prepare yöntemine verdiğiniz sırayla açmanız yeterlidir.

    model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
        model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
    )

Şimdi modeli eğitin:

    for epoch in range(5):
        model.train()
        for batch in train_dataloader:
            inputs = (batch["image"] - mean) / std
            outputs = model(inputs)
            loss = torch.nn.functional.cross_entropy(outputs, batch["label"])
            accelerator.backward(loss)
            optimizer.step()
            lr_scheduler.step()
            optimizer.zero_grad()

Değerlendirme döngüsü, eğitim döngüsüne kıyasla biraz farklı görünecektir. Her bir partinin iletildiği öğe sayısı ve genel toplam doğruluğu, iki sabite eklenecektir:

        model.eval()
        accurate = 0
        num_elems = 0

Sonraki adım, standart PyTorch döngüsünün geri kalanıdır:

        for batch in eval_dataloader:
            inputs = (batch["image"] - mean) / std
            with torch.no_grad():
                outputs = model(inputs)
            predictions = outputs.argmax(dim=-1)

Ve son olarak, son büyük fark.

Dağıtık değerlendirme sırasında, tüm verilerin geçerli cihazda kullanılabilir hale gelmesi ve doğru hesaplanmış bir metrik elde edilebilmesi için tahminlerin ve etiketlerin gather() üzerinden geçirilmesi gerekir:

            accurate_preds = accelerator.gather(predictions) == accelerator.gather(batch["label"])
            num_elems += accurate_preds.shape[0]
            accurate += accurate_preds.long().sum()

Şimdi bu sorun için gerçek metriği hesaplameniz yeterlidir ve bunu ana süreçte print() kullanarak yazdırabilirsiniz:

        eval_metric = accurate.item() / num_elems
        accelerator.print(f"epoch {epoch}: {100 * eval_metric:.2f}")

Bu eğitim döngüsünün tam sürümü aşağıda mevcuttur:

def training_loop(mixed_precision="fp16", seed: int = 42, batch_size: int = 64):
    set_seed(seed)
    # Initialize accelerator
    accelerator = Accelerator(mixed_precision=mixed_precision)
    # Build dataloaders
    train_dataloader, eval_dataloader = get_dataloaders(batch_size)

    # Instantiate the model (you build the model here so that the seed also controls new weight initializations)
    model = create_model("resnet50d", pretrained=True, num_classes=len(label_to_id))

    # Freeze the base model
    for param in model.parameters():
        param.requires_grad = False
    for param in model.get_classifier().parameters():
        param.requires_grad = True

    # You can normalize the batches of images to be a bit faster
    mean = torch.tensor(model.default_cfg["mean"])[None, :, None, None]
    std = torch.tensor(model.default_cfg["std"])[None, :, None, None]

    # To make these constants available on the active device, set it to the accelerator device
    mean = mean.to(accelerator.device)
    std = std.to(accelerator.device)

    # Instantiate the optimizer
    optimizer = torch.optim.Adam(params=model.parameters(), lr=3e-2 / 25)

    # Instantiate the learning rate scheduler
    lr_scheduler = OneCycleLR(optimizer=optimizer, max_lr=3e-2, epochs=5, steps_per_epoch=len(train_dataloader))

    # Prepare everything
    # There is no specific order to remember, you just need to unpack the objects in the same order you gave them to the
    # prepare method.
    model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
        model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
    )

    # Now you train the model
    for epoch in range(5):
        model.train()
        for batch in train_dataloader:
            inputs = (batch["image"] - mean) / std
            outputs = model(inputs)
            loss = torch.nn.functional.cross_entropy(outputs, batch["label"])
            accelerator.backward(loss)
            optimizer.step()
            lr_scheduler.step()
            optimizer.zero_grad()

        model.eval()
        accurate = 0
        num_elems = 0
        for batch in eval_dataloader:
            inputs = (batch["image"] - mean) / std
            with torch.no_grad():
                outputs = model(inputs)
            predictions = outputs.argmax(dim=-1)
            accurate_preds = accelerator.gather(predictions) == accelerator.gather(batch["label"])
            num_elems += accurate_preds.shape[0]
            accurate += accurate_preds.long().sum()

        eval_metric = accurate.item() / num_elems
        # Use accelerator.print to print only on the main process.
        accelerator.print(f"epoch {epoch}: {100 * eval_metric:.2f}")

notebook_launcher kullanma

Yapılması gereken tek şey notebook_launcher() kullanmaktır.

Fonksiyonu, argümanları (bir tuple olarak) ve eğitim için kullanılacak işlem sayısını iletirsiniz. (Daha fazla bilgi için belgelere bakın)

from accelerate import notebook_launcher
args = ("fp16", 42, 64)
notebook_launcher(training_loop, args, num_processes=2)

Çoklu düğüm üzerinde çalıştırma durumunda, her düğümde bir Jupyter oturumu kurmanız ve başlatma hücresini aynı anda çalıştırmanız gerekir.

2 düğümün (bilgisayarın) her birinde 8 GPU ve ana bilgisayarın IP adresinin “172.31.43.8” olduğu bir ortam için görünüm şu şekilde olacaktır:

notebook_launcher(training_loop, args, master_addr="172.31.43.8", node_rank=0, num_nodes=2, num_processes=8)

Ve diğer makinedeki ikinci Jupyter oturumunda:

node_rank'in nasıl değiştiğine dikkat edin

notebook_launcher(training_loop, args, master_addr="172.31.43.8", node_rank=1, num_nodes=2, num_processes=8)

TPU üzerinde çalıştırma durumunda, görünümü şu şekilde olacaktır:

model = create_model("resnet50d", pretrained=True, num_classes=len(label_to_id))

args = (model, "fp16", 42, 64)
notebook_launcher(training_loop, args, num_processes=8)

Elastisite ile eğitim sürecini başlatarak hata toleransını etkinleştirmek için PyTorch tarafından sağlanan elastic_launch özelliğini kullanabilirsiniz. Bunun için rdzv_backend ve max_restarts gibi ek parametrelerin ayarlanması gerekir. Elastik yeteneklerle notebook_launcher kullanımına dair bir örnek aşağıdadır:

notebook_launcher(
    training_loop,
    args,
    num_processes=2,
    max_restarts=3
)

Çalışırken ilerlemeyi ve kaç cihazda çalıştırıldığını da yazdırır. Bu eğitim iki GPU ile çalıştırıldı:

Launching training on 2 GPUs.
epoch 0: 88.12
epoch 1: 91.73
epoch 2: 92.58
epoch 3: 93.90
epoch 4: 94.71

Ve tamam!

Lütfen notebook_launcher()'ın Accelerate yapılandırma dosyasını yoksaydığını, yapılandırmaya göre başlatmak için şunun kullanılması gerektiğini unutmayın:

accelerate launch

Hata Ayıklama

notebook_launcher çalıştırılırken karşılaşılan yaygın bir sorun, CUDA/XPU has already been initialized hatasının alınmasıdır. Bu durum genellikle, notebook içindeki bir import veya önceki kodun PyTorch torch.cuda veya torch.xpu alt kütüphanelerine çağrı yapmasından kaynaklanır. Sorunun kaynağını daraltmak için, ortamınıza ACCELERATE_DEBUG_MODE=yes ekleyerek notebook_launcher’ı başlatabilirsiniz; bu durumda, süreç oluşturulurken CUDA/XPU’nun sorunsuz bir şekilde oluşturulup kullanılabildiğine dair ek bir kontrol yapılır. (CUDA/XPU kodunuz daha sonra yine çalıştırılabilir).

Sonuç

Bu notebook, bir Jupyter Notebook'un içinden dağıtık eğitimin nasıl gerçekleştirileceğini gösterdi. Hatırlanması gereken bazı önemli notlar:

  • CUDA/XPU kullanan (veya CUDA/XPU içe aktarmaları içeren) tüm kodu, notebook_launcher() fonksiyonuna geçirilen fonksiyon için kaydetmeyi unutmayın.
  • Eğitimde kullanılan cihaz sayısını (GPU, XPU, CPU, TPU sayısı vb.) belirtmek için num_processes değerini ayarlayın
  • TPU kullanılıyorsa, modelinizi eğitim döngüsü işlevinin dışında tanımlayın
Update on GitHub