{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":5441,"databundleVersionId":38425,"isSourceIdPinned":false}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## CV\n\nссылка на соревнование: https://www.kaggle.com/competitions/dogs-vs-cats-redux-kernels-edition\n\nпостановка задачи: научиться различать котиков и собачек на фотографиях.\n\nидея решения: кажется, что лучше, чем существующие решения, мы не придумаем, но для начала можно попробовать cnn, посмотреть, как отработает, а потом уже transfer learning чего-нибудь.","metadata":{}},{"cell_type":"markdown","source":"скелет: построим простую сверточную нейронную сеть для бейзлайна.","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\n\nimport torchvision\nfrom torchvision import transforms\n\nfrom sklearn.model_selection import train_test_split","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-12T16:24:57.122737Z","iopub.execute_input":"2026-03-12T16:24:57.123485Z","iopub.status.idle":"2026-03-12T16:24:57.127488Z","shell.execute_reply.started":"2026-03-12T16:24:57.123453Z","shell.execute_reply":"2026-03-12T16:24:57.126773Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DogsCatsDataset(Dataset):\n    def __init__(self, image_paths, labels=None, transform=None):\n        self.image_paths = image_paths\n        self.labels = labels\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.image_paths)\n    \n    def __getitem__(self, idx):\n        img = Image.open(self.image_paths[idx]).convert(\"RGB\")\n        if self.transform:\n            img = self.transform(img)\n        if self.labels is not None:\n            label = torch.tensor(self.labels[idx]).long()\n            return img, label\n        else:\n            return img, self.image_paths[idx]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T17:16:17.716076Z","iopub.execute_input":"2026-03-12T17:16:17.716398Z","iopub.status.idle":"2026-03-12T17:16:17.721842Z","shell.execute_reply.started":"2026-03-12T17:16:17.716369Z","shell.execute_reply":"2026-03-12T17:16:17.721285Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMAGE_SIZE = 128\n\ntrain_transform = transforms.Compose([\n    \n    transforms.Resize((IMAGE_SIZE,IMAGE_SIZE)),\n    transforms.ToTensor()\n])\n\nval_transform = transforms.Compose([\n    \n    transforms.Resize((IMAGE_SIZE,IMAGE_SIZE)),\n    transforms.ToTensor()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T17:19:50.111902Z","iopub.execute_input":"2026-03-12T17:19:50.112165Z","iopub.status.idle":"2026-03-12T17:19:50.116450Z","shell.execute_reply.started":"2026-03-12T17:19:50.112142Z","shell.execute_reply":"2026-03-12T17:19:50.115781Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = '/kaggle/working/train'\nTEST_DIR = '/kaggle/working/test'\n\nimage_paths = []\nlabels = []\n\nfor f in os.listdir(DATA_DIR):\n    full_path = os.path.join(DATA_DIR,f)\n    image_paths.append(full_path)\n    labels.append(0 if f.startswith(\"cat\") else 1)\n\ntrain_paths, val_paths, train_labels, val_labels = train_test_split(\n    image_paths, labels, test_size=0.2, stratify=labels, random_state=42\n)\n\ntrain_dataset = DogsCatsDataset(train_paths, train_labels, train_transform)\nval_dataset   = DogsCatsDataset(val_paths, val_labels, val_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)\nval_loader   = DataLoader(val_dataset, batch_size=64, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T17:19:51.857612Z","iopub.execute_input":"2026-03-12T17:19:51.858090Z","iopub.status.idle":"2026-03-12T17:19:51.915386Z","shell.execute_reply.started":"2026-03-12T17:19:51.858053Z","shell.execute_reply":"2026-03-12T17:19:51.914586Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SimpleCNN(nn.Module):\n    def __init__(self):\n        super().__init__()\n        \n        self.conv_layers = nn.Sequential(\n            nn.Conv2d(3,32,3,padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(32),\n            nn.MaxPool2d(2),\n            nn.Dropout(0.2),\n            \n            nn.Conv2d(32,64,3,padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(64),\n            nn.MaxPool2d(2),\n            nn.Dropout(0.2),\n            \n            nn.Conv2d(64,128,3,padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(128),\n            nn.MaxPool2d(2),\n            nn.Dropout(0.2),\n            \n            nn.Conv2d(128,256,3,padding=1),\n            nn.ReLU(),\n            nn.BatchNorm2d(256),\n            nn.MaxPool2d(2),\n            nn.Dropout(0.2)\n        )\n        \n        self.fc_layers = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(256*8*8, 512),\n            nn.ReLU(),\n            nn.BatchNorm1d(512),\n            nn.Dropout(0.2),\n            nn.Linear(512,2)\n        )\n        \n    def forward(self,x):\n        x = self.conv_layers(x)\n        x = self.fc_layers(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T17:19:58.663781Z","iopub.execute_input":"2026-03-12T17:19:58.664544Z","iopub.status.idle":"2026-03-12T17:19:58.671044Z","shell.execute_reply.started":"2026-03-12T17:19:58.664515Z","shell.execute_reply":"2026-03-12T17:19:58.670236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = SimpleCNN().to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T17:20:01.276056Z","iopub.execute_input":"2026-03-12T17:20:01.276791Z","iopub.status.idle":"2026-03-12T17:20:01.357699Z","shell.execute_reply.started":"2026-03-12T17:20:01.276759Z","shell.execute_reply":"2026-03-12T17:20:01.356936Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 20\n\nfor epoch in range(epochs):\n    model.train()\n    train_loss = 0\n    for imgs, labels in train_loader:\n        imgs, labels = imgs.to(device), labels.to(device)\n        optimizer.zero_grad()\n        outputs = model(imgs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        train_loss += loss.item()\n    train_loss /= len(train_loader)\n    \n    model.eval()\n    val_correct = 0\n    val_total = 0\n    with torch.no_grad():\n        for imgs, labels in val_loader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            outputs = model(imgs)\n            preds = outputs.argmax(dim=1)\n            val_correct += (preds==labels).sum().item()\n            val_total += labels.size(0)\n    val_acc = val_correct / val_total\n    print(f\"Epoch {epoch+1}: Train loss={train_loss:.4f}, Val acc={val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T17:20:09.021085Z","iopub.execute_input":"2026-03-12T17:20:09.021415Z","iopub.status.idle":"2026-03-12T17:35:38.930199Z","shell.execute_reply.started":"2026-03-12T17:20:09.021387Z","shell.execute_reply":"2026-03-12T17:35:38.928329Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_files = [os.path.join(TEST_DIR,f) for f in os.listdir(TEST_DIR)]\ntest_dataset = DogsCatsDataset(test_files, labels=None, transform=val_transform)\ntest_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)\n\nmodel.eval()\npreds = []\nids = []\n\nwith torch.no_grad():\n    for imgs, paths in test_loader:\n        imgs = imgs.to(device)\n        outputs = model(imgs)\n        probs = torch.softmax(outputs, dim=1)[:,1]\n        preds.extend(probs.cpu().numpy())\n        for p in paths:\n            ids.append(int(os.path.basename(p).split(\".\")[0]))\n\nsubmission = pd.DataFrame({\"id\": ids, \"label\": preds})\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T17:59:45.601131Z","iopub.execute_input":"2026-03-12T17:59:45.601456Z","iopub.status.idle":"2026-03-12T18:00:15.868890Z","shell.execute_reply.started":"2026-03-12T17:59:45.601417Z","shell.execute_reply":"2026-03-12T18:00:15.868302Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn.functional as F  # я забыла что нужен был логлосс\nfrom sklearn.metrics import log_loss\n\n\ndef evaluate_logloss(model, loader, device):\n    model.eval()\n    all_probs = []\n    all_labels = []\n\n    with torch.no_grad():\n        for imgs, labels in loader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            outputs = model(imgs)\n            probs = F.softmax(outputs, dim=1)[:,1]\n            all_probs.extend(probs.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n\n    return log_loss(all_labels, all_probs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T18:39:29.958146Z","iopub.execute_input":"2026-03-12T18:39:29.958859Z","iopub.status.idle":"2026-03-12T18:39:29.963876Z","shell.execute_reply.started":"2026-03-12T18:39:29.958827Z","shell.execute_reply":"2026-03-12T18:39:29.963336Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"logloss: 0.32\n\nlb logloss: 0.44\n\nплохо, и большой gap, кроме того, что архитектура слабовата, вряд ли наша модель хорошо обобщает. ","metadata":{}},{"cell_type":"markdown","source":"гипотеза 0: аугментации улучшат обобщающую способность.","metadata":{}},{"cell_type":"code","source":"transform_aug = transforms.Compose([\n    transforms.Resize((128,128)),\n    \n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(15),\n    \n    transforms.ColorJitter(\n        brightness=0.2,\n        contrast=0.2,\n        saturation=0.2\n    ),\n    \n    transforms.ToTensor()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T19:15:56.217137Z","iopub.execute_input":"2026-03-12T19:15:56.217896Z","iopub.status.idle":"2026-03-12T19:15:56.221893Z","shell.execute_reply.started":"2026-03-12T19:15:56.217865Z","shell.execute_reply":"2026-03-12T19:15:56.221321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = DogsCatsDataset(train_paths, train_labels, transform_aug)\nval_dataset   = DogsCatsDataset(val_paths, val_labels, val_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)\nval_loader   = DataLoader(val_dataset, batch_size=64, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T18:10:56.756581Z","iopub.execute_input":"2026-03-12T18:10:56.757082Z","iopub.status.idle":"2026-03-12T18:10:56.761069Z","shell.execute_reply.started":"2026-03-12T18:10:56.757049Z","shell.execute_reply":"2026-03-12T18:10:56.760274Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = SimpleCNN().to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n\nepochs = 10\n\nfor epoch in range(epochs):\n    model.train()\n    train_loss = 0\n    for imgs, labels in train_loader:\n        imgs, labels = imgs.to(device), labels.to(device)\n        optimizer.zero_grad()\n        outputs = model(imgs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        train_loss += loss.item()\n    train_loss /= len(train_loader)\n    \n    model.eval()\n    val_correct = 0\n    val_total = 0\n    with torch.no_grad():\n        for imgs, labels in val_loader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            outputs = model(imgs)\n            preds = outputs.argmax(dim=1)\n            val_correct += (preds==labels).sum().item()\n            val_total += labels.size(0)\n    val_acc = val_correct / val_total\n    print(f\"Epoch {epoch+1}: Train loss={train_loss:.4f}, Val acc={val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T18:10:59.394240Z","iopub.execute_input":"2026-03-12T18:10:59.395018Z","iopub.status.idle":"2026-03-12T18:26:10.981351Z","shell.execute_reply.started":"2026-03-12T18:10:59.394987Z","shell.execute_reply":"2026-03-12T18:26:10.980551Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\npreds = []\nids = []\n\nwith torch.no_grad():\n    for imgs, paths in test_loader:\n        imgs = imgs.to(device)\n        outputs = model(imgs)\n        probs = torch.softmax(outputs, dim=1)[:,1]\n        preds.extend(probs.cpu().numpy())\n        for p in paths:\n            ids.append(int(os.path.basename(p).split(\".\")[0]))\n\nsubmission = pd.DataFrame({\"id\": ids, \"label\": preds})\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T18:28:15.599349Z","iopub.execute_input":"2026-03-12T18:28:15.600090Z","iopub.status.idle":"2026-03-12T18:28:45.820028Z","shell.execute_reply.started":"2026-03-12T18:28:15.600051Z","shell.execute_reply":"2026-03-12T18:28:45.819452Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"evaluate_logloss(model, val_loader, device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T18:39:32.460344Z","iopub.execute_input":"2026-03-12T18:39:32.461044Z","iopub.status.idle":"2026-03-12T18:39:44.571523Z","shell.execute_reply.started":"2026-03-12T18:39:32.461017Z","shell.execute_reply":"2026-03-12T18:39:44.570874Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"logloss: 0.20\n\nlb logloss: 0.23\n\nзначительно сократилась разница, при том, что акураси при обучении не изменилась. гипотеза верна.","metadata":{}},{"cell_type":"markdown","source":"гипотеза 1: simple cnn показывает некоторые результаты, но все же хочется попробовать использовать pre-trained веса, например, inceptoinv3. заморозим все, кроме верхних слоев, чтобы стабильно дообучиться на наших данных.","metadata":{}},{"cell_type":"code","source":"import os\nimport shutil\n\nsrc_dir = \"/kaggle/working/train\"\ndst_dir = \"/kaggle/working/train_split\"\n\ncat_dir = os.path.join(dst_dir, \"cat\")\ndog_dir = os.path.join(dst_dir, \"dog\")\n\nos.makedirs(cat_dir, exist_ok=True)\nos.makedirs(dog_dir, exist_ok=True)\n\nfor file in os.listdir(src_dir):\n    \n    src_path = os.path.join(src_dir, file)\n    \n    if file.startswith(\"cat\"):\n        shutil.copy(src_path, os.path.join(cat_dir, file))\n        \n    elif file.startswith(\"dog\"):\n        shutil.copy(src_path, os.path.join(dog_dir, file))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np # перешла в керас\nimport pandas as pd\nimport tensorflow as tf\n\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import InceptionV3\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dense\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import SGD\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T20:45:14.484204Z","iopub.execute_input":"2026-03-12T20:45:14.484799Z","iopub.status.idle":"2026-03-12T20:45:54.325652Z","shell.execute_reply.started":"2026-03-12T20:45:14.484762Z","shell.execute_reply":"2026-03-12T20:45:54.325061Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMAGE_WIDTH = 299\nIMAGE_HEIGHT = 299\n\nBATCH_SIZE = 64\nVAL_SPLIT = 0.2\n\nNUM_CLASSES = 2\n\ntrain_dir = \"/kaggle/working/train_split\"\ntest_dir = \"/kaggle/working/test\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T20:47:03.749824Z","iopub.execute_input":"2026-03-12T20:47:03.750185Z","iopub.status.idle":"2026-03-12T20:47:03.754204Z","shell.execute_reply.started":"2026-03-12T20:47:03.750158Z","shell.execute_reply":"2026-03-12T20:47:03.753603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rescale=1/255,\n    horizontal_flip=True,\n    validation_split=VAL_SPLIT\n)\n\ntrain_generator = train_datagen.flow_from_directory(\n    train_dir,\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    batch_size=BATCH_SIZE,\n    subset=\"training\",\n    seed=42,\n    shuffle=True,\n    class_mode=\"categorical\"\n)\n\nval_generator = train_datagen.flow_from_directory(\n    train_dir,\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    batch_size=BATCH_SIZE,\n    subset=\"validation\",\n    seed=42,\n    shuffle=True,\n    class_mode=\"categorical\"\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T20:47:06.089661Z","iopub.execute_input":"2026-03-12T20:47:06.089991Z","iopub.status.idle":"2026-03-12T20:47:06.381579Z","shell.execute_reply.started":"2026-03-12T20:47:06.089965Z","shell.execute_reply":"2026-03-12T20:47:06.381051Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base_model = InceptionV3(\n    weights=\"imagenet\",\n    include_top=False,\n    input_shape=(IMAGE_WIDTH, IMAGE_HEIGHT, 3)\n)\n\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\n\npredictions = Dense(NUM_CLASSES, activation=\"softmax\")(x)\n\nmodel = Model(inputs=base_model.input, outputs=predictions)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T20:47:20.363759Z","iopub.execute_input":"2026-03-12T20:47:20.364102Z","iopub.status.idle":"2026-03-12T20:47:27.578806Z","shell.execute_reply.started":"2026-03-12T20:47:20.364074Z","shell.execute_reply":"2026-03-12T20:47:27.577983Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"layer_to_freeze = 276\n\nfor layer in model.layers[:layer_to_freeze]:\n    layer.trainable = False\n\nfor layer in model.layers[layer_to_freeze:]:\n    layer.trainable = True","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T20:47:33.525487Z","iopub.execute_input":"2026-03-12T20:47:33.526137Z","iopub.status.idle":"2026-03-12T20:47:33.532818Z","shell.execute_reply.started":"2026-03-12T20:47:33.526108Z","shell.execute_reply":"2026-03-12T20:47:33.532203Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"optimizer = SGD(\n    learning_rate=0.01,\n    decay=1e-6,\n    momentum=0.9,\n    nesterov=True\n)\n\nmodel.compile(\n    optimizer=optimizer,\n    loss=\"binary_crossentropy\",\n    metrics=[\"accuracy\"]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T20:48:00.059705Z","iopub.execute_input":"2026-03-12T20:48:00.060036Z","iopub.status.idle":"2026-03-12T20:48:00.078158Z","shell.execute_reply.started":"2026-03-12T20:48:00.059979Z","shell.execute_reply":"2026-03-12T20:48:00.077277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"lr_callback = ReduceLROnPlateau(\n    monitor=\"val_accuracy\",\n    patience=3,\n    factor=0.5,\n    verbose=2,\n    min_lr=1e-5\n)\n\nearly_stop = EarlyStopping(\n    monitor=\"val_accuracy\",\n    patience=7,\n    restore_best_weights=True\n)\n\ncallbacks = [early_stop, lr_callback]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T20:48:13.616033Z","iopub.execute_input":"2026-03-12T20:48:13.616560Z","iopub.status.idle":"2026-03-12T20:48:13.620691Z","shell.execute_reply.started":"2026-03-12T20:48:13.616534Z","shell.execute_reply":"2026-03-12T20:48:13.619931Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 30\n\nhistory = model.fit(\n    train_generator,\n    epochs=EPOCHS,\n    validation_data=val_generator,\n    verbose=1,\n    callbacks=callbacks\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T20:48:23.081395Z","iopub.execute_input":"2026-03-12T20:48:23.081675Z","iopub.status.idle":"2026-03-12T21:00:36.145182Z","shell.execute_reply.started":"2026-03-12T20:48:23.081652Z","shell.execute_reply":"2026-03-12T21:00:36.144465Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"score = model.evaluate(val_generator, verbose=1)\n\nprint(\"Validation loss:\", score[0])\nprint(\"Validation accuracy:\", score[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T21:25:09.386989Z","iopub.execute_input":"2026-03-12T21:25:09.387327Z","iopub.status.idle":"2026-03-12T21:25:27.013881Z","shell.execute_reply.started":"2026-03-12T21:25:09.387300Z","shell.execute_reply":"2026-03-12T21:25:27.013173Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_datagen = ImageDataGenerator(rescale=1/255)\n\ntest_generator = test_datagen.flow_from_directory(\n    '/kaggle/working/test',\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    batch_size=1,\n    seed=42,\n    shuffle=False,\n    class_mode=None\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T21:30:44.184385Z","iopub.execute_input":"2026-03-12T21:30:44.184891Z","iopub.status.idle":"2026-03-12T21:30:44.277989Z","shell.execute_reply.started":"2026-03-12T21:30:44.184861Z","shell.execute_reply":"2026-03-12T21:30:44.277458Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dir = \"/kaggle/input/dogs-vs-cats/test1\"\n\ntest_dataset = TestDataset(test_dir, val_transform)\n\ntest_loader = DataLoader(test_dataset,\n                         batch_size=1,\n                         shuffle=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_pred = model.predict(test_generator, verbose=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T21:33:02.060266Z","iopub.execute_input":"2026-03-12T21:33:02.060868Z","iopub.status.idle":"2026-03-12T21:35:54.092611Z","shell.execute_reply.started":"2026-03-12T21:33:02.060835Z","shell.execute_reply":"2026-03-12T21:35:54.091974Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame({\n    \"id\": pd.Series(test_generator.filenames),\n    \"label\": pd.Series(y_pred.clip(0.02, 0.98)[:,1])\n})\n\nsubmission[\"id\"] = submission.id.str.extract(\"(\\d+)\")\nsubmission[\"id\"] = pd.to_numeric(submission[\"id\"])\n\nsubmission = submission.sort_values(\"id\")\n\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-12T21:37:55.084970Z","iopub.execute_input":"2026-03-12T21:37:55.085606Z","iopub.status.idle":"2026-03-12T21:37:55.170106Z","shell.execute_reply.started":"2026-03-12T21:37:55.085577Z","shell.execute_reply":"2026-03-12T21:37:55.169505Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"logloss: 0.021\n\nlb logloss: 0.05\n\nкак и ожидалось, отличное качество (сопоставимо в топами lb), акураси 99%. ","metadata":{}},{"cell_type":"markdown","source":"вывод: даже простые cnn дают разумные решения, аугментации критичны для обобщения => улучшения метрик в соревнованиях, но для топ результата скорее всего необходим transfer learning. ","metadata":{}}]}