{"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":"import pandas as pd\nimport shutil\nimport os\n\ndef convert_dataset(csv_filename, pre_path, root_path):\n    path_lst = []\n    # pre_path = \"af2020cv-2020-05-09-v5-dev/data\"\n\n\n    data_file = pd.read_csv(csv_filename)\n    id_tuple = tuple(data_file[\"image\"].values.tolist())\n    classes_tuple = tuple(data_file[\"labels\"].values.tolist())\n\n\n    for i in range(17031):\n        new_path = os.path.join(root_path, str(classes_tuple[i]))\n        if not os.path.exists(new_path):\n            os.makedirs(new_path)\n        shutil.copy(os.path.join(pre_path, id_tuple[i]),os.path.join(new_path,id_tuple[i]+\".jpg\"))\n\n\n\npre_path = '../input/plant-pathology-2021-fgvc8/train_images'  #图片文件之前所在文件夹\ntrain_root_path = \"./train/\"  #待训练图片存储文件夹的路径\n#test_root_path = \"images/test\"    #训练时验证集存储文件夹的路径\ntrain_filename = '../input/apple/train.csv'   #待读取的训练csv文件\n#test_filename = 'af2020cv-2020-05-09-v5-dev/annotation.csv'                             #待读取的验证csv文件\n\n\n# 生成ImageFolder所要求的图片格式\nif __name__ == '__main__':\n    convert_dataset(train_filename, pre_path, train_root_path)\n    #convert_dataset(test_filename, pre_path, test_root_path)\n    print(\"dataset converting is finished!\")\n\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-07-16T01:25:38.732704Z","iopub.execute_input":"2021-07-16T01:25:38.733046Z","iopub.status.idle":"2021-07-16T01:29:50.223376Z","shell.execute_reply.started":"2021-07-16T01:25:38.73301Z","shell.execute_reply":"2021-07-16T01:29:50.221388Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport sys\nimport json\nimport pickle\nimport random\nfrom PIL import Image\nfrom torch.utils.data import Dataset\nfrom tqdm import tqdm\nimport os\nimport argparse\nimport torch.optim as optim\nfrom torchvision import transforms\nimport torch.optim.lr_scheduler as lr_scheduler\nimport matplotlib.pyplot as plt\nimport math\nimport copy\nfrom functools import partial\nfrom collections import OrderedDict\nfrom typing import Optional, Callable\nimport torch\nimport torch.nn as nn\nfrom torch import Tensor\nfrom torch.nn import functional as F\n\n\ndef _make_divisible(ch, divisor=8, min_ch=None):\n    \"\"\"\n    This function is taken from the original tf repo.\n    It ensures that all layers have a channel number that is divisible by 8\n    It can be seen here:\n    https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py\n    \"\"\"\n    if min_ch is None:\n        min_ch = divisor\n    new_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor)\n    # Make sure that round down does not go down by more than 10%.\n    if new_ch < 0.9 * ch:\n        new_ch += divisor\n    return new_ch\n\n\nclass ConvBNActivation(nn.Sequential):\n    def __init__(self,\n                 in_planes: int,\n                 out_planes: int,\n                 kernel_size: int = 3,\n                 stride: int = 1,\n                 groups: int = 1,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None,\n                 activation_layer: Optional[Callable[..., nn.Module]] = None):\n        padding = (kernel_size - 1) // 2\n        if norm_layer is None:\n            norm_layer = nn.BatchNorm2d\n        if activation_layer is None:\n            activation_layer = nn.SiLU  # alias Swish  (torch>=1.7)\n\n        super(ConvBNActivation, self).__init__(nn.Conv2d(in_channels=in_planes,\n                                                         out_channels=out_planes,\n                                                         kernel_size=kernel_size,\n                                                         stride=stride,\n                                                         padding=padding,\n                                                         groups=groups,\n                                                         bias=False),\n                                               norm_layer(out_planes),\n                                               activation_layer())\n\n\nclass SqueezeExcitation(nn.Module):\n    def __init__(self,\n                 input_c: int,   # block input channel\n                 expand_c: int,  # block expand channel\n                 squeeze_factor: int = 4):\n        super(SqueezeExcitation, self).__init__()\n        squeeze_c = input_c // squeeze_factor\n        self.fc1 = nn.Conv2d(expand_c, squeeze_c, 1)\n        self.ac1 = nn.SiLU()  # alias Swish\n        self.fc2 = nn.Conv2d(squeeze_c, expand_c, 1)\n        self.ac2 = nn.Sigmoid()\n\n    def forward(self, x: Tensor) -> Tensor:\n        scale = F.adaptive_avg_pool2d(x, output_size=(1, 1))\n        scale = self.fc1(scale)\n        scale = self.ac1(scale)\n        scale = self.fc2(scale)\n        scale = self.ac2(scale)\n        return scale * x\n\n\nclass InvertedResidualConfig:\n    # kernel_size, in_channel, out_channel, exp_ratio, strides, use_SE, drop_connect_rate\n    def __init__(self,\n                 kernel: int,          # 3 or 5\n                 input_c: int,\n                 out_c: int,\n                 expanded_ratio: int,  # 1 or 6\n                 stride: int,          # 1 or 2\n                 use_se: bool,         # True\n                 drop_rate: float,\n                 index: str,           # 1a, 2a, 2b, ...\n                 width_coefficient: float):\n        self.input_c = self.adjust_channels(input_c, width_coefficient)\n        self.kernel = kernel\n        self.expanded_c = self.input_c * expanded_ratio\n        self.out_c = self.adjust_channels(out_c, width_coefficient)\n        self.use_se = use_se\n        self.stride = stride\n        self.drop_rate = drop_rate\n        self.index = index\n\n    @staticmethod\n    def adjust_channels(channels: int, width_coefficient: float):\n        return _make_divisible(channels * width_coefficient, 8)\n\n\nclass InvertedResidual(nn.Module):\n    def __init__(self,\n                 cnf: InvertedResidualConfig,\n                 norm_layer: Callable[..., nn.Module]):\n        super(InvertedResidual, self).__init__()\n\n        if cnf.stride not in [1, 2]:\n            raise ValueError(\"illegal stride value.\")\n\n        self.use_res_connect = (cnf.stride == 1 and cnf.input_c == cnf.out_c)\n\n        layers = OrderedDict()\n        activation_layer = nn.SiLU  # alias Swish\n\n        # expand\n        if cnf.expanded_c != cnf.input_c:\n            layers.update({\"expand_conv\": ConvBNActivation(cnf.input_c,\n                                                           cnf.expanded_c,\n                                                           kernel_size=1,\n                                                           norm_layer=norm_layer,\n                                                           activation_layer=activation_layer)})\n\n        # depthwise\n        layers.update({\"dwconv\": ConvBNActivation(cnf.expanded_c,\n                                                  cnf.expanded_c,\n                                                  kernel_size=cnf.kernel,\n                                                  stride=cnf.stride,\n                                                  groups=cnf.expanded_c,\n                                                  norm_layer=norm_layer,\n                                                  activation_layer=activation_layer)})\n\n        if cnf.use_se:\n            layers.update({\"se\": SqueezeExcitation(cnf.input_c,\n                                                   cnf.expanded_c)})\n\n        # project\n        layers.update({\"project_conv\": ConvBNActivation(cnf.expanded_c,\n                                                        cnf.out_c,\n                                                        kernel_size=1,\n                                                        norm_layer=norm_layer,\n                                                        activation_layer=nn.Identity)})\n\n        self.block = nn.Sequential(layers)\n        self.out_channels = cnf.out_c\n        self.is_strided = cnf.stride > 1\n\n        # 只有在使用shortcut连接时才使用dropout层\n        if self.use_res_connect and cnf.drop_rate > 0:\n            self.dropout = nn.Dropout2d(p=cnf.drop_rate, inplace=True)\n        else:\n            self.dropout = nn.Identity()\n\n    def forward(self, x: Tensor) -> Tensor:\n        result = self.block(x)\n        result = self.dropout(result)\n        if self.use_res_connect:\n            result += x\n\n        return result\n\n\nclass EfficientNet(nn.Module):\n    def __init__(self,\n                 width_coefficient: float,\n                 depth_coefficient: float,\n                 num_classes: int = 1000,\n                 dropout_rate: float = 0.2,\n                 drop_connect_rate: float = 0.2,\n                 block: Optional[Callable[..., nn.Module]] = None,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None\n                 ):\n        super(EfficientNet, self).__init__()\n\n        # kernel_size, in_channel, out_channel, exp_ratio, strides, use_SE, drop_connect_rate, repeats\n        default_cnf = [[3, 32, 16, 1, 1, True, drop_connect_rate, 1],\n                       [3, 16, 24, 6, 2, True, drop_connect_rate, 2],\n                       [5, 24, 40, 6, 2, True, drop_connect_rate, 2],\n                       [3, 40, 80, 6, 2, True, drop_connect_rate, 3],\n                       [5, 80, 112, 6, 1, True, drop_connect_rate, 3],\n                       [5, 112, 192, 6, 2, True, drop_connect_rate, 4],\n                       [3, 192, 320, 6, 1, True, drop_connect_rate, 1]]\n\n        def round_repeats(repeats):\n            \"\"\"Round number of repeats based on depth multiplier.\"\"\"\n            return int(math.ceil(depth_coefficient * repeats))\n\n        if block is None:\n            block = InvertedResidual\n\n        if norm_layer is None:\n            norm_layer = partial(nn.BatchNorm2d, eps=1e-3, momentum=0.1)\n\n        adjust_channels = partial(InvertedResidualConfig.adjust_channels,\n                                  width_coefficient=width_coefficient)\n\n        # build inverted_residual_setting\n        bneck_conf = partial(InvertedResidualConfig,\n                             width_coefficient=width_coefficient)\n\n        b = 0\n        num_blocks = float(sum(round_repeats(i[-1]) for i in default_cnf))\n        inverted_residual_setting = []\n        for stage, args in enumerate(default_cnf):\n            cnf = copy.copy(args)\n            for i in range(round_repeats(cnf.pop(-1))):\n                if i > 0:\n                    # strides equal 1 except first cnf\n                    cnf[-3] = 1  # strides\n                    cnf[1] = cnf[2]  # input_channel equal output_channel\n\n                cnf[-1] *= b / num_blocks  # update dropout ratio\n                index = str(stage + 1) + chr(i + 97)  # 1a, 2a, 2b, ...\n                inverted_residual_setting.append(bneck_conf(*cnf, index))\n                b += 1\n\n        # create layers\n        layers = OrderedDict()\n\n        # first conv\n        layers.update({\"stem_conv\": ConvBNActivation(in_planes=3,\n                                                     out_planes=adjust_channels(32),\n                                                     kernel_size=3,\n                                                     stride=2,\n                                                     norm_layer=norm_layer)})\n\n        # building inverted residual blocks\n        for cnf in inverted_residual_setting:\n            layers.update({cnf.index: block(cnf, norm_layer)})\n\n        # build top\n        last_conv_input_c = inverted_residual_setting[-1].out_c\n        last_conv_output_c = adjust_channels(1280)\n        layers.update({\"top\": ConvBNActivation(in_planes=last_conv_input_c,\n                                               out_planes=last_conv_output_c,\n                                               kernel_size=1,\n                                               norm_layer=norm_layer)})\n\n        self.features = nn.Sequential(layers)\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n\n        classifier = []\n        if dropout_rate > 0:\n            classifier.append(nn.Dropout(p=dropout_rate, inplace=True))\n        classifier.append(nn.Linear(last_conv_output_c, num_classes))\n        self.classifier = nn.Sequential(*classifier)\n\n        # initial weights\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_normal_(m.weight, mode=\"fan_out\")\n                if m.bias is not None:\n                    nn.init.zeros_(m.bias)\n            elif isinstance(m, nn.BatchNorm2d):\n                nn.init.ones_(m.weight)\n                nn.init.zeros_(m.bias)\n            elif isinstance(m, nn.Linear):\n                nn.init.normal_(m.weight, 0, 0.01)\n                nn.init.zeros_(m.bias)\n\n    def _forward_impl(self, x: Tensor) -> Tensor:\n        x = self.features(x)\n        x = self.avgpool(x)\n        x = torch.flatten(x, 1)\n        x = self.classifier(x)\n\n        return x\n\n    def forward(self, x: Tensor) -> Tensor:\n        return self._forward_impl(x)\n\n\ndef efficientnet_b0(num_classes=1000):\n    # input image size 224x224\n    return EfficientNet(width_coefficient=1.0,\n                        depth_coefficient=1.0,\n                        dropout_rate=0.2,\n                        num_classes=num_classes)\n\n\ndef efficientnet_b1(num_classes=1000):\n    # input image size 240x240\n    return EfficientNet(width_coefficient=1.0,\n                        depth_coefficient=1.1,\n                        dropout_rate=0.2,\n                        num_classes=num_classes)\n\n\ndef efficientnet_b2(num_classes=1000):\n    # input image size 260x260\n    return EfficientNet(width_coefficient=1.1,\n                        depth_coefficient=1.2,\n                        dropout_rate=0.3,\n                        num_classes=num_classes)\n\n\ndef efficientnet_b3(num_classes=1000):\n    # input image size 300x300\n    return EfficientNet(width_coefficient=1.2,\n                        depth_coefficient=1.4,\n                        dropout_rate=0.3,\n                        num_classes=num_classes)\n\n\ndef efficientnet_b4(num_classes=1000):\n    # input image size 380x380\n    return EfficientNet(width_coefficient=1.4,\n                        depth_coefficient=1.8,\n                        dropout_rate=0.4,\n                        num_classes=num_classes)\n\n\ndef efficientnet_b5(num_classes=1000):\n    # input image size 456x456\n    return EfficientNet(width_coefficient=1.6,\n                        depth_coefficient=2.2,\n                        dropout_rate=0.4,\n                        num_classes=num_classes)\n\n\ndef efficientnet_b6(num_classes=1000):\n    # input image size 528x528\n    return EfficientNet(width_coefficient=1.8,\n                        depth_coefficient=2.6,\n                        dropout_rate=0.5,\n                        num_classes=num_classes)\n\n\ndef efficientnet_b7(num_classes=1000):\n    # input image size 600x600\n    return EfficientNet(width_coefficient=2.0,\n                        depth_coefficient=3.1,\n                        dropout_rate=0.5,\n                        num_classes=num_classes)\n\ncreate_model=efficientnet_b7()\ndef read_split_data(root: str, val_rate: float = 0.2):\n    random.seed(0)  # 保证随机结果可复现\n    assert os.path.exists(root), \"dataset root: {} does not exist.\".format(root)\n\n    # 遍历文件夹，一个文件夹对应一个类别\n    flower_class = [cla for cla in os.listdir(root) if os.path.isdir(os.path.join(root, cla))]\n    # 排序，保证顺序一致\n    flower_class.sort()\n    # 生成类别名称以及对应的数字索引\n    class_indices = dict((k, v) for v, k in enumerate(flower_class))\n    json_str = json.dumps(dict((val, key) for key, val in class_indices.items()), indent=4)\n    with open('class_indices.json', 'w') as json_file:\n        json_file.write(json_str)\n\n    train_images_path = []  # 存储训练集的所有图片路径\n    train_images_label = []  # 存储训练集图片对应索引信息\n    val_images_path = []  # 存储验证集的所有图片路径\n    val_images_label = []  # 存储验证集图片对应索引信息\n    every_class_num = []  # 存储每个类别的样本总数\n    supported = [\".jpg\", \".JPG\", \".png\", \".PNG\"]  # 支持的文件后缀类型\n    # 遍历每个文件夹下的文件\n    for cla in flower_class:\n        cla_path = os.path.join(root, cla)\n        # 遍历获取supported支持的所有文件路径\n        images = [os.path.join(root, cla, i) for i in os.listdir(cla_path)\n                  if os.path.splitext(i)[-1] in supported]\n        # 获取该类别对应的索引\n        image_class = class_indices[cla]\n        # 记录该类别的样本数量\n        every_class_num.append(len(images))\n        # 按比例随机采样验证样本\n        val_path = random.sample(images, k=int(len(images) * val_rate))\n\n        for img_path in images:\n            if img_path in val_path:  # 如果该路径在采样的验证集样本中则存入验证集\n                val_images_path.append(img_path)\n                val_images_label.append(image_class)\n            else:  # 否则存入训练集\n                train_images_path.append(img_path)\n                train_images_label.append(image_class)\n\n    print(\"{} images were found in the dataset.\".format(sum(every_class_num)))\n    print(\"{} images for training.\".format(len(train_images_path)))\n    print(\"{} images for validation.\".format(len(val_images_path)))\n\n    plot_image = False\n    if plot_image:\n        # 绘制每种类别个数柱状图\n        plt.bar(range(len(flower_class)), every_class_num, align='center')\n        # 将横坐标0,1,2,3,4替换为相应的类别名称\n        plt.xticks(range(len(flower_class)), flower_class)\n        # 在柱状图上添加数值标签\n        for i, v in enumerate(every_class_num):\n            plt.text(x=i, y=v + 5, s=str(v), ha='center')\n        # 设置x坐标\n        plt.xlabel('image class')\n        # 设置y坐标\n        plt.ylabel('number of images')\n        # 设置柱状图的标题\n        plt.title('flower class distribution')\n        plt.show()\n\n    return train_images_path, train_images_label, val_images_path, val_images_label\n\n\ndef plot_data_loader_image(data_loader):\n    batch_size = data_loader.batch_size\n    plot_num = min(batch_size, 4)\n\n    json_path = './class_indices.json'\n    assert os.path.exists(json_path), json_path + \" does not exist.\"\n    json_file = open(json_path, 'r')\n    class_indices = json.load(json_file)\n\n    for data in data_loader:\n        images, labels = data\n        for i in range(plot_num):\n            # [C, H, W] -> [H, W, C]\n            img = images[i].numpy().transpose(1, 2, 0)\n            # 反Normalize操作\n            img = (img * [0.229, 0.224, 0.225] + [0.485, 0.456, 0.406]) * 255\n            label = labels[i].item()\n            plt.subplot(1, plot_num, i+1)\n            plt.xlabel(class_indices[str(label)])\n            plt.xticks([])  # 去掉x轴的刻度\n            plt.yticks([])  # 去掉y轴的刻度\n            plt.imshow(img.astype('uint8'))\n        plt.show()\n\n\ndef write_pickle(list_info: list, file_name: str):\n    with open(file_name, 'wb') as f:\n        pickle.dump(list_info, f)\n\n\ndef read_pickle(file_name: str) -> list:\n    with open(file_name, 'rb') as f:\n        info_list = pickle.load(f)\n        return info_list\n\n\ndef train_one_epoch(model, optimizer, data_loader, device, epoch):\n    model.train()\n    loss_function = torch.nn.CrossEntropyLoss()\n    mean_loss = torch.zeros(1).to(device)\n    optimizer.zero_grad()\n\n    data_loader = tqdm(data_loader)\n\n    for step, data in enumerate(data_loader):\n        images, labels = data\n\n        pred = model(images.to(device))\n\n        loss = loss_function(pred, labels.to(device))\n        loss.backward()\n        mean_loss = (mean_loss * step + loss.detach()) / (step + 1)  # update mean losses\n\n        data_loader.desc = \"[epoch {}] mean loss {}\".format(epoch, round(mean_loss.item(), 3))\n\n        if not torch.isfinite(loss):\n            print('WARNING: non-finite loss, ending training ', loss)\n            sys.exit(1)\n\n        optimizer.step()\n        optimizer.zero_grad()\n\n    return mean_loss.item()\n\n\n@torch.no_grad()\ndef evaluate(model, data_loader, device):\n    model.eval()\n\n    # 用于存储预测正确的样本个数\n    sum_num = torch.zeros(1).to(device)\n\n    data_loader = tqdm(data_loader)\n\n    for step, data in enumerate(data_loader):\n        images, labels = data\n        pred = model(images.to(device))\n        pred = torch.max(pred, dim=1)[1]\n        sum_num += torch.eq(pred, labels.to(device)).sum()\n\n    return sum_num.item()\n\n\n\n\nclass MyDataSet(Dataset):\n    \"\"\"自定义数据集\"\"\"\n\n    def __init__(self, images_path: list, images_class: list, transform=None):\n        self.images_path = images_path\n        self.images_class = images_class\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.images_path)\n\n    def __getitem__(self, item):\n        img = Image.open(self.images_path[item])\n        # RGB为彩色图片，L为灰度图片\n        if img.mode != 'RGB':\n            raise ValueError(\"image: {} isn't RGB mode.\".format(self.images_path[item]))\n        label = self.images_class[item]\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        return img, label\n\n    @staticmethod\n    def collate_fn(batch):\n        # 官方实现的default_collate可以参考\n        # https://github.com/pytorch/pytorch/blob/67b7e751e6b5931a9f45274653f4f653a4e6cdf6/torch/utils/data/_utils/collate.py\n        images, labels = tuple(zip(*batch))\n\n        images = torch.stack(images, dim=0)\n        labels = torch.as_tensor(labels)\n        return images, labels\n\n\ndef main(args):\n    device = torch.device(args.device if torch.cuda.is_available() else \"cpu\")\n\n    print(args)\n    #print('Start Tensorboard with \"tensorboard --logdir=runs\", view at http://localhost:6006/')\n    #tb_writer = SummaryWriter()\n    if os.path.exists(\"./weights\") is False:\n        os.makedirs(\"./weights\")\n\n    train_images_path, train_images_label, val_images_path, val_images_label = read_split_data('./train')\n    img_size = {\"B0\": 224,\n                \"B1\": 240,\n                \"B2\": 260,\n                \"B3\": 300,\n                \"B4\": 380,\n                \"B5\": 456,\n                \"B6\": 528,\n                \"B7\": 600}\n    num_model = \"B7\"\n\n    data_transform = {\n        \"train\": transforms.Compose([transforms.RandomResizedCrop(img_size[num_model]),\n                                     transforms.RandomHorizontalFlip(),\n                                     transforms.ToTensor(),\n                                     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),\n        \"val\": transforms.Compose([transforms.Resize(img_size[num_model]),\n                                   transforms.CenterCrop(img_size[num_model]),\n                                   transforms.ToTensor(),\n                                   transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}\n\n    # 实例化训练数据集\n    train_data_set = MyDataSet(images_path=train_images_path,\n                               images_class=train_images_label,\n                               transform=data_transform[\"train\"])\n\n    # 实例化验证数据集\n    val_data_set = MyDataSet(images_path=val_images_path,\n                             images_class=val_images_label,\n                             transform=data_transform[\"val\"])\n\n    batch_size = args.batch_size\n    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers\n    print('Using {} dataloader workers every process'.format(nw))\n    train_loader = torch.utils.data.DataLoader(train_data_set,\n                                               batch_size=batch_size,\n                                               shuffle=True,\n                                               pin_memory=True,\n                                               num_workers=nw,\n                                               collate_fn=train_data_set.collate_fn)\n\n    val_loader = torch.utils.data.DataLoader(val_data_set,\n                                             batch_size=batch_size,\n                                             shuffle=False,\n                                             pin_memory=True,\n                                             num_workers=nw,\n                                             collate_fn=val_data_set.collate_fn)\n\n    # 如果存在预训练权重则载入\n    model = efficientnet_b7(num_classes=7).to(device)\n    if os.path.exists(args.weights):\n        weights_dict = torch.load(args.weights, map_location=device)\n        load_weights_dict = {k: v for k, v in weights_dict.items()\n                             if model.state_dict()[k].numel() == v.numel()}\n        print(model.load_state_dict(load_weights_dict, strict=False))\n\n    # 是否冻结权重\n    if args.freeze_layers:\n        for name, para in model.named_parameters():\n            # 除最后一个卷积层和全连接层外，其他权重全部冻结\n            if (\"features.top\" not in name) and (\"classifier\" not in name):\n                para.requires_grad_(False)\n            else:\n                print(\"training {}\".format(name))\n\n    pg = [p for p in model.parameters() if p.requires_grad]\n    optimizer = optim.SGD(pg, lr=args.lr, momentum=0.9, weight_decay=1E-4)\n    # Scheduler https://arxiv.org/pdf/1812.01187.pdf\n    lf = lambda x: ((1 + math.cos(x * math.pi / args.epochs)) / 2) * (1 - args.lrf) + args.lrf  # cosine\n    scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)\n\n    for epoch in range(args.epochs):\n        # train\n        mean_loss = train_one_epoch(model=model,\n                                    optimizer=optimizer,\n                                    data_loader=train_loader,\n                                    device=device,\n                                    epoch=epoch)\n\n        scheduler.step()\n        torch.save(model.state_dict(), \"./weights/model-train-{}.pth\".format(epoch))\n        torch.save(model.state_dict(), \"./model-train-{}.pth\".format(epoch))\n        torch.save(model.state_dict() , f'efficientnet-b7-{epoch }.pth ')\n\n        # validate\n        sum_num = evaluate(model=model,\n                           data_loader=val_loader,\n                           device=device)\n        acc = sum_num / len(val_data_set)\n        print(\"[epoch {}] accuracy: {}\".format(epoch, round(acc, 3)))\n        tags = [\"loss\", \"accuracy\", \"learning_rate\"]\n        # tb_writer.add_scalar(tags[0], mean_loss, epoch)\n        # tb_writer.add_scalar(tags[1], acc, epoch)\n        # tb_writer.add_scalar(tags[2], optimizer.param_groups[0][\"lr\"], epoch)\n\n        torch.save(model.state_dict(), \"./weights/model-{}.pth\".format(epoch))\n        torch.save(model.state_dict(), \"./model-{}.pth\".format(epoch))\n        torch.save(model.state_dict() , f'efficientnet-test-b7-{epoch }.pth ')\n","metadata":{"execution":{"iopub.status.busy":"2021-07-16T02:46:06.951119Z","iopub.execute_input":"2021-07-16T02:46:06.951704Z","iopub.status.idle":"2021-07-16T02:46:10.54463Z","shell.execute_reply.started":"2021-07-16T02:46:06.951585Z","shell.execute_reply":"2021-07-16T02:46:10.543489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--num_classes', type=int, default=7)\n    parser.add_argument('--epochs', type=int, default=1)\n    parser.add_argument('--batch-size', type=int, default=1)\n    parser.add_argument('--lr', type=float, default=0.01)\n    parser.add_argument('--lrf', type=float, default=0.01)\n\n    # 数据集所在根目录\n    # http://download.tensorflow.org/example_images/flower_photos.tgz\n    parser.add_argument('--data-path', type=str,\n                        default=\"../output/train\" )\n\n    # download model weights\n    # 链接: https://pan.baidu.com/s/1ouX0UmjCsmSx3ZrqXbowjw  密码: 090i\n    parser.add_argument('--weights', type=str, default='./efficientnetb7.pth',\n                        help='initial weights path')\n    parser.add_argument('--freeze-layers', type=bool, default=False)\n    parser.add_argument('--device', default='cuda:0', help='device id (i.e. 0 or 0,1 or cpu)')\n\n    opt = parser.parse_known_args()[0]\n\n\n    main(opt)","metadata":{"execution":{"iopub.status.busy":"2021-07-16T02:46:21.295594Z","iopub.execute_input":"2021-07-16T02:46:21.296118Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport json\n\nimport torch\nfrom PIL import Image\nfrom torchvision import transforms\nimport matplotlib.pyplot as plt\n\n\nos.environ['KMP_DUPLICATE_LIB_OK']='TRUE'\ndef main():\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n\n    img_size = {\"B0\": 224,\n                \"B1\": 240,\n                \"B2\": 260,\n                \"B3\": 300,\n                \"B4\": 380,\n                \"B5\": 456,\n                \"B6\": 528,\n                \"B7\": 600}\n    num_model = \"B7\"\n\n    data_transform = transforms.Compose(\n        [transforms.Resize(img_size[num_model]),\n         transforms.CenterCrop(img_size[num_model]),\n         transforms.ToTensor(),\n         transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])\n\n    # load image\n    img_path = \"../input/plant-pathology-2021-fgvc8/test_images/ad8770db05586b59.jpg\"\n    assert os.path.exists(img_path), \"file: '{}' dose not exist.\".format(img_path)\n    img = Image.open(img_path)\n    plt.imshow(img)\n    # [N, C, H, W]\n    img = data_transform(img)\n    # expand batch dimension\n    img = torch.unsqueeze(img, dim=0)\n\n    # read class_indict\n    json_path = '../input/applez/class_indices.json'\n    #assert os.path.exists(json_path), \"file: '{}' dose not exist.\".format(json_path)\n\n    json_file = open(json_path, \"r\")\n    class_indict = json.load(json_file)\n\n    # create model\n    model = efficientnet_b7(num_classes=7).to(device)\n    # load model weights\n    model_weight_path = \"./weights/model-train-{}.pth\"\n    model.load_state_dict(torch.load(model_weight_path, map_location=device))\n    model.eval()\n    with torch.no_grad():\n        # predict class\n        output = torch.squeeze(model(img.to(device))).cpu()\n        predict = torch.softmax(output, dim=0)\n        predict_cla = torch.argmax(predict).numpy()\n\n    print_res = \"class: {}   prob: {:.3}\".format(class_indict[str(predict_cla)],\n                                                 predict[predict_cla].numpy())\n    plt.title(print_res)\n    print(print_res)\n    plt.show()\n\n\nif __name__ == '__main__':\n    main()\n","metadata":{"execution":{"iopub.status.busy":"2021-07-16T00:51:55.801159Z","iopub.execute_input":"2021-07-16T00:51:55.801475Z","iopub.status.idle":"2021-07-16T00:52:03.557314Z","shell.execute_reply.started":"2021-07-16T00:51:55.801445Z","shell.execute_reply":"2021-07-16T00:52:03.555802Z"},"trusted":true},"execution_count":null,"outputs":[]}]}