{"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":"采用了 Neko Kiku同学提供的baseline (之前没有发现可以直接用kaggle写 结果一直自己的ide琢磨)\n2023了 距离这个比赛刚开放已经过去了两年 想试试一些最新的SOTA的训练效","metadata":{}},{"cell_type":"code","source":"!pip install timm # 一些必要的库 ","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:21.008685Z","iopub.execute_input":"2023-03-04T03:23:21.009426Z","iopub.status.idle":"2023-03-04T03:23:30.956157Z","shell.execute_reply.started":"2023-03-04T03:23:21.009377Z","shell.execute_reply":"2023-03-04T03:23:30.954642Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install ttach","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:30.95919Z","iopub.execute_input":"2023-03-04T03:23:30.959868Z","iopub.status.idle":"2023-03-04T03:23:40.46528Z","shell.execute_reply.started":"2023-03-04T03:23:30.959813Z","shell.execute_reply":"2023-03-04T03:23:40.463867Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import 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","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.467937Z","iopub.execute_input":"2023-03-04T03:23:40.468397Z","iopub.status.idle":"2023-03-04T03:23:40.475286Z","shell.execute_reply.started":"2023-03-04T03:23:40.468346Z","shell.execute_reply":"2023-03-04T03:23:40.474236Z"},"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":"2023-03-04T03:23:40.478699Z","iopub.execute_input":"2023-03-04T03:23:40.479618Z","iopub.status.idle":"2023-03-04T03:23:40.514209Z","shell.execute_reply.started":"2023-03-04T03:23:40.479579Z","shell.execute_reply":"2023-03-04T03:23:40.513095Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_dataframe.describe()","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.516007Z","iopub.execute_input":"2023-03-04T03:23:40.516386Z","iopub.status.idle":"2023-03-04T03:23:40.539982Z","shell.execute_reply.started":"2023-03-04T03:23:40.516352Z","shell.execute_reply":"2023-03-04T03:23:40.5389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 把label文件排个序\nleaves_labels = sorted(list(set(labels_dataframe['label'])))\nn_classes = len(leaves_labels)\n# 这里是label总共的种类数 最后一层的输出要按照 这里的种类来调整\nprint(n_classes)\nleaves_labels[:10]","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.541521Z","iopub.execute_input":"2023-03-04T03:23:40.541872Z","iopub.status.idle":"2023-03-04T03:23:40.551735Z","shell.execute_reply.started":"2023-03-04T03:23:40.541838Z","shell.execute_reply":"2023-03-04T03:23:40.550655Z"},"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":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-03-04T03:23:40.553217Z","iopub.execute_input":"2023-03-04T03:23:40.553837Z","iopub.status.idle":"2023-03-04T03:23:40.5684Z","shell.execute_reply.started":"2023-03-04T03:23:40.553796Z","shell.execute_reply":"2023-03-04T03:23:40.567307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 再转换回来，方便最后预测的时候使用\nnum_to_class = {v : k for k, v in class_to_num.items()}\nnum_to_class","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.570054Z","iopub.execute_input":"2023-03-04T03:23:40.57044Z","iopub.status.idle":"2023-03-04T03:23:40.58455Z","shell.execute_reply.started":"2023-03-04T03:23:40.570404Z","shell.execute_reply":"2023-03-04T03:23:40.58348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 继承pytorch的dataset 创建本次训练所需要用到的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","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.587796Z","iopub.execute_input":"2023-03-04T03:23:40.588062Z","iopub.status.idle":"2023-03-04T03:23:40.602694Z","shell.execute_reply.started":"2023-03-04T03:23:40.588037Z","shell.execute_reply":"2023-03-04T03:23:40.601531Z"},"jupyter":{"source_hidden":true},"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')\n# 判断一下数据是否正确读入了 \nprint(train_dataset)\nprint(val_dataset)\nprint(test_dataset)","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.607028Z","iopub.execute_input":"2023-03-04T03:23:40.607329Z","iopub.status.idle":"2023-03-04T03:23:40.652899Z","shell.execute_reply.started":"2023-03-04T03:23:40.607297Z","shell.execute_reply":"2023-03-04T03:23:40.651621Z"},"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=0\n    )\n\nval_loader = torch.utils.data.DataLoader(\n        dataset=val_dataset,\n        batch_size=8,\n        shuffle=False,\n        num_workers=0,\n    )\ntest_loader = torch.utils.data.DataLoader(\n        dataset=test_dataset,\n        batch_size=8,\n        shuffle=False,\n        num_workers=0\n    )","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.654829Z","iopub.execute_input":"2023-03-04T03:23:40.655256Z","iopub.status.idle":"2023-03-04T03:23:40.666098Z","shell.execute_reply.started":"2023-03-04T03:23:40.655215Z","shell.execute_reply":"2023-03-04T03:23:40.664683Z"},"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":"2023-03-04T03:23:40.668319Z","iopub.execute_input":"2023-03-04T03:23:40.668746Z","iopub.status.idle":"2023-03-04T03:23:40.680393Z","shell.execute_reply.started":"2023-03-04T03:23:40.668704Z","shell.execute_reply":"2023-03-04T03:23:40.679425Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# cutmix 进行数据增强\nMosaic数据增强方法是YOLOV4论文中提出来的，主要思想是将四张图片进行随机裁剪，再拼接到一张图上作为训练数据。\n这样做的好处是丰富了图片的背景，并且四张图片拼接在一起变相地提高了batch_size，在进行batch normalization的时候也会计算四张图片，所以对本身batch_size不是很依赖，单块GPU就可以训练YOLOV4。\n## 分类\n1. Mixup:将随机的两张样本按比例混合，分类的结果按比例分配；\n2. Cutout:随机的将样本中的部分区域cut掉，并且填充0像素值，分类的结果不变；\n3. CutMix:就是将一部分区域cut掉但不填充0像素而是随机填充训练集中的其他数据的区域像素值，分类结果按一定的比例分配\n## 优点:\n1. 在训练过程中不会出现非信息像素，从而能够提高训练效率；\n2. 保留了regional dropout的优势，能够关注目标的non-discriminative parts；\n3. 通过要求模型从局部视图识别对象，对cut区域中添加其他样本的信息，能够进一步增强模型的定位能力；\n4. 不会有图像混合后不自然的情形，能够提升模型分类的表现；训练和推理代价保持不变。","metadata":{}},{"cell_type":"code","source":"\"\"\"输入为：样本的size和生成的随机lamda值\"\"\"\ndef rand_bbox(size, lam):\n    W = size[2]\n    H = size[3]\n    \"\"\"1.论文里的公式2，求出B的rw,rh\"\"\"\n    cut_rat = np.sqrt(1. - lam)\n    cut_w = np.int(W * cut_rat)\n    cut_h = np.int(H * cut_rat)\n\n    # uniform\n    \"\"\"2.论文里的公式2，求出B的rx,ry（bbox的中心点）\"\"\"\n    cx = np.random.randint(W)\n    cy = np.random.randint(H)\n    #限制坐标区域不超过样本大小\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    \"\"\"3.返回剪裁B区域的坐标值\"\"\"\n    return bbx1, bby1, bbx2, bby2","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.681811Z","iopub.execute_input":"2023-03-04T03:23:40.682509Z","iopub.status.idle":"2023-03-04T03:23:40.691306Z","shell.execute_reply.started":"2023-03-04T03:23:40.682471Z","shell.execute_reply":"2023-03-04T03:23:40.690311Z"},"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":"2023-03-04T03:23:40.692929Z","iopub.execute_input":"2023-03-04T03:23:40.693721Z","iopub.status.idle":"2023-03-04T03:23:40.70094Z","shell.execute_reply.started":"2023-03-04T03:23:40.693671Z","shell.execute_reply":"2023-03-04T03:23:40.700006Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import timm\nimport pprint\nmodel1 = timm.create_model('convit_tiny', pretrained=True)\nmodel2  = timm.create_model('cait_xxs24_224',pretrained=True)","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:40.702328Z","iopub.execute_input":"2023-03-04T03:23:40.703013Z","iopub.status.idle":"2023-03-04T03:23:41.389072Z","shell.execute_reply.started":"2023-03-04T03:23:40.702976Z","shell.execute_reply":"2023-03-04T03:23:41.388019Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torch import optim\n# loss and  optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model1.parameters(),lr = learning_rate)\n\"\"\"\nmode: min 和 max 两种模式 min表示当指标不再降低时(如监测loss) max表示当指标不再升高时(如监测accuracy)\nfactor: 学习率调整倍数 即 学习率更新为 lr = lr * factor\npatience: 即忍受该指标多少个step不变化 当忍无可忍时 调整学习率\nverbose: 是否打印学习率\n\"\"\" \nscheduler = optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer, mode='min',\n    factor=0.1, patience=10, verbose=False, threshold=0.0001, threshold_mode='rel', \n    cooldown=0, min_lr=0, eps=1e-08\n)","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:41.390888Z","iopub.execute_input":"2023-03-04T03:23:41.391301Z","iopub.status.idle":"2023-03-04T03:23:41.399175Z","shell.execute_reply.started":"2023-03-04T03:23:41.391247Z","shell.execute_reply":"2023-03-04T03:23:41.39809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model3 = timm.create_model('resnext101_32x8d',pretrained=True)\nmodel3","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:41.400788Z","iopub.execute_input":"2023-03-04T03:23:41.401152Z","iopub.status.idle":"2023-03-04T03:23:41.419202Z","shell.execute_reply.started":"2023-03-04T03:23:41.401116Z","shell.execute_reply":"2023-03-04T03:23:41.418038Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model3.fc = nn.Linear(model3.fc.in_features,176)\nnn.init.xavier_uniform_(model3.fc.weight)\nmodel3 = model3.to(device) #GPU\nmodel3.device = device\ndevice","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:41.421012Z","iopub.execute_input":"2023-03-04T03:23:41.421428Z","iopub.status.idle":"2023-03-04T03:23:41.470028Z","shell.execute_reply.started":"2023-03-04T03:23:41.421392Z","shell.execute_reply":"2023-03-04T03:23:41.469165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from timm import optim\nfrom types import SimpleNamespace\nfrom timm.optim.optim_factory import create_optimizer\nfrom timm import create_model\n\n#训练函数\ndef my_train(model):\n    args = SimpleNamespace()\n    args.weight_decay = 0\n    args.lr = 1e-4\n    args.opt = 'adam' #'lookahead_adam' to use `lookahead`\n    args.momentum = 0.9\n    #optimizer = train_fine_tuning(model, learning_rate)\n    optimizer = optim.create_optimizer(args,model3)\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":"2023-03-04T03:23:41.471673Z","iopub.execute_input":"2023-03-04T03:23:41.472019Z","iopub.status.idle":"2023-03-04T03:23:41.488738Z","shell.execute_reply.started":"2023-03-04T03:23:41.471986Z","shell.execute_reply":"2023-03-04T03:23:41.487716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"saveFileName = './submission.csv'","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:41.490151Z","iopub.execute_input":"2023-03-04T03:23:41.49065Z","iopub.status.idle":"2023-03-04T03:23:41.499522Z","shell.execute_reply.started":"2023-03-04T03:23:41.490614Z","shell.execute_reply":"2023-03-04T03:23:41.49863Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mytrain(model3)","metadata":{"execution":{"iopub.status.busy":"2023-03-04T03:23:41.500938Z","iopub.execute_input":"2023-03-04T03:23:41.501323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#加载预训练模型\nmodel3.load_state_dict(torch.load(model_path))\n\n# 确保模型处于eval模式.\n# 一些模块如 Dropout or BatchNorm 会影响性能 如果模型处于训练模式.\nmodel3.eval()\n\nimport ttach as tta\n#TTA(Test-Time Augmentation) ,即测试时的数据增强\n#加载TTA\n#tta_model = tta.ClassificationTTAWrapper(model, tta.aliases.d4_transform(),  merge_mode='mean')\ntta_model_1 = tta.ClassificationTTAWrapper(model3, 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 = logits_1\n        #logits = logits_2+logits_3\n    # 以最大的logit类为预测，并记录下来\n    predictions.extend(logits.argmax(dim=-1).cpu().numpy().tolist())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds = []\npredictions\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":[]}]}