{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"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":"2022-03-01T06:40:50.469239Z","iopub.execute_input":"2022-03-01T06:40:50.469635Z","iopub.status.idle":"2022-03-01T06:40:50.476991Z","shell.execute_reply.started":"2022-03-01T06:40:50.469596Z","shell.execute_reply":"2022-03-01T06:40:50.475600Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 看看label文件长啥样\nlabels_dataframe = pd.read_csv('../input/classify-leaves/train.csv')\n# labels_dataframe.head(5)","metadata":{"execution":{"iopub.status.busy":"2022-03-01T06:15:21.007247Z","iopub.execute_input":"2022-03-01T06:15:21.008223Z","iopub.status.idle":"2022-03-01T06:15:21.042022Z","shell.execute_reply.started":"2022-03-01T06:15:21.008174Z","shell.execute_reply":"2022-03-01T06:15:21.040988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_dataframe.describe()","metadata":{"execution":{"iopub.status.busy":"2022-03-01T06:15:21.045828Z","iopub.execute_input":"2022-03-01T06:15:21.046155Z","iopub.status.idle":"2022-03-01T06:15:21.107009Z","shell.execute_reply.started":"2022-03-01T06:15:21.046124Z","shell.execute_reply":"2022-03-01T06:15:21.105742Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 把label文件排个序\n# set() 函数创建一个无序不重复元素集\n# list() 创建列表\nleaves_labels = sorted(list(set(labels_dataframe['label'])))\nn_classes = len(leaves_labels)\nprint(n_classes)\nleaves_labels[:10]","metadata":{"execution":{"iopub.status.busy":"2022-03-01T06:40:54.863135Z","iopub.execute_input":"2022-03-01T06:40:54.863548Z","iopub.status.idle":"2022-03-01T06:40:54.875212Z","shell.execute_reply.started":"2022-03-01T06:40:54.863516Z","shell.execute_reply":"2022-03-01T06:40:54.873696Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 把label转成对应的数字\nclass_to_num = dict(zip(leaves_labels, range(n_classes)))\n# class_to_num","metadata":{"execution":{"iopub.status.busy":"2022-03-01T06:40:58.823507Z","iopub.execute_input":"2022-03-01T06:40:58.823863Z","iopub.status.idle":"2022-03-01T06:40:58.829313Z","shell.execute_reply.started":"2022-03-01T06:40:58.823832Z","shell.execute_reply":"2022-03-01T06:40:58.827614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 再转换回来，方便最后预测的时候使用\nnum_to_class = {v : k for k, v in class_to_num.items()}\n# num_to_class","metadata":{"execution":{"iopub.status.busy":"2022-03-01T06:41:00.247215Z","iopub.execute_input":"2022-03-01T06:41:00.247592Z","iopub.status.idle":"2022-03-01T06:41:00.253137Z","shell.execute_reply.started":"2022-03-01T06:41:00.247545Z","shell.execute_reply":"2022-03-01T06:41:00.251824Z"},"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.RandomResizedCrop((224,224), scale=(0.8, 1), ratio=(0.8, 1.2)), #随机剪裁\n                # transforms.transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5), #颜色亮度色调\n                # transforms.Resize((224, 224)),\n                transforms.RandomHorizontalFlip(p=0.5),   #随机水平翻转 选择一个概率\n                transforms.RandomVerticalFlip(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":"2022-03-01T06:41:44.053684Z","iopub.execute_input":"2022-03-01T06:41:44.054161Z","iopub.status.idle":"2022-03-01T06:41:44.071763Z","shell.execute_reply.started":"2022-03-01T06:41:44.054114Z","shell.execute_reply":"2022-03-01T06:41:44.070364Z"},"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":"2022-03-01T06:41:53.846680Z","iopub.execute_input":"2022-03-01T06:41:53.847130Z","iopub.status.idle":"2022-03-01T06:41:53.902663Z","shell.execute_reply.started":"2022-03-01T06:41:53.847099Z","shell.execute_reply":"2022-03-01T06:41:53.901606Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"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=16, \n        shuffle=False,\n        num_workers=5\n    )\n\nval_loader = torch.utils.data.DataLoader(\n        dataset=val_dataset,\n        batch_size=16, \n        shuffle=False,\n        num_workers=5\n    )\ntest_loader = torch.utils.data.DataLoader(\n        dataset=test_dataset,\n        batch_size=16, \n        shuffle=False,\n        num_workers=5\n    )","metadata":{"execution":{"iopub.status.busy":"2022-03-01T06:42:02.599771Z","iopub.execute_input":"2022-03-01T06:42:02.600184Z","iopub.status.idle":"2022-03-01T06:42:02.610016Z","shell.execute_reply.started":"2022-03-01T06:42:02.600151Z","shell.execute_reply":"2022-03-01T06:42:02.608687Z"},"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":"2022-03-01T06:42:07.803371Z","iopub.execute_input":"2022-03-01T06:42:07.803728Z","iopub.status.idle":"2022-03-01T06:42:07.810432Z","shell.execute_reply.started":"2022-03-01T06:42:07.803696Z","shell.execute_reply":"2022-03-01T06:42:07.808939Z"},"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 i, param in enumerate(model.children()):\n#             if i == 8:\n#                 break\n#             param.requires_grad = False\n    if feature_extracting:\n        model = model\n        for param in model.parameters():\n            param.requires_grad = False\n            \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(\n#         nn.Linear(num_ftrs, 512),\n#         nn.ReLU(inplace=True),\n#         nn.Dropout(.3),\n#         nn.Linear(512, len(num_to_class))\n#     )\n    model_ft.fc = nn.Sequential(\n        nn.Linear(num_ftrs, num_classes)\n    )\n    return model_ft\n\n# resnext50模型\ndef resnext_model(num_classes, feature_extract = False, use_pretrained=True):\n\n    model_ft = models.resnext50_32x4d(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":"2022-03-01T06:49:37.787879Z","iopub.execute_input":"2022-03-01T06:49:37.788273Z","iopub.status.idle":"2022-03-01T06:49:37.797165Z","shell.execute_reply.started":"2022-03-01T06:49:37.788225Z","shell.execute_reply":"2022-03-01T06:49:37.795507Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 超参数\nlearning_rate = 3e-4\nweight_decay = 1e-3\nnum_epoch = 50\nmodel_path = '../input/pre-resnext-model/pre_resnext_model .ckpt'","metadata":{"execution":{"iopub.status.busy":"2022-03-01T06:49:48.693620Z","iopub.execute_input":"2022-03-01T06:49:48.694115Z","iopub.status.idle":"2022-03-01T06:49:48.699018Z","shell.execute_reply.started":"2022-03-01T06:49:48.694082Z","shell.execute_reply":"2022-03-01T06:49:48.697586Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n# # Initialize a model, and put it on the device specified.\n# #model = res_model(176)\n# model = resnext_model(176)\n# model = model.to(device)\n# model.device = device\n# # For the classification task, we use cross-entropy as the measurement of performance.\n# criterion = nn.CrossEntropyLoss()\n\n# # Initialize optimizer, you may fine-tune some hyperparameters such as learning rate on your own.\n# # optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr = learning_rate, weight_decay=weight_decay)\n# optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate, weight_decay=weight_decay)\n# scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10, eta_min=0, last_epoch=-1)\n\n# # The number of training epochs.\n# n_epochs = num_epoch\n\n# best_acc = 0.0\n# for 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#     i = 0\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#         scheduler.step()\n#         if(i % 500 == 0):\n#             print(\"learning_rate:\", scheduler.get_last_lr()[0])\n#         i = i + 1\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\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#     # ---------- 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":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"saveFileName = './submission.csv'","metadata":{"execution":{"iopub.status.busy":"2022-03-01T06:15:21.616838Z","iopub.execute_input":"2022-03-01T06:15:21.617629Z","iopub.status.idle":"2022-03-01T06:15:21.626663Z","shell.execute_reply.started":"2022-03-01T06:15:21.617585Z","shell.execute_reply":"2022-03-01T06:15:21.625391Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## predict\n#model = res_model(176)\nmodel = resnext_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":"2022-03-01T11:04:06.223062Z","iopub.execute_input":"2022-03-01T11:04:06.223472Z","iopub.status.idle":"2022-03-01T11:04:40.385606Z","shell.execute_reply.started":"2022-03-01T11:04:06.223437Z","shell.execute_reply":"2022-03-01T11:04:40.384306Z"},"trusted":true},"execution_count":null,"outputs":[]}]}