{"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":"nvidiaTeslaT4","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":"基于d2l（李沐老师的包）进行训练网络","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, SequentialSampler\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#d2l包（注意会出现冲突）\n!pip install matplotlib_inline\n!pip install d2l\nfrom d2l import torch as d2l","metadata":{"execution":{"iopub.status.busy":"2024-04-02T02:26:25.621031Z","iopub.execute_input":"2024-04-02T02:26:25.621417Z","iopub.status.idle":"2024-04-02T02:27:10.115363Z","shell.execute_reply.started":"2024-04-02T02:26:25.621343Z","shell.execute_reply":"2024-04-02T02:27:10.114357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#连接数据\nlabels_dataframe = pd.read_csv('../input/classify-leaves/train.csv')\nlabels_dataframe.head(10)","metadata":{"execution":{"iopub.status.busy":"2024-04-02T02:27:10.117186Z","iopub.execute_input":"2024-04-02T02:27:10.117502Z","iopub.status.idle":"2024-04-02T02:27:10.168446Z","shell.execute_reply.started":"2024-04-02T02:27:10.117469Z","shell.execute_reply":"2024-04-02T02:27:10.167650Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_dataframe.describe()","metadata":{"execution":{"iopub.status.busy":"2024-04-01T08:12:15.195390Z","iopub.execute_input":"2024-04-01T08:12:15.195752Z","iopub.status.idle":"2024-04-01T08:12:15.260598Z","shell.execute_reply.started":"2024-04-01T08:12:15.195723Z","shell.execute_reply":"2024-04-01T08:12:15.259669Z"},"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()+ val # x- position \n        y = p.get_y() + p.get_height()/2 #y-position\n        ax.annotate(round(val,1),(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-04-01T08:12:24.941771Z","iopub.execute_input":"2024-04-01T08:12:24.942218Z","iopub.status.idle":"2024-04-01T08:12:29.595220Z","shell.execute_reply.started":"2024-04-01T08:12:24.942174Z","shell.execute_reply":"2024-04-01T08:12:29.594290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#创建一个无序且不重复的元素集合,即集合(set)。集合是一种可变的数据类型,其中的元素是无序的,并且不允许有重复的元素。\nleaves_labels = list(set(labels_dataframe['label']))\nn_labels = len(leaves_labels)\n#创建label-taget集合\nclass_to_num = dict(zip(leaves_labels,range(176)))  #zip函数将不同元素打包成一个个元组迭代为一个可迭代的zip\n#再设置一个用于test的dict\nnum_to_class = {i:j for j, i in class_to_num.items()}\n#.items() 是dict类型的一个方法，以tuple的形式返回字典中所有的键值对（key-value pairs）\nfor i, j in class_to_num.items():\n    print(i, j)\n    break","metadata":{"execution":{"iopub.status.busy":"2024-04-02T02:49:36.704523Z","iopub.execute_input":"2024-04-02T02:49:36.704899Z","iopub.status.idle":"2024-04-02T02:49:36.713933Z","shell.execute_reply.started":"2024-04-02T02:49:36.704866Z","shell.execute_reply":"2024-04-02T02:49:36.712959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#定时复习：target就是一个数字，在计算交叉熵损失的时候自动将target对应到计算出的softmax概率tensor中，预测的时候最后也会取argmax来输出\nclass dataset_leaves(Dataset):\n    def __init__(self, csv_path, file_path, mode='train', valid_ratio=0.9, resize_height=224, resize_width=224):\n        self.resize_height = resize_height\n        self.resize_width = resize_width\n        self.file_path = file_path\n        self.mode = mode\n        self.data_info = pd.read_csv(csv_path, header=0)\n        self.data_len = len(self.data_info.index)\n        self.train_len = int(self.data_len * (1 - valid_ratio))\n\n        if self.mode == 'train':\n            self.train_img = np.asarray(self.data_info.iloc[0:self.train_len,0])\n            self.train_label = np.asarray(self.data_info.iloc[0:self.train_len,1])\n            self.image_arr = self.train_img\n            self.label_arr = self.train_label\n        elif self.mode == 'valid':\n            self.valid_img = 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_img\n            self.label_arr = self.valid_label\n        elif self.mode == 'test':\n            self.test_img = np.asarray(self.data_info.iloc[:,0])\n            self.image_arr = self.test_img\n\n        self.len_image = len(self.image_arr)\n        print(f'扫描完所有的{mode}数据集，总计{self.len_image}张图片')\n\n    def __getitem__(self, idx):\n        self.img = Image.open(self.file_path+self.image_arr[idx])  #用＋来联系，两个path都有引号难以用句子拼接\n        if self.mode == 'train':\n            trans = transforms.Compose(\n                [transforms.Resize((self.resize_height,self.resize_width)),\n                 transforms.RandomHorizontalFlip(p=0.5),\n                 transforms.RandomVerticalFlip(p=0.5),\n                 transforms.ToTensor(),\n#                  transforms.Normalize(\n#     [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n                 ])\n            self.img = trans(self.img)\n        else:\n            trans = transforms.Compose(\n                [transforms.Resize((self.resize_height,self.resize_width)),\n                 transforms.ToTensor(),\n#                  transforms.Normalize(\n#     [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n                 ])\n            self.img = trans(self.img)\n        if self.mode == 'test':\n            return self.img\n        else:\n            self.label = class_to_num[self.label_arr[idx]]\n            return self.img , self.label\n\n\n    def __call__(self, idx):\n        return self.__getitem__(idx)\n\n    def __len__(self):\n        return self.len_image\n    \ntrain_path = '../input/classify-leaves/train.csv'\ntest_path = '../input/classify-leaves/test.csv'\nfile_path = '../input/classify-leaves/'\n\ntrain_dataset = dataset_leaves(train_path,file_path)\nvalid_dataset = dataset_leaves(train_path,file_path,mode='valid')\ntest_dataset = dataset_leaves(test_path,file_path,mode='test')\n\n\n#验证数据集\nimg, label = train_dataset(0)\nprint(img)\nnum_to_class[label]","metadata":{"execution":{"iopub.status.busy":"2024-04-02T03:01:31.251739Z","iopub.execute_input":"2024-04-02T03:01:31.252069Z","iopub.status.idle":"2024-04-02T03:01:31.327921Z","shell.execute_reply.started":"2024-04-02T03:01:31.252042Z","shell.execute_reply":"2024-04-02T03:01:31.327065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#获取训练网络结构并微调\npretrained_net = models.resnet18(pretrained=True)\npretrained_net.fc  #查看顶层输出","metadata":{"execution":{"iopub.status.busy":"2024-04-02T02:50:14.386157Z","iopub.execute_input":"2024-04-02T02:50:14.386729Z","iopub.status.idle":"2024-04-02T02:50:14.733767Z","shell.execute_reply.started":"2024-04-02T02:50:14.386644Z","shell.execute_reply":"2024-04-02T02:50:14.732760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#改变顶层\nclassify_leaves_net = pretrained_net\nclassify_leaves_net.fc = nn.Linear(classify_leaves_net.fc.in_features, 176)  #随机初始化为一个线性层输出176类树叶种类\nnn.init.xavier_uniform_(classify_leaves_net.fc.weight)","metadata":{"execution":{"iopub.status.busy":"2024-04-02T02:50:18.244704Z","iopub.execute_input":"2024-04-02T02:50:18.245078Z","iopub.status.idle":"2024-04-02T02:50:18.259412Z","shell.execute_reply.started":"2024-04-02T02:50:18.245046Z","shell.execute_reply":"2024-04-02T02:50:18.258340Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#微调模型\ndef train_mymode(net,train_dataset,valid_dataset,learning_rate,batch_size,num_epoch, param_group=True):\n    train_iter = DataLoader(train_dataset,batch_size=batch_size,shuffle=True,num_workers=4)\n    valid_iter = DataLoader(valid_dataset,batch_size= batch_size,shuffle=True,num_workers=4)\n    devices = d2l.try_all_gpus()\n    loss = nn.CrossEntropyLoss(reduction=\"none\")\n    if param_group:\n        params_1x = [param for name, param in net.named_parameters()\n                     if name not in [\"fc.weight\", \"fc.bias\"]]\n        trainer = torch.optim.Adam([{'params': params_1x},\n                                   {'params': net.fc.parameters(),\n                                    'lr': learning_rate * 10}],  # 最后一层用十倍的learning rate(lr调的很低，其实应该说模型其他参数学习率很低)\n                                  lr=learning_rate, weight_decay=0.001)\n\n    else:\n        trainer = torch.optim.Adam(net.parameters(), lr=learning_rate,\n                                  weight_decay=0.001)\n\n    net = nn.DataParallel(net, device_ids=devices).to(devices[0])\n\n    timer = d2l.Timer()\n    animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epoch], ylim=[0, 1],\n                            legend=['train loss', 'train acc', 'vaild acc'])\n    for epoch in range(num_epoch):\n        net.train()\n        timer.start()\n        metric = d2l.Accumulator(4)\n        for i, (imgs, labels) in enumerate(train_iter):\n            num_batches =len(train_iter)\n            imgs, labels = imgs.to(devices[0]), labels.to(devices[0])\n            trainer.zero_grad()\n            pred = net(imgs)\n            l = loss(pred, labels)\n            l.sum().backward()\n            trainer.step()\n            l = l.sum()\n            acc = d2l.accuracy(pred, labels)\n            metric.add(l, acc, labels.shape[0], labels.numel())\n            timer.stop()\n            if (i + 1) % (batch_size // 5) == 0 or i == num_batches - 1:\n                animator.add(epoch + (i + 1) / num_batches,\n                         (metric[0] / metric[2], metric[1] / metric[3],\n                          None))\n\n        valid_acc = d2l.evaluate_accuracy_gpu(net, valid_iter)\n        animator.add(epoch + 1, (None, None, valid_acc))\n    print(f'loss {metric[0] / metric[2]:.3f}, train acc '\n          f'{metric[1] / metric[3]:.3f}, valid acc {valid_acc:.3f}')\n    print(f'{metric[2] * num_epoch / timer.sum():.1f} examples/sec on '\n          f'{str(devices)}')","metadata":{"execution":{"iopub.status.busy":"2024-04-02T02:50:23.249205Z","iopub.execute_input":"2024-04-02T02:50:23.249590Z","iopub.status.idle":"2024-04-02T02:50:23.265373Z","shell.execute_reply.started":"2024-04-02T02:50:23.249558Z","shell.execute_reply":"2024-04-02T02:50:23.264486Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_mymode(net=classify_leaves_net,train_dataset=train_dataset,valid_dataset = valid_dataset,learning_rate=1e-4,batch_size=128,num_epoch=10, param_group=True)","metadata":{"execution":{"iopub.status.busy":"2024-04-02T02:50:28.662189Z","iopub.execute_input":"2024-04-02T02:50:28.662570Z","iopub.status.idle":"2024-04-02T02:58:42.460860Z","shell.execute_reply.started":"2024-04-02T02:50:28.662539Z","shell.execute_reply":"2024-04-02T02:58:42.459686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_to_class","metadata":{"execution":{"iopub.status.busy":"2024-04-02T03:01:41.193286Z","iopub.execute_input":"2024-04-02T03:01:41.193683Z","iopub.status.idle":"2024-04-02T03:01:41.205078Z","shell.execute_reply.started":"2024-04-02T03:01:41.193644Z","shell.execute_reply":"2024-04-02T03:01:41.204188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 超参数分享,保存模型参数\n# learning_rate =  1e-4\n# weight_decay = 1e-3  \n# num_epoch =   10\n# torch.save(classify_leaves_net.state_dict(),'./classify_net.pth') #保存模型\nsequential_sampler = SequentialSampler(train_dataset)  #sequentialsampler用于顺序提取，判断测试\nnet=classify_leaves_net\ndevice = 'cuda:0'\nnet = net.to(device)\nnet.eval()\npreds = []\nfor i ,j in DataLoader(train_dataset,sampler=sequential_sampler,batch_size=1,shuffle = False):\n    with torch.no_grad():\n        img = i\n        img = img.cuda()\n        output = net(img)\n        output = output.argmax(1)\n        preds.append(output.cpu().numpy())\n        \npreds_class=[]\nfor i in preds:\n    preds_class.append(num_to_class[int(i)])\npreds_class\n\n\n\n\n","metadata":{"execution":{"iopub.status.busy":"2024-04-02T03:01:48.388891Z","iopub.execute_input":"2024-04-02T03:01:48.389268Z","iopub.status.idle":"2024-04-02T03:02:12.042820Z","shell.execute_reply.started":"2024-04-02T03:01:48.389230Z","shell.execute_reply":"2024-04-02T03:02:12.041945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install numpy\n!pip install pandas\nimport numpy\nimport pandas","metadata":{"execution":{"iopub.status.busy":"2024-04-02T03:04:15.867922Z","iopub.execute_input":"2024-04-02T03:04:15.868316Z","iopub.status.idle":"2024-04-02T03:04:29.969336Z","shell.execute_reply.started":"2024-04-02T03:04:15.868279Z","shell.execute_reply":"2024-04-02T03:04:29.968372Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nsequential_sampler = SequentialSampler(test_dataset)  #sequentialsampler用于顺序提取\nnet=classify_leaves_net\ndevice = 'cuda:0'\nnet = net.to(device)\nnet.eval()\npreds = []\nfor i  in DataLoader(test_dataset,sampler=sequential_sampler,batch_size=1,shuffle = False):\n    with torch.no_grad():\n        img = i\n        img = img.cuda()\n        output = net(img)\n        output = output.argmax(1)\n        preds.append(output.cpu().numpy())\n        \npreds_class=[]\nfor i in preds:\n    preds_class.append(num_to_class[int(i)])\n\npreds_class\n\n","metadata":{"execution":{"iopub.status.busy":"2024-04-02T03:06:27.673800Z","iopub.execute_input":"2024-04-02T03:06:27.674170Z","iopub.status.idle":"2024-04-02T03:09:42.915158Z","shell.execute_reply.started":"2024-04-02T03:06:27.674138Z","shell.execute_reply":"2024-04-02T03:09:42.914271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"torch.save(preds_class, 'preds_class')#保存预测数据在本地操作concat","metadata":{"execution":{"iopub.status.busy":"2024-04-02T03:12:01.013130Z","iopub.execute_input":"2024-04-02T03:12:01.013518Z","iopub.status.idle":"2024-04-02T03:12:01.023703Z","shell.execute_reply.started":"2024-04-02T03:12:01.013481Z","shell.execute_reply":"2024-04-02T03:12:01.022675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#读取数据添加后再返回为csv（不能直接操作csv）\nsaveFileName='./submission.csv'\n\ntest_data = pd.read_csv(test_path)\ntest_data['label'] = pd.Series(preds_class)\nsubmission = pd.concat([test_data['image'], test_data['label']], axis=1)\nsubmission.to_csv(saveFileName, index=False)\noutput_final = pd.read_csv(saveFileName)\noutput_final.head(10)  #输出判断是否符合要求  这里安装了d2l后有些问题无法输出可以重新pip pandas或者导入dict到本地","metadata":{"execution":{"iopub.status.busy":"2024-04-02T03:10:53.886303Z","iopub.execute_input":"2024-04-02T03:10:53.886651Z","iopub.status.idle":"2024-04-02T03:10:53.934562Z","shell.execute_reply.started":"2024-04-02T03:10:53.886620Z","shell.execute_reply":"2024-04-02T03:10:53.932908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#后续二次操作：加载模型\ntest_net = models.resnet18()\ntest_net.fc = nn.Linear(test_net.fc.in_features, 176)\ntest_net.load_state_dict(torch.load('./resnet18.pth'))\n","metadata":{"execution":{"iopub.status.busy":"2024-04-01T13:10:33.552818Z","iopub.execute_input":"2024-04-01T13:10:33.553249Z","iopub.status.idle":"2024-04-01T13:10:33.830077Z","shell.execute_reply.started":"2024-04-01T13:10:33.553209Z","shell.execute_reply":"2024-04-01T13:10:33.827997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#开始预测\nnet=classify_leaves_net\n#device = 'cuda:0'\n# net = net.to(device)\nsequential_sampler = SequentialSampler(test_dataset)  #sequentialsampler用于特定顺序提取（可以通过对dataset操作改变顺序e.g:sorted这里要顺序）\nnet=test_net\nnet.eval()\npreds = []\nfor i in DataLoader(test_dataset,batch_size=1,shuffle=False,sampler=sequential_sampler):\n    with torch.no_grad():\n        img = i\n        img = img.cuda()\n        output = net(img)\n        output = output.argmax(1)\n        preds.append(output.cpu().numpy())\n# preds","metadata":{"execution":{"iopub.status.busy":"2024-04-01T10:05:38.266698Z","iopub.execute_input":"2024-04-01T10:05:38.267057Z","iopub.status.idle":"2024-04-01T10:07:40.744593Z","shell.execute_reply.started":"2024-04-01T10:05:38.267027Z","shell.execute_reply":"2024-04-01T10:07:40.743708Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds_class=[]\nfor i in preds:\n    preds_class.append(num_to_class[int(i)])\npreds_class\n\n#读取数据添加后再返回为csv（不能直接操作csv）\nsaveFileName='./submission.csv'\n\ntest_data = pd.read_csv(test_path)\ntest_data['label'] = pd.Series(preds_class)\nsubmission = pd.concat([test_data['image'], test_data['label']], axis=1)\nsubmission #这里没法转换为csv文件，应该是安装d2l不兼容问题，可以在自己电脑用已训练的参数来完成后续操作","metadata":{"execution":{"iopub.status.busy":"2024-04-01T10:24:39.687843Z","iopub.execute_input":"2024-04-01T10:24:39.688217Z","iopub.status.idle":"2024-04-01T10:24:39.738705Z","shell.execute_reply.started":"2024-04-01T10:24:39.688186Z","shell.execute_reply":"2024-04-01T10:24:39.737826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"结束,感谢NEKO KIKU的开源代码，学习到了很多！","metadata":{}}]}