{"cells":[{"cell_type":"markdown","metadata":{},"source":"# Recod.ai/LUC Scientific Image Forgery Detection - WayneIA V3\n\n**Competition**: recodai-luc-scientific-image-forgery-detection  \n**Prize**: $55,000  \n**Model**: DINOv2 + Dual-Head (Classification + Segmentation)  \n**Training Metrics**: Dice=0.6981, AUC=0.9022, F1=0.7964\n\nCODE_KEY[166] FORGERY_CLASSIFIER_MATRIX\n\nWayneIA Position_1 | December 23, 2025 | Year-8 RHINOCEROS G9"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Fix protobuf conflict BEFORE any imports\nimport os\nos.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n\nimport sys\nimport warnings\nwarnings.filterwarnings('ignore')\n\nprint(\"=\"*60)\nprint(\"Recod.ai/LUC Forgery Detection - WayneIA V3\")\nprint(\"CODE_KEY[166] FORGERY_CLASSIFIER_MATRIX\")\nprint(\"=\"*60)\n\nIN_KAGGLE = 'kaggle_web_client' in sys.modules or os.path.exists('/kaggle/input')\nprint(f\"Kaggle environment: {IN_KAGGLE}\")\nprint(f\"Python version: {sys.version}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"import numpy as np\nimport pandas as pd\nfrom pathlib import Path\nimport cv2\nfrom PIL import Image\nfrom tqdm.auto import tqdm\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nfrom transformers import Dinov2Model, AutoImageProcessor\nprint(\"[OK] transformers available\")\n\nprint(f\"PyTorch version: {torch.__version__}\")\nprint(f\"CUDA available: {torch.cuda.is_available()}\")\nif torch.cuda.is_available():\n    print(f\"GPU: {torch.cuda.get_device_name(0)}\")\nprint(\"[OK] All imports successful\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Configuration\nclass Config:\n    if IN_KAGGLE:\n        DATA_DIR = Path(\"/kaggle/input/recodai-luc-scientific-image-forgery-detection\")\n        MODEL_DIR = Path(\"/kaggle/input/recod-wayneia-model\")\n        OUTPUT_DIR = Path(\"/kaggle/working\")\n    else:\n        DATA_DIR = Path(\"/mnt/wayne/competitions/recod_ai_luc/extracted\")\n        MODEL_DIR = Path(\"/mnt/wayne/competitions/recod_ai_luc/experiments/phase2_20251222_192821\")\n        OUTPUT_DIR = Path(\"/mnt/wayne/competitions/recod_ai_luc/output\")\n    \n    DINOV2_MODEL = \"facebook/dinov2-base\"\n    FEATURE_LAYERS = [6, 9, 12]\n    IMG_SIZE = 518\n    CROP_SIZE = 504\n    THRESHOLD = 0.5\n\nconfig = Config()\nprint(f\"Data dir: {config.DATA_DIR}\")\nprint(f\"Model dir: {config.MODEL_DIR}\")\nprint(f\"Model files: {list(config.MODEL_DIR.glob('*'))}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Model Architecture\nclass SegmentationHead(nn.Module):\n    def __init__(self, feature_dim=768, decoder_dim=256):\n        super().__init__()\n        self.lateral1 = nn.Conv2d(feature_dim, decoder_dim, kernel_size=1)\n        self.lateral2 = nn.Conv2d(feature_dim, decoder_dim, kernel_size=1)\n        self.lateral3 = nn.Conv2d(feature_dim, decoder_dim, kernel_size=1)\n        self.upsample1 = nn.Sequential(nn.ConvTranspose2d(decoder_dim, decoder_dim, 4, 2, 1), nn.BatchNorm2d(decoder_dim), nn.ReLU(True))\n        self.upsample2 = nn.Sequential(nn.ConvTranspose2d(decoder_dim, decoder_dim, 4, 2, 1), nn.BatchNorm2d(decoder_dim), nn.ReLU(True))\n        self.upsample3 = nn.Sequential(nn.ConvTranspose2d(decoder_dim, decoder_dim, 4, 2, 1), nn.BatchNorm2d(decoder_dim), nn.ReLU(True))\n        self.upsample4 = nn.Sequential(nn.ConvTranspose2d(decoder_dim, decoder_dim, 4, 2, 1), nn.BatchNorm2d(decoder_dim), nn.ReLU(True))\n        self.final_conv = nn.Conv2d(decoder_dim, 1, kernel_size=1)\n\n    def forward(self, features):\n        f1, f2, f3 = features\n        p3 = self.upsample1(self.lateral3(f3))\n        p2 = self.lateral2(f2) + F.interpolate(p3, size=f2.shape[2:], mode='bilinear', align_corners=False)\n        p2 = self.upsample2(p2)\n        p1 = self.lateral1(f1) + F.interpolate(p2, size=f1.shape[2:], mode='bilinear', align_corners=False)\n        return self.final_conv(self.upsample4(self.upsample3(p1)))\n\nclass ClassificationHead(nn.Module):\n    def __init__(self, feature_dim=768, hidden_dim=256, dropout=0.3):\n        super().__init__()\n        self.fc = nn.Sequential(nn.Linear(feature_dim, hidden_dim), nn.ReLU(True), nn.Dropout(dropout), nn.Linear(hidden_dim, 1))\n    def forward(self, x): return self.fc(x)\n\nclass DualHeadDINOv2(nn.Module):\n    def __init__(self, model_name=\"facebook/dinov2-base\", feature_layers=[6, 9, 12]):\n        super().__init__()\n        self.backbone = Dinov2Model.from_pretrained(model_name)\n        self.feature_layers = feature_layers\n        self.segmentation_head = SegmentationHead(768, 256)\n        self.classification_head = ClassificationHead(768, 256, 0.3)\n\n    def forward(self, x):\n        outputs = self.backbone(x, output_hidden_states=True)\n        cls_token = outputs.hidden_states[-1][:, 0, :]\n        B, _, H, W = x.shape\n        h, w = H // 14, W // 14\n        spatial = [outputs.hidden_states[i][:, 1:, :].reshape(B, h, w, -1).permute(0, 3, 1, 2) for i in self.feature_layers]\n        cls_logits = self.classification_head(cls_token)\n        mask_logits = F.interpolate(self.segmentation_head(spatial), size=(H, W), mode='bilinear', align_corners=False)\n        return cls_logits, mask_logits\n\nprint(\"[OK] Model architecture defined\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Load Model\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"Device: {device}\")\n\nmodel = DualHeadDINOv2(config.DINOV2_MODEL, config.FEATURE_LAYERS)\ncheckpoint = torch.load(config.MODEL_DIR / \"best_model.pth\", map_location=device)\nmodel.load_state_dict(checkpoint['model_state_dict'])\nprint(f\"Loaded checkpoint from epoch {checkpoint.get('epoch', 'unknown')}\")\nmodel = model.to(device).eval()\nprint(\"[OK] Model loaded\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Inference\ntransform = A.Compose([\n    A.SmallestMaxSize(max_size=config.IMG_SIZE),\n    A.CenterCrop(height=config.CROP_SIZE, width=config.CROP_SIZE),\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2(),\n])\n\ntest_dir = config.DATA_DIR / \"test_images\"\ntest_images = sorted(list(test_dir.glob(\"*.png\")) + list(test_dir.glob(\"*.jpg\")))\nprint(f\"Found {len(test_images)} test images\")\n\npredictions = []\nfor img_path in tqdm(test_images, desc=\"Inference\"):\n    img = cv2.cvtColor(cv2.imread(str(img_path)), cv2.COLOR_BGR2RGB)\n    tensor = transform(image=img)['image'].unsqueeze(0).to(device)\n    with torch.no_grad():\n        cls_logits, _ = model(tensor)\n        prob = torch.sigmoid(cls_logits).item()\n    annotation = \"forged\" if prob > config.THRESHOLD else \"authentic\"\n    predictions.append({'case_id': img_path.stem, 'annotation': annotation})\n    print(f\"  {img_path.stem}: {annotation} (conf={prob:.4f})\")\n\nprint(f\"Processed {len(predictions)} images\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Save Submission\nsubmission_df = pd.DataFrame(predictions)\nconfig.OUTPUT_DIR.mkdir(parents=True, exist_ok=True)\nsubmission_df.to_csv(config.OUTPUT_DIR / \"submission.csv\", index=False)\nprint(f\"Submission saved: {config.OUTPUT_DIR / 'submission.csv'}\")\nprint(submission_df)\nprint(f\"\\nDistribution: {submission_df['annotation'].value_counts().to_dict()}\")\nprint(\"\\n\" + \"=\"*60)\nprint(\"Recod.ai/LUC WayneIA V3 - COMPLETE\")\nprint(\"CODE_KEY[166] FORGERY_CLASSIFIER_MATRIX\")\nprint(\"WayneIA: The AND is the AGI\")\nprint(\"=\"*60)"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10.0"}},"nbformat":4,"nbformat_minor":4}