{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:16:04.420687Z","iopub.execute_input":"2025-11-27T11:16:04.421181Z","iopub.status.idle":"2025-11-27T11:16:04.424921Z","shell.execute_reply.started":"2025-11-27T11:16:04.421157Z","shell.execute_reply":"2025-11-27T11:16:04.424018Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torch.nn import functional as F\nfrom matplotlib import pyplot as plt\nfrom PIL import Image\nfrom torchvision import transforms as tsf\nimport csv\n%pylab inline","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:16:05.161087Z","iopub.execute_input":"2025-11-27T11:16:05.161343Z","iopub.status.idle":"2025-11-27T11:16:11.058964Z","shell.execute_reply.started":"2025-11-27T11:16:05.161324Z","shell.execute_reply":"2025-11-27T11:16:11.05825Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import math\n\nclass Flatten(nn.Module):\n    def forward(self, x):\n        return x.view(x.size(0), -1)\nclass ChannelGate(nn.Module):\n    def __init__(self, gate_channel, reduction_ratio=16, num_layers=1):\n        super(ChannelGate, self).__init__()\n        #self.gate_activation = gate_activation\n        self.gate_c = nn.Sequential()\n        self.gate_c.add_module( 'flatten', Flatten() )\n        gate_channels = [gate_channel]\n        gate_channels += [gate_channel // reduction_ratio] * num_layers\n        gate_channels += [gate_channel]\n        for i in range( len(gate_channels) - 2 ):\n            self.gate_c.add_module( 'gate_c_fc_%d'%i, nn.Linear(gate_channels[i], gate_channels[i+1]) )\n            self.gate_c.add_module( 'gate_c_bn_%d'%(i+1), nn.BatchNorm1d(gate_channels[i+1]) )\n            self.gate_c.add_module( 'gate_c_relu_%d'%(i+1), nn.ReLU() )\n        self.gate_c.add_module( 'gate_c_fc_final', nn.Linear(gate_channels[-2], gate_channels[-1]) )\n    def forward(self, in_tensor):\n        avg_pool = F.avg_pool2d( in_tensor, in_tensor.size(2), stride=in_tensor.size(2) )\n        return self.gate_c( avg_pool ).unsqueeze(2).unsqueeze(3).expand_as(in_tensor)\n\nclass SpatialGate(nn.Module):\n    def __init__(self, gate_channel, reduction_ratio=16, dilation_conv_num=2, dilation_val=4):\n        super(SpatialGate, self).__init__()\n        self.gate_s = nn.Sequential()\n        self.gate_s.add_module( 'gate_s_conv_reduce0', nn.Conv2d(gate_channel, gate_channel//reduction_ratio, kernel_size=1))\n        self.gate_s.add_module( 'gate_s_bn_reduce0',\tnn.BatchNorm2d(gate_channel//reduction_ratio) )\n        self.gate_s.add_module( 'gate_s_relu_reduce0',nn.ReLU() )\n        for i in range( dilation_conv_num ):\n            self.gate_s.add_module( 'gate_s_conv_di_%d'%i, nn.Conv2d(gate_channel//reduction_ratio, gate_channel//reduction_ratio, kernel_size=3, \\\n\t\t\t\t\t\tpadding=dilation_val, dilation=dilation_val) )\n            self.gate_s.add_module( 'gate_s_bn_di_%d'%i, nn.BatchNorm2d(gate_channel//reduction_ratio) )\n            self.gate_s.add_module( 'gate_s_relu_di_%d'%i, nn.ReLU() )\n        self.gate_s.add_module( 'gate_s_conv_final', nn.Conv2d(gate_channel//reduction_ratio, 1, kernel_size=1) )\n    def forward(self, in_tensor):\n        return self.gate_s( in_tensor ).expand_as(in_tensor)\nclass BAM(nn.Module):\n    def __init__(self, gate_channel):\n        super(BAM, self).__init__()\n        self.channel_att = ChannelGate(gate_channel)\n        self.spatial_att = SpatialGate(gate_channel)\n    def forward(self,in_tensor):\n        att = 1 + F.sigmoid( self.channel_att(in_tensor) * self.spatial_att(in_tensor) )\n        return att * in_tensor\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class BasicConv(nn.Module):\n    def __init__(self, in_planes, out_planes, kernel_size, stride=1, padding=0, dilation=1, groups=1, relu=True, bn=True, bias=False):\n        super(BasicConv, self).__init__()\n        self.out_channels = out_planes\n        self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups, bias=bias)\n        self.bn = nn.BatchNorm2d(out_planes,eps=1e-5, momentum=0.01, affine=True) if bn else None\n        self.relu = nn.ReLU() if relu else None\n\n    def forward(self, x):\n        x = self.conv(x)\n        if self.bn is not None:\n            x = self.bn(x)\n        if self.relu is not None:\n            x = self.relu(x)\n        return x\n\nclass CFlatten(nn.Module):\n    def forward(self, x):\n        return x.view(x.size(0), -1)\n\nclass CChannelGate(nn.Module):\n    def __init__(self, gate_channels, reduction_ratio=16, pool_types=['avg', 'max']):\n        super(CChannelGate, self).__init__()\n        self.gate_channels = gate_channels\n        self.mlp = nn.Sequential(\n            Flatten(),\n            nn.Linear(gate_channels, gate_channels // reduction_ratio),\n            nn.ReLU(),\n            nn.Linear(gate_channels // reduction_ratio, gate_channels)\n            )\n        self.pool_types = pool_types\n    def forward(self, x):\n        channel_att_sum = None\n        for pool_type in self.pool_types:\n            if pool_type=='avg':\n                avg_pool = F.avg_pool2d( x, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))\n                channel_att_raw = self.mlp( avg_pool )\n            elif pool_type=='max':\n                max_pool = F.max_pool2d( x, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))\n                channel_att_raw = self.mlp( max_pool )\n            elif pool_type=='lp':\n                lp_pool = F.lp_pool2d( x, 2, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))\n                channel_att_raw = self.mlp( lp_pool )\n            elif pool_type=='lse':\n                # LSE pool only\n                lse_pool = logsumexp_2d(x)\n                channel_att_raw = self.mlp( lse_pool )\n\n            if channel_att_sum is None:\n                channel_att_sum = channel_att_raw\n            else:\n                channel_att_sum = channel_att_sum + channel_att_raw\n\n        scale = F.sigmoid( channel_att_sum ).unsqueeze(2).unsqueeze(3).expand_as(x)\n        return x * scale\n\ndef logsumexp_2d(tensor):\n    tensor_flatten = tensor.view(tensor.size(0), tensor.size(1), -1)\n    s, _ = torch.max(tensor_flatten, dim=2, keepdim=True)\n    outputs = s + (tensor_flatten - s).exp().sum(dim=2, keepdim=True).log()\n    return outputs\n\nclass ChannelPool(nn.Module):\n    def forward(self, x):\n        return torch.cat( (torch.max(x,1)[0].unsqueeze(1), torch.mean(x,1).unsqueeze(1)), dim=1 )\n\nclass CSpatialGate(nn.Module):\n    def __init__(self):\n        super(CSpatialGate, self).__init__()\n        kernel_size = 7\n        self.compress = ChannelPool()\n        self.spatial = BasicConv(2, 1, kernel_size, stride=1, padding=(kernel_size-1) // 2, relu=False)\n    def forward(self, x):\n        x_compress = self.compress(x)\n        x_out = self.spatial(x_compress)\n        scale = F.sigmoid(x_out) # broadcasting\n        return x * scale\n\nclass CBAM(nn.Module):\n    def __init__(self, gate_channels, reduction_ratio=16, pool_types=['avg', 'max'], no_spatial=False):\n        super(CBAM, self).__init__()\n        self.CChannelGate = CChannelGate(gate_channels, reduction_ratio, pool_types)\n        self.no_spatial=no_spatial\n        if not no_spatial:\n            self.CSpatialGate = CSpatialGate()\n    def forward(self, x):\n        x_out = self.CChannelGate(x)\n        if not self.no_spatial:\n            x_out = self.CSpatialGate(x_out)\n        return x_out","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport csv\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\ndef csv_reader(path):\n    \"\"\"Reads the CSV file and returns its content as a list.\"\"\"\n    with open(path, \"r\") as f:\n        reader = csv.reader(f)\n        data = list(reader)\n    return data\n\ndef get_datas(image_dir, csv_path, suffix=\".png\"):\n    \"\"\"\n    Reads image file paths and labels from the CSV.\n    Args:\n        image_dir: directory containing training images\n        csv_path: path to train.csv\n    Returns:\n        image_paths, labels\n    \"\"\"\n    train = csv_reader(csv_path)\n    image_paths = []\n    labels = []\n\n    for row in train[1:]:  # skip header\n        image_name = f\"{row[0]}{suffix}\"\n        label = int(row[1])\n        image_paths.append(os.path.join(image_dir, image_name))\n        labels.append(label)\n\n    return image_paths, labels\n\ndef show_batch(img_paths, num_images=25):\n    \"\"\"\n    Randomly displays a grid of images from the given paths.\n    \"\"\"\n    if len(img_paths) == 0:\n        print(\"No images found.\")\n        return\n\n    num_images = min(num_images, len(img_paths))\n    selected_indices = np.random.choice(len(img_paths), num_images, replace=False)\n\n    plt.figure(figsize=(10, 10))\n    for idx, i in enumerate(selected_indices):\n        img = Image.open(img_paths[i])\n        plt.subplot(5, 5, idx + 1)\n        plt.imshow(img)\n        plt.axis('off')\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:16:22.811209Z","iopub.execute_input":"2025-11-27T11:16:22.811732Z","iopub.status.idle":"2025-11-27T11:16:22.818914Z","shell.execute_reply.started":"2025-11-27T11:16:22.81171Z","shell.execute_reply":"2025-11-27T11:16:22.818277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_dir = \"/kaggle/input/aptos2019-blindness-detection/train_images\"\ncsv_path = \"/kaggle/input/aptos2019-blindness-detection/train.csv\"\n\nimage_paths, labels = get_datas(image_dir, csv_path)\nprint(\"Total images:\", len(image_paths))\nprint(\"First 5 samples:\", list(zip(image_paths[:5], labels[:5])))\n\nshow_batch(image_paths)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\nimport os\nimport numpy as np\ndef csv_reader(path):\n    with open(path, \"r\") as f:\n        breast = list(csv.reader(f))\n    return breast\ndef get_datas(image_dir,suffix=\".png\"):\n    image_paths = []\n    labels = []\n    train=csv_reader(\"/kaggle/input/aptos2019-blindness-detection/train.csv\")\n    for i in train[1:]:\n        #y_this=[0.0,0.0,0.0,0.0,0.0]\n        #y_this[int(i[1])]=1.0\n        image_paths.append(image_dir+'/'+i[0]+'.png')\n        labels.append(int(i[1]))\n\n    return image_paths,labels\n    \ndef show_batch(img_paths):\n    '''\n    img_paths:list, 所有图像的路径\n    '''\n    randomed = []\n    if len(img_paths) <= 25:\n        randomed = list(range(0, len(img_paths)))\n    else:\n        for i in range(0, len(img_paths)):\n            random = np.random.randint(0, len(img_paths))\n            if random not in randomed:\n                randomed.append(random)\n            if len(randomed) == 25:\n                break\n    plt.figure(dpi=224)\n    for i in range(len(randomed)):\n        img = Image.open(img_paths[randomed[i]])\n        plt.subplot(5, 5, i+1)\n        plt.imshow(img)\n        plt.xticks([])\n        plt.yticks([])\n    plt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"img_dir = os.path.abspath('/kaggle/input/aptos2019-blindness-detection/train_images')\nshuffix = \".png\"\nimg_paths, labels = get_datas(img_dir, suffix=shuffix)\nshow_batch(img_paths)  ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn as nn\nimport math\nimport torch.utils.model_zoo as model_zoo\n\n\n__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101',\n           'resnet152']\n\n\nmodel_urls = {\n    'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',\n    'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',\n    'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',\n    'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',\n    'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',\n}\n\n\ndef conv3x3(in_planes, out_planes, stride=1):\n    \"3x3 convolution with padding\"\n    return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,\n                     padding=1, bias=False)\n\n\nclass BasicBlock(nn.Module):\n    expansion = 1\n\n    def __init__(self, inplanes, planes, stride=1, downsample=None, use_cbam=False):\n        super(BasicBlock, self).__init__()\n        self.conv1 = conv3x3(inplanes, planes, stride)\n        self.bn1 = nn.BatchNorm2d(planes)\n        self.relu = nn.ReLU(inplace=True)\n        self.conv2 = conv3x3(planes, planes)\n        self.bn2 = nn.BatchNorm2d(planes)\n        self.downsample = downsample\n        self.stride = stride\n        if use_cbam:\n            self.cbam = CBAM( planes, 16 )\n        else:\n            self.cbam = None\n    def forward(self, x):\n        residual = x\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        out = self.bn2(out)\n\n        if self.downsample is not None:\n            residual = self.downsample(x)\n        if not self.cbam is None:\n            out = self.cbam(out)\n        out += residual\n        out = self.relu(out)\n\n        return out\n\n\nclass Bottleneck(nn.Module):\n    expansion = 4\n\n    def __init__(self, inplanes, planes, stride=1, downsample=None, use_cbam=False):\n        super(Bottleneck, self).__init__()\n        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(planes)\n        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,\n                               padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(planes)\n        self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)\n        self.bn3 = nn.BatchNorm2d(planes * 4)\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n        self.stride = stride\n        if use_cbam:\n            self.cbam = CBAM( planes * 4, 16 )\n        else:\n            self.cbam = None\n\n    def forward(self, x):\n        residual = x\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        out = self.bn2(out)\n        out = self.relu(out)\n\n        out = self.conv3(out)\n        out = self.bn3(out)\n\n        if self.downsample is not None:\n            residual = self.downsample(x)\n        if not self.cbam is None:\n            out = self.cbam(out)\n        out += residual\n        out = self.relu(out)\n\n        return out\n\n\nclass ResNet(nn.Module):\n\n    def __init__(self, block, layers, num_classes=1000, att_type=None):\n        self.inplanes = 64\n        super(ResNet, self).__init__()\n        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,\n                               bias=False)\n        self.bn1 = nn.BatchNorm2d(64)\n        self.relu = nn.ReLU(inplace=True)\n        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n        if att_type=='BAM':\n            self.bam1 = BAM(64*block.expansion)\n            self.bam2 = BAM(128*block.expansion)\n            self.bam3 = BAM(256*block.expansion)\n        else:\n            self.bam1, self.bam2, self.bam3 = None, None, None\n        self.layer1 = self._make_layer(block, 64, layers[0], att_type=att_type)\n        self.layer2 = self._make_layer(block, 128, layers[1], stride=2, att_type=att_type)\n        self.layer3 = self._make_layer(block, 256, layers[2], stride=2, att_type=att_type)\n        self.layer4 = self._make_layer(block, 512, layers[3], stride=2, att_type=att_type)\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n        self.fc = nn.Linear(512 * block.expansion, 1000)\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels\n                m.weight.data.normal_(0, math.sqrt(2. / n))\n            elif isinstance(m, nn.BatchNorm2d):\n                m.weight.data.fill_(1)\n                m.bias.data.zero_()\n\n    def _make_layer(self, block, planes, blocks, stride=1,att_type=None):\n        downsample = None\n        if stride != 1 or self.inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                nn.Conv2d(self.inplanes, planes * block.expansion,\n                          kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(planes * block.expansion),\n            )\n\n        layers = []\n        layers.append(block(self.inplanes, planes, stride, downsample, use_cbam=att_type=='CBAM'))\n        self.inplanes = planes * block.expansion\n        for i in range(1, blocks):\n            layers.append(block(self.inplanes, planes, use_cbam=att_type=='CBAM'))\n\n        return nn.Sequential(*layers)\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.maxpool(x)\n\n        x = self.layer1(x)\n        if not self.bam1 is None:\n            x = self.bam1(x)\n        x = self.layer2(x)\n        if not self.bam2 is None:\n            x = self.bam2(x)\n        x = self.layer3(x)\n        if not self.bam3 is None:\n            x = self.bam3(x)\n        feat = self.layer4(x)\n\n        x = self.avgpool(feat)\n        x = x.view(x.size(0), -1)\n        x = self.fc(x)\n\n        return feat, x\n\n\ndef resnet18(pretrained=False, att_type=None):\n    \"\"\"Constructs a ResNet-18 model.\n    Args:\n        pretrained (bool): If True, returns a model pre-trained on ImageNet\n    \"\"\"\n    model = ResNet(BasicBlock, [2, 2, 2, 2], att_type=att_type)\n    if pretrained:\n        save_model = model_zoo.load_url(model_urls['resnet18'])\n        model_dict =  model.state_dict()\n        state_dict = {k:v for k,v in save_model.items() if k in model_dict.keys()}\n        model_dict.update(state_dict)\n        model.load_state_dict(model_dict)\n    return model\n\n\ndef resnet34(pretrained=False, att_type=None):\n    \"\"\"Constructs a ResNet-34 model.\n    Args:\n        pretrained (bool): If True, returns a model pre-trained on ImageNet\n    \"\"\"\n    model = ResNet(BasicBlock, [3, 4, 6, 3], att_type=att_type)\n    if pretrained:\n        model.load_state_dict(model_zoo.load_url(model_urls['resnet34']))\n    return model\n\n\ndef resnet50(pretrained=False, **kwargs):\n    \"\"\"Constructs a ResNet-50 model.\n    Args:\n        pretrained (bool): If True, returns a model pre-trained on ImageNet\n    \"\"\"\n    model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)\n    if pretrained:\n        model.load_state_dict(model_zoo.load_url(model_urls['resnet50']))\n    return model\n\n\ndef resnet101(pretrained=False, att_type=None):\n    \"\"\"Constructs a ResNet-101 model.\n    Args:\n        pretrained (bool): If True, returns a model pre-trained on ImageNet\n    \"\"\"\n    model = ResNet(Bottleneck, [3, 4, 23, 3], att_type=att_type)\n    if pretrained:\n        save_model = model_zoo.load_url(model_urls['resnet101'])\n        model_dict =  model.state_dict()\n        state_dict = {k:v for k,v in save_model.items() if k in model_dict.keys()}\n        model_dict.update(state_dict)\n        model.load_state_dict(model_dict)\n    return model\n\n\ndef resnet152(pretrained=False, att_type=None):\n    \"\"\"Constructs a ResNet-152 model.\n    Args:\n        pretrained (bool): If True, returns a model pre-trained on ImageNet\n    \"\"\"\n    model = ResNet(Bottleneck, [3, 8, 36, 3], att_type=att_type)\n    if pretrained:\n        save_model = model_zoo.load_url(model_urls['resnet152'])\n        model_dict =  model.state_dict()\n        state_dict = {k:v for k,v in save_model.items() if k in model_dict.keys()}\n        model_dict.update(state_dict)\n        model.load_state_dict(model_dict)\n    return model","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset\n\nclass EyeDataset(Dataset):\n    def __init__(self, img_paths, labels, gray=False, transform=None):\n        super(EyeDataset, self).__init__()\n        self.img_paths = img_paths\n        self.labels = labels\n        self.gray = gray\n        self.transform = transform\n        self.length = len(img_paths)\n\n    def __len__(self):\n        return self.length\n\n    def __getitem__(self, index):\n        img_path = self.img_paths[index]\n        label = self.labels[index]\n        img = Image.open(img_path)\n        if self.gray:\n            img.convert(\"L\")\n        else:\n            img.convert(\"RGB\")\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        return img, label","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data.sampler import WeightedRandomSampler\nfrom sklearn.model_selection import train_test_split\ntrain_paths, val_paths, train_labels, val_labels = train_test_split(img_paths, labels, test_size=0.3, random_state=0, stratify=labels)\nimg_size = 224\ntrain_transform = tsf.Compose([\n    tsf.Resize((img_size, img_size)),\n    tsf.RandomHorizontalFlip(),\n    tsf.RandomVerticalFlip(),\n    tsf.ToTensor(),\n    tsf.Normalize(mean=[0.5, 0.5, 0.5], std=[0.25, 0.25, 0.25])\n])\nval_transform = tsf.Compose([\n    tsf.Resize((img_size, img_size)),\n    tsf.ToTensor(),\n    tsf.Normalize(mean=[0.5, 0.5, 0.5], std=[0.25, 0.25, 0.25])\n])\ntrain_dataset =EyeDataset(train_paths, torch.tensor(train_labels), transform=train_transform)\nval_dataset = EyeDataset(val_paths, torch.tensor(val_labels), transform=val_transform)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import DataLoader\nwights=[]\ntrain_time=[1,5,2,9,6]\nfor i in train_labels:\n    wights.append(train_time[int(i)])\nsampler = WeightedRandomSampler(wights, len(wights),replacement=True)\ntrain_loader = DataLoader(train_dataset, shuffle=False, batch_size=8, num_workers=4, sampler=sampler)\nval_loader = DataLoader(val_dataset, shuffle=False, batch_size=1, num_workers=4)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_for(x,label,opt,model,losses,total,number,count):\n    for _ in range(number):\n        x = x.to(device, dtype=torch.float32)\n        label = label.to(device, dtype=torch.long)\n        batch = x.size(0)\n        total += batch\n        opt.zero_grad() \n        _,pred = model(x)\n        loss = criterion(pred, label)\n        loss.backward()\n        pred = torch.max(pred, dim=1)[1]\n        for i in range(len(pred)):\n            if int(pred[i])==int(label[i]):\n                count+=1\n        if total%128==0:\n            print(loss)\n            print(total)\n            print(\"acc: \"+str(count/128))\n            count=0\n        opt.step() \n        losses += loss.item() * batch\n    return opt,model,losses,total,count","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix, ConfusionMatrixDisplay, classification_report\nimport time\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom prettytable import PrettyTable\n\n# ----- MODEL SETUP -----\n# from your_model_package import resnet101  # Make sure this import is correct\n\nmodel = resnet101(pretrained=True, att_type='BAM')  # Ensure correct import\nmodel.fc = nn.Linear(2048, 5)\n\ncriterion = nn.CrossEntropyLoss()\nopt = optim.SGD(model.parameters(), lr=0.004, momentum=0.9, nesterov=True)\nscheduler = optim.lr_scheduler.StepLR(opt, step_size=6, gamma=0.1, last_epoch=-1)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n\n# ----- TRAIN FUNCTION -----\ndef train_one_epoch(epoch, model, loader, optimizer, criterion, device):\n    model.train()\n    running_loss = 0.0\n    all_preds, all_labels = [], []\n    start_time = time.time()\n\n    for i, (x, labels) in enumerate(loader):\n        x, labels = x.to(device), labels.to(device)\n        optimizer.zero_grad()\n\n        outputs = model(x)\n        if isinstance(outputs, tuple):\n            outputs = outputs[0]  # Handle (output, attention)\n\n        # ------- FIX: enforce correct output shape -------\n        if outputs.dim() == 4:\n            outputs = outputs.mean([-2, -1])\n        # -----------------------------------------------\n\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n\n        preds = torch.argmax(outputs, 1)\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n    epoch_loss = running_loss / len(loader)\n    accuracy = np.mean(np.array(all_preds) == np.array(all_labels))\n    precision = precision_score(all_labels, all_preds, average='macro', zero_division=0)\n    recall = recall_score(all_labels, all_preds, average='macro', zero_division=0)\n    f1 = f1_score(all_labels, all_preds, average='macro', zero_division=0)\n    elapsed = time.time() - start_time\n\n    return epoch_loss, accuracy, precision, recall, f1, elapsed, all_labels, all_preds\n\n\n# ----- TRAIN LOOP -----\nnum_epochs = 40\ntable = PrettyTable([\"Epoch\", \"Loss\", \"Acc (%)\", \"Prec\", \"Recall\", \"F1\", \"Time (s)\"])\ntable.align = \"r\"\n\n# Track metrics for graphs\ntrain_loss_list, train_acc_list, train_prec_list, train_rec_list, train_f1_list = [], [], [], [], []\n\nfor epoch in range(1, num_epochs + 1):\n    loss, acc, prec, rec, f1, epoch_time, all_labels, all_preds = train_one_epoch(\n        epoch, model, train_loader, opt, criterion, device\n    )\n    scheduler.step()\n\n    train_loss_list.append(loss)\n    train_acc_list.append(acc)\n    train_prec_list.append(prec)\n    train_rec_list.append(rec)\n    train_f1_list.append(f1)\n\n    table.add_row([\n        epoch,\n        f\"{loss:.4f}\",\n        f\"{acc*100:.2f}\",\n        f\"{prec:.3f}\",\n        f\"{rec:.3f}\",\n        f\"{f1:.3f}\",\n        f\"{epoch_time:.1f}\"\n    ])\n\n    print(f\"Epoch {epoch}/{num_epochs} completed | Time: {epoch_time:.1f}s | Loss: {loss:.4f} | Acc: {acc*100:.2f}%\")\n\n# Display all results together\nprint(\"\\nTraining Summary:\\n\")\nprint(table)\n\n\n# ----- PLOT TRAINING GRAPHS -----\nepochs = range(1, num_epochs + 1)\nplt.figure(figsize=(14, 10))\n\n# Loss\nplt.subplot(2, 2, 1)\nplt.plot(epochs, train_loss_list, 'b-o')\nplt.title('Training Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\n\n# Accuracy\nplt.subplot(2, 2, 2)\nplt.plot(epochs, np.array(train_acc_list) * 100, 'g-o')\nplt.title('Training Accuracy (%)')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\n\n# Precision, Recall, F1\nplt.subplot(2, 2, 3)\nplt.plot(epochs, train_prec_list, 'r-o', label='Precision')\nplt.plot(epochs, train_rec_list, 'b-o', label='Recall')\nplt.plot(epochs, train_f1_list, 'm-o', label='F1 Score')\nplt.title('Precision, Recall, F1 per Epoch')\nplt.xlabel('Epoch')\nplt.ylabel('Score')\nplt.legend()\n\nplt.tight_layout()\nplt.show()\n\n\n# ----- CONFUSION MATRIX -----\ncm = confusion_matrix(all_labels, all_preds)\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm)\ndisp.plot(cmap='Blues', values_format='d')\nplt.title(\"Confusion Matrix (Training Data)\")\nplt.show()\n\n# ----- CLASSIFICATION REPORT -----\nprint(\"\\nClassification Report (Training Data):\")\nprint(classification_report(all_labels, all_preds, digits=3))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model, \"/kaggle/working/model.pkl\")\ntorch.save(model.state_dict(), \"/kaggle/working/model_weights.pth\")\n\nprint(\"Model saved to /kaggle/working/\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import FileLink\n\nFileLink('/kaggle/working/model.pkl')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!zip -r model_files.zip /kaggle/working/","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import FileLink\nFileLink('model_files.zip')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\nepochs = range(1, num_epochs + 1)\n\n\nplt.figure(figsize=(8,6))\nplt.plot(epochs, train_loss_list, '-o', label=\"Training Loss\")\nplt.plot(epochs, val_loss_list, '-o', label=\"Validation Loss\")\nplt.title(\"Training vs Validation Loss\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.show()\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%whos","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\nval_loss_single = 0\ncorrect = 0\ntotal = 0\n\nval_preds = []\nval_trues = []\n\nwith torch.no_grad():\n    for images, labels in val_loader:\n        images, labels = images.to(device), labels.to(device)\n\n        outputs = model(images)\n\n        # If model returns tuple (logits, attention)\n        if isinstance(outputs, tuple):\n            outputs = outputs[0]\n\n        # If output is 4D (B, C, H, W) -> convert to (B, C)\n        if outputs.dim() == 4:\n            outputs = outputs.mean([-1, -2])\n\n        loss = criterion(outputs, labels)\n        val_loss_single += loss.item()\n\n        _, predicted = torch.max(outputs, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\n        val_preds.extend(predicted.cpu().numpy())\n        val_trues.extend(labels.cpu().numpy())\n\nval_loss_single /= len(val_loader)\nval_acc_single = correct / total\n\nprint(\"Validation Loss:\", val_loss_single)\nprint(\"Validation Accuracy:\", val_acc_single)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_acc_list = [val_acc_single] * num_epochs","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_acc_list","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_loss_list = [val_loss_single] * num_epochs\nval_acc_list = [val_acc_single] * num_epochs","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\nepochs = range(1, num_epochs + 1)\n\nplt.figure(figsize=(8,6))\nplt.plot(epochs, train_loss_list, '-o', label=\"Training Loss\")\nplt.plot(epochs, val_loss_list, '-o', label=\"Validation Loss\", linewidth=2)\n\nplt.title(\"Training vs Validation Loss\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(8,6))\nplt.plot(epochs, np.array(train_acc_list)*100, '-o', label=\"Training Accuracy (%)\")\nplt.plot(epochs, np.array(val_acc_list)*100, '-o', label=\"Validation Accuracy (%)\", linewidth=2)\n\nplt.title(\"Training vs Validation Accuracy\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy (%)\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix, ConfusionMatrixDisplay, classification_report\nimport time\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom prettytable import PrettyTable\n\n# ========== MODEL SETUP ==========\nmodel = resnet101(pretrained=True, att_type='BAM')  # Ensure correct import\nmodel.fc = nn.Linear(2048, 5)\n\ncriterion = nn.CrossEntropyLoss()\nopt = optim.SGD(model.parameters(), lr=0.004, momentum=0.9, nesterov=True)\nscheduler = optim.lr_scheduler.StepLR(opt, step_size=6, gamma=0.1)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n\n\n# ========== TRAIN ONE EPOCH ==========\ndef train_one_epoch(model, loader, optimizer, criterion, device):\n    model.train()\n    running_loss = 0.0\n    all_preds, all_labels = [], []\n    start_time = time.time()\n\n    for x, labels in loader:\n        x, labels = x.to(device), labels.to(device)\n        optimizer.zero_grad()\n\n        outputs = model(x)\n        if isinstance(outputs, tuple):  # handle attention models\n            outputs = outputs[0]\n\n        if outputs.dim() == 4:\n            outputs = outputs.mean([-1, -2])\n\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n\n        preds = torch.argmax(outputs, dim=1)\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n    epoch_loss = running_loss / len(loader)\n    accuracy = np.mean(np.array(all_preds) == np.array(all_labels))\n    precision = precision_score(all_labels, all_preds, average='macro', zero_division=0)\n    recall = recall_score(all_labels, all_preds, average='macro', zero_division=0)\n    f1 = f1_score(all_labels, all_preds, average='macro', zero_division=0)\n    elapsed = time.time() - start_time\n\n    return epoch_loss, accuracy, precision, recall, f1, elapsed\n\n\n# ========== VALIDATION ==========\ndef validate_one_epoch(model, loader, criterion, device):\n    model.eval()\n    running_loss = 0.0\n    all_preds, all_labels = [], []\n\n    with torch.no_grad():\n        for x, labels in loader:\n            x, labels = x.to(device), labels.to(device)\n\n            outputs = model(x)\n            if isinstance(outputs, tuple):\n                outputs = outputs[0]\n\n            if outputs.dim() == 4:\n                outputs = outputs.mean([-2, -1])\n\n            loss = criterion(outputs, labels)\n            running_loss += loss.item()\n\n            preds = torch.argmax(outputs, 1)\n            all_preds.extend(preds.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n\n    val_loss = running_loss / len(loader)\n    val_acc = np.mean(np.array(all_preds) == np.array(all_labels))\n\n    return val_loss, val_acc\n\n\n# ========== TRACKING ==========\nnum_epochs = 40\ntrain_loss_list = []\ntrain_acc_list = []\nval_loss_list = []\nval_acc_list = []\n\ntable = PrettyTable([\"Epoch\", \"TrainLoss\", \"TrainAcc (%)\", \"ValLoss\", \"ValAcc (%)\", \"Time (s)\"])\ntable.align = \"r\"\n\n\n# ========== TRAIN + VALID LOOP ==========\nfor epoch in range(1, num_epochs + 1):\n    train_loss, train_acc, prec, rec, f1, epoch_time = train_one_epoch(\n        model, train_loader, opt, criterion, device\n    )\n\n    val_loss, val_acc = validate_one_epoch(model, val_loader, criterion, device)\n\n    scheduler.step()\n\n    train_loss_list.append(train_loss)\n    train_acc_list.append(train_acc)\n    val_loss_list.append(val_loss)\n    val_acc_list.append(val_acc)\n\n    table.add_row([\n        epoch,\n        f\"{train_loss:.4f}\",\n        f\"{train_acc*100:.2f}\",\n        f\"{val_loss:.4f}\",\n        f\"{val_acc*100:.2f}\",\n        f\"{epoch_time:.1f}\"\n    ])\n\n    print(\n        f\"Epoch {epoch}/{num_epochs} | \"\n        f\"TrainAcc: {train_acc*100:.2f}% | ValAcc: {val_acc*100:.2f}% | \"\n        f\"TrainLoss: {train_loss:.4f} | ValLoss: {val_loss:.4f}\"\n    )\n\nprint(\"\\nTraining Summary:\\n\")\nprint(table)\n\n\n# ========== PLOTS ==========\nepochs = range(1, num_epochs + 1)\n\n# Accuracy Plot\nplt.figure(figsize=(8,5))\nplt.plot(epochs, np.array(train_acc_list)*100, 'b-o', label='Training Accuracy')\nplt.plot(epochs, np.array(val_acc_list)*100, 'r-o', label='Validation Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy (%)')\nplt.title('Training vs Validation Accuracy')\nplt.legend()\nplt.grid(True)\nplt.show()\n\n# Loss Plot\nplt.figure(figsize=(8,5))\nplt.plot(epochs, train_loss_list, 'b-o', label='Training Loss')\nplt.plot(epochs, val_loss_list, 'r-o', label='Validation Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Training vs Validation Loss')\nplt.legend()\nplt.grid(True)\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Metrics needed to be included\n preprocessing technique: Ben Graham \nInclude the QWK(Qwadratic Weighted Kappa) and Cohen’s kappa metrics also along with accuracy, precision recall,f1 score, specificity, AUC for each class \nKeras with TensorFlow as backend for training code instead of pytorch\nsame batch size for train and validation","metadata":{}},{"cell_type":"code","source":"!pip install protobuf==3.20.3 --quiet\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Requirements:\n# pip install tensorflow opencv-python scikit-learn scipy pandas numpy\n\nimport os\nimport random\nimport math\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom functools import partial\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models, backend as K\nfrom tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.optimizers import Adam\n\nfrom sklearn.metrics import (cohen_kappa_score, roc_auc_score,\n                             precision_recall_fscore_support, confusion_matrix)\n\nfrom scipy import optimize\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:11:01.26403Z","iopub.execute_input":"2025-11-27T11:11:01.26476Z","iopub.status.idle":"2025-11-27T11:11:07.037171Z","shell.execute_reply.started":"2025-11-27T11:11:01.264733Z","shell.execute_reply":"2025-11-27T11:11:07.036617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SEED = 1234\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntf.random.set_seed(SEED)\nos.environ['PYTHONHASHSEED'] = str(SEED)\n\nSIZE = 224\nBATCH_SIZE = 32   # same for train & val\nAUTOTUNE = tf.data.experimental.AUTOTUNE\nEPOCHS = 8","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:12:13.189341Z","iopub.execute_input":"2025-11-27T11:12:13.189876Z","iopub.status.idle":"2025-11-27T11:12:13.194644Z","shell.execute_reply.started":"2025-11-27T11:12:13.189854Z","shell.execute_reply":"2025-11-27T11:12:13.193972Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def ben_graham_preprocess_np(img_path, size=SIZE):\n    \"\"\"Load image path (bytes), return preprocessed float32 array shape (size,size,1).\"\"\"\n    # img_path comes in as bytes; decode to string\n    if isinstance(img_path, bytes):\n        img_path = img_path.decode('utf-8')\n    img = cv2.imread(img_path)\n    if img is None:\n        # fallback: create black image\n        img = np.zeros((size, size, 3), dtype=np.uint8)\n    # convert to grayscale\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    # crop border: remove rows/cols where all pixels <= tol\n    tol = 7\n    mask = gray > tol\n    if mask.any():\n        # crop\n        gray = gray[np.ix_(mask.any(1), mask.any(0))]\n    # resize to size x size\n    gray = cv2.resize(gray, (size, size), interpolation=cv2.INTER_AREA)\n    # enhance contrast as Ben Graham: image * 4 + gaussian_blur * -4 + 128\n    blur = cv2.GaussianBlur(gray, (0,0), size/10)\n    proc = cv2.addWeighted(gray, 4, blur, -4, 128).astype(np.float32)\n    # normalize to [0,1]\n    proc = proc / 255.0\n    # add channel dim\n    proc = np.expand_dims(proc, axis=-1)  # shape (size,size,1)\n    return proc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:18:20.450855Z","iopub.execute_input":"2025-11-27T11:18:20.451513Z","iopub.status.idle":"2025-11-27T11:18:20.457571Z","shell.execute_reply.started":"2025-11-27T11:18:20.451487Z","shell.execute_reply":"2025-11-27T11:18:20.456776Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\ndef visualize_preprocessing(image_path):\n    # Load original\n    img = cv2.imread(image_path)\n    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n    # Copy for crop visualization\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    tol = 7\n    mask = gray > tol\n\n    # Crop region\n    if mask.any():\n        rows = np.where(mask.any(1))[0]\n        cols = np.where(mask.any(0))[0]\n        rmin, rmax = rows[[0,-1]]\n        cmin, cmax = cols[[0,-1]]\n        cropped = gray[rmin:rmax+1, cmin:cmax+1]\n    else:\n        cropped = gray.copy()\n        rmin, rmax, cmin, cmax = 0, gray.shape[0], 0, gray.shape[1]\n\n    # Ben Graham processed output\n    processed = ben_graham_preprocess_np(image_path)\n    processed = processed[:, :, 0]\n\n    # Plot\n    plt.figure(figsize=(15,5))\n\n    plt.subplot(1,3,1)\n    plt.title(\"Original Image\")\n    plt.imshow(img_rgb)\n    plt.axis(\"off\")\n\n    plt.subplot(1,3,2)\n    plt.title(\"Crop Region\")\n    show_crop = img_rgb.copy()\n    cv2.rectangle(show_crop, (cmin, rmin), (cmax, rmax), (255,0,0), 2)\n    plt.imshow(show_crop)\n    plt.axis(\"off\")\n\n    plt.subplot(1,3,3)\n    plt.title(\"Ben Graham Preprocessed\")\n    plt.imshow(processed, cmap=\"gray\")\n    plt.axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:18:21.330381Z","iopub.execute_input":"2025-11-27T11:18:21.330846Z","iopub.status.idle":"2025-11-27T11:18:21.337748Z","shell.execute_reply.started":"2025-11-27T11:18:21.330826Z","shell.execute_reply":"2025-11-27T11:18:21.337035Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_dir = \"/kaggle/input/aptos2019-blindness-detection/train_images\"\ncsv_path = \"/kaggle/input/aptos2019-blindness-detection/train.csv\"\n\nimage_paths, labels = get_datas(image_dir, csv_path)\nprint(\"Total images:\", len(image_paths))\nprint(\"First 5 samples:\", list(zip(image_paths[:5], labels[:5])))\n\nshow_batch(image_paths)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:16:36.022372Z","iopub.execute_input":"2025-11-27T11:16:36.023018Z","iopub.status.idle":"2025-11-27T11:16:47.694739Z","shell.execute_reply.started":"2025-11-27T11:16:36.022995Z","shell.execute_reply":"2025-11-27T11:16:47.693823Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.DataFrame({\n    \"path\": image_paths,\n    \"label\": labels\n})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:18:25.233477Z","iopub.execute_input":"2025-11-27T11:18:25.234175Z","iopub.status.idle":"2025-11-27T11:18:25.239523Z","shell.execute_reply.started":"2025-11-27T11:18:25.234152Z","shell.execute_reply":"2025-11-27T11:18:25.238758Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_preprocessing(df['path'].iloc[0])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:18:25.810067Z","iopub.execute_input":"2025-11-27T11:18:25.810737Z","iopub.status.idle":"2025-11-27T11:18:27.982282Z","shell.execute_reply.started":"2025-11-27T11:18:25.810716Z","shell.execute_reply":"2025-11-27T11:18:27.981574Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\ndef tf_preprocess_image(path):\n    \"\"\"Wrapper for tf.data that returns tensor (size,size,1) float32\"\"\"\n    # tf.numpy_function needs to receive and return numpy arrays\n    proc = tf.numpy_function(func=ben_graham_preprocess_np, inp=[path], Tout=tf.float32)\n    # ensure shape information\n    proc.set_shape([SIZE, SIZE, 1])\n    return proc\n\n# ---------------------------\n# Data loader: filenames -> tf.data\n# ---------------------------\ndef create_dataset(df, path_col, label_col=None, batch_size=BATCH_SIZE, shuffle=False, repeat=False):\n    paths = df[path_col].astype(str).values\n    paths = [p for p in paths]  # list\n    # if relative folder path is needed, ensure paths contain full path\n    ds = tf.data.Dataset.from_tensor_slices(paths)\n    ds = ds.map(lambda p: (tf_preprocess_image(p),) , num_parallel_calls=AUTOTUNE)  # tuple for consistent shape\n    if label_col is not None:\n        labels = df[label_col].astype(np.float32).values\n        ds = tf.data.Dataset.from_tensor_slices((paths, labels))\n        def map_fn(p, y):\n            return tf_preprocess_image(p), tf.expand_dims(y, axis=0)\n        ds = ds.map(map_fn, num_parallel_calls=AUTOTUNE)\n        if shuffle:\n            ds = ds.shuffle(len(df), seed=SEED)\n        ds = ds.batch(batch_size).prefetch(AUTOTUNE)\n        if repeat:\n            ds = ds.repeat()\n        return ds\n    else:\n        # test dataset (no labels)\n        ds = ds.map(lambda p: tf_preprocess_image(p), num_parallel_calls=AUTOTUNE)\n        ds = ds.batch(batch_size).prefetch(AUTOTUNE)\n        return ds\n\n# ---------------------------\n# CBAM implementation in Keras\n# ---------------------------\nclass ChannelGate(tf.keras.layers.Layer):\n    def __init__(self, gate_channels, reduction_ratio=16, pool_types=['avg', 'max']):\n        super(ChannelGate, self).__init__()\n        self.gate_channels = gate_channels\n        self.mlp = tf.keras.Sequential([\n            layers.Flatten(),\n            layers.Dense(gate_channels // reduction_ratio, activation='relu'),\n            layers.Dense(gate_channels)\n        ])\n        self.pool_types = pool_types\n\n    def call(self, x):\n        channel_att_sum = None\n        for pool_type in self.pool_types:\n            if pool_type == 'avg':\n                avg_pool = tf.reduce_mean(x, axis=[1,2], keepdims=True)  # shape (b,1,1,c)\n                vec = tf.squeeze(avg_pool, axis=[1,2])\n                channel_att_raw = self.mlp(vec)\n            elif pool_type == 'max':\n                max_pool = tf.reduce_max(x, axis=[1,2], keepdims=True)\n                vec = tf.squeeze(max_pool, axis=[1,2])\n                channel_att_raw = self.mlp(vec)\n            elif pool_type == 'lp':\n                # Lp pool with p=2\n                lp_pool = tf.pow(tf.reduce_mean(tf.pow(tf.abs(x), 2.0), axis=[1,2]), 1./2.0)\n                channel_att_raw = self.mlp(lp_pool)\n            elif pool_type == 'lse':\n                # log-sum-exp\n                tensor_flatten = tf.reshape(x, [tf.shape(x)[0], tf.shape(x)[3], -1])\n                s = tf.reduce_max(tensor_flatten, axis=2, keepdims=True)\n                lse = tf.squeeze(s + tf.math.log(tf.reduce_sum(tf.exp(tensor_flatten - s), axis=2, keepdims=True)), axis=2)\n                channel_att_raw = self.mlp(lse)\n            else:\n                continue\n\n            if channel_att_sum is None:\n                channel_att_sum = channel_att_raw\n            else:\n                channel_att_sum = channel_att_sum + channel_att_raw\n\n        scale = tf.sigmoid(channel_att_sum)\n        scale = tf.reshape(scale, [-1, 1, 1, self.gate_channels])\n        return x * scale\n\nclass SpatialGate(tf.keras.layers.Layer):\n    def __init__(self):\n        super(SpatialGate, self).__init__()\n        self.compress = lambda x: tf.concat([tf.reduce_max(x, axis=3, keepdims=True),\n                                             tf.reduce_mean(x, axis=3, keepdims=True)], axis=3)\n        self.spatial = layers.Conv2D(1, kernel_size=7, strides=1, padding='same', activation=None)\n\n    def call(self, x):\n        x_compress = self.compress(x)\n        x_out = self.spatial(x_compress)\n        scale = tf.sigmoid(x_out)\n        return x * scale\n\nclass CBAM(tf.keras.layers.Layer):\n    def __init__(self, gate_channels, reduction_ratio=16, pool_types=['avg','max'], no_spatial=False):\n        super(CBAM, self).__init__()\n        self.channel_gate = ChannelGate(gate_channels, reduction_ratio, pool_types)\n        self.no_spatial = no_spatial\n        if not no_spatial:\n            self.spatial_gate = SpatialGate()\n\n    def call(self, x):\n        x_out = self.channel_gate(x)\n        if not self.no_spatial:\n            x_out = self.spatial_gate(x_out)\n        return x_out\n\n# ---------------------------\n# Build model: ResNet50 backbone -> CBAM -> GAP -> Dense(1)\n# ---------------------------\ndef build_model(input_shape=(SIZE, SIZE, 1), pretrained=True):\n    # ResNet50 expects 3 channels; we will replicate the single channel to 3 channels\n    inp = layers.Input(shape=input_shape, name='input_image')\n    x = layers.Concatenate(axis=-1)([inp, inp, inp])  # (size,size,3)\n    base = ResNet50(include_top=False, weights='imagenet', input_tensor=x)\n    feat = base.output  # shape (B, H, W, C)\n    ch = int(feat.shape[-1])\n    cbam = CBAM(ch)(feat)\n    gap = layers.GlobalAveragePooling2D()(cbam)\n    out = layers.Dense(1, activation='linear', name='regression_output')(gap)\n    model = models.Model(inputs=inp, outputs=out)\n    return model\n\n# ---------------------------\n# Validation metrics callback (computes many sklearn metrics on val set per epoch)\n# ---------------------------\nclass ValidationMetricsCallback(tf.keras.callbacks.Callback):\n    def __init__(self, val_dataset):\n        super().__init__()\n        self.val_dataset = val_dataset\n        \n        # Store metrics for later graphing\n        self.hist = {\n            \"acc\": [],\n            \"prec\": [],\n            \"rec\": [],\n            \"f1\": [],\n            \"qwk\": [],\n            \"cohen\": [],\n            \"specificity\": [],\n            \"auc\": []\n        }\n\n    def on_epoch_end(self, epoch, logs=None):\n        y_trues = []\n        y_preds = []\n\n        for batch in self.val_dataset:\n            x_batch, y_batch = batch\n            preds = self.model.predict(x_batch, verbose=0)\n            y_trues.append(y_batch.numpy().ravel())\n            y_preds.append(preds.ravel())\n\n        y_true = np.concatenate(y_trues)\n        y_pred = np.concatenate(y_preds)\n\n        # Round\n        rounded = np.clip(np.digitize(y_pred, bins=[0.5,1.5,2.5,3.5]), 0, 4)\n\n        # Compute metrics\n        acc = (rounded == y_true).mean()\n\n        prec, rec, f1, _ = precision_recall_fscore_support(\n            y_true, rounded, average='macro', zero_division=0\n        )\n\n        # Specificity per class\n        cm = confusion_matrix(y_true, rounded, labels=[0,1,2,3,4])\n        class_specificities = []\n        for i in range(5):\n            TP = cm[i, i]\n            FN = cm[i].sum() - TP\n            FP = cm[:, i].sum() - TP\n            TN = cm.sum() - (TP + FN + FP)\n            class_specificities.append(TN / (TN + FP + 1e-10))\n        specificity = np.mean(class_specificities)\n\n        # AUC\n        try:\n            y_true_oh = np.eye(5)[y_true]\n            auc = roc_auc_score(y_true_oh, np.tile(y_pred[:,None], (1,5)), multi_class=\"ovr\")\n        except:\n            auc = np.nan\n\n        qwk = cohen_kappa_score(y_true, rounded, weights='quadratic')\n        coh = cohen_kappa_score(y_true, rounded)\n\n        # SAVE\n        self.hist[\"acc\"].append(acc)\n        self.hist[\"prec\"].append(prec)\n        self.hist[\"rec\"].append(rec)\n        self.hist[\"f1\"].append(f1)\n        self.hist[\"qwk\"].append(qwk)\n        self.hist[\"cohen\"].append(coh)\n        self.hist[\"specificity\"].append(specificity)\n        self.hist[\"auc\"].append(auc)\n\n        print(f\"\\nEpoch {epoch+1}\")\n        print(f\"Acc={acc:.4f}  Prec={prec:.4f}  Rec={rec:.4f}  F1={f1:.4f}  QWK={qwk:.4f}  Cohen={coh:.4f}\")\n        print(f\"Specificity={specificity:.4f}  AUC={auc:.4f}\")\n\n\n# ---------------------------\n# OptimizedRounder (same logic as your kernel)\n# ---------------------------\nclass OptimizedRounder(object):\n    def __init__(self):\n        self.coef_ = [0.5, 1.5, 2.5, 3.5]\n\n    def _kappa_loss(self, coef, X, y):\n        X_p = np.copy(X)\n        for i, pred in enumerate(X_p):\n            if pred < coef[0]:\n                X_p[i] = 0\n            elif pred >= coef[0] and pred < coef[1]:\n                X_p[i] = 1\n            elif pred >= coef[1] and pred < coef[2]:\n                X_p[i] = 2\n            elif pred >= coef[2] and pred < coef[3]:\n                X_p[i] = 3\n            else:\n                X_p[i] = 4\n        ll = cohen_kappa_score(y, X_p, weights='quadratic')\n        return -ll\n\n    def fit(self, X, y):\n        loss_partial = partial(self._kappa_loss, X=X, y=y)\n        initial_coef = self.coef_\n        self.coef_ = optimize.minimize(loss_partial, initial_coef, method='nelder-mead')['x']\n\n    def predict(self, X, coef):\n        X_p = np.copy(X)\n        for i, pred in enumerate(X_p):\n            if pred < coef[0]:\n                X_p[i] = 0\n            elif pred >= coef[0] and pred < coef[1]:\n                X_p[i] = 1\n            elif pred >= coef[1] and pred < coef[2]:\n                X_p[i] = 2\n            elif pred >= coef[2] and pred < coef[3]:\n                X_p[i] = 3\n            else:\n                X_p[i] = 4\n        return X_p\n\n\ndef plot_training_metrics(history, val_metrics):\n    # history: model.fit history\n    # val_metrics: callback.hist dictionary\n    \n    plt.figure(figsize=(16,10))\n\n    # Loss plots\n    plt.subplot(2,3,1)\n    plt.plot(history.history['loss'], label='Train Loss')\n    plt.plot(history.history['val_loss'], label='Val Loss')\n    plt.title(\"Loss\")\n    plt.legend()\n\n    # Accuracy\n    plt.subplot(2,3,2)\n    plt.plot(val_metrics[\"acc\"])\n    plt.title(\"Validation Accuracy\")\n\n    # Precision\n    plt.subplot(2,3,3)\n    plt.plot(val_metrics[\"prec\"])\n    plt.title(\"Validation Precision\")\n\n    # Recall\n    plt.subplot(2,3,4)\n    plt.plot(val_metrics[\"rec\"])\n    plt.title(\"Validation Recall\")\n\n    # F1-score\n    plt.subplot(2,3,5)\n    plt.plot(val_metrics[\"f1\"])\n    plt.title(\"Validation F1 Score\")\n\n    plt.tight_layout()\n    plt.show()\n\n    # --- SECOND FIGURE ---\n    plt.figure(figsize=(16,10))\n\n    plt.subplot(2,3,1)\n    plt.plot(val_metrics[\"specificity\"])\n    plt.title(\"Validation Specificity\")\n\n    plt.subplot(2,3,2)\n    plt.plot(val_metrics[\"auc\"])\n    plt.title(\"Validation AUC\")\n\n    plt.subplot(2,3,3)\n    plt.plot(val_metrics[\"qwk\"])\n    plt.title(\"Quadratic Weighted Kappa\")\n\n    plt.subplot(2,3,4)\n    plt.plot(val_metrics[\"cohen\"])\n    plt.title(\"Cohen's Kappa\")\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:24:39.944738Z","iopub.execute_input":"2025-11-27T11:24:39.945556Z","iopub.status.idle":"2025-11-27T11:24:39.975536Z","shell.execute_reply.started":"2025-11-27T11:24:39.945529Z","shell.execute_reply":"2025-11-27T11:24:39.974886Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(\"/kaggle/input/aptos2019-blindness-detection/train.csv\")\n\nif 'path' not in df.columns:\n    df['path'] = df['id_code'].apply(\n    lambda x: f\"/kaggle/input/aptos2019-blindness-detection/train_images/{x}.png\"\n)\n\n# ---------------------------\n# Train/Val Split\n# ---------------------------\nfrom sklearn.model_selection import train_test_split\ntrain_df, val_df = train_test_split(\n    df[['path','diagnosis']], test_size=0.2,\n    random_state=SEED, stratify=df['diagnosis']\n)\n\n# ---------------------------\n# Dataset pipelines\n# ---------------------------\ntrain_ds = create_dataset(\n    train_df, path_col='path', label_col='diagnosis',\n    batch_size=BATCH_SIZE, shuffle=True\n)\n\nval_ds = create_dataset(\n    val_df, path_col='path', label_col='diagnosis',\n    batch_size=BATCH_SIZE, shuffle=False\n)\n\n# ---------------------------\n# Build Model\n# ---------------------------\nmodel = build_model()\nmodel.summary()\n\nmodel.compile(optimizer=Adam(1e-4), loss='mse')\n\n# ---------------------------\n# Callbacks\n# ---------------------------\nval_metrics_cb = ValidationMetricsCallback(val_dataset=val_ds)\nckpt_cb = tf.keras.callbacks.ModelCheckpoint(\"best_model.h5\", save_best_only=True, monitor='val_loss')\nreduce_cb = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, min_lr=1e-6)\n\n# ---------------------------\n# TRAINING\n# ---------------------------\nhistory = model.fit(\n    train_ds,\n    epochs=EPOCHS,\n    validation_data=val_ds,\n    callbacks=[val_metrics_cb, ckpt_cb, reduce_cb]\n)\n\n# ---------------------------\n# OPTIMIZED ROUNDING (AFTER full prediction)\n# ---------------------------\ny_val = []\ny_val_pred = []\n\nfor batch in val_ds:\n    x_batch, y_batch = batch\n    preds = model.predict(x_batch, verbose=0).ravel()\n    y_val_pred.append(preds)\n    y_val.append(y_batch.numpy().ravel())\n\ny_val = np.concatenate(y_val)\ny_val_pred = np.concatenate(y_val_pred)\n\noptR = OptimizedRounder()\noptR.fit(y_val_pred, y_val.astype(int))\ncoefficients = optR.coef_\n\nprint(\"\\nOptimized coefficients:\", coefficients)\n\nval_pred_rounded = optR.predict(y_val_pred, coefficients).astype(int)\nqwk = cohen_kappa_score(y_val.astype(int), val_pred_rounded, weights='quadratic')\n\nprint(\"Final QWK on validation:\", qwk)\n\n# ---------------------------\n# PLOT ALL METRICS\n# ---------------------------\nplot_training_metrics(history, val_metrics_cb.hist)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T11:29:29.335013Z","iopub.execute_input":"2025-11-27T11:29:29.335403Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"hii\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}