{"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":"markdown","source":"Private Leaderboard 13th\n\n作为一个零基础跟李沐老师学过来的初学者，首先感谢沐神提供了一个方便像我这样初学者入门的优质教程\n\n其次重点感谢Neko Kiku同学提供的baseline，以及讨论区的各位同学提供的技巧。\n\n注：由于我是在线上训练好多个模型保存到本地共同做的预测，直接放在一起运行好像会因为连续运行时间超过10小时而自动取消运算，所以这里我注释掉了训练模型的函数，直接加载已经训练好的模型进行预测。","metadata":{}},{"cell_type":"code","source":"pip install ttach #Test Time Augmentation","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:12:19.421359Z","iopub.execute_input":"2021-06-28T07:12:19.421686Z","iopub.status.idle":"2021-06-28T07:12:25.076461Z","shell.execute_reply.started":"2021-06-28T07:12:19.421656Z","shell.execute_reply":"2021-06-28T07:12:25.075503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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 ttach as tta\nimport os\nimport matplotlib.pyplot as plt\nimport torchvision.models as models\n# This is for the progress bar.\nfrom tqdm import tqdm","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:12:25.078471Z","iopub.execute_input":"2021-06-28T07:12:25.078849Z","iopub.status.idle":"2021-06-28T07:12:25.084752Z","shell.execute_reply.started":"2021-06-28T07:12:25.078796Z","shell.execute_reply":"2021-06-28T07:12:25.083572Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 看看label文件长啥样\nlabels_dataframe = pd.read_csv('../input/classify-leaves/train.csv')\nlabels_dataframe.head(3)","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:12:25.086681Z","iopub.execute_input":"2021-06-28T07:12:25.087057Z","iopub.status.idle":"2021-06-28T07:12:25.122645Z","shell.execute_reply.started":"2021-06-28T07:12:25.086979Z","shell.execute_reply":"2021-06-28T07:12:25.121912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 提取出lable并统计类别个数 排序\nleaves_labels = sorted(list(set(labels_dataframe['label']))) #set删掉重复的 sort排序\nn_classes = len(leaves_labels) #类别长度\nprint(n_classes) ","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:12:27.374259Z","iopub.execute_input":"2021-06-28T07:12:27.374613Z","iopub.status.idle":"2021-06-28T07:12:27.381235Z","shell.execute_reply.started":"2021-06-28T07:12:27.374584Z","shell.execute_reply":"2021-06-28T07:12:27.380443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 把label转成对应的数字\nclass_to_num = dict(zip(leaves_labels, range(n_classes)))\n# 再转换回来，方便最后预测的时候使用\nnum_to_class = {v : k for k, v in class_to_num.items()}","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:12:28.561680Z","iopub.execute_input":"2021-06-28T07:12:28.562001Z","iopub.status.idle":"2021-06-28T07:12:28.568292Z","shell.execute_reply.started":"2021-06-28T07:12:28.561971Z","shell.execute_reply":"2021-06-28T07:12:28.566204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 继承pytorch的dataset，创建自己的Data\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       #所以需要-1\n        self.train_len = int(self.data_len * (1 - valid_ratio))\n        \n        if mode == 'train':\n            # 第一列包含图像文件的名称   例如images/0.jpg\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       例如maclura_pomifera  （叶子种类）\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]  #self.image_arr[0]='images/0.jpg'\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(300),\n                #transforms.CenterCrop(224),\n                transforms.Resize(224),\n                transforms.RandomHorizontalFlip(p=0.5),   #随机水平翻转\n                transforms.RandomVerticalFlip(p=0.5),     #除了水平竖直反转之外其他的处理方法貌似都会降低acc\n                #transforms.RandomResizedCrop((224, 224), scale=(0.7, 1)),\n                #transforms.RandomCrop((60, 120)), # 随机剪裁\n                # transforms.ColorJitter(0.3, 0.3, 0.2), # 修改亮度、对比度和饱和度\n                #transforms.RandomRotation(180), # 依degrees 随机旋转一定角度   10\n                transforms.ToTensor(),\n                transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n                # Normalize(mean, std)按通道进行标准化，即先减均值，再除以标准差std\n                 ])\n        else:\n            # valid和test不做数据增强  只需要裁剪变成张量Tensor\n            transform = transforms.Compose([\n                transforms.Resize(224),\n                #transforms.CenterCrop(224),\n                transforms.ToTensor(),\n                transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\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]   #例子self.label_arr[0] = maclura_pomifera\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  #self.real_len = len(self.image_arr) 返回的是训练/验证/测试/图像的数量","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:12:30.017057Z","iopub.execute_input":"2021-06-28T07:12:30.017385Z","iopub.status.idle":"2021-06-28T07:12:30.032110Z","shell.execute_reply.started":"2021-06-28T07:12:30.017354Z","shell.execute_reply":"2021-06-28T07:12:30.031032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#设置文件路径并得到数据集\ntrain_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":"2021-06-28T07:12:33.965615Z","iopub.execute_input":"2021-06-28T07:12:33.965941Z","iopub.status.idle":"2021-06-28T07:12:34.011761Z","shell.execute_reply.started":"2021-06-28T07:12:33.965910Z","shell.execute_reply":"2021-06-28T07:12:34.010749Z"},"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=90,      \n        shuffle=True,     #打开乱序  False\n        num_workers=0\n    )\n\nval_loader = torch.utils.data.DataLoader(\n        dataset=val_dataset,\n        batch_size=90,   \n        shuffle=True,    #打开乱序  False\n        num_workers=0\n    )\ntest_loader = torch.utils.data.DataLoader(\n        dataset=test_dataset,\n        batch_size=90, \n        shuffle=False,\n        num_workers=0\n    )","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:12:37.885514Z","iopub.execute_input":"2021-06-28T07:12:37.885827Z","iopub.status.idle":"2021-06-28T07:12:37.894070Z","shell.execute_reply.started":"2021-06-28T07:12:37.885797Z","shell.execute_reply":"2021-06-28T07:12:37.892973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 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":"2021-06-28T07:12:43.433782Z","iopub.execute_input":"2021-06-28T07:12:43.434115Z","iopub.status.idle":"2021-06-28T07:12:43.439267Z","shell.execute_reply.started":"2021-06-28T07:12:43.434083Z","shell.execute_reply":"2021-06-28T07:12:43.438213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 超参数\nlearning_rate = 1e-4   #1e-4\nweight_decay = 1e-3\nnum_epoch = 50\nbeta = 1              #cutmix参数\nmodel_path = './pre_res_model.ckpt' #保存中间模型数据，方便加载","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:12:59.030656Z","iopub.execute_input":"2021-06-28T07:12:59.030982Z","iopub.status.idle":"2021-06-28T07:12:59.036295Z","shell.execute_reply.started":"2021-06-28T07:12:59.030950Z","shell.execute_reply":"2021-06-28T07:12:59.035084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#微调学习率 \ndef train_fine_tuning(net,learning_rate,param_group=True):\n    if param_group:\n        params_1x = [\n            param for name, param in net.named_parameters()\n            if name not in [\"fc.weight\", \"fc.bias\"]]\n        optimizer = torch.optim.Adam([{\n            'params': params_1x}, {\n                'params': net.fc.parameters(),\n                'lr': learning_rate * 10}], lr=learning_rate,  #10\n                                    weight_decay=0.001)\n    else:\n        optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate* 0.2,\n                                      weight_decay=0.001)  \n    return optimizer","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:13:00.538947Z","iopub.execute_input":"2021-06-28T07:13:00.539299Z","iopub.status.idle":"2021-06-28T07:13:00.546768Z","shell.execute_reply.started":"2021-06-28T07:13:00.539264Z","shell.execute_reply":"2021-06-28T07:13:00.545718Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install timm\n!pip install torchinfo\nimport timm                     #timm库有更丰富的预训练模型\nfrom torchinfo import summary","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:13:02.435626Z","iopub.execute_input":"2021-06-28T07:13:02.435938Z","iopub.status.idle":"2021-06-28T07:13:13.561201Z","shell.execute_reply.started":"2021-06-28T07:13:02.435909Z","shell.execute_reply":"2021-06-28T07:13:13.560300Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"这里选择同时训练三个模型，一个seresnext50，两个resnext50","metadata":{}},{"cell_type":"code","source":"model_1 = timm.create_model('seresnext50_32x4d', pretrained=True)\nmodel_1.fc = nn.Linear(model_1.fc.in_features, 176)\nnn.init.xavier_uniform_(model_1.fc.weight);\nmodel_1 = model_1.to(device) #GPU\nmodel_1.device = device","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:13:13.563046Z","iopub.execute_input":"2021-06-28T07:13:13.563440Z","iopub.status.idle":"2021-06-28T07:13:14.199610Z","shell.execute_reply.started":"2021-06-28T07:13:13.563401Z","shell.execute_reply":"2021-06-28T07:13:14.198729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_2 = models.resnext50_32x4d(pretrained=True)\nmodel_2.fc = nn.Linear(model_2.fc.in_features, 176)\nnn.init.xavier_uniform_(model_2.fc.weight);\nmodel_2 = model_2.to(device) #GPU\nmodel_2.device = device","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:13:14.201374Z","iopub.execute_input":"2021-06-28T07:13:14.201722Z","iopub.status.idle":"2021-06-28T07:13:14.720669Z","shell.execute_reply.started":"2021-06-28T07:13:14.201687Z","shell.execute_reply":"2021-06-28T07:13:14.719765Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_3 = models.resnext50_32x4d(pretrained=True)\nmodel_3.fc = nn.Linear(model_3.fc.in_features, 176)\nnn.init.xavier_uniform_(model_3.fc.weight);\nmodel_3 = model_3.to(device) #GPU\nmodel_3.device = device","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:13:14.722211Z","iopub.execute_input":"2021-06-28T07:13:14.722583Z","iopub.status.idle":"2021-06-28T07:13:15.272463Z","shell.execute_reply.started":"2021-06-28T07:13:14.722547Z","shell.execute_reply":"2021-06-28T07:13:15.271472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#cutmix计算裁剪区域\ndef rand_bbox(size, lamb):  #计算自定义裁剪区域\n    W = size[2]\n    H = size[3]\n    cut_rat = np.sqrt(1. - lamb)\n    cut_w = np.int(W * cut_rat)\n    cut_h = np.int(H * cut_rat)\n\n    # uniform\n    cx = np.random.randint(W)\n    cy = np.random.randint(H)\n\n    bbx1 = np.clip(cx - cut_w // 2, 0, W)\n    bby1 = np.clip(cy - cut_h // 2, 0, H)\n    bbx2 = np.clip(cx + cut_w // 2, 0, W)\n    bby2 = np.clip(cy + cut_h // 2, 0, H)\n\n    return bbx1, bby1, bbx2, bby2","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:13:19.329619Z","iopub.execute_input":"2021-06-28T07:13:19.329946Z","iopub.status.idle":"2021-06-28T07:13:19.339641Z","shell.execute_reply.started":"2021-06-28T07:13:19.329915Z","shell.execute_reply":"2021-06-28T07:13:19.338628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"训练函数这里采用了lr_scheduler改变学习率\n\n个人感觉ReduceLROnPlateau相对而言更加方便一些","metadata":{}},{"cell_type":"code","source":"#训练函数\ndef train_2(model): \n    optimizer = train_fine_tuning(model, learning_rate)\n    # 对于分类任务，我们使用交叉熵作为性能的度量。\n    criterion = nn.CrossEntropyLoss()\n    # 定义学习率衰减\n    #scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1) #按照epoch衰减  不好用\n    #scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=40,eta_min=0.00000001)  #余弦变化\n    scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=3, verbose=True, min_lr=0.0000001)\n    # 初始化优化器，您可以自行微调一些超参数，如学习速率。此处用的Adam\n    #optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate, weight_decay=weight_decay)\n    \n    # 训练次数\n    n_epochs = num_epoch\n    \n    best_acc = 0.0\n    for epoch in range(n_epochs):\n        # ---------- Training ----------  以下是训练模型\n        # 训练前确保模型处于训练模式。\n        model.train() \n        # 记录训练信息\n        train_loss = []\n        train_accs = []\n        # 按批迭代训练集。\n        for batch in tqdm(train_loader):\n            # batch由图像数据和相应的标签组成。\n            imgs, labels = batch\n            imgs = imgs.to(device)    #数据移动到GPU\n            labels = labels.to(device)\n            \n            #图片裁剪 CUTMIX训练代码\n            lam = np.random.beta(beta, beta) #生成随机裁剪权重\n            rand_index = torch.randperm(imgs.size()[0]).to(device) #打乱样本生成拼接样本\n            labels_a = labels  #正常样本标签\n            labels_b = labels[rand_index]  #乱序样本标签\n            bbx1, bby1, bbx2, bby2 = rand_bbox(imgs.size(), lam) #生成裁剪区域\n            #将原样本中bbx1:bbx2, bby1:bby2区域改成乱序样本标签对应的区域\n            imgs[:, :, bbx1:bbx2, bby1:bby2] = imgs[rand_index, :, bbx1:bbx2, bby1:bby2]\n            #重新计算lambda以精确匹配像素比率（因为有可能裁剪超出边界）\n            lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (imgs.size()[-1] * imgs.size()[-2]))\n            # Forward the data. (Make sure data and model are on the same device.)\n            logits = model(imgs)  #将图形数据带入模型计算预测\n            # 计算交叉熵损失(注意是两个样本的损失按照分割比例加权求和)\n            loss = criterion(logits, labels_a) * lam + criterion(logits, labels_b) * (1. - lam)\n            \n            \n            # 清除上一步中存储在参数中的梯度。\n            optimizer.zero_grad()\n            # 计算参数的梯度。\n            loss.backward()\n            # 用计算的梯度更新参数。\n            optimizer.step()\n        \n            # 计算当前批次的精度。\n            acc = (logits.argmax(dim=-1) == labels).float().mean()\n    \n            # 记录损失和准确度\n            train_loss.append(loss.item())\n            train_accs.append(acc)\n            \n                \n            \n        # 训练集的平均损失和精度是记录值的平均值。\n        train_loss = sum(train_loss) / len(train_loss)\n        train_acc = sum(train_accs) / len(train_accs)\n        \n        \n        #更新学习率\n        print(\"第%d个epoch的学习率：%f\" % (epoch, optimizer.param_groups[0]['lr']))\n        scheduler.step(train_loss)\n        \n        \n        # 打印信息\n        print(f\"[ Train | {epoch + 1:03d}/{n_epochs:03d} ] loss = {train_loss:.5f}, acc = {train_acc:.5f}\")\n            \n            \n        # ---------- 验证 ----------\n        #这里我认为在验证处也加上TTA也许更加合理，因为前面对图像进行了变换，\n        #在验证的时候也将图像进行相对应的变换可能能够提取更多的特征。\n        # 确保模型处于eval模式，以便禁用dropout等模块并正常工作。\n        model.eval()\n        # 这些用于记录验证中的信息\n        valid_loss = []\n        valid_accs = []\n        \n        # 逐批迭代验证集。\n        for batch in tqdm(val_loader):\n            imgs, labels = batch\n            # 不需要梯度验证.\n            # Using  torch.no_grad()  accelerates the forward process.\n            with torch.no_grad():\n                logits = model(imgs.to(device))\n                  \n            # 我们仍然可以计算损失（但不能计算梯度）。\n            loss = criterion(logits, labels.to(device))\n    \n            # 计算当前批次的精度。\n            acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()\n    \n            # 记录损失和准确性\n            valid_loss.append(loss.item())\n            valid_accs.append(acc)\n              \n        # 整个验证集的平均损失和准确度是记录值的平均值\n        valid_loss = sum(valid_loss) / len(valid_loss)\n        valid_acc = sum(valid_accs) / len(valid_accs)\n    \n        # 打印信息.\n        print(f\"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}\")\n            \n        # 如果模型改进了，在这个时间点保存一个检查点\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":"2021-06-28T07:13:22.298787Z","iopub.execute_input":"2021-06-28T07:13:22.299108Z","iopub.status.idle":"2021-06-28T07:13:22.315821Z","shell.execute_reply.started":"2021-06-28T07:13:22.299077Z","shell.execute_reply":"2021-06-28T07:13:22.314524Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"saveFileName = './submission.csv'","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:13:27.211329Z","iopub.execute_input":"2021-06-28T07:13:27.211681Z","iopub.status.idle":"2021-06-28T07:13:27.215260Z","shell.execute_reply.started":"2021-06-28T07:13:27.211651Z","shell.execute_reply":"2021-06-28T07:13:27.214400Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#分别训练三个模型并将最优参数保存到相应的模型里\n#train_2(model_1)\n#model_1.load_state_dict(torch.load(model_path)) #加载训练结果\n#train_2(model_2)\n#model_2.load_state_dict(torch.load(model_path))\n#train_2(model_3)\n#model_3.load_state_dict(torch.load(model_path))","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:13:28.366563Z","iopub.execute_input":"2021-06-28T07:13:28.366877Z","iopub.status.idle":"2021-06-28T07:13:28.370814Z","shell.execute_reply.started":"2021-06-28T07:13:28.366847Z","shell.execute_reply":"2021-06-28T07:13:28.369872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#加载预训练模型   \nmodel_1.load_state_dict(torch.load('../input/modlesckpy/seresnext50_32x4d_981.ckpt'))\nmodel_2.load_state_dict(torch.load('../input/modlesckpy/resnext50_32x4d_982.ckpt'))\nmodel_3.load_state_dict(torch.load('../input/modlesckpy/resnext50_32x4d_981.ckpt'))","metadata":{"execution":{"iopub.status.busy":"2021-06-28T07:15:03.567908Z","iopub.execute_input":"2021-06-28T07:15:03.568268Z","iopub.status.idle":"2021-06-28T07:15:08.615721Z","shell.execute_reply.started":"2021-06-28T07:15:03.568214Z","shell.execute_reply":"2021-06-28T07:15:08.614906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 确保模型处于eval模式.\n# 一些模块如 Dropout or BatchNorm 会影响性能 如果模型处于训练模式.\nmodel_1.eval()\nmodel_2.eval()\nmodel_3.eval()\n#加载TTA\n#tta_model = tta.ClassificationTTAWrapper(model, tta.aliases.d4_transform(),  merge_mode='mean')\ntta_model_1 = tta.ClassificationTTAWrapper(model_1, tta.aliases.flip_transform(),  merge_mode='mean')\ntta_model_2 = tta.ClassificationTTAWrapper(model_2, tta.aliases.flip_transform(),  merge_mode='mean')\ntta_model_3 = tta.ClassificationTTAWrapper(model_3, tta.aliases.flip_transform(),  merge_mode='mean')\n\n# 初始化存储预测的列表。\npredictions = []\n# 逐批迭代测试集.\nfor batch in tqdm(test_loader):\n    \n    imgs = batch\n    with torch.no_grad():\n        logits_1 = tta_model_1(imgs.to(device))  #预测\n        logits_2 = tta_model_2(imgs.to(device))\n        logits_3 = tta_model_3(imgs.to(device))\n        \n        logits = 0.5*logits_1+0.5*logits_2+0.5*logits_3\n        #logits = logits_2+logits_3\n    # 以最大的logit类为预测，并记录下来\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_count":null,"outputs":[]}]}