Dua bahan proyek kini tersedia: backbone terlatih dari Bagian 1 dan 30 sampel tugas B dari Bagian 2. Bagian ini merangkainya lewat tiga langkah baku transfer learning — muat bobot, bekukan backbone, latih head baru — semuanya di dalam src/transfer.py. Mekanisme pembekuan adalah intinya: atribut requires_grad=False memberi tahu autograd agar tidak menghitung gradien bagi parameter backbone, sehingga optimizer hanya mengubah head dan fitur hasil pelatihan tugas A terjaga utuh.

Membekukan Backbone, Memasang Head Baru #

Dua fungsi pertama pada src/transfer.py melakukan persis alur tersebut:

python src/transfer.py — fungsi muat_backbone dan buat_model_transfer
def muat_backbone():
    """Muat backbone terlatih; latih tugas A dulu bila berkas belum ada."""
    if not BERKAS_BACKBONE.exists():
        latih_backbone(cetak=False)
    backbone = CNNBackbone()
    backbone.load_state_dict(
        torch.load(BERKAS_BACKBONE, map_location="cpu", weights_only=True)
    )
    return backbone


def buat_model_transfer():
    """Bekukan backbone lalu pasang head baru; kembalikan model utuh."""
    backbone = muat_backbone()
    for p in backbone.parameters():
        p.requires_grad_(False)          # inti transfer learning: freeze
    torch.manual_seed(SEED)              # init head tetap sama apa pun
    kepala_b = nn.Linear(512, 5)         # keadaan RNG sebelumnya
    return ModelGabung(backbone, kepala_b)

Mari kita telaah fungsi di atas baris per baris:

  1. if not BERKAS_BACKBONE.exists(): skrip bersifat mandiri — pembaca yang belum menjalankan pretrained.py tetap bisa menjalankan transfer.py karena backbone dilatih otomatis saat diperlukan; bila berkas sudah ada, pemuatan hanya butuh sepersekian detik.
  2. backbone.load_state_dict(torch.load(..., weights_only=True)): bobot dari /tmp/backbone_digit.pt mengisi arsitektur CNNBackbone yang masih acak — weights_only=True membatasi checkpoint berisi tensor saja, mode aman bila checkpoint berasal dari sumber lain.
  3. p.requires_grad_(False): seluruh 4800 parameter backbone dibekukan dalam satu perulangan; setelah baris ini, autograd berhenti melacaknya dan .grad parameter backbone akan tetap None.
  4. torch.manual_seed(SEED) sebelum nn.Linear(512, 5): inisialisasi head dibuat deterministik apa pun urutan pemanggilan sebelumnya, sehingga hasil transfer.py identik baik dijalankan sendiri maupun lewat bandingkan.py.

Melatih Head Saja #

Fungsi latih_transfer menyaring parameter yang masih belajar sebelum membuat optimizer — blok pencetakan ringkas di antara kedua bagian ini dilewati agar fokus tetap pada logika:

python src/transfer.py — inti latih_transfer (indentasi asli ada di dalam fungsi)
model = buat_model_transfer()
param_dilatih = [p for p in model.parameters() if p.requires_grad]
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(param_dilatih, lr=1e-3)

gen = torch.Generator().manual_seed(SEED)
for _ in range(jumlah_epoch):
    acakan = torch.randperm(len(X_latih), generator=gen)
    latih_epoch(model, optimizer, criterion,
                X_latih, y_latih, acakan, UKURAN_BATCH_B)

Baris param_dilatih = [p for p in model.parameters() if p.requires_grad] adalah penjaga kejujuran eksperimen: optimizer hanya menerima 2565 parameter head. PyTorch memang melewatkan parameter tanpa gradien di dalam torch.optim.Adam, tetapi penyaringan eksplisit membuat kontrak “hanya head yang belajar” terlihat langsung di kode — dan angka 2565 parameter dilatih pada keluaran berasal dari daftar ini, bukan dari asumsi.

Menjalankan Transfer Learning #

bash
python3 src/transfer.py

Output yang Dihasilkan:

text
data tugas B : 30 latih + 200 uji (digit 5-9)
backbone     : 4800 parameter terbekukan (requires_grad=False)
head baru    : 2565 parameter dilatih
pelatihan    : Adam lr=0.001, batch 15, 10 epoch (hanya head)
akurasi uji tugas B: 0.835

Dari 30 sampel latih (enam gambar per kelas secara rata-rata), model mencapai akurasi 0.835 pada 200 sampel uji — 167 gambar digit 5-9 dikenali benar tanpa satu pun parameter backbone berubah. Keluaran juga mencatat pembagian peran secara eksplisit: 4800 parameter beku, 2565 parameter dilatih.

Angka 0.835 baru bermakna setelah ada pembanding. Bagian berikutnya melatih arsitektur yang sama dari bobot acak lalu menyandingkan keduanya dalam satu tabel.