Accelerate documentation
Jupyter Defterlerinden Dağıtık Eğitimi Başlatma
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 notebookVeri 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_dataloaderSon olarak, daha sonra kullanılacak zamanlayıcıyı içe aktarmalısınız:
from torch.optim.lr_scheduler import CosineAnnealingLREğ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 = TrueGö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 = 0Sonraki 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_launcherargs = ("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.71Ve 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_processesdeğerini ayarlayın - TPU kullanılıyorsa, modelinizi eğitim döngüsü işlevinin dışında tanımlayın