{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":25563,"databundleVersionId":2094376,"sourceType":"competition"}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport json\nimport random\nimport torch\nimport torchvision.transforms as T\nimport albumentations as A\nimport albumentations.pytorch\nimport pandas as pd\nimport numpy as np\nimport torch.nn as nn\nimport matplotlib.pyplot as plt\nimport torch.optim as optim\nimport torchvision.models as models\nimport torch.utils.data as data\nfrom torch.optim.lr_scheduler import CosineAnnealingLR\nfrom torch.utils.tensorboard import SummaryWriter\nfrom PIL import Image\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:32:51.690400Z","iopub.execute_input":"2025-02-28T21:32:51.690631Z","iopub.status.idle":"2025-02-28T21:33:13.543591Z","shell.execute_reply.started":"2025-02-28T21:32:51.690599Z","shell.execute_reply":"2025-02-28T21:33:13.542919Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 💾 Параметры\nBATCH_SIZE = 1024\nNUM_EPOCHS = 3\nLR = 1e-4\nIMG_SIZE = 224\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:13.544633Z","iopub.execute_input":"2025-02-28T21:33:13.545184Z","iopub.status.idle":"2025-02-28T21:33:13.600282Z","shell.execute_reply.started":"2025-02-28T21:33:13.545145Z","shell.execute_reply":"2025-02-28T21:33:13.599216Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DEVICE","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:13.602028Z","iopub.execute_input":"2025-02-28T21:33:13.602259Z","iopub.status.idle":"2025-02-28T21:33:13.632255Z","shell.execute_reply.started":"2025-02-28T21:33:13.602236Z","shell.execute_reply":"2025-02-28T21:33:13.631595Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 💿 Подготовка датасета\nDATA_DIR = \"/kaggle/input/plant-pathology-2021-fgvc8\"\nCSV_PATH = os.path.join(DATA_DIR, \"train.csv\")\n\ndf = pd.read_csv(CSV_PATH)\ndf[\"image\"] = df[\"image\"].apply(lambda x: os.path.join(DATA_DIR, \"images\", x))\n\n# 🎭 Преобразование классов в one-hot\nLABELS = [\"healthy\", \"scab\", \"rust\", \"complex\"]\nfor label in LABELS:\n    df[label] = df[\"labels\"].apply(lambda x: 1 if label in x else 0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:13.633686Z","iopub.execute_input":"2025-02-28T21:33:13.634011Z","iopub.status.idle":"2025-02-28T21:33:13.733506Z","shell.execute_reply.started":"2025-02-28T21:33:13.633976Z","shell.execute_reply":"2025-02-28T21:33:13.732887Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Аугментации (Albu + Torch)\ntrain_transforms = A.Compose([\n    A.Resize(IMG_SIZE, IMG_SIZE),\n    A.RandomResizedCrop(IMG_SIZE, IMG_SIZE, scale=(0.8, 1.0)),\n    A.HorizontalFlip(),\n    A.VerticalFlip(),\n    A.RandomBrightnessContrast(),\n    A.Normalize(),\n    A.pytorch.ToTensorV2(),\n])\n\nval_transforms = A.Compose([\n    A.Resize(IMG_SIZE, IMG_SIZE),\n    A.Normalize(),\n    A.pytorch.ToTensorV2(),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:13.734280Z","iopub.execute_input":"2025-02-28T21:33:13.734511Z","iopub.status.idle":"2025-02-28T21:33:13.742689Z","shell.execute_reply.started":"2025-02-28T21:33:13.734490Z","shell.execute_reply":"2025-02-28T21:33:13.742047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 📦 Датасет\nclass PlantDataset(data.Dataset):\n    def __init__(self, df, transform):\n        self.df = df\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        im_file_name = row[\"image\"].replace(\"images\", \"train_images\")\n        image = np.array(Image.open(im_file_name).convert(\"RGB\"))\n        label = row[LABELS].values.astype(np.float32)\n        image = self.transform(image=image)[\"image\"]\n        return image, torch.tensor(label, dtype=torch.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:13.743508Z","iopub.execute_input":"2025-02-28T21:33:13.743813Z","iopub.status.idle":"2025-02-28T21:33:13.758712Z","shell.execute_reply.started":"2025-02-28T21:33:13.743769Z","shell.execute_reply":"2025-02-28T21:33:13.757975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 🔀 Разделение данных\ntrain_df, val_df = np.split(df.sample(frac=1, random_state=42), [int(0.8 * len(df))])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:13.759436Z","iopub.execute_input":"2025-02-28T21:33:13.759697Z","iopub.status.idle":"2025-02-28T21:33:13.788137Z","shell.execute_reply.started":"2025-02-28T21:33:13.759677Z","shell.execute_reply":"2025-02-28T21:33:13.787423Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 🏋️ Dataloader с балансировщиком классов\ntrain_dataset = PlantDataset(train_df, train_transforms)\nval_dataset = PlantDataset(val_df, val_transforms)\n\ntrain_loader = data.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4)\nval_loader = data.DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:13.789762Z","iopub.execute_input":"2025-02-28T21:33:13.790011Z","iopub.status.idle":"2025-02-28T21:33:13.794070Z","shell.execute_reply.started":"2025-02-28T21:33:13.789992Z","shell.execute_reply":"2025-02-28T21:33:13.793215Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 🔥 Вывод примеров картинок с аугментациями\ndef show_augmented_images(dataset, num_samples=6):\n    fig, axes = plt.subplots(1, num_samples, figsize=(15, 5))\n    for i in range(num_samples):\n        image, label = dataset[i]\n        image = image.permute(1, 2, 0).cpu().numpy()  # Переводим в numpy\n        image = (image - image.min()) / (image.max() - image.min())  # Нормализация для отображения\n        axes[i].imshow(image)\n        axes[i].axis(\"off\")\n        axes[i].set_title(\", \".join(np.array(LABELS)[label > 0]))\n    plt.tight_layout()\n    plt.savefig(\"sample_images.png\")\n    plt.show()\n\nshow_augmented_images(train_dataset)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:13.794912Z","iopub.execute_input":"2025-02-28T21:33:13.795095Z","iopub.status.idle":"2025-02-28T21:33:16.289616Z","shell.execute_reply.started":"2025-02-28T21:33:13.795079Z","shell.execute_reply":"2025-02-28T21:33:16.288701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 🔥 Модель ResNet + Transfer Learning\n# model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2\nmodel = models.resnet18(pretrained=True)\nnum_features = model.fc.in_features\nmodel.fc = nn.Linear(num_features, len(LABELS))  # Заменяем последний слой\n\n# 🔒 Замораживаем все слои, кроме последнего\nfor param in model.parameters():\n    param.requires_grad = False  # Заморозили все слои\n\n# 🔓 Размораживаем последний слой\nfor param in model.fc.parameters():\n    param.requires_grad = True  # Обучаем только fc-слой\n\nmodel = model.to(DEVICE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:16.290533Z","iopub.execute_input":"2025-02-28T21:33:16.290791Z","iopub.status.idle":"2025-02-28T21:33:17.007782Z","shell.execute_reply.started":"2025-02-28T21:33:16.290767Z","shell.execute_reply":"2025-02-28T21:33:17.007106Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ⚙️ Оптимизатор и лосс\ncriterion = nn.BCEWithLogitsLoss()\noptimizer = optim.AdamW(model.parameters(), lr=LR)\nscheduler = CosineAnnealingLR(optimizer, T_max=NUM_EPOCHS)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:17.008555Z","iopub.execute_input":"2025-02-28T21:33:17.008768Z","iopub.status.idle":"2025-02-28T21:33:17.013285Z","shell.execute_reply.started":"2025-02-28T21:33:17.008749Z","shell.execute_reply":"2025-02-28T21:33:17.012500Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 📝 TensorBoard\nwriter = SummaryWriter(\"runs/plant_pathology\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:17.014012Z","iopub.execute_input":"2025-02-28T21:33:17.014252Z","iopub.status.idle":"2025-02-28T21:33:17.029480Z","shell.execute_reply.started":"2025-02-28T21:33:17.014231Z","shell.execute_reply":"2025-02-28T21:33:17.028865Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 🚀 Тренировка\ndef train(model, train_loader, val_loader, criterion, optimizer, scheduler, num_epochs):\n    best_val_loss = float(\"inf\")\n\n    for epoch in range(num_epochs):\n        model.train()\n        train_loss = 0\n        for images, labels in tqdm(train_loader, desc=f\"Epoch {epoch+1}/{num_epochs} [TRAIN]\"):\n            images, labels = images.to(DEVICE), labels.to(DEVICE)\n\n            optimizer.zero_grad()\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n\n            train_loss += loss.item()\n\n        # 🔍 Валидация\n        model.eval()\n        val_loss = 0\n        correct = 0\n        total = 0\n\n        with torch.no_grad():\n            for images, labels in tqdm(val_loader, desc=f\"Epoch {epoch+1}/{num_epochs} [VAL]\"):\n                images, labels = images.to(DEVICE), labels.to(DEVICE)\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n                val_loss += loss.item()\n\n                preds = torch.sigmoid(outputs) > 0.5\n                correct += (preds == labels).sum().item()\n                total += labels.numel()\n\n        train_loss /= len(train_loader)\n        val_loss /= len(val_loader)\n        accuracy = correct / total\n\n        print(f\"Epoch {epoch+1}: Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}, Accuracy: {accuracy:.4f}\")\n\n        writer.add_scalar(\"Loss/train\", train_loss, epoch)\n        writer.add_scalar(\"Loss/val\", val_loss, epoch)\n        writer.add_scalar(\"Accuracy/val\", accuracy, epoch)\n\n        scheduler.step()\n\n        # 💾 Сохранение лучшей модели\n        if val_loss < best_val_loss:\n            best_val_loss = val_loss\n            torch.save(model.state_dict(), \"best_model.pth\")\n            print(\"✅ Model saved!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:17.030205Z","iopub.execute_input":"2025-02-28T21:33:17.030442Z","iopub.status.idle":"2025-02-28T21:33:17.039365Z","shell.execute_reply.started":"2025-02-28T21:33:17.030422Z","shell.execute_reply":"2025-02-28T21:33:17.038751Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train(model, train_loader, val_loader, criterion, optimizer, scheduler, NUM_EPOCHS)\nwriter.close()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-28T21:33:17.040176Z","iopub.execute_input":"2025-02-28T21:33:17.040464Z","execution_failed":"2025-02-28T21:36:51.809Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}