{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# TEST MODE","metadata":{}},{"cell_type":"code","source":"TEST_MODE=True","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:04.056865Z","iopub.execute_input":"2025-10-06T12:34:04.057399Z","iopub.status.idle":"2025-10-06T12:34:04.063445Z","shell.execute_reply.started":"2025-10-06T12:34:04.057371Z","shell.execute_reply":"2025-10-06T12:34:04.062742Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Сиды","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport os\nimport torch\nimport random\n\nseed=42\n\nos.environ['PYTHONHASHSEED']=str(seed)\nrandom.seed(seed)\nnp.random.seed(seed)\ntorch.cuda.manual_seed(seed)\ntorch.cuda.manual_seed_all(seed)\ntorch.manual_seed(seed)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:04.080933Z","iopub.execute_input":"2025-10-06T12:34:04.081228Z","iopub.status.idle":"2025-10-06T12:34:08.327349Z","shell.execute_reply.started":"2025-10-06T12:34:04.081196Z","shell.execute_reply":"2025-10-06T12:34:08.326689Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:08.328647Z","iopub.execute_input":"2025-10-06T12:34:08.329103Z","iopub.status.idle":"2025-10-06T12:34:08.399007Z","shell.execute_reply.started":"2025-10-06T12:34:08.329085Z","shell.execute_reply":"2025-10-06T12:34:08.398136Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Импорты","metadata":{}},{"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\nfrom torch import nn \nimport timm\nfrom torchvision import transforms as v2\nimport PIL\nfrom PIL import Image\nimport tqdm as tqdm \nfrom sklearn.metrics import accuracy_score\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:08.399848Z","iopub.execute_input":"2025-10-06T12:34:08.400099Z","iopub.status.idle":"2025-10-06T12:34:16.667544Z","shell.execute_reply.started":"2025-10-06T12:34:08.400080Z","shell.execute_reply":"2025-10-06T12:34:16.666933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Загрузка данных","metadata":{}},{"cell_type":"code","source":"train=pd.read_csv('/kaggle/input/cassava-leaf-disease-classification/train.csv')\n#test=pd.read_csv('')\nsample=pd.read_csv('/kaggle/input/cassava-leaf-disease-classification/sample_submission.csv')\n\ntrain_img_dir='/kaggle/input/cassava-leaf-disease-classification/train_images'\ntest_img_dir='/kaggle/input/cassava-leaf-disease-classification/test_images'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.669315Z","iopub.execute_input":"2025-10-06T12:34:16.669711Z","iopub.status.idle":"2025-10-06T12:34:16.704511Z","shell.execute_reply.started":"2025-10-06T12:34:16.669693Z","shell.execute_reply":"2025-10-06T12:34:16.703950Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.705271Z","iopub.execute_input":"2025-10-06T12:34:16.705507Z","iopub.status.idle":"2025-10-06T12:34:16.728236Z","shell.execute_reply.started":"2025-10-06T12:34:16.705485Z","shell.execute_reply":"2025-10-06T12:34:16.727619Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if TEST_MODE:\n    train=train[:10000]\nelse:\n    train=train","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.728871Z","iopub.execute_input":"2025-10-06T12:34:16.729072Z","iopub.status.idle":"2025-10-06T12:34:16.741136Z","shell.execute_reply.started":"2025-10-06T12:34:16.729055Z","shell.execute_reply":"2025-10-06T12:34:16.740485Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Датасет","metadata":{}},{"cell_type":"code","source":"class CassavaDataset(Dataset):\n    def __init__(self, df, img_dir, transforms, is_train):\n        self.df=df\n        self.img_dir=img_dir\n        self.transforms=transforms\n        self.is_train=is_train\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row=self.df.iloc[idx]\n        img_df=row['image_id']\n        img_all=os.path.join(self.img_dir, f'{img_df}')\n        image=Image.open(img_all).convert('RGB')\n\n        if self.transforms is not None:\n            image=self.transforms(image)\n        else:\n            image=image\n\n\n        label=torch.tensor(int(row['label']), dtype=torch.long)\n\n        if self.is_train:\n            return {\n                'image': image,\n                'label': label\n            }\n        else:\n            return{\n                'image': image, \n                'id': img_df\n            }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.741891Z","iopub.execute_input":"2025-10-06T12:34:16.742146Z","iopub.status.idle":"2025-10-06T12:34:16.757345Z","shell.execute_reply.started":"2025-10-06T12:34:16.742118Z","shell.execute_reply":"2025-10-06T12:34:16.756607Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Аугментации","metadata":{}},{"cell_type":"code","source":"IMG_SIZE=512","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.758002Z","iopub.execute_input":"2025-10-06T12:34:16.758230Z","iopub.status.idle":"2025-10-06T12:34:16.771793Z","shell.execute_reply.started":"2025-10-06T12:34:16.758212Z","shell.execute_reply":"2025-10-06T12:34:16.771161Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transforms=v2.Compose([\n    v2.Resize((IMG_SIZE, IMG_SIZE)),\n    v2.ToTensor(),\n    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\neval_transforms=v2.Compose([\n    v2.Resize((IMG_SIZE, IMG_SIZE)),\n    v2.ToTensor(),\n    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n\ntest_transforms=v2.Compose([\n    v2.Resize((IMG_SIZE, IMG_SIZE)),\n    v2.ToTensor(),\n    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.772508Z","iopub.execute_input":"2025-10-06T12:34:16.772681Z","iopub.status.idle":"2025-10-06T12:34:16.787702Z","shell.execute_reply.started":"2025-10-06T12:34:16.772666Z","shell.execute_reply":"2025-10-06T12:34:16.786898Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transforms = v2.Compose([\n    v2.RandomResizedCrop(IMG_SIZE, scale=(0.8, 1.0)),\n    v2.RandomHorizontalFlip(p=0.5),\n    #v2.VerticalFlip(p=0.5),\n    #v2.RandomRotation(15),\n    v2.ColorJitter(0.2, 0.2, 0.2, 0.1),\n    v2.ToTensor(),\n    v2.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]),\n    #v2.RandomErasing(p=0.25)\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.789818Z","iopub.execute_input":"2025-10-06T12:34:16.790425Z","iopub.status.idle":"2025-10-06T12:34:16.805434Z","shell.execute_reply.started":"2025-10-06T12:34:16.790396Z","shell.execute_reply":"2025-10-06T12:34:16.804743Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"eval_transforms = v2.Compose([\n    v2.Resize(IMG_SIZE),\n    \n    v2.ToTensor(),\n    v2.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]),\n])\ntest_transforms = eval_transforms","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.806074Z","iopub.execute_input":"2025-10-06T12:34:16.806309Z","iopub.status.idle":"2025-10-06T12:34:16.820461Z","shell.execute_reply.started":"2025-10-06T12:34:16.806291Z","shell.execute_reply":"2025-10-06T12:34:16.819845Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Создание даталоадеров","metadata":{}},{"cell_type":"code","source":"train_data, eval_data=train_test_split(train, random_state=seed, stratify=train['label'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.821087Z","iopub.execute_input":"2025-10-06T12:34:16.821302Z","iopub.status.idle":"2025-10-06T12:34:16.841905Z","shell.execute_reply.started":"2025-10-06T12:34:16.821286Z","shell.execute_reply":"2025-10-06T12:34:16.841148Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset=CassavaDataset(train_data, train_img_dir, train_transforms, is_train=True)\neval_dataset=CassavaDataset(eval_data, train_img_dir, eval_transforms, is_train=True)\ntest_dataset=CassavaDataset(sample, test_img_dir, test_transforms, is_train=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.842734Z","iopub.execute_input":"2025-10-06T12:34:16.843366Z","iopub.status.idle":"2025-10-06T12:34:16.847596Z","shell.execute_reply.started":"2025-10-06T12:34:16.843342Z","shell.execute_reply":"2025-10-06T12:34:16.847016Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataloader=DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=8)\neval_dataloader=DataLoader(eval_dataset, batch_size=16, shuffle=False, num_workers=8)\ntest_dataloader=DataLoader(test_dataset, batch_size=16, shuffle=False, num_workers=8)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.848378Z","iopub.execute_input":"2025-10-06T12:34:16.848625Z","iopub.status.idle":"2025-10-06T12:34:16.865116Z","shell.execute_reply.started":"2025-10-06T12:34:16.848604Z","shell.execute_reply":"2025-10-06T12:34:16.864458Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"model=timm.create_model('resnext50_32x4d', pretrained=True, num_classes=5).to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:16.865754Z","iopub.execute_input":"2025-10-06T12:34:16.865951Z","iopub.status.idle":"2025-10-06T12:34:20.233406Z","shell.execute_reply.started":"2025-10-06T12:34:16.865935Z","shell.execute_reply":"2025-10-06T12:34:20.232605Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# EPOCHS","metadata":{}},{"cell_type":"code","source":"EPOCHS=5","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:20.234370Z","iopub.execute_input":"2025-10-06T12:34:20.234645Z","iopub.status.idle":"2025-10-06T12:34:20.237903Z","shell.execute_reply.started":"2025-10-06T12:34:20.234622Z","shell.execute_reply":"2025-10-06T12:34:20.237300Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Criterion","metadata":{}},{"cell_type":"code","source":"#criterion=torch.nn.CrossEntropyLoss(label_smoothing=0.1)\nclass_counts = train['label'].value_counts().sort_index().values.astype(float)\nw = (1.0 / np.maximum(class_counts, 1))\nw = w / w.mean()\ncls_weights = torch.tensor(w, dtype=torch.float, device=device)\n\ncriterion = nn.CrossEntropyLoss(weight=cls_weights, label_smoothing=0.05)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:20.238759Z","iopub.execute_input":"2025-10-06T12:34:20.239020Z","iopub.status.idle":"2025-10-06T12:34:20.260708Z","shell.execute_reply.started":"2025-10-06T12:34:20.238998Z","shell.execute_reply":"2025-10-06T12:34:20.260093Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# optimizer","metadata":{}},{"cell_type":"code","source":"optimizer=torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-6)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:20.261364Z","iopub.execute_input":"2025-10-06T12:34:20.261581Z","iopub.status.idle":"2025-10-06T12:34:20.271995Z","shell.execute_reply.started":"2025-10-06T12:34:20.261565Z","shell.execute_reply":"2025-10-06T12:34:20.271439Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#optimizer=torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:20.272735Z","iopub.execute_input":"2025-10-06T12:34:20.272923Z","iopub.status.idle":"2025-10-06T12:34:20.286972Z","shell.execute_reply.started":"2025-10-06T12:34:20.272909Z","shell.execute_reply":"2025-10-06T12:34:20.286322Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# scheduler","metadata":{}},{"cell_type":"code","source":"steps_per_epoch = len(train_dataloader)\ntotal_steps = steps_per_epoch * EPOCHS","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:20.287615Z","iopub.execute_input":"2025-10-06T12:34:20.287852Z","iopub.status.idle":"2025-10-06T12:34:20.301870Z","shell.execute_reply.started":"2025-10-06T12:34:20.287829Z","shell.execute_reply":"2025-10-06T12:34:20.301360Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"scheduler=torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS,)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:20.302518Z","iopub.execute_input":"2025-10-06T12:34:20.302709Z","iopub.status.idle":"2025-10-06T12:34:20.316306Z","shell.execute_reply.started":"2025-10-06T12:34:20.302688Z","shell.execute_reply":"2025-10-06T12:34:20.315550Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training loop","metadata":{}},{"cell_type":"code","source":"'''\nfor epoch in range(1, EPOCHS+1):\n    model.train()\n    running_loss, running_correct, n=0,0.0, 0.0\n    optimizer.zero_grad()\n    pbar=tqdm.tqdm(train_dataloader, desc='Training', leave=False)\n\n    for step, batch in enumerate(pbar):\n        X=batch['image'].to(device)\n        y=batch['label'].to(device)\n        \n        optimizer.zero_grad()\n        \n        logits=model(X)\n\n        loss=criterion(logits, y)\n        loss.backward()\n        \n        running_loss+=loss.item()*X.size(0)\n        \n        optimizer.step()\n        \n        preds=logits.argmax(dim=1)\n        running_correct+=(preds==y).sum().item()\n        n+=X.size(0)\n        pbar.set_postfix(loss=running_loss/ max(n, 1), acc=running_correct / max(n, 1))\n        \n\n    scheduler.step()\n    print(f'train {epoch} epoch : loss={running_loss/n:.4f}, acc={running_correct/n:.4f}')\n    model.eval()\n    loss_sum, correct, n=0.0,0,0\n    with torch.no_grad():\n        \n        pbar_eval=tqdm.tqdm(eval_dataloader, desc='Evaluation: ', leave=False)\n        for batch in pbar_eval:\n            X=batch['image'].to(device)\n            y=batch['label'].to(device)\n\n            logits=model(X)\n            loss=criterion(logits, y)\n\n            loss_sum+=loss.item()* X.size(0)\n\n            \n\n            n+=X.size(0)\n            preds=logits.argmax(dim=1)\n\n            correct+=(preds==y).sum().item()\n            \n            #acc=accuracy_score(preds, y)\n\n        \n            #pbar_eval.set_prefix()\n        val_loss = loss_sum / n\n        val_acc  = correct / n\n        print(f\"valid {epoch} epoch : loss={val_loss:.4f}, acc={val_acc:.4f}\")\n\n        \n    \n  '''  ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"scaler = torch.cuda.amp.GradScaler(enabled=(device.type=='cuda'))\nbest_acc = 0.0\nBEST_PATH = \"/kaggle/working/best_resnext50.pth\"\n\nfor epoch in range(1, EPOCHS+1):\n    model.train()\n    running_loss = 0.0\n    running_correct = 0\n    n = 0\n\n    for batch in tqdm.tqdm(train_dataloader, desc=f'Training {epoch}', leave=False):\n        X = batch['image'].to(device, non_blocking=True)\n        y = batch['label'].to(device, non_blocking=True)\n\n        optimizer.zero_grad(set_to_none=True)\n        with torch.cuda.amp.autocast(enabled=(device.type=='cuda')):\n            logits = model(X)\n            loss   = criterion(logits, y)\n\n        scaler.scale(loss).backward()\n        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)\n        scaler.step(optimizer)\n        scaler.update()\n\n        running_loss += loss.item() * X.size(0)\n        running_correct += (logits.argmax(1) == y).sum().item()\n        n += X.size(0)\n\n    scheduler.step()\n    print(f'train {epoch}: loss={running_loss/n:.4f}, acc={running_correct/n:.4f}')\n\n    # ------ eval ------\n    model.eval()\n    loss_sum = 0.0; correct = 0; nval = 0\n    with torch.no_grad(), torch.cuda.amp.autocast(enabled=(device.type=='cuda')):\n        for batch in tqdm.tqdm(eval_dataloader, desc='Evaluation', leave=False):\n            X = batch['image'].to(device, non_blocking=True)\n            y = batch['label'].to(device, non_blocking=True)\n            logits = model(X)\n            loss = criterion(logits, y)\n            loss_sum += loss.item() * X.size(0)\n            correct  += (logits.argmax(1) == y).sum().item()\n            nval += X.size(0)\n\n    val_loss = loss_sum / nval\n    val_acc  = correct / nval\n    print(f\"valid {epoch}: loss={val_loss:.4f}, acc={val_acc:.4f}\")\n\n    if val_acc > best_acc:\n        best_acc = val_acc\n        torch.save({\n            \"state_dict\": model.state_dict(),\n            \"meta\": {\n                \"model_name\": \"resnext50_32x4d\",\n                \"num_classes\": 5,\n                \"img_size\": IMG_SIZE,\n                \"mean\": [0.485,0.456,0.406],\n                \"std\":  [0.229,0.224,0.225],\n            }\n        }, BEST_PATH)\n        print(\">> saved best to\", BEST_PATH)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T12:34:20.317163Z","iopub.execute_input":"2025-10-06T12:34:20.317783Z","iopub.status.idle":"2025-10-06T13:01:31.548277Z","shell.execute_reply.started":"2025-10-06T12:34:20.317764Z","shell.execute_reply":"2025-10-06T13:01:31.547222Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# resnet50 valid 2 epoch : loss=0.5259, acc=0.8160\n\n8320","metadata":{}},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SAVE_PATH = \"/kaggle/working/resnext50_32x4d.pth\"\n\nmeta = {\n    \"model_name\": \"resnext50_32x4d\",   # важно для recreate\n    \"num_classes\": 5,\n    \"img_size\": 224,\n    \"mean\": [0.485, 0.456, 0.406],\n    \"std\":  [0.229, 0.224, 0.225],\n}\ntorch.save({\"state_dict\": model.state_dict(), \"meta\": meta}, SAVE_PATH)\nprint(\"saved to\", SAVE_PATH)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-06T13:01:31.557351Z","iopub.execute_input":"2025-10-06T13:01:31.557590Z","iopub.status.idle":"2025-10-06T13:01:31.715652Z","shell.execute_reply.started":"2025-10-06T13:01:31.557566Z","shell.execute_reply":"2025-10-06T13:01:31.714998Z"}},"outputs":[],"execution_count":null}]}