Tutorial Image Classification dengan Transfer Learning: ResNet, EfficientNet, ViT

# Klasifikasi Gambar dengan Transfer Learning: Tutorial Komprehensif ## Daftar Isi 1. [Pendahuluan](#pendahuluan) 2. [Prasyarat](#prasyarat) 3. [Memahami Transfer Learning](#memahami-transfer-learni...

By Ruby Abdullah · · tutorial
Transfer LearningImage ClassificationResNetEfficientNetViTPyTorch

Klasifikasi Gambar dengan Transfer Learning: Tutorial Komprehensif

Daftar Isi

  • Pendahuluan
  • Prasyarat
  • Memahami Transfer Learning
  • Memilih Model Pre-trained
  • Pemuatan dan Persiapan Data
  • Fine-tuning dengan PyTorch
  • Implementasi Training Loop
  • Evaluasi dan Metrik
  • Ekspor dan Deployment Model
  • Praktik Terbaik
  • Kesimpulan

  • Pendahuluan

    Transfer learning adalah teknik machine learning di mana model yang telah dilatih pada dataset besar digunakan kembali untuk tugas yang berbeda namun terkait. Dalam computer vision, teknik ini biasanya melibatkan pengambilan model yang telah dilatih sebelumnya pada ImageNet (1,4 juta gambar, 1000 kelas) dan mengadaptasinya untuk masalah klasifikasi spesifik Anda. Pendekatan ini secara dramatis mengurangi kebutuhan data dan komputasi untuk mencapai akurasi tinggi.

    Tutorial ini mencakup alur kerja lengkap: memilih model pre-trained, menyiapkan data, fine-tuning dengan PyTorch, mengevaluasi performa, dan melakukan deployment model akhir.


    Prasyarat

    pip install torch torchvision
    

    pip install timm # PyTorch Image Models - koleksi model yang luas

    pip install albumentations # Augmentasi lanjutan

    pip install scikit-learn # Metrik evaluasi

    pip install matplotlib seaborn # Visualisasi

    pip install onnx onnxruntime # Ekspor dan deployment

    Persyaratan sistem:
    • Python 3.8 atau lebih tinggi
    • GPU dengan minimal 6 GB VRAM (untuk pelatihan), CPU cukup untuk inferensi
    • Pemahaman dasar tentang jaringan saraf dan PyTorch

    import torch
    

    import torchvision

    import timm

    print(f"PyTorch: {torch.version}")

    print(f"Torchvision: {torchvision.version}")

    print(f"CUDA: {torch.cuda.isavailable()}")

    print(f"Timm: {timm.version}")

    print(f"Model timm tersedia: {len(timm.listmodels())}")


    Memahami Transfer Learning

    Transfer learning berhasil karena layer-layer awal pada CNN mempelajari fitur universal (tepi, tekstur, pola) yang berlaku untuk hampir semua tugas vision. Hanya layer-layer akhir yang menjadi spesifik terhadap tugas tertentu.

    Ada dua strategi utama:

  • Ekstraksi Fitur (Feature Extraction): Bekukan seluruh model pre-trained dan hanya latih classification head yang baru. Metode ini cepat, membutuhkan data minimal, dan bekerja dengan baik ketika tugas Anda mirip dengan ImageNet.
  • Fine-tuning: Buka beberapa atau seluruh layer model pre-trained dan latih dengan learning rate yang rendah. Ini memungkinkan model mengadaptasi fitur-fiturnya ke domain spesifik Anda dan umumnya menghasilkan hasil yang lebih baik, terutama dengan data yang lebih banyak.
  • # Strategi 1: Ekstraksi fitur
    

    def buatekstraktorfitur(namamodel, jumlahkelas):

    """Bekukan semua layer kecuali classification head."""

    model = timm.createmodel(namamodel, pretrained=True, numclasses=jumlahkelas)

    # Bekukan semua parameter

    for param in model.parameters():

    param.requiresgrad = False

    # Buka kunci classification head

    if hasattr(model, 'classifier'):

    for param in model.classifier.parameters():

    param.requiresgrad = True

    elif hasattr(model, 'fc'):

    for param in model.fc.parameters():

    param.requiresgrad = True

    elif hasattr(model, 'head'):

    for param in model.head.parameters():

    param.requiresgrad = True

    return model

    Strategi 2: Fine-tuning penuh dengan learning rate diskriminatif

    def buatmodelfinetuning(namamodel, jumlahkelas):

    """Semua layer dapat dilatih, tetapi dengan learning rate berbeda."""

    model = timm.createmodel(namamodel, pretrained=True, numclasses=jumlahkelas)

    # Semua parameter dapat dilatih secara default

    return model


    Memilih Model Pre-trained

    Perbandingan Arsitektur Populer

    import timm
    
    

    def bandingkanmodel():

    """Membandingkan model populer berdasarkan ukuran, kecepatan, dan akurasi."""

    infomodel = {

    "resnet50": {

    "params": "25.6M",

    "top1imagenet": 80.4,

    "kecepatan": "cepat",

    "kasuspenggunaan": "Serbaguna, baseline yang baik"

    },

    "efficientnetb3": {

    "params": "12.2M",

    "top1imagenet": 82.0,

    "kecepatan": "sedang",

    "kasuspenggunaan": "Tradeoff akurasi-efisiensi terbaik"

    },

    "efficientnetv2s": {

    "params": "21.5M",

    "top1imagenet": 83.9,

    "kecepatan": "sedang",

    "kasuspenggunaan": "EfficientNet yang ditingkatkan, pelatihan lebih cepat"

    },

    "vitbasepatch16224": {

    "params": "86.6M",

    "top1imagenet": 84.5,

    "kecepatan": "lambat",

    "kasuspenggunaan": "Vision Transformer, terbaik dengan dataset besar"

    },

    "convnexttiny": {

    "params": "28.6M",

    "top1imagenet": 82.1,

    "kecepatan": "sedang",

    "kasuspenggunaan": "CNN modern yang menyamai performa ViT"

    },

    }

    for nama, info in infomodel.items():

    print(f"{nama:30s} | Params: {info['params']:>8s} | "

    f"Top-1: {info['top1imagenet']:.1f}% | "

    f"Kecepatan: {info['kecepatan']:>6s} | {info['kasuspenggunaan']}")

    return infomodel

    bandingkanmodel()

    Memuat Model dengan timm

    import timm
    
    

    def muatmodelpretrained(namamodel, jumlahkelas, pretrained=True):

    """

    Memuat model pre-trained dan mengadaptasinya untuk jumlah kelas Anda.

    timm secara otomatis mengganti classification head.

    """

    model = timm.createmodel(

    namamodel,

    pretrained=pretrained,

    numclasses=jumlahkelas,

    )

    # Dapatkan konfigurasi preprocessing spesifik model

    dataconfig = timm.data.resolvemodeldataconfig(model)

    print(f"Model: {namamodel}")

    print(f"Ukuran input: {dataconfig['inputsize']}")

    print(f"Mean: {dataconfig['mean']}")

    print(f"Std: {dataconfig['std']}")

    print(f"Parameter: {sum(p.numel() for p in model.parameters()):,}")

    return model, dataconfig

    model, config = muatmodelpretrained("efficientnetb3", jumlahkelas=10)


    Pemuatan dan Persiapan Data

    Struktur Direktori

    dataset/
    

    train/

    kelasa/

    img001.jpg

    img002.jpg

    kelasb/

    img003.jpg

    img004.jpg

    val/

    kelasa/

    img005.jpg

    kelasb/

    img006.jpg

    Membuat Data Loader

    import torch
    

    from torch.utils.data import DataLoader

    from torchvision import datasets

    import albumentations as A

    from albumentations.pytorch import ToTensorV2

    import numpy as np

    class DatasetAlbumentations(torch.utils.data.Dataset):

    """Membungkus dataset torchvision dengan transformasi Albumentations."""

    def init(self, rootdir, transform=None):

    self.dataset = datasets.ImageFolder(rootdir)

    self.transform = transform

    self.classes = self.dataset.classes

    def len(self):

    return len(self.dataset)

    def getitem(self, idx):

    image, label = self.dataset[idx]

    image = np.array(image)

    if self.transform:

    augmented = self.transform(image=image)

    image = augmented["image"]

    return image, label

    def buatdataloader(dirdata, ukuraninput=224, batchsize=32, numworkers=4):

    """Membuat data loader pelatihan dan validasi dengan augmentasi yang tepat."""

    transformtrain = A.Compose([

    A.RandomResizedCrop(height=ukuraninput, width=ukuraninput, scale=(0.8, 1.0)),

    A.HorizontalFlip(p=0.5),

    A.VerticalFlip(p=0.1),

    A.Rotate(limit=15, p=0.3),

    A.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1, p=0.5),

    A.GaussNoise(varlimit=(10, 50), p=0.2),

    A.CoarseDropout(maxholes=8, maxheight=16, maxwidth=16, p=0.2),

    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),

    ToTensorV2(),

    ])

    transformval = A.Compose([

    A.Resize(height=int(ukuraninput 1.14), width=int(ukuraninput 1.14)),

    A.CenterCrop(height=ukuraninput, width=ukuraninput),

    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),

    ToTensorV2(),

    ])

    datasettrain = DatasetAlbumentations(f"{dirdata}/train", transform=transformtrain)

    datasetval = DatasetAlbumentations(f"{dirdata}/val", transform=transformval)

    loadertrain = DataLoader(

    datasettrain, batchsize=batchsize, shuffle=True,

    numworkers=numworkers, pinmemory=True, droplast=True,

    )

    loaderval = DataLoader(

    datasetval, batchsize=batchsize, shuffle=False,

    numworkers=numworkers, pinmemory=True,

    )

    print(f"Sampel pelatihan: {len(datasettrain)}")

    print(f"Sampel validasi: {len(datasetval)}")

    print(f"Kelas: {datasettrain.classes}")

    return loadertrain, loaderval, datasettrain.classes

    loadertrain, loaderval, kelas = buatdataloader("./dataset", ukuraninput=300, batchsize=32)


    Fine-tuning dengan PyTorch

    Mengatur Konfigurasi Pelatihan

    import torch.nn as nn
    

    import torch.optim as optim

    from torch.optim.lrscheduler import CosineAnnealingLR

    def setuppelatihan(model, jumlahepoch=30, lr=1e-4, weightdecay=1e-4):

    """Mengkonfigurasi optimizer, fungsi loss, dan scheduler."""

    # Learning rate diskriminatif: lebih rendah untuk layer pre-trained,

    # lebih tinggi untuk classification head yang baru

    paramhead = []

    parambackbone = []

    for nama, param in model.namedparameters():

    if not param.requiresgrad:

    continue

    if "classifier" in nama or "fc" in nama or "head" in nama:

    paramhead.append(param)

    else:

    parambackbone.append(param)

    paramgroups = [

    {"params": parambackbone, "lr": lr 0.1}, # LR 10x lebih rendah untuk backbone

    {"params": paramhead, "lr": lr}, # LR penuh untuk head

    ]

    optimizer = optim.AdamW(paramgroups, weightdecay=weightdecay)

    scheduler = CosineAnnealingLR(

    optimizer,

    Tmax=jumlahepoch,

    etamin=lr 0.01,

    )

    criterion = nn.CrossEntropyLoss(labelsmoothing=0.1)

    return optimizer, scheduler, criterion

    optimizer, scheduler, criterion = setuppelatihan(model, jumlahepoch=30, lr=1e-4)


    Implementasi Training Loop

    import time
    

    from collections import defaultdict

    class Pelatih:

    """Pipeline pelatihan lengkap untuk klasifikasi gambar."""

    def init(self, model, optimizer, scheduler, criterion, device="cuda"):

    self.model = model.to(device)

    self.optimizer = optimizer

    self.scheduler = scheduler

    self.criterion = criterion

    self.device = device

    self.riwayat = defaultdict(list)

    self.akurasivalterbaik = 0.0

    def latihsatuepoch(self, trainloader):

    """Melatih selama satu epoch."""

    self.model.train()

    totalloss = 0.0

    benar = 0

    total = 0

    for batchidx, (gambar, label) in enumerate(trainloader):

    gambar = gambar.to(self.device)

    label = label.to(self.device)

    # Forward pass

    output = self.model(gambar)

    loss = self.criterion(output, label)

    # Backward pass

    self.optimizer.zerograd()

    loss.backward()

    # Gradient clipping untuk stabilitas

    torch.nn.utils.clipgradnorm(self.model.parameters(), maxnorm=1.0)

    self.optimizer.step()

    # Lacak metrik

    totalloss += loss.item() gambar.size(0)

    , prediksi = output.max(1)

    total += label.size(0)

    benar += prediksi.eq(label).sum().item()

    epochloss = totalloss / total

    epochacc = 100.0 benar / total

    return epochloss, epochacc

    @torch.nograd()

    def validasi(self, valloader):

    """Mengevaluasi pada set validasi."""

    self.model.eval()

    totalloss = 0.0

    benar = 0

    total = 0

    for gambar, label in valloader:

    gambar = gambar.to(self.device)

    label = label.to(self.device)

    output = self.model(gambar)

    loss = self.criterion(output, label)

    totalloss += loss.item() gambar.size(0)

    , prediksi = output.max(1)

    total += label.size(0)

    benar += prediksi.eq(label).sum().item()

    epochloss = totalloss / total

    epochacc = 100.0 benar / total

    return epochloss, epochacc

    def latih(self, trainloader, valloader, jumlahepoch, savepath="modelterbaik.pth"):

    """Training loop lengkap dengan validasi dan penyimpanan model."""

    print(f"{'Epoch':>6} | {'Loss Train':>10} | {'Akur Train':>10} | "

    f"{'Loss Val':>10} | {'Akur Val':>10} | {'LR':>10} | {'Waktu':>6}")

    print("-" 78)

    for epoch in range(jumlahepoch):

    waktumulai = time.time()

    trainloss, trainacc = self.latihsatuepoch(trainloader)

    valloss, valacc = self.validasi(valloader)

    self.scheduler.step()

    lrsekarang = self.optimizer.paramgroups[0]["lr"]

    durasi = time.time() - waktumulai

    # Catat riwayat

    self.riwayat["trainloss"].append(trainloss)

    self.riwayat["trainacc"].append(trainacc)

    self.riwayat["valloss"].append(valloss)

    self.riwayat["valacc"].append(valacc)

    print(f"{epoch + 1:>6} | {trainloss:>10.4f} | {trainacc:>9.2f}% | "

    f"{valloss:>10.4f} | {valacc:>9.2f}% | {lrsekarang:>10.6f} | "

    f"{durasi:>5.1f}s")

    # Simpan model terbaik

    if valacc > self.akurasivalterbaik:

    self.akurasivalterbaik = valacc

    torch.save({

    "epoch": epoch,

    "modelstatedict": self.model.statedict(),

    "optimizerstatedict": self.optimizer.statedict(),

    "valacc": valacc,

    "valloss": valloss,

    }, savepath)

    print(f" -> Model terbaik disimpan (akurasival: {valacc:.2f}%)")

    print(f"\nAkurasi validasi terbaik: {self.akurasivalterbaik:.2f}%")

    return self.riwayat

    Jalankan pelatihan

    pelatih = Pelatih(model, optimizer, scheduler, criterion)

    riwayat = pelatih.latih(loadertrain, loaderval, jumlahepoch=30)

    Visualisasi Riwayat Pelatihan

    import matplotlib.pyplot as plt
    
    

    def plotriwayatpelatihan(riwayat):

    """Membuat plot metrik pelatihan dan validasi."""

    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))

    # Plot loss

    ax1.plot(riwayat["trainloss"], label="Loss Pelatihan")

    ax1.plot(riwayat["valloss"], label="Loss Validasi")

    ax1.setxlabel("Epoch")

    ax1.setylabel("Loss")

    ax1.settitle("Loss per Epoch")

    ax1.legend()

    ax1.grid(True)

    # Plot akurasi

    ax2.plot(riwayat["trainacc"], label="Akurasi Pelatihan")

    ax2.plot(riwayat["valacc"], label="Akurasi Validasi")

    ax2.setxlabel("Epoch")

    ax2.setylabel("Akurasi (%)")

    ax2.settitle("Akurasi per Epoch")

    ax2.legend()

    ax2.grid(True)

    plt.tightlayout()

    plt.savefig("riwayatpelatihan.png", dpi=150)

    plt.close()

    plotriwayatpelatihan(riwayat)


    Evaluasi dan Metrik

    from sklearn.metrics import classificationreport, confusionmatrix
    

    import seaborn as sns

    import numpy as np

    @torch.nograd()

    def evaluasimodel(model, valloader, namakelas, device="cuda"):

    """Menghasilkan metrik evaluasi yang komprehensif."""

    model.eval()

    semuaprediksi = []

    semualabel = []

    semuaprobabilitas = []

    for gambar, label in valloader:

    gambar = gambar.to(device)

    output = model(gambar)

    probabilitas = torch.softmax(output, dim=1)

    , prediksi = output.max(1)

    semuaprediksi.extend(prediksi.cpu().numpy())

    semualabel.extend(label.numpy())

    semuaprobabilitas.extend(probabilitas.cpu().numpy())

    semuaprediksi = np.array(semuaprediksi)

    semualabel = np.array(semualabel)

    # Laporan klasifikasi

    print("Laporan Klasifikasi:")

    print(classificationreport(semualabel, semuaprediksi,

    targetnames=namakelas, digits=3))

    # Confusion matrix

    cm = confusionmatrix(semualabel, semuaprediksi)

    fig, ax = plt.subplots(figsize=(10, 8))

    sns.heatmap(cm, annot=True, fmt="d", cmap="Blues",

    xticklabels=namakelas, yticklabels=namakelas, ax=ax)

    ax.setxlabel("Prediksi")

    ax.setylabel("Aktual")

    ax.settitle("Confusion Matrix")

    plt.tightlayout()

    plt.savefig("confusionmatrix.png", dpi=150)

    plt.close()

    akurasi = (semuaprediksi == semualabel).mean() 100

    print(f"\nAkurasi Keseluruhan: {akurasi:.2f}%")

    return akurasi

    evaluasimodel(model, loaderval, kelas)


    Ekspor dan Deployment Model

    Ekspor ke ONNX

    def eksporkeonnx(model, ukuraninput=224, outputpath="model.onnx"):
    

    """Mengekspor model ke format ONNX untuk deployment lintas platform."""

    model.eval()

    model.cpu()

    dummyinput = torch.randn(1, 3, ukuraninput, ukuraninput)

    torch.onnx.export(

    model,

    dummyinput,

    outputpath,

    exportparams=True,

    opsetversion=17,

    doconstantfolding=True,

    inputnames=["input"],

    outputnames=["output"],

    dynamicaxes={

    "input": {0: "batchsize"},

    "output": {0: "batchsize"},

    },

    )

    print(f"Model diekspor ke {outputpath}")

    eksporkeonnx(model, ukuraninput=300)

    Inferensi dengan ONNX Runtime

    import onnxruntime as ort
    

    import cv2

    import numpy as np

    class KlasifierONNX:

    """Menjalankan inferensi menggunakan ONNX Runtime."""

    def init(self, modelpath, namakelas, ukuraninput=224):

    self.session = ort.InferenceSession(

    modelpath,

    providers=["CUDAExecutionProvider", "CPUExecutionProvider"]

    )

    self.namakelas = namakelas

    self.ukuraninput = ukuraninput

    self.namainput = self.session.getinputs()[0].name

    def preprocess(self, imagepath):

    """Memproses gambar untuk inferensi."""

    gambar = cv2.imread(imagepath)

    gambar = cv2.cvtColor(gambar, cv2.COLORBGR2RGB)

    gambar = cv2.resize(gambar, (self.ukuraninput, self.ukuraninput))

    gambar = gambar.astype(np.float32) / 255.0

    mean = np.array([0.485, 0.456, 0.406])

    std = np.array([0.229, 0.224, 0.225])

    gambar = (gambar - mean) / std

    gambar = gambar.transpose(2, 0, 1) # HWC -> CHW

    gambar = np.expanddims(gambar, axis=0).astype(np.float32)

    return gambar

    def prediksi(self, imagepath, topk=5):

    """Menjalankan inferensi dan mengembalikan prediksi top-k."""

    inputtensor = self.preprocess(imagepath)

    output = self.session.run(None, {self.namainput: inputtensor})

    logits = output[0][0]

    # Terapkan softmax

    explogits = np.exp(logits - np.max(logits))

    probabilitas = explogits / explogits.sum()

    indekstop = np.argsort(probabilitas)[::-1][:topk]

    hasil = [

    {"kelas": self.namakelas[i], "probabilitas": float(probabilitas[i])}

    for i in indekstop

    ]

    return hasil

    Penggunaan

    klasifier = KlasifierONNX("model.onnx", namakelas=kelas, ukuraninput=300)

    prediksi = klasifier.prediksi("gambartest.jpg")

    for pred in prediksi:

    print(f"{pred['kelas']}: {pred['probabilitas']:.4f}")

    Deployment dengan FastAPI

    from fastapi import FastAPI, File, UploadFile
    

    import io

    from PIL import Image

    app = FastAPI()

    klasifier = KlasifierONNX("model.onnx", namakelas=kelas, ukuraninput=300)

    @app.post("/klasifikasi")

    async def klasifikasigambar(file: UploadFile = File(...)):

    """Mengklasifikasikan gambar yang diunggah."""

    konten = await file.read()

    gambar = np.array(Image.open(io.BytesIO(konten)).convert("RGB"))

    temppath = "/tmp/tempklasifikasi.jpg"

    cv2.imwrite(temppath, cv2.cvtColor(gambar, cv2.COLORRGB2BGR))

    prediksi = klasifier.prediksi(temppath, topk=5)

    return {"prediksi": prediksi}


    Praktik Terbaik

  • Mulai dengan ekstraksi fitur, lalu fine-tune. Mulailah dengan melatih hanya head untuk menetapkan baseline. Kemudian buka kunci layer secara bertahap untuk fine-tuning guna mendapatkan akurasi lebih tinggi.
  • Gunakan learning rate diskriminatif. Terapkan learning rate yang lebih rendah untuk layer pre-trained (biasanya 10x lebih rendah) dan rate yang lebih tinggi untuk head baru. Ini mencegah catastrophic forgetting sambil tetap mengadaptasi fitur.
  • Terapkan label smoothing (0.1). Ini mencegah prediksi yang terlalu percaya diri dan berfungsi sebagai regularizer, terutama dengan dataset yang lebih kecil.
  • Gunakan cosine annealing atau one-cycle learning rate schedule. Jadwal ini secara konsisten menghasilkan hasil yang lebih baik dibandingkan step decay.
  • Selalu terapkan augmentasi validasi yang tepat. Gunakan hanya transformasi deterministik (resize + center crop + normalize) untuk validasi -- jangan pernah gunakan augmentasi acak.
  • Pantau overfitting. Jika akurasi pelatihan terus naik sementara akurasi validasi stagnan, tingkatkan regularisasi: lebih banyak augmentasi, dropout lebih tinggi, atau weight decay lebih besar.
  • Pilih model yang tepat untuk batasan Anda. EfficientNet-B0/B3 untuk deployment di edge, ConvNeXt atau ViT untuk inferensi di server. Lebih banyak parameter tidak selalu berarti hasil yang lebih baik dengan data terbatas.
  • Gunakan mixed precision training dengan torch.amp.autocast dan GradScaler untuk mengurangi penggunaan memori dan mempercepat pelatihan sebesar 30-50% pada GPU modern.

  • Kesimpulan

    Transfer learning adalah pendekatan paling praktis untuk klasifikasi gambar di sebagian besar aplikasi dunia nyata. Dengan memanfaatkan model pre-trained, Anda dapat mencapai akurasi yang sangat baik dengan data dan komputasi minimal. Alur kerja yang dibahas dalam tutorial ini -- pemilihan model, persiapan data, fine-tuning dengan learning rate diskriminatif, evaluasi yang ketat, dan ekspor ONNX -- merepresentasikan pipeline yang siap produksi dan dapat diterapkan untuk tugas klasifikasi apapun.

    Poin-poin kunci: mulai dari yang sederhana (ekstraksi fitur), ukur dengan cermat (validasi yang tepat), dan iterasi secara bertahap (pembukaan kunci layer bertahap, penyesuaian hyperparameter). Dengan library timm yang menyediakan akses ke ratusan arsitektur state-of-the-art, hambatan utama bukan lagi ketersediaan model tetapi kualitas data dan metodologi pelatihan.

    Artikel Terkait

    DeepSpeed: Melatih Model Raksasa di GPU Terbatas dengan ZeRO dan Offload

    DeepSpeed: Melatih Model Raksasa di GPU Terbatas dengan ZeRO dan Offload Halo temen-temen, kali ini kita masuk ke topik ...

    HuggingFace Accelerate: Satu Kode PyTorch untuk CPU, Satu GPU, Multi-GPU, dan Mixed Precision

    HuggingFace Accelerate: Satu Kode PyTorch untuk CPU, Satu GPU, Multi-GPU, dan Mixed Precision Halo temen-temen, kali ini...

    timm: Gudang 1000+ Model Computer Vision Siap Pakai di PyTorch

    timm: Gudang 1000+ Model Computer Vision Siap Pakai di PyTorch Halo temen-temen, di tutorial kali ini aku mau ngajak kal...

    Depth Anything V2: Prediksi Kedalaman dari Satu Gambar dengan Python

    Depth Anything V2: Prediksi Kedalaman dari Satu Gambar dengan Python Halo temen-temen, di tutorial kali ini aku mau ngaj...