{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"},{"sourceId":11116340,"sourceType":"datasetVersion","datasetId":6931283}],"dockerImageVersionId":31011,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision\nfrom torchvision.transforms import v2\nimport matplotlib.pyplot as plt\n\nfrom torch.utils.data import DataLoader, Dataset\nfrom sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report, confusion_matrix\nimport seaborn as sns\nfrom PIL import Image\nimport copy\nimport datetime\nimport random\nimport traceback\nfrom IPython.display import display, clear_output\nimport os\nimport shutil\nfrom torchvision.datasets import ImageFolder\n\nseed = 0\nrandom.seed(seed)\nnp.random.seed(seed)\n\ntorch.random.manual_seed(seed)\ntorch.cuda.manual_seed(seed)\ntorch.backends.cudnn.deterministic=False\ndevice = 'cuda:0' if torch.cuda.is_available() else 'cpu'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:16.485695Z","iopub.execute_input":"2025-04-14T08:06:16.485951Z","iopub.status.idle":"2025-04-14T08:06:24.428722Z","shell.execute_reply.started":"2025-04-14T08:06:16.485931Z","shell.execute_reply":"2025-04-14T08:06:24.428124Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nprint(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:24.429945Z","iopub.execute_input":"2025-04-14T08:06:24.430350Z","iopub.status.idle":"2025-04-14T08:06:24.434722Z","shell.execute_reply.started":"2025-04-14T08:06:24.430324Z","shell.execute_reply":"2025-04-14T08:06:24.433828Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = v2.Compose([\n    v2.ToImage(), \n    v2.ToDtype(torch.float32, scale=True),  \n    v2.RandomHorizontalFlip(),\n    v2.RandomVerticalFlip(),\n    v2.RandomCrop(32, padding=4),  \n    v2.Normalize(mean=[0.485, 0.456, 0.406],\n                 std=[0.229, 0.224, 0.225])  \n])\n\ntest_transform = v2.Compose([\n    v2.ToImage(), \n    v2.ToDtype(torch.float32, scale=True),    \n    v2.Normalize(mean=[0.485, 0.456, 0.406],\n                 std=[0.229, 0.224, 0.225])  \n])\n\ntrain_datasets = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)\ntest_datasets = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=test_transform)\ntrain_loader = torch.utils.data.DataLoader(train_datasets, batch_size=100, shuffle=True)\ntest_loader = torch.utils.data.DataLoader(test_datasets, batch_size=100, shuffle=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:24.435433Z","iopub.execute_input":"2025-04-14T08:06:24.435691Z","iopub.status.idle":"2025-04-14T08:06:45.015403Z","shell.execute_reply.started":"2025-04-14T08:06:24.435669Z","shell.execute_reply":"2025-04-14T08:06:45.014641Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"classes = train_datasets.classes\ntrain_datasets.classes","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:45.017251Z","iopub.execute_input":"2025-04-14T08:06:45.017456Z","iopub.status.idle":"2025-04-14T08:06:45.022913Z","shell.execute_reply.started":"2025-04-14T08:06:45.017440Z","shell.execute_reply":"2025-04-14T08:06:45.022084Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class fc(torch.nn.Module):\n    # Конструктор для класса\n    # Инициализация нейронной сети\n    def __init__(self):\n\n        super(fc, self).__init__()\n        self.conv1 = torch.nn.Conv2d(in_channels = 3, out_channels = 32, kernel_size = (3,3), padding=1)\n        self.mp1 = torch.nn.MaxPool2d(kernel_size = (2,2))\n        #32*16*16\n        self.relu = torch.nn.ReLU()\n        self.conv2 = torch.nn.Conv2d(in_channels = 32, out_channels = 64, kernel_size = (3,3), padding=1)\n        self.mp2 = torch.nn.MaxPool2d(kernel_size = (2,2))\n        #64*8*8\n        self.conv3 = torch.nn.Conv2d(in_channels = 64, out_channels = 128, kernel_size = (3,3), padding=1)\n        #128*8*8\n        self.fc1 = torch.nn.Linear(128*8*8, 512)\n        self.activ1 = torch.nn.ReLU()\n        self.fc2 = torch.nn.Linear(512, 256)\n        self.activ2 = torch.nn.ReLU()\n        self.fc3 = torch.nn.Linear(256, 64)\n        self.activ3 = torch.nn.ReLU()\n        self.fc4 = torch.nn.Linear(64, 32)\n        self.activ4 = torch.nn.ReLU()\n        self.fc5 = torch.nn.Linear(32, 16)\n        self.activ5 = torch.nn.ReLU()\n        self.fc6 = torch.nn.Linear(16, 10)\n        self.reg = torch.nn.Dropout(0.1)\n        self.sm = torch.nn.Softmax(dim=1)\n\n    def forward(self, data_input):\n        x = self.conv1(data_input)\n        x = self.relu(x)\n        x = self.mp1(x)\n        x = self.conv2(x)\n        x = self.relu(x)\n        x = self.mp2(x)\n        x = self.conv3(x)\n        x = self.relu(x)\n        x = x.reshape(-1, 128*8*8)\n        x = self.reg(x)\n        x = self.fc1(x)\n        x = self.activ1(x)\n        x = self.fc2(x)\n        x = self.activ2(x)\n        x = self.fc3(x)\n        x = self.activ3(x)\n        x = self.fc4(x)\n        x = self.activ4(x)\n        x = self.fc5(x)\n        x = self.activ5(x)\n        x = self.fc6(x)\n        return x\n\n    def inference(self, data_input):\n        with torch.no_grad():\n            x = self.forward(data_input)\n            x = self.sm(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:45.023889Z","iopub.execute_input":"2025-04-14T08:06:45.024133Z","iopub.status.idle":"2025-04-14T08:06:45.063671Z","shell.execute_reply.started":"2025-04-14T08:06:45.024112Z","shell.execute_reply":"2025-04-14T08:06:45.063062Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def copy_data_to_device(data, device):\n    if torch.is_tensor(data):\n        return data.to(device)\n    elif isinstance(data, (list, tuple)):\n        return [copy_data_to_device(elem, device) for elem in data]\n    raise ValueError('Недопустимый тип данных {}'.format(type(data)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:45.064266Z","iopub.execute_input":"2025-04-14T08:06:45.064467Z","iopub.status.idle":"2025-04-14T08:06:45.078543Z","shell.execute_reply.started":"2025-04-14T08:06:45.064452Z","shell.execute_reply":"2025-04-14T08:06:45.077822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_eval_loop(model, train_dataset, val_dataset, criterion,\n                    lr=1e-4, epoch_n=10, batch_size=32,\n                    device=None, early_stopping_patience=100, l2_reg_alpha=0,\n                    max_batches_per_epoch_train=10000,\n                    max_batches_per_epoch_val=1000,\n                    data_loader_ctor=torch.utils.data.DataLoader,\n                    optimizer_ctor=None,\n                    lr_scheduler_ctor=None,\n                    shuffle_train=True,\n                    dataloader_workers_n=0, \n                    plot=False):\n    if device is None:\n        device = 'cuda' if torch.cuda.is_available() else 'cpu'\n    model.to(device)\n\n    if optimizer_ctor is None:\n        optimizer = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=l2_reg_alpha)\n    else:\n        optimizer = optimizer_ctor(model.parameters(), lr=lr)\n\n    if lr_scheduler_ctor is not None:\n        lr_scheduler = lr_scheduler_ctor(optimizer)\n    else:\n        lr_scheduler = None\n\n    train_dataloader = data_loader_ctor(train_dataset, batch_size=batch_size, shuffle=shuffle_train,\n                                        num_workers=dataloader_workers_n)\n    val_dataloader = data_loader_ctor(val_dataset, batch_size=batch_size, shuffle=False,\n                                      num_workers=dataloader_workers_n)\n\n    best_val_loss = float('inf')\n    best_epoch_i = 0\n    best_model = copy.deepcopy(model)\n\n# Dynamic plot\n    if plot:\n        plot_epoch_data = []\n        plot_train_loss = []\n        plot_val_loss = []\n        plot_acc_train = []\n        plot_acc_val = []\n        \n        fig, ax = plt.subplots(2,1, figsize=(10,6))\n        line_train, = ax[0].plot([], [], 'r-')\n        line_val, = ax[0].plot([], [], 'b-')\n        line_acc_train, = ax[1].plot([], [], 'r-')\n        line_acc_val, = ax[1].plot([], [], 'b-')\n        ax[0].legend(['train', 'val'])\n        ax[0].set_xlim(0, epoch_n)\n        ax[1].legend(['train', 'val'])\n        ax[1].set_xlim(0, epoch_n)\n        ax[0].title.set_text('Функции потерь')\n        ax[1].title.set_text('Accuracy')\n\n        def add_point(epoch_i, train_loss, val_loss, acc_train, acc_val):\n            max_loss = max(ax[0].viewLim.y1 / 1.1, train_loss, val_loss)\n            ax[0].set_ylim(0, max_loss * 1.1)\n            ax[1].set_ylim(0, 110)\n            plot_epoch_data.append(epoch_i)\n            plot_train_loss.append(train_loss)\n            plot_val_loss.append(val_loss)\n            plot_acc_train.append(100 * acc_train)\n            plot_acc_val.append(100 * acc_val)\n            line_train.set_data(plot_epoch_data, plot_train_loss)\n            line_val.set_data(plot_epoch_data, plot_val_loss)\n            line_acc_train.set_data(plot_epoch_data, plot_acc_train)\n            line_acc_val.set_data(plot_epoch_data, plot_acc_val)\n            clear_output(wait=True)\n            display(fig)\n\n\n    for epoch_i in range(epoch_n):\n        try:\n            epoch_start = datetime.datetime.now()\n            \n\n            print('Эпоха {}'.format(epoch_i))\n\n            model.train()\n            mean_train_loss = 0\n            mean_train_acc = 0\n            train_batches_n = 0\n            for batch_i, (batch_x, batch_y) in enumerate(train_dataloader):\n                if batch_i > max_batches_per_epoch_train:\n                    break\n                batch_x = copy_data_to_device(batch_x, device)\n                batch_y = copy_data_to_device(batch_y, device)\n\n                pred = model(batch_x)\n                loss = criterion(pred, batch_y)\n                model.zero_grad()\n                loss.backward()\n\n                optimizer.step()\n                temp = []\n                for i in range(len(pred)):\n                    temp.append(torch.argmax(pred[i]).cpu().numpy())\n                acc = accuracy_score(temp, batch_y.cpu().numpy())\n                mean_train_loss += float(loss)\n                mean_train_acc += float(acc)\n                train_batches_n += 1\n                \n            \n            mean_train_loss /= train_batches_n\n            mean_train_acc /= train_batches_n\n\n            print('Эпоха: {} итераций, {:0.2f} сек'.format(train_batches_n,\n                                                           (datetime.datetime.now() - epoch_start).total_seconds()))\n            print('Среднее значение функции потерь на обучении', mean_train_loss)\n            print('Средняя accuracy на обучении', mean_train_acc)\n\n\n\n            model.eval()\n            mean_val_loss = 0\n            mean_val_acc = 0\n            val_batches_n = 0\n\n            with torch.no_grad():\n                for batch_i, (batch_x, batch_y) in enumerate(val_dataloader):\n                    if batch_i > max_batches_per_epoch_val:\n                        break\n\n                    batch_x = copy_data_to_device(batch_x, device)\n                    batch_y = copy_data_to_device(batch_y, device)\n\n                    pred = model(batch_x)\n                    loss = criterion(pred, batch_y)\n                    temp = []\n                    for i in range(len(pred)):\n                        temp.append(torch.argmax(pred[i]).cpu().numpy())\n                    acc = accuracy_score(temp, batch_y.cpu().numpy())\n                    mean_val_loss += float(loss)\n                    mean_val_acc += float(acc)\n                    val_batches_n += 1\n\n            mean_val_loss /= val_batches_n\n            mean_val_acc /= val_batches_n\n\n            if plot:\n                add_point(epoch_i, mean_train_loss, mean_val_loss, mean_train_acc, mean_val_acc)\n            else:\n                pass\n            \n            print('Среднее значение функции потерь на валидации', mean_val_loss)\n            print('Среднее значение accuracy на валидации', mean_val_acc)\n\n            if mean_val_loss < best_val_loss:\n                best_epoch_i = epoch_i\n                best_val_loss = mean_val_loss\n                best_model = copy.deepcopy(model)\n                print('Новая лучшая модель! На эпохе {}'.format(epoch_i))\n            elif epoch_i - best_epoch_i > early_stopping_patience:\n                print('Модель не улучшилась за последние {} эпох, прекращаем обучение'.format(\n                    early_stopping_patience))\n                break\n\n            if lr_scheduler is not None:\n                lr_scheduler.step(mean_val_loss)\n\n            print()\n        except KeyboardInterrupt:\n            print('Досрочно остановлено пользователем')\n            break\n        except Exception as ex:\n            print('Ошибка при обучении: {}\\n{}'.format(ex, traceback.format_exc()))\n            break\n        finally:\n            if plot:\n                plt.close(fig)\n\n    return best_val_loss, best_model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:45.079707Z","iopub.execute_input":"2025-04-14T08:06:45.079980Z","iopub.status.idle":"2025-04-14T08:06:45.098358Z","shell.execute_reply.started":"2025-04-14T08:06:45.079956Z","shell.execute_reply":"2025-04-14T08:06:45.097674Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = fc()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:45.098968Z","iopub.execute_input":"2025-04-14T08:06:45.099154Z","iopub.status.idle":"2025-04-14T08:06:45.165586Z","shell.execute_reply.started":"2025-04-14T08:06:45.099139Z","shell.execute_reply":"2025-04-14T08:06:45.165112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_loss, best_model = train_eval_loop(model=model, \n                train_dataset=train_datasets, \n                val_dataset=test_datasets, \n                criterion=torch.nn.CrossEntropyLoss(),\n                lr=1e-3, \n                epoch_n=20, \n                batch_size=16,\n                device=device, \n                l2_reg_alpha=0.5,\n                max_batches_per_epoch_train=10000,\n                max_batches_per_epoch_val=1000,\n                optimizer_ctor=torch.optim.Adam,\n                data_loader_ctor=torch.utils.data.DataLoader,\n                lr_scheduler_ctor=torch.optim.lr_scheduler.ReduceLROnPlateau,\n                shuffle_train=True,\n                dataloader_workers_n=2,\n                plot=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:06:45.166243Z","iopub.execute_input":"2025-04-14T08:06:45.166410Z","iopub.status.idle":"2025-04-14T08:16:00.867238Z","shell.execute_reply.started":"2025-04-14T08:06:45.166397Z","shell.execute_reply":"2025-04-14T08:16:00.866342Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predicted_labels = []\nactual_labels = []\n\nmodel.eval()\nwith torch.inference_mode():\n    for images, labels in test_loader:\n        images = images.to(device)\n        labels = labels.to(device)\n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n        predicted_labels.extend(predicted.cpu().numpy())\n        actual_labels.extend(labels.cpu().numpy())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:00.870099Z","iopub.execute_input":"2025-04-14T08:16:00.870344Z","iopub.status.idle":"2025-04-14T08:16:03.423447Z","shell.execute_reply.started":"2025-04-14T08:16:00.870321Z","shell.execute_reply":"2025-04-14T08:16:03.422906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"accuracy = accuracy_score(actual_labels, predicted_labels)\nprecision = precision_score(actual_labels, predicted_labels, average='weighted', zero_division=0)\nrecall = recall_score(actual_labels, predicted_labels, average='weighted', zero_division=0)\nf1 = f1_score(actual_labels, predicted_labels, average='weighted', zero_division=0)\n\n# Принт метрик\nprint(f\"Model Accuracy: {accuracy * 100:.2f}%\")\nprint(f\"Model Precision: {precision * 100:.2f}%\")\nprint(f\"Model Recall: {recall * 100:.2f}%\")\nprint(f\"Model F1 Score: {f1 * 100:.2f}%\")\n\ncm = confusion_matrix(actual_labels, predicted_labels)\nclass_names = test_loader.dataset.classes\n\nplt.figure(figsize=(10, 10))\nsns.heatmap(cm, annot=True, fmt='d', xticklabels=class_names, yticklabels=class_names, cmap='Blues', annot_kws={\"size\": 15})\nplt.title('Confusion Matrix')\nplt.xlabel('Predicted Labels')\nplt.ylabel('Actual Labels')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:03.424133Z","iopub.execute_input":"2025-04-14T08:16:03.424404Z","iopub.status.idle":"2025-04-14T08:16:04.081897Z","shell.execute_reply.started":"2025-04-14T08:16:03.424371Z","shell.execute_reply":"2025-04-14T08:16:04.081117Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.DataFrame({'preds':predicted_labels, 'actual':actual_labels})\ndf['err'] = abs(df.preds - df.actual)\ndf.query('err > 0')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:04.082713Z","iopub.execute_input":"2025-04-14T08:16:04.082955Z","iopub.status.idle":"2025-04-14T08:16:04.149727Z","shell.execute_reply.started":"2025-04-14T08:16:04.082937Z","shell.execute_reply":"2025-04-14T08:16:04.148944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"i = 0\nfor el in iter(test_loader):\n    for j in range(16):\n        if i + j in df.query('err > 0').index and i + j < 1000:\n            image = el[0][j]\n            if image.shape[0] == 3:\n                image = image[0, ...]\n            plt.imshow(image)\n            plt.title(f'Предсказали: {df.loc[i+j,\"preds\"]}; Настоящее: {df.loc[i+j,\"actual\"]}')\n    i+=16\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:04.150526Z","iopub.execute_input":"2025-04-14T08:16:04.150843Z","iopub.status.idle":"2025-04-14T08:16:20.566256Z","shell.execute_reply.started":"2025-04-14T08:16:04.150824Z","shell.execute_reply":"2025-04-14T08:16:20.565638Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = v2.Compose([\n    v2.ToImage(), \n    v2.ToDtype(torch.float32, scale=True),  \n    v2.RandomHorizontalFlip(),\n    v2.RandomVerticalFlip(),\n    v2.Resize((224,224)),  \n    v2.Normalize(mean=[0.485, 0.456, 0.406],\n                 std=[0.229, 0.224, 0.225])  \n])\n\ntest_transform = v2.Compose([\n    v2.ToImage(),\n    v2.ToDtype(torch.float32, scale=True),\n    v2.Resize((224,224)), \n    v2.Normalize(mean=[0.485, 0.456, 0.406],\n                 std=[0.229, 0.224, 0.225])  \n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:20.566983Z","iopub.execute_input":"2025-04-14T08:16:20.567241Z","iopub.status.idle":"2025-04-14T08:16:20.572408Z","shell.execute_reply.started":"2025-04-14T08:16:20.567217Z","shell.execute_reply":"2025-04-14T08:16:20.571858Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class cnn(torch.nn.Module):\n    # Конструктор для класса\n    # Инициализация нейронной сети\n    def __init__(self):\n\n        super(cnn, self).__init__()\n        self.conv1 = torch.nn.Conv2d(in_channels = 3, out_channels = 32, kernel_size = (3,3), padding=1)\n        self.mp1 = torch.nn.MaxPool2d(kernel_size = (2,2), stride=2)\n        #32*112*112\n        self.relu = torch.nn.ReLU()\n        self.conv2 = torch.nn.Conv2d(in_channels = 32, out_channels = 64, kernel_size = (3,3), padding=1)\n        self.mp2 = torch.nn.MaxPool2d(kernel_size = (2,2), stride=2)\n        #64*56*56\n        self.conv3 = torch.nn.Conv2d(in_channels = 64, out_channels = 128, kernel_size = (3,3), padding=1)\n        self.mp3 = torch.nn.MaxPool2d(kernel_size = (2,2), stride=2)\n        #128*28*28\n        self.conv4 = torch.nn.Conv2d(in_channels = 128, out_channels = 140, kernel_size = (3,3), padding=1)\n        self.mp4 = torch.nn.MaxPool2d(kernel_size = (2,2), stride=2)\n        #140*14*14\n        self.conv5 = torch.nn.Conv2d(in_channels = 140, out_channels = 180, kernel_size = (3,3), padding=1)\n        self.mp5 = torch.nn.MaxPool2d(kernel_size = (2,2), stride=2)\n        #180*7*7\n        self.fc1 = torch.nn.Linear(180*7*7, 512)\n        self.activ1 = torch.nn.ReLU()\n        self.fc2 = torch.nn.Linear(512, 256)\n        self.activ2 = torch.nn.ReLU()\n        self.fc3 = torch.nn.Linear(256, 200)\n        self.activ3 = torch.nn.ReLU()\n        self.fc4 = torch.nn.Linear(200, 150)\n        self.activ4 = torch.nn.ReLU()\n        self.fc5 = torch.nn.Linear(150, 100)\n        self.activ5 = torch.nn.ReLU()\n        self.fc6 = torch.nn.Linear(100, 90)\n        self.reg = torch.nn.Dropout(0.1)\n        self.sm = torch.nn.Softmax(dim=1)\n\n    def forward(self, data_input):\n        x = self.conv1(data_input)\n        x = self.relu(x)\n        x = self.mp1(x)\n        x = self.conv2(x)\n        x = self.relu(x)\n        x = self.mp2(x)\n        x = self.conv3(x)\n        x = self.relu(x)\n        x = self.mp3(x)\n        x = self.conv4(x)\n        x = self.relu(x)\n        x = self.mp4(x)\n        x = self.conv5(x)\n        x = self.relu(x)\n        x = self.mp5(x)\n        x = x.reshape(-1, 180*7*7)\n        x = self.reg(x)\n        x = self.fc1(x)\n        x = self.activ1(x)\n        x = self.fc2(x)\n        x = self.activ2(x)\n        x = self.fc3(x)\n        x = self.activ3(x)\n        x = self.fc4(x)\n        x = self.activ4(x)\n        x = self.fc5(x)\n        x = self.activ5(x)\n        x = self.fc6(x)\n        return x\n\n    def inference(self, data_input):\n        with torch.no_grad():\n            x = self.forward(data_input)\n            x = self.sm(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:20.573199Z","iopub.execute_input":"2025-04-14T08:16:20.573475Z","iopub.status.idle":"2025-04-14T08:16:20.594045Z","shell.execute_reply.started":"2025-04-14T08:16:20.573452Z","shell.execute_reply":"2025-04-14T08:16:20.593396Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MyDataset(torch.utils.data.Dataset):\n\n    def __init__(self, root_dir, transform=None):\n        self.root_dir = root_dir\n        self.transform = transform\n        self.classes = sorted(os.listdir(root_dir))\n        self.class_to_idx = {cls_name: i for i, cls_name in enumerate(self.classes)}\n        self.images = []\n        \n        for cls_name in self.classes:\n            cls_dir = os.path.join(root_dir, cls_name)\n            for img_name in os.listdir(cls_dir):\n                if img_name.lower().endswith(('.png', '.jpg', '.jpeg')):\n                    self.images.append((os.path.join(cls_dir, img_name), self.class_to_idx[cls_name]))\n    \n    def __len__(self):\n        return len(self.images)\n    \n    def __getitem__(self, idx):\n        img_path, label = self.images[idx]\n        image = Image.open(img_path).convert('RGB')\n        \n        if self.transform:\n            image = self.transform(image)\n            \n        return image, label\n    \n    def get_class_names(self):\n        return self.classes","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:20.594661Z","iopub.execute_input":"2025-04-14T08:16:20.594861Z","iopub.status.idle":"2025-04-14T08:16:20.608539Z","shell.execute_reply.started":"2025-04-14T08:16:20.594846Z","shell.execute_reply":"2025-04-14T08:16:20.607793Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"root_dir = '/kaggle/input/animal-categories-90-masters-of-survival/animals/animals/animals'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:20.609373Z","iopub.execute_input":"2025-04-14T08:16:20.609609Z","iopub.status.idle":"2025-04-14T08:16:20.622005Z","shell.execute_reply.started":"2025-04-14T08:16:20.609588Z","shell.execute_reply":"2025-04-14T08:16:20.621299Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"full_dataset = MyDataset(root_dir=root_dir, transform=train_transform)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:20.622752Z","iopub.execute_input":"2025-04-14T08:16:20.623001Z","iopub.status.idle":"2025-04-14T08:16:21.464388Z","shell.execute_reply.started":"2025-04-14T08:16:20.622981Z","shell.execute_reply":"2025-04-14T08:16:21.463780Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_size = int(0.8 * len(full_dataset))\nval_size = len(full_dataset) - train_size\ntrain_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [train_size, val_size])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:21.465223Z","iopub.execute_input":"2025-04-14T08:16:21.465409Z","iopub.status.idle":"2025-04-14T08:16:21.469720Z","shell.execute_reply.started":"2025-04-14T08:16:21.465385Z","shell.execute_reply":"2025-04-14T08:16:21.469203Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_dataset.dataset.transform = test_transform","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:21.470411Z","iopub.execute_input":"2025-04-14T08:16:21.470592Z","iopub.status.idle":"2025-04-14T08:16:21.482466Z","shell.execute_reply.started":"2025-04-14T08:16:21.470577Z","shell.execute_reply":"2025-04-14T08:16:21.481695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"batch_size = 64\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4)\nval_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:21.483156Z","iopub.execute_input":"2025-04-14T08:16:21.483431Z","iopub.status.idle":"2025-04-14T08:16:21.494426Z","shell.execute_reply.started":"2025-04-14T08:16:21.483415Z","shell.execute_reply":"2025-04-14T08:16:21.493695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_names = full_dataset.get_class_names()\nnum_classes = len(class_names)\nprint(f\"Number of classes: {num_classes}\")\nprint(f\"Class names: {class_names}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:21.495130Z","iopub.execute_input":"2025-04-14T08:16:21.495343Z","iopub.status.idle":"2025-04-14T08:16:21.507227Z","shell.execute_reply.started":"2025-04-14T08:16:21.495328Z","shell.execute_reply":"2025-04-14T08:16:21.506492Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = cnn()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:21.508001Z","iopub.execute_input":"2025-04-14T08:16:21.508241Z","iopub.status.idle":"2025-04-14T08:16:21.564253Z","shell.execute_reply.started":"2025-04-14T08:16:21.508218Z","shell.execute_reply":"2025-04-14T08:16:21.563477Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_loss, best_model = train_eval_loop(model=model, \n                train_dataset=train_dataset, \n                val_dataset=val_dataset, \n                criterion=torch.nn.CrossEntropyLoss(),\n                lr=1e-3, \n                epoch_n=20, \n                batch_size=batch_size,\n                device=device, \n                l2_reg_alpha=0.5,\n                max_batches_per_epoch_train=10000,\n                max_batches_per_epoch_val=1000,\n                optimizer_ctor=torch.optim.Adam,\n                data_loader_ctor=torch.utils.data.DataLoader,\n                lr_scheduler_ctor=torch.optim.lr_scheduler.ReduceLROnPlateau,\n                shuffle_train=True,\n                dataloader_workers_n=2,\n                plot=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T08:16:21.565140Z","iopub.execute_input":"2025-04-14T08:16:21.565404Z","iopub.status.idle":"2025-04-14T08:39:17.372045Z","shell.execute_reply.started":"2025-04-14T08:16:21.565383Z","shell.execute_reply":"2025-04-14T08:39:17.371297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predicted_labels = []\nactual_labels = []\n\nmodel.eval()\nwith torch.inference_mode():\n    for images, labels in val_loader:\n        images = images.to(device)\n        labels = labels.to(device)\n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n        predicted_labels.extend(predicted.cpu().numpy())\n        actual_labels.extend(labels.cpu().numpy())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T11:29:15.053546Z","iopub.execute_input":"2025-04-16T11:29:15.053835Z","iopub.status.idle":"2025-04-16T11:29:25.791522Z","shell.execute_reply.started":"2025-04-16T11:29:15.053804Z","shell.execute_reply":"2025-04-16T11:29:25.790494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"accuracy = accuracy_score(actual_labels, predicted_labels)\nprecision = precision_score(actual_labels, predicted_labels, average='weighted', zero_division=0)\nrecall = recall_score(actual_labels, predicted_labels, average='weighted', zero_division=0)\nf1 = f1_score(actual_labels, predicted_labels, average='weighted', zero_division=0)\n\n# Принт метрик\nprint(f\"Model Accuracy: {accuracy * 100:.2f}%\")\nprint(f\"Model Precision: {precision * 100:.2f}%\")\nprint(f\"Model Recall: {recall * 100:.2f}%\")\nprint(f\"Model F1 Score: {f1 * 100:.2f}%\")\n\ncm = confusion_matrix(actual_labels, predicted_labels)\nclass_names = val_loader.dataset.classes\n\nplt.figure(figsize=(10, 10))\nsns.heatmap(cm, annot=True, fmt='d', xticklabels=class_names, yticklabels=class_names, cmap='Blues', annot_kws={\"size\": 15})\nplt.title('Confusion Matrix')\nplt.xlabel('Predicted Labels')\nplt.ylabel('Actual Labels')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T11:29:39.130753Z","iopub.execute_input":"2025-04-16T11:29:39.131399Z","iopub.status.idle":"2025-04-16T11:29:39.174403Z","shell.execute_reply.started":"2025-04-16T11:29:39.131358Z","shell.execute_reply":"2025-04-16T11:29:39.173441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.DataFrame({'preds':predicted_labels, 'actual':actual_labels})\ndf['err'] = abs(df.preds - df.actual)\ndf.query('err > 0')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T11:30:24.557314Z","iopub.execute_input":"2025-04-16T11:30:24.557892Z","iopub.status.idle":"2025-04-16T11:30:24.577087Z","shell.execute_reply.started":"2025-04-16T11:30:24.557870Z","shell.execute_reply":"2025-04-16T11:30:24.576366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"i = 0\nfor el in iter(val_loader):\n    for j in range(16):\n        if i + j in df.query('err > 0').index and i + j < 10:\n            image = el[0][j]\n            if image.shape[0] == 3:\n                image = image[0, ...]\n            plt.imshow(image)\n            plt.title(f'Предсказали: {df.loc[i+j,\"preds\"]}; Настоящее: {df.loc[i+j,\"actual\"]}')\n    i+=16\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-16T11:30:35.374671Z","iopub.execute_input":"2025-04-16T11:30:35.375306Z","iopub.status.idle":"2025-04-16T11:30:45.781433Z","shell.execute_reply.started":"2025-04-16T11:30:35.375285Z","shell.execute_reply":"2025-04-16T11:30:45.780734Z"}},"outputs":[],"execution_count":null}]}