{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.10","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":30097,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"以前都是学着别人的baseline用，这次终于轮到我可以为大家贡献一下了哈哈哈","metadata":{}},{"cell_type":"code","source":"# 首先导入包\nimport torch\nimport torch.nn as nn\nimport pandas as pd\nimport numpy as np\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\nimport torchvision.models as models\n# This is for the progress bar.\nfrom tqdm import tqdm\nimport seaborn as sns\n","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:15.132134Z","iopub.execute_input":"2024-05-03T11:56:15.132786Z","iopub.status.idle":"2024-05-03T11:56:17.342059Z","shell.execute_reply.started":"2024-05-03T11:56:15.132646Z","shell.execute_reply":"2024-05-03T11:56:17.340973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 看看label文件长啥样\nlabels_dataframe = pd.read_csv('../input/classify-leaves/train.csv')\nlabels_dataframe.head(5)","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:17.343650Z","iopub.execute_input":"2024-05-03T11:56:17.344010Z","iopub.status.idle":"2024-05-03T11:56:17.395622Z","shell.execute_reply.started":"2024-05-03T11:56:17.343976Z","shell.execute_reply":"2024-05-03T11:56:17.394620Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_dataframe.describe()","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:17.397727Z","iopub.execute_input":"2024-05-03T11:56:17.398191Z","iopub.status.idle":"2024-05-03T11:56:17.460970Z","shell.execute_reply.started":"2024-05-03T11:56:17.398147Z","shell.execute_reply":"2024-05-03T11:56:17.459532Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#function to show bar length\n\ndef barw(ax): \n    \n    for p in ax.patches:\n        val = p.get_width() #height of the bar\n        x = p.get_x()+ p.get_width() # x- position \n        y = p.get_y() + p.get_height()/2 #y-position\n        ax.annotate(round(val,2),(x,y))\n        \n#finding top leaves\n\nplt.figure(figsize = (15,30))\nax0 =sns.countplot(y=labels_dataframe['label'],order=labels_dataframe['label'].value_counts().index)\nbarw(ax0)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:17.463134Z","iopub.execute_input":"2024-05-03T11:56:17.463578Z","iopub.status.idle":"2024-05-03T11:56:22.417558Z","shell.execute_reply.started":"2024-05-03T11:56:17.463529Z","shell.execute_reply":"2024-05-03T11:56:22.416598Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 把label文件排个序\nleaves_labels = sorted(list(set(labels_dataframe['label'])))\nn_classes = len(leaves_labels)\nprint(n_classes)\nleaves_labels[:10]","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:22.418921Z","iopub.execute_input":"2024-05-03T11:56:22.419253Z","iopub.status.idle":"2024-05-03T11:56:22.430453Z","shell.execute_reply.started":"2024-05-03T11:56:22.419205Z","shell.execute_reply":"2024-05-03T11:56:22.429180Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 把label转成对应的数字\nclass_to_num = dict(zip(leaves_labels, range(n_classes)))\nclass_to_num","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:22.432120Z","iopub.execute_input":"2024-05-03T11:56:22.432558Z","iopub.status.idle":"2024-05-03T11:56:22.449822Z","shell.execute_reply.started":"2024-05-03T11:56:22.432512Z","shell.execute_reply":"2024-05-03T11:56:22.448667Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 再转换回来，方便最后预测的时候使用\nnum_to_class = {v : k for k, v in class_to_num.items()}","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:22.451120Z","iopub.execute_input":"2024-05-03T11:56:22.451514Z","iopub.status.idle":"2024-05-03T11:56:22.457284Z","shell.execute_reply.started":"2024-05-03T11:56:22.451479Z","shell.execute_reply":"2024-05-03T11:56:22.456258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 继承pytorch的dataset，创建自己的\nclass LeavesData(Dataset):\n    def __init__(self, csv_path, file_path, mode='train', valid_ratio=0.2, resize_height=256, resize_width=256):\n        \"\"\"\n        Args:\n            csv_path (string): csv 文件路径\n            img_path (string): 图像文件所在路径\n            mode (string): 训练模式还是测试模式\n            valid_ratio (float): 验证集比例\n        \"\"\"\n        \n        # 需要调整后的照片尺寸，我这里每张图片的大小尺寸不一致#\n        self.resize_height = resize_height\n        self.resize_width = resize_width\n\n        self.file_path = file_path\n        self.mode = mode\n\n        # 读取 csv 文件\n        # 利用pandas读取csv文件\n        self.data_info = pd.read_csv(csv_path, header=None)  #header=None是去掉表头部分\n        # 计算 length\n        self.data_len = len(self.data_info.index) - 1\n        self.train_len = int(self.data_len * (1 - valid_ratio))\n        \n        if mode == 'train':\n            # 第一列包含图像文件的名称\n            self.train_image = np.asarray(self.data_info.iloc[1:self.train_len, 0])  #self.data_info.iloc[1:,0]表示读取第一列，从第二行开始到train_len\n            # 第二列是图像的 label\n            self.train_label = np.asarray(self.data_info.iloc[1:self.train_len, 1])\n            self.image_arr = self.train_image \n            self.label_arr = self.train_label\n        elif mode == 'valid':\n            self.valid_image = np.asarray(self.data_info.iloc[self.train_len:, 0])  \n            self.valid_label = np.asarray(self.data_info.iloc[self.train_len:, 1])\n            self.image_arr = self.valid_image\n            self.label_arr = self.valid_label\n        elif mode == 'test':\n            self.test_image = np.asarray(self.data_info.iloc[1:, 0])\n            self.image_arr = self.test_image\n            \n        self.real_len = len(self.image_arr)\n\n        print('Finished reading the {} set of Leaves Dataset ({} samples found)'\n              .format(mode, self.real_len))\n\n    def __getitem__(self, index):\n        # 从 image_arr中得到索引对应的文件名\n        single_image_name = self.image_arr[index]\n\n        # 读取图像文件\n        img_as_img = Image.open(self.file_path + single_image_name)\n\n        #如果需要将RGB三通道的图片转换成灰度图片可参考下面两行\n#         if img_as_img.mode != 'L':\n#             img_as_img = img_as_img.convert('L')\n\n        #设置好需要转换的变量，还可以包括一系列的nomarlize等等操作\n        if self.mode == 'train':\n            transform = transforms.Compose([\n                transforms.Resize((224, 224)),\n                transforms.RandomHorizontalFlip(p=0.5),   #随机水平翻转 选择一个概率\n                transforms.ToTensor()\n            ])\n        else:\n            # valid和test不做数据增强\n            transform = transforms.Compose([\n                transforms.Resize((224, 224)),\n                transforms.ToTensor()\n            ])\n        \n        img_as_img = transform(img_as_img)\n        \n        if self.mode == 'test':\n            return img_as_img\n        else:\n            # 得到图像的 string label\n            label = self.label_arr[index]\n            # number label\n            number_label = class_to_num[label]\n\n            return img_as_img, number_label  #返回每一个index对应的图片数据和对应的label\n\n    def __len__(self):\n        return self.real_len\n","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:22.460303Z","iopub.execute_input":"2024-05-03T11:56:22.460695Z","iopub.status.idle":"2024-05-03T11:56:22.477475Z","shell.execute_reply.started":"2024-05-03T11:56:22.460662Z","shell.execute_reply":"2024-05-03T11:56:22.476518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_path = '../input/classify-leaves/train.csv'\ntest_path = '../input/classify-leaves/test.csv'\n# csv文件中已经images的路径了，因此这里只到上一级目录\nimg_path = '../input/classify-leaves/'\n\ntrain_dataset = LeavesData(train_path, img_path, mode='train')\nval_dataset = LeavesData(train_path, img_path, mode='valid')\ntest_dataset = LeavesData(test_path, img_path, mode='test')\nprint(train_dataset)\nprint(val_dataset)\nprint(test_dataset)","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:22.478908Z","iopub.execute_input":"2024-05-03T11:56:22.479214Z","iopub.status.idle":"2024-05-03T11:56:22.545575Z","shell.execute_reply.started":"2024-05-03T11:56:22.479182Z","shell.execute_reply":"2024-05-03T11:56:22.544577Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 定义data loader\ntrain_loader = torch.utils.data.DataLoader(\n        dataset=train_dataset,\n        batch_size=8, \n        shuffle=False,\n        num_workers=5\n    )\n\nval_loader = torch.utils.data.DataLoader(\n        dataset=val_dataset,\n        batch_size=8, \n        shuffle=False,\n        num_workers=5\n    )\ntest_loader = torch.utils.data.DataLoader(\n        dataset=test_dataset,\n        batch_size=8, \n        shuffle=False,\n        num_workers=5\n    )","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:22.546736Z","iopub.execute_input":"2024-05-03T11:56:22.547038Z","iopub.status.idle":"2024-05-03T11:56:22.552814Z","shell.execute_reply.started":"2024-05-03T11:56:22.547009Z","shell.execute_reply":"2024-05-03T11:56:22.551840Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 给大家展示一下数据长啥样\ndef im_convert(tensor):\n    \"\"\" 展示数据\"\"\"\n    \n    image = tensor.to(\"cpu\").clone().detach()\n    image = image.numpy().squeeze()\n    image = image.transpose(1,2,0)\n    image = image.clip(0, 1)\n\n    return image\n\nfig=plt.figure(figsize=(20, 12))\ncolumns = 4\nrows = 2\n\ndataiter = iter(val_loader)\ninputs, classes = dataiter.next()\n\nfor idx in range (columns*rows):\n    ax = fig.add_subplot(rows, columns, idx+1, xticks=[], yticks=[])\n    ax.set_title(num_to_class[int(classes[idx])])\n    plt.imshow(im_convert(inputs[idx]))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:22.553950Z","iopub.execute_input":"2024-05-03T11:56:22.554216Z","iopub.status.idle":"2024-05-03T11:56:23.615211Z","shell.execute_reply.started":"2024-05-03T11:56:22.554189Z","shell.execute_reply":"2024-05-03T11:56:23.614135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 看一下是在cpu还是GPU上\ndef get_device():\n    return 'cuda' if torch.cuda.is_available() else 'cpu'\n\ndevice = get_device()\nprint(device)","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:23.617081Z","iopub.execute_input":"2024-05-03T11:56:23.617451Z","iopub.status.idle":"2024-05-03T11:56:23.675577Z","shell.execute_reply.started":"2024-05-03T11:56:23.617402Z","shell.execute_reply":"2024-05-03T11:56:23.674352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 是否要冻住模型的前面一些层\ndef set_parameter_requires_grad(model, feature_extracting):\n    if feature_extracting:\n        model = model\n        for param in model.parameters():\n            param.requires_grad = False\n# resnet34模型\ndef res_model(num_classes, feature_extract = False, use_pretrained=True):\n\n    model_ft = models.resnet34(pretrained=use_pretrained)\n    set_parameter_requires_grad(model_ft, feature_extract)\n    num_ftrs = model_ft.fc.in_features\n    model_ft.fc = nn.Sequential(nn.Linear(num_ftrs, num_classes))\n\n    return model_ft","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:23.677228Z","iopub.execute_input":"2024-05-03T11:56:23.677840Z","iopub.status.idle":"2024-05-03T11:56:23.686685Z","shell.execute_reply.started":"2024-05-03T11:56:23.677798Z","shell.execute_reply":"2024-05-03T11:56:23.685700Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 超参数\nlearning_rate = 3e-4\nweight_decay = 1e-3\nnum_epoch = 10\nmodel_path = './pre_res_model.ckpt'","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:23.688101Z","iopub.execute_input":"2024-05-03T11:56:23.688480Z","iopub.status.idle":"2024-05-03T11:56:23.696000Z","shell.execute_reply.started":"2024-05-03T11:56:23.688444Z","shell.execute_reply":"2024-05-03T11:56:23.695186Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n# Initialize a model, and put it on the device specified.\nmodel = res_model(176)\nmodel = model.to(device)\nmodel.device = device\n# For the classification task, we use cross-entropy as the measurement of performance.\ncriterion = nn.CrossEntropyLoss()\n\n# Initialize optimizer, you may fine-tune some hyperparameters such as learning rate on your own.\noptimizer = torch.optim.Adam(model.parameters(), lr = learning_rate, weight_decay=weight_decay)\n\n# The number of training epochs.\nn_epochs = num_epoch\n\nbest_acc = 0.0\nfor epoch in range(n_epochs):\n    # ---------- Training ----------\n    # Make sure the model is in train mode before training.\n    model.train() \n    # These are used to record information in training.\n    train_loss = []\n    train_accs = []\n    # Iterate the training set by batches.\n    for batch in tqdm(train_loader):\n        # A batch consists of image data and corresponding labels.\n        imgs, labels = batch\n        imgs = imgs.to(device)\n        labels = labels.to(device)\n        # Forward the data. (Make sure data and model are on the same device.)\n        logits = model(imgs)\n        # Calculate the cross-entropy loss.\n        # We don't need to apply softmax before computing cross-entropy as it is done automatically.\n        loss = criterion(logits, labels)\n        \n        # Gradients stored in the parameters in the previous step should be cleared out first.\n        optimizer.zero_grad()\n        # Compute the gradients for parameters.\n        loss.backward()\n        # Update the parameters with computed gradients.\n        optimizer.step()\n        \n        # Compute the accuracy for current batch.\n        acc = (logits.argmax(dim=-1) == labels).float().mean()\n\n        # Record the loss and accuracy.\n        train_loss.append(loss.item())\n        train_accs.append(acc)\n        \n    # The average loss and accuracy of the training set is the average of the recorded values.\n    train_loss = sum(train_loss) / len(train_loss)\n    train_acc = sum(train_accs) / len(train_accs)\n\n    # Print the information.\n    print(f\"[ Train | {epoch + 1:03d}/{n_epochs:03d} ] loss = {train_loss:.5f}, acc = {train_acc:.5f}\")\n    \n    \n    # ---------- Validation ----------\n    # Make sure the model is in eval mode so that some modules like dropout are disabled and work normally.\n    model.eval()\n    # These are used to record information in validation.\n    valid_loss = []\n    valid_accs = []\n    \n    # Iterate the validation set by batches.\n    for batch in tqdm(val_loader):\n        imgs, labels = batch\n        # We don't need gradient in validation.\n        # Using torch.no_grad() accelerates the forward process.\n        with torch.no_grad():\n            logits = model(imgs.to(device))\n            \n        # We can still compute the loss (but not the gradient).\n        loss = criterion(logits, labels.to(device))\n\n        # Compute the accuracy for current batch.\n        acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()\n\n        # Record the loss and accuracy.\n        valid_loss.append(loss.item())\n        valid_accs.append(acc)\n        \n    # The average loss and accuracy for entire validation set is the average of the recorded values.\n    valid_loss = sum(valid_loss) / len(valid_loss)\n    valid_acc = sum(valid_accs) / len(valid_accs)\n\n    # Print the information.\n    print(f\"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}\")\n    \n    # if the model improves, save a checkpoint at this epoch\n    if valid_acc > best_acc:\n        best_acc = valid_acc\n        torch.save(model.state_dict(), model_path)\n        print('saving model with acc {:.3f}'.format(best_acc))","metadata":{"execution":{"iopub.status.busy":"2024-05-03T11:56:23.697359Z","iopub.execute_input":"2024-05-03T11:56:23.697711Z","iopub.status.idle":"2024-05-03T12:20:10.929510Z","shell.execute_reply.started":"2024-05-03T11:56:23.697677Z","shell.execute_reply":"2024-05-03T12:20:10.928325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"saveFileName = './submission.csv'","metadata":{"execution":{"iopub.status.busy":"2024-05-03T12:20:10.931033Z","iopub.execute_input":"2024-05-03T12:20:10.931349Z","iopub.status.idle":"2024-05-03T12:20:10.935873Z","shell.execute_reply.started":"2024-05-03T12:20:10.931312Z","shell.execute_reply":"2024-05-03T12:20:10.934848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## predict\nmodel = res_model(176)\n\n# create model and load weights from checkpoint\nmodel = model.to(device)\nmodel.load_state_dict(torch.load(model_path))\n\n# Make sure the model is in eval mode.\n# Some modules like Dropout or BatchNorm affect if the model is in training mode.\nmodel.eval()\n\n# Initialize a list to store the predictions.\npredictions = []\n# Iterate the testing set by batches.\nfor batch in tqdm(test_loader):\n    \n    imgs = batch\n    with torch.no_grad():\n        logits = model(imgs.to(device))\n    \n    # Take the class with greatest logit as prediction and record it.\n    predictions.extend(logits.argmax(dim=-1).cpu().numpy().tolist())\n\npreds = []\nfor i in predictions:\n    preds.append(num_to_class[i])\n\ntest_data = pd.read_csv(test_path)\ntest_data['label'] = pd.Series(preds)\nsubmission = pd.concat([test_data['image'], test_data['label']], axis=1)\nsubmission.to_csv(saveFileName, index=False)\nprint(\"Done!!!!!!!!!!!!!!!!!!!!!!!!!!!\")","metadata":{"execution":{"iopub.status.busy":"2024-05-03T12:20:10.937154Z","iopub.execute_input":"2024-05-03T12:20:10.937507Z","iopub.status.idle":"2024-05-03T12:20:39.166057Z","shell.execute_reply.started":"2024-05-03T12:20:10.937474Z","shell.execute_reply":"2024-05-03T12:20:39.165057Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom sklearn.metrics import confusion_matrix\nimport pandas as pd\nimport itertools\n\n\n# ... existing code for model loading and prediction ...\n\n# Get ground truth labels (assuming they're in a 'label' column)\ntrue_labels = test_data['label'].tolist()\n\n# Confusion matrix calculation\ncm = confusion_matrix(true_labels, preds)\n\n# Print confusion matrix\nprint(\"Confusion Matrix:\\n\", cm)\n\n# ... existing code for saving predictions ...\n\n# Optional: Confusion matrix visualization with matplotlib (install if needed: pip install matplotlib)\ntry:\n  import matplotlib.pyplot as plt\n\n  plt.figure(figsize=(10, 7))\n  plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)\n  plt.colorbar()\n  tick_marks = np.arange(len(num_to_class))\n  plt.xticks(tick_marks, num_to_class, rotation=45)\n  plt.yticks(tick_marks, num_to_class)\n  plt.xlabel('Predicted Label')\n  plt.ylabel('True Label')\n  plt.title('Confusion Matrix')\n\n  # Add text to each cell (optional)\n  fmt = 'd'\n  thresh = cm.max() / 2.\n  for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n      plt.text(j, i, format(cm[i, j], fmt),\n          ha=\"center\", va=\"center\",\n          color=\"white\" if cm[i, j] > thresh else \"black\")\n\n  plt.tight_layout()\n  plt.show()\nexcept ImportError:\n  print(\"Warning: matplotlib not found. Skipping confusion matrix visualization.\")\n","metadata":{"execution":{"iopub.status.busy":"2024-05-03T12:50:09.527454Z","iopub.execute_input":"2024-05-03T12:50:09.527836Z","iopub.status.idle":"2024-05-03T12:52:11.877869Z","shell.execute_reply.started":"2024-05-03T12:50:09.527804Z","shell.execute_reply":"2024-05-03T12:52:11.876729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"要是大家觉得有用的话 多多给vote哈哈哈","metadata":{}}]}