File size: 81,128 Bytes
5bc5196 | 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"gpuType":"T4","authorship_tag":"ABX9TyMY0NlsDNafJ25zhsFIBq5w"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"},"accelerator":"GPU"},"cells":[{"cell_type":"code","source":["import os\n","import math\n","import torch\n","import torch.nn as nn\n","import torch.nn.functional as F\n","import torch.optim as optim\n","from torch.utils.data import DataLoader, Dataset, random_split\n","import torchvision\n","import torchvision.transforms as transforms\n","\n","# ==============================================================================\n","# 1. PIPELINE DATA & PREPROCESSING (CALTECH-256)\n","# ==============================================================================\n","\n","# Menggunakan standardisasi mean & std global ImageNet karena karakteristik objek mirip\n","transform_train = transforms.Compose([\n"," transforms.Lambda(lambda image: image.convert(\"RGB\")), # Mengatasi gambar berformat Grayscale/CMYK di Caltech256\n"," transforms.Resize((96, 96)), # Menyamakan resolusi input ke cetakan V7\n"," transforms.RandomHorizontalFlip(), # Augmentasi geometri ringan\n"," transforms.ToTensor(),\n"," transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))\n","])\n","\n","transform_test = transforms.Compose([\n"," transforms.Lambda(lambda image: image.convert(\"RGB\")),\n"," transforms.Resize((96, 96)),\n"," transforms.ToTensor(),\n"," transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))\n","])\n","\n","# Load dataset mentah tanpa transform awal agar pembagian subset tidak cacat tipe data\n","full_dataset = torchvision.datasets.Caltech256(root='./data', download=True)\n","\n","# Membagi porsi dataset: 80% Training, 20% Testing secara acak\n","total_len = len(full_dataset)\n","train_len = int(0.8 * total_len)\n","test_len = total_len - train_len\n","train_subset, test_subset = random_split(full_dataset, [train_len, test_len])\n","\n","# BERHASIL DIREPARASI: Wrapper Dataset agar DataLoader menerima Tensor, bukan PIL Image\n","class ApplyTransform(Dataset):\n"," def __init__(self, dataset_subset, transform=None):\n"," self.dataset_subset = dataset_subset\n"," self.transform = transform\n","\n"," def __getitem__(self, index):\n"," x, y = self.dataset_subset[index]\n"," if self.transform:\n"," x = self.transform(x)\n"," return x, y\n","\n"," def __len__(self):\n"," return len(self.dataset_subset)\n","\n","# Memasang pipa transformasi ke masing-masing subset\n","train_dataset = ApplyTransform(train_subset, transform=transform_train)\n","test_dataset = ApplyTransform(test_subset, transform=transform_test)\n","\n","train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2)\n","val_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=2)\n","\n","print(f\"β
Dataset Terkonfigurasi! Luas Train: {len(train_dataset)} | Luas Test: {len(test_dataset)}\")\n","\n","\n","# ==============================================================================\n","# 2. CORE LAYER: LOOKTHEM CORE COGNITION LAYER\n","# ==============================================================================\n","class LookThemLayer(nn.Module):\n"," def __init__(self, num_tokens, in_features, hidden_dim):\n"," super(LookThemLayer, self).__init__()\n"," self.num_tokens = num_tokens\n"," self.in_features = in_features\n","\n"," # Modul Proyeksi Relasional Pasangan 1\n"," self.mod1_w1 = nn.Parameter(torch.randn(num_tokens, in_features, hidden_dim))\n"," self.mod1_b1 = nn.Parameter(torch.zeros(num_tokens, hidden_dim))\n"," self.mod1_w2 = nn.Parameter(torch.randn(num_tokens, hidden_dim, 1))\n"," self.mod1_b2 = nn.Parameter(torch.zeros(num_tokens, 1))\n","\n"," # Modul Proyeksi Relasional Pasangan 2 (Komparator Rasio)\n"," self.mod2_w1 = nn.Parameter(torch.randn(num_tokens, in_features, hidden_dim))\n"," self.mod2_b1 = nn.Parameter(torch.zeros(num_tokens, hidden_dim))\n"," self.mod2_w2 = nn.Parameter(torch.randn(num_tokens, hidden_dim, 1))\n"," self.mod2_b2 = nn.Parameter(torch.zeros(num_tokens, 1))\n","\n"," # Bobot Transformasi Interaksi Akhir\n"," self.trans_w = nn.Parameter(torch.randn(num_tokens, 1, 1))\n"," self.trans_b = nn.Parameter(torch.zeros(num_tokens, 1))\n"," self._init_weights()\n","\n"," def _init_weights(self):\n"," for w in [self.mod1_w1, self.mod2_w1, self.mod1_w2, self.mod2_w2, self.trans_w]:\n"," nn.init.kaiming_uniform_(w, a=math.sqrt(5))\n","\n"," def forward(self, x):\n"," N = self.num_tokens\n","\n"," # Ekstraksi fitur internal per token via Einstein Summation (Einsum)\n"," h1 = torch.einsum('bti,tij->btj', x, self.mod1_w1) + self.mod1_b1\n"," out_m1 = torch.einsum('btj,tjk->btk', F.gelu(h1), self.mod1_w2) + self.mod1_b2\n","\n"," h2 = torch.einsum('bti,tij->btj', x, self.mod2_w1) + self.mod2_b1\n"," out_m2 = torch.einsum('btj,tjk->btk', F.gelu(h2), self.mod2_w2) + self.mod2_b2\n","\n"," # Ratio Based Attention: Membandingkan pecahan nilai antar-token\n"," out_m2_safe = out_m2 + 1e-5\n"," compare = torch.tanh(out_m1.unsqueeze(2) / out_m2_safe.unsqueeze(1))\n"," compare2 = torch.tanh(out_m1.unsqueeze(1) / out_m2_safe.unsqueeze(2))\n","\n"," bias_reshaped = self.trans_b.view(1, 1, N, 1)\n"," trans_compare = torch.einsum('bije,jef->bijf', compare, self.trans_w) + bias_reshaped\n"," trans_compare2 = torch.einsum('bije,jef->bijf', compare2, self.trans_w) + bias_reshaped\n","\n"," # Penggabungan matriks interaksi afinitas\n"," interaksi = (trans_compare * x.unsqueeze(2) + trans_compare2 * x.unsqueeze(1)) / 2\n","\n"," # Masking Self-Token: Memaksa token hanya memperhatikan hubungan dengan token LAIN\n"," mask = 1.0 - torch.eye(N, device=x.device)\n"," interaksi_masked = interaksi * mask.view(1, N, N, 1)\n","\n"," return interaksi_masked.sum(dim=2) / (N - 1.0)\n","\n","\n","# ==============================================================================\n","# 3. INDUK ARSITEKTUR: LOOKTHEM V7 (JEPA LATENT-CORRUPTER EDITION)\n","# ==============================================================================\n","class LookThemV7_Caltech256(nn.Module):\n"," def __init__(self):\n"," super(LookThemV7_Caltech256, self).__init__()\n","\n"," # --- STREAM A: FITUR MAKRO (STRIDE AGRESIF) ---\n"," self.stream_a = nn.Sequential(\n"," nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1), # [B, 16, 48, 48]\n"," nn.BatchNorm2d(16), nn.GELU(),\n"," nn.Conv2d(16, 32, kernel_size=3, stride=2, padding=1), # [B, 32, 24, 24]\n"," nn.BatchNorm2d(32), nn.GELU(),\n"," nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # [B, 64, 128, 12]\n"," nn.BatchNorm2d(64), nn.GELU(),\n"," nn.AdaptiveMaxPool2d((8, 8)) # Output spasial dikunci ke [8x8 = 64 Token]\n"," )\n","\n"," # --- STREAM B: FITUR MIKRO/LOKAL (STRIDE HALUS) ---\n"," self.stream_b = nn.Sequential(\n"," nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1), # [B, 16, 96, 96]\n"," nn.BatchNorm2d(16), nn.GELU(),\n"," nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1), # [B, 32, 96, 96]\n"," nn.BatchNorm2d(32), nn.GELU(),\n"," nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # [B, 64, 48, 48]\n"," nn.BatchNorm2d(64), nn.GELU(),\n"," nn.AdaptiveMaxPool2d((8, 8)) # Output spasial dikunci ke [8x8 = 64 Token]\n"," )\n","\n"," # Brain Layers (64 Token dengan dimensi internal 64)\n"," self.lookthemA = LookThemLayer(num_tokens=64, in_features=64, hidden_dim=16)\n"," self.lookthemB = LookThemLayer(num_tokens=64, in_features=64, hidden_dim=16)\n","\n"," # Kombinator Relasional Utama (Menerima asimetris gabungan 128 dimensi fitur)\n"," self.lookthem = LookThemLayer(num_tokens=64, in_features=128, hidden_dim=32)\n","\n"," # Mengurangi dimensi fitur gabungan secara linier dari 128 ke 32 sebelum klasifikasi\n"," self.compressor = nn.AdaptiveAvgPool1d(32)\n","\n"," # --- CLASSIFIER (Target Kelas Caltech-256: 257 Output) ---\n"," self.classifier = nn.Sequential(\n"," nn.Flatten(),\n"," nn.Linear(64 * 32, 1024),\n"," nn.ReLU(),\n"," nn.Dropout(0.4),\n"," nn.Linear(1024, 512),\n"," nn.ReLU(),\n"," nn.Dropout(0.4),\n"," nn.Linear(512, 257) # 256 Objek + 1 Clutter Background\n"," )\n","\n"," # INSPIRED BY JEPA: Menghancurkan 30% informasi token laten secara acak (Stochastic Masking)\n"," self.imageCorrupter = nn.Dropout(0.3)\n","\n"," def forward(self, x):\n"," batch_size = x.size(0)\n","\n"," # 1. Ekstrak Jalur Stream A (Makro)\n"," feat_a = self.stream_a(x)\n"," feat_a_flat = feat_a.view(batch_size, 64, 64)\n"," feat_a_tokens = feat_a_flat.transpose(1, 2) # Hasil akhir susunan: [B, 64 Token, 64 Fitur]\n","\n"," # PERBAIKAN: imageCorrupter merusak token laten, BUKAN piksel mentah RGB input!\n"," feat_a_tokens = self.imageCorrupter(feat_a_tokens)\n"," feat_a_lt = self.lookthemA(feat_a_tokens)\n","\n"," # 2. Ekstrak Jalur Stream B (Mikro)\n"," feat_b = self.stream_b(x)\n"," feat_b_tokens = feat_b.view(batch_size, 64, 64).transpose(1, 2)\n","\n"," # Merusak token di cabang B secara independen (Multi-view Corrupted Context)\n"," feat_b_tokens = self.imageCorrupter(feat_b_tokens)\n"," feat_b_lt = self.lookthemB(feat_b_tokens)\n","\n"," # 3. Penggabungan Asimetris Tingkat Fitur (Lebar fitur membesar, urutan koordinat spasial tetap 64)\n"," tokens_combined = torch.cat([feat_a_lt, feat_b_lt], dim=2) # Dimensi keluaran: [B, 64 Token, 128 Fitur]\n","\n"," # 4. Kognisi Relasional & Pematangan Klasifikasi\n"," out_lookthem = self.lookthem(tokens_combined)\n"," compressed = self.compressor(out_lookthem)\n"," return self.classifier(compressed)\n","\n","\n","# ==============================================================================\n","# 4. RUNTIME TRAINING SYSTEM WITH AUTO-RESUME\n","# ==============================================================================\n","device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n","model = LookThemV7_Caltech256().to(device)\n","\n","criterion = nn.CrossEntropyLoss()\n","optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)\n","scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20)\n","\n","start_epoch = 0\n","checkpoint_path = \"lookthem_caltech_checkpoint.pth\"\n","\n","# Fitur Auto-Resume anti putus koneksi Colab/Termux\n","if os.path.exists(checkpoint_path):\n"," print(\"πΎ Checkpoint V7 ditemukan! Memulihkan kondisi mesin eksperimen...\")\n"," checkpoint = torch.load(checkpoint_path)\n"," model.load_state_dict(checkpoint['model_state_dict'])\n"," optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n"," scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n"," start_epoch = checkpoint['epoch']\n"," print(f\"βΆοΈ Berhasil resume dari ambang batas Epoch ke-{start_epoch+1}\")\n","\n","print(f\"π Memulai pelatihan LookThem V7 (JEPA-Token Engine) di device: {device}...\")\n","\n","for epoch in range(start_epoch, 20):\n"," model.train()\n"," total_loss, correct, total = 0, 0, 0\n","\n"," for data, target in train_loader:\n"," data, target = data.to(device), target.to(device)\n","\n"," optimizer.zero_grad()\n"," output = model(data)\n"," loss = criterion(output, target)\n"," loss.backward()\n"," optimizer.step()\n","\n"," total_loss += loss.item()\n"," _, predicted = output.max(1)\n"," total += target.size(0)\n"," correct += predicted.eq(target).sum().item()\n","\n"," scheduler.step()\n"," acc = 100. * correct / total\n"," current_lr = optimizer.param_groups[0]['lr']\n"," print(f\"Epoch {epoch+1:02d}/100 -> Train Loss: {total_loss/len(train_loader):.4f} | Train Acc: {acc:.2f}% | LR: {current_lr:.6f}\")\n","\n"," # Mengunci progres ke disk storage setiap kelipatan 5 Epoch\n"," if (epoch + 1) % 5 == 0:\n"," torch.save({\n"," 'epoch': epoch + 1,\n"," 'model_state_dict': model.state_dict(),\n"," 'optimizer_state_dict': optimizer.state_dict(),\n"," 'scheduler_state_dict': scheduler.state_dict(),\n"," }, checkpoint_path)\n"," print(f\"π [SYSTEM-SAVER] Garis aman Epoch {epoch+1} berhasil dibekukan ke disk!\")\n","\n","\n","# ==============================================================================\n","# 5. VALIDASI & EVALUASI AKHIR REAL-WORLD\n","# ==============================================================================\n","model.eval()\n","test_loss, test_correct, test_total = 0, 0, 0\n","\n","print(\"\\nπ Membuka gerbang evaluasi test set Caltech-256...\")\n","with torch.no_grad():\n"," for data, target in val_loader:\n"," data, target = data.to(device), target.to(device)\n"," output = model(data)\n"," loss = criterion(output, target)\n","\n"," test_loss += loss.item()\n"," _, predicted = output.max(1)\n"," test_total += target.size(0)\n"," test_correct += predicted.eq(target).sum().item()\n","\n","final_test_acc = 100. * test_correct / test_total\n","print(\"\\n=== EVALUASI TOTAL SELESAI (LOOKTHEM V7) ===\")\n","print(f\"Final Test Loss: {test_loss/len(val_loader):.4f} | Final Test Accuracy: {final_test_acc:.2f}%\")\n","\n","# Simpan bobot murni siap deploy ke Hugging Face Space Demo\n","torch.save(model.state_dict(), \"LookThem_Caltech256.pth\")\n","print(f\"π File model bersih tersimpan! Ukuran file final: {os.path.getsize('LookThem_Caltech256.pth') / (1024*1024):.2f} MB\")"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"ykKI1t96VLVn","executionInfo":{"status":"ok","timestamp":1779180527226,"user_tz":-420,"elapsed":1289269,"user":{"displayName":"Cici rizky plk","userId":"03714270658772765776"}},"outputId":"c6f9cc08-a264-43dd-ab99-c42bce07f063"},"execution_count":2,"outputs":[{"output_type":"stream","name":"stdout","text":["β
Dataset Terkonfigurasi! Luas Train: 24485 | Luas Test: 6122\n","π Memulai pelatihan LookThem V7 (JEPA-Token Engine) di device: cuda...\n","Epoch 01/100 -> Train Loss: 5.2903 | Train Acc: 4.91% | LR: 0.000994\n","Epoch 02/100 -> Train Loss: 4.9245 | Train Acc: 8.63% | LR: 0.000976\n","Epoch 03/100 -> Train Loss: 4.6806 | Train Acc: 10.59% | LR: 0.000946\n","Epoch 04/100 -> Train Loss: 4.4570 | Train Acc: 12.66% | LR: 0.000905\n","Epoch 05/100 -> Train Loss: 4.2697 | Train Acc: 14.80% | LR: 0.000854\n","π [SYSTEM-SAVER] Garis aman Epoch 5 berhasil dibekukan ke disk!\n","Epoch 06/100 -> Train Loss: 4.0827 | Train Acc: 17.28% | LR: 0.000794\n","Epoch 07/100 -> Train Loss: 3.9384 | Train Acc: 19.01% | LR: 0.000727\n","Epoch 08/100 -> Train Loss: 3.9740 | Train Acc: 18.39% | LR: 0.000655\n","Epoch 09/100 -> Train Loss: 4.0061 | Train Acc: 18.80% | LR: 0.000578\n","Epoch 10/100 -> Train Loss: 3.7903 | Train Acc: 21.23% | LR: 0.000500\n","π [SYSTEM-SAVER] Garis aman Epoch 10 berhasil dibekukan ke disk!\n","Epoch 11/100 -> Train Loss: 3.6362 | Train Acc: 23.42% | LR: 0.000422\n","Epoch 12/100 -> Train Loss: 3.7068 | Train Acc: 22.12% | LR: 0.000345\n","Epoch 13/100 -> Train Loss: 3.3686 | Train Acc: 27.17% | LR: 0.000273\n","Epoch 14/100 -> Train Loss: 3.2990 | Train Acc: 28.29% | LR: 0.000206\n","Epoch 15/100 -> Train Loss: 3.6845 | Train Acc: 23.08% | LR: 0.000146\n","π [SYSTEM-SAVER] Garis aman Epoch 15 berhasil dibekukan ke disk!\n","Epoch 16/100 -> Train Loss: 3.4845 | Train Acc: 25.30% | LR: 0.000095\n","Epoch 17/100 -> Train Loss: 3.2328 | Train Acc: 29.13% | LR: 0.000054\n","Epoch 18/100 -> Train Loss: 3.1660 | Train Acc: 30.08% | LR: 0.000024\n","Epoch 19/100 -> Train Loss: 3.1333 | Train Acc: 30.92% | LR: 0.000006\n","Epoch 20/100 -> Train Loss: 3.1246 | Train Acc: 31.20% | LR: 0.000000\n","π [SYSTEM-SAVER] Garis aman Epoch 20 berhasil dibekukan ke disk!\n","\n","π Membuka gerbang evaluasi test set Caltech-256...\n","\n","=== EVALUASI TOTAL SELESAI (LOOKTHEM V7) ===\n","Final Test Loss: 3.2142 | Final Test Accuracy: 31.04%\n","π File model bersih tersimpan! Ukuran file final: 13.78 MB\n"]}]},{"cell_type":"code","source":["import os\n","import math\n","import torch\n","import torch.nn as nn\n","import torch.nn.functional as F\n","import torch.optim as optim\n","from torch.utils.data import DataLoader, Dataset, random_split\n","import torchvision\n","import torchvision.transforms as transforms\n","\n","# ==============================================================================\n","# 1. PIPELINE DATA & PREPROCESSING (CALTECH-256)\n","# ==============================================================================\n","\n","# Menggunakan standardisasi mean & std global ImageNet karena karakteristik objek mirip\n","transform_train = transforms.Compose([\n"," transforms.Lambda(lambda image: image.convert(\"RGB\")), # Mengatasi gambar berformat Grayscale/CMYK di Caltech256\n"," transforms.Resize((96, 96)), # Menyamakan resolusi input ke cetakan V7\n"," transforms.RandomHorizontalFlip(), # Augmentasi geometri ringan\n"," transforms.ToTensor(),\n"," transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))\n","])\n","\n","transform_test = transforms.Compose([\n"," transforms.Lambda(lambda image: image.convert(\"RGB\")),\n"," transforms.Resize((96, 96)),\n"," transforms.ToTensor(),\n"," transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))\n","])\n","\n","# Load dataset mentah tanpa transform awal agar pembagian subset tidak cacat tipe data\n","full_dataset = torchvision.datasets.Caltech256(root='./data', download=True)\n","\n","# Membagi porsi dataset: 80% Training, 20% Testing secara acak\n","total_len = len(full_dataset)\n","train_len = int(0.8 * total_len)\n","test_len = total_len - train_len\n","train_subset, test_subset = random_split(full_dataset, [train_len, test_len])\n","\n","# BERHASIL DIREPARASI: Wrapper Dataset agar DataLoader menerima Tensor, bukan PIL Image\n","class ApplyTransform(Dataset):\n"," def __init__(self, dataset_subset, transform=None):\n"," self.dataset_subset = dataset_subset\n"," self.transform = transform\n","\n"," def __getitem__(self, index):\n"," x, y = self.dataset_subset[index]\n"," if self.transform:\n"," x = self.transform(x)\n"," return x, y\n","\n"," def __len__(self):\n"," return len(self.dataset_subset)\n","\n","# Memasang pipa transformasi ke masing-masing subset\n","train_dataset = ApplyTransform(train_subset, transform=transform_train)\n","test_dataset = ApplyTransform(test_subset, transform=transform_test)\n","\n","train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2)\n","val_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=2)\n","\n","print(f\"β
Dataset Terkonfigurasi! Luas Train: {len(train_dataset)} | Luas Test: {len(test_dataset)}\")\n","\n","\n","# ==============================================================================\n","# 2. CORE LAYER: LOOKTHEM CORE COGNITION LAYER\n","# ==============================================================================\n","class LookThemLayer(nn.Module):\n"," def __init__(self, num_tokens, in_features, hidden_dim):\n"," super(LookThemLayer, self).__init__()\n"," self.num_tokens = num_tokens\n"," self.in_features = in_features\n","\n"," # Modul Proyeksi Relasional Pasangan 1\n"," self.mod1_w1 = nn.Parameter(torch.randn(num_tokens, in_features, hidden_dim))\n"," self.mod1_b1 = nn.Parameter(torch.zeros(num_tokens, hidden_dim))\n"," self.mod1_w2 = nn.Parameter(torch.randn(num_tokens, hidden_dim, 1))\n"," self.mod1_b2 = nn.Parameter(torch.zeros(num_tokens, 1))\n","\n"," # Modul Proyeksi Relasional Pasangan 2 (Komparator Rasio)\n"," self.mod2_w1 = nn.Parameter(torch.randn(num_tokens, in_features, hidden_dim))\n"," self.mod2_b1 = nn.Parameter(torch.zeros(num_tokens, hidden_dim))\n"," self.mod2_w2 = nn.Parameter(torch.randn(num_tokens, hidden_dim, 1))\n"," self.mod2_b2 = nn.Parameter(torch.zeros(num_tokens, 1))\n","\n"," # Bobot Transformasi Interaksi Akhir\n"," self.trans_w = nn.Parameter(torch.randn(num_tokens, 1, 1))\n"," self.trans_b = nn.Parameter(torch.zeros(num_tokens, 1))\n"," self._init_weights()\n","\n"," def _init_weights(self):\n"," for w in [self.mod1_w1, self.mod2_w1, self.mod1_w2, self.mod2_w2, self.trans_w]:\n"," nn.init.kaiming_uniform_(w, a=math.sqrt(5))\n","\n"," def forward(self, x):\n"," N = self.num_tokens\n","\n"," # Ekstraksi fitur internal per token via Einstein Summation (Einsum)\n"," h1 = torch.einsum('bti,tij->btj', x, self.mod1_w1) + self.mod1_b1\n"," out_m1 = torch.einsum('btj,tjk->btk', F.gelu(h1), self.mod1_w2) + self.mod1_b2\n","\n"," h2 = torch.einsum('bti,tij->btj', x, self.mod2_w1) + self.mod2_b1\n"," out_m2 = torch.einsum('btj,tjk->btk', F.gelu(h2), self.mod2_w2) + self.mod2_b2\n","\n"," # Ratio Based Attention: Membandingkan pecahan nilai antar-token\n"," out_m2_safe = out_m2 + 1e-5\n"," compare = torch.tanh(out_m1.unsqueeze(2) / out_m2_safe.unsqueeze(1))\n"," compare2 = torch.tanh(out_m1.unsqueeze(1) / out_m2_safe.unsqueeze(2))\n","\n"," bias_reshaped = self.trans_b.view(1, 1, N, 1)\n"," trans_compare = torch.einsum('bije,jef->bijf', compare, self.trans_w) + bias_reshaped\n"," trans_compare2 = torch.einsum('bije,jef->bijf', compare2, self.trans_w) + bias_reshaped\n","\n"," # Penggabungan matriks interaksi afinitas\n"," interaksi = (trans_compare * x.unsqueeze(2) + trans_compare2 * x.unsqueeze(1)) / 2\n","\n"," # Masking Self-Token: Memaksa token hanya memperhatikan hubungan dengan token LAIN\n"," mask = 1.0 - torch.eye(N, device=x.device)\n"," interaksi_masked = interaksi * mask.view(1, N, N, 1)\n","\n"," return interaksi_masked.sum(dim=2) / (N - 1.0)\n","\n","\n","# ==============================================================================\n","# 3. INDUK ARSITEKTUR: LOOKTHEM V7 (JEPA LATENT-CORRUPTER EDITION)\n","# ==============================================================================\n","class LookThemV7_Caltech256(nn.Module):\n"," def __init__(self):\n"," super(LookThemV7_Caltech256, self).__init__()\n","\n"," # --- STREAM A: FITUR MAKRO (STRIDE AGRESIF) ---\n"," self.stream_a = nn.Sequential(\n"," nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1), # [B, 16, 48, 48]\n"," nn.BatchNorm2d(16), nn.GELU(),\n"," nn.Conv2d(16, 32, kernel_size=3, stride=2, padding=1), # [B, 32, 24, 24]\n"," nn.BatchNorm2d(32), nn.GELU(),\n"," nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # [B, 64, 128, 12]\n"," nn.BatchNorm2d(64), nn.GELU(),\n"," nn.AdaptiveMaxPool2d((8, 8)) # Output spasial dikunci ke [8x8 = 64 Token]\n"," )\n","\n"," # --- STREAM B: FITUR MIKRO/LOKAL (STRIDE HALUS) ---\n"," self.stream_b = nn.Sequential(\n"," nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1), # [B, 16, 96, 96]\n"," nn.BatchNorm2d(16), nn.GELU(),\n"," nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1), # [B, 32, 96, 96]\n"," nn.BatchNorm2d(32), nn.GELU(),\n"," nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # [B, 64, 48, 48]\n"," nn.BatchNorm2d(64), nn.GELU(),\n"," nn.AdaptiveMaxPool2d((8, 8)) # Output spasial dikunci ke [8x8 = 64 Token]\n"," )\n","\n"," # Brain Layers (64 Token dengan dimensi internal 64)\n"," self.lookthemA = LookThemLayer(num_tokens=64, in_features=64, hidden_dim=16)\n"," self.lookthemB = LookThemLayer(num_tokens=64, in_features=64, hidden_dim=16)\n","\n"," # Kombinator Relasional Utama (Menerima asimetris gabungan 128 dimensi fitur)\n"," self.lookthem = LookThemLayer(num_tokens=64, in_features=128, hidden_dim=32)\n","\n"," # Mengurangi dimensi fitur gabungan secara linier dari 128 ke 32 sebelum klasifikasi\n"," self.compressor = nn.AdaptiveAvgPool1d(32)\n","\n"," # --- CLASSIFIER (Target Kelas Caltech-256: 257 Output) ---\n"," self.classifier = nn.Sequential(\n"," nn.Flatten(),\n"," nn.Linear(64 * 32, 1024),\n"," nn.ReLU(),\n"," nn.Dropout(0.4),\n"," nn.Linear(1024, 512),\n"," nn.ReLU(),\n"," nn.Dropout(0.4),\n"," nn.Linear(512, 257) # 256 Objek + 1 Clutter Background\n"," )\n","\n"," # INSPIRED BY JEPA: Menghancurkan 30% informasi token laten secara acak (Stochastic Masking)\n"," self.imageCorrupter = nn.Dropout(0.3)\n","\n"," def forward(self, x):\n"," batch_size = x.size(0)\n","\n"," # 1. Ekstrak Jalur Stream A (Makro)\n"," feat_a = self.stream_a(x)\n"," feat_a_flat = feat_a.view(batch_size, 64, 64)\n"," feat_a_tokens = feat_a_flat.transpose(1, 2) # Hasil akhir susunan: [B, 64 Token, 64 Fitur]\n","\n"," # PERBAIKAN: imageCorrupter merusak token laten, BUKAN piksel mentah RGB input!\n"," feat_a_tokens = self.imageCorrupter(feat_a_tokens)\n"," feat_a_lt = self.lookthemA(feat_a_tokens)\n","\n"," # 2. Ekstrak Jalur Stream B (Mikro)\n"," feat_b = self.stream_b(x)\n"," feat_b_tokens = feat_b.view(batch_size, 64, 64).transpose(1, 2)\n","\n"," # Merusak token di cabang B secara independen (Multi-view Corrupted Context)\n"," feat_b_tokens = self.imageCorrupter(feat_b_tokens)\n"," feat_b_lt = self.lookthemB(feat_b_tokens)\n","\n"," # 3. Penggabungan Asimetris Tingkat Fitur (Lebar fitur membesar, urutan koordinat spasial tetap 64)\n"," tokens_combined = torch.cat([feat_a_lt, feat_b_lt], dim=2) # Dimensi keluaran: [B, 64 Token, 128 Fitur]\n","\n"," # 4. Kognisi Relasional & Pematangan Klasifikasi\n"," out_lookthem = self.lookthem(tokens_combined)\n"," compressed = self.compressor(out_lookthem)\n"," return self.classifier(compressed)\n","\n","\n","# ==============================================================================\n","# 4. RUNTIME TRAINING SYSTEM WITH AUTO-RESUME\n","# ==============================================================================\n","device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n","model = LookThemV7_Caltech256().to(device)\n","\n","criterion = nn.CrossEntropyLoss()\n","optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)\n","scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20)\n","\n","start_epoch = 0\n","checkpoint_path = \"lookthem_caltech_checkpoint.pth\"\n","\n","# Fitur Auto-Resume anti putus koneksi Colab/Termux\n","if os.path.exists(checkpoint_path):\n"," print(\"πΎ Checkpoint V7 ditemukan! Memulihkan kondisi mesin eksperimen...\")\n"," checkpoint = torch.load(checkpoint_path)\n"," model.load_state_dict(checkpoint['model_state_dict'])\n"," optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n"," scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n"," start_epoch = checkpoint['epoch']\n"," print(f\"βΆοΈ Berhasil resume dari ambang batas Epoch ke-{start_epoch+1}\")\n","\n","print(f\"π Memulai pelatihan LookThem V7 (JEPA-Token Engine) di device: {device}...\")\n","\n","for epoch in range(start_epoch, 40):\n"," model.train()\n"," total_loss, correct, total = 0, 0, 0\n","\n"," for data, target in train_loader:\n"," data, target = data.to(device), target.to(device)\n","\n"," optimizer.zero_grad()\n"," output = model(data)\n"," loss = criterion(output, target)\n"," loss.backward()\n"," optimizer.step()\n","\n"," total_loss += loss.item()\n"," _, predicted = output.max(1)\n"," total += target.size(0)\n"," correct += predicted.eq(target).sum().item()\n","\n"," scheduler.step()\n"," acc = 100. * correct / total\n"," current_lr = optimizer.param_groups[0]['lr']\n"," print(f\"Epoch {epoch+1:02d}/100 -> Train Loss: {total_loss/len(train_loader):.4f} | Train Acc: {acc:.2f}% | LR: {current_lr:.6f}\")\n","\n"," # Mengunci progres ke disk storage setiap kelipatan 5 Epoch\n"," if (epoch + 1) % 5 == 0:\n"," torch.save({\n"," 'epoch': epoch + 1,\n"," 'model_state_dict': model.state_dict(),\n"," 'optimizer_state_dict': optimizer.state_dict(),\n"," 'scheduler_state_dict': scheduler.state_dict(),\n"," }, checkpoint_path)\n"," print(f\"π [SYSTEM-SAVER] Garis aman Epoch {epoch+1} berhasil dibekukan ke disk!\")\n","\n","\n","# ==============================================================================\n","# 5. VALIDASI & EVALUASI AKHIR REAL-WORLD\n","# ==============================================================================\n","model.eval()\n","test_loss, test_correct, test_total = 0, 0, 0\n","\n","print(\"\\nπ Membuka gerbang evaluasi test set Caltech-256...\")\n","with torch.no_grad():\n"," for data, target in val_loader:\n"," data, target = data.to(device), target.to(device)\n"," output = model(data)\n"," loss = criterion(output, target)\n","\n"," test_loss += loss.item()\n"," _, predicted = output.max(1)\n"," test_total += target.size(0)\n"," test_correct += predicted.eq(target).sum().item()\n","\n","final_test_acc = 100. * test_correct / test_total\n","print(\"\\n=== EVALUASI TOTAL SELESAI (LOOKTHEM V7) ===\")\n","print(f\"Final Test Loss: {test_loss/len(val_loader):.4f} | Final Test Accuracy: {final_test_acc:.2f}%\")\n","\n","# Simpan bobot murni siap deploy ke Hugging Face Space Demo\n","torch.save(model.state_dict(), \"LookThem_Caltech256.pth\")\n","print(f\"π File model bersih tersimpan! Ukuran file final: {os.path.getsize('LookThem_Caltech256.pth') / (1024*1024):.2f} MB\")"],"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"id":"W_XOOuLhbZ8o","executionInfo":{"status":"error","timestamp":1779180853178,"user_tz":-420,"elapsed":207803,"user":{"displayName":"Cici rizky plk","userId":"03714270658772765776"}},"outputId":"d803543a-99c9-44e7-9e33-d5248f679594"},"execution_count":3,"outputs":[{"output_type":"stream","name":"stdout","text":["β
Dataset Terkonfigurasi! Luas Train: 24485 | Luas Test: 6122\n","πΎ Checkpoint V7 ditemukan! Memulihkan kondisi mesin eksperimen...\n","βΆοΈ Berhasil resume dari ambang batas Epoch ke-21\n","π Memulai pelatihan LookThem V7 (JEPA-Token Engine) di device: cuda...\n","Epoch 21/100 -> Train Loss: 3.2013 | Train Acc: 30.16% | LR: 0.000006\n","Epoch 22/100 -> Train Loss: 3.2087 | Train Acc: 30.25% | LR: 0.000024\n","Epoch 23/100 -> Train Loss: 3.1956 | Train Acc: 30.56% | LR: 0.000054\n"]},{"output_type":"error","ename":"KeyboardInterrupt","evalue":"","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)","\u001b[0;32m/tmp/ipykernel_2848/1905920579.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 240\u001b[0m \u001b[0moutput\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcriterion\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 242\u001b[0;31m \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 243\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 244\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/torch/_tensor.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(self, gradient, retain_graph, create_graph, inputs)\u001b[0m\n\u001b[1;32m 628\u001b[0m \u001b[0minputs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minputs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 629\u001b[0m )\n\u001b[0;32m--> 630\u001b[0;31m torch.autograd.backward(\n\u001b[0m\u001b[1;32m 631\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgradient\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minputs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minputs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 632\u001b[0m )\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/torch/autograd/__init__.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001b[0m\n\u001b[1;32m 362\u001b[0m \u001b[0;31m# some Python versions print out the first line of a multi-line function\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 363\u001b[0m \u001b[0;31m# calls in the traceback and some print out the last line\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 364\u001b[0;31m _engine_run_backward(\n\u001b[0m\u001b[1;32m 365\u001b[0m \u001b[0mtensors\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 366\u001b[0m \u001b[0mgrad_tensors_\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/torch/autograd/graph.py\u001b[0m in \u001b[0;36m_engine_run_backward\u001b[0;34m(t_outputs, *args, **kwargs)\u001b[0m\n\u001b[1;32m 863\u001b[0m \u001b[0munregister_hooks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_register_logging_hooks_on_whole_graph\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt_outputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 864\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 865\u001b[0;31m return Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass\n\u001b[0m\u001b[1;32m 866\u001b[0m \u001b[0mt_outputs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 867\u001b[0m ) # Calls into the C++ engine to run the backward pass\n","\u001b[0;31mKeyboardInterrupt\u001b[0m: "]}]},{"cell_type":"code","source":["import os\n","import math\n","import torch\n","import torch.nn as nn\n","import torch.nn.functional as F\n","import torch.optim as optim\n","from torch.utils.data import DataLoader, Dataset, random_split\n","import torchvision\n","import torchvision.transforms as transforms\n","\n","# ==============================================================================\n","# 1. PIPELINE DATA & PREPROCESSING (CALTECH-256)\n","# ==============================================================================\n","\n","# Menggunakan standardisasi mean & std global ImageNet karena karakteristik objek mirip\n","transform_train = transforms.Compose([\n"," transforms.Lambda(lambda image: image.convert(\"RGB\")), # Mengatasi gambar berformat Grayscale/CMYK di Caltech256\n"," transforms.Resize((96, 96)), # Menyamakan resolusi input ke cetakan V7\n"," transforms.RandomHorizontalFlip(), # Augmentasi geometri ringan\n"," transforms.ToTensor(),\n"," transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))\n","])\n","\n","transform_test = transforms.Compose([\n"," transforms.Lambda(lambda image: image.convert(\"RGB\")),\n"," transforms.Resize((96, 96)),\n"," transforms.ToTensor(),\n"," transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))\n","])\n","\n","# Load dataset mentah tanpa transform awal agar pembagian subset tidak cacat tipe data\n","full_dataset = torchvision.datasets.Caltech256(root='./data', download=True)\n","\n","# Membagi porsi dataset: 80% Training, 20% Testing secara acak\n","total_len = len(full_dataset)\n","train_len = int(0.8 * total_len)\n","test_len = total_len - train_len\n","train_subset, test_subset = random_split(full_dataset, [train_len, test_len])\n","\n","# BERHASIL DIREPARASI: Wrapper Dataset agar DataLoader menerima Tensor, bukan PIL Image\n","class ApplyTransform(Dataset):\n"," def __init__(self, dataset_subset, transform=None):\n"," self.dataset_subset = dataset_subset\n"," self.transform = transform\n","\n"," def __getitem__(self, index):\n"," x, y = self.dataset_subset[index]\n"," if self.transform:\n"," x = self.transform(x)\n"," return x, y\n","\n"," def __len__(self):\n"," return len(self.dataset_subset)\n","\n","# Memasang pipa transformasi ke masing-masing subset\n","train_dataset = ApplyTransform(train_subset, transform=transform_train)\n","test_dataset = ApplyTransform(test_subset, transform=transform_test)\n","\n","train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2)\n","val_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=2)\n","\n","print(f\"β
Dataset Terkonfigurasi! Luas Train: {len(train_dataset)} | Luas Test: {len(test_dataset)}\")\n","\n","\n","# ==============================================================================\n","# 2. CORE LAYER: LOOKTHEM CORE COGNITION LAYER\n","# ==============================================================================\n","class LookThemLayer(nn.Module):\n"," def __init__(self, num_tokens, in_features, hidden_dim):\n"," super(LookThemLayer, self).__init__()\n"," self.num_tokens = num_tokens\n"," self.in_features = in_features\n","\n"," # Modul Proyeksi Relasional Pasangan 1\n"," self.mod1_w1 = nn.Parameter(torch.randn(num_tokens, in_features, hidden_dim))\n"," self.mod1_b1 = nn.Parameter(torch.zeros(num_tokens, hidden_dim))\n"," self.mod1_w2 = nn.Parameter(torch.randn(num_tokens, hidden_dim, 1))\n"," self.mod1_b2 = nn.Parameter(torch.zeros(num_tokens, 1))\n","\n"," # Modul Proyeksi Relasional Pasangan 2 (Komparator Rasio)\n"," self.mod2_w1 = nn.Parameter(torch.randn(num_tokens, in_features, hidden_dim))\n"," self.mod2_b1 = nn.Parameter(torch.zeros(num_tokens, hidden_dim))\n"," self.mod2_w2 = nn.Parameter(torch.randn(num_tokens, hidden_dim, 1))\n"," self.mod2_b2 = nn.Parameter(torch.zeros(num_tokens, 1))\n","\n"," # Bobot Transformasi Interaksi Akhir\n"," self.trans_w = nn.Parameter(torch.randn(num_tokens, 1, 1))\n"," self.trans_b = nn.Parameter(torch.zeros(num_tokens, 1))\n"," self._init_weights()\n","\n"," def _init_weights(self):\n"," for w in [self.mod1_w1, self.mod2_w1, self.mod1_w2, self.mod2_w2, self.trans_w]:\n"," nn.init.kaiming_uniform_(w, a=math.sqrt(5))\n","\n"," def forward(self, x):\n"," N = self.num_tokens\n","\n"," # Ekstraksi fitur internal per token via Einstein Summation (Einsum)\n"," h1 = torch.einsum('bti,tij->btj', x, self.mod1_w1) + self.mod1_b1\n"," out_m1 = torch.einsum('btj,tjk->btk', F.gelu(h1), self.mod1_w2) + self.mod1_b2\n","\n"," h2 = torch.einsum('bti,tij->btj', x, self.mod2_w1) + self.mod2_b1\n"," out_m2 = torch.einsum('btj,tjk->btk', F.gelu(h2), self.mod2_w2) + self.mod2_b2\n","\n"," # Ratio Based Attention: Membandingkan pecahan nilai antar-token\n"," out_m2_safe = out_m2 + 1e-5\n"," compare = torch.tanh(out_m1.unsqueeze(2) / out_m2_safe.unsqueeze(1))\n"," compare2 = torch.tanh(out_m1.unsqueeze(1) / out_m2_safe.unsqueeze(2))\n","\n"," bias_reshaped = self.trans_b.view(1, 1, N, 1)\n"," trans_compare = torch.einsum('bije,jef->bijf', compare, self.trans_w) + bias_reshaped\n"," trans_compare2 = torch.einsum('bije,jef->bijf', compare2, self.trans_w) + bias_reshaped\n","\n"," # Penggabungan matriks interaksi afinitas\n"," interaksi = (trans_compare * x.unsqueeze(2) + trans_compare2 * x.unsqueeze(1)) / 2\n","\n"," # Masking Self-Token: Memaksa token hanya memperhatikan hubungan dengan token LAIN\n"," mask = 1.0 - torch.eye(N, device=x.device)\n"," interaksi_masked = interaksi * mask.view(1, N, N, 1)\n","\n"," return interaksi_masked.sum(dim=2) / (N - 1.0)\n","\n","\n","# ==============================================================================\n","# 3. INDUK ARSITEKTUR: LOOKTHEM V7 (JEPA LATENT-CORRUPTER EDITION)\n","# ==============================================================================\n","class LookThemV7_Caltech256(nn.Module):\n"," def __init__(self):\n"," super(LookThemV7_Caltech256, self).__init__()\n","\n"," # --- STREAM A: FITUR MAKRO (STRIDE AGRESIF) ---\n"," self.stream_a = nn.Sequential(\n"," nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1), # [B, 16, 48, 48]\n"," nn.BatchNorm2d(16), nn.GELU(),\n"," nn.Conv2d(16, 32, kernel_size=3, stride=2, padding=1), # [B, 32, 24, 24]\n"," nn.BatchNorm2d(32), nn.GELU(),\n"," nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # [B, 64, 128, 12]\n"," nn.BatchNorm2d(64), nn.GELU(),\n"," nn.AdaptiveMaxPool2d((8, 8)) # Output spasial dikunci ke [8x8 = 64 Token]\n"," )\n","\n"," # --- STREAM B: FITUR MIKRO/LOKAL (STRIDE HALUS) ---\n"," self.stream_b = nn.Sequential(\n"," nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1), # [B, 16, 96, 96]\n"," nn.BatchNorm2d(16), nn.GELU(),\n"," nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1), # [B, 32, 96, 96]\n"," nn.BatchNorm2d(32), nn.GELU(),\n"," nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # [B, 64, 48, 48]\n"," nn.BatchNorm2d(64), nn.GELU(),\n"," nn.AdaptiveMaxPool2d((8, 8)) # Output spasial dikunci ke [8x8 = 64 Token]\n"," )\n","\n"," # Brain Layers (64 Token dengan dimensi internal 64)\n"," self.lookthemA = LookThemLayer(num_tokens=64, in_features=64, hidden_dim=16)\n"," self.lookthemB = LookThemLayer(num_tokens=64, in_features=64, hidden_dim=16)\n","\n"," # Kombinator Relasional Utama (Menerima asimetris gabungan 128 dimensi fitur)\n"," self.lookthem = LookThemLayer(num_tokens=64, in_features=128, hidden_dim=32)\n","\n"," # Mengurangi dimensi fitur gabungan secara linier dari 128 ke 32 sebelum klasifikasi\n"," self.compressor = nn.AdaptiveAvgPool1d(32)\n","\n"," # --- CLASSIFIER (Target Kelas Caltech-256: 257 Output) ---\n"," self.classifier = nn.Sequential(\n"," nn.Flatten(),\n"," nn.Linear(64 * 32, 1024),\n"," nn.ReLU(),\n"," nn.Dropout(0.4),\n"," nn.Linear(1024, 512),\n"," nn.ReLU(),\n"," nn.Dropout(0.4),\n"," nn.Linear(512, 257) # 256 Objek + 1 Clutter Background\n"," )\n","\n"," # INSPIRED BY JEPA: Menghancurkan 30% informasi token laten secara acak (Stochastic Masking)\n"," self.imageCorrupter = nn.Dropout(0.3)\n","\n"," def forward(self, x):\n"," batch_size = x.size(0)\n","\n"," # 1. Ekstrak Jalur Stream A (Makro)\n"," feat_a = self.stream_a(x)\n"," feat_a_flat = feat_a.view(batch_size, 64, 64)\n"," feat_a_tokens = feat_a_flat.transpose(1, 2) # Hasil akhir susunan: [B, 64 Token, 64 Fitur]\n","\n"," # PERBAIKAN: imageCorrupter merusak token laten, BUKAN piksel mentah RGB input!\n"," feat_a_tokens = self.imageCorrupter(feat_a_tokens)\n"," feat_a_lt = self.lookthemA(feat_a_tokens)\n","\n"," # 2. Ekstrak Jalur Stream B (Mikro)\n"," feat_b = self.stream_b(x)\n"," feat_b_tokens = feat_b.view(batch_size, 64, 64).transpose(1, 2)\n","\n"," # Merusak token di cabang B secara independen (Multi-view Corrupted Context)\n"," feat_b_tokens = self.imageCorrupter(feat_b_tokens)\n"," feat_b_lt = self.lookthemB(feat_b_tokens)\n","\n"," # 3. Penggabungan Asimetris Tingkat Fitur (Lebar fitur membesar, urutan koordinat spasial tetap 64)\n"," tokens_combined = torch.cat([feat_a_lt, feat_b_lt], dim=2) # Dimensi keluaran: [B, 64 Token, 128 Fitur]\n","\n"," # 4. Kognisi Relasional & Pematangan Klasifikasi\n"," out_lookthem = self.lookthem(tokens_combined)\n"," compressed = self.compressor(out_lookthem)\n"," return self.classifier(compressed)\n","\n","\n","# ==============================================================================\n","# 4. RUNTIME TRAINING SYSTEM WITH AUTO-RESUME\n","# ==============================================================================\n","device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n","model = LookThemV7_Caltech256().to(device)\n","\n","criterion = nn.CrossEntropyLoss()\n","optimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=1e-4)\n","scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20)\n","\n","start_epoch = 0\n","checkpoint_path = \"lookthem_caltech_checkpoint.pth\"\n","\n","# Fitur Auto-Resume anti putus koneksi Colab/Termux\n","if os.path.exists(checkpoint_path):\n"," print(\"πΎ Checkpoint V7 ditemukan! Memulihkan kondisi mesin eksperimen...\")\n"," checkpoint = torch.load(checkpoint_path)\n"," model.load_state_dict(checkpoint['model_state_dict'])\n"," optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n"," scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n"," start_epoch = checkpoint['epoch']\n"," print(f\"βΆοΈ Berhasil resume dari ambang batas Epoch ke-{start_epoch+1}\")\n","\n","print(f\"π Memulai pelatihan LookThem V7 (JEPA-Token Engine) di device: {device}...\")\n","\n","for epoch in range(start_epoch, 40):\n"," model.train()\n"," total_loss, correct, total = 0, 0, 0\n","\n"," for data, target in train_loader:\n"," data, target = data.to(device), target.to(device)\n","\n"," optimizer.zero_grad()\n"," output = model(data)\n"," loss = criterion(output, target)\n"," loss.backward()\n"," optimizer.step()\n","\n"," total_loss += loss.item()\n"," _, predicted = output.max(1)\n"," total += target.size(0)\n"," correct += predicted.eq(target).sum().item()\n","\n"," scheduler.step()\n"," acc = 100. * correct / total\n"," current_lr = optimizer.param_groups[0]['lr']\n"," print(f\"Epoch {epoch+1:02d}/100 -> Train Loss: {total_loss/len(train_loader):.4f} | Train Acc: {acc:.2f}% | LR: {current_lr:.6f}\")\n","\n"," # Mengunci progres ke disk storage setiap kelipatan 5 Epoch\n"," if (epoch + 1) % 5 == 0:\n"," torch.save({\n"," 'epoch': epoch + 1,\n"," 'model_state_dict': model.state_dict(),\n"," 'optimizer_state_dict': optimizer.state_dict(),\n"," 'scheduler_state_dict': scheduler.state_dict(),\n"," }, checkpoint_path)\n"," print(f\"π [SYSTEM-SAVER] Garis aman Epoch {epoch+1} berhasil dibekukan ke disk!\")\n","\n","\n","# ==============================================================================\n","# 5. VALIDASI & EVALUASI AKHIR REAL-WORLD\n","# ==============================================================================\n","model.eval()\n","test_loss, test_correct, test_total = 0, 0, 0\n","\n","print(\"\\nπ Membuka gerbang evaluasi test set Caltech-256...\")\n","with torch.no_grad():\n"," for data, target in val_loader:\n"," data, target = data.to(device), target.to(device)\n"," output = model(data)\n"," loss = criterion(output, target)\n","\n"," test_loss += loss.item()\n"," _, predicted = output.max(1)\n"," test_total += target.size(0)\n"," test_correct += predicted.eq(target).sum().item()\n","\n","final_test_acc = 100. * test_correct / test_total\n","print(\"\\n=== EVALUASI TOTAL SELESAI (LOOKTHEM V7) ===\")\n","print(f\"Final Test Loss: {test_loss/len(val_loader):.4f} | Final Test Accuracy: {final_test_acc:.2f}%\")\n","\n","# Simpan bobot murni siap deploy ke Hugging Face Space Demo\n","torch.save(model.state_dict(), \"LookThem_Caltech256.pth\")\n","print(f\"π File model bersih tersimpan! Ukuran file final: {os.path.getsize('LookThem_Caltech256.pth') / (1024*1024):.2f} MB\")"],"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":914},"executionInfo":{"status":"error","timestamp":1779181161600,"user_tz":-420,"elapsed":272480,"user":{"displayName":"Cici rizky plk","userId":"03714270658772765776"}},"outputId":"e3af6bfb-8481-48a7-cbd9-84e37e909a6f","id":"IEr-UfrVcYG7"},"execution_count":4,"outputs":[{"output_type":"stream","name":"stdout","text":["β
Dataset Terkonfigurasi! Luas Train: 24485 | Luas Test: 6122\n","πΎ Checkpoint V7 ditemukan! Memulihkan kondisi mesin eksperimen...\n","βΆοΈ Berhasil resume dari ambang batas Epoch ke-21\n","π Memulai pelatihan LookThem V7 (JEPA-Token Engine) di device: cuda...\n","Epoch 21/100 -> Train Loss: 3.1998 | Train Acc: 30.07% | LR: 0.000006\n","Epoch 22/100 -> Train Loss: 3.2050 | Train Acc: 30.02% | LR: 0.000024\n","Epoch 23/100 -> Train Loss: 3.2069 | Train Acc: 30.19% | LR: 0.000054\n","Epoch 24/100 -> Train Loss: 3.2034 | Train Acc: 30.41% | LR: 0.000095\n"]},{"output_type":"error","ename":"KeyboardInterrupt","evalue":"","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)","\u001b[0;32m/tmp/ipykernel_2848/3103544416.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 234\u001b[0m \u001b[0mtotal_loss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcorrect\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtotal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 235\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 236\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtrain_loader\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 237\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py\u001b[0m in \u001b[0;36m__next__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 739\u001b[0m \u001b[0;31m# TODO(https://github.com/pytorch/pytorch/issues/76750)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 740\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_reset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# type: ignore[call-arg]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 741\u001b[0;31m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_next_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 742\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_num_yielded\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 743\u001b[0m if (\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py\u001b[0m in \u001b[0;36m_next_data\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1522\u001b[0m \u001b[0;34m\"Invalid iterator state: shutdown or no outstanding tasks when fetching next data\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1523\u001b[0m )\n\u001b[0;32m-> 1524\u001b[0;31m \u001b[0midx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1525\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_tasks_outstanding\u001b[0m \u001b[0;34m-=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1526\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_dataset_kind\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0m_DatasetKind\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mIterable\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py\u001b[0m in \u001b[0;36m_get_data\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1481\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1482\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1483\u001b[0;31m \u001b[0msuccess\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_try_get_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1484\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0msuccess\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1485\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py\u001b[0m in \u001b[0;36m_try_get_data\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 1308\u001b[0m \u001b[0;31m# (bool: whether successfully get data, any: data if successful else None)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1309\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1310\u001b[0;31m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_data_queue\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1311\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1312\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/multiprocessing/queues.py\u001b[0m in \u001b[0;36mget\u001b[0;34m(self, block, timeout)\u001b[0m\n\u001b[1;32m 111\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mblock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 112\u001b[0m \u001b[0mtimeout\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdeadline\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmonotonic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 113\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_poll\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 114\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mEmpty\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 115\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_poll\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/multiprocessing/connection.py\u001b[0m in \u001b[0;36mpoll\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_check_closed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 256\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_check_readable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 257\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_poll\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 258\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 259\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__enter__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/multiprocessing/connection.py\u001b[0m in \u001b[0;36m_poll\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 438\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 439\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_poll\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 440\u001b[0;31m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwait\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 441\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mbool\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 442\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/multiprocessing/connection.py\u001b[0m in \u001b[0;36mwait\u001b[0;34m(object_list, timeout)\u001b[0m\n\u001b[1;32m 1134\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1135\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1136\u001b[0;31m \u001b[0mready\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mselector\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mselect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1137\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mready\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1138\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfileobj\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mevents\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mready\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/selectors.py\u001b[0m in \u001b[0;36mselect\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 413\u001b[0m \u001b[0mready\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 414\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 415\u001b[0;31m \u001b[0mfd_event_list\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_selector\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpoll\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 416\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mInterruptedError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 417\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mready\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;31mKeyboardInterrupt\u001b[0m: "]}]},{"cell_type":"code","source":[],"metadata":{"id":"Z0ZC-UNJcXAR"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["import os\n","import math\n","import torch\n","import torch.nn as nn\n","import torch.nn.functional as F\n","import torch.optim as optim\n","from torch.utils.data import DataLoader, Dataset, random_split\n","import torchvision\n","import torchvision.transforms as transforms\n","\n","# ==============================================================================\n","# 1. PIPELINE DATA & PREPROCESSING (CALTECH-256)\n","# ==============================================================================\n","\n","# Menggunakan standardisasi mean & std global ImageNet karena karakteristik objek mirip\n","transform_train = transforms.Compose([\n"," transforms.Lambda(lambda image: image.convert(\"RGB\")), # Mengatasi gambar berformat Grayscale/CMYK di Caltech256\n"," transforms.Resize((96, 96)), # Menyamakan resolusi input ke cetakan V7\n"," transforms.RandomHorizontalFlip(), # Augmentasi geometri ringan\n"," transforms.ToTensor(),\n"," transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))\n","])\n","\n","transform_test = transforms.Compose([\n"," transforms.Lambda(lambda image: image.convert(\"RGB\")),\n"," transforms.Resize((96, 96)),\n"," transforms.ToTensor(),\n"," transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))\n","])\n","\n","# Load dataset mentah tanpa transform awal agar pembagian subset tidak cacat tipe data\n","full_dataset = torchvision.datasets.Caltech256(root='./data', download=True)\n","\n","# Membagi porsi dataset: 80% Training, 20% Testing secara acak\n","total_len = len(full_dataset)\n","train_len = int(0.8 * total_len)\n","test_len = total_len - train_len\n","train_subset, test_subset = random_split(full_dataset, [train_len, test_len])\n","\n","# BERHASIL DIREPARASI: Wrapper Dataset agar DataLoader menerima Tensor, bukan PIL Image\n","class ApplyTransform(Dataset):\n"," def __init__(self, dataset_subset, transform=None):\n"," self.dataset_subset = dataset_subset\n"," self.transform = transform\n","\n"," def __getitem__(self, index):\n"," x, y = self.dataset_subset[index]\n"," if self.transform:\n"," x = self.transform(x)\n"," return x, y\n","\n"," def __len__(self):\n"," return len(self.dataset_subset)\n","\n","# Memasang pipa transformasi ke masing-masing subset\n","train_dataset = ApplyTransform(train_subset, transform=transform_train)\n","test_dataset = ApplyTransform(test_subset, transform=transform_test)\n","\n","train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2)\n","val_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=2)\n","\n","print(f\"β
Dataset Terkonfigurasi! Luas Train: {len(train_dataset)} | Luas Test: {len(test_dataset)}\")\n","\n","\n","# ==============================================================================\n","# 2. CORE LAYER: LOOKTHEM CORE COGNITION LAYER\n","# ==============================================================================\n","class LookThemLayer(nn.Module):\n"," def __init__(self, num_tokens, in_features, hidden_dim):\n"," super(LookThemLayer, self).__init__()\n"," self.num_tokens = num_tokens\n"," self.in_features = in_features\n","\n"," # Modul Proyeksi Relasional Pasangan 1\n"," self.mod1_w1 = nn.Parameter(torch.randn(num_tokens, in_features, hidden_dim))\n"," self.mod1_b1 = nn.Parameter(torch.zeros(num_tokens, hidden_dim))\n"," self.mod1_w2 = nn.Parameter(torch.randn(num_tokens, hidden_dim, 1))\n"," self.mod1_b2 = nn.Parameter(torch.zeros(num_tokens, 1))\n","\n"," # Modul Proyeksi Relasional Pasangan 2 (Komparator Rasio)\n"," self.mod2_w1 = nn.Parameter(torch.randn(num_tokens, in_features, hidden_dim))\n"," self.mod2_b1 = nn.Parameter(torch.zeros(num_tokens, hidden_dim))\n"," self.mod2_w2 = nn.Parameter(torch.randn(num_tokens, hidden_dim, 1))\n"," self.mod2_b2 = nn.Parameter(torch.zeros(num_tokens, 1))\n","\n"," # Bobot Transformasi Interaksi Akhir\n"," self.trans_w = nn.Parameter(torch.randn(num_tokens, 1, 1))\n"," self.trans_b = nn.Parameter(torch.zeros(num_tokens, 1))\n"," self._init_weights()\n","\n"," def _init_weights(self):\n"," for w in [self.mod1_w1, self.mod2_w1, self.mod1_w2, self.mod2_w2, self.trans_w]:\n"," nn.init.kaiming_uniform_(w, a=math.sqrt(5))\n","\n"," def forward(self, x):\n"," N = self.num_tokens\n","\n"," # Ekstraksi fitur internal per token via Einstein Summation (Einsum)\n"," h1 = torch.einsum('bti,tij->btj', x, self.mod1_w1) + self.mod1_b1\n"," out_m1 = torch.einsum('btj,tjk->btk', F.gelu(h1), self.mod1_w2) + self.mod1_b2\n","\n"," h2 = torch.einsum('bti,tij->btj', x, self.mod2_w1) + self.mod2_b1\n"," out_m2 = torch.einsum('btj,tjk->btk', F.gelu(h2), self.mod2_w2) + self.mod2_b2\n","\n"," # Ratio Based Attention: Membandingkan pecahan nilai antar-token\n"," out_m2_safe = out_m2 + 1e-5\n"," compare = torch.tanh(out_m1.unsqueeze(2) / out_m2_safe.unsqueeze(1))\n"," compare2 = torch.tanh(out_m1.unsqueeze(1) / out_m2_safe.unsqueeze(2))\n","\n"," bias_reshaped = self.trans_b.view(1, 1, N, 1)\n"," trans_compare = torch.einsum('bije,jef->bijf', compare, self.trans_w) + bias_reshaped\n"," trans_compare2 = torch.einsum('bije,jef->bijf', compare2, self.trans_w) + bias_reshaped\n","\n"," # Penggabungan matriks interaksi afinitas\n"," interaksi = (trans_compare * x.unsqueeze(2) + trans_compare2 * x.unsqueeze(1)) / 2\n","\n"," # Masking Self-Token: Memaksa token hanya memperhatikan hubungan dengan token LAIN\n"," mask = 1.0 - torch.eye(N, device=x.device)\n"," interaksi_masked = interaksi * mask.view(1, N, N, 1)\n","\n"," return interaksi_masked.sum(dim=2) / (N - 1.0)\n","\n","\n","# ==============================================================================\n","# 3. INDUK ARSITEKTUR: LOOKTHEM V7 (JEPA LATENT-CORRUPTER EDITION)\n","# ==============================================================================\n","class LookThemV7_Caltech256(nn.Module):\n"," def __init__(self):\n"," super(LookThemV7_Caltech256, self).__init__()\n","\n"," # --- STREAM A: FITUR MAKRO (STRIDE AGRESIF) ---\n"," self.stream_a = nn.Sequential(\n"," nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1), # [B, 16, 48, 48]\n"," nn.BatchNorm2d(16), nn.GELU(),\n"," nn.Conv2d(16, 32, kernel_size=3, stride=2, padding=1), # [B, 32, 24, 24]\n"," nn.BatchNorm2d(32), nn.GELU(),\n"," nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # [B, 64, 128, 12]\n"," nn.BatchNorm2d(64), nn.GELU(),\n"," nn.AdaptiveMaxPool2d((8, 8)) # Output spasial dikunci ke [8x8 = 64 Token]\n"," )\n","\n"," # --- STREAM B: FITUR MIKRO/LOKAL (STRIDE HALUS) ---\n"," self.stream_b = nn.Sequential(\n"," nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1), # [B, 16, 96, 96]\n"," nn.BatchNorm2d(16), nn.GELU(),\n"," nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1), # [B, 32, 96, 96]\n"," nn.BatchNorm2d(32), nn.GELU(),\n"," nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # [B, 64, 48, 48]\n"," nn.BatchNorm2d(64), nn.GELU(),\n"," nn.AdaptiveMaxPool2d((8, 8)) # Output spasial dikunci ke [8x8 = 64 Token]\n"," )\n","\n"," # Brain Layers (64 Token dengan dimensi internal 64)\n"," self.lookthemA = LookThemLayer(num_tokens=64, in_features=64, hidden_dim=16)\n"," self.lookthemB = LookThemLayer(num_tokens=64, in_features=64, hidden_dim=16)\n","\n"," # Kombinator Relasional Utama (Menerima asimetris gabungan 128 dimensi fitur)\n"," self.lookthem = LookThemLayer(num_tokens=64, in_features=128, hidden_dim=32)\n","\n"," # Mengurangi dimensi fitur gabungan secara linier dari 128 ke 32 sebelum klasifikasi\n"," self.compressor = nn.AdaptiveAvgPool1d(32)\n","\n"," # --- CLASSIFIER (Target Kelas Caltech-256: 257 Output) ---\n"," self.classifier = nn.Sequential(\n"," nn.Flatten(),\n"," nn.Linear(64 * 32, 1024),\n"," nn.ReLU(),\n"," nn.Dropout(0.4),\n"," nn.Linear(1024, 512),\n"," nn.ReLU(),\n"," nn.Dropout(0.4),\n"," nn.Linear(512, 257) # 256 Objek + 1 Clutter Background\n"," )\n","\n"," # INSPIRED BY JEPA: Menghancurkan 30% informasi token laten secara acak (Stochastic Masking)\n"," self.imageCorrupter = nn.Dropout(0.3)\n","\n"," def forward(self, x):\n"," batch_size = x.size(0)\n","\n"," # 1. Ekstrak Jalur Stream A (Makro)\n"," feat_a = self.stream_a(x)\n"," feat_a_flat = feat_a.view(batch_size, 64, 64)\n"," feat_a_tokens = feat_a_flat.transpose(1, 2) # Hasil akhir susunan: [B, 64 Token, 64 Fitur]\n","\n"," # PERBAIKAN: imageCorrupter merusak token laten, BUKAN piksel mentah RGB input!\n"," feat_a_tokens = self.imageCorrupter(feat_a_tokens)\n"," feat_a_lt = self.lookthemA(feat_a_tokens)\n","\n"," # 2. Ekstrak Jalur Stream B (Mikro)\n"," feat_b = self.stream_b(x)\n"," feat_b_tokens = feat_b.view(batch_size, 64, 64).transpose(1, 2)\n","\n"," # Merusak token di cabang B secara independen (Multi-view Corrupted Context)\n"," feat_b_tokens = self.imageCorrupter(feat_b_tokens)\n"," feat_b_lt = self.lookthemB(feat_b_tokens)\n","\n"," # 3. Penggabungan Asimetris Tingkat Fitur (Lebar fitur membesar, urutan koordinat spasial tetap 64)\n"," tokens_combined = torch.cat([feat_a_lt, feat_b_lt], dim=2) # Dimensi keluaran: [B, 64 Token, 128 Fitur]\n","\n"," # 4. Kognisi Relasional & Pematangan Klasifikasi\n"," out_lookthem = self.lookthem(tokens_combined)\n"," compressed = self.compressor(out_lookthem)\n"," return self.classifier(compressed)\n","\n","\n","# ==============================================================================\n","# 4. RUNTIME TRAINING SYSTEM WITH AUTO-RESUME\n","# ==============================================================================\n","device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n","model = LookThemV7_Caltech256().to(device)\n","\n","criterion = nn.CrossEntropyLoss()\n","optimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=1e-4)\n","scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=30)\n","\n","start_epoch = 0\n","checkpoint_path = \"lookthem_caltech_checkpoint.pth\"\n","\n","# Fitur Auto-Resume anti putus koneksi Colab/Termux\n","if os.path.exists(checkpoint_path):\n"," print(\"πΎ Checkpoint V7 ditemukan! Memulihkan kondisi mesin eksperimen...\")\n"," checkpoint = torch.load(checkpoint_path)\n"," model.load_state_dict(checkpoint['model_state_dict'])\n"," optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n"," scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n"," start_epoch = checkpoint['epoch']\n"," print(f\"βΆοΈ Berhasil resume dari ambang batas Epoch ke-{start_epoch+1}\")\n","\n","print(f\"π Memulai pelatihan LookThem V7 (JEPA-Token Engine) di device: {device}...\")\n","\n","for epoch in range(start_epoch, 30):\n"," model.train()\n"," total_loss, correct, total = 0, 0, 0\n","\n"," for data, target in train_loader:\n"," data, target = data.to(device), target.to(device)\n","\n"," optimizer.zero_grad()\n"," output = model(data)\n"," loss = criterion(output, target)\n"," loss.backward()\n"," optimizer.step()\n","\n"," total_loss += loss.item()\n"," _, predicted = output.max(1)\n"," total += target.size(0)\n"," correct += predicted.eq(target).sum().item()\n","\n"," scheduler.step()\n"," acc = 100. * correct / total\n"," current_lr = optimizer.param_groups[0]['lr']\n"," print(f\"Epoch {epoch+1:02d}/100 -> Train Loss: {total_loss/len(train_loader):.4f} | Train Acc: {acc:.2f}% | LR: {current_lr:.6f}\")\n","\n"," # Mengunci progres ke disk storage setiap kelipatan 5 Epoch\n"," if (epoch + 1) % 5 == 0:\n"," torch.save({\n"," 'epoch': epoch + 1,\n"," 'model_state_dict': model.state_dict(),\n"," 'optimizer_state_dict': optimizer.state_dict(),\n"," 'scheduler_state_dict': scheduler.state_dict(),\n"," }, checkpoint_path)\n"," print(f\"π [SYSTEM-SAVER] Garis aman Epoch {epoch+1} berhasil dibekukan ke disk!\")\n","\n","\n","# ==============================================================================\n","# 5. VALIDASI & EVALUASI AKHIR REAL-WORLD\n","# ==============================================================================\n","model.eval()\n","test_loss, test_correct, test_total = 0, 0, 0\n","\n","print(\"\\nπ Membuka gerbang evaluasi test set Caltech-256...\")\n","with torch.no_grad():\n"," for data, target in val_loader:\n"," data, target = data.to(device), target.to(device)\n"," output = model(data)\n"," loss = criterion(output, target)\n","\n"," test_loss += loss.item()\n"," _, predicted = output.max(1)\n"," test_total += target.size(0)\n"," test_correct += predicted.eq(target).sum().item()\n","\n","final_test_acc = 100. * test_correct / test_total\n","print(\"\\n=== EVALUASI TOTAL SELESAI (LOOKTHEM V7) ===\")\n","print(f\"Final Test Loss: {test_loss/len(val_loader):.4f} | Final Test Accuracy: {final_test_acc:.2f}%\")\n","\n","# Simpan bobot murni siap deploy ke Hugging Face Space Demo\n","torch.save(model.state_dict(), \"LookThem_Caltech256.pth\")\n","print(f\"π File model bersih tersimpan! Ukuran file final: {os.path.getsize('LookThem_Caltech256.pth') / (1024*1024):.2f} MB\")"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1779183136445,"user_tz":-420,"elapsed":1913067,"user":{"displayName":"Cici rizky plk","userId":"03714270658772765776"}},"outputId":"ff9794ab-48cf-4cc6-bafd-8230c0515cf0","id":"_ExlEXtododA"},"execution_count":5,"outputs":[{"output_type":"stream","name":"stdout","text":["β
Dataset Terkonfigurasi! Luas Train: 24485 | Luas Test: 6122\n","π Memulai pelatihan LookThem V7 (JEPA-Token Engine) di device: cuda...\n","Epoch 01/100 -> Train Loss: 5.2614 | Train Acc: 5.46% | LR: 0.000997\n","Epoch 02/100 -> Train Loss: 4.9494 | Train Acc: 8.16% | LR: 0.000989\n","Epoch 03/100 -> Train Loss: 4.6232 | Train Acc: 11.49% | LR: 0.000976\n","Epoch 04/100 -> Train Loss: 4.3470 | Train Acc: 14.33% | LR: 0.000957\n","Epoch 05/100 -> Train Loss: 4.3895 | Train Acc: 13.93% | LR: 0.000933\n","π [SYSTEM-SAVER] Garis aman Epoch 5 berhasil dibekukan ke disk!\n","Epoch 06/100 -> Train Loss: 4.0457 | Train Acc: 17.93% | LR: 0.000905\n","Epoch 07/100 -> Train Loss: 3.8262 | Train Acc: 20.62% | LR: 0.000872\n","Epoch 08/100 -> Train Loss: 3.6565 | Train Acc: 23.05% | LR: 0.000835\n","Epoch 09/100 -> Train Loss: 3.8954 | Train Acc: 20.29% | LR: 0.000794\n","Epoch 10/100 -> Train Loss: 3.5500 | Train Acc: 24.62% | LR: 0.000750\n","π [SYSTEM-SAVER] Garis aman Epoch 10 berhasil dibekukan ke disk!\n","Epoch 11/100 -> Train Loss: 3.3962 | Train Acc: 26.87% | LR: 0.000703\n","Epoch 12/100 -> Train Loss: 3.2765 | Train Acc: 28.60% | LR: 0.000655\n","Epoch 13/100 -> Train Loss: 3.1718 | Train Acc: 29.99% | LR: 0.000604\n","Epoch 14/100 -> Train Loss: 3.0678 | Train Acc: 31.61% | LR: 0.000552\n","Epoch 15/100 -> Train Loss: 2.9802 | Train Acc: 33.31% | LR: 0.000500\n","π [SYSTEM-SAVER] Garis aman Epoch 15 berhasil dibekukan ke disk!\n","Epoch 16/100 -> Train Loss: 2.9108 | Train Acc: 34.52% | LR: 0.000448\n","Epoch 17/100 -> Train Loss: 2.8435 | Train Acc: 35.47% | LR: 0.000396\n","Epoch 18/100 -> Train Loss: 2.8011 | Train Acc: 36.03% | LR: 0.000345\n","Epoch 19/100 -> Train Loss: 2.7366 | Train Acc: 37.67% | LR: 0.000297\n","Epoch 20/100 -> Train Loss: 2.7159 | Train Acc: 37.40% | LR: 0.000250\n","π [SYSTEM-SAVER] Garis aman Epoch 20 berhasil dibekukan ke disk!\n","Epoch 21/100 -> Train Loss: 2.6481 | Train Acc: 38.73% | LR: 0.000206\n","Epoch 22/100 -> Train Loss: 2.5987 | Train Acc: 39.33% | LR: 0.000165\n","Epoch 23/100 -> Train Loss: 2.5569 | Train Acc: 40.34% | LR: 0.000128\n","Epoch 24/100 -> Train Loss: 2.5373 | Train Acc: 40.71% | LR: 0.000095\n","Epoch 25/100 -> Train Loss: 2.5075 | Train Acc: 40.87% | LR: 0.000067\n","π [SYSTEM-SAVER] Garis aman Epoch 25 berhasil dibekukan ke disk!\n","Epoch 26/100 -> Train Loss: 2.4793 | Train Acc: 41.56% | LR: 0.000043\n","Epoch 27/100 -> Train Loss: 2.4694 | Train Acc: 41.82% | LR: 0.000024\n","Epoch 28/100 -> Train Loss: 2.4636 | Train Acc: 42.12% | LR: 0.000011\n","Epoch 29/100 -> Train Loss: 2.4367 | Train Acc: 42.91% | LR: 0.000003\n","Epoch 30/100 -> Train Loss: 2.4388 | Train Acc: 42.30% | LR: 0.000000\n","π [SYSTEM-SAVER] Garis aman Epoch 30 berhasil dibekukan ke disk!\n","\n","π Membuka gerbang evaluasi test set Caltech-256...\n","\n","=== EVALUASI TOTAL SELESAI (LOOKTHEM V7) ===\n","Final Test Loss: 2.8915 | Final Test Accuracy: 38.03%\n","π File model bersih tersimpan! Ukuran file final: 13.78 MB\n"]}]}]} |