{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":29193,"databundleVersionId":2318453,"sourceType":"competition"}],"dockerImageVersionId":30787,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -U albumentations==1.4.1","metadata":{"execution":{"iopub.status.busy":"2024-11-02T01:36:29.311384Z","iopub.execute_input":"2024-11-02T01:36:29.312231Z","iopub.status.idle":"2024-11-02T01:36:40.906478Z","shell.execute_reply.started":"2024-11-02T01:36:29.312190Z","shell.execute_reply":"2024-11-02T01:36:40.905436Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport hashlib\nimport albumentations as A\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.optim as optim\nimport torch.utils.data as data\nimport torchvision.models as models\nfrom PIL import Image\nfrom torch import nn\nfrom tqdm import tqdm\n\n\ndef plot(train, test=None):\n    plt.figure(figsize=(8, 6))\n    epochs = range(1, len(train) + 1)\n    plt.plot(epochs, train, label='Train', color='b')\n    if test is not None:\n        plt.plot(epochs, test, label='Validation', color='r')\n    plt.xlim(1, len(epochs))\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.legend()\n    plt.show()\n\n\ndef get_hash(image):\n    md5 = hashlib.md5()\n    md5.update(np.array(image).tobytes())\n    return md5.hexdigest()\n\n\ndef load_data(path):\n    train_data = pd.read_csv(os.path.join(path, r'train.csv'))\n    if False:\n        for i, img in enumerate(train_data['image']):\n            image = Image.open(os.path.join(path, img))\n            train_data.loc[i, 'hash'] = get_hash(image)\n\n        dp = train_data['hash'].value_counts().reset_index()\n        dp.columns = ['hash', 'dup_number']\n        dp = dp[dp['dup_number'] > 1]\n        dp = train_data.merge(dp, on='hash')\n        print(dp.head(10))\n\n        duplicate_hashes = train_data[train_data.duplicated(subset=['hash'], keep=False)]\n        hashes_to_remove = duplicate_hashes[duplicate_hashes.duplicated(subset=['hash', 'label'], keep=False) == False][\n            'hash'].unique()\n        train_data = train_data[~train_data['hash'].isin(hashes_to_remove)]\n\n        train_data = train_data.drop_duplicates(subset=['hash', 'label'], keep='first')\n    train_img = train_data['image'].values\n    train_y, label = pd.factorize(train_data['label'])\n    test_data = pd.read_csv(os.path.join(path, r'test.csv'))\n    test_img = test_data['image'].values\n    return label, train_img, train_y, test_img\n\n\nclass MyDataSet(data.Dataset):\n    def __init__(self, root_dir, img, y=None):\n        self.root_dir = root_dir\n        self.img = img\n        if y is None:\n            self.y = self.img\n            self.trans = norm_trans()\n        else:\n            self.y = y\n            self.trans = get_trans()\n\n    def __getitem__(self, item):\n        img = Image.open(os.path.join(self.root_dir, self.img[item]))\n        img = np.array(img)\n        img = self.trans(image=img)['image']\n        return img.transpose((2, 0, 1)), self.y[item]\n\n    def __len__(self):\n        return len(self.img)\n\n\ndef eval(dataset, net, loss):\n    loss_sum = 0\n    acc_sum = 0\n    for img, y in data.DataLoader(dataset, eval_batch_size, num_workers=num_workers):\n        img = img.to(device)\n        y = y.to(device)\n        o = net(img)\n        l = loss(o, y)\n        loss_sum += l.item()\n        acc_sum += (torch.argmax(o, 1) == y).sum().item()\n    return loss_sum / (len(dataset) + 1e-6), acc_sum / (len(dataset) + 1e-6)\n\n\ndef train():\n    dataset = MyDataSet(data_path, train_img, train_y)\n    train_data, valid_data = data.random_split(dataset, lengths=[int(len(dataset) * train_ratio),\n                                                                 len(dataset) - int(len(dataset) * train_ratio)])\n    net, loss, optimizer, scheduler = get_net()\n    train_loss_ls = []\n    train_acc_ls = []\n    valid_loss_ls = []\n    valid_acc_ls = []\n    for epoch in range(epochs):\n        net.train()\n        for img, y in data.DataLoader(train_data, batch_size, shuffle=True, num_workers=num_workers):\n            img = img.to(device)\n            y = y.to(device)\n            optimizer.zero_grad()\n            o = net(img)\n            l = loss(o, y) / batch_size\n            l.backward()\n            optimizer.step()\n        scheduler.step()\n        net.eval()\n        with torch.no_grad():\n            l, a = eval(train_data, net, loss)\n            train_loss_ls.append(l)\n            train_acc_ls.append(a)\n            l, a = eval(valid_data, net, loss)\n            valid_loss_ls.append(l)\n            valid_acc_ls.append(a)\n        print(f'epoch {epoch:02d} train loss {train_loss_ls[-1]:.4f} acc {train_acc_ls[-1]:.4f}, '\n              f'valid loss {valid_loss_ls[-1]:.4f} acc {valid_acc_ls[-1]:.4f}')\n    plot(train_loss_ls, valid_loss_ls)\n    plot(train_acc_ls, valid_acc_ls)\n    return net\n\n\ndef predict(net):\n    image = []\n    res = []\n    dataset = MyDataSet(data_path, test_img)\n    net.eval()\n    with torch.no_grad():\n        for img, n in tqdm(data.DataLoader(dataset, eval_batch_size, num_workers=num_workers)):\n            img = img.to(device)\n            o = net(img)\n            image.append(n)\n            res.append(label[torch.argmax(o, dim=1).cpu()])\n    image = np.concatenate(image, axis=0)\n    res = np.concatenate(res, axis=0)\n    return pd.DataFrame({\n        'image': list(image),\n        'label': list(res)\n    })\n\n\ndef get_trans():\n    return A.Compose([\n        A.Resize(360, 360),\n        A.RandomResizedCrop(height=224, width=224, scale=(0.64, 1.0)),\n        A.OneOf([\n            A.Compose([A.HorizontalFlip(p=0.5)]),\n            A.Compose([A.Transpose(p=1), A.VerticalFlip(p=0.5)]),\n            A.Compose([A.VerticalFlip(p=1), A.HorizontalFlip(p=0.5)]),\n            A.Compose([A.Transpose(p=1), A.HorizontalFlip(p=1), A.VerticalFlip(p=0.5)])\n        ], p=1),\n        A.ShiftScaleRotate(shift_limit=0.25, scale_limit=0.1, rotate_limit=0, border_mode=4),\n        A.GaussNoise(var_limit=(1, 75)),\n        A.RandomBrightnessContrast(brightness_limit=0.1),\n        norm_trans()\n    ])\n\n\ndef norm_trans():\n    return A.Normalize(\n        [0.485, 0.456, 0.406], [0.229, 0.224, 0.225],\n        max_pixel_value=255.0, always_apply=True\n    )\n\n\ndef init_weights(m):\n    if isinstance(m, nn.Linear):\n        nn.init.xavier_normal_(m.weight)\n        nn.init.zeros_(m.bias)\n    elif isinstance(m, nn.BatchNorm1d):\n        nn.init.ones_(m.weight)\n        nn.init.zeros_(m.bias)\n\n\ndef get_net():\n    if pretrained:\n        net = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)\n    else:\n        net = models.resnet50(num_classes=176)\n    net.fc = nn.Sequential(\n        nn.Linear(net.fc.in_features, 512),\n        nn.BatchNorm1d(512),\n        nn.ReLU(),\n        nn.Dropout(p=0.5),\n        nn.Linear(512, 176)\n    )\n    net.fc.apply(init_weights)\n    net = net.to(device)\n    loss = nn.CrossEntropyLoss(reduction='sum').to(device)\n    optimizer = optim.AdamW(\n        [{'params': [param for name, param in net.named_parameters() if 'fc.' not in name]},\n         {'params': net.fc.parameters(), 'lr': lr * 10}],\n        lr=lr, weight_decay=weight_decay\n    )\n    scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10)\n    return net, loss, optimizer, scheduler\n\n\nif __name__ == '__main__':\n    np.random.seed(144)\n    torch.manual_seed(144)\n    data_path = '/kaggle/input/classify-leaves' if torch.cuda.is_available() else 'datasets/classify-leaves'\n    device = 'cuda' if torch.cuda.is_available() else 'cpu'\n    pretrained = torch.cuda.is_available()\n    train_ratio = 0.9\n    num_workers = 2\n    lr = 0.0001\n    weight_decay = 0.01\n    batch_size = 24\n    eval_batch_size = 128\n    epochs = 40\n    label, train_img, train_y, test_img = load_data(data_path)\n    net = train()\n    # torch.save(net.state_dict(), 'model/model.pth')\n    result = predict(net)\n    result.to_csv('submission.csv', index=False)\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-11-02T01:36:40.909017Z","iopub.execute_input":"2024-11-02T01:36:40.909702Z"},"trusted":true},"outputs":[],"execution_count":null}]}